From 10c97f48d890b798dd95731a3da060e642ff9576 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 31 Jan 2022 21:23:12 +0000 Subject: [PATCH] Introduce the MapLoan to infinite scrolled map --- agb/examples/chicken.rs | 10 ++--- agb/examples/test_logo.rs | 3 +- agb/examples/wave.rs | 4 +- agb/src/display/background.rs | 59 +++++++++++++++++++++------ agb/src/display/example_logo.rs | 4 +- agb/src/display/video.rs | 10 +++-- examples/the-purple-night/src/main.rs | 33 ++++++++------- 7 files changed, 80 insertions(+), 43 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index af2a7294..344952e1 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -46,20 +46,20 @@ fn main(mut gba: agb::Gba) -> ! { .unwrap() }; - let mut gfx = gba.display.video.tiled0(); + let (gfx, mut vram) = gba.display.video.tiled0(); let vblank = agb::interrupt::VBlank::get(); 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_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() { let i = i as u16; background.set_tile( - &mut gfx.vram, + &mut vram, (i % 32, i / 32).into(), tileset_ref, TileSetting::from_raw(tile), diff --git a/agb/examples/test_logo.rs b/agb/examples/test_logo.rs index b7436e08..674867c9 100644 --- a/agb/examples/test_logo.rs +++ b/agb/examples/test_logo.rs @@ -5,10 +5,9 @@ use agb::display::example_logo; #[agb::entry] 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 vram = gfx.vram; example_logo::display_logo(&mut map, &mut vram); diff --git a/agb/examples/wave.rs b/agb/examples/wave.rs index af1d5049..fd513367 100644 --- a/agb/examples/wave.rs +++ b/agb/examples/wave.rs @@ -17,11 +17,11 @@ struct BackCosines { #[agb::entry] 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); - example_logo::display_logo(&mut background, &mut gfx.vram); + example_logo::display_logo(&mut background, &mut vram); let mut time = 0; let cosines = [0_u16; 32]; diff --git a/agb/src/display/background.rs b/agb/src/display/background.rs index 275f8629..2c1b2070 100644 --- a/agb/src/display/background.rs +++ b/agb/src/display/background.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::ops::{Deref, DerefMut}; use alloc::vec::Vec; use alloc::{boxed::Box, vec}; @@ -424,17 +425,17 @@ impl RegularMap { } } -pub struct InfiniteScrolledMap { - map: RegularMap, +pub struct InfiniteScrolledMap<'a> { + map: MapLoan<'a, RegularMap>, get_tile: Box) -> (TileSetReference, TileSetting)>, current_pos: Vector2D, offset: Vector2D, } -impl InfiniteScrolledMap { +impl<'a> InfiniteScrolledMap<'a> { pub fn new( - map: RegularMap, + map: MapLoan<'a, RegularMap>, get_tile: Box) -> (TileSetReference, TileSetting)>, ) -> Self { Self { @@ -613,25 +614,21 @@ fn div_ceil(x: i32, y: i32) -> i32 { } } -pub struct Tiled0<'a> { +pub struct Tiled0 { regular: RefCell>, - - pub vram: VRamManager<'a>, } -impl Tiled0<'_> { +impl Tiled0 { pub(crate) unsafe fn new() -> Self { set_graphics_settings(GraphicsSettings::empty() | GraphicsSettings::SPRITE1_D); set_graphics_mode(DisplayMode::Tiled0); Self { 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 new_background = regular.first_zero().unwrap(); if new_background >= 4 { @@ -642,7 +639,7 @@ impl Tiled0<'_> { regular.set(new_background, true); - bg + MapLoan::new(bg, new_background as u8, &self.regular) } } @@ -651,3 +648,41 @@ impl TileSetReference { Self { id, generation } } } + +pub struct MapLoan<'a, T> { + map: T, + background_id: u8, + regular_map_list: &'a RefCell>, +} + +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>) -> 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); + } +} diff --git a/agb/src/display/example_logo.rs b/agb/src/display/example_logo.rs index 4d94208e..21ef97d5 100644 --- a/agb/src/display/example_logo.rs +++ b/agb/src/display/example_logo.rs @@ -33,11 +33,11 @@ mod tests { #[test_case] 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); - display_logo(&mut map, &mut gfx.vram); + display_logo(&mut map, &mut vram); crate::test_runner::assert_image_output("gfx/test_logo.png"); } diff --git a/agb/src/display/video.rs b/agb/src/display/video.rs index d4f61270..fce2b572 100644 --- a/agb/src/display/video.rs +++ b/agb/src/display/video.rs @@ -1,4 +1,8 @@ -use super::{background::Tiled0, bitmap3::Bitmap3, bitmap4::Bitmap4}; +use super::{ + background::{Tiled0, VRamManager}, + bitmap3::Bitmap3, + bitmap4::Bitmap4, +}; #[non_exhaustive] pub struct Video {} @@ -14,7 +18,7 @@ impl Video { unsafe { Bitmap4::new() } } - pub fn tiled0(&mut self) -> Tiled0 { - unsafe { Tiled0::new() } + pub fn tiled0(&mut self) -> (Tiled0, VRamManager<'_>) { + (unsafe { Tiled0::new() }, VRamManager::new()) } } diff --git a/examples/the-purple-night/src/main.rs b/examples/the-purple-night/src/main.rs index 3db060b3..abca5419 100644 --- a/examples/the-purple-night/src/main.rs +++ b/examples/the-purple-night/src/main.rs @@ -28,21 +28,21 @@ agb::include_gfx!("gfx/background.toml"); type Number = FixedNum<8>; -struct Level { - background: InfiniteScrolledMap, - foreground: InfiniteScrolledMap, - clouds: InfiniteScrolledMap, +struct Level<'a> { + background: InfiniteScrolledMap<'a>, + foreground: InfiniteScrolledMap<'a>, + clouds: InfiniteScrolledMap<'a>, slime_spawns: Vec<(u16, u16)>, bat_spawns: Vec<(u16, u16)>, emu_spawns: Vec<(u16, u16)>, } -impl Level { +impl<'a> Level<'a> { fn load_level( - mut backdrop: InfiniteScrolledMap, - mut foreground: InfiniteScrolledMap, - mut clouds: InfiniteScrolledMap, + mut backdrop: InfiniteScrolledMap<'a>, + mut foreground: InfiniteScrolledMap<'a>, + mut clouds: InfiniteScrolledMap<'a>, vram: &mut VRamManager, ) -> Self { backdrop.init(vram, (8, 8).into()); @@ -1772,7 +1772,7 @@ struct Game<'a> { player: Player<'a>, input: ButtonController, frame_count: u32, - level: Level, + level: Level<'a>, offset: Vector2D, shake_time: u16, sunrise_timer: u16, @@ -2086,7 +2086,7 @@ impl<'a> Game<'a> { 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 offset = (8, 8).into(); if start_at_boss { @@ -2138,12 +2138,11 @@ fn game_with_level(gba: &mut agb::Gba) { let mut start_at_boss = false; loop { - let mut background = gba.display.video.tiled0(); - background - .vram - .set_background_palettes(background::background.palettes); + let (background, mut vram) = gba.display.video.tiled0(); - 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, TileFormat::FourBpp, )); @@ -2189,7 +2188,7 @@ fn game_with_level(gba: &mut agb::Gba) { let mut game = Game::new( &object, - Level::load_level(backdrop, foreground, clouds, &mut background.vram), + Level::load_level(backdrop, foreground, clouds, &mut vram), start_at_boss, ); @@ -2197,7 +2196,7 @@ fn game_with_level(gba: &mut agb::Gba) { sfx.frame(); vblank.wait_for_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::Lost => { break false;