diff --git a/CHANGELOG.md b/CHANGELOG.md index c5e86550..833f2d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Made Vector2D::new a const function. - The template now uses rust 2021 edition by default. +- All objects which should only be created once now have the correct lifetimes to only allow one to exist. ### Fixed - Alpha channel is now considered by `include_gfx!()` even when `transparent_colour` is absent. diff --git a/agb/src/display/bitmap3.rs b/agb/src/display/bitmap3.rs index 01a69e11..2674d47e 100644 --- a/agb/src/display/bitmap3.rs +++ b/agb/src/display/bitmap3.rs @@ -4,19 +4,23 @@ use super::{ set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, HEIGHT, WIDTH, }; -use core::convert::TryInto; +use core::{convert::TryInto, marker::PhantomData}; const BITMAP_MODE_3: MemoryMapped2DArray = unsafe { MemoryMapped2DArray::new(0x600_0000) }; #[non_exhaustive] -pub struct Bitmap3 {} +pub struct Bitmap3<'gba> { + phantom: PhantomData<&'gba ()>, +} -impl Bitmap3 { +impl Bitmap3<'_> { pub(crate) unsafe fn new() -> Self { set_graphics_mode(DisplayMode::Bitmap3); set_graphics_settings(GraphicsSettings::LAYER_BG2); - Bitmap3 {} + Bitmap3 { + phantom: PhantomData, + } } /// Draws point to screen at (x, y) coordinates with colour and panics if diff --git a/agb/src/display/bitmap4.rs b/agb/src/display/bitmap4.rs index b40eff9c..b225bce1 100644 --- a/agb/src/display/bitmap4.rs +++ b/agb/src/display/bitmap4.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use crate::memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray}; use super::{ @@ -25,13 +27,17 @@ pub enum Page { } #[non_exhaustive] -pub struct Bitmap4 {} +pub struct Bitmap4<'gba> { + phantom: PhantomData<&'gba ()>, +} -impl Bitmap4 { +impl Bitmap4<'_> { pub(crate) unsafe fn new() -> Self { set_graphics_mode(DisplayMode::Bitmap4); set_graphics_settings(GraphicsSettings::LAYER_BG2); - Bitmap4 {} + Bitmap4 { + phantom: PhantomData, + } } /// Draws point on specified page at (x, y) coordinates with colour index diff --git a/agb/src/display/blend.rs b/agb/src/display/blend.rs index f44c8ded..01443e63 100644 --- a/agb/src/display/blend.rs +++ b/agb/src/display/blend.rs @@ -15,6 +15,8 @@ //! ``` //! where `gba` is a mutable [Gba][crate::Gba] struct. +use core::marker::PhantomData; + use crate::{fixnum::Num, memory_mapped::set_bits}; use super::tiled::BackgroundID; @@ -43,21 +45,22 @@ pub enum BlendMode { /// Manages the blending, won't cause anything to change unless [Blend::commit] /// is called. -pub struct Blend { +pub struct Blend<'gba> { targets: u16, blend_weights: u16, fade_weight: u16, + phantom: PhantomData<&'gba ()>, } /// When making many modifications to a layer, it is convenient to operate on /// that layer directly. This is created by the [Blend::layer] function and /// operates on that layer. -pub struct BlendLayer<'blend> { - blend: &'blend mut Blend, +pub struct BlendLayer<'blend, 'gba> { + blend: &'blend mut Blend<'gba>, layer: Layer, } -impl BlendLayer<'_> { +impl<'gba> BlendLayer<'_, 'gba> { /// Set whether a background is enabled for blending on this layer. pub fn set_background_enable(&mut self, background: BackgroundID, enable: bool) -> &mut Self { self.blend @@ -95,12 +98,13 @@ const BLEND_ALPHAS: *mut u16 = 0x0400_0052 as *mut _; const BLEND_FADES: *mut u16 = 0x0400_0054 as *mut _; -impl Blend { +impl<'gba> Blend<'gba> { pub(crate) fn new() -> Self { let blend = Self { targets: 0, blend_weights: 0, fade_weight: 0, + phantom: PhantomData, }; blend.commit(); @@ -137,7 +141,7 @@ impl Blend { /// Creates a layer object whose functions work only on that layer, /// convenient when performing multiple operations on that layer without the /// need of specifying the layer every time. - pub fn layer(&mut self, layer: Layer) -> BlendLayer { + pub fn layer(&mut self, layer: Layer) -> BlendLayer<'_, 'gba> { BlendLayer { blend: self, layer } } @@ -209,7 +213,7 @@ impl Blend { } } -impl Drop for Blend { +impl Drop for Blend<'_> { fn drop(&mut self) { self.reset().commit(); } diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index fba3aaa5..2b5d1e44 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -79,7 +79,7 @@ pub struct Display { pub struct ObjectDistribution; impl ObjectDistribution { - pub fn get(&mut self) -> ObjectController { + pub fn get(&mut self) -> ObjectController<'_> { ObjectController::new() } } @@ -88,7 +88,7 @@ impl ObjectDistribution { pub struct WindowDist; impl WindowDist { - pub fn get(&mut self) -> Windows { + pub fn get(&mut self) -> Windows<'_> { Windows::new() } } @@ -97,7 +97,7 @@ impl WindowDist { pub struct BlendDist; impl BlendDist { - pub fn get(&mut self) -> Blend { + pub fn get(&mut self) -> Blend<'_> { Blend::new() } } diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 6e617127..df1eb842 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -694,11 +694,12 @@ impl ObjectControllerStatic { /// A controller that distributes objects and sprites. This controls sprites and /// objects being copied to vram when it needs to be. -pub struct ObjectController { +pub struct ObjectController<'gba> { + phantom: PhantomData<&'gba ()>, inner: ObjectControllerReference<'static>, } -impl Drop for ObjectController { +impl<'gba> Drop for ObjectController<'gba> { fn drop(&mut self) { unsafe { ObjectControllerReference::uninit(); @@ -708,7 +709,7 @@ impl Drop for ObjectController { const HIDDEN_VALUE: u16 = 0b10 << 8; -impl ObjectController { +impl<'gba> ObjectController<'gba> { /// Commits the objects to vram and delete sprites where possible. This /// should be called shortly after having waited for the next vblank to /// ensure what is displayed on screen doesn't change part way through. @@ -759,6 +760,7 @@ impl ObjectController { } Self { + phantom: PhantomData, inner: unsafe { ObjectControllerReference::init() }, } } diff --git a/agb/src/display/tiled/tiled0.rs b/agb/src/display/tiled/tiled0.rs index 66ff0054..a0e0dfa4 100644 --- a/agb/src/display/tiled/tiled0.rs +++ b/agb/src/display/tiled/tiled0.rs @@ -1,4 +1,4 @@ -use core::cell::RefCell; +use core::{cell::RefCell, marker::PhantomData}; use super::{ CreatableRegularTiledMode, MapLoan, RegularBackgroundSize, RegularMap, RegularTiledMode, @@ -9,18 +9,20 @@ use crate::{ display::{set_graphics_mode, DisplayMode, Priority}, }; -pub struct Tiled0 { +pub struct Tiled0<'gba> { regular: RefCell>, screenblocks: RefCell>, + phantom: PhantomData<&'gba ()>, } -impl Tiled0 { +impl Tiled0<'_> { pub(crate) unsafe fn new() -> Self { set_graphics_mode(DisplayMode::Tiled0); Self { regular: Default::default(), screenblocks: Default::default(), + phantom: PhantomData, } } @@ -33,13 +35,13 @@ impl Tiled0 { } } -impl TiledMode for Tiled0 { +impl TiledMode for Tiled0<'_> { fn screenblocks(&self) -> &RefCell> { &self.screenblocks } } -impl CreatableRegularTiledMode for Tiled0 { +impl CreatableRegularTiledMode for Tiled0<'_> { const REGULAR_BACKGROUNDS: usize = 4; fn regular(&self) -> &RefCell> { diff --git a/agb/src/display/tiled/tiled1.rs b/agb/src/display/tiled/tiled1.rs index 6126e46a..b65b6df4 100644 --- a/agb/src/display/tiled/tiled1.rs +++ b/agb/src/display/tiled/tiled1.rs @@ -1,4 +1,4 @@ -use core::cell::RefCell; +use core::{cell::RefCell, marker::PhantomData}; use super::{ AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode, @@ -10,13 +10,14 @@ use crate::{ display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority}, }; -pub struct Tiled1 { +pub struct Tiled1<'gba> { regular: RefCell>, affine: RefCell>, screenblocks: RefCell>, + phantom: PhantomData<&'gba ()>, } -impl Tiled1 { +impl Tiled1<'_> { pub(crate) unsafe fn new() -> Self { set_graphics_mode(DisplayMode::Tiled1); @@ -29,6 +30,7 @@ impl Tiled1 { regular: Default::default(), affine, screenblocks: Default::default(), + phantom: PhantomData, } } @@ -45,13 +47,13 @@ impl Tiled1 { } } -impl TiledMode for Tiled1 { +impl TiledMode for Tiled1<'_> { fn screenblocks(&self) -> &RefCell> { &self.screenblocks } } -impl CreatableRegularTiledMode for Tiled1 { +impl CreatableRegularTiledMode for Tiled1<'_> { const REGULAR_BACKGROUNDS: usize = 2; fn regular(&self) -> &RefCell> { @@ -59,7 +61,7 @@ impl CreatableRegularTiledMode for Tiled1 { } } -impl CreatableAffineTiledMode for Tiled1 { +impl CreatableAffineTiledMode for Tiled1<'_> { const AFFINE_BACKGROUNDS: usize = 1; fn affine(&self) -> &RefCell> { diff --git a/agb/src/display/tiled/tiled2.rs b/agb/src/display/tiled/tiled2.rs index bcef9f31..c4874ac6 100644 --- a/agb/src/display/tiled/tiled2.rs +++ b/agb/src/display/tiled/tiled2.rs @@ -1,4 +1,4 @@ -use core::cell::RefCell; +use core::{cell::RefCell, marker::PhantomData}; use super::{ AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode, MapLoan, TiledMode, @@ -8,12 +8,13 @@ use crate::{ display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority}, }; -pub struct Tiled2 { +pub struct Tiled2<'gba> { affine: RefCell>, screenblocks: RefCell>, + phantom: PhantomData<&'gba ()>, } -impl Tiled2 { +impl Tiled2<'_> { pub(crate) unsafe fn new() -> Self { set_graphics_mode(DisplayMode::Tiled2); @@ -25,6 +26,7 @@ impl Tiled2 { Self { affine, screenblocks: Default::default(), + phantom: PhantomData, } } @@ -37,13 +39,13 @@ impl Tiled2 { } } -impl TiledMode for Tiled2 { +impl TiledMode for Tiled2<'_> { fn screenblocks(&self) -> &RefCell> { &self.screenblocks } } -impl CreatableAffineTiledMode for Tiled2 { +impl CreatableAffineTiledMode for Tiled2<'_> { const AFFINE_BACKGROUNDS: usize = 2; fn affine(&self) -> &RefCell> { diff --git a/agb/src/display/video.rs b/agb/src/display/video.rs index d0edf2e1..32c6147f 100644 --- a/agb/src/display/video.rs +++ b/agb/src/display/video.rs @@ -13,27 +13,27 @@ pub struct Video; impl Video { /// Bitmap mode that provides a 16-bit colour framebuffer - pub fn bitmap3(&mut self) -> Bitmap3 { + pub fn bitmap3(&mut self) -> Bitmap3<'_> { unsafe { Bitmap3::new() } } /// Bitmap 4 provides two 8-bit paletted framebuffers with page switching - pub fn bitmap4(&mut self) -> Bitmap4 { + pub fn bitmap4(&mut self) -> Bitmap4<'_> { unsafe { Bitmap4::new() } } /// Tiled 0 mode provides 4 regular, tiled backgrounds - pub fn tiled0(&mut self) -> (Tiled0, VRamManager) { + pub fn tiled0(&mut self) -> (Tiled0<'_>, VRamManager) { (unsafe { Tiled0::new() }, VRamManager::new()) } /// Tiled 1 mode provides 2 regular tiled backgrounds and 1 affine tiled background - pub fn tiled1(&mut self) -> (Tiled1, VRamManager) { + pub fn tiled1(&mut self) -> (Tiled1<'_>, VRamManager) { (unsafe { Tiled1::new() }, VRamManager::new()) } /// Tiled 2 mode provides 2 affine tiled backgrounds - pub fn tiled2(&mut self) -> (Tiled2, VRamManager) { + pub fn tiled2(&mut self) -> (Tiled2<'_>, VRamManager) { (unsafe { Tiled2::new() }, VRamManager::new()) } } diff --git a/agb/src/display/window.rs b/agb/src/display/window.rs index 1f8e41b1..5a454b44 100644 --- a/agb/src/display/window.rs +++ b/agb/src/display/window.rs @@ -1,5 +1,7 @@ #![deny(missing_docs)] //! The window feature of the GBA. +use core::marker::PhantomData; + use crate::{fixnum::Rect, memory_mapped::MemoryMapped}; use super::{tiled::BackgroundID, DISPLAY_CONTROL, HEIGHT, WIDTH}; @@ -7,10 +9,11 @@ use super::{tiled::BackgroundID, DISPLAY_CONTROL, HEIGHT, WIDTH}; /// The windows feature of the Game Boy Advance can selectively display /// backgrounds or objects on the screen and can selectively enable and disable /// effects. This gives out references and holds changes before they can be committed. -pub struct Windows { +pub struct Windows<'gba> { wins: [MovableWindow; 2], out: Window, obj: Window, + phantom: PhantomData<&'gba ()>, } const REG_HORIZONTAL_BASE: *mut u16 = 0x0400_0040 as *mut _; @@ -26,12 +29,13 @@ pub enum WinIn { Win1, } -impl Windows { +impl<'gba> Windows<'gba> { pub(crate) fn new() -> Self { let s = Self { wins: [MovableWindow::new(), MovableWindow::new()], out: Window::new(), obj: Window::new(), + phantom: PhantomData, }; s.commit(); s diff --git a/agb/src/save/mod.rs b/agb/src/save/mod.rs index b240efad..915f89ed 100644 --- a/agb/src/save/mod.rs +++ b/agb/src/save/mod.rs @@ -375,6 +375,7 @@ mod marker { /// Allows access to the cartridge's save data. #[non_exhaustive] pub struct SaveManager {} + impl SaveManager { pub(crate) const fn new() -> Self { SaveManager {} diff --git a/agb/src/sound/mixer/mod.rs b/agb/src/sound/mixer/mod.rs index 3f43d7b5..ca2a36ab 100644 --- a/agb/src/sound/mixer/mod.rs +++ b/agb/src/sound/mixer/mod.rs @@ -114,7 +114,7 @@ impl MixerController { } /// Get a [`Mixer`] in order to start producing sounds. - pub fn mixer(&mut self, frequency: Frequency) -> Mixer { + pub fn mixer(&mut self, frequency: Frequency) -> Mixer<'_> { Mixer::new(frequency) } } diff --git a/agb/src/sound/mixer/sw_mixer.rs b/agb/src/sound/mixer/sw_mixer.rs index 505f3fc5..4f32e786 100644 --- a/agb/src/sound/mixer/sw_mixer.rs +++ b/agb/src/sound/mixer/sw_mixer.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::marker::PhantomData; use core::pin::Pin; use alloc::boxed::Box; @@ -78,7 +79,7 @@ extern "C" { /// } /// # } /// ``` -pub struct Mixer { +pub struct Mixer<'gba> { interrupt_timer: Timer, // SAFETY: Has to go before buffer because it holds a reference to it _interrupt_handler: InterruptHandler<'static>, @@ -91,6 +92,8 @@ pub struct Mixer { working_buffer: Box<[Num], InternalAllocator>, fifo_timer: Timer, + + phantom: PhantomData<&'gba ()>, } /// A pointer to a currently playing channel. @@ -116,7 +119,7 @@ pub struct Mixer { /// ``` pub struct ChannelId(usize, i32); -impl Mixer { +impl Mixer<'_> { pub(super) fn new(frequency: Frequency) -> Self { let buffer = Box::pin_in(MixerBuffer::new(frequency), InternalAllocator); @@ -158,6 +161,8 @@ impl Mixer { working_buffer: working_buffer.into_boxed_slice(), fifo_timer, + + phantom: PhantomData, } } diff --git a/agb/src/timer.rs b/agb/src/timer.rs index 6b7c8906..307f5b64 100644 --- a/agb/src/timer.rs +++ b/agb/src/timer.rs @@ -1,3 +1,5 @@ +use core::marker::PhantomData; + use crate::memory_mapped::MemoryMapped; const fn timer_data(timer: usize) -> MemoryMapped { @@ -39,16 +41,20 @@ pub struct Timer { } #[non_exhaustive] -pub struct Timers { +pub struct Timers<'gba> { pub timer2: Timer, pub timer3: Timer, + + phantom: PhantomData<&'gba ()>, } -impl Timers { +impl Timers<'_> { pub(crate) unsafe fn new() -> Self { Self { timer2: Timer::new(2), timer3: Timer::new(3), + + phantom: PhantomData, } } } @@ -129,7 +135,7 @@ impl TimerController { Self {} } - pub fn timers(&mut self) -> Timers { + pub fn timers(&mut self) -> Timers<'_> { unsafe { Timers::new() } } } diff --git a/examples/hyperspace-roll/src/lib.rs b/examples/hyperspace-roll/src/lib.rs index 61506591..1859aaf5 100644 --- a/examples/hyperspace-roll/src/lib.rs +++ b/examples/hyperspace-roll/src/lib.rs @@ -90,7 +90,7 @@ pub struct PlayerDice { } struct Agb<'a> { - obj: ObjectController, + obj: ObjectController<'a>, vblank: VBlank, star_background: StarBackground<'a>, vram: VRamManager, @@ -101,7 +101,7 @@ pub fn main(mut gba: agb::Gba) -> ! { save::init_save(&mut gba).expect("Could not initialize save game"); if save::load_high_score() > 1000 { - save::save_high_score(&mut gba, 0).expect("Could not reset high score"); + save::save_high_score(&mut gba.save, 0).expect("Could not reset high score"); } let gfx = gba.display.object.get(); @@ -208,7 +208,7 @@ pub fn main(mut gba: agb::Gba) -> ! { agb.obj.commit(); agb.sfx.customise(); if save::load_high_score() < current_level { - save::save_high_score(&mut gba, current_level) + save::save_high_score(&mut gba.save, current_level) .expect("Could not save high score"); } break; diff --git a/examples/hyperspace-roll/src/save.rs b/examples/hyperspace-roll/src/save.rs index 804fa4bb..e2154a87 100644 --- a/examples/hyperspace-roll/src/save.rs +++ b/examples/hyperspace-roll/src/save.rs @@ -1,4 +1,4 @@ -use agb::save::Error; +use agb::save::{Error, SaveManager}; use agb::sync::Static; use agb::Gba; @@ -15,7 +15,7 @@ pub fn init_save(gba: &mut Gba) -> Result<(), Error> { if buffer[0] != 0 { access.prepare_write(0..1)?.write(0, &[0])?; core::mem::drop(access); - save_high_score(gba, 0)?; + save_high_score(&mut gba.save, 0)?; } else { let mut buffer = [0; 4]; access.read(1, &mut buffer)?; @@ -35,9 +35,8 @@ pub fn load_high_score() -> u32 { HIGH_SCORE.read() } -pub fn save_high_score(gba: &mut Gba, score: u32) -> Result<(), Error> { - gba.save - .access()? +pub fn save_high_score(save: &mut SaveManager, score: u32) -> Result<(), Error> { + save.access()? .prepare_write(1..5)? .write(1, &score.to_le_bytes())?; HIGH_SCORE.write(score); diff --git a/examples/hyperspace-roll/src/sfx.rs b/examples/hyperspace-roll/src/sfx.rs index d2d877c4..c01ce5ea 100644 --- a/examples/hyperspace-roll/src/sfx.rs +++ b/examples/hyperspace-roll/src/sfx.rs @@ -45,14 +45,14 @@ enum BattleOrMenu { } pub struct Sfx<'a> { - mixer: &'a mut Mixer, + mixer: &'a mut Mixer<'a>, state: BattleOrMenu, current_bgm: ChannelId, } impl<'a> Sfx<'a> { - pub fn new(mixer: &'a mut Mixer) -> Self { + pub fn new(mixer: &'a mut Mixer<'a>) -> Self { let mut title_music = SoundChannel::new_high_priority(TITLE_BGM); title_music.should_loop(); let title_channel = mixer.play_sound(title_music).unwrap(); diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index d7ddd3b0..ef6fe5b8 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -20,6 +20,7 @@ use agb::{ sound::mixer::Frequency, }; use alloc::boxed::Box; +use sfx::SfxPlayer; mod enemies; mod level_display; @@ -693,7 +694,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { fn update_frame( &mut self, - sfx_player: &mut sfx::SfxPlayer, + sfx_player: &mut SfxPlayer, vram: &mut VRamManager, controller: &'a ObjectController, ) -> UpdateState { @@ -798,13 +799,17 @@ pub fn main(mut agb: agb::Gba) -> ! { } } + let mut mixer = agb.mixer.mixer(Frequency::Hz10512); + + mixer.enable(); + let mut sfx = sfx::SfxPlayer::new(&mut mixer); + world_display.commit(&mut vram); world_display.show(); splash_screen::show_splash_screen( splash_screen::SplashScreen::Start, - None, - None, + &mut sfx, &mut splash_screen, &mut vram, ); @@ -816,10 +821,6 @@ pub fn main(mut agb: agb::Gba) -> ! { vram.set_background_palettes(tile_sheet::PALETTES); let object = agb.display.object.get(); - let mut mixer = agb.mixer.mixer(Frequency::Hz10512); - - mixer.enable(); - let mut music_box = sfx::MusicBox::new(); let vblank = agb::interrupt::VBlank::get(); let mut current_level = 0; @@ -829,8 +830,7 @@ pub fn main(mut agb: agb::Gba) -> ! { break; } - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); level_display::write_level( @@ -844,8 +844,7 @@ pub fn main(mut agb: agb::Gba) -> ! { world_display.commit(&mut vram); world_display.show(); - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); let map_current_level = current_level; @@ -889,20 +888,17 @@ pub fn main(mut agb: agb::Gba) -> ! { ); while level.background.init_background(&mut vram) != PartialUpdateStatus::Done { - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); } while level.background.init_foreground(&mut vram) != PartialUpdateStatus::Done { - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); } for _ in 0..20 { - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); } @@ -913,17 +909,12 @@ pub fn main(mut agb: agb::Gba) -> ! { world_display.hide(); loop { - match level.update_frame( - &mut sfx::SfxPlayer::new(&mut mixer, &music_box), - &mut vram, - &object, - ) { + match level.update_frame(&mut sfx, &mut vram, &object) { UpdateState::Normal => {} UpdateState::Dead => { level.dead_start(); while level.dead_update(&object) { - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); object.commit(); } @@ -935,8 +926,7 @@ pub fn main(mut agb: agb::Gba) -> ! { } } - music_box.before_frame(&mut mixer); - mixer.frame(); + sfx.frame(); vblank.wait_for_vblank(); object.commit(); } @@ -949,8 +939,7 @@ pub fn main(mut agb: agb::Gba) -> ! { splash_screen::show_splash_screen( splash_screen::SplashScreen::End, - Some(&mut mixer), - Some(&mut music_box), + &mut sfx, &mut splash_screen, &mut vram, ); diff --git a/examples/the-hat-chooses-the-wizard/src/sfx.rs b/examples/the-hat-chooses-the-wizard/src/sfx.rs index f471ae57..033b1a82 100644 --- a/examples/the-hat-chooses-the-wizard/src/sfx.rs +++ b/examples/the-hat-chooses-the-wizard/src/sfx.rs @@ -37,40 +37,14 @@ mod effects { pub const SNAIL_DEATH: &[u8] = agb::include_wav!("sfx/snail-death.wav"); } -pub struct MusicBox { - frame: i32, -} - -impl MusicBox { - pub fn new() -> Self { - MusicBox { frame: 0 } - } - - pub fn before_frame(&mut self, mixer: &mut Mixer) { - if self.frame == 0 { - // play the introduction - mixer.play_sound(SoundChannel::new_high_priority(music_data::INTRO_MUSIC)); - } else if self.frame == music_data::TRIGGER_MUSIC_POINT - || (self.frame - music_data::TRIGGER_MUSIC_POINT) % music_data::LOOP_MUSIC == 0 - { - mixer.play_sound(SoundChannel::new_high_priority(music_data::LOOP)); - } - - self.frame += 1; - } -} - pub struct SfxPlayer<'a> { - mixer: &'a mut Mixer, + mixer: &'a mut Mixer<'a>, frame: i32, } impl<'a> SfxPlayer<'a> { - pub fn new(mixer: &'a mut Mixer, music_box: &MusicBox) -> Self { - SfxPlayer { - mixer, - frame: music_box.frame, - } + pub fn new(mixer: &'a mut Mixer<'a>) -> Self { + SfxPlayer { mixer, frame: 0 } } pub fn catch(&mut self) { @@ -120,7 +94,24 @@ impl<'a> SfxPlayer<'a> { fn play_random(&mut self, effect: &[&'static [u8]]) { self.mixer.play_sound(SoundChannel::new( - effect[(self.frame as usize) % effect.len()], + effect[agb::rng::gen() as usize % effect.len()], )); } + + pub fn frame(&mut self) { + if self.frame == 0 { + // play the introduction + self.mixer + .play_sound(SoundChannel::new_high_priority(music_data::INTRO_MUSIC)); + } else if self.frame == music_data::TRIGGER_MUSIC_POINT + || (self.frame - music_data::TRIGGER_MUSIC_POINT) % music_data::LOOP_MUSIC == 0 + { + self.mixer + .play_sound(SoundChannel::new_high_priority(music_data::LOOP)); + } + + self.frame += 1; + + self.mixer.frame(); + } } diff --git a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs index 12991514..80883647 100644 --- a/examples/the-hat-chooses-the-wizard/src/splash_screen.rs +++ b/examples/the-hat-chooses-the-wizard/src/splash_screen.rs @@ -1,8 +1,5 @@ -use super::sfx::MusicBox; -use agb::{ - display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager}, - sound::mixer::Mixer, -}; +use super::sfx::SfxPlayer; +use agb::display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager}; agb::include_gfx!("gfx/splash_screens.toml"); @@ -13,8 +10,7 @@ pub enum SplashScreen { pub fn show_splash_screen( which: SplashScreen, - mut mixer: Option<&mut Mixer>, - mut music_box: Option<&mut MusicBox>, + sfx: &mut SfxPlayer, map: &mut RegularMap, vram: &mut VRamManager, ) { @@ -32,13 +28,7 @@ pub fn show_splash_screen( let mut input = agb::input::ButtonController::new(); - if let Some(ref mut mixer) = mixer { - if let Some(ref mut music_box) = music_box { - music_box.before_frame(mixer); - } - mixer.frame(); - } - + sfx.frame(); vblank.wait_for_vblank(); for y in 0..20u16 { @@ -51,13 +41,7 @@ pub fn show_splash_screen( ); } - if let Some(ref mut mixer) = mixer { - if let Some(ref mut music_box) = music_box { - music_box.before_frame(mixer); - } - mixer.frame(); - } - + sfx.frame(); vblank.wait_for_vblank(); } @@ -75,12 +59,8 @@ pub fn show_splash_screen( ) { break; } - if let Some(mixer) = &mut mixer { - if let Some(music_box) = &mut music_box { - music_box.before_frame(mixer); - } - mixer.frame(); - } + + sfx.frame(); vblank.wait_for_vblank(); } diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 21491cd7..d77ee71e 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -533,7 +533,7 @@ struct Player<'a> { } impl<'a> Player<'a> { - fn new(object_controller: &'a ObjectController) -> Player { + fn new(object_controller: &'a ObjectController<'a>) -> Player { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), diff --git a/examples/the-purple-night/src/sfx.rs b/examples/the-purple-night/src/sfx.rs index 0e8ac5be..445dd148 100644 --- a/examples/the-purple-night/src/sfx.rs +++ b/examples/the-purple-night/src/sfx.rs @@ -27,11 +27,11 @@ const BLUE_SPIRIT: &[u8] = agb::include_wav!("sfx/03 - Blue Spirit (Main Loop).w pub struct Sfx<'a> { bgm: Option, - mixer: &'a mut Mixer, + mixer: &'a mut Mixer<'a>, } impl<'a> Sfx<'a> { - pub fn new(mixer: &'a mut Mixer) -> Self { + pub fn new(mixer: &'a mut Mixer<'a>) -> Self { Self { mixer, bgm: None } }