Add correct lifetimes to prevent double creation (#391)

Fixes #390 

- [x] Changelog updated / no changelog update needed
This commit is contained in:
Gwilym Inzani 2023-02-23 22:17:58 +00:00 committed by GitHub
commit adad443e67
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 145 additions and 147 deletions

View file

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Made Vector2D::new a const function. - Made Vector2D::new a const function.
- The template now uses rust 2021 edition by default. - 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 ### Fixed
- Alpha channel is now considered by `include_gfx!()` even when `transparent_colour` is absent. - Alpha channel is now considered by `include_gfx!()` even when `transparent_colour` is absent.

View file

@ -4,19 +4,23 @@ use super::{
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, HEIGHT, WIDTH, 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<u16, { WIDTH as usize }, { HEIGHT as usize }> = const BITMAP_MODE_3: MemoryMapped2DArray<u16, { WIDTH as usize }, { HEIGHT as usize }> =
unsafe { MemoryMapped2DArray::new(0x600_0000) }; unsafe { MemoryMapped2DArray::new(0x600_0000) };
#[non_exhaustive] #[non_exhaustive]
pub struct Bitmap3 {} pub struct Bitmap3<'gba> {
phantom: PhantomData<&'gba ()>,
}
impl Bitmap3 { impl Bitmap3<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Bitmap3); set_graphics_mode(DisplayMode::Bitmap3);
set_graphics_settings(GraphicsSettings::LAYER_BG2); set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap3 {} Bitmap3 {
phantom: PhantomData,
}
} }
/// Draws point to screen at (x, y) coordinates with colour and panics if /// Draws point to screen at (x, y) coordinates with colour and panics if

View file

@ -1,3 +1,5 @@
use core::marker::PhantomData;
use crate::memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray}; use crate::memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray};
use super::{ use super::{
@ -25,13 +27,17 @@ pub enum Page {
} }
#[non_exhaustive] #[non_exhaustive]
pub struct Bitmap4 {} pub struct Bitmap4<'gba> {
phantom: PhantomData<&'gba ()>,
}
impl Bitmap4 { impl Bitmap4<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Bitmap4); set_graphics_mode(DisplayMode::Bitmap4);
set_graphics_settings(GraphicsSettings::LAYER_BG2); set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap4 {} Bitmap4 {
phantom: PhantomData,
}
} }
/// Draws point on specified page at (x, y) coordinates with colour index /// Draws point on specified page at (x, y) coordinates with colour index

View file

@ -15,6 +15,8 @@
//! ``` //! ```
//! where `gba` is a mutable [Gba][crate::Gba] struct. //! where `gba` is a mutable [Gba][crate::Gba] struct.
use core::marker::PhantomData;
use crate::{fixnum::Num, memory_mapped::set_bits}; use crate::{fixnum::Num, memory_mapped::set_bits};
use super::tiled::BackgroundID; use super::tiled::BackgroundID;
@ -43,21 +45,22 @@ pub enum BlendMode {
/// Manages the blending, won't cause anything to change unless [Blend::commit] /// Manages the blending, won't cause anything to change unless [Blend::commit]
/// is called. /// is called.
pub struct Blend { pub struct Blend<'gba> {
targets: u16, targets: u16,
blend_weights: u16, blend_weights: u16,
fade_weight: u16, fade_weight: u16,
phantom: PhantomData<&'gba ()>,
} }
/// When making many modifications to a layer, it is convenient to operate on /// 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 /// that layer directly. This is created by the [Blend::layer] function and
/// operates on that layer. /// operates on that layer.
pub struct BlendLayer<'blend> { pub struct BlendLayer<'blend, 'gba> {
blend: &'blend mut Blend, blend: &'blend mut Blend<'gba>,
layer: Layer, layer: Layer,
} }
impl BlendLayer<'_> { impl<'gba> BlendLayer<'_, 'gba> {
/// Set whether a background is enabled for blending on this layer. /// Set whether a background is enabled for blending on this layer.
pub fn set_background_enable(&mut self, background: BackgroundID, enable: bool) -> &mut Self { pub fn set_background_enable(&mut self, background: BackgroundID, enable: bool) -> &mut Self {
self.blend self.blend
@ -95,12 +98,13 @@ const BLEND_ALPHAS: *mut u16 = 0x0400_0052 as *mut _;
const BLEND_FADES: *mut u16 = 0x0400_0054 as *mut _; const BLEND_FADES: *mut u16 = 0x0400_0054 as *mut _;
impl Blend { impl<'gba> Blend<'gba> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let blend = Self { let blend = Self {
targets: 0, targets: 0,
blend_weights: 0, blend_weights: 0,
fade_weight: 0, fade_weight: 0,
phantom: PhantomData,
}; };
blend.commit(); blend.commit();
@ -137,7 +141,7 @@ impl Blend {
/// Creates a layer object whose functions work only on that layer, /// Creates a layer object whose functions work only on that layer,
/// convenient when performing multiple operations on that layer without the /// convenient when performing multiple operations on that layer without the
/// need of specifying the layer every time. /// 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 } BlendLayer { blend: self, layer }
} }
@ -209,7 +213,7 @@ impl Blend {
} }
} }
impl Drop for Blend { impl Drop for Blend<'_> {
fn drop(&mut self) { fn drop(&mut self) {
self.reset().commit(); self.reset().commit();
} }

View file

@ -79,7 +79,7 @@ pub struct Display {
pub struct ObjectDistribution; pub struct ObjectDistribution;
impl ObjectDistribution { impl ObjectDistribution {
pub fn get(&mut self) -> ObjectController { pub fn get(&mut self) -> ObjectController<'_> {
ObjectController::new() ObjectController::new()
} }
} }
@ -88,7 +88,7 @@ impl ObjectDistribution {
pub struct WindowDist; pub struct WindowDist;
impl WindowDist { impl WindowDist {
pub fn get(&mut self) -> Windows { pub fn get(&mut self) -> Windows<'_> {
Windows::new() Windows::new()
} }
} }
@ -97,7 +97,7 @@ impl WindowDist {
pub struct BlendDist; pub struct BlendDist;
impl BlendDist { impl BlendDist {
pub fn get(&mut self) -> Blend { pub fn get(&mut self) -> Blend<'_> {
Blend::new() Blend::new()
} }
} }

View file

@ -694,11 +694,12 @@ impl ObjectControllerStatic {
/// A controller that distributes objects and sprites. This controls sprites and /// A controller that distributes objects and sprites. This controls sprites and
/// objects being copied to vram when it needs to be. /// objects being copied to vram when it needs to be.
pub struct ObjectController { pub struct ObjectController<'gba> {
phantom: PhantomData<&'gba ()>,
inner: ObjectControllerReference<'static>, inner: ObjectControllerReference<'static>,
} }
impl Drop for ObjectController { impl<'gba> Drop for ObjectController<'gba> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
ObjectControllerReference::uninit(); ObjectControllerReference::uninit();
@ -708,7 +709,7 @@ impl Drop for ObjectController {
const HIDDEN_VALUE: u16 = 0b10 << 8; const HIDDEN_VALUE: u16 = 0b10 << 8;
impl ObjectController { impl<'gba> ObjectController<'gba> {
/// Commits the objects to vram and delete sprites where possible. This /// Commits the objects to vram and delete sprites where possible. This
/// should be called shortly after having waited for the next vblank to /// should be called shortly after having waited for the next vblank to
/// ensure what is displayed on screen doesn't change part way through. /// ensure what is displayed on screen doesn't change part way through.
@ -759,6 +760,7 @@ impl ObjectController {
} }
Self { Self {
phantom: PhantomData,
inner: unsafe { ObjectControllerReference::init() }, inner: unsafe { ObjectControllerReference::init() },
} }
} }

View file

@ -1,4 +1,4 @@
use core::cell::RefCell; use core::{cell::RefCell, marker::PhantomData};
use super::{ use super::{
CreatableRegularTiledMode, MapLoan, RegularBackgroundSize, RegularMap, RegularTiledMode, CreatableRegularTiledMode, MapLoan, RegularBackgroundSize, RegularMap, RegularTiledMode,
@ -9,18 +9,20 @@ use crate::{
display::{set_graphics_mode, DisplayMode, Priority}, display::{set_graphics_mode, DisplayMode, Priority},
}; };
pub struct Tiled0 { pub struct Tiled0<'gba> {
regular: RefCell<Bitarray<1>>, regular: RefCell<Bitarray<1>>,
screenblocks: RefCell<Bitarray<1>>, screenblocks: RefCell<Bitarray<1>>,
phantom: PhantomData<&'gba ()>,
} }
impl Tiled0 { impl Tiled0<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Tiled0); set_graphics_mode(DisplayMode::Tiled0);
Self { Self {
regular: Default::default(), regular: Default::default(),
screenblocks: 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<Bitarray<1>> { fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
&self.screenblocks &self.screenblocks
} }
} }
impl CreatableRegularTiledMode for Tiled0 { impl CreatableRegularTiledMode for Tiled0<'_> {
const REGULAR_BACKGROUNDS: usize = 4; const REGULAR_BACKGROUNDS: usize = 4;
fn regular(&self) -> &RefCell<Bitarray<1>> { fn regular(&self) -> &RefCell<Bitarray<1>> {

View file

@ -1,4 +1,4 @@
use core::cell::RefCell; use core::{cell::RefCell, marker::PhantomData};
use super::{ use super::{
AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode, AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode,
@ -10,13 +10,14 @@ use crate::{
display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority}, display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority},
}; };
pub struct Tiled1 { pub struct Tiled1<'gba> {
regular: RefCell<Bitarray<1>>, regular: RefCell<Bitarray<1>>,
affine: RefCell<Bitarray<1>>, affine: RefCell<Bitarray<1>>,
screenblocks: RefCell<Bitarray<1>>, screenblocks: RefCell<Bitarray<1>>,
phantom: PhantomData<&'gba ()>,
} }
impl Tiled1 { impl Tiled1<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Tiled1); set_graphics_mode(DisplayMode::Tiled1);
@ -29,6 +30,7 @@ impl Tiled1 {
regular: Default::default(), regular: Default::default(),
affine, affine,
screenblocks: Default::default(), screenblocks: Default::default(),
phantom: PhantomData,
} }
} }
@ -45,13 +47,13 @@ impl Tiled1 {
} }
} }
impl TiledMode for Tiled1 { impl TiledMode for Tiled1<'_> {
fn screenblocks(&self) -> &RefCell<Bitarray<1>> { fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
&self.screenblocks &self.screenblocks
} }
} }
impl CreatableRegularTiledMode for Tiled1 { impl CreatableRegularTiledMode for Tiled1<'_> {
const REGULAR_BACKGROUNDS: usize = 2; const REGULAR_BACKGROUNDS: usize = 2;
fn regular(&self) -> &RefCell<Bitarray<1>> { fn regular(&self) -> &RefCell<Bitarray<1>> {
@ -59,7 +61,7 @@ impl CreatableRegularTiledMode for Tiled1 {
} }
} }
impl CreatableAffineTiledMode for Tiled1 { impl CreatableAffineTiledMode for Tiled1<'_> {
const AFFINE_BACKGROUNDS: usize = 1; const AFFINE_BACKGROUNDS: usize = 1;
fn affine(&self) -> &RefCell<Bitarray<1>> { fn affine(&self) -> &RefCell<Bitarray<1>> {

View file

@ -1,4 +1,4 @@
use core::cell::RefCell; use core::{cell::RefCell, marker::PhantomData};
use super::{ use super::{
AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode, MapLoan, TiledMode, AffineBackgroundSize, AffineMap, AffineTiledMode, CreatableAffineTiledMode, MapLoan, TiledMode,
@ -8,12 +8,13 @@ use crate::{
display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority}, display::{set_graphics_mode, tiled::AFFINE_BG_ID_OFFSET, DisplayMode, Priority},
}; };
pub struct Tiled2 { pub struct Tiled2<'gba> {
affine: RefCell<Bitarray<1>>, affine: RefCell<Bitarray<1>>,
screenblocks: RefCell<Bitarray<1>>, screenblocks: RefCell<Bitarray<1>>,
phantom: PhantomData<&'gba ()>,
} }
impl Tiled2 { impl Tiled2<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Tiled2); set_graphics_mode(DisplayMode::Tiled2);
@ -25,6 +26,7 @@ impl Tiled2 {
Self { Self {
affine, affine,
screenblocks: Default::default(), screenblocks: Default::default(),
phantom: PhantomData,
} }
} }
@ -37,13 +39,13 @@ impl Tiled2 {
} }
} }
impl TiledMode for Tiled2 { impl TiledMode for Tiled2<'_> {
fn screenblocks(&self) -> &RefCell<Bitarray<1>> { fn screenblocks(&self) -> &RefCell<Bitarray<1>> {
&self.screenblocks &self.screenblocks
} }
} }
impl CreatableAffineTiledMode for Tiled2 { impl CreatableAffineTiledMode for Tiled2<'_> {
const AFFINE_BACKGROUNDS: usize = 2; const AFFINE_BACKGROUNDS: usize = 2;
fn affine(&self) -> &RefCell<Bitarray<1>> { fn affine(&self) -> &RefCell<Bitarray<1>> {

View file

@ -13,27 +13,27 @@ pub struct Video;
impl Video { impl Video {
/// Bitmap mode that provides a 16-bit colour framebuffer /// Bitmap mode that provides a 16-bit colour framebuffer
pub fn bitmap3(&mut self) -> Bitmap3 { pub fn bitmap3(&mut self) -> Bitmap3<'_> {
unsafe { Bitmap3::new() } unsafe { Bitmap3::new() }
} }
/// Bitmap 4 provides two 8-bit paletted framebuffers with page switching /// 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() } unsafe { Bitmap4::new() }
} }
/// Tiled 0 mode provides 4 regular, tiled backgrounds /// 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()) (unsafe { Tiled0::new() }, VRamManager::new())
} }
/// Tiled 1 mode provides 2 regular tiled backgrounds and 1 affine tiled background /// 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()) (unsafe { Tiled1::new() }, VRamManager::new())
} }
/// Tiled 2 mode provides 2 affine tiled backgrounds /// 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()) (unsafe { Tiled2::new() }, VRamManager::new())
} }
} }

View file

@ -1,5 +1,7 @@
#![deny(missing_docs)] #![deny(missing_docs)]
//! The window feature of the GBA. //! The window feature of the GBA.
use core::marker::PhantomData;
use crate::{fixnum::Rect, memory_mapped::MemoryMapped}; use crate::{fixnum::Rect, memory_mapped::MemoryMapped};
use super::{tiled::BackgroundID, DISPLAY_CONTROL, HEIGHT, WIDTH}; 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 /// The windows feature of the Game Boy Advance can selectively display
/// backgrounds or objects on the screen and can selectively enable and disable /// 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. /// effects. This gives out references and holds changes before they can be committed.
pub struct Windows { pub struct Windows<'gba> {
wins: [MovableWindow; 2], wins: [MovableWindow; 2],
out: Window, out: Window,
obj: Window, obj: Window,
phantom: PhantomData<&'gba ()>,
} }
const REG_HORIZONTAL_BASE: *mut u16 = 0x0400_0040 as *mut _; const REG_HORIZONTAL_BASE: *mut u16 = 0x0400_0040 as *mut _;
@ -26,12 +29,13 @@ pub enum WinIn {
Win1, Win1,
} }
impl Windows { impl<'gba> Windows<'gba> {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
let s = Self { let s = Self {
wins: [MovableWindow::new(), MovableWindow::new()], wins: [MovableWindow::new(), MovableWindow::new()],
out: Window::new(), out: Window::new(),
obj: Window::new(), obj: Window::new(),
phantom: PhantomData,
}; };
s.commit(); s.commit();
s s

View file

@ -375,6 +375,7 @@ mod marker {
/// Allows access to the cartridge's save data. /// Allows access to the cartridge's save data.
#[non_exhaustive] #[non_exhaustive]
pub struct SaveManager {} pub struct SaveManager {}
impl SaveManager { impl SaveManager {
pub(crate) const fn new() -> Self { pub(crate) const fn new() -> Self {
SaveManager {} SaveManager {}

View file

@ -114,7 +114,7 @@ impl MixerController {
} }
/// Get a [`Mixer`] in order to start producing sounds. /// 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) Mixer::new(frequency)
} }
} }

View file

@ -1,4 +1,5 @@
use core::cell::RefCell; use core::cell::RefCell;
use core::marker::PhantomData;
use core::pin::Pin; use core::pin::Pin;
use alloc::boxed::Box; use alloc::boxed::Box;
@ -78,7 +79,7 @@ extern "C" {
/// } /// }
/// # } /// # }
/// ``` /// ```
pub struct Mixer { pub struct Mixer<'gba> {
interrupt_timer: Timer, interrupt_timer: Timer,
// SAFETY: Has to go before buffer because it holds a reference to it // SAFETY: Has to go before buffer because it holds a reference to it
_interrupt_handler: InterruptHandler<'static>, _interrupt_handler: InterruptHandler<'static>,
@ -91,6 +92,8 @@ pub struct Mixer {
working_buffer: Box<[Num<i16, 4>], InternalAllocator>, working_buffer: Box<[Num<i16, 4>], InternalAllocator>,
fifo_timer: Timer, fifo_timer: Timer,
phantom: PhantomData<&'gba ()>,
} }
/// A pointer to a currently playing channel. /// A pointer to a currently playing channel.
@ -116,7 +119,7 @@ pub struct Mixer {
/// ``` /// ```
pub struct ChannelId(usize, i32); pub struct ChannelId(usize, i32);
impl Mixer { impl Mixer<'_> {
pub(super) fn new(frequency: Frequency) -> Self { pub(super) fn new(frequency: Frequency) -> Self {
let buffer = Box::pin_in(MixerBuffer::new(frequency), InternalAllocator); let buffer = Box::pin_in(MixerBuffer::new(frequency), InternalAllocator);
@ -158,6 +161,8 @@ impl Mixer {
working_buffer: working_buffer.into_boxed_slice(), working_buffer: working_buffer.into_boxed_slice(),
fifo_timer, fifo_timer,
phantom: PhantomData,
} }
} }

View file

@ -1,3 +1,5 @@
use core::marker::PhantomData;
use crate::memory_mapped::MemoryMapped; use crate::memory_mapped::MemoryMapped;
const fn timer_data(timer: usize) -> MemoryMapped<u16> { const fn timer_data(timer: usize) -> MemoryMapped<u16> {
@ -39,16 +41,20 @@ pub struct Timer {
} }
#[non_exhaustive] #[non_exhaustive]
pub struct Timers { pub struct Timers<'gba> {
pub timer2: Timer, pub timer2: Timer,
pub timer3: Timer, pub timer3: Timer,
phantom: PhantomData<&'gba ()>,
} }
impl Timers { impl Timers<'_> {
pub(crate) unsafe fn new() -> Self { pub(crate) unsafe fn new() -> Self {
Self { Self {
timer2: Timer::new(2), timer2: Timer::new(2),
timer3: Timer::new(3), timer3: Timer::new(3),
phantom: PhantomData,
} }
} }
} }
@ -129,7 +135,7 @@ impl TimerController {
Self {} Self {}
} }
pub fn timers(&mut self) -> Timers { pub fn timers(&mut self) -> Timers<'_> {
unsafe { Timers::new() } unsafe { Timers::new() }
} }
} }

View file

@ -90,7 +90,7 @@ pub struct PlayerDice {
} }
struct Agb<'a> { struct Agb<'a> {
obj: ObjectController, obj: ObjectController<'a>,
vblank: VBlank, vblank: VBlank,
star_background: StarBackground<'a>, star_background: StarBackground<'a>,
vram: VRamManager, vram: VRamManager,
@ -101,7 +101,7 @@ pub fn main(mut gba: agb::Gba) -> ! {
save::init_save(&mut gba).expect("Could not initialize save game"); save::init_save(&mut gba).expect("Could not initialize save game");
if save::load_high_score() > 1000 { 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(); let gfx = gba.display.object.get();
@ -208,7 +208,7 @@ pub fn main(mut gba: agb::Gba) -> ! {
agb.obj.commit(); agb.obj.commit();
agb.sfx.customise(); agb.sfx.customise();
if save::load_high_score() < current_level { 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"); .expect("Could not save high score");
} }
break; break;

View file

@ -1,4 +1,4 @@
use agb::save::Error; use agb::save::{Error, SaveManager};
use agb::sync::Static; use agb::sync::Static;
use agb::Gba; use agb::Gba;
@ -15,7 +15,7 @@ pub fn init_save(gba: &mut Gba) -> Result<(), Error> {
if buffer[0] != 0 { if buffer[0] != 0 {
access.prepare_write(0..1)?.write(0, &[0])?; access.prepare_write(0..1)?.write(0, &[0])?;
core::mem::drop(access); core::mem::drop(access);
save_high_score(gba, 0)?; save_high_score(&mut gba.save, 0)?;
} else { } else {
let mut buffer = [0; 4]; let mut buffer = [0; 4];
access.read(1, &mut buffer)?; access.read(1, &mut buffer)?;
@ -35,9 +35,8 @@ pub fn load_high_score() -> u32 {
HIGH_SCORE.read() HIGH_SCORE.read()
} }
pub fn save_high_score(gba: &mut Gba, score: u32) -> Result<(), Error> { pub fn save_high_score(save: &mut SaveManager, score: u32) -> Result<(), Error> {
gba.save save.access()?
.access()?
.prepare_write(1..5)? .prepare_write(1..5)?
.write(1, &score.to_le_bytes())?; .write(1, &score.to_le_bytes())?;
HIGH_SCORE.write(score); HIGH_SCORE.write(score);

View file

@ -45,14 +45,14 @@ enum BattleOrMenu {
} }
pub struct Sfx<'a> { pub struct Sfx<'a> {
mixer: &'a mut Mixer, mixer: &'a mut Mixer<'a>,
state: BattleOrMenu, state: BattleOrMenu,
current_bgm: ChannelId, current_bgm: ChannelId,
} }
impl<'a> Sfx<'a> { 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); let mut title_music = SoundChannel::new_high_priority(TITLE_BGM);
title_music.should_loop(); title_music.should_loop();
let title_channel = mixer.play_sound(title_music).unwrap(); let title_channel = mixer.play_sound(title_music).unwrap();

View file

@ -20,6 +20,7 @@ use agb::{
sound::mixer::Frequency, sound::mixer::Frequency,
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use sfx::SfxPlayer;
mod enemies; mod enemies;
mod level_display; mod level_display;
@ -693,7 +694,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> {
fn update_frame( fn update_frame(
&mut self, &mut self,
sfx_player: &mut sfx::SfxPlayer, sfx_player: &mut SfxPlayer,
vram: &mut VRamManager, vram: &mut VRamManager,
controller: &'a ObjectController, controller: &'a ObjectController,
) -> UpdateState { ) -> 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.commit(&mut vram);
world_display.show(); world_display.show();
splash_screen::show_splash_screen( splash_screen::show_splash_screen(
splash_screen::SplashScreen::Start, splash_screen::SplashScreen::Start,
None, &mut sfx,
None,
&mut splash_screen, &mut splash_screen,
&mut vram, &mut vram,
); );
@ -816,10 +821,6 @@ pub fn main(mut agb: agb::Gba) -> ! {
vram.set_background_palettes(tile_sheet::PALETTES); vram.set_background_palettes(tile_sheet::PALETTES);
let object = agb.display.object.get(); 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 vblank = agb::interrupt::VBlank::get();
let mut current_level = 0; let mut current_level = 0;
@ -829,8 +830,7 @@ pub fn main(mut agb: agb::Gba) -> ! {
break; break;
} }
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
level_display::write_level( level_display::write_level(
@ -844,8 +844,7 @@ pub fn main(mut agb: agb::Gba) -> ! {
world_display.commit(&mut vram); world_display.commit(&mut vram);
world_display.show(); world_display.show();
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
let map_current_level = current_level; 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 { while level.background.init_background(&mut vram) != PartialUpdateStatus::Done {
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }
while level.background.init_foreground(&mut vram) != PartialUpdateStatus::Done { while level.background.init_foreground(&mut vram) != PartialUpdateStatus::Done {
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }
for _ in 0..20 { for _ in 0..20 {
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }
@ -913,17 +909,12 @@ pub fn main(mut agb: agb::Gba) -> ! {
world_display.hide(); world_display.hide();
loop { loop {
match level.update_frame( match level.update_frame(&mut sfx, &mut vram, &object) {
&mut sfx::SfxPlayer::new(&mut mixer, &music_box),
&mut vram,
&object,
) {
UpdateState::Normal => {} UpdateState::Normal => {}
UpdateState::Dead => { UpdateState::Dead => {
level.dead_start(); level.dead_start();
while level.dead_update(&object) { while level.dead_update(&object) {
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
object.commit(); object.commit();
} }
@ -935,8 +926,7 @@ pub fn main(mut agb: agb::Gba) -> ! {
} }
} }
music_box.before_frame(&mut mixer); sfx.frame();
mixer.frame();
vblank.wait_for_vblank(); vblank.wait_for_vblank();
object.commit(); object.commit();
} }
@ -949,8 +939,7 @@ pub fn main(mut agb: agb::Gba) -> ! {
splash_screen::show_splash_screen( splash_screen::show_splash_screen(
splash_screen::SplashScreen::End, splash_screen::SplashScreen::End,
Some(&mut mixer), &mut sfx,
Some(&mut music_box),
&mut splash_screen, &mut splash_screen,
&mut vram, &mut vram,
); );

View file

@ -37,40 +37,14 @@ mod effects {
pub const SNAIL_DEATH: &[u8] = agb::include_wav!("sfx/snail-death.wav"); 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> { pub struct SfxPlayer<'a> {
mixer: &'a mut Mixer, mixer: &'a mut Mixer<'a>,
frame: i32, frame: i32,
} }
impl<'a> SfxPlayer<'a> { impl<'a> SfxPlayer<'a> {
pub fn new(mixer: &'a mut Mixer, music_box: &MusicBox) -> Self { pub fn new(mixer: &'a mut Mixer<'a>) -> Self {
SfxPlayer { SfxPlayer { mixer, frame: 0 }
mixer,
frame: music_box.frame,
}
} }
pub fn catch(&mut self) { pub fn catch(&mut self) {
@ -120,7 +94,24 @@ impl<'a> SfxPlayer<'a> {
fn play_random(&mut self, effect: &[&'static [u8]]) { fn play_random(&mut self, effect: &[&'static [u8]]) {
self.mixer.play_sound(SoundChannel::new( 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();
}
} }

View file

@ -1,8 +1,5 @@
use super::sfx::MusicBox; use super::sfx::SfxPlayer;
use agb::{ use agb::display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager};
display::tiled::{RegularMap, TileFormat, TileSet, TileSetting, TiledMap, VRamManager},
sound::mixer::Mixer,
};
agb::include_gfx!("gfx/splash_screens.toml"); agb::include_gfx!("gfx/splash_screens.toml");
@ -13,8 +10,7 @@ pub enum SplashScreen {
pub fn show_splash_screen( pub fn show_splash_screen(
which: SplashScreen, which: SplashScreen,
mut mixer: Option<&mut Mixer>, sfx: &mut SfxPlayer,
mut music_box: Option<&mut MusicBox>,
map: &mut RegularMap, map: &mut RegularMap,
vram: &mut VRamManager, vram: &mut VRamManager,
) { ) {
@ -32,13 +28,7 @@ pub fn show_splash_screen(
let mut input = agb::input::ButtonController::new(); let mut input = agb::input::ButtonController::new();
if let Some(ref mut mixer) = mixer { sfx.frame();
if let Some(ref mut music_box) = music_box {
music_box.before_frame(mixer);
}
mixer.frame();
}
vblank.wait_for_vblank(); vblank.wait_for_vblank();
for y in 0..20u16 { for y in 0..20u16 {
@ -51,13 +41,7 @@ pub fn show_splash_screen(
); );
} }
if let Some(ref mut mixer) = mixer { sfx.frame();
if let Some(ref mut music_box) = music_box {
music_box.before_frame(mixer);
}
mixer.frame();
}
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }
@ -75,12 +59,8 @@ pub fn show_splash_screen(
) { ) {
break; break;
} }
if let Some(mixer) = &mut mixer {
if let Some(music_box) = &mut music_box { sfx.frame();
music_box.before_frame(mixer);
}
mixer.frame();
}
vblank.wait_for_vblank(); vblank.wait_for_vblank();
} }

View file

@ -533,7 +533,7 @@ struct Player<'a> {
} }
impl<'a> 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( let mut entity = Entity::new(
object_controller, object_controller,
Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()),

View file

@ -27,11 +27,11 @@ const BLUE_SPIRIT: &[u8] = agb::include_wav!("sfx/03 - Blue Spirit (Main Loop).w
pub struct Sfx<'a> { pub struct Sfx<'a> {
bgm: Option<ChannelId>, bgm: Option<ChannelId>,
mixer: &'a mut Mixer, mixer: &'a mut Mixer<'a>,
} }
impl<'a> Sfx<'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 } Self { mixer, bgm: None }
} }