Introduce the MapLoan to infinite scrolled map

This commit is contained in:
Gwilym Kuiper 2022-01-31 21:23:12 +00:00
parent 2c8fce40d3
commit 10c97f48d8
7 changed files with 80 additions and 43 deletions

View file

@ -46,20 +46,20 @@ fn main(mut gba: agb::Gba) -> ! {
.unwrap() .unwrap()
}; };
let mut gfx = gba.display.video.tiled0(); let (gfx, mut vram) = gba.display.video.tiled0();
let vblank = agb::interrupt::VBlank::get(); let vblank = agb::interrupt::VBlank::get();
let mut input = agb::input::ButtonController::new(); let mut input = agb::input::ButtonController::new();
gfx.vram.set_background_palette_raw(&MAP_PALETTE); vram.set_background_palette_raw(&MAP_PALETTE);
let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp); let tileset = TileSet::new(&MAP_TILES, TileFormat::FourBpp);
let tileset_ref = gfx.vram.add_tileset(tileset); let tileset_ref = vram.add_tileset(tileset);
let mut background = gfx.background(); let mut background = gfx.background(agb::display::Priority::P0);
for (i, &tile) in MAP_MAP.iter().enumerate() { for (i, &tile) in MAP_MAP.iter().enumerate() {
let i = i as u16; let i = i as u16;
background.set_tile( background.set_tile(
&mut gfx.vram, &mut vram,
(i % 32, i / 32).into(), (i % 32, i / 32).into(),
tileset_ref, tileset_ref,
TileSetting::from_raw(tile), TileSetting::from_raw(tile),

View file

@ -5,10 +5,9 @@ use agb::display::example_logo;
#[agb::entry] #[agb::entry]
fn main(mut gba: agb::Gba) -> ! { fn main(mut gba: agb::Gba) -> ! {
let mut gfx = gba.display.video.tiled0(); let (gfx, mut vram) = gba.display.video.tiled0();
let mut map = gfx.background(agb::display::Priority::P0); let mut map = gfx.background(agb::display::Priority::P0);
let mut vram = gfx.vram;
example_logo::display_logo(&mut map, &mut vram); example_logo::display_logo(&mut map, &mut vram);

View file

@ -17,11 +17,11 @@ struct BackCosines {
#[agb::entry] #[agb::entry]
fn main(mut gba: agb::Gba) -> ! { fn main(mut gba: agb::Gba) -> ! {
let mut gfx = gba.display.video.tiled0(); let (gfx, mut vram) = gba.display.video.tiled0();
let mut background = gfx.background(agb::display::Priority::P0); let mut background = gfx.background(agb::display::Priority::P0);
example_logo::display_logo(&mut background, &mut gfx.vram); example_logo::display_logo(&mut background, &mut vram);
let mut time = 0; let mut time = 0;
let cosines = [0_u16; 32]; let cosines = [0_u16; 32];

View file

@ -1,4 +1,5 @@
use core::cell::RefCell; use core::cell::RefCell;
use core::ops::{Deref, DerefMut};
use alloc::vec::Vec; use alloc::vec::Vec;
use alloc::{boxed::Box, vec}; use alloc::{boxed::Box, vec};
@ -424,17 +425,17 @@ impl RegularMap {
} }
} }
pub struct InfiniteScrolledMap { pub struct InfiniteScrolledMap<'a> {
map: RegularMap, map: MapLoan<'a, RegularMap>,
get_tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>, get_tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>,
current_pos: Vector2D<i32>, current_pos: Vector2D<i32>,
offset: Vector2D<i32>, offset: Vector2D<i32>,
} }
impl InfiniteScrolledMap { impl<'a> InfiniteScrolledMap<'a> {
pub fn new( pub fn new(
map: RegularMap, map: MapLoan<'a, RegularMap>,
get_tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>, get_tile: Box<dyn Fn(Vector2D<i32>) -> (TileSetReference, TileSetting)>,
) -> Self { ) -> Self {
Self { Self {
@ -613,25 +614,21 @@ fn div_ceil(x: i32, y: i32) -> i32 {
} }
} }
pub struct Tiled0<'a> { pub struct Tiled0 {
regular: RefCell<Bitarray<1>>, regular: RefCell<Bitarray<1>>,
pub vram: VRamManager<'a>,
} }
impl Tiled0<'_> { impl Tiled0 {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D);
set_graphics_mode(DisplayMode::Tiled0); set_graphics_mode(DisplayMode::Tiled0);
Self { Self {
regular: Default::default(), regular: Default::default(),
vram: VRamManager::new(),
} }
} }
pub fn background(&mut self, priority: Priority) -> RegularMap { pub fn background(&self, priority: Priority) -> MapLoan<'_, RegularMap> {
let mut regular = self.regular.borrow_mut(); let mut regular = self.regular.borrow_mut();
let new_background = regular.first_zero().unwrap(); let new_background = regular.first_zero().unwrap();
if new_background >= 4 { if new_background >= 4 {
@ -642,7 +639,7 @@ impl Tiled0<'_> {
regular.set(new_background, true); regular.set(new_background, true);
bg MapLoan::new(bg, new_background as u8, &self.regular)
} }
} }
@ -651,3 +648,41 @@ impl TileSetReference {
Self { id, generation } Self { id, generation }
} }
} }
pub struct MapLoan<'a, T> {
map: T,
background_id: u8,
regular_map_list: &'a RefCell<Bitarray<1>>,
}
impl<'a, T> Deref for MapLoan<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.map
}
}
impl<'a, T> DerefMut for MapLoan<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.map
}
}
impl<'a, T> MapLoan<'a, T> {
fn new(map: T, background_id: u8, regular_map_list: &'a RefCell<Bitarray<1>>) -> Self {
MapLoan {
map,
background_id,
regular_map_list,
}
}
}
impl<'a, T> Drop for MapLoan<'a, T> {
fn drop(&mut self) {
self.regular_map_list
.borrow_mut()
.set(self.background_id as usize, false);
}
}

View file

@ -33,11 +33,11 @@ mod tests {
#[test_case] #[test_case]
fn logo_display(gba: &mut crate::Gba) { fn logo_display(gba: &mut crate::Gba) {
let mut gfx = gba.display.video.tiled0(); let (gfx, mut vram) = gba.display.video.tiled0();
let mut map = gfx.background(crate::display::Priority::P0); let mut map = gfx.background(crate::display::Priority::P0);
display_logo(&mut map, &mut gfx.vram); display_logo(&mut map, &mut vram);
crate::test_runner::assert_image_output("gfx/test_logo.png"); crate::test_runner::assert_image_output("gfx/test_logo.png");
} }

View file

@ -1,4 +1,8 @@
use super::{background::Tiled0, bitmap3::Bitmap3, bitmap4::Bitmap4}; use super::{
background::{Tiled0, VRamManager},
bitmap3::Bitmap3,
bitmap4::Bitmap4,
};
#[non_exhaustive] #[non_exhaustive]
pub struct Video {} pub struct Video {}
@ -14,7 +18,7 @@ impl Video {
unsafe { Bitmap4::new() } unsafe { Bitmap4::new() }
} }
pub fn tiled0(&mut self) -> Tiled0 { pub fn tiled0(&mut self) -> (Tiled0, VRamManager<'_>) {
unsafe { Tiled0::new() } (unsafe { Tiled0::new() }, VRamManager::new())
} }
} }

View file

@ -28,21 +28,21 @@ agb::include_gfx!("gfx/background.toml");
type Number = FixedNum<8>; type Number = FixedNum<8>;
struct Level { struct Level<'a> {
background: InfiniteScrolledMap, background: InfiniteScrolledMap<'a>,
foreground: InfiniteScrolledMap, foreground: InfiniteScrolledMap<'a>,
clouds: InfiniteScrolledMap, clouds: InfiniteScrolledMap<'a>,
slime_spawns: Vec<(u16, u16)>, slime_spawns: Vec<(u16, u16)>,
bat_spawns: Vec<(u16, u16)>, bat_spawns: Vec<(u16, u16)>,
emu_spawns: Vec<(u16, u16)>, emu_spawns: Vec<(u16, u16)>,
} }
impl Level { impl<'a> Level<'a> {
fn load_level( fn load_level(
mut backdrop: InfiniteScrolledMap, mut backdrop: InfiniteScrolledMap<'a>,
mut foreground: InfiniteScrolledMap, mut foreground: InfiniteScrolledMap<'a>,
mut clouds: InfiniteScrolledMap, mut clouds: InfiniteScrolledMap<'a>,
vram: &mut VRamManager, vram: &mut VRamManager,
) -> Self { ) -> Self {
backdrop.init(vram, (8, 8).into()); backdrop.init(vram, (8, 8).into());
@ -1772,7 +1772,7 @@ struct Game<'a> {
player: Player<'a>, player: Player<'a>,
input: ButtonController, input: ButtonController,
frame_count: u32, frame_count: u32,
level: Level, level: Level<'a>,
offset: Vector2D<Number>, offset: Vector2D<Number>,
shake_time: u16, shake_time: u16,
sunrise_timer: u16, sunrise_timer: u16,
@ -2086,7 +2086,7 @@ impl<'a> Game<'a> {
vram.set_background_palettes(&modified_palettes); vram.set_background_palettes(&modified_palettes);
} }
fn new(object: &'a ObjectControl, level: Level, start_at_boss: bool) -> Self { fn new(object: &'a ObjectControl, level: Level<'a>, start_at_boss: bool) -> Self {
let mut player = Player::new(object); let mut player = Player::new(object);
let mut offset = (8, 8).into(); let mut offset = (8, 8).into();
if start_at_boss { if start_at_boss {
@ -2138,12 +2138,11 @@ fn game_with_level(gba: &mut agb::Gba) {
let mut start_at_boss = false; let mut start_at_boss = false;
loop { loop {
let mut background = gba.display.video.tiled0(); let (background, mut vram) = gba.display.video.tiled0();
background
.vram
.set_background_palettes(background::background.palettes);
let tileset_ref = background.vram.add_tileset(TileSet::new( vram.set_background_palettes(background::background.palettes);
let tileset_ref = vram.add_tileset(TileSet::new(
background::background.tiles, background::background.tiles,
TileFormat::FourBpp, TileFormat::FourBpp,
)); ));
@ -2189,7 +2188,7 @@ fn game_with_level(gba: &mut agb::Gba) {
let mut game = Game::new( let mut game = Game::new(
&object, &object,
Level::load_level(backdrop, foreground, clouds, &mut background.vram), Level::load_level(backdrop, foreground, clouds, &mut vram),
start_at_boss, start_at_boss,
); );
@ -2197,7 +2196,7 @@ fn game_with_level(gba: &mut agb::Gba) {
sfx.frame(); sfx.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
sfx.after_vblank(); sfx.after_vblank();
match game.advance_frame(&object, &mut background.vram, &mut sfx) { match game.advance_frame(&object, &mut vram, &mut sfx) {
GameStatus::Continue => {} GameStatus::Continue => {}
GameStatus::Lost => { GameStatus::Lost => {
break false; break false;