nicer way of using mutable backing storages

This commit is contained in:
Corwin Kuiper 2021-08-07 18:07:31 +01:00
parent d6b388e1d6
commit 7798f4c95f
3 changed files with 79 additions and 26 deletions

View file

@ -3,7 +3,7 @@
extern crate agb; extern crate agb;
use agb::{ use agb::{
display::{object::ObjectStandard, HEIGHT, WIDTH}, display::{object::ObjectStandard, tiled0::Map, HEIGHT, WIDTH},
input::Button, input::Button,
}; };
use core::convert::TryInto; use core::convert::TryInto;
@ -52,11 +52,7 @@ pub fn main() -> ! {
gfx.set_background_tilemap(0, &MAP_TILES); gfx.set_background_tilemap(0, &MAP_TILES);
let mut background = gfx.get_background().unwrap(); let mut background = gfx.get_background().unwrap();
background.set_map(agb::display::tiled0::Map { background.set_map(Map::new(&MAP_MAP, (32_u32, 32_u32).into(), 0));
store: MAP_MAP.as_ref(),
dimensions: (32_u32, 32_u32).into(),
default: 0,
});
background.show(); background.show();
background.commit(); background.commit();

View file

@ -2,9 +2,8 @@ use crate::display::tiled0::Tiled0;
crate::include_gfx!("gfx/agb_logo.toml"); crate::include_gfx!("gfx/agb_logo.toml");
use super::tiled0::Map;
pub fn display_logo(gfx: &mut Tiled0) { pub fn display_logo(gfx: &mut Tiled0) {
use super::tiled0::Map;
gfx.set_background_palettes(agb_logo::test_logo.palettes); gfx.set_background_palettes(agb_logo::test_logo.palettes);
gfx.set_background_tilemap(0, agb_logo::test_logo.tiles); gfx.set_background_tilemap(0, agb_logo::test_logo.tiles);
@ -16,11 +15,7 @@ pub fn display_logo(gfx: &mut Tiled0) {
entries[tile_id as usize] = tile_id | (palette_entry << 12); entries[tile_id as usize] = tile_id | (palette_entry << 12);
} }
back.set_map(Map { back.set_map(Map::new(&entries, (30_u32, 20_u32).into(), 0));
store: entries.as_ref(),
dimensions: (30_u32, 20_u32).into(),
default: 0,
});
back.show(); back.show();
} }

View file

@ -30,30 +30,86 @@ pub enum BackgroundSize {
S64x64 = 3, S64x64 = 3,
} }
pub trait MapStorage: Deref<Target = [u16]> {} #[derive(PartialEq, Eq, Clone, Copy)]
impl MapStorage for &[u16] {} enum Mutability {
impl MapStorage for &mut [u16] {} Immutable,
Mutable,
}
struct MapStorage<'a> {
s: *const [u16],
mutability: Mutability,
_phantom: core::marker::PhantomData<&'a ()>,
}
impl<'a> Index<usize> for MapStorage<'a> {
type Output = u16;
fn index(&self, index: usize) -> &Self::Output {
&self.get()[index]
}
}
impl<'a> MapStorage<'a> {
fn new(store: &[u16]) -> MapStorage {
MapStorage {
s: store as *const _,
mutability: Mutability::Immutable,
_phantom: core::marker::PhantomData,
}
}
fn new_mutable(store: &mut [u16]) -> MapStorage {
MapStorage {
s: store as *const _,
mutability: Mutability::Mutable,
_phantom: core::marker::PhantomData,
}
}
fn get(&self) -> &[u16] {
unsafe { &*self.s }
}
fn get_mut(&mut self) -> &mut [u16] {
assert!(
self.mutability == Mutability::Mutable,
"backing storage must be mutable in order to get internal storage mutably"
);
unsafe { &mut *(self.s as *mut _) }
}
}
/// The map background is the method of drawing game maps to the screen. It /// The map background is the method of drawing game maps to the screen. It
/// automatically handles copying the correct portion of a provided map to the /// automatically handles copying the correct portion of a provided map to the
/// assigned block depending on given coordinates. /// assigned block depending on given coordinates.
pub struct Background<S: MapStorage> { pub struct Background<'a> {
background: u8, background: u8,
block: u8, block: u8,
commited_position: Vector2D<i32>, commited_position: Vector2D<i32>,
shadowed_position: Vector2D<i32>, shadowed_position: Vector2D<i32>,
poisoned: bool, poisoned: bool,
shadowed_register: u16, shadowed_register: u16,
map: Option<Map<S>>, map: Option<Map<'a>>,
} }
pub struct Map<S: MapStorage> { pub struct Map<'a> {
pub store: S, store: MapStorage<'a>,
pub dimensions: Vector2D<u32>, pub dimensions: Vector2D<u32>,
pub default: u16, pub default: u16,
} }
impl<'a, S: MapStorage> Map<S> { impl<'a> Map<'a> {
pub fn new(map: &[u16], dimensions: Vector2D<u32>, default: u16) -> Map {
Map {
store: MapStorage::new(map),
dimensions,
default,
}
}
pub fn new_mutable(map: &mut [u16], dimensions: Vector2D<u32>, default: u16) -> Map {
Map {
store: MapStorage::new_mutable(map),
dimensions,
default,
}
}
fn get_position(&self, x: i32, y: i32) -> u16 { fn get_position(&self, x: i32, y: i32) -> u16 {
if x < 0 || x as u32 >= self.dimensions.x { if x < 0 || x as u32 >= self.dimensions.x {
self.default self.default
@ -63,10 +119,16 @@ impl<'a, S: MapStorage> Map<S> {
self.store[y as usize * self.dimensions.x as usize + x as usize] self.store[y as usize * self.dimensions.x as usize + x as usize]
} }
} }
pub fn get_store(&self) -> &[u16] {
self.store.get()
}
pub fn get_mutable_store(&mut self) -> &mut [u16] {
self.store.get_mut()
}
} }
impl<'a, S: MapStorage> Background<S> { impl<'a> Background<'a> {
unsafe fn new(background: u8, block: u8) -> Background<S> { unsafe fn new(background: u8, block: u8) -> Background<'a> {
let mut b = Background { let mut b = Background {
background, background,
block, block,
@ -135,12 +197,12 @@ impl<'a, S: MapStorage> Background<S> {
self.shadowed_position = position; self.shadowed_position = position;
} }
pub fn get_map(&mut self) -> Option<&mut Map<S>> { pub fn get_map(&mut self) -> Option<&mut Map<'a>> {
self.poisoned = true; self.poisoned = true;
self.map.as_mut() self.map.as_mut()
} }
pub fn set_map(&mut self, map: Map<S>) { pub fn set_map(&mut self, map: Map<'a>) {
self.poisoned = true; self.poisoned = true;
self.map = Some(map); self.map = Some(map);
} }
@ -266,7 +328,7 @@ impl Tiled0 {
} }
/// Gets a map background if possible and assigns an unused block to it. /// Gets a map background if possible and assigns an unused block to it.
pub fn get_background<S: MapStorage>(&mut self) -> Result<Background<S>, &'static str> { pub fn get_background(&mut self) -> Result<Background, &'static str> {
if self.num_backgrounds >= 4 { if self.num_backgrounds >= 4 {
return Err("too many backgrounds created, maximum is 4"); return Err("too many backgrounds created, maximum is 4");
} }