From ab082c59a1d53785c7125cc61e99eddf9ff12204 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Apr 2023 03:10:07 +0100 Subject: [PATCH 01/39] a mad redo of how objects work --- agb/Cargo.toml | 1 + agb/examples/chicken.rs | 26 +- agb/examples/sprites.rs | 20 +- agb/src/display/mod.rs | 18 +- agb/src/display/object.rs | 1343 +---------------- agb/src/display/object/managed.rs | 386 +++++ agb/src/display/object/sprites.rs | 7 + agb/src/display/object/sprites/sprite.rs | 369 +++++ .../object/sprites/sprite_allocator.rs | 288 ++++ agb/src/display/object/unmanaged.rs | 5 + .../display/object/unmanaged/attributes.rs | 204 +++ agb/src/display/object/unmanaged/object.rs | 187 +++ agb/src/display/palette16.rs | 6 + 13 files changed, 1509 insertions(+), 1351 deletions(-) create mode 100644 agb/src/display/object/managed.rs create mode 100644 agb/src/display/object/sprites.rs create mode 100644 agb/src/display/object/sprites/sprite.rs create mode 100644 agb/src/display/object/sprites/sprite_allocator.rs create mode 100644 agb/src/display/object/unmanaged.rs create mode 100644 agb/src/display/object/unmanaged/attributes.rs create mode 100644 agb/src/display/object/unmanaged/object.rs diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 02cdc17b..70797e43 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -30,6 +30,7 @@ agb_fixnum = { version = "0.13.0", path = "../agb-fixnum" } bare-metal = "1" modular-bitfield = "0.11" rustc-hash = { version = "1", default-features = false } +slotmap = { version = "1.0", default-features = false } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 3615ee79..9bc7bfee 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -4,7 +4,7 @@ use agb::{ display::tiled::{TileFormat, TileSet, TileSetting, TiledMap}, display::{ - object::{Object, ObjectController, Size, Sprite}, + object::{Object, Size, Sprite, StaticSpriteLoader}, palette16::Palette16, tiled::RegularBackgroundSize, HEIGHT, WIDTH, @@ -74,11 +74,11 @@ fn main(mut gba: agb::Gba) -> ! { background.show(); background.commit(&mut vram); - let object = gba.display.object.get(); + let (object, mut sprites) = gba.display.object.get_managed(); - let sprite = object.sprite(&CHICKEN_SPRITES[0]); + let sprite = sprites.get_vram_sprite(&CHICKEN_SPRITES[0]); let mut chicken = Character { - object: object.object(sprite), + object: object.add_object(sprite), position: Vector2D { x: (6 * 8) << 8, y: ((7 * 8) - 4) << 8, @@ -137,15 +137,15 @@ fn main(mut gba: agb::Gba) -> ! { } restrict_to_screen(&mut chicken); - update_chicken_object(&mut chicken, &object, state, frame_count); + update_chicken_object(&mut chicken, &mut sprites, state, frame_count); object.commit(); } } -fn update_chicken_object<'a>( - chicken: &'_ mut Character<'a>, - object: &'a ObjectController, +fn update_chicken_object( + chicken: &'_ mut Character<'_>, + sprites: &mut StaticSpriteLoader, state: State, frame_count: u32, ) { @@ -158,19 +158,19 @@ fn update_chicken_object<'a>( State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { chicken.object.set_sprite( - object.sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), + sprites.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), ); } else { chicken .object - .set_sprite(object.sprite(&CHICKEN_SPRITES[0])); + .set_sprite(sprites.get_vram_sprite(&CHICKEN_SPRITES[0])); } } State::Upwards => {} State::Flapping => { - chicken - .object - .set_sprite(object.sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)])); + chicken.object.set_sprite( + sprites.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)]), + ); } } diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index c5ea5ba8..41c4247c 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -3,7 +3,7 @@ extern crate alloc; -use agb::display::object::{Graphics, ObjectController, Sprite, TagMap}; +use agb::display::object::{Graphics, OAMManager, Sprite, StaticSpriteLoader, TagMap}; use alloc::vec::Vec; const GRAPHICS: &Graphics = agb::include_aseprite!( @@ -15,13 +15,13 @@ const GRAPHICS: &Graphics = agb::include_aseprite!( const SPRITES: &[Sprite] = GRAPHICS.sprites(); const TAG_MAP: &TagMap = GRAPHICS.tags(); -fn all_sprites(gfx: &ObjectController) { +fn all_sprites(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); for y in 0..9 { for x in 0..14 { - let mut obj = gfx.object(gfx.sprite(&SPRITES[0])); + let mut obj = gfx.add_object(sprites.get_vram_sprite(&SPRITES[0])); obj.show(); obj.set_position((x * 16 + 8, y * 16 + 8).into()); objs.push(obj); @@ -48,14 +48,14 @@ fn all_sprites(gfx: &ObjectController) { image %= SPRITES.len(); for (i, obj) in objs.iter_mut().enumerate() { let this_image = (image + i) % SPRITES.len(); - obj.set_sprite(gfx.sprite(&SPRITES[this_image])); + obj.set_sprite(sprites.get_vram_sprite(&SPRITES[this_image])); } gfx.commit(); } } } -fn all_tags(gfx: &ObjectController) { +fn all_tags(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); @@ -65,7 +65,7 @@ fn all_tags(gfx: &ObjectController) { let sprite = v.sprite(0); let (size_x, size_y) = sprite.size().to_width_height(); let (size_x, size_y) = (size_x as i32, size_y as i32); - let mut obj = gfx.object(gfx.sprite(sprite)); + let mut obj = gfx.add_object(sprites.get_vram_sprite(sprite)); obj.show(); obj.set_position((x * 32 + 16 - size_x / 2, y * 32 + 16 - size_y / 2).into()); objs.push((obj, v)); @@ -90,7 +90,7 @@ fn all_tags(gfx: &ObjectController) { if count % 5 == 0 { image += 1; for (obj, tag) in objs.iter_mut() { - obj.set_sprite(gfx.sprite(tag.animation_sprite(image))); + obj.set_sprite(sprites.get_vram_sprite(tag.animation_sprite(image))); } gfx.commit(); } @@ -99,12 +99,12 @@ fn all_tags(gfx: &ObjectController) { #[agb::entry] fn main(mut gba: agb::Gba) -> ! { - let gfx = gba.display.object.get(); + let (gfx, mut ssl) = gba.display.object.get_managed(); loop { - all_tags(&gfx); + all_tags(&gfx, &mut ssl); gfx.commit(); - all_sprites(&gfx); + all_sprites(&gfx, &mut ssl); gfx.commit(); } } diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 939c22d3..5c30e9d0 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -4,7 +4,11 @@ use bitflags::bitflags; use modular_bitfield::BitfieldSpecifier; use video::Video; -use self::{blend::Blend, object::ObjectController, window::Windows}; +use self::{ + blend::Blend, + object::{initilise_oam, OAMManager, StaticSpriteLoader, UnmanagedOAM}, + window::Windows, +}; /// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer. pub mod bitmap3; @@ -80,8 +84,14 @@ pub struct Display { pub struct ObjectDistribution; impl ObjectDistribution { - pub fn get(&mut self) -> ObjectController<'_> { - ObjectController::new() + pub fn get_unmanaged(&mut self) -> (UnmanagedOAM<'_>, StaticSpriteLoader) { + unsafe { initilise_oam() }; + (UnmanagedOAM::new(), StaticSpriteLoader::new()) + } + + pub fn get_managed(&mut self) -> (OAMManager<'_>, StaticSpriteLoader) { + unsafe { initilise_oam() }; + (OAMManager::new(), StaticSpriteLoader::new()) } } @@ -143,7 +153,7 @@ pub fn busy_wait_for_vblank() { while VCOUNT.get() < 160 {} } -#[derive(BitfieldSpecifier, Clone, Copy)] +#[derive(BitfieldSpecifier, Clone, Copy, Debug)] pub enum Priority { P0 = 0, P1 = 1, diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index df1eb842..299c7734 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,1331 +1,26 @@ -#![deny(missing_docs)] -use alloc::rc::{Rc, Weak}; -use alloc::vec::Vec; -use core::alloc::Layout; +mod managed; +mod sprites; +mod unmanaged; -use core::cell::UnsafeCell; -use core::marker::PhantomData; -use core::mem::MaybeUninit; -use core::ops::DerefMut; -use core::ptr::NonNull; -use core::slice; -use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9}; -use modular_bitfield::{bitfield, BitfieldSpecifier}; - -const BYTES_PER_TILE_4BPP: usize = 32; - -use super::palette16::Palette16; -use super::{Priority, DISPLAY_CONTROL}; -use crate::agb_alloc::block_allocator::BlockAllocator; -use crate::agb_alloc::bump_allocator::StartEnd; -use crate::dma; -use crate::fixnum::Vector2D; -use crate::hash_map::HashMap; - -use attributes::*; - -/// Include this type if you call `get_object_controller` in impl block. This -/// helps you use the right lifetimes and doesn't impl Sync (using from two -/// "threads" without synchronisation is not safe), but sending to another -/// "thread" is safe. -#[derive(Clone, Copy)] -struct ObjectControllerReference<'a> { - #[cfg(debug_assertions)] - reference: &'a core::cell::RefCell, - - _ref: PhantomData<&'a UnsafeCell<()>>, -} - -#[cfg(debug_assertions)] -static mut OBJECT_CONTROLLER: MaybeUninit> = - MaybeUninit::uninit(); -#[cfg(not(debug_assertions))] -static mut OBJECT_CONTROLLER: MaybeUninit = MaybeUninit::uninit(); - -impl<'a> ObjectControllerReference<'a> { - unsafe fn init() -> Self { - #[cfg(debug_assertions)] - OBJECT_CONTROLLER.write(core::cell::RefCell::new(ObjectControllerStatic::new())); - #[cfg(not(debug_assertions))] - OBJECT_CONTROLLER.write(ObjectControllerStatic::new()); - Self { - #[cfg(debug_assertions)] - reference: unsafe { OBJECT_CONTROLLER.assume_init_ref() }, - _ref: PhantomData, - } - } - - unsafe fn uninit() { - OBJECT_CONTROLLER.assume_init_drop(); - } - - #[track_caller] - #[cfg(debug_assertions)] - fn borrow_cell_ref(self) -> core::cell::RefMut<'a, ObjectControllerStatic> { - self.reference.borrow_mut() - } - #[track_caller] - #[cfg(not(debug_assertions))] - unsafe fn borrow_direct(self) -> &'a mut ObjectControllerStatic { - unsafe { OBJECT_CONTROLLER.assume_init_mut() } - } - - #[track_caller] - unsafe fn borrow_mut(self) -> impl DerefMut + 'a { - #[cfg(debug_assertions)] - { - self.reference.borrow_mut() - } - #[cfg(not(debug_assertions))] - unsafe { - OBJECT_CONTROLLER.assume_init_mut() - } - } -} - -static SPRITE_ALLOCATOR: BlockAllocator = unsafe { - BlockAllocator::new(StartEnd { - start: || TILE_SPRITE, - end: || TILE_SPRITE + 1024 * 8 * 4, - }) +pub use sprites::{ + include_aseprite, DynamicSprite, Graphics, Size, Sprite, SpriteVram, StaticSpriteLoader, Tag, + TagMap, }; -static PALETTE_ALLOCATOR: BlockAllocator = unsafe { - BlockAllocator::new(StartEnd { - start: || PALETTE_SPRITE, - end: || PALETTE_SPRITE + 0x200, - }) -}; +pub use managed::{OAMManager, Object}; +pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; + +use super::DISPLAY_CONTROL; -const PALETTE_SPRITE: usize = 0x0500_0200; -const TILE_SPRITE: usize = 0x06010000; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; -/// Sprite data. Refers to the palette, pixel data, and the size of the sprite. -pub struct Sprite { - palette: &'static Palette16, - data: &'static [u8], - size: Size, -} - -/// Sprite data that can be used to create sprites in vram. -pub struct DynamicSprite<'a> { - data: &'a [u8], - size: Size, -} - -impl DynamicSprite<'_> { - #[must_use] - /// Creates a new dynamic sprite from underlying bytes. Note that despite - /// being an array of u8, this must be aligned to at least a 2 byte - /// boundary. - pub fn new(data: &[u8], size: Size) -> DynamicSprite { - let ptr = &data[0] as *const _ as usize; - if ptr % 2 != 0 { - panic!("data is not aligned to a 2 byte boundary"); - } - if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP { - panic!( - "data is not of expected length, got {} expected {}", - data.len(), - size.number_of_tiles() * BYTES_PER_TILE_4BPP - ); - } - DynamicSprite { data, size } - } - - #[must_use] - /// Tries to copy the sprite to vram to be used to set object sprites. - /// Returns None if there is no room in sprite vram. - pub fn try_vram(&self, palette: PaletteVram) -> Option { - Some(SpriteBorrow { - sprite: unsafe { SpriteVram::new(self.data, self.size, palette)? }, - }) - } - - #[must_use] - /// Tries to copy the sprite to vram to be used to set object sprites. - /// Panics if there is no room in sprite vram. - pub fn to_vram(&self, palette: PaletteVram) -> SpriteBorrow { - self.try_vram(palette) - .expect("No slot for sprite available") - } -} - -/// The sizes of sprite supported by the GBA. -#[derive(Clone, Copy, PartialEq, Eq)] -#[allow(missing_docs)] -pub enum Size { - // stored as attr0 attr1 - S8x8 = 0b00_00, - S16x16 = 0b00_01, - S32x32 = 0b00_10, - S64x64 = 0b00_11, - - S16x8 = 0b01_00, - S32x8 = 0b01_01, - S32x16 = 0b01_10, - S64x32 = 0b01_11, - - S8x16 = 0b10_00, - S8x32 = 0b10_01, - S16x32 = 0b10_10, - S32x64 = 0b10_11, -} - -#[doc(hidden)] -#[repr(C)] // guarantee 'bytes' comes after '_align' -pub struct AlignedAs { - pub _align: [Align; 0], - pub bytes: Bytes, -} - -#[doc(hidden)] -#[macro_export] -macro_rules! align_bytes { - ($align_ty:ty, $data:literal) => {{ - use $crate::display::object::AlignedAs; - - const ALIGNED: &AlignedAs<$align_ty, [u8]> = &AlignedAs { - _align: [], - bytes: *$data, - }; - - &ALIGNED.bytes - }}; -} - -/// Includes sprites found in the referenced aseprite files. Can include -/// multiple at once and optimises palettes of all included in the single call -/// together. See [Size] for supported sizes. Returns a reference to [Graphics]. -/// -/// ```rust,no_run -/// # #![no_std] -/// # #![no_main] -/// # use agb::{display::object::Graphics, include_aseprite}; -/// const GRAPHICS: &Graphics = include_aseprite!( -/// "examples/gfx/boss.aseprite", -/// "examples/gfx/objects.aseprite" -/// ); -/// ``` -/// The tags from the aseprite file are included so you can refer to sprites by -/// name in code. You should ensure tags are unique as this is not enforced by -/// aseprite. -/// -#[macro_export] -macro_rules! include_aseprite { - ($($aseprite_path: expr),*) => {{ - use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics}; - use $crate::display::palette16::Palette16; - use $crate::align_bytes; - - $crate::include_aseprite_inner!($($aseprite_path),*); - - &Graphics::new(SPRITES, TAGS) - }}; -} - -/// Stores sprite and tag data returned by [include_aseprite]. -pub struct Graphics { - sprites: &'static [Sprite], - tag_map: &'static TagMap, -} - -impl Graphics { - #[doc(hidden)] - /// Creates graphics data from sprite data and a tag_map. This is used - /// internally by [include_aseprite] and would be otherwise difficult to - /// use. - #[must_use] - pub const fn new(sprites: &'static [Sprite], tag_map: &'static TagMap) -> Self { - Self { sprites, tag_map } - } - #[must_use] - /// Gets the tag map from the aseprite files. This allows reference to - /// sprite sequences by name. - pub const fn tags(&self) -> &TagMap { - self.tag_map - } - /// Gets a big list of the sprites themselves. Using tags is often easier. - #[must_use] - pub const fn sprites(&self) -> &[Sprite] { - self.sprites - } -} - -/// Stores aseprite tags. Can be used to refer to animation sequences by name. -/// ```rust,no_run -/// # #![no_std] -/// # #![no_main] -/// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; -/// const GRAPHICS: &Graphics = include_aseprite!( -/// "examples/gfx/boss.aseprite", -/// "examples/gfx/objects.aseprite" -/// ); -/// -/// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); -/// ``` -/// This being the whole animation associated with the walk sequence of the emu. -/// See [Tag] for details on how to use this. -pub struct TagMap { - tags: &'static [(&'static str, Tag)], -} - -const fn const_byte_compare(a: &[u8], b: &[u8]) -> bool { - if a.len() != b.len() { - return false; - } - - let mut i = 0; - while i < a.len() { - if a[i] != b[i] { - return false; - } - i += 1; - } - true -} - -impl TagMap { - #[doc(hidden)] - /// Creates a new tag map from (name, Tag) pairs. Used internally by - /// [include_aseprite] and should not really be used outside of it. - #[must_use] - pub const fn new(tags: &'static [(&'static str, Tag)]) -> TagMap { - Self { tags } - } - - #[doc(hidden)] - /// Attempts to get a tag. Generally should not be used. - #[must_use] - pub const fn try_get(&'static self, tag: &str) -> Option<&'static Tag> { - let mut i = 0; - while i < self.tags.len() { - let s = self.tags[i].0; - if const_byte_compare(s.as_bytes(), tag.as_bytes()) { - return Some(&self.tags[i].1); - } - - i += 1; - } - - None - } - - /// Gets a tag associated with the name. A tag in aseprite refers to a - /// sequence of sprites with some metadata for how to animate it. You should - /// call this in a constant context so it is evaluated at compile time. It - /// is inefficient to call this elsewhere. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// ``` - /// - /// See [Tag] for more details. - #[must_use] - pub const fn get(&'static self, tag: &str) -> &'static Tag { - let t = self.try_get(tag); - match t { - Some(t) => t, - None => panic!("The requested tag does not exist"), - } - } - - /// Takes an iterator over all the tags in the map. Not generally useful. - pub fn values(&self) -> impl Iterator { - self.tags.iter().map(|x| &x.1) - } -} - -#[derive(Clone, Copy)] -enum Direction { - Forward, - Backward, - PingPong, -} - -impl Direction { - const fn from_usize(a: usize) -> Self { - match a { - 0 => Direction::Forward, - 1 => Direction::Backward, - 2 => Direction::PingPong, - _ => panic!("Invalid direction, this is a bug in image converter or agb"), - } - } -} - -/// A sequence of sprites from aseprite. -pub struct Tag { - sprites: *const Sprite, - len: usize, - direction: Direction, -} - -impl Tag { - /// The individual sprites that make up the animation themselves. - #[must_use] - pub fn sprites(&self) -> &'static [Sprite] { - unsafe { slice::from_raw_parts(self.sprites, self.len) } - } - - /// A single sprite referred to by index in the animation sequence. - #[must_use] - pub const fn sprite(&self, idx: usize) -> &'static Sprite { - if idx >= self.len { - panic!("out of bounds access to sprite"); - } - unsafe { &*self.sprites.add(idx) } - } - - /// A sprite that follows the animation sequence. For instance, in aseprite - /// tags can be specified to animate: - /// * Forward - /// * Backward - /// * Ping pong - /// - /// This takes the animation type in account and returns the correct sprite - /// following these requirements. - #[inline] - #[must_use] - pub fn animation_sprite(&self, idx: usize) -> &'static Sprite { - let len_sub_1 = self.len - 1; - match self.direction { - Direction::Forward => self.sprite(idx % self.len), - Direction::Backward => self.sprite(len_sub_1 - (idx % self.len)), - Direction::PingPong => self.sprite( - (((idx + len_sub_1) % (len_sub_1 * 2)) as isize - len_sub_1 as isize) - .unsigned_abs(), - ), - } - } - - #[doc(hidden)] - /// Creates a new sprite from it's constituent parts. Used internally by - /// [include_aseprite] and should generally not be used elsewhere. - #[must_use] - pub const fn new(sprites: &'static [Sprite], from: usize, to: usize, direction: usize) -> Self { - assert!(from <= to); - assert!(to < sprites.len()); - Self { - sprites: &sprites[from] as *const Sprite, - len: to - from + 1, - direction: Direction::from_usize(direction), - } - } -} - -impl Size { - const fn number_of_tiles(self) -> usize { - match self { - Size::S8x8 => 1, - Size::S16x16 => 4, - Size::S32x32 => 16, - Size::S64x64 => 64, - Size::S16x8 => 2, - Size::S32x8 => 4, - Size::S32x16 => 8, - Size::S64x32 => 32, - Size::S8x16 => 2, - Size::S8x32 => 4, - Size::S16x32 => 8, - Size::S32x64 => 32, - } - } - const fn shape_size(self) -> (u8, u8) { - (self as u8 >> 2, self as u8 & 0b11) - } - - fn layout(self) -> Layout { - Layout::from_size_align(self.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() - } - - #[must_use] - /// Creates a size from width and height in pixels, panics if the width and - /// height is not representable by GBA sprites. - pub const fn from_width_height(width: usize, height: usize) -> Self { - match (width, height) { - (8, 8) => Size::S8x8, - (16, 16) => Size::S16x16, - (32, 32) => Size::S32x32, - (64, 64) => Size::S64x64, - (16, 8) => Size::S16x8, - (32, 8) => Size::S32x8, - (32, 16) => Size::S32x16, - (64, 32) => Size::S64x32, - (8, 16) => Size::S8x16, - (8, 32) => Size::S8x32, - (16, 32) => Size::S16x32, - (32, 64) => Size::S32x64, - (_, _) => panic!("Bad width and height!"), - } - } - - #[must_use] - /// Returns the width and height of the size in pixels. - pub const fn to_width_height(self) -> (usize, usize) { - match self { - Size::S8x8 => (8, 8), - Size::S16x16 => (16, 16), - Size::S32x32 => (32, 32), - Size::S64x64 => (64, 64), - Size::S16x8 => (16, 8), - Size::S32x8 => (32, 8), - Size::S32x16 => (32, 16), - Size::S64x32 => (64, 32), - Size::S8x16 => (8, 16), - Size::S8x32 => (8, 32), - Size::S16x32 => (16, 32), - Size::S32x64 => (32, 64), - } - } -} - -/// A reference to a sprite that is currently copied in vram. This is reference -/// counted and can be cloned to keep it in vram or otherwise manipulated. If -/// objects no longer refer to this sprite, then it's vram slot is freed for the -/// next sprite. This is obtained from the [ObjectController]. -#[derive(Clone)] -pub struct SpriteBorrow { - sprite: SpriteVram, -} - -#[derive(PartialEq, Eq)] -struct Attributes { - a0: ObjectAttribute0, - a1s: ObjectAttribute1Standard, - a1a: ObjectAttribute1Affine, - a2: ObjectAttribute2, -} - -impl Attributes { - fn new() -> Self { - Self { - a0: ObjectAttribute0::new(), - a1s: ObjectAttribute1Standard::new(), - a1a: ObjectAttribute1Affine::new(), - a2: ObjectAttribute2::new(), - } - } - - fn commit(&self, location: usize) { - let mode = self.a0.object_mode(); - let attrs: [[u8; 2]; 3] = match mode { - ObjectMode::Normal => [ - self.a0.into_bytes(), - self.a1s.into_bytes(), - self.a2.into_bytes(), - ], - _ => [ - self.a0.into_bytes(), - self.a1a.into_bytes(), - self.a2.into_bytes(), - ], - }; - - unsafe { - let attrs: [u16; 3] = core::mem::transmute(attrs); - let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(location * 4); - - ptr.add(0).write_volatile(attrs[0]); - ptr.add(1).write_volatile(attrs[1]); - ptr.add(2).write_volatile(attrs[2]); - }; - } -} - -/// An object that may be displayed on screen. The object can be modified using -/// the available methods. This is obtained from the [ObjectController]. -pub struct Object<'a> { - loan: Loan<'a>, -} - -#[derive(Clone, Copy)] -struct Location(usize); - -impl Location { - fn from_sprite_ptr(d: NonNull) -> Self { - Self(((d.as_ptr() as usize) - TILE_SPRITE) / BYTES_PER_TILE_4BPP) - } - fn from_palette_ptr(d: NonNull) -> Self { - Self((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) - } - fn as_palette_ptr(self) -> *mut u8 { - (self.0 * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 - } - fn as_sprite_ptr(self) -> *mut u8 { - (self.0 * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 - } -} - -#[derive(Clone)] -/// The palette data in Vram, this is reference counted and the palette data is -/// removed and can be reused from vram when no strong references remain. -pub struct PaletteVram(Rc); - -#[derive(Clone)] -struct SpriteVram(Rc); - -struct PaletteData { - location: Location, -} - -impl PaletteVram { - /// Creates a palette in vram from the given palette. Can be used to create - /// sprites in vram in the [DynamicSprite] functions. - #[must_use] - pub fn new(palette: &Palette16) -> Option { - let dest = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout())? }; - - unsafe { - dma::dma_copy16( - palette.colours.as_ptr().cast(), - dest.as_ptr().cast(), - palette.colours.len(), - ); - } - - Some(PaletteVram(Rc::new(PaletteData { - location: Location::from_palette_ptr(dest), - }))) - } -} - -impl SpriteVram { - /// # Safety - /// data should be aligned to a 2 byte boundary - unsafe fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option { - let dest = unsafe { SPRITE_ALLOCATOR.alloc(size.layout())? }; - - unsafe { - dma::dma_copy16(data.as_ptr().cast(), dest.as_ptr().cast(), data.len() / 2); - } - - Some(SpriteVram(Rc::new(SpriteArena { - location: Location::from_sprite_ptr(dest), - size, - palette, - }))) - } -} - -impl Drop for PaletteData { - fn drop(&mut self) { - unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) }; - } -} - -struct SpriteArena { - location: Location, - size: Size, - palette: PaletteVram, -} - -impl Drop for SpriteArena { - fn drop(&mut self) { - unsafe { SPRITE_ALLOCATOR.dealloc(self.location.as_sprite_ptr(), self.size.layout()) } - } -} - -#[derive(Default)] -struct SpriteControllerInner { - static_palette_map: HashMap>, - static_sprite_map: HashMap>, -} - -struct Loan<'a> { - index: u8, - controller: ObjectControllerReference<'a>, -} - -impl Drop for Loan<'_> { - fn drop(&mut self) { - let mut s = unsafe { self.controller.borrow_mut() }; - - unsafe { - s.shadow_oam[self.index as usize] - .as_mut() - .unwrap_unchecked() - .destroy = true; - }; - } -} - -struct ObjectInner { - attrs: Attributes, - sprite: SpriteBorrow, - previous_sprite: SpriteBorrow, - destroy: bool, - z: i32, -} - -struct ObjectControllerStatic { - _free_affine_matrices: Vec, - free_object: Vec, - shadow_oam: Vec>, - z_order: Vec, - sprite_controller: SpriteControllerInner, -} - -impl ObjectControllerStatic { - unsafe fn new() -> Self { - Self { - shadow_oam: (0..128).map(|_| None).collect(), - z_order: (0..128).collect(), - free_object: (0..128).collect(), - _free_affine_matrices: (0..32).collect(), - sprite_controller: SpriteControllerInner::new(), - } - } - - fn update_z_ordering(&mut self) { - let shadow_oam = &self.shadow_oam; - self.z_order - .sort_by_key(|&a| shadow_oam[a as usize].as_ref().map_or(i32::MAX, |s| s.z)); - } -} - -/// A controller that distributes objects and sprites. This controls sprites and -/// objects being copied to vram when it needs to be. -pub struct ObjectController<'gba> { - phantom: PhantomData<&'gba ()>, - inner: ObjectControllerReference<'static>, -} - -impl<'gba> Drop for ObjectController<'gba> { - fn drop(&mut self) { - unsafe { - ObjectControllerReference::uninit(); - } - } -} - -const HIDDEN_VALUE: u16 = 0b10 << 8; - -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. - pub fn commit(&self) { - let mut s = unsafe { self.inner.borrow_mut() }; - - let s = &mut *s; - - for (i, &z) in s.z_order.iter().enumerate() { - if let Some(o) = &mut s.shadow_oam[z as usize] { - if o.destroy { - s.free_object.push(z); - - unsafe { - (OBJECT_ATTRIBUTE_MEMORY as *mut u16) - .add(i * 4) - .write_volatile(HIDDEN_VALUE); - } - - let _ = unsafe { s.shadow_oam[z as usize].take().unwrap_unchecked() }; - } else { - o.attrs.commit(i); - o.previous_sprite = o.sprite.clone(); - } - } else { - unsafe { - (OBJECT_ATTRIBUTE_MEMORY as *mut u16) - .add(i * 4) - .write_volatile(HIDDEN_VALUE); - } - } - } - - s.sprite_controller.gc(); - } - - pub(crate) fn new() -> Self { - DISPLAY_CONTROL.set_bits(1, 1, 0x6); - DISPLAY_CONTROL.set_bits(1, 1, 0xC); - DISPLAY_CONTROL.set_bits(0, 1, 0x7); - - for i in 0..128 { - unsafe { - (OBJECT_ATTRIBUTE_MEMORY as *mut u16) - .add(i * 4) - .write_volatile(HIDDEN_VALUE); - } - } - - Self { - phantom: PhantomData, - inner: unsafe { ObjectControllerReference::init() }, - } - } - - #[must_use] - /// Creates an object with it's initial sprite being the sprite reference. - /// Panics if there is no space for the sprite or if there are no free - /// objects. This will reuse an existing copy of the sprite in vram if - /// possible. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let emu = object_controller.object_sprite(EMU_WALK.animation_sprite(0)); - /// # } - /// ``` - pub fn object_sprite<'a>(&'a self, sprite: &'static Sprite) -> Object<'a> { - let sprite = self.sprite(sprite); - self.object(sprite) - } - - #[must_use] - /// Creates an object with it's initial sprite being the sprite reference. - /// Returns [None] if the sprite or object could not be allocated. This will - /// reuse an existing copy of the sprite in vram if possible. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let emu = object_controller.try_get_object_sprite( - /// EMU_WALK.animation_sprite(0) - /// ).expect("the sprite or object could be allocated"); - /// # } - /// ``` - pub fn try_get_object_sprite<'a>(&'a self, sprite: &'static Sprite) -> Option> { - let sprite = self.try_get_sprite(sprite)?; - self.try_get_object(sprite) - } - - /// Creates an object with it's initial sprite being what is in the - /// [SpriteBorrow]. Panics if there are no objects left. A [SpriteBorrow] is - /// created using the [ObjectController::sprite] function. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let emu = object_controller.object(object_controller.sprite(EMU_WALK.animation_sprite(0))); - /// # } - /// ``` - #[must_use] - pub fn object(&self, sprite: SpriteBorrow) -> Object { - self.try_get_object(sprite).expect("No object available") - } - - /// Creates an object with it's initial sprite being what is in the - /// [SpriteBorrow]. A [SpriteBorrow] is created using the - /// [ObjectController::try_get_sprite] function. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let emu = object_controller.try_get_object( - /// object_controller.sprite(EMU_WALK.animation_sprite(0)) - /// ).expect("the object should be allocatable"); - /// # } - /// ``` - #[must_use] - pub fn try_get_object(&self, sprite: SpriteBorrow) -> Option { - let mut s = unsafe { self.inner.borrow_mut() }; - - let mut attrs = Attributes::new(); - - attrs.a2.set_tile_index(sprite.sprite.0.location.0 as u16); - let shape_size = sprite.sprite.0.size.shape_size(); - attrs - .a2 - .set_palette_bank((sprite.sprite.0.palette.0.location.0) as u8); - attrs.a0.set_shape(shape_size.0); - attrs.a1a.set_size(shape_size.1); - attrs.a1s.set_size(shape_size.1); - - let index = s.free_object.pop()?; - - s.shadow_oam[index as usize] = Some(ObjectInner { - attrs, - z: 0, - previous_sprite: sprite.clone(), - destroy: false, - sprite, - }); - - let loan = Loan { - index, - controller: self.inner, - }; - - s.update_z_ordering(); - - Some(Object { loan }) - } - - /// Creates a [SpriteBorrow] from the given sprite, panics if the sprite - /// could not be allocated. This will reuse an existing copy of the sprite - /// in vram if possible. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let sprite = object_controller.sprite(EMU_WALK.animation_sprite(0)); - /// # } - /// ``` - #[must_use] - pub fn sprite(&self, sprite: &'static Sprite) -> SpriteBorrow { - self.try_get_sprite(sprite) - .expect("No slot for sprite available") - } - - /// Creates a [SpriteBorrow] from the given sprite. This will reuse an - /// existing copy of the sprite in vram if possible. - /// ```rust,no_run - /// # #![no_std] - /// # #![no_main] - /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; - /// const GRAPHICS: &Graphics = include_aseprite!( - /// "examples/gfx/boss.aseprite", - /// "examples/gfx/objects.aseprite" - /// ); - /// - /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); - /// - /// # fn foo(gba: &mut agb::Gba) { - /// # let object_controller = gba.display.object.get(); - /// let sprite = object_controller.try_get_sprite( - /// EMU_WALK.animation_sprite(0) - /// ).expect("the sprite should be allocatable"); - /// # } - /// ``` - #[must_use] - pub fn try_get_sprite(&self, sprite: &'static Sprite) -> Option { - unsafe { self.inner.borrow_mut() } - .sprite_controller - .try_get_sprite(sprite) - } -} - -impl<'a> Object<'a> { - #[inline(always)] - unsafe fn object_inner(&self) -> impl DerefMut + 'a { - #[cfg(debug_assertions)] - { - core::cell::RefMut::map(self.loan.controller.borrow_cell_ref(), |s| { - s.shadow_oam[self.loan.index as usize] - .as_mut() - .unwrap_unchecked() - }) - } - #[cfg(not(debug_assertions))] - { - self.loan.controller.borrow_direct().shadow_oam[self.loan.index as usize] - .as_mut() - .unwrap_unchecked() - } - } - - /// Swaps out the current sprite. This handles changing of size, palette, - /// etc. No change will be seen until [ObjectController::commit] is called. - pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow) { - let mut object_inner = unsafe { self.object_inner() }; - object_inner - .attrs - .a2 - .set_tile_index(sprite.sprite.0.location.0 as u16); - let shape_size = sprite.sprite.0.size.shape_size(); - object_inner - .attrs - .a2 - .set_palette_bank(sprite.sprite.0.palette.0.location.0 as u8); - object_inner.attrs.a0.set_shape(shape_size.0); - object_inner.attrs.a1a.set_size(shape_size.1); - object_inner.attrs.a1s.set_size(shape_size.1); - - object_inner.sprite = sprite; - } - - /// Shows the sprite. No change will be seen until - /// [ObjectController::commit] is called. - pub fn show(&mut self) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_object_mode(ObjectMode::Normal); - } - - self - } - - /// Controls whether the sprite is flipped horizontally, for example useful - /// for reusing the same sprite for the left and right walking directions. - /// No change will be seen until [ObjectController::commit] is called. - pub fn set_hflip(&mut self, flip: bool) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1s.set_horizontal_flip(flip); - } - self - } - - /// Controls whether the sprite is flipped vertically, for example useful - /// for reusing the same sprite for the up and down walking directions. No - /// change will be seen until [ObjectController::commit] is called. - pub fn set_vflip(&mut self, flip: bool) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1s.set_vertical_flip(flip); - } - self - } - - /// Sets the x position of the object. The coordinate refers to the top-left - /// corner of the sprite. No change will be seen until - /// [ObjectController::commit] is called. - pub fn set_x(&mut self, x: u16) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a1a.set_x(x.rem_euclid(1 << 9)); - object_inner.attrs.a1s.set_x(x.rem_euclid(1 << 9)); - } - self - } - - /// Sets the z priority of the sprite. Higher priority will be displayed - /// above background layers with lower priorities. No change will be seen - /// until [ObjectController::commit] is called. - pub fn set_priority(&mut self, priority: Priority) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a2.set_priority(priority); - } - self - } - - /// Hides the object. No change will be seen until - /// [ObjectController::commit] is called. - pub fn hide(&mut self) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_object_mode(ObjectMode::Disabled); - } - self - } - - /// Sets the y position of the sprite. The coordinate refers to the top-left - /// corner of the sprite. No change will be seen until - /// [ObjectController::commit] is called. - pub fn set_y(&mut self, y: u16) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_y(y as u8); - } - - self - } - - /// Sets the z position of the sprite, this controls which sprites are above - /// each other. No change will be seen until [ObjectController::commit] is - /// called. - pub fn set_z(&mut self, z: i32) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.z = z; - } - unsafe { self.loan.controller.borrow_mut().update_z_ordering() }; - - self - } - - /// Sets the position of the sprite using a [Vector2D]. The coordinate - /// refers to the top-left corner of the sprite. No change will be seen - /// until [ObjectController::commit] is called. - pub fn set_position(&mut self, position: Vector2D) -> &mut Self { - { - let mut object_inner = unsafe { self.object_inner() }; - object_inner.attrs.a0.set_y(position.y as u8); - object_inner - .attrs - .a1a - .set_x(position.x.rem_euclid(1 << 9) as u16); - object_inner - .attrs - .a1s - .set_x(position.x.rem_euclid(1 << 9) as u16); - } - self - } -} - -/// The Sprite Id is a thin wrapper around the pointer to the sprite in -/// rom and is therefore a unique identifier to a sprite -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -struct SpriteId(usize); - -/// The palette id is a thin wrapper around the pointer to the palette in rom -/// and is therefore a unique reference to a palette -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct PaletteId(usize); - -impl Palette16 { - fn id(&'static self) -> PaletteId { - PaletteId(self as *const _ as usize) - } - const fn layout() -> Layout { - Layout::new::() - } -} - -impl Sprite { - fn id(&'static self) -> SpriteId { - SpriteId(self as *const _ as usize) - } - fn layout(&self) -> Layout { - Layout::from_size_align(self.size.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() - } - #[doc(hidden)] - /// Creates a sprite from it's constituent data, used internally by - /// [include_aseprite] and should generally not be used outside it. - /// - /// # Safety - /// The data should be aligned to a 2 byte boundary - #[must_use] - pub const unsafe fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self { - Self { - palette, - data, - size, - } - } - #[must_use] - /// The size of the sprite in it's form that is displayable on the GBA. - pub const fn size(&self) -> Size { - self.size - } -} - -impl SpriteControllerInner { - fn try_get_sprite(&mut self, sprite: &'static Sprite) -> Option { - let id = sprite.id(); - if let Some(storage) = self.static_sprite_map.get_mut(&id) { - if let Some(strong) = storage.upgrade() { - return Some(SpriteBorrow { - sprite: SpriteVram(strong), - }); - } - } - - // layout is non zero sized, so this is safe to call - - let palette_location = self.palette(sprite.palette); - let palette_location = match palette_location { - Some(a) => a, - None => { - return None; - } - }; - - let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? }; - - unsafe { - dma::dma_copy16( - sprite.data.as_ptr().cast(), - dest.as_ptr().cast(), - sprite.data.len() / 2, - ); - } - - let sprite = SpriteVram(Rc::new(SpriteArena { - location: Location::from_sprite_ptr(dest), - size: sprite.size(), - palette: palette_location, - })); - - self.static_sprite_map.insert(id, Rc::downgrade(&sprite.0)); - - Some(SpriteBorrow { sprite }) - } - - /// Cleans up weak references to sprites and palettes no longer in vram - fn gc(&mut self) { - self.static_palette_map.retain(|_, v| v.strong_count() != 0); - self.static_sprite_map.retain(|_, v| v.strong_count() != 0); - } - - fn new() -> Self { - Default::default() - } - fn palette(&mut self, palette: &'static Palette16) -> Option { - let id = palette.id(); - if let Some(storage) = self.static_palette_map.get(&id) { - if let Some(up) = storage.upgrade() { - return Some(PaletteVram(up)); - } - } - - let palette_vram = PaletteVram::new(palette)?; - - self.static_palette_map - .insert(id, Rc::downgrade(&palette_vram.0)); - - Some(palette_vram) - } -} - -#[derive(BitfieldSpecifier, Clone, Copy)] -enum ObjectMode { - Normal, - Affine, - Disabled, - AffineDouble, -} - -#[derive(BitfieldSpecifier, Clone, Copy)] -#[bits = 2] -enum GraphicsMode { - Normal, - AlphaBlending, - Window, -} - -#[derive(BitfieldSpecifier, Clone, Copy)] -enum ColourMode { - Four, - Eight, -} - -// this mod is not public, so the internal parts don't need documenting. -#[allow(dead_code)] -mod attributes { - use super::*; - #[bitfield] - #[derive(Clone, Copy, PartialEq, Eq)] - pub(super) struct ObjectAttribute0 { - pub y: B8, - pub object_mode: ObjectMode, - pub graphics_mode: GraphicsMode, - pub mosaic: bool, - pub colour_mode: ColourMode, - pub shape: B2, - } - - #[bitfield] - #[derive(Clone, Copy, PartialEq, Eq)] - pub(super) struct ObjectAttribute1Standard { - pub x: B9, - #[skip] - __: B3, - pub horizontal_flip: bool, - pub vertical_flip: bool, - pub size: B2, - } - - #[bitfield] - #[derive(Clone, Copy, PartialEq, Eq)] - pub(super) struct ObjectAttribute1Affine { - pub x: B9, - pub affine_index: B5, - pub size: B2, - } - - #[bitfield] - #[derive(Clone, Copy, PartialEq, Eq)] - pub(super) struct ObjectAttribute2 { - pub tile_index: B10, - pub priority: Priority, - pub palette_bank: B4, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use core::mem::size_of; - - #[test_case] - fn size_of_ObjectControllerReference(_: &mut crate::Gba) { - if !cfg!(debug_assertions) { - assert_eq!(size_of::(), 0); - } - } - - #[test_case] - fn object_usage(gba: &mut crate::Gba) { - const GRAPHICS: &Graphics = include_aseprite!( - "../examples/the-purple-night/gfx/objects.aseprite", - "../examples/the-purple-night/gfx/boss.aseprite" - ); - - const BOSS: &Tag = GRAPHICS.tags().get("Boss"); - const EMU: &Tag = GRAPHICS.tags().get("emu - idle"); - - let object = gba.display.object.get(); - - { - let mut objects: Vec<_> = alloc::vec![ - object.object(object.sprite(BOSS.sprite(0))), - object.object(object.sprite(EMU.sprite(0))), - ] - .into_iter() - .map(Some) - .collect(); - - object.commit(); - - let x = objects[0].as_mut().unwrap(); - x.set_hflip(true); - x.set_vflip(true); - x.set_position((1, 1).into()); - x.set_z(100); - x.set_sprite(object.sprite(BOSS.sprite(2))); - - object.commit(); - } - - object.commit(); - } +pub(super) unsafe fn initilise_oam() { + for i in 0..128 { + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 4); + ptr.write_volatile(0b10 << 8); + } + + DISPLAY_CONTROL.set_bits(1, 1, 0x6); + DISPLAY_CONTROL.set_bits(1, 1, 0xC); + DISPLAY_CONTROL.set_bits(0, 1, 0x7); } diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs new file mode 100644 index 00000000..9c4d889d --- /dev/null +++ b/agb/src/display/object/managed.rs @@ -0,0 +1,386 @@ +use core::{ + cell::{Cell, UnsafeCell}, + marker::PhantomData, +}; + +use agb_fixnum::Vector2D; +use slotmap::{new_key_type, SlotMap}; + +use crate::display::Priority; + +use super::{AffineMode, SpriteVram, UnmanagedOAM, UnmanagedObject}; + +new_key_type! {struct ObjectKey; } + +#[derive(Clone, Copy)] +struct Ordering { + next: Option, + previous: Option, +} + +struct ObjectItem { + object: UnsafeCell, + z_order: Cell, + z_index: Cell, +} + +struct Store { + store: UnsafeCell>, + first_z: Cell>, +} + +struct StoreIterator<'store> { + store: &'store slotmap::SlotMap, + current: Option, +} + +impl<'store> Iterator for StoreIterator<'store> { + type Item = &'store ObjectItem; + + fn next(&mut self) -> Option { + let to_output = &self.store[self.current?]; + self.current = to_output.z_order.get().next; + Some(to_output) + } +} + +impl Store { + /// SAFETY: while this exists, no other store related operations should be + /// performed. Notably this means you shouldn't drop the ObjectItem as this + /// implementation will touch this. + unsafe fn iter(&self) -> StoreIterator { + StoreIterator { + store: unsafe { &*self.store.get() }, + current: self.first_z.get(), + } + } + + fn is_all_ordered_right(&self) -> bool { + let mut previous_z = i32::MIN; + let mut current_index = self.first_z.get(); + + while let Some(ci) = current_index { + let obj = self.get_object(ci); + let this_z = obj.z_index.get(); + if this_z < previous_z { + return false; + } + previous_z = this_z; + current_index = obj.z_order.get().next; + } + + true + } + + fn insert_object(&self, object: UnmanagedObject) -> Object { + let object_item = ObjectItem { + object: UnsafeCell::new(object), + z_order: Cell::new(Ordering { + next: None, + previous: None, + }), + z_index: Cell::new(0), + }; + let idx = { + let data = unsafe { &mut *self.store.get() }; + data.insert(object_item) + }; + + if let Some(first) = self.first_z.get() { + let mut this_index = first; + while self.get_object(this_index).z_index.get() < 0 { + if let Some(idx) = self.get_object(this_index).z_order.get().next { + this_index = idx; + } else { + break; + } + } + if self.get_object(this_index).z_index.get() < 0 { + add_after_element(self, idx, this_index); + } else { + add_before_element(self, idx, this_index); + } + } else { + self.first_z.set(Some(idx)); + } + + Object { + me: idx, + store: self, + } + } + + fn remove_object(&self, object: ObjectKey) { + remove_from_linked_list(self, object); + + let data = unsafe { &mut *self.store.get() }; + data.remove(object); + } + + fn get_object(&self, key: ObjectKey) -> &ObjectItem { + &(unsafe { &*self.store.get() }[key]) + } +} + +pub struct OAMManager<'gba> { + phantom: PhantomData<&'gba ()>, + object_store: Store, +} + +impl OAMManager<'_> { + pub(crate) fn new() -> Self { + Self { + phantom: PhantomData, + object_store: Store { + store: UnsafeCell::new(SlotMap::with_key()), + first_z: Cell::new(None), + }, + } + } + + pub fn commit(&self) { + let mut count = 0; + + let mut unmanaged = UnmanagedOAM::new(); + + for (object, mut slot) in unsafe { self.object_store.iter() } + .map(|item| unsafe { &*item.object.get() }) + .filter(|object| object.is_visible()) + .zip(unmanaged.iter()) + { + slot.set(object); + count += 1; + } + + crate::println!("{}", count); + + unmanaged.clear_from(count); + } + + pub fn add_object(&self, sprite: SpriteVram) -> Object<'_> { + self.object_store + .insert_object(UnmanagedObject::new(sprite)) + } +} + +pub struct Object<'controller> { + me: ObjectKey, + store: &'controller Store, +} + +impl Drop for Object<'_> { + fn drop(&mut self) { + self.store.remove_object(self.me); + } +} + +fn remove_from_linked_list(store: &Store, to_remove: ObjectKey) { + let my_current_neighbours = store.get_object(to_remove).z_order.get(); + + if let Some(previous) = my_current_neighbours.previous { + let stored_part = &store.get_object(previous).z_order; + let mut neighbour_left = stored_part.get(); + neighbour_left.next = my_current_neighbours.next; + stored_part.set(neighbour_left); + } else { + store.first_z.set(my_current_neighbours.next); + } + + if let Some(next) = my_current_neighbours.next { + let stored_part = &store.get_object(next).z_order; + let mut neighbour_right = stored_part.get(); + neighbour_right.previous = my_current_neighbours.previous; + stored_part.set(neighbour_right); + } + + store.get_object(to_remove).z_order.set(Ordering { + next: None, + previous: None, + }); +} + +fn add_before_element(store: &Store, elem: ObjectKey, before_this: ObjectKey) { + assert_ne!(elem, before_this); + + let this_element_store = &store.get_object(elem).z_order; + let mut this_element = this_element_store.get(); + + let before_store = &store.get_object(before_this).z_order; + let mut before = before_store.get(); + + if let Some(previous) = before.previous { + let neighbour_left_store = &store.get_object(previous).z_order; + let mut neighbour_left = neighbour_left_store.get(); + neighbour_left.next = Some(elem); + neighbour_left_store.set(neighbour_left); + } else { + store.first_z.set(Some(elem)); + } + this_element.next = Some(before_this); + this_element.previous = before.previous; + + before.previous = Some(elem); + + this_element_store.set(this_element); + before_store.set(before); +} + +fn add_after_element(store: &Store, elem: ObjectKey, after_this: ObjectKey) { + assert_ne!(elem, after_this); + + let this_element_store = &store.get_object(elem).z_order; + let mut this_element = this_element_store.get(); + + let after_store = &store.get_object(after_this).z_order; + let mut after = after_store.get(); + + if let Some(next) = after.next { + let neighbour_left_store = &store.get_object(next).z_order; + let mut neighbour_right = neighbour_left_store.get(); + neighbour_right.previous = Some(elem); + neighbour_left_store.set(neighbour_right); + } + + this_element.previous = Some(after_this); + this_element.next = after.next; + + after.next = Some(elem); + + this_element_store.set(this_element); + after_store.set(after); +} + +fn move_before(store: &Store, source: ObjectKey, before_this: ObjectKey) { + assert_ne!(source, before_this); + + remove_from_linked_list(store, source); + add_before_element(store, source, before_this); +} + +fn move_after(store: &Store, source: ObjectKey, after_this: ObjectKey) { + assert_ne!(source, after_this); + + remove_from_linked_list(store, source); + add_after_element(store, source, after_this); +} + +impl Object<'_> { + pub fn set_z(&mut self, z_index: i32) -> &mut Self { + let my_object = &self.store.get_object(self.me); + + let order = z_index.cmp(&my_object.z_index.get()); + + match order { + core::cmp::Ordering::Equal => {} + core::cmp::Ordering::Less => { + let mut previous_index = self.me; + let mut current_index = self.me; + while self.store.get_object(current_index).z_index.get() > z_index { + previous_index = current_index; + let previous = self.store.get_object(current_index).z_order.get().previous; + if let Some(previous) = previous { + current_index = previous; + } else { + break; + } + } + if previous_index != self.me { + move_before(self.store, self.me, previous_index); + } + } + core::cmp::Ordering::Greater => { + let mut previous_index = self.me; + let mut current_index = self.me; + while self.store.get_object(current_index).z_index.get() < z_index { + previous_index = current_index; + let next = self.store.get_object(current_index).z_order.get().next; + if let Some(next) = next { + current_index = next; + } else { + break; + } + } + if previous_index != self.me { + move_after(self.store, self.me, previous_index); + } + } + } + + my_object.z_index.set(z_index); + + self + } + + fn object(&mut self) -> &mut UnmanagedObject { + unsafe { &mut *self.store.get_object(self.me).object.get() } + } + + fn object_shared(&self) -> &UnmanagedObject { + unsafe { &*self.store.get_object(self.me).object.get() } + } + + #[must_use] + pub fn is_visible(&self) -> bool { + self.object_shared().is_visible() + } + + pub fn show(&mut self) -> &mut Self { + self.object().show(); + + self + } + + pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { + self.object().show_affine(affine_mode); + + self + } + + pub fn set_hflip(&mut self, flip: bool) -> &mut Self { + self.object().set_hflip(flip); + + self + } + + pub fn set_vflip(&mut self, flip: bool) -> &mut Self { + self.object().set_vflip(flip); + + self + } + + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.object().set_x(x); + + self + } + + pub fn set_priority(&mut self, priority: Priority) -> &mut Self { + self.object().set_priority(priority); + + self + } + + pub fn hide(&mut self) -> &mut Self { + self.object().hide(); + + self + } + + pub fn set_y(&mut self, y: u16) -> &mut Self { + self.object().set_y(y); + + self + } + + pub fn set_position(&mut self, position: Vector2D) -> &mut Self { + self.object().set_position(position); + + self + } + + pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { + self.object().set_sprite(sprite); + + self + } +} diff --git a/agb/src/display/object/sprites.rs b/agb/src/display/object/sprites.rs new file mode 100644 index 00000000..1c331e46 --- /dev/null +++ b/agb/src/display/object/sprites.rs @@ -0,0 +1,7 @@ +mod sprite; +mod sprite_allocator; + +const BYTES_PER_TILE_4BPP: usize = 32; + +pub use sprite::{include_aseprite, Graphics, Size, Sprite, Tag, TagMap}; +pub use sprite_allocator::{DynamicSprite, SpriteVram, StaticSpriteLoader}; diff --git a/agb/src/display/object/sprites/sprite.rs b/agb/src/display/object/sprites/sprite.rs new file mode 100644 index 00000000..49774897 --- /dev/null +++ b/agb/src/display/object/sprites/sprite.rs @@ -0,0 +1,369 @@ +use core::{alloc::Layout, slice}; + +use crate::display::palette16::Palette16; + +use super::BYTES_PER_TILE_4BPP; + +/// Sprite data. Refers to the palette, pixel data, and the size of the sprite. +pub struct Sprite { + pub(crate) palette: &'static Palette16, + pub(crate) data: &'static [u8], + pub(crate) size: Size, +} + +impl Sprite { + #[doc(hidden)] + /// Creates a sprite from it's constituent data, used internally by + /// [include_aseprite] and should generally not be used outside it. + /// + /// # Safety + /// The data should be aligned to a 2 byte boundary + #[must_use] + pub const unsafe fn new(palette: &'static Palette16, data: &'static [u8], size: Size) -> Self { + Self { + palette, + data, + size, + } + } + + #[must_use] + pub fn size(&self) -> Size { + self.size + } +} + +/// The sizes of sprite supported by the GBA. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +#[allow(missing_docs)] +pub enum Size { + // stored as attr0 attr1 + S8x8 = 0b00_00, + S16x16 = 0b00_01, + S32x32 = 0b00_10, + S64x64 = 0b00_11, + + S16x8 = 0b01_00, + S32x8 = 0b01_01, + S32x16 = 0b01_10, + S64x32 = 0b01_11, + + S8x16 = 0b10_00, + S8x32 = 0b10_01, + S16x32 = 0b10_10, + S32x64 = 0b10_11, +} + +#[doc(hidden)] +#[macro_export] +macro_rules! align_bytes { + ($align_ty:ty, $data:literal) => {{ + #[repr(C)] // guarantee 'bytes' comes after '_align' + struct AlignedAs { + pub _align: [Align; 0], + pub bytes: Bytes, + } + + const ALIGNED: &AlignedAs<$align_ty, [u8]> = &AlignedAs { + _align: [], + bytes: *$data, + }; + + &ALIGNED.bytes + }}; +} + +/// Includes sprites found in the referenced aseprite files. Can include +/// multiple at once and optimises palettes of all included in the single call +/// together. See [Size] for supported sizes. Returns a reference to [Graphics]. +/// +/// ```rust,no_run +/// # #![no_std] +/// # #![no_main] +/// # use agb::{display::object::Graphics, include_aseprite}; +/// const GRAPHICS: &Graphics = include_aseprite!( +/// "examples/gfx/boss.aseprite", +/// "examples/gfx/objects.aseprite" +/// ); +/// ``` +/// The tags from the aseprite file are included so you can refer to sprites by +/// name in code. You should ensure tags are unique as this is not enforced by +/// aseprite. +/// +#[macro_export] +macro_rules! include_aseprite { + ($($aseprite_path: expr),*) => {{ + use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics}; + use $crate::display::palette16::Palette16; + use $crate::align_bytes; + + $crate::include_aseprite_inner!($($aseprite_path),*); + + &Graphics::new(SPRITES, TAGS) + }}; +} + +pub use include_aseprite; + +/// Stores sprite and tag data returned by [include_aseprite]. +pub struct Graphics { + sprites: &'static [Sprite], + tag_map: &'static TagMap, +} + +impl Graphics { + #[doc(hidden)] + /// Creates graphics data from sprite data and a tag_map. This is used + /// internally by [include_aseprite] and would be otherwise difficult to + /// use. + #[must_use] + pub const fn new(sprites: &'static [Sprite], tag_map: &'static TagMap) -> Self { + Self { sprites, tag_map } + } + #[must_use] + /// Gets the tag map from the aseprite files. This allows reference to + /// sprite sequences by name. + pub const fn tags(&self) -> &TagMap { + self.tag_map + } + /// Gets a big list of the sprites themselves. Using tags is often easier. + #[must_use] + pub const fn sprites(&self) -> &[Sprite] { + self.sprites + } +} + +/// Stores aseprite tags. Can be used to refer to animation sequences by name. +/// ```rust,no_run +/// # #![no_std] +/// # #![no_main] +/// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; +/// const GRAPHICS: &Graphics = include_aseprite!( +/// "examples/gfx/boss.aseprite", +/// "examples/gfx/objects.aseprite" +/// ); +/// +/// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); +/// ``` +/// This being the whole animation associated with the walk sequence of the emu. +/// See [Tag] for details on how to use this. +pub struct TagMap { + tags: &'static [(&'static str, Tag)], +} + +const fn const_byte_compare(a: &[u8], b: &[u8]) -> bool { + if a.len() != b.len() { + return false; + } + + let mut i = 0; + while i < a.len() { + if a[i] != b[i] { + return false; + } + i += 1; + } + true +} + +impl TagMap { + #[doc(hidden)] + /// Creates a new tag map from (name, Tag) pairs. Used internally by + /// [include_aseprite] and should not really be used outside of it. + #[must_use] + pub const fn new(tags: &'static [(&'static str, Tag)]) -> TagMap { + Self { tags } + } + + #[doc(hidden)] + /// Attempts to get a tag. Generally should not be used. + #[must_use] + pub const fn try_get(&'static self, tag: &str) -> Option<&'static Tag> { + let mut i = 0; + while i < self.tags.len() { + let s = self.tags[i].0; + if const_byte_compare(s.as_bytes(), tag.as_bytes()) { + return Some(&self.tags[i].1); + } + + i += 1; + } + + None + } + + /// Gets a tag associated with the name. A tag in aseprite refers to a + /// sequence of sprites with some metadata for how to animate it. You should + /// call this in a constant context so it is evaluated at compile time. It + /// is inefficient to call this elsewhere. + /// ```rust,no_run + /// # #![no_std] + /// # #![no_main] + /// # use agb::{display::object::{Graphics, Tag}, include_aseprite}; + /// const GRAPHICS: &Graphics = include_aseprite!( + /// "examples/gfx/boss.aseprite", + /// "examples/gfx/objects.aseprite" + /// ); + /// + /// const EMU_WALK: &Tag = GRAPHICS.tags().get("emu-walk"); + /// ``` + /// + /// See [Tag] for more details. + #[must_use] + pub const fn get(&'static self, tag: &str) -> &'static Tag { + let t = self.try_get(tag); + match t { + Some(t) => t, + None => panic!("The requested tag does not exist"), + } + } + + /// Takes an iterator over all the tags in the map. Not generally useful. + pub fn values(&self) -> impl Iterator { + self.tags.iter().map(|x| &x.1) + } +} + +#[derive(Clone, Copy)] +enum Direction { + Forward, + Backward, + PingPong, +} + +impl Direction { + const fn from_usize(a: usize) -> Self { + match a { + 0 => Direction::Forward, + 1 => Direction::Backward, + 2 => Direction::PingPong, + _ => panic!("Invalid direction, this is a bug in image converter or agb"), + } + } +} + +/// A sequence of sprites from aseprite. +pub struct Tag { + sprites: *const Sprite, + len: usize, + direction: Direction, +} + +impl Tag { + /// The individual sprites that make up the animation themselves. + #[must_use] + pub fn sprites(&self) -> &'static [Sprite] { + unsafe { slice::from_raw_parts(self.sprites, self.len) } + } + + /// A single sprite referred to by index in the animation sequence. + #[must_use] + pub const fn sprite(&self, idx: usize) -> &'static Sprite { + if idx >= self.len { + panic!("out of bounds access to sprite"); + } + unsafe { &*self.sprites.add(idx) } + } + + /// A sprite that follows the animation sequence. For instance, in aseprite + /// tags can be specified to animate: + /// * Forward + /// * Backward + /// * Ping pong + /// + /// This takes the animation type in account and returns the correct sprite + /// following these requirements. + #[inline] + #[must_use] + pub fn animation_sprite(&self, idx: usize) -> &'static Sprite { + let len_sub_1 = self.len - 1; + match self.direction { + Direction::Forward => self.sprite(idx % self.len), + Direction::Backward => self.sprite(len_sub_1 - (idx % self.len)), + Direction::PingPong => self.sprite( + (((idx + len_sub_1) % (len_sub_1 * 2)) as isize - len_sub_1 as isize) + .unsigned_abs(), + ), + } + } + + #[doc(hidden)] + /// Creates a new sprite from it's constituent parts. Used internally by + /// [include_aseprite] and should generally not be used elsewhere. + #[must_use] + pub const fn new(sprites: &'static [Sprite], from: usize, to: usize, direction: usize) -> Self { + assert!(from <= to); + assert!(to < sprites.len()); + Self { + sprites: &sprites[from] as *const Sprite, + len: to - from + 1, + direction: Direction::from_usize(direction), + } + } +} + +impl Size { + pub(crate) const fn number_of_tiles(self) -> usize { + match self { + Size::S8x8 => 1, + Size::S16x16 => 4, + Size::S32x32 => 16, + Size::S64x64 => 64, + Size::S16x8 => 2, + Size::S32x8 => 4, + Size::S32x16 => 8, + Size::S64x32 => 32, + Size::S8x16 => 2, + Size::S8x32 => 4, + Size::S16x32 => 8, + Size::S32x64 => 32, + } + } + pub(crate) const fn shape_size(self) -> (u16, u16) { + (self as u16 >> 2, self as u16 & 0b11) + } + + pub(crate) fn layout(self) -> Layout { + Layout::from_size_align(self.number_of_tiles() * BYTES_PER_TILE_4BPP, 8).unwrap() + } + + #[must_use] + /// Creates a size from width and height in pixels, panics if the width and + /// height is not representable by GBA sprites. + pub const fn from_width_height(width: usize, height: usize) -> Self { + match (width, height) { + (8, 8) => Size::S8x8, + (16, 16) => Size::S16x16, + (32, 32) => Size::S32x32, + (64, 64) => Size::S64x64, + (16, 8) => Size::S16x8, + (32, 8) => Size::S32x8, + (32, 16) => Size::S32x16, + (64, 32) => Size::S64x32, + (8, 16) => Size::S8x16, + (8, 32) => Size::S8x32, + (16, 32) => Size::S16x32, + (32, 64) => Size::S32x64, + (_, _) => panic!("Bad width and height!"), + } + } + + #[must_use] + /// Returns the width and height of the size in pixels. + pub const fn to_width_height(self) -> (usize, usize) { + match self { + Size::S8x8 => (8, 8), + Size::S16x16 => (16, 16), + Size::S32x32 => (32, 32), + Size::S64x64 => (64, 64), + Size::S16x8 => (16, 8), + Size::S32x8 => (32, 8), + Size::S32x16 => (32, 16), + Size::S64x32 => (64, 32), + Size::S8x16 => (8, 16), + Size::S8x32 => (8, 32), + Size::S16x32 => (16, 32), + Size::S32x64 => (32, 64), + } + } +} diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs new file mode 100644 index 00000000..072a190a --- /dev/null +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -0,0 +1,288 @@ +use core::ptr::NonNull; + +use alloc::rc::{Rc, Weak}; + +use crate::{ + agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd}, + display::palette16::Palette16, + hash_map::HashMap, +}; + +use super::{ + sprite::{Size, Sprite}, + BYTES_PER_TILE_4BPP, +}; + +const PALETTE_SPRITE: usize = 0x0500_0200; +const TILE_SPRITE: usize = 0x06010000; + +static SPRITE_ALLOCATOR: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: || TILE_SPRITE, + end: || TILE_SPRITE + 1024 * 8 * 4, + }) +}; + +static PALETTE_ALLOCATOR: BlockAllocator = unsafe { + BlockAllocator::new(StartEnd { + start: || PALETTE_SPRITE, + end: || PALETTE_SPRITE + 0x200, + }) +}; + +/// The Sprite Id is a thin wrapper around the pointer to the sprite in +/// rom and is therefore a unique identifier to a sprite +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +struct SpriteId(usize); + +impl SpriteId { + fn from_static_sprite(sprite: &'static Sprite) -> SpriteId { + SpriteId(sprite as *const _ as usize) + } +} + +/// The palette id is a thin wrapper around the pointer to the palette in rom +/// and is therefore a unique reference to a palette +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +struct PaletteId(usize); + +impl PaletteId { + fn from_static_palette(palette: &'static Palette16) -> PaletteId { + PaletteId(palette as *const _ as usize) + } +} + +/// This holds loading of static sprites and palettes. +pub struct StaticSpriteLoader { + static_palette_map: HashMap>, + static_sprite_map: HashMap>, +} + +#[derive(Clone, Copy, Debug)] +struct Location(usize); + +impl Location { + fn from_sprite_ptr(d: NonNull) -> Self { + Self(((d.as_ptr() as usize) - TILE_SPRITE) / BYTES_PER_TILE_4BPP) + } + fn from_palette_ptr(d: NonNull) -> Self { + Self((d.as_ptr() as usize - PALETTE_SPRITE) / Palette16::layout().size()) + } + fn as_palette_ptr(self) -> *mut u8 { + (self.0 * Palette16::layout().size() + PALETTE_SPRITE) as *mut u8 + } + fn as_sprite_ptr(self) -> *mut u8 { + (self.0 * BYTES_PER_TILE_4BPP + TILE_SPRITE) as *mut u8 + } +} + +#[derive(Debug)] +struct PaletteVramData { + location: Location, +} + +#[derive(Debug)] +pub struct PaletteVram { + data: Rc, +} + +impl PaletteVram { + fn new(palette: &Palette16) -> Option { + let allocated = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout()) }?; + + unsafe { + allocated + .as_ptr() + .cast::() + .copy_from_nonoverlapping(palette.colours.as_ptr(), palette.colours.len()); + } + + Some(PaletteVram { + data: Rc::new(PaletteVramData { + location: Location::from_palette_ptr(allocated), + }), + }) + } +} + +#[derive(Debug)] +struct SpriteVramData { + location: Location, + size: Size, + palette: PaletteVram, +} + +impl Drop for SpriteVramData { + fn drop(&mut self) { + unsafe { SPRITE_ALLOCATOR.dealloc(self.location.as_sprite_ptr(), self.size.layout()) } + } +} + +#[derive(Clone, Debug)] +pub struct SpriteVram { + data: Rc, +} + +impl SpriteVram { + fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option { + let allocated = unsafe { SPRITE_ALLOCATOR.alloc(size.layout()) }?; + unsafe { + allocated + .as_ptr() + .copy_from_nonoverlapping(data.as_ptr(), data.len()); + } + Some(SpriteVram { + data: Rc::new(SpriteVramData { + location: Location::from_sprite_ptr(allocated), + size, + palette, + }), + }) + } + + pub(crate) fn location(&self) -> u16 { + self.data.location.0 as u16 + } + + pub(crate) fn size(&self) -> Size { + self.data.size + } + + pub(crate) fn palette_location(&self) -> u16 { + self.data.palette.data.location.0 as u16 + } +} + +impl StaticSpriteLoader { + fn create_sprite_no_insert( + palette_map: &mut HashMap>, + sprite: &'static Sprite, + ) -> Option<(Weak, SpriteVram)> { + let palette = Self::try_get_vram_palette_asoc(palette_map, sprite.palette)?; + + let sprite = SpriteVram::new(sprite.data, sprite.size, palette)?; + Some((Rc::downgrade(&sprite.data), sprite)) + } + + fn try_get_vram_palette_asoc( + palette_map: &mut HashMap>, + palette: &'static Palette16, + ) -> Option { + let id = PaletteId::from_static_palette(palette); + Some(match palette_map.entry(id) { + crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { + Some(data) => PaletteVram { data }, + None => { + let pv = PaletteVram::new(palette)?; + entry.insert(Rc::downgrade(&pv.data)); + pv + } + }, + crate::hash_map::Entry::Vacant(entry) => { + let pv = PaletteVram::new(palette)?; + entry.insert(Rc::downgrade(&pv.data)); + pv + } + }) + } + + pub fn try_get_vram_sprite(&mut self, sprite: &'static Sprite) -> Option { + // check if we already have the sprite in vram + + let id = SpriteId::from_static_sprite(sprite); + + Some(match self.static_sprite_map.entry(id) { + crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { + Some(data) => SpriteVram { data }, + None => { + let (weak, vram) = + Self::create_sprite_no_insert(&mut self.static_palette_map, sprite)?; + entry.insert(weak); + vram + } + }, + crate::hash_map::Entry::Vacant(entry) => { + let (weak, vram) = + Self::create_sprite_no_insert(&mut self.static_palette_map, sprite)?; + entry.insert(weak); + vram + } + }) + } + + pub fn try_get_vram_palette(&mut self, palette: &'static Palette16) -> Option { + Self::try_get_vram_palette_asoc(&mut self.static_palette_map, palette) + } + + pub fn get_vram_sprite(&mut self, sprite: &'static Sprite) -> SpriteVram { + self.try_get_vram_sprite(sprite) + .expect("no free sprite slots") + } + + pub fn get_vram_palette(&mut self, palette: &'static Palette16) -> PaletteVram { + self.try_get_vram_palette(palette) + .expect("no free palette slots") + } + + pub(crate) fn new() -> Self { + Self { + static_palette_map: HashMap::new(), + static_sprite_map: HashMap::new(), + } + } + + fn gc(&mut self) { + self.static_sprite_map + .retain(|_, v| Weak::strong_count(v) != 0); + self.static_palette_map + .retain(|_, v| Weak::strong_count(v) != 0); + } +} + +impl Default for StaticSpriteLoader { + fn default() -> Self { + Self::new() + } +} + +/// Sprite data that can be used to create sprites in vram. +pub struct DynamicSprite<'a> { + data: &'a [u8], + size: Size, +} + +impl DynamicSprite<'_> { + #[must_use] + /// Creates a new dynamic sprite from underlying bytes. Note that despite + /// being an array of u8, this must be aligned to at least a 2 byte + /// boundary. + pub fn new(data: &[u8], size: Size) -> DynamicSprite { + let ptr = &data[0] as *const _ as usize; + if ptr % 2 != 0 { + panic!("data is not aligned to a 2 byte boundary"); + } + if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP { + panic!( + "data is not of expected length, got {} expected {}", + data.len(), + size.number_of_tiles() * BYTES_PER_TILE_4BPP + ); + } + DynamicSprite { data, size } + } + + #[must_use] + /// Tries to copy the sprite to vram to be used to set object sprites. + /// Returns None if there is no room in sprite vram. + pub fn try_vram(&self, palette: PaletteVram) -> Option { + SpriteVram::new(self.data, self.size, palette) + } + + #[must_use] + /// Tries to copy the sprite to vram to be used to set object sprites. + /// Panics if there is no room in sprite vram. + pub fn to_vram(&self, palette: PaletteVram) -> SpriteVram { + self.try_vram(palette) + .expect("No slot for sprite available") + } +} diff --git a/agb/src/display/object/unmanaged.rs b/agb/src/display/object/unmanaged.rs new file mode 100644 index 00000000..3a9db454 --- /dev/null +++ b/agb/src/display/object/unmanaged.rs @@ -0,0 +1,5 @@ +mod attributes; +mod object; + +pub use attributes::AffineMode; +pub use object::{OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs new file mode 100644 index 00000000..69575c4b --- /dev/null +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -0,0 +1,204 @@ +use modular_bitfield::BitfieldSpecifier; + +use crate::display::Priority; + +use self::attributes::{ + ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2, +}; + +#[derive(PartialEq, Eq, Debug)] +pub struct Attributes { + a0: ObjectAttribute0, + a1s: ObjectAttribute1Standard, + a1a: ObjectAttribute1Affine, + a2: ObjectAttribute2, +} + +impl Default for Attributes { + fn default() -> Self { + Self { + a0: ObjectAttribute0::from_bytes([0b01, 0]), + a1s: Default::default(), + a1a: Default::default(), + a2: Default::default(), + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub enum AffineMode { + Affine = 1, + AffineDouble = 3, +} + +impl Attributes { + pub fn bytes(&self) -> [u8; 6] { + let mode = self.a0.object_mode(); + let attrs = match mode { + ObjectMode::Normal => [ + self.a0.into_bytes(), + self.a1s.into_bytes(), + self.a2.into_bytes(), + ], + _ => [ + self.a0.into_bytes(), + self.a1a.into_bytes(), + self.a2.into_bytes(), + ], + }; + + // Safety: length and alignment are the same, and every possible value is valid + unsafe { core::mem::transmute(attrs) } + } + + pub fn is_visible(&self) -> bool { + self.a0.object_mode() != ObjectMode::Disabled + } + + pub fn show(&mut self) -> &mut Self { + self.a0.set_object_mode(ObjectMode::Normal); + + self + } + + pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { + self.a0.set_object_mode(match affine_mode { + AffineMode::Affine => ObjectMode::Affine, + AffineMode::AffineDouble => ObjectMode::AffineDouble, + }); + + self + } + + pub fn set_hflip(&mut self, flip: bool) -> &mut Self { + self.a1s.set_horizontal_flip(flip); + + self + } + + pub fn set_vflip(&mut self, flip: bool) -> &mut Self { + self.a1s.set_vertical_flip(flip); + + self + } + + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.a1a.set_x(x.rem_euclid(1 << 9)); + self.a1s.set_x(x.rem_euclid(1 << 9)); + + self + } + + pub fn set_priority(&mut self, priority: Priority) -> &mut Self { + self.a2.set_priority(priority); + + self + } + + pub fn hide(&mut self) -> &mut Self { + self.a0.set_object_mode(ObjectMode::Disabled); + + self + } + + pub fn set_y(&mut self, y: u16) -> &mut Self { + self.a0.set_y(y as u8); + + self + } + + pub fn set_palette(&mut self, palette_id: u16) -> &mut Self { + self.a2.set_palette_bank(palette_id as u8); + + self + } + + pub fn set_affine_matrix(&mut self, affine_matrix_id: u16) -> &mut Self { + self.a1a.set_affine_index(affine_matrix_id as u8); + + self + } + + pub fn set_sprite(&mut self, sprite_id: u16, shape: u16, size: u16) -> &mut Self { + self.a2.set_tile_index(sprite_id); + self.a1a.set_size(size as u8); + self.a1s.set_size(size as u8); + self.a0.set_shape(shape as u8); + + self + } +} + +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +enum ObjectMode { + Normal, + Affine, + Disabled, + AffineDouble, +} + +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +#[bits = 2] +enum GraphicsMode { + Normal, + AlphaBlending, + Window, +} + +#[derive(BitfieldSpecifier, Clone, Copy, Debug, PartialEq, Eq)] +enum ColourMode { + Four, + Eight, +} + +// this mod is not public, so the internal parts don't need documenting. +#[allow(dead_code)] +#[allow(clippy::all)] +#[allow(clippy::map_unwrap_or)] +mod attributes { + use modular_bitfield::{ + bitfield, + specifiers::{B10, B2, B3, B4, B5, B8, B9}, + }; + + use crate::display::Priority; + + use super::*; + #[bitfield] + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub(super) struct ObjectAttribute0 { + pub y: B8, + pub object_mode: ObjectMode, + pub graphics_mode: GraphicsMode, + pub mosaic: bool, + pub colour_mode: ColourMode, + pub shape: B2, + } + + #[bitfield] + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub(super) struct ObjectAttribute1Standard { + pub x: B9, + #[skip] + __: B3, + pub horizontal_flip: bool, + pub vertical_flip: bool, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub(super) struct ObjectAttribute1Affine { + pub x: B9, + pub affine_index: B5, + pub size: B2, + } + + #[bitfield] + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub(super) struct ObjectAttribute2 { + pub tile_index: B10, + pub priority: Priority, + pub palette_bank: B4, + } +} diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs new file mode 100644 index 00000000..9c28bd0a --- /dev/null +++ b/agb/src/display/object/unmanaged/object.rs @@ -0,0 +1,187 @@ +use core::{cell::UnsafeCell, marker::PhantomData}; + +use agb_fixnum::Vector2D; + +use crate::display::{ + object::{sprites::SpriteVram, OBJECT_ATTRIBUTE_MEMORY}, + Priority, +}; + +use super::attributes::{AffineMode, Attributes}; + +pub struct UnmanagedOAM<'gba> { + phantom: PhantomData<&'gba ()>, +} + +pub struct OAMIterator<'oam> { + phantom: PhantomData<&'oam ()>, + index: usize, +} + +pub struct OAMSlot<'oam> { + phantom: PhantomData<&'oam ()>, + slot: usize, +} + +impl OAMSlot<'_> { + pub fn set(&mut self, object: &UnmanagedObject) { + self.set_bytes(object.attributes.bytes()); + + // SAFETY: This is called here and in set_sprite, neither of which call the other. + let sprites = unsafe { &mut *object.sprites.get() }; + + sprites.previous_sprite = Some(sprites.sprite.clone()); + } + + fn set_bytes(&mut self, bytes: [u8; 6]) { + unsafe { + let address = (OBJECT_ATTRIBUTE_MEMORY as *mut u8).add(self.slot * 8); + address.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len()); + } + } +} + +impl<'oam> Iterator for OAMIterator<'oam> { + type Item = OAMSlot<'oam>; + + fn next(&mut self) -> Option { + let idx = self.index; + self.index += 1; + + if idx >= 128 { + None + } else { + Some(OAMSlot { + phantom: PhantomData, + slot: idx, + }) + } + } +} + +impl UnmanagedOAM<'_> { + pub fn iter(&mut self) -> OAMIterator<'_> { + OAMIterator { + phantom: PhantomData, + index: 0, + } + } + + pub(crate) fn new() -> Self { + Self { + phantom: PhantomData, + } + } + + pub fn clear_from(&self, from: usize) { + if from >= 128 { + return; + } + + for i in from..128 { + unsafe { + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 4); + ptr.write_volatile(0b10 << 8); + } + } + } +} + +#[derive(Debug)] +struct VramSprites { + sprite: SpriteVram, + previous_sprite: Option, +} + +#[derive(Debug)] +pub struct UnmanagedObject { + attributes: Attributes, + sprites: UnsafeCell, +} + +impl UnmanagedObject { + #[must_use] + pub fn new(sprite: SpriteVram) -> Self { + Self { + attributes: Attributes::default(), + sprites: UnsafeCell::new(VramSprites { + sprite, + previous_sprite: None, + }), + } + } + + pub fn is_visible(&self) -> bool { + self.attributes.is_visible() + } + + pub fn show(&mut self) -> &mut Self { + self.attributes.show(); + + self + } + + pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { + self.attributes.show_affine(affine_mode); + + self + } + + pub fn set_hflip(&mut self, flip: bool) -> &mut Self { + self.attributes.set_hflip(flip); + + self + } + + pub fn set_vflip(&mut self, flip: bool) -> &mut Self { + self.attributes.set_vflip(flip); + + self + } + + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.attributes.set_x(x); + + self + } + + pub fn set_priority(&mut self, priority: Priority) -> &mut Self { + self.attributes.set_priority(priority); + + self + } + + pub fn hide(&mut self) -> &mut Self { + self.attributes.hide(); + + self + } + + pub fn set_y(&mut self, y: u16) -> &mut Self { + self.attributes.set_y(y); + + self + } + + pub fn set_position(&mut self, position: Vector2D) -> &mut Self { + self.set_y(position.y.rem_euclid(1 << 9) as u16); + self.set_x(position.x.rem_euclid(1 << 9) as u16); + + self + } + + pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { + // SAFETY: This is called here and in OAMSlot set, neither of which call the other. + let sprites = unsafe { &mut *self.sprites.get() }; + + let size = sprite.size(); + let (shape, size) = size.shape_size(); + + self.attributes.set_sprite(sprite.location(), shape, size); + self.attributes.set_palette(sprite.palette_location()); + + sprites.sprite = sprite; + + self + } +} diff --git a/agb/src/display/palette16.rs b/agb/src/display/palette16.rs index 6a4441ba..449eb31c 100644 --- a/agb/src/display/palette16.rs +++ b/agb/src/display/palette16.rs @@ -1,3 +1,5 @@ +use core::alloc::Layout; + #[repr(C)] #[derive(Clone)] pub struct Palette16 { @@ -21,4 +23,8 @@ impl Palette16 { pub fn colour(&self, index: usize) -> u16 { self.colours[index] } + + pub(crate) const fn layout() -> Layout { + Layout::new::() + } } From c03c1664cb9642836609596f36a9d5fd77a6d91a Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Apr 2023 16:34:51 +0100 Subject: [PATCH 02/39] move static sprite loader into managed oam --- agb/examples/chicken.rs | 16 +++--- agb/examples/sprites.rs | 20 +++---- agb/src/display/mod.rs | 4 +- agb/src/display/object/managed.rs | 54 ++++++++++++++++--- .../object/sprites/sprite_allocator.rs | 2 +- 5 files changed, 67 insertions(+), 29 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 9bc7bfee..e0fb6757 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -4,7 +4,7 @@ use agb::{ display::tiled::{TileFormat, TileSet, TileSetting, TiledMap}, display::{ - object::{Object, Size, Sprite, StaticSpriteLoader}, + object::{OAMManager, Object, Size, Sprite, StaticSpriteLoader}, palette16::Palette16, tiled::RegularBackgroundSize, HEIGHT, WIDTH, @@ -74,9 +74,9 @@ fn main(mut gba: agb::Gba) -> ! { background.show(); background.commit(&mut vram); - let (object, mut sprites) = gba.display.object.get_managed(); + let object = gba.display.object.get_managed(); - let sprite = sprites.get_vram_sprite(&CHICKEN_SPRITES[0]); + let sprite = object.get_vram_sprite(&CHICKEN_SPRITES[0]); let mut chicken = Character { object: object.add_object(sprite), position: Vector2D { @@ -137,7 +137,7 @@ fn main(mut gba: agb::Gba) -> ! { } restrict_to_screen(&mut chicken); - update_chicken_object(&mut chicken, &mut sprites, state, frame_count); + update_chicken_object(&mut chicken, &object, state, frame_count); object.commit(); } @@ -145,7 +145,7 @@ fn main(mut gba: agb::Gba) -> ! { fn update_chicken_object( chicken: &'_ mut Character<'_>, - sprites: &mut StaticSpriteLoader, + gfx: &OAMManager, state: State, frame_count: u32, ) { @@ -158,18 +158,18 @@ fn update_chicken_object( State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { chicken.object.set_sprite( - sprites.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), + gfx.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), ); } else { chicken .object - .set_sprite(sprites.get_vram_sprite(&CHICKEN_SPRITES[0])); + .set_sprite(gfx.get_vram_sprite(&CHICKEN_SPRITES[0])); } } State::Upwards => {} State::Flapping => { chicken.object.set_sprite( - sprites.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)]), + gfx.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)]), ); } } diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 41c4247c..4ff1fb7b 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -3,7 +3,7 @@ extern crate alloc; -use agb::display::object::{Graphics, OAMManager, Sprite, StaticSpriteLoader, TagMap}; +use agb::display::object::{Graphics, OAMManager, Sprite, TagMap}; use alloc::vec::Vec; const GRAPHICS: &Graphics = agb::include_aseprite!( @@ -15,13 +15,13 @@ const GRAPHICS: &Graphics = agb::include_aseprite!( const SPRITES: &[Sprite] = GRAPHICS.sprites(); const TAG_MAP: &TagMap = GRAPHICS.tags(); -fn all_sprites(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { +fn all_sprites(gfx: &OAMManager) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); for y in 0..9 { for x in 0..14 { - let mut obj = gfx.add_object(sprites.get_vram_sprite(&SPRITES[0])); + let mut obj = gfx.add_object_static_sprite(&SPRITES[0]); obj.show(); obj.set_position((x * 16 + 8, y * 16 + 8).into()); objs.push(obj); @@ -48,14 +48,14 @@ fn all_sprites(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { image %= SPRITES.len(); for (i, obj) in objs.iter_mut().enumerate() { let this_image = (image + i) % SPRITES.len(); - obj.set_sprite(sprites.get_vram_sprite(&SPRITES[this_image])); + obj.set_sprite(gfx.get_vram_sprite(&SPRITES[this_image])); } gfx.commit(); } } } -fn all_tags(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { +fn all_tags(gfx: &OAMManager) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); @@ -65,7 +65,7 @@ fn all_tags(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { let sprite = v.sprite(0); let (size_x, size_y) = sprite.size().to_width_height(); let (size_x, size_y) = (size_x as i32, size_y as i32); - let mut obj = gfx.add_object(sprites.get_vram_sprite(sprite)); + let mut obj = gfx.add_object_static_sprite(sprite); obj.show(); obj.set_position((x * 32 + 16 - size_x / 2, y * 32 + 16 - size_y / 2).into()); objs.push((obj, v)); @@ -90,7 +90,7 @@ fn all_tags(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { if count % 5 == 0 { image += 1; for (obj, tag) in objs.iter_mut() { - obj.set_sprite(sprites.get_vram_sprite(tag.animation_sprite(image))); + obj.set_sprite(gfx.get_vram_sprite(tag.animation_sprite(image))); } gfx.commit(); } @@ -99,12 +99,12 @@ fn all_tags(gfx: &OAMManager, sprites: &mut StaticSpriteLoader) { #[agb::entry] fn main(mut gba: agb::Gba) -> ! { - let (gfx, mut ssl) = gba.display.object.get_managed(); + let gfx = gba.display.object.get_managed(); loop { - all_tags(&gfx, &mut ssl); + all_tags(&gfx); gfx.commit(); - all_sprites(&gfx, &mut ssl); + all_sprites(&gfx); gfx.commit(); } } diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 5c30e9d0..cf0923fb 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -89,9 +89,9 @@ impl ObjectDistribution { (UnmanagedOAM::new(), StaticSpriteLoader::new()) } - pub fn get_managed(&mut self) -> (OAMManager<'_>, StaticSpriteLoader) { + pub fn get_managed(&mut self) -> OAMManager<'_> { unsafe { initilise_oam() }; - (OAMManager::new(), StaticSpriteLoader::new()) + OAMManager::new() } } diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 9c4d889d..861ffa53 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -4,11 +4,12 @@ use core::{ }; use agb_fixnum::Vector2D; +use alloc::vec::Vec; use slotmap::{new_key_type, SlotMap}; -use crate::display::Priority; +use crate::{display::Priority, sync::Static}; -use super::{AffineMode, SpriteVram, UnmanagedOAM, UnmanagedObject}; +use super::{AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject}; new_key_type! {struct ObjectKey; } @@ -26,6 +27,7 @@ struct ObjectItem { struct Store { store: UnsafeCell>, + removal_list: UnsafeCell>, first_z: Cell>, } @@ -111,12 +113,24 @@ impl Store { } fn remove_object(&self, object: ObjectKey) { - remove_from_linked_list(self, object); - let data = unsafe { &mut *self.store.get() }; data.remove(object); } + fn remove_all_in_removal_list(&self) { + let removal_list = unsafe { &mut *self.removal_list.get() }; + for object in removal_list.drain(..) { + self.remove_object(object); + } + } + + fn mark_for_removal(&self, object: ObjectKey) { + let removal_list = unsafe { &mut *self.removal_list.get() }; + removal_list.push(object); + + remove_from_linked_list(self, object); + } + fn get_object(&self, key: ObjectKey) -> &ObjectItem { &(unsafe { &*self.store.get() }[key]) } @@ -125,6 +139,7 @@ impl Store { pub struct OAMManager<'gba> { phantom: PhantomData<&'gba ()>, object_store: Store, + sprite_loader: UnsafeCell, } impl OAMManager<'_> { @@ -133,16 +148,29 @@ impl OAMManager<'_> { phantom: PhantomData, object_store: Store { store: UnsafeCell::new(SlotMap::with_key()), + removal_list: UnsafeCell::new(Vec::new()), first_z: Cell::new(None), }, + sprite_loader: UnsafeCell::new(StaticSpriteLoader::new()), } } + pub fn do_work_with_sprite_loader(&self, c: C) -> T + where + C: Fn(&mut StaticSpriteLoader) -> T, + { + let sprite_loader = unsafe { &mut *self.sprite_loader.get() }; + + c(sprite_loader) + } + pub fn commit(&self) { let mut count = 0; let mut unmanaged = UnmanagedOAM::new(); + // do interactions with OAM + for (object, mut slot) in unsafe { self.object_store.iter() } .map(|item| unsafe { &*item.object.get() }) .filter(|object| object.is_visible()) @@ -151,16 +179,26 @@ impl OAMManager<'_> { slot.set(object); count += 1; } - - crate::println!("{}", count); - unmanaged.clear_from(count); + + // finished OAM interactions + + self.object_store.remove_all_in_removal_list(); + self.do_work_with_sprite_loader(StaticSpriteLoader::garbage_collect); } pub fn add_object(&self, sprite: SpriteVram) -> Object<'_> { self.object_store .insert_object(UnmanagedObject::new(sprite)) } + + pub fn get_vram_sprite(&self, sprite: &'static Sprite) -> SpriteVram { + self.do_work_with_sprite_loader(|sprite_loader| sprite_loader.get_vram_sprite(sprite)) + } + + pub fn add_object_static_sprite(&self, sprite: &'static Sprite) -> Object<'_> { + self.add_object(self.get_vram_sprite(sprite)) + } } pub struct Object<'controller> { @@ -170,7 +208,7 @@ pub struct Object<'controller> { impl Drop for Object<'_> { fn drop(&mut self) { - self.store.remove_object(self.me); + self.store.mark_for_removal(self.me); } } diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index 072a190a..207186ac 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -231,7 +231,7 @@ impl StaticSpriteLoader { } } - fn gc(&mut self) { + pub fn garbage_collect(&mut self) { self.static_sprite_map .retain(|_, v| Weak::strong_count(v) != 0); self.static_palette_map From 660adbe9618d4c43eb076aef4b3149db61d6c83c Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 2 Apr 2023 17:51:35 +0100 Subject: [PATCH 03/39] fix games --- agb/src/display/object/managed.rs | 2 +- agb/src/display/object/unmanaged/object.rs | 26 ++++- examples/combo/Cargo.lock | 82 ++++++++++++- examples/hyperspace-roll/Cargo.lock | 13 +++ examples/hyperspace-roll/src/battle.rs | 7 +- .../hyperspace-roll/src/battle/display.rs | 52 ++++----- examples/hyperspace-roll/src/customise.rs | 27 +++-- examples/hyperspace-roll/src/graphics.rs | 39 ++++--- examples/hyperspace-roll/src/lib.rs | 6 +- .../the-hat-chooses-the-wizard/Cargo.lock | 10 ++ .../the-hat-chooses-the-wizard/src/enemies.rs | 34 +++--- .../the-hat-chooses-the-wizard/src/lib.rs | 43 ++++--- examples/the-purple-night/Cargo.lock | 10 ++ examples/the-purple-night/src/lib.rs | 109 +++++++++--------- 14 files changed, 295 insertions(+), 165 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 861ffa53..9d99ae2b 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -7,7 +7,7 @@ use agb_fixnum::Vector2D; use alloc::vec::Vec; use slotmap::{new_key_type, SlotMap}; -use crate::{display::Priority, sync::Static}; +use crate::display::Priority; use super::{AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject}; diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 9c28bd0a..ef50b9c9 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -102,13 +102,22 @@ pub struct UnmanagedObject { impl UnmanagedObject { #[must_use] pub fn new(sprite: SpriteVram) -> Self { - Self { + let sprite_location = sprite.location(); + let palette_location = sprite.palette_location(); + let (shape, size) = sprite.size().shape_size(); + + let mut sprite = Self { attributes: Attributes::default(), sprites: UnsafeCell::new(VramSprites { sprite, previous_sprite: None, }), - } + }; + + sprite.attributes.set_sprite(sprite_location, shape, size); + sprite.attributes.set_palette(palette_location); + + sprite } pub fn is_visible(&self) -> bool { @@ -170,16 +179,21 @@ impl UnmanagedObject { self } - pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { - // SAFETY: This is called here and in OAMSlot set, neither of which call the other. - let sprites = unsafe { &mut *self.sprites.get() }; - + fn set_sprite_attributes(&mut self, sprite: &SpriteVram) -> &mut Self { let size = sprite.size(); let (shape, size) = size.shape_size(); self.attributes.set_sprite(sprite.location(), shape, size); self.attributes.set_palette(sprite.palette_location()); + self + } + + pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { + self.set_sprite_attributes(&sprite); + + // SAFETY: This is called here and in OAMSlot set, neither of which call the other. + let sprites = unsafe { &mut *self.sprites.get() }; sprites.sprite = sprite; self diff --git a/examples/combo/Cargo.lock b/examples/combo/Cargo.lock index 68bc03c2..d44a3230 100644 --- a/examples/combo/Cargo.lock +++ b/examples/combo/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", + "slotmap", ] [[package]] @@ -201,7 +202,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a62391ecb864cf12ed06b2af4eda2e609b97657950d6a8f06841b17726ab253" dependencies = [ - "hashbrown", + "hashbrown 0.11.2", "ttf-parser", ] @@ -234,6 +235,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hound" version = "3.5.0" @@ -245,7 +252,6 @@ name = "hyperspace-roll" version = "0.1.0" dependencies = [ "agb", - "bare-metal", ] [[package]] @@ -263,6 +269,16 @@ dependencies = [ "png", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "itoa" version = "1.0.6" @@ -296,6 +312,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -467,6 +489,24 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -533,11 +573,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -564,6 +629,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] + [[package]] name = "xml-rs" version = "0.8.4" diff --git a/examples/hyperspace-roll/Cargo.lock b/examples/hyperspace-roll/Cargo.lock index da95b861..84a8548d 100644 --- a/examples/hyperspace-roll/Cargo.lock +++ b/examples/hyperspace-roll/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", + "slotmap", ] [[package]] @@ -423,6 +424,18 @@ dependencies = [ ] [[package]] +<<<<<<< HEAD +======= +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +>>>>>>> ff9ab890 (fix games) name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/examples/hyperspace-roll/src/battle.rs b/examples/hyperspace-roll/src/battle.rs index 04f76fdd..cce4cd80 100644 --- a/examples/hyperspace-roll/src/battle.rs +++ b/examples/hyperspace-roll/src/battle.rs @@ -494,7 +494,7 @@ pub(crate) fn battle_screen( let obj = &agb.obj; - let mut select_box_obj = agb.obj.object(agb.obj.sprite(SELECT_BOX.sprite(0))); + let mut select_box_obj = agb.obj.add_object_static_sprite(SELECT_BOX.sprite(0)); select_box_obj.show(); let num_dice = player_dice.dice.len(); @@ -585,7 +585,10 @@ pub(crate) fn battle_screen( select_box_obj .set_y(120 - 4) .set_x(selected_die as u16 * 40 + 28 - 4) - .set_sprite(agb.obj.sprite(SELECT_BOX.animation_sprite(counter / 10))); + .set_sprite( + agb.obj + .get_vram_sprite(SELECT_BOX.animation_sprite(counter / 10)), + ); agb.star_background.update(); agb.sfx.frame(); diff --git a/examples/hyperspace-roll/src/battle/display.rs b/examples/hyperspace-roll/src/battle/display.rs index 1b33009f..973d7e79 100644 --- a/examples/hyperspace-roll/src/battle/display.rs +++ b/examples/hyperspace-roll/src/battle/display.rs @@ -1,4 +1,4 @@ -use agb::display::object::{Object, ObjectController}; +use agb::display::object::{OAMManager, Object}; use agb::rng; use alloc::vec; use alloc::vec::Vec; @@ -39,7 +39,7 @@ pub struct BattleScreenDisplay<'a> { const HEALTH_BAR_WIDTH: usize = 48; impl<'a> BattleScreenDisplay<'a> { - pub fn new(obj: &'a ObjectController, current_battle_state: &CurrentBattleState) -> Self { + pub fn new(obj: &'a OAMManager, current_battle_state: &CurrentBattleState) -> Self { let mut misc_sprites = vec![]; let player_x = 12; let player_y = 8; @@ -52,8 +52,8 @@ impl<'a> BattleScreenDisplay<'a> { Ship::PilotedShip }); - let mut player_obj = obj.object(obj.sprite(player_sprite)); - let mut enemy_obj = obj.object(obj.sprite(enemy_sprite)); + let mut player_obj = obj.add_object_static_sprite(player_sprite); + let mut enemy_obj = obj.add_object_static_sprite(enemy_sprite); player_obj.set_x(player_x).set_y(player_y).set_z(1).show(); enemy_obj.set_x(enemy_x).set_y(player_y).set_z(1).show(); @@ -66,7 +66,7 @@ impl<'a> BattleScreenDisplay<'a> { .faces_to_render() .enumerate() .map(|(i, (face, _))| { - let mut die_obj = obj.object(obj.sprite(FACE_SPRITES.sprite_for_face(face))); + let mut die_obj = obj.add_object_static_sprite(FACE_SPRITES.sprite_for_face(face)); die_obj.set_y(120).set_x(i as u16 * 40 + 28).show(); @@ -89,7 +89,7 @@ impl<'a> BattleScreenDisplay<'a> { let player_shield: Vec<_> = (0..5) .map(|i| { - let mut shield_obj = obj.object(obj.sprite(shield_sprite)); + let mut shield_obj = obj.add_object_static_sprite(shield_sprite); shield_obj .set_x(player_x + 18 + 11 * i) .set_y(player_y) @@ -101,7 +101,7 @@ impl<'a> BattleScreenDisplay<'a> { let enemy_shield: Vec<_> = (0..5) .map(|i| { - let mut shield_obj = obj.object(obj.sprite(shield_sprite)); + let mut shield_obj = obj.add_object_static_sprite(shield_sprite); shield_obj .set_x(enemy_x - 16 - 11 * i) .set_y(player_y) @@ -146,8 +146,8 @@ impl<'a> BattleScreenDisplay<'a> { let enemy_attack_display = (0..2) .map(|i| { - let mut attack_obj = obj.object( - obj.sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(EnemyAttackType::Attack)), + let mut attack_obj = obj.add_object_static_sprite( + ENEMY_ATTACK_SPRITES.sprite_for_attack(EnemyAttackType::Attack), ); let attack_obj_position = (120, 56 + 32 * i).into(); @@ -189,14 +189,14 @@ impl<'a> BattleScreenDisplay<'a> { pub fn update( &mut self, - obj: &'a ObjectController, + obj: &'a OAMManager, current_battle_state: &CurrentBattleState, ) -> Vec { for (i, player_shield) in self.objs.player_shield.iter_mut().enumerate() { if i < current_battle_state.player.shield_count as usize { player_shield .show() - .set_sprite(obj.sprite(SHIELD.sprite(0))); + .set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -206,7 +206,7 @@ impl<'a> BattleScreenDisplay<'a> { if i < current_battle_state.enemy.shield_count as usize { player_shield .show() - .set_sprite(obj.sprite(SHIELD.sprite(0))); + .set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -250,7 +250,7 @@ impl<'a> BattleScreenDisplay<'a> { .zip(current_battle_state.rolled_dice.faces_to_render()) .zip(self.objs.dice_cooldowns.iter_mut()) { - die_obj.set_sprite(obj.sprite(FACE_SPRITES.sprite_for_face(current_face))); + die_obj.set_sprite(obj.get_vram_sprite(FACE_SPRITES.sprite_for_face(current_face))); if let Some(cooldown) = cooldown { cooldown_healthbar @@ -279,7 +279,7 @@ impl<'a> BattleScreenDisplay<'a> { actions_to_apply } - pub fn add_action(&mut self, action: Action, obj: &'a ObjectController, sfx: &mut Sfx) { + pub fn add_action(&mut self, action: Action, obj: &'a OAMManager, sfx: &mut Sfx) { play_sound_for_action_start(&action, sfx); self.animations @@ -309,10 +309,10 @@ impl<'a> EnemyAttackDisplay<'a> { } } - pub fn update(&mut self, attack: &Option, obj: &'a ObjectController) { + pub fn update(&mut self, attack: &Option, obj: &'a OAMManager) { if let Some(attack) = attack { self.face.show().set_sprite( - obj.sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), + obj.get_vram_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), ); self.cooldown .set_value((attack.cooldown * 48 / attack.max_cooldown) as usize, obj); @@ -350,27 +350,27 @@ enum AnimationUpdateState { } impl<'a> AnimationStateHolder<'a> { - fn for_action(a: Action, obj: &'a ObjectController) -> Self { + fn for_action(a: Action, obj: &'a OAMManager) -> Self { let state = match a { Action::PlayerActivateShield { amount, .. } => { AnimationState::PlayerActivateShield { amount, frame: 0 } } Action::PlayerShoot { .. } => AnimationState::PlayerShoot { - bullet: obj.object(obj.sprite(BULLET_SPRITE)), + bullet: obj.add_object_static_sprite(BULLET_SPRITE), x: 64, }, Action::PlayerDisrupt { .. } => AnimationState::PlayerDisrupt { - bullet: obj.object(obj.sprite(DISRUPT_BULLET)), + bullet: obj.add_object_static_sprite(DISRUPT_BULLET), x: 64, }, Action::PlayerHeal { .. } => AnimationState::PlayerHeal {}, Action::PlayerBurstShield { .. } => AnimationState::PlayerBurstShield { frame: 0 }, Action::PlayerSendBurstShield { .. } => AnimationState::PlayerSendBurstShield { - bullet: obj.object(obj.sprite(BURST_BULLET)), + bullet: obj.add_object_static_sprite(BURST_BULLET), x: 64, }, Action::EnemyShoot { .. } => AnimationState::EnemyShoot { - bullet: obj.object(obj.sprite(BULLET_SPRITE)), + bullet: obj.add_object_static_sprite(BULLET_SPRITE), x: 175, }, Action::EnemyShield { amount, .. } => AnimationState::EnemyShield { amount, frame: 0 }, @@ -383,7 +383,7 @@ impl<'a> AnimationStateHolder<'a> { fn update( &mut self, objs: &mut BattleScreenDisplayObjects<'a>, - obj: &'a ObjectController, + obj: &'a OAMManager, current_battle_state: &CurrentBattleState, ) -> AnimationUpdateState { match &mut self.state { @@ -414,7 +414,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_player_shields..*amount { objs.player_shield[i as usize] .show() - .set_sprite(obj.sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.get_vram_sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -445,7 +445,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_enemy_shields..*amount { objs.enemy_shield[i as usize] .show() - .set_sprite(obj.sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.get_vram_sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -468,7 +468,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationState::PlayerBurstShield { frame } => { if *frame < 10 { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.sprite(SHIELD.sprite(*frame / 2))); + shield.set_sprite(obj.get_vram_sprite(SHIELD.sprite(*frame / 2))); } *frame += 1; @@ -476,7 +476,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationUpdateState::Continue } else { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.sprite(SHIELD.sprite(0))); + shield.set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); } AnimationUpdateState::RemoveWithAction(self.action.clone()) diff --git a/examples/hyperspace-roll/src/customise.rs b/examples/hyperspace-roll/src/customise.rs index 407797af..5e02580d 100644 --- a/examples/hyperspace-roll/src/customise.rs +++ b/examples/hyperspace-roll/src/customise.rs @@ -1,6 +1,6 @@ use agb::{ display::{ - object::{Object, ObjectController}, + object::{OAMManager, Object}, tiled::{RegularMap, TiledMap}, HEIGHT, WIDTH, }, @@ -91,10 +91,10 @@ fn move_net_position_ud(idx: usize, direction: Tri) -> usize { } } -fn create_dice_display<'a>(gfx: &'a ObjectController, dice: &'_ PlayerDice) -> Vec> { +fn create_dice_display<'a>(gfx: &'a OAMManager, dice: &'_ PlayerDice) -> Vec> { let mut objects = Vec::new(); for (idx, dice) in dice.dice.iter().enumerate() { - let mut obj = gfx.object(gfx.sprite(FACE_SPRITES.sprite_for_face(dice.faces[1]))); + let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(dice.faces[1])); obj.set_x((idx as i32 * 32 - 24 / 2 + 20) as u16); obj.set_y(16 - 24 / 2); @@ -105,10 +105,10 @@ fn create_dice_display<'a>(gfx: &'a ObjectController, dice: &'_ PlayerDice) -> V objects } -fn create_net<'a>(gfx: &'a ObjectController, die: &'_ Die, modified: &[usize]) -> Vec> { +fn create_net<'a>(gfx: &'a OAMManager, die: &'_ Die, modified: &[usize]) -> Vec> { let mut objects = Vec::new(); for (idx, &face) in die.faces.iter().enumerate() { - let mut obj = gfx.object(gfx.sprite(FACE_SPRITES.sprite_for_face(face))); + let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(face)); let (x, y) = screen_position_for_index(idx); obj.set_x((x - 24 / 2) as u16); obj.set_y((y - 24 / 2) as u16); @@ -119,7 +119,7 @@ fn create_net<'a>(gfx: &'a ObjectController, die: &'_ Die, modified: &[usize]) - } for &m in modified.iter().chain(core::iter::once(&3)) { - let mut obj = gfx.object(gfx.sprite(MODIFIED_BOX)); + let mut obj = gfx.add_object_static_sprite(MODIFIED_BOX); let (x, y) = screen_position_for_index(m); obj.set_x((x - 32 / 2) as u16); obj.set_y((y - 32 / 2) as u16); @@ -139,10 +139,10 @@ fn upgrade_position(idx: usize) -> (u32, u32) { ) } -fn create_upgrade_objects<'a>(gfx: &'a ObjectController, upgrades: &[Face]) -> Vec> { +fn create_upgrade_objects<'a>(gfx: &'a OAMManager, upgrades: &[Face]) -> Vec> { let mut objects = Vec::new(); for (idx, &upgrade) in upgrades.iter().enumerate() { - let mut obj = gfx.object(gfx.sprite(FACE_SPRITES.sprite_for_face(upgrade))); + let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(upgrade)); let (x, y) = upgrade_position(idx); obj.set_x((x - 24 / 2) as u16); obj.set_y((y - 24 / 2) as u16); @@ -180,13 +180,13 @@ pub(crate) fn customise_screen( let mut input = agb::input::ButtonController::new(); - let mut select_box = agb.obj.object(agb.obj.sprite(SELECT_BOX.sprite(0))); + let mut select_box = agb.obj.add_object_static_sprite(SELECT_BOX.sprite(0)); select_box.show(); - let mut selected_dice = agb.obj.object(agb.obj.sprite(SELECTED_BOX)); + let mut selected_dice = agb.obj.add_object_static_sprite(SELECTED_BOX); selected_dice.hide(); - let mut selected_face = agb.obj.object(agb.obj.sprite(SELECTED_BOX)); + let mut selected_face = agb.obj.add_object_static_sprite(SELECTED_BOX); selected_face.hide(); agb.sfx.frame(); @@ -338,7 +338,10 @@ pub(crate) fn customise_screen( break; } - select_box.set_sprite(agb.obj.sprite(SELECT_BOX.animation_sprite(counter / 10))); + select_box.set_sprite( + agb.obj + .get_vram_sprite(SELECT_BOX.animation_sprite(counter / 10)), + ); agb.star_background.update(); let _ = agb::rng::gen(); diff --git a/examples/hyperspace-roll/src/graphics.rs b/examples/hyperspace-roll/src/graphics.rs index 8a3f54ad..88bad3ee 100644 --- a/examples/hyperspace-roll/src/graphics.rs +++ b/examples/hyperspace-roll/src/graphics.rs @@ -1,5 +1,5 @@ use agb::{ - display::object::{Object, ObjectController, Sprite, Tag}, + display::object::{OAMManager, Object, Sprite, Tag}, fixnum::Vector2D, }; use alloc::vec::Vec; @@ -141,14 +141,12 @@ pub struct HealthBar<'a> { } impl<'a> HealthBar<'a> { - pub fn new(pos: Vector2D, max: usize, obj: &'a ObjectController) -> Self { + pub fn new(pos: Vector2D, max: usize, obj: &'a OAMManager) -> Self { assert_eq!(max % 8, 0); let sprites = (0..(max / 8)) .map(|i| { - let health_sprite = obj.sprite(SMALL_SPRITES.red_bar(0)); - - let mut health_object = obj.object(health_sprite); + let mut health_object = obj.add_object_static_sprite(SMALL_SPRITES.red_bar(0)); health_object .set_position(pos + (i as i32 * 8, 0).into()) .show(); @@ -159,16 +157,18 @@ impl<'a> HealthBar<'a> { Self { max, sprites } } - pub fn set_value(&mut self, new_value: usize, obj: &'a ObjectController) { + pub fn set_value(&mut self, new_value: usize, obj: &'a OAMManager) { assert!(new_value <= self.max); for (i, sprite) in self.sprites.iter_mut().enumerate() { if (i + 1) * 8 < new_value { - sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(0))); + sprite.set_sprite(obj.get_vram_sprite(SMALL_SPRITES.red_bar(0))); } else if i * 8 < new_value { - sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8)))); + sprite.set_sprite( + obj.get_vram_sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8))), + ); } else { - sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(8))); + sprite.set_sprite(obj.get_vram_sprite(SMALL_SPRITES.red_bar(8))); } } } @@ -195,22 +195,22 @@ pub struct FractionDisplay<'a> { } impl<'a> FractionDisplay<'a> { - pub fn new(pos: Vector2D, digits: usize, obj: &'a ObjectController) -> Self { + pub fn new(pos: Vector2D, digits: usize, obj: &'a OAMManager) -> Self { let mut sprites = Vec::with_capacity(digits * 2 + 1); for i in 0..digits { - let mut left_digit = obj.object(obj.sprite(SMALL_SPRITES.number(0))); + let mut left_digit = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); left_digit.set_position(pos + (i as i32 * 4, 0).into()); sprites.push(left_digit); - let mut right_digit = obj.object(obj.sprite(SMALL_SPRITES.number(0))); + let mut right_digit = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); right_digit.set_position(pos + (i as i32 * 4 + digits as i32 * 4 + 7, 0).into()); sprites.push(right_digit); } - let mut slash = obj.object(obj.sprite(SMALL_SPRITES.slash())); + let mut slash = obj.add_object_static_sprite(SMALL_SPRITES.slash()); slash.set_position(pos + (digits as i32 * 4 + 1, 0).into()); sprites.push(slash); @@ -222,7 +222,7 @@ impl<'a> FractionDisplay<'a> { } } - pub fn set_value(&mut self, current: usize, max: usize, obj: &'a ObjectController) { + pub fn set_value(&mut self, current: usize, max: usize, obj: &'a OAMManager) { if self.current_current == current && self.current_max == max { return; } @@ -235,12 +235,13 @@ impl<'a> FractionDisplay<'a> { current /= 10; let current_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 2]; current_value_sprite - .set_sprite(obj.sprite(SMALL_SPRITES.number(current_value_digit as u32))); + .set_sprite(obj.get_vram_sprite(SMALL_SPRITES.number(current_value_digit as u32))); let max_value_digit = max % 10; max /= 10; let max_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 1]; - max_value_sprite.set_sprite(obj.sprite(SMALL_SPRITES.number(max_value_digit as u32))); + max_value_sprite + .set_sprite(obj.get_vram_sprite(SMALL_SPRITES.number(max_value_digit as u32))); } } } @@ -260,7 +261,7 @@ impl<'a> NumberDisplay<'a> { } } - pub fn set_value(&mut self, new_value: Option, obj: &'a ObjectController) { + pub fn set_value(&mut self, new_value: Option, obj: &'a OAMManager) { if self.value == new_value { return; } @@ -271,7 +272,7 @@ impl<'a> NumberDisplay<'a> { if let Some(mut new_value) = new_value { if new_value == 0 { - let mut zero_object = obj.object(obj.sprite(SMALL_SPRITES.number(0))); + let mut zero_object = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); zero_object.show().set_position(self.position); self.objects.push(zero_object); @@ -284,7 +285,7 @@ impl<'a> NumberDisplay<'a> { new_value /= 10; let mut current_value_obj = - obj.object(obj.sprite(SMALL_SPRITES.number(current_value_digit))); + obj.add_object_static_sprite(SMALL_SPRITES.number(current_value_digit)); current_value_obj .show() diff --git a/examples/hyperspace-roll/src/lib.rs b/examples/hyperspace-roll/src/lib.rs index 793eed6c..ff0bc020 100644 --- a/examples/hyperspace-roll/src/lib.rs +++ b/examples/hyperspace-roll/src/lib.rs @@ -12,7 +12,7 @@ #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -use agb::display::object::ObjectController; +use agb::display::object::OAMManager; use agb::display::tiled::{TileFormat, TiledMap, VRamManager}; use agb::display::Priority; use agb::interrupt::VBlank; @@ -90,7 +90,7 @@ pub struct PlayerDice { } struct Agb<'a> { - obj: ObjectController<'a>, + obj: OAMManager<'a>, vblank: VBlank, star_background: StarBackground<'a>, vram: VRamManager, @@ -104,7 +104,7 @@ pub fn main(mut gba: agb::Gba) -> ! { 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_managed(); let vblank = agb::interrupt::VBlank::get(); let (tiled, mut vram) = gba.display.video.tiled0(); diff --git a/examples/the-hat-chooses-the-wizard/Cargo.lock b/examples/the-hat-chooses-the-wizard/Cargo.lock index 44a85826..1ebec2ad 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.lock +++ b/examples/the-hat-chooses-the-wizard/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", + "slotmap", ] [[package]] @@ -438,6 +439,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index 223f067d..9acef75a 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -2,7 +2,7 @@ use crate::TAG_MAP; use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use agb::{ - display::object::{ObjectController, Tag}, + display::object::{OAMManager, Tag}, fixnum::Vector2D, }; @@ -35,11 +35,11 @@ pub enum EnemyUpdateState { } impl<'a> Enemy<'a> { - pub fn new_slime(object: &'a ObjectController, start_pos: Vector2D) -> Self { + pub fn new_slime(object: &'a OAMManager, start_pos: Vector2D) -> Self { Enemy::Slime(Slime::new(object, start_pos + (0, 1).into())) } - pub fn new_snail(object: &'a ObjectController, start_pos: Vector2D) -> Self { + pub fn new_snail(object: &'a OAMManager, start_pos: Vector2D) -> Self { Enemy::Snail(Snail::new(object, start_pos)) } @@ -52,7 +52,7 @@ impl<'a> Enemy<'a> { pub fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -94,7 +94,7 @@ struct EnemyInfo<'a> { impl<'a> EnemyInfo<'a> { fn new( - object: &'a ObjectController, + object: &'a OAMManager, start_pos: Vector2D, collision: Vector2D, ) -> Self { @@ -135,7 +135,7 @@ pub struct Slime<'a> { } impl<'a> Slime<'a> { - fn new(object: &'a ObjectController, start_pos: Vector2D) -> Self { + fn new(object: &'a OAMManager, start_pos: Vector2D) -> Self { let slime = Slime { enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()), state: SlimeState::Idle, @@ -146,7 +146,7 @@ impl<'a> Slime<'a> { fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -161,7 +161,7 @@ impl<'a> Slime<'a> { let offset = (timer / 16) as usize; let frame = SLIME_IDLE.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -201,7 +201,7 @@ impl<'a> Slime<'a> { self.state = SlimeState::Idle; } else { let frame = SLIME_JUMP.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -227,7 +227,7 @@ impl<'a> Slime<'a> { } let frame = SLIME_SPLAT.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -257,7 +257,7 @@ pub struct Snail<'a> { } impl<'a> Snail<'a> { - fn new(object: &'a ObjectController, start_pos: Vector2D) -> Self { + fn new(object: &'a OAMManager, start_pos: Vector2D) -> Self { let snail = Snail { enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()), state: SnailState::Idle(0), @@ -272,7 +272,7 @@ impl<'a> Snail<'a> { fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -298,7 +298,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_IDLE.animation_sprite(0); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); if player_has_collided { @@ -318,7 +318,7 @@ impl<'a> Snail<'a> { self.enemy_info.entity.velocity = (0, 0).into(); let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -340,7 +340,7 @@ impl<'a> Snail<'a> { let offset = (timer - time) as usize / 8; let frame = SNAIL_MOVE.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -374,7 +374,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); @@ -403,7 +403,7 @@ impl<'a> Snail<'a> { return UpdateState::Remove; }; - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index cfd98e30..57117e3d 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -8,7 +8,7 @@ extern crate alloc; use agb::{ display::{ - object::{Graphics, Object, ObjectController, Tag, TagMap}, + object::{Graphics, OAMManager, Object, Tag, TagMap}, tiled::{ InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet, TileSetting, TiledMap, VRamManager, @@ -124,12 +124,11 @@ pub struct Entity<'a> { } impl<'a> Entity<'a> { - pub fn new(object: &'a ObjectController, collision_mask: Vector2D) -> Self { - let dummy_sprite = object.sprite(WALKING.sprite(0)); - let mut sprite = object.object(dummy_sprite); - sprite.set_priority(Priority::P1); + pub fn new(object: &'a OAMManager, collision_mask: Vector2D) -> Self { + let mut dummy_object = object.add_object_static_sprite(WALKING.sprite(0)); + dummy_object.set_priority(Priority::P1); Entity { - sprite, + sprite: dummy_object, collision_mask, position: (0, 0).into(), velocity: (0, 0).into(), @@ -348,15 +347,15 @@ fn ping_pong(i: i32, n: i32) -> i32 { } impl<'a> Player<'a> { - fn new(controller: &'a ObjectController, start_position: Vector2D) -> Self { + fn new(controller: &'a OAMManager, start_position: Vector2D) -> Self { let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); wizard .sprite - .set_sprite(controller.sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.get_vram_sprite(HAT_SPIN_1.sprite(0))); hat.sprite - .set_sprite(controller.sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.get_vram_sprite(HAT_SPIN_1.sprite(0))); wizard.sprite.show(); hat.sprite.show(); @@ -382,7 +381,7 @@ impl<'a> Player<'a> { fn update_frame( &mut self, input: &ButtonController, - controller: &'a ObjectController, + controller: &'a OAMManager, timer: i32, level: &Level, enemies: &[enemies::Enemy], @@ -461,7 +460,7 @@ impl<'a> Player<'a> { self.wizard_frame = offset as u8; let frame = WALKING.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -471,7 +470,7 @@ impl<'a> Player<'a> { self.wizard_frame = 5; let frame = JUMPING.animation_sprite(0); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.wizard.sprite.set_sprite(sprite); } else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 { @@ -486,7 +485,7 @@ impl<'a> Player<'a> { self.wizard_frame = 0; let frame = FALLING.animation_sprite(offset); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -513,13 +512,13 @@ impl<'a> Player<'a> { self.wizard.sprite.set_hflip(true); self.hat .sprite - .set_sprite(controller.sprite(hat_base_tile.sprite(5))); + .set_sprite(controller.get_vram_sprite(hat_base_tile.sprite(5))); } agb::input::Tri::Positive => { self.wizard.sprite.set_hflip(false); self.hat .sprite - .set_sprite(controller.sprite(hat_base_tile.sprite(0))); + .set_sprite(controller.get_vram_sprite(hat_base_tile.sprite(0))); } _ => {} } @@ -545,7 +544,7 @@ impl<'a> Player<'a> { let hat_sprite_offset = (timer / hat_sprite_divider) as usize; self.hat.sprite.set_sprite( - controller.sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), + controller.get_vram_sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), ); if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() { @@ -578,7 +577,7 @@ impl<'a> Player<'a> { } HatState::WizardTowards => { self.hat.sprite.set_sprite( - controller.sprite(hat_base_tile.animation_sprite(timer as usize / 2)), + controller.get_vram_sprite(hat_base_tile.animation_sprite(timer as usize / 2)), ); let distance_vector = self.hat.position - self.wizard.position + hat_resting_position; @@ -616,7 +615,7 @@ enum UpdateState { impl<'a, 'b> PlayingLevel<'a, 'b> { fn open_level( level: &'a Level, - object_control: &'a ObjectController, + object_control: &'a OAMManager, background: &'a mut InfiniteScrolledMap<'b>, foreground: &'a mut InfiniteScrolledMap<'b>, input: ButtonController, @@ -677,11 +676,11 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { self.player.wizard.sprite.set_priority(Priority::P0); } - fn dead_update(&mut self, controller: &'a ObjectController) -> bool { + fn dead_update(&mut self, controller: &'a OAMManager) -> bool { self.timer += 1; let frame = PLAYER_DEATH.animation_sprite(self.timer as usize / 8); - let sprite = controller.sprite(frame); + let sprite = controller.get_vram_sprite(frame); self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); self.player.wizard.position += self.player.wizard.velocity; @@ -696,7 +695,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { &mut self, sfx_player: &mut SfxPlayer, vram: &mut VRamManager, - controller: &'a ObjectController, + controller: &'a OAMManager, ) -> UpdateState { self.timer += 1; self.input.update(); @@ -828,7 +827,7 @@ pub fn main(mut agb: agb::Gba) -> ! { vram.set_background_palettes(tile_sheet::PALETTES); - let object = agb.display.object.get(); + let object = agb.display.object.get_managed(); let vblank = agb::interrupt::VBlank::get(); let mut current_level = 0; diff --git a/examples/the-purple-night/Cargo.lock b/examples/the-purple-night/Cargo.lock index 47cd52a5..d90f423c 100644 --- a/examples/the-purple-night/Cargo.lock +++ b/examples/the-purple-night/Cargo.lock @@ -26,6 +26,7 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", + "slotmap", ] [[package]] @@ -457,6 +458,15 @@ dependencies = [ "serde", ] +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 4325f879..9d08bf28 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -14,7 +14,7 @@ use alloc::{boxed::Box, vec::Vec}; use agb::{ display::{ - object::{Graphics, Object, ObjectController, Sprite, Tag, TagMap}, + object::{Graphics, OAMManager, Object, Sprite, Tag, TagMap}, tiled::{ InfiniteScrolledMap, RegularBackgroundSize, TileFormat, TileSet, TileSetting, VRamManager, @@ -164,9 +164,8 @@ struct Entity<'a> { } impl<'a> Entity<'a> { - fn new(object_controller: &'a ObjectController, collision_mask: Rect) -> Self { - let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0)); - let mut sprite = object_controller.object(s); + fn new(object_controller: &'a OAMManager, collision_mask: Rect) -> Self { + let mut sprite = object_controller.add_object_static_sprite(LONG_SWORD_IDLE.sprite(0)); sprite.set_priority(Priority::P1); Entity { sprite, @@ -533,12 +532,12 @@ struct Player<'a> { } impl<'a> Player<'a> { - fn new(object_controller: &'a ObjectController<'a>) -> Player { + fn new(object_controller: &'a OAMManager<'a>) -> Player { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), ); - let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0)); + let s = object_controller.get_vram_sprite(LONG_SWORD_IDLE.sprite(0)); entity.sprite.set_sprite(s); entity.sprite.show(); entity.position = (144, 0).into(); @@ -559,7 +558,7 @@ impl<'a> Player<'a> { fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, buttons: &ButtonController, level: &Level, sfx: &mut sfx::Sfx, @@ -590,12 +589,12 @@ impl<'a> Player<'a> { self.entity.sprite.set_hflip(self.facing == Tri::Negative); self.entity.velocity.x += self.sword.ground_walk_force() * x as i32; if self.entity.velocity.x.abs() > Number::new(1) / 10 { - let sprite = - controller.sprite(self.sword.walk_animation(self.sprite_offset)); + let sprite = controller + .get_vram_sprite(self.sword.walk_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } else { - let sprite = - controller.sprite(self.sword.idle_animation(self.sprite_offset)); + let sprite = controller + .get_vram_sprite(self.sword.idle_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } @@ -615,7 +614,8 @@ impl<'a> Player<'a> { let frame = self.sword.attack_frame(*a); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; let tag = self.sword.attack_tag(); - let sprite = controller.sprite(tag.animation_sprite(frame as usize)); + let sprite = + controller.get_vram_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.ground_attack_hurtbox(frame); @@ -629,7 +629,8 @@ impl<'a> Player<'a> { let frame = self.sword.hold_frame(); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; let tag = self.sword.attack_tag(); - let sprite = controller.sprite(tag.animation_sprite(frame as usize)); + let sprite = + controller.get_vram_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if *a == 0 { self.attack_timer = AttackTimer::Idle; @@ -654,7 +655,8 @@ impl<'a> Player<'a> { 2 }; let tag = self.sword.jump_tag(); - let sprite = controller.sprite(tag.animation_sprite(frame as usize)); + let sprite = + controller.get_vram_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if x != Tri::Zero { @@ -676,7 +678,8 @@ impl<'a> Player<'a> { *a -= 1; let frame = self.sword.jump_attack_frame(*a); let tag = self.sword.jump_attack_tag(); - let sprite = controller.sprite(tag.animation_sprite(frame as usize)); + let sprite = + controller.get_vram_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.air_attack_hurtbox(frame); @@ -811,7 +814,7 @@ impl BatData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -839,7 +842,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 8); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -874,7 +877,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 2); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -901,7 +904,7 @@ impl BatData { BatState::Dead => { const BAT_DEAD: &Tag = TAG_MAP.get("bat dead"); let sprite = BAT_DEAD.sprite(0); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -944,7 +947,7 @@ impl SlimeData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -969,7 +972,7 @@ impl SlimeData { const IDLE: &Tag = TAG_MAP.get("slime idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1009,7 +1012,7 @@ impl SlimeData { const CHASE: &Tag = TAG_MAP.get("Slime jump"); let sprite = CHASE.sprite(frame as usize); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1039,7 +1042,7 @@ impl SlimeData { if *count < 5 * 4 { const DEATH: &Tag = TAG_MAP.get("Slime death"); let sprite = DEATH.sprite(*count as usize / 4); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); *count += 1; @@ -1073,7 +1076,7 @@ impl MiniFlameData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1107,7 +1110,7 @@ impl MiniFlameData { } } else { let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 8); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into(); @@ -1155,7 +1158,7 @@ impl MiniFlameData { } let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 2); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); } MiniFlameState::Dead => { @@ -1167,7 +1170,7 @@ impl MiniFlameData { const DEATH: &Tag = TAG_MAP.get("angry boss dead"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 12); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1202,7 +1205,7 @@ impl EmuData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1228,7 +1231,7 @@ impl EmuData { const IDLE: &Tag = TAG_MAP.get("emu - idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); if (entity.position.y - player.entity.position.y).abs() < 10.into() { @@ -1275,7 +1278,7 @@ impl EmuData { const WALK: &Tag = TAG_MAP.get("emu-walk"); let sprite = WALK.sprite(self.sprite_offset as usize / 2); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); let gravity: Number = 1.into(); @@ -1330,7 +1333,7 @@ impl EmuData { const DEATH: &Tag = TAG_MAP.get("emu - die"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 4); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1375,7 +1378,7 @@ impl EnemyData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1396,11 +1399,11 @@ struct Enemy<'a> { } impl<'a> Enemy<'a> { - fn new(object_controller: &'a ObjectController, enemy_data: EnemyData) -> Self { + fn new(object_controller: &'a OAMManager, enemy_data: EnemyData) -> Self { let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); let sprite = enemy_data.sprite(); - let sprite = object_controller.sprite(sprite); + let sprite = object_controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); entity.sprite.show(); @@ -1410,7 +1413,7 @@ impl<'a> Enemy<'a> { fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -1441,7 +1444,7 @@ impl ParticleData { fn update<'a>( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1454,7 +1457,7 @@ impl ParticleData { const DUST: &Tag = TAG_MAP.get("dust"); let sprite = DUST.sprite(*frame as usize / 3); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1468,7 +1471,7 @@ impl ParticleData { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1494,7 +1497,7 @@ impl ParticleData { ParticleData::BossHealer(frame, target) => { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1529,7 +1532,7 @@ struct Particle<'a> { impl<'a> Particle<'a> { fn new( - object_controller: &'a ObjectController, + object_controller: &'a OAMManager, particle_data: ParticleData, position: Vector2D, ) -> Self { @@ -1548,7 +1551,7 @@ impl<'a> Particle<'a> { fn update( &mut self, - controller: &'a ObjectController, + controller: &'a OAMManager, player: &Player, level: &Level, ) -> UpdateInstruction { @@ -1575,7 +1578,7 @@ impl<'a> BossState<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a ObjectController, + object_controller: &'a OAMManager, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1610,7 +1613,7 @@ struct FollowingBoss<'a> { } impl<'a> FollowingBoss<'a> { - fn new(object_controller: &'a ObjectController, position: Vector2D) -> Self { + fn new(object_controller: &'a OAMManager, position: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()), @@ -1625,7 +1628,7 @@ impl<'a> FollowingBoss<'a> { gone: false, } } - fn update(&mut self, controller: &'a ObjectController, player: &Player) { + fn update(&mut self, controller: &'a OAMManager, player: &Player) { let difference = player.entity.position - self.entity.position; self.timer += 1; @@ -1658,7 +1661,7 @@ impl<'a> FollowingBoss<'a> { const BOSS: &Tag = TAG_MAP.get("happy boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = controller.sprite(sprite); + let sprite = controller.get_vram_sprite(sprite); self.entity.sprite.set_sprite(sprite); @@ -1694,7 +1697,7 @@ enum BossInstruction { } impl<'a> Boss<'a> { - fn new(object_controller: &'a ObjectController, screen_coords: Vector2D) -> Self { + fn new(object_controller: &'a OAMManager, screen_coords: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()), @@ -1713,7 +1716,7 @@ impl<'a> Boss<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a ObjectController, + object_controller: &'a OAMManager, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1794,7 +1797,7 @@ impl<'a> Boss<'a> { const BOSS: &Tag = TAG_MAP.get("Boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = object_controller.sprite(sprite); + let sprite = object_controller.get_vram_sprite(sprite); self.entity.sprite.set_sprite(sprite); @@ -1817,7 +1820,7 @@ impl<'a> Boss<'a> { self.entity .commit_with_size(offset + shake, (32, 32).into()); } - fn explode(&self, enemies: &mut Arena>, object_controller: &'a ObjectController) { + fn explode(&self, enemies: &mut Arena>, object_controller: &'a OAMManager) { for _ in 0..(6 - self.health) { let x_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; let y_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; @@ -1891,7 +1894,7 @@ impl<'a> Game<'a> { fn advance_frame( &mut self, - object_controller: &'a ObjectController, + object_controller: &'a OAMManager, vram: &mut VRamManager, sfx: &mut sfx::Sfx, ) -> GameStatus { @@ -2105,7 +2108,7 @@ impl<'a> Game<'a> { } } - fn load_enemies(&mut self, object_controller: &'a ObjectController) { + fn load_enemies(&mut self, object_controller: &'a OAMManager) { if self.slime_load < self.level.slime_spawns.len() { for (idx, slime_spawn) in self .level @@ -2175,7 +2178,7 @@ impl<'a> Game<'a> { vram.set_background_palettes(&modified_palettes); } - fn new(object: &'a ObjectController, level: Level<'a>, start_at_boss: bool) -> Self { + fn new(object: &'a OAMManager, level: Level<'a>, start_at_boss: bool) -> Self { let mut player = Player::new(object); let mut offset = (8, 8).into(); if start_at_boss { @@ -2222,7 +2225,7 @@ fn game_with_level(gba: &mut agb::Gba) { let tileset = TileSet::new(background::background.tiles, TileFormat::FourBpp); - let object = gba.display.object.get(); + let object = gba.display.object.get_managed(); let backdrop = InfiniteScrolledMap::new( background.background( From f86bf9c07a7d53daf5e81dff0db07af83484a4a7 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 01:53:04 +0100 Subject: [PATCH 04/39] woah there, that shouldn't be public! --- agb/src/display/object/managed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 9d99ae2b..bdc62002 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -155,7 +155,7 @@ impl OAMManager<'_> { } } - pub fn do_work_with_sprite_loader(&self, c: C) -> T + fn do_work_with_sprite_loader(&self, c: C) -> T where C: Fn(&mut StaticSpriteLoader) -> T, { From 82b68fc335ccb72efa9aa1855e7536304359c957 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 01:54:40 +0100 Subject: [PATCH 05/39] mark function that can be used unsafely unsafe --- agb/src/display/object/managed.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index bdc62002..dd9e3d04 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -155,7 +155,9 @@ impl OAMManager<'_> { } } - fn do_work_with_sprite_loader(&self, c: C) -> T + /// SAFETY: + /// Do not reenter or recurse or otherwise use sprite loader cell during this. + unsafe fn do_work_with_sprite_loader(&self, c: C) -> T where C: Fn(&mut StaticSpriteLoader) -> T, { @@ -184,7 +186,10 @@ impl OAMManager<'_> { // finished OAM interactions self.object_store.remove_all_in_removal_list(); - self.do_work_with_sprite_loader(StaticSpriteLoader::garbage_collect); + // safety: not reentrant + unsafe { + self.do_work_with_sprite_loader(StaticSpriteLoader::garbage_collect); + } } pub fn add_object(&self, sprite: SpriteVram) -> Object<'_> { @@ -193,7 +198,10 @@ impl OAMManager<'_> { } pub fn get_vram_sprite(&self, sprite: &'static Sprite) -> SpriteVram { - self.do_work_with_sprite_loader(|sprite_loader| sprite_loader.get_vram_sprite(sprite)) + // safety: not reentrant + unsafe { + self.do_work_with_sprite_loader(|sprite_loader| sprite_loader.get_vram_sprite(sprite)) + } } pub fn add_object_static_sprite(&self, sprite: &'static Sprite) -> Object<'_> { From d8f0d78d4b3040ed4408df46c6f7c6f068f310de Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 02:20:13 +0100 Subject: [PATCH 06/39] unsafe is unsafe --- agb/src/display/object/managed.rs | 41 +++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index dd9e3d04..58000a7e 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -357,75 +357,90 @@ impl Object<'_> { self } - fn object(&mut self) -> &mut UnmanagedObject { + /// Safety: + /// Only have *ONE* of these at a time, do not call any functions that modify the slot map while having this. + unsafe fn object(&mut self) -> &mut UnmanagedObject { unsafe { &mut *self.store.get_object(self.me).object.get() } } - fn object_shared(&self) -> &UnmanagedObject { + /// Safety: + /// Don't have a mutable one of these while having one of these, do not call any functions that modify the slot map while having this. + unsafe fn object_shared(&self) -> &UnmanagedObject { unsafe { &*self.store.get_object(self.me).object.get() } } #[must_use] pub fn is_visible(&self) -> bool { - self.object_shared().is_visible() + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object_shared() }.is_visible() } pub fn show(&mut self) -> &mut Self { - self.object().show(); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().show() }; self } pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { - self.object().show_affine(affine_mode); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().show_affine(affine_mode) }; self } pub fn set_hflip(&mut self, flip: bool) -> &mut Self { - self.object().set_hflip(flip); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_hflip(flip) }; self } pub fn set_vflip(&mut self, flip: bool) -> &mut Self { - self.object().set_vflip(flip); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_vflip(flip) }; self } pub fn set_x(&mut self, x: u16) -> &mut Self { - self.object().set_x(x); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_x(x) }; self } pub fn set_priority(&mut self, priority: Priority) -> &mut Self { - self.object().set_priority(priority); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_priority(priority) }; self } pub fn hide(&mut self) -> &mut Self { - self.object().hide(); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().hide() }; self } pub fn set_y(&mut self, y: u16) -> &mut Self { - self.object().set_y(y); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_y(y) }; self } pub fn set_position(&mut self, position: Vector2D) -> &mut Self { - self.object().set_position(position); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_position(position) }; self } pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { - self.object().set_sprite(sprite); + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_sprite(sprite) }; self } From 57f24acdf9cfacd3f1de144e6a2cf9b2e4660d17 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 02:43:18 +0100 Subject: [PATCH 07/39] use drop of iterator to clear rest of objects --- agb/src/display/object/managed.rs | 4 -- agb/src/display/object/unmanaged/object.rs | 45 +++++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 58000a7e..569019d1 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -167,8 +167,6 @@ impl OAMManager<'_> { } pub fn commit(&self) { - let mut count = 0; - let mut unmanaged = UnmanagedOAM::new(); // do interactions with OAM @@ -179,9 +177,7 @@ impl OAMManager<'_> { .zip(unmanaged.iter()) { slot.set(object); - count += 1; } - unmanaged.clear_from(count); // finished OAM interactions diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index ef50b9c9..efca61a1 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -1,4 +1,7 @@ -use core::{cell::UnsafeCell, marker::PhantomData}; +use core::{ + cell::{Cell, UnsafeCell}, + marker::PhantomData, +}; use agb_fixnum::Vector2D; @@ -11,16 +14,17 @@ use super::attributes::{AffineMode, Attributes}; pub struct UnmanagedOAM<'gba> { phantom: PhantomData<&'gba ()>, + up_to: Cell, } pub struct OAMIterator<'oam> { - phantom: PhantomData<&'oam ()>, index: usize, + up_to: &'oam Cell, } pub struct OAMSlot<'oam> { - phantom: PhantomData<&'oam ()>, slot: usize, + up_to: &'oam Cell, } impl OAMSlot<'_> { @@ -31,6 +35,8 @@ impl OAMSlot<'_> { let sprites = unsafe { &mut *object.sprites.get() }; sprites.previous_sprite = Some(sprites.sprite.clone()); + + self.up_to.set(self.slot as i32); } fn set_bytes(&mut self, bytes: [u8; 6]) { @@ -52,39 +58,42 @@ impl<'oam> Iterator for OAMIterator<'oam> { None } else { Some(OAMSlot { - phantom: PhantomData, slot: idx, + up_to: self.up_to, }) } } } +impl Drop for OAMIterator<'_> { + fn drop(&mut self) { + let last_written = self.up_to.get(); + + let number_writen = (last_written + 1) as usize; + + for idx in number_writen..128 { + unsafe { + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(idx * 4); + ptr.write_volatile(0b10 << 8); + } + } + } +} + impl UnmanagedOAM<'_> { pub fn iter(&mut self) -> OAMIterator<'_> { OAMIterator { - phantom: PhantomData, index: 0, + up_to: &mut self.up_to, } } pub(crate) fn new() -> Self { Self { + up_to: Cell::new(0), phantom: PhantomData, } } - - pub fn clear_from(&self, from: usize) { - if from >= 128 { - return; - } - - for i in from..128 { - unsafe { - let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 4); - ptr.write_volatile(0b10 << 8); - } - } - } } #[derive(Debug)] From 6007b95926d9b14f6a4c8090ff29c51b916b9fa2 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 02:43:27 +0100 Subject: [PATCH 08/39] deallocate palettes --- agb/src/display/object/sprites/sprite_allocator.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index 207186ac..caa5e38b 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -81,6 +81,12 @@ struct PaletteVramData { location: Location, } +impl Drop for PaletteVramData { + fn drop(&mut self) { + unsafe { PALETTE_ALLOCATOR.dealloc(self.location.as_palette_ptr(), Palette16::layout()) } + } +} + #[derive(Debug)] pub struct PaletteVram { data: Rc, From e0af614cac628786b0c610b1b35f40898121bd3b Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 02:44:58 +0100 Subject: [PATCH 09/39] correctly initialise as not visible --- agb/src/display/object/unmanaged/attributes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index 69575c4b..4e4bb3a2 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -17,7 +17,7 @@ pub struct Attributes { impl Default for Attributes { fn default() -> Self { Self { - a0: ObjectAttribute0::from_bytes([0b01, 0]), + a0: ObjectAttribute0::from_bytes([0, 0b10]), a1s: Default::default(), a1a: Default::default(), a2: Default::default(), From e6937bbb619453d6502b193726114850116507e1 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 02:50:12 +0100 Subject: [PATCH 10/39] be able to clear sprite 0 on drop --- agb/src/display/object/unmanaged/object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index efca61a1..c79bf24c 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -90,7 +90,7 @@ impl UnmanagedOAM<'_> { pub(crate) fn new() -> Self { Self { - up_to: Cell::new(0), + up_to: Cell::new(-1), phantom: PhantomData, } } From 63e87b046bbe92d1a82e89463707bedcdf55b416 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 12:06:51 +0100 Subject: [PATCH 11/39] correctly initialise before iteration --- agb/src/display/object/unmanaged/object.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index c79bf24c..1b7d68cd 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -82,9 +82,10 @@ impl Drop for OAMIterator<'_> { impl UnmanagedOAM<'_> { pub fn iter(&mut self) -> OAMIterator<'_> { + self.up_to.set(-1); OAMIterator { index: 0, - up_to: &mut self.up_to, + up_to: &self.up_to, } } From 26b9a50e4ed76b190d13a92dc1bccc9b03860fb8 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 17:54:15 +0100 Subject: [PATCH 12/39] manage sprites better --- agb/src/display/object/managed.rs | 32 +++-------- agb/src/display/object/unmanaged/object.rs | 64 +++++++++++++--------- 2 files changed, 44 insertions(+), 52 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 569019d1..0e7eebe0 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -27,7 +27,6 @@ struct ObjectItem { struct Store { store: UnsafeCell>, - removal_list: UnsafeCell>, first_z: Cell>, } @@ -113,45 +112,32 @@ impl Store { } fn remove_object(&self, object: ObjectKey) { + remove_from_linked_list(self, object); + let data = unsafe { &mut *self.store.get() }; data.remove(object); } - fn remove_all_in_removal_list(&self) { - let removal_list = unsafe { &mut *self.removal_list.get() }; - for object in removal_list.drain(..) { - self.remove_object(object); - } - } - - fn mark_for_removal(&self, object: ObjectKey) { - let removal_list = unsafe { &mut *self.removal_list.get() }; - removal_list.push(object); - - remove_from_linked_list(self, object); - } - fn get_object(&self, key: ObjectKey) -> &ObjectItem { &(unsafe { &*self.store.get() }[key]) } } pub struct OAMManager<'gba> { - phantom: PhantomData<&'gba ()>, object_store: Store, sprite_loader: UnsafeCell, + unmanaged: UnsafeCell>, } impl OAMManager<'_> { pub(crate) fn new() -> Self { Self { - phantom: PhantomData, object_store: Store { store: UnsafeCell::new(SlotMap::with_key()), - removal_list: UnsafeCell::new(Vec::new()), first_z: Cell::new(None), }, sprite_loader: UnsafeCell::new(StaticSpriteLoader::new()), + unmanaged: UnsafeCell::new(UnmanagedOAM::new()), } } @@ -167,9 +153,8 @@ impl OAMManager<'_> { } pub fn commit(&self) { - let mut unmanaged = UnmanagedOAM::new(); - - // do interactions with OAM + // safety: commit is not reentrant + let unmanaged = unsafe { &mut *self.unmanaged.get() }; for (object, mut slot) in unsafe { self.object_store.iter() } .map(|item| unsafe { &*item.object.get() }) @@ -179,9 +164,6 @@ impl OAMManager<'_> { slot.set(object); } - // finished OAM interactions - - self.object_store.remove_all_in_removal_list(); // safety: not reentrant unsafe { self.do_work_with_sprite_loader(StaticSpriteLoader::garbage_collect); @@ -212,7 +194,7 @@ pub struct Object<'controller> { impl Drop for Object<'_> { fn drop(&mut self) { - self.store.mark_for_removal(self.me); + self.store.remove_object(self.me); } } diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 1b7d68cd..e10161d3 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -1,9 +1,7 @@ -use core::{ - cell::{Cell, UnsafeCell}, - marker::PhantomData, -}; +use core::{cell::UnsafeCell, marker::PhantomData}; use agb_fixnum::Vector2D; +use alloc::vec::Vec; use crate::display::{ object::{sprites::SpriteVram, OBJECT_ATTRIBUTE_MEMORY}, @@ -12,31 +10,41 @@ use crate::display::{ use super::attributes::{AffineMode, Attributes}; +#[derive(Default, Debug)] +struct OamFrameModifyables { + up_to: i32, + this_frame_sprites: Vec, +} + pub struct UnmanagedOAM<'gba> { phantom: PhantomData<&'gba ()>, - up_to: Cell, + frame_data: UnsafeCell, + previous_frame_sprites: Vec, } pub struct OAMIterator<'oam> { index: usize, - up_to: &'oam Cell, + frame_data: &'oam UnsafeCell, } pub struct OAMSlot<'oam> { slot: usize, - up_to: &'oam Cell, + frame_data: &'oam UnsafeCell, } impl OAMSlot<'_> { pub fn set(&mut self, object: &UnmanagedObject) { self.set_bytes(object.attributes.bytes()); + // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. + let frame_data = unsafe { &mut *self.frame_data.get() }; + // SAFETY: This is called here and in set_sprite, neither of which call the other. - let sprites = unsafe { &mut *object.sprites.get() }; + let sprite = unsafe { &mut *object.sprites.get() }; - sprites.previous_sprite = Some(sprites.sprite.clone()); + frame_data.this_frame_sprites.push(sprite.clone()); - self.up_to.set(self.slot as i32); + frame_data.up_to = self.slot as i32; } fn set_bytes(&mut self, bytes: [u8; 6]) { @@ -59,7 +67,7 @@ impl<'oam> Iterator for OAMIterator<'oam> { } else { Some(OAMSlot { slot: idx, - up_to: self.up_to, + frame_data: self.frame_data, }) } } @@ -67,7 +75,7 @@ impl<'oam> Iterator for OAMIterator<'oam> { impl Drop for OAMIterator<'_> { fn drop(&mut self) { - let last_written = self.up_to.get(); + let last_written = unsafe { &*self.frame_data.get() }.up_to; let number_writen = (last_written + 1) as usize; @@ -82,31 +90,36 @@ impl Drop for OAMIterator<'_> { impl UnmanagedOAM<'_> { pub fn iter(&mut self) -> OAMIterator<'_> { - self.up_to.set(-1); + let frame_data = self.frame_data.get_mut(); + frame_data.up_to = -1; + + // We drain the previous frame sprites here to reuse the Vecs allocation and remove the now unused sprites. + // Any sprites currently being shown will now be put in the new Vec. + self.previous_frame_sprites.drain(..); + core::mem::swap( + &mut frame_data.this_frame_sprites, + &mut self.previous_frame_sprites, + ); + OAMIterator { index: 0, - up_to: &self.up_to, + frame_data: &self.frame_data, } } pub(crate) fn new() -> Self { Self { - up_to: Cell::new(-1), + frame_data: Default::default(), phantom: PhantomData, + previous_frame_sprites: Default::default(), } } } -#[derive(Debug)] -struct VramSprites { - sprite: SpriteVram, - previous_sprite: Option, -} - #[derive(Debug)] pub struct UnmanagedObject { attributes: Attributes, - sprites: UnsafeCell, + sprites: UnsafeCell, } impl UnmanagedObject { @@ -118,10 +131,7 @@ impl UnmanagedObject { let mut sprite = Self { attributes: Attributes::default(), - sprites: UnsafeCell::new(VramSprites { - sprite, - previous_sprite: None, - }), + sprites: UnsafeCell::new(sprite), }; sprite.attributes.set_sprite(sprite_location, shape, size); @@ -204,7 +214,7 @@ impl UnmanagedObject { // SAFETY: This is called here and in OAMSlot set, neither of which call the other. let sprites = unsafe { &mut *self.sprites.get() }; - sprites.sprite = sprite; + *sprites = sprite; self } From 8d23af010f288ee73e47208b6255a7e1e7e109d1 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 18:02:05 +0100 Subject: [PATCH 13/39] remove unused import --- agb/examples/chicken.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index e0fb6757..06a3e4e8 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -4,7 +4,7 @@ use agb::{ display::tiled::{TileFormat, TileSet, TileSetting, TiledMap}, display::{ - object::{OAMManager, Object, Size, Sprite, StaticSpriteLoader}, + object::{OAMManager, Object, Size, Sprite}, palette16::Palette16, tiled::RegularBackgroundSize, HEIGHT, WIDTH, From 63088f29078ac5033fde46130eb1e9da6a41759a Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 20:14:05 +0100 Subject: [PATCH 14/39] affine matricies! --- agb/src/display/affine.rs | 9 ++ agb/src/display/object.rs | 4 + agb/src/display/object/affine.rs | 115 ++++++++++++++++++ agb/src/display/object/managed.rs | 17 ++- .../display/object/unmanaged/attributes.rs | 2 +- agb/src/display/object/unmanaged/object.rs | 36 ++++-- agb/src/lib.rs | 1 + 7 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 agb/src/display/object/affine.rs diff --git a/agb/src/display/affine.rs b/agb/src/display/affine.rs index 38412619..fa1b269c 100644 --- a/agb/src/display/affine.rs +++ b/agb/src/display/affine.rs @@ -356,6 +356,15 @@ impl AffineMatrixObject { y: 0.into(), } } + + pub(crate) fn components(self) -> [u16; 4] { + [ + self.a.to_raw() as u16, + self.b.to_raw() as u16, + self.c.to_raw() as u16, + self.d.to_raw() as u16, + ] + } } impl From for AffineMatrix { diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 299c7734..621b52c7 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,3 +1,4 @@ +mod affine; mod managed; mod sprites; mod unmanaged; @@ -7,9 +8,12 @@ pub use sprites::{ TagMap, }; +pub use affine::AffineMatrix; pub use managed::{OAMManager, Object}; pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; +pub(crate) use affine::init_affine; + use super::DISPLAY_CONTROL; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs new file mode 100644 index 00000000..c54a44ff --- /dev/null +++ b/agb/src/display/object/affine.rs @@ -0,0 +1,115 @@ +use alloc::rc::Rc; + +use crate::{display::affine::AffineMatrixObject, sync::Static}; + +use super::OBJECT_ATTRIBUTE_MEMORY; + +#[derive(Debug)] +struct AffineMatrixLocation { + location: u16, +} + +#[derive(Debug, Clone)] +pub(crate) struct AffineMatrixVram(Rc); + +#[derive(Debug)] +pub struct AffineMatrix { + location: AffineMatrixVram, +} + +impl AffineMatrix { + pub fn new(affine_matrix: AffineMatrixObject) -> Option { + let mut matrix = AFFINE_MATRIX_DISTRIBUTOR.get_matrix()?; + matrix.write(affine_matrix); + + Some(matrix) + } + + pub(crate) fn vram(self) -> AffineMatrixVram { + self.location + } + + fn write(&mut self, affine_matrix: AffineMatrixObject) { + let components = affine_matrix.components(); + let location = self.location.0.location as usize; + for (idx, component) in components.iter().enumerate() { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16) + .add(location * 4 * idx + 3) + .write_volatile(*component); + } + } + } +} + +impl AffineMatrixVram { + pub fn location(&self) -> u16 { + self.0.location + } +} + +impl Drop for AffineMatrixLocation { + fn drop(&mut self) { + // safety: obtained via affine matrix distributor + unsafe { AFFINE_MATRIX_DISTRIBUTOR.return_matrix(self.location) } + } +} + +struct AffineMatrixDistributor { + tip: Static, +} + +static AFFINE_MATRIX_DISTRIBUTOR: AffineMatrixDistributor = AffineMatrixDistributor::new(); + +pub(crate) unsafe fn init_affine() { + AFFINE_MATRIX_DISTRIBUTOR.initialise_affine_matricies(); +} + +impl AffineMatrixDistributor { + const fn new() -> Self { + AffineMatrixDistributor { + tip: Static::new(u16::MAX), + } + } + + unsafe fn initialise_affine_matricies(&self) { + for i in 0..32 { + let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 16 + 3); + + if i == 31 { + // none + ptr.write_volatile(u16::MAX); + } else { + ptr.write_volatile(i as u16 + 1); + } + } + + self.tip.write(0); + } + + fn location_of(affine_matrix_location: u16) -> *mut u16 { + unsafe { + (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(affine_matrix_location as usize * 16 + 3) + } + } + + fn get_matrix(&self) -> Option { + let location = self.tip.read(); + if location == u16::MAX { + return None; + } + + let next_tip = unsafe { Self::location_of(location).read_volatile() }; + + self.tip.write(next_tip); + + Some(AffineMatrix { + location: AffineMatrixVram(Rc::new(AffineMatrixLocation { location })), + }) + } + + unsafe fn return_matrix(&self, mat_id: u16) { + Self::location_of(mat_id).write_volatile(mat_id); + self.tip.write(mat_id); + } +} diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 0e7eebe0..1c87c935 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -1,15 +1,13 @@ -use core::{ - cell::{Cell, UnsafeCell}, - marker::PhantomData, -}; +use core::cell::{Cell, UnsafeCell}; use agb_fixnum::Vector2D; -use alloc::vec::Vec; use slotmap::{new_key_type, SlotMap}; use crate::display::Priority; -use super::{AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject}; +use super::{ + AffineMatrix, AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject, +}; new_key_type! {struct ObjectKey; } @@ -416,6 +414,13 @@ impl Object<'_> { self } + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_affine_matrix(affine_matrix) }; + + self + } + pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_sprite(sprite) }; diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index 4e4bb3a2..f32f45f8 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -6,7 +6,7 @@ use self::attributes::{ ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2, }; -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug, Clone)] pub struct Attributes { a0: ObjectAttribute0, a1s: ObjectAttribute1Standard, diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index e10161d3..0ea32529 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -4,7 +4,9 @@ use agb_fixnum::Vector2D; use alloc::vec::Vec; use crate::display::{ - object::{sprites::SpriteVram, OBJECT_ATTRIBUTE_MEMORY}, + object::{ + affine::AffineMatrixVram, sprites::SpriteVram, AffineMatrix, OBJECT_ATTRIBUTE_MEMORY, + }, Priority, }; @@ -39,10 +41,7 @@ impl OAMSlot<'_> { // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. let frame_data = unsafe { &mut *self.frame_data.get() }; - // SAFETY: This is called here and in set_sprite, neither of which call the other. - let sprite = unsafe { &mut *object.sprites.get() }; - - frame_data.this_frame_sprites.push(sprite.clone()); + frame_data.this_frame_sprites.push(object.sprite.clone()); frame_data.up_to = self.slot as i32; } @@ -116,10 +115,11 @@ impl UnmanagedOAM<'_> { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UnmanagedObject { attributes: Attributes, - sprites: UnsafeCell, + sprite: SpriteVram, + affine_matrix: Option, } impl UnmanagedObject { @@ -131,7 +131,8 @@ impl UnmanagedObject { let mut sprite = Self { attributes: Attributes::default(), - sprites: UnsafeCell::new(sprite), + sprite, + affine_matrix: None, }; sprite.attributes.set_sprite(sprite_location, shape, size); @@ -140,6 +141,7 @@ impl UnmanagedObject { sprite } + #[must_use] pub fn is_visible(&self) -> bool { self.attributes.is_visible() } @@ -151,6 +153,11 @@ impl UnmanagedObject { } pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { + assert!( + self.affine_matrix.is_some(), + "affine matrix must be set before enabling affine matrix!" + ); + self.attributes.show_affine(affine_mode); self @@ -199,6 +206,15 @@ impl UnmanagedObject { self } + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + let vram = affine_matrix.vram(); + let location = vram.location(); + self.affine_matrix = Some(vram); + self.attributes.set_affine_matrix(location); + + self + } + fn set_sprite_attributes(&mut self, sprite: &SpriteVram) -> &mut Self { let size = sprite.size(); let (shape, size) = size.shape_size(); @@ -212,9 +228,7 @@ impl UnmanagedObject { pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { self.set_sprite_attributes(&sprite); - // SAFETY: This is called here and in OAMSlot set, neither of which call the other. - let sprites = unsafe { &mut *self.sprites.get() }; - *sprites = sprite; + self.sprite = sprite; self } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 90bd7166..741ae90f 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -236,6 +236,7 @@ impl Gba { #[doc(hidden)] #[must_use] pub unsafe fn new_in_entry() -> Self { + display::object::init_affine(); Self::single_new() } From 354cf7addc2ae8c69b9dcb418cfb40bee65aa5e4 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 22:11:37 +0100 Subject: [PATCH 15/39] keep and drain affine matricies --- agb/src/display/object/unmanaged/object.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 0ea32529..6857b267 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -16,12 +16,14 @@ use super::attributes::{AffineMode, Attributes}; struct OamFrameModifyables { up_to: i32, this_frame_sprites: Vec, + this_frame_affine: Vec, } pub struct UnmanagedOAM<'gba> { phantom: PhantomData<&'gba ()>, frame_data: UnsafeCell, previous_frame_sprites: Vec, + previous_frame_affine: Vec, } pub struct OAMIterator<'oam> { @@ -42,6 +44,9 @@ impl OAMSlot<'_> { let frame_data = unsafe { &mut *self.frame_data.get() }; frame_data.this_frame_sprites.push(object.sprite.clone()); + if let Some(affine) = &object.affine_matrix { + frame_data.this_frame_affine.push(affine.clone()); + } frame_data.up_to = self.slot as i32; } @@ -100,6 +105,13 @@ impl UnmanagedOAM<'_> { &mut self.previous_frame_sprites, ); + // Drain previous frame affine matricies + self.previous_frame_affine.drain(..); + core::mem::swap( + &mut frame_data.this_frame_affine, + &mut self.previous_frame_affine, + ); + OAMIterator { index: 0, frame_data: &self.frame_data, @@ -111,6 +123,7 @@ impl UnmanagedOAM<'_> { frame_data: Default::default(), phantom: PhantomData, previous_frame_sprites: Default::default(), + previous_frame_affine: Default::default(), } } } From 0e9910c517bb67663fd9d3d39fefa9bd88dc2c22 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 22:29:59 +0100 Subject: [PATCH 16/39] copy affine matricies at copy time --- agb/src/display/object.rs | 2 - agb/src/display/object/affine.rs | 131 +++++++----------- .../display/object/unmanaged/attributes.rs | 6 +- agb/src/display/object/unmanaged/object.rs | 39 +++--- agb/src/lib.rs | 1 - 5 files changed, 74 insertions(+), 105 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 621b52c7..010f1250 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -12,8 +12,6 @@ pub use affine::AffineMatrix; pub use managed::{OAMManager, Object}; pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; -pub(crate) use affine::init_affine; - use super::DISPLAY_CONTROL; const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs index c54a44ff..87597a1e 100644 --- a/agb/src/display/object/affine.rs +++ b/agb/src/display/object/affine.rs @@ -1,37 +1,63 @@ +use core::cell::Cell; + use alloc::rc::Rc; -use crate::{display::affine::AffineMatrixObject, sync::Static}; +use crate::display::affine::AffineMatrixObject; use super::OBJECT_ATTRIBUTE_MEMORY; #[derive(Debug)] -struct AffineMatrixLocation { - location: u16, +struct AffineMatrixData { + frame_count: Cell, + location: Cell, + matrix: AffineMatrixObject, } #[derive(Debug, Clone)] -pub(crate) struct AffineMatrixVram(Rc); +pub(crate) struct AffineMatrixVram(Rc); -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct AffineMatrix { location: AffineMatrixVram, } impl AffineMatrix { - pub fn new(affine_matrix: AffineMatrixObject) -> Option { - let mut matrix = AFFINE_MATRIX_DISTRIBUTOR.get_matrix()?; - matrix.write(affine_matrix); - - Some(matrix) + #[must_use] + pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrix { + AffineMatrix { + location: AffineMatrixVram(Rc::new(AffineMatrixData { + frame_count: Cell::new(u32::MAX), + location: Cell::new(u32::MAX), + matrix: affine_matrix, + })), + } } pub(crate) fn vram(self) -> AffineMatrixVram { self.location } +} - fn write(&mut self, affine_matrix: AffineMatrixObject) { - let components = affine_matrix.components(); - let location = self.location.0.location as usize; +impl AffineMatrixVram { + pub fn frame_count(&self) -> u32 { + self.0.frame_count.get() + } + + pub fn set_frame_count(&self, frame: u32) { + self.0.frame_count.set(frame); + } + + pub fn location(&self) -> u32 { + self.0.location.get() + } + + pub fn set_location(&self, location: u32) { + self.0.location.set(location); + } + + pub fn write_to_location(&self) { + let components = self.0.matrix.components(); + let location = self.0.location.get() as usize; for (idx, component) in components.iter().enumerate() { unsafe { (OBJECT_ATTRIBUTE_MEMORY as *mut u16) @@ -42,74 +68,15 @@ impl AffineMatrix { } } -impl AffineMatrixVram { - pub fn location(&self) -> u16 { - self.0.location - } -} - -impl Drop for AffineMatrixLocation { - fn drop(&mut self) { - // safety: obtained via affine matrix distributor - unsafe { AFFINE_MATRIX_DISTRIBUTOR.return_matrix(self.location) } - } -} - -struct AffineMatrixDistributor { - tip: Static, -} - -static AFFINE_MATRIX_DISTRIBUTOR: AffineMatrixDistributor = AffineMatrixDistributor::new(); - -pub(crate) unsafe fn init_affine() { - AFFINE_MATRIX_DISTRIBUTOR.initialise_affine_matricies(); -} - -impl AffineMatrixDistributor { - const fn new() -> Self { - AffineMatrixDistributor { - tip: Static::new(u16::MAX), - } - } - - unsafe fn initialise_affine_matricies(&self) { - for i in 0..32 { - let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 16 + 3); - - if i == 31 { - // none - ptr.write_volatile(u16::MAX); - } else { - ptr.write_volatile(i as u16 + 1); - } - } - - self.tip.write(0); - } - - fn location_of(affine_matrix_location: u16) -> *mut u16 { - unsafe { - (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(affine_matrix_location as usize * 16 + 3) - } - } - - fn get_matrix(&self) -> Option { - let location = self.tip.read(); - if location == u16::MAX { - return None; - } - - let next_tip = unsafe { Self::location_of(location).read_volatile() }; - - self.tip.write(next_tip); - - Some(AffineMatrix { - location: AffineMatrixVram(Rc::new(AffineMatrixLocation { location })), - }) - } - - unsafe fn return_matrix(&self, mat_id: u16) { - Self::location_of(mat_id).write_volatile(mat_id); - self.tip.write(mat_id); +#[cfg(test)] +mod tests { + use super::*; + + #[test_case] + fn niche_optimisation(_gba: &mut crate::Gba) { + assert_eq!( + core::mem::size_of::(), + core::mem::size_of::>() + ); } } diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index f32f45f8..7223d6ae 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -6,7 +6,7 @@ use self::attributes::{ ObjectAttribute0, ObjectAttribute1Affine, ObjectAttribute1Standard, ObjectAttribute2, }; -#[derive(PartialEq, Eq, Debug, Clone)] +#[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct Attributes { a0: ObjectAttribute0, a1s: ObjectAttribute1Standard, @@ -32,7 +32,7 @@ pub enum AffineMode { } impl Attributes { - pub fn bytes(&self) -> [u8; 6] { + pub fn bytes(self) -> [u8; 6] { let mode = self.a0.object_mode(); let attrs = match mode { ObjectMode::Normal => [ @@ -51,7 +51,7 @@ impl Attributes { unsafe { core::mem::transmute(attrs) } } - pub fn is_visible(&self) -> bool { + pub fn is_visible(self) -> bool { self.a0.object_mode() != ObjectMode::Disabled } diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 6857b267..84b8b0ae 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -16,14 +16,14 @@ use super::attributes::{AffineMode, Attributes}; struct OamFrameModifyables { up_to: i32, this_frame_sprites: Vec, - this_frame_affine: Vec, + frame: u32, + affine_matrix_count: u32, } pub struct UnmanagedOAM<'gba> { phantom: PhantomData<&'gba ()>, frame_data: UnsafeCell, previous_frame_sprites: Vec, - previous_frame_affine: Vec, } pub struct OAMIterator<'oam> { @@ -38,16 +38,29 @@ pub struct OAMSlot<'oam> { impl OAMSlot<'_> { pub fn set(&mut self, object: &UnmanagedObject) { - self.set_bytes(object.attributes.bytes()); - + let mut attributes = object.attributes; // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. let frame_data = unsafe { &mut *self.frame_data.get() }; - frame_data.this_frame_sprites.push(object.sprite.clone()); - if let Some(affine) = &object.affine_matrix { - frame_data.this_frame_affine.push(affine.clone()); + if let Some(affine_matrix) = &object.affine_matrix { + if affine_matrix.frame_count() != frame_data.frame { + affine_matrix.set_frame_count(frame_data.frame); + assert!( + frame_data.affine_matrix_count <= 32, + "too many affine matricies in one frame" + ); + affine_matrix.set_location(frame_data.affine_matrix_count); + frame_data.affine_matrix_count += 1; + affine_matrix.write_to_location(); + } + + attributes.set_affine_matrix(affine_matrix.location() as u16); } + self.set_bytes(attributes.bytes()); + + frame_data.this_frame_sprites.push(object.sprite.clone()); + frame_data.up_to = self.slot as i32; } @@ -96,6 +109,8 @@ impl UnmanagedOAM<'_> { pub fn iter(&mut self) -> OAMIterator<'_> { let frame_data = self.frame_data.get_mut(); frame_data.up_to = -1; + frame_data.frame = frame_data.frame.wrapping_add(1); + frame_data.affine_matrix_count = 0; // We drain the previous frame sprites here to reuse the Vecs allocation and remove the now unused sprites. // Any sprites currently being shown will now be put in the new Vec. @@ -105,13 +120,6 @@ impl UnmanagedOAM<'_> { &mut self.previous_frame_sprites, ); - // Drain previous frame affine matricies - self.previous_frame_affine.drain(..); - core::mem::swap( - &mut frame_data.this_frame_affine, - &mut self.previous_frame_affine, - ); - OAMIterator { index: 0, frame_data: &self.frame_data, @@ -123,7 +131,6 @@ impl UnmanagedOAM<'_> { frame_data: Default::default(), phantom: PhantomData, previous_frame_sprites: Default::default(), - previous_frame_affine: Default::default(), } } } @@ -221,9 +228,7 @@ impl UnmanagedObject { pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { let vram = affine_matrix.vram(); - let location = vram.location(); self.affine_matrix = Some(vram); - self.attributes.set_affine_matrix(location); self } diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 741ae90f..90bd7166 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -236,7 +236,6 @@ impl Gba { #[doc(hidden)] #[must_use] pub unsafe fn new_in_entry() -> Self { - display::object::init_affine(); Self::single_new() } From 7265576e2a841561c44cb496e3131808b5dce892 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 22:41:13 +0100 Subject: [PATCH 17/39] fix affine matrix copying --- agb/src/display/object/affine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs index 87597a1e..5dd44a6a 100644 --- a/agb/src/display/object/affine.rs +++ b/agb/src/display/object/affine.rs @@ -61,7 +61,7 @@ impl AffineMatrixVram { for (idx, component) in components.iter().enumerate() { unsafe { (OBJECT_ATTRIBUTE_MEMORY as *mut u16) - .add(location * 4 * idx + 3) + .add(location * 16 + idx * 4 + 3) .write_volatile(*component); } } From 875d53c0407dfeec5588332f95818db0edcc3268 Mon Sep 17 00:00:00 2001 From: Corwin Date: Mon, 3 Apr 2023 22:41:26 +0100 Subject: [PATCH 18/39] add rotation to sprite example --- agb/examples/sprites.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 4ff1fb7b..5fb7aa16 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -3,7 +3,12 @@ extern crate alloc; -use agb::display::object::{Graphics, OAMManager, Sprite, TagMap}; +use agb::display::{ + affine::AffineMatrix, + object::{self, Graphics, OAMManager, Sprite, TagMap}, +}; +use agb::fixnum::num; +use agb_fixnum::Num; use alloc::vec::Vec; const GRAPHICS: &Graphics = agb::include_aseprite!( @@ -15,14 +20,20 @@ const GRAPHICS: &Graphics = agb::include_aseprite!( const SPRITES: &[Sprite] = GRAPHICS.sprites(); const TAG_MAP: &TagMap = GRAPHICS.tags(); -fn all_sprites(gfx: &OAMManager) { +fn all_sprites(gfx: &OAMManager, rotation_speed: Num) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); + let mut rotation: Num = num!(0.); + + let rotation_matrix = AffineMatrix::from_rotation(rotation); + let matrix = object::AffineMatrix::new(rotation_matrix.to_object_wrapping()); + for y in 0..9 { for x in 0..14 { let mut obj = gfx.add_object_static_sprite(&SPRITES[0]); - obj.show(); + obj.set_affine_matrix(matrix.clone()); + obj.show_affine(object::AffineMode::Affine); obj.set_position((x * 16 + 8, y * 16 + 8).into()); objs.push(obj); } @@ -41,6 +52,15 @@ fn all_sprites(gfx: &OAMManager) { break; } + rotation += rotation_speed; + let rotation_matrix = AffineMatrix::from_rotation(rotation); + + let matrix = object::AffineMatrix::new(rotation_matrix.to_object_wrapping()); + + for obj in objs.iter_mut() { + obj.set_affine_matrix(matrix.clone()); + } + count += 1; if count % 5 == 0 { @@ -50,8 +70,8 @@ fn all_sprites(gfx: &OAMManager) { let this_image = (image + i) % SPRITES.len(); obj.set_sprite(gfx.get_vram_sprite(&SPRITES[this_image])); } - gfx.commit(); } + gfx.commit(); } } @@ -103,8 +123,7 @@ fn main(mut gba: agb::Gba) -> ! { loop { all_tags(&gfx); - gfx.commit(); - all_sprites(&gfx); - gfx.commit(); + all_sprites(&gfx, num!(0.)); + all_sprites(&gfx, num!(0.01)); } } From d183b8373dc47fd7fee8796fafaf31a8a0708689 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 4 Apr 2023 17:34:11 +0100 Subject: [PATCH 19/39] pull out where to write --- agb/src/display/object.rs | 4 ++-- agb/src/display/object/affine.rs | 7 ++----- agb/src/display/object/unmanaged/object.rs | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 010f1250..97e456dc 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -14,11 +14,11 @@ pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObj use super::DISPLAY_CONTROL; -const OBJECT_ATTRIBUTE_MEMORY: usize = 0x0700_0000; +const OBJECT_ATTRIBUTE_MEMORY: *mut u16 = 0x0700_0000 as *mut u16; pub(super) unsafe fn initilise_oam() { for i in 0..128 { - let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(i * 4); + let ptr = (OBJECT_ATTRIBUTE_MEMORY).add(i * 4); ptr.write_volatile(0b10 << 8); } diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs index 5dd44a6a..50818763 100644 --- a/agb/src/display/object/affine.rs +++ b/agb/src/display/object/affine.rs @@ -4,8 +4,6 @@ use alloc::rc::Rc; use crate::display::affine::AffineMatrixObject; -use super::OBJECT_ATTRIBUTE_MEMORY; - #[derive(Debug)] struct AffineMatrixData { frame_count: Cell, @@ -55,13 +53,12 @@ impl AffineMatrixVram { self.0.location.set(location); } - pub fn write_to_location(&self) { + pub fn write_to_location(&self, oam: *mut u16) { let components = self.0.matrix.components(); let location = self.0.location.get() as usize; for (idx, component) in components.iter().enumerate() { unsafe { - (OBJECT_ATTRIBUTE_MEMORY as *mut u16) - .add(location * 16 + idx * 4 + 3) + oam.add(location * 16 + idx * 4 + 3) .write_volatile(*component); } } diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 84b8b0ae..982d4eb3 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -51,7 +51,7 @@ impl OAMSlot<'_> { ); affine_matrix.set_location(frame_data.affine_matrix_count); frame_data.affine_matrix_count += 1; - affine_matrix.write_to_location(); + affine_matrix.write_to_location(OBJECT_ATTRIBUTE_MEMORY); } attributes.set_affine_matrix(affine_matrix.location() as u16); From d6006c78089505049dcfce52e42dd4c9e11462fd Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 4 Apr 2023 17:35:53 +0100 Subject: [PATCH 20/39] rename parts of objects --- agb/examples/chicken.rs | 4 +-- agb/examples/sprites.rs | 10 +++--- agb/src/display/mod.rs | 10 +++--- agb/src/display/object.rs | 9 +++-- agb/src/display/object/affine.rs | 12 +++---- agb/src/display/object/managed.rs | 31 ++++++++--------- agb/src/display/object/sprites.rs | 2 +- .../object/sprites/sprite_allocator.rs | 6 ++-- agb/src/display/object/unmanaged.rs | 2 +- agb/src/display/object/unmanaged/object.rs | 33 ++++++++++--------- 10 files changed, 60 insertions(+), 59 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 06a3e4e8..e7905802 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -4,7 +4,7 @@ use agb::{ display::tiled::{TileFormat, TileSet, TileSetting, TiledMap}, display::{ - object::{OAMManager, Object, Size, Sprite}, + object::{OamManager, Object, Size, Sprite}, palette16::Palette16, tiled::RegularBackgroundSize, HEIGHT, WIDTH, @@ -145,7 +145,7 @@ fn main(mut gba: agb::Gba) -> ! { fn update_chicken_object( chicken: &'_ mut Character<'_>, - gfx: &OAMManager, + gfx: &OamManager, state: State, frame_count: u32, ) { diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 5fb7aa16..776ecafd 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -5,7 +5,7 @@ extern crate alloc; use agb::display::{ affine::AffineMatrix, - object::{self, Graphics, OAMManager, Sprite, TagMap}, + object::{self, Graphics, OamManager, Sprite, TagMap}, }; use agb::fixnum::num; use agb_fixnum::Num; @@ -20,14 +20,14 @@ const GRAPHICS: &Graphics = agb::include_aseprite!( const SPRITES: &[Sprite] = GRAPHICS.sprites(); const TAG_MAP: &TagMap = GRAPHICS.tags(); -fn all_sprites(gfx: &OAMManager, rotation_speed: Num) { +fn all_sprites(gfx: &OamManager, rotation_speed: Num) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); let mut rotation: Num = num!(0.); let rotation_matrix = AffineMatrix::from_rotation(rotation); - let matrix = object::AffineMatrix::new(rotation_matrix.to_object_wrapping()); + let matrix = object::AffineMatrixInstance::new(rotation_matrix.to_object_wrapping()); for y in 0..9 { for x in 0..14 { @@ -55,7 +55,7 @@ fn all_sprites(gfx: &OAMManager, rotation_speed: Num) { rotation += rotation_speed; let rotation_matrix = AffineMatrix::from_rotation(rotation); - let matrix = object::AffineMatrix::new(rotation_matrix.to_object_wrapping()); + let matrix = object::AffineMatrixInstance::new(rotation_matrix.to_object_wrapping()); for obj in objs.iter_mut() { obj.set_affine_matrix(matrix.clone()); @@ -75,7 +75,7 @@ fn all_sprites(gfx: &OAMManager, rotation_speed: Num) { } } -fn all_tags(gfx: &OAMManager) { +fn all_tags(gfx: &OamManager) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index cf0923fb..8971814c 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -6,7 +6,7 @@ use video::Video; use self::{ blend::Blend, - object::{initilise_oam, OAMManager, StaticSpriteLoader, UnmanagedOAM}, + object::{initilise_oam, OamManager, OamUnmanaged, SpriteLoader}, window::Windows, }; @@ -84,14 +84,14 @@ pub struct Display { pub struct ObjectDistribution; impl ObjectDistribution { - pub fn get_unmanaged(&mut self) -> (UnmanagedOAM<'_>, StaticSpriteLoader) { + pub fn get_unmanaged(&mut self) -> (OamUnmanaged<'_>, SpriteLoader) { unsafe { initilise_oam() }; - (UnmanagedOAM::new(), StaticSpriteLoader::new()) + (OamUnmanaged::new(), SpriteLoader::new()) } - pub fn get_managed(&mut self) -> OAMManager<'_> { + pub fn get_managed(&mut self) -> OamManager<'_> { unsafe { initilise_oam() }; - OAMManager::new() + OamManager::new() } } diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 97e456dc..5406ecbb 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -4,13 +4,12 @@ mod sprites; mod unmanaged; pub use sprites::{ - include_aseprite, DynamicSprite, Graphics, Size, Sprite, SpriteVram, StaticSpriteLoader, Tag, - TagMap, + include_aseprite, DynamicSprite, Graphics, Size, Sprite, SpriteLoader, SpriteVram, Tag, TagMap, }; -pub use affine::AffineMatrix; -pub use managed::{OAMManager, Object}; -pub use unmanaged::{AffineMode, OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; +pub use affine::AffineMatrixInstance; +pub use managed::{OamManager, Object}; +pub use unmanaged::{AffineMode, OamIterator, OamSlot, OamUnmanaged, ObjectUnmanaged}; use super::DISPLAY_CONTROL; diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs index 50818763..703ab160 100644 --- a/agb/src/display/object/affine.rs +++ b/agb/src/display/object/affine.rs @@ -15,14 +15,14 @@ struct AffineMatrixData { pub(crate) struct AffineMatrixVram(Rc); #[derive(Debug, Clone)] -pub struct AffineMatrix { +pub struct AffineMatrixInstance { location: AffineMatrixVram, } -impl AffineMatrix { +impl AffineMatrixInstance { #[must_use] - pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrix { - AffineMatrix { + pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrixInstance { + AffineMatrixInstance { location: AffineMatrixVram(Rc::new(AffineMatrixData { frame_count: Cell::new(u32::MAX), location: Cell::new(u32::MAX), @@ -72,8 +72,8 @@ mod tests { #[test_case] fn niche_optimisation(_gba: &mut crate::Gba) { assert_eq!( - core::mem::size_of::(), - core::mem::size_of::>() + core::mem::size_of::(), + core::mem::size_of::>() ); } } diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 1c87c935..2d1b6a45 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -6,7 +6,8 @@ use slotmap::{new_key_type, SlotMap}; use crate::display::Priority; use super::{ - AffineMatrix, AffineMode, Sprite, SpriteVram, StaticSpriteLoader, UnmanagedOAM, UnmanagedObject, + AffineMatrixInstance, AffineMode, OamUnmanaged, ObjectUnmanaged, Sprite, SpriteLoader, + SpriteVram, }; new_key_type! {struct ObjectKey; } @@ -18,7 +19,7 @@ struct Ordering { } struct ObjectItem { - object: UnsafeCell, + object: UnsafeCell, z_order: Cell, z_index: Cell, } @@ -71,7 +72,7 @@ impl Store { true } - fn insert_object(&self, object: UnmanagedObject) -> Object { + fn insert_object(&self, object: ObjectUnmanaged) -> Object { let object_item = ObjectItem { object: UnsafeCell::new(object), z_order: Cell::new(Ordering { @@ -121,21 +122,21 @@ impl Store { } } -pub struct OAMManager<'gba> { +pub struct OamManager<'gba> { object_store: Store, - sprite_loader: UnsafeCell, - unmanaged: UnsafeCell>, + sprite_loader: UnsafeCell, + unmanaged: UnsafeCell>, } -impl OAMManager<'_> { +impl OamManager<'_> { pub(crate) fn new() -> Self { Self { object_store: Store { store: UnsafeCell::new(SlotMap::with_key()), first_z: Cell::new(None), }, - sprite_loader: UnsafeCell::new(StaticSpriteLoader::new()), - unmanaged: UnsafeCell::new(UnmanagedOAM::new()), + sprite_loader: UnsafeCell::new(SpriteLoader::new()), + unmanaged: UnsafeCell::new(OamUnmanaged::new()), } } @@ -143,7 +144,7 @@ impl OAMManager<'_> { /// Do not reenter or recurse or otherwise use sprite loader cell during this. unsafe fn do_work_with_sprite_loader(&self, c: C) -> T where - C: Fn(&mut StaticSpriteLoader) -> T, + C: Fn(&mut SpriteLoader) -> T, { let sprite_loader = unsafe { &mut *self.sprite_loader.get() }; @@ -164,13 +165,13 @@ impl OAMManager<'_> { // safety: not reentrant unsafe { - self.do_work_with_sprite_loader(StaticSpriteLoader::garbage_collect); + self.do_work_with_sprite_loader(SpriteLoader::garbage_collect); } } pub fn add_object(&self, sprite: SpriteVram) -> Object<'_> { self.object_store - .insert_object(UnmanagedObject::new(sprite)) + .insert_object(ObjectUnmanaged::new(sprite)) } pub fn get_vram_sprite(&self, sprite: &'static Sprite) -> SpriteVram { @@ -335,13 +336,13 @@ impl Object<'_> { /// Safety: /// Only have *ONE* of these at a time, do not call any functions that modify the slot map while having this. - unsafe fn object(&mut self) -> &mut UnmanagedObject { + unsafe fn object(&mut self) -> &mut ObjectUnmanaged { unsafe { &mut *self.store.get_object(self.me).object.get() } } /// Safety: /// Don't have a mutable one of these while having one of these, do not call any functions that modify the slot map while having this. - unsafe fn object_shared(&self) -> &UnmanagedObject { + unsafe fn object_shared(&self) -> &ObjectUnmanaged { unsafe { &*self.store.get_object(self.me).object.get() } } @@ -414,7 +415,7 @@ impl Object<'_> { self } - pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_affine_matrix(affine_matrix) }; diff --git a/agb/src/display/object/sprites.rs b/agb/src/display/object/sprites.rs index 1c331e46..c384be6a 100644 --- a/agb/src/display/object/sprites.rs +++ b/agb/src/display/object/sprites.rs @@ -4,4 +4,4 @@ mod sprite_allocator; const BYTES_PER_TILE_4BPP: usize = 32; pub use sprite::{include_aseprite, Graphics, Size, Sprite, Tag, TagMap}; -pub use sprite_allocator::{DynamicSprite, SpriteVram, StaticSpriteLoader}; +pub use sprite_allocator::{DynamicSprite, SpriteLoader, SpriteVram}; diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index caa5e38b..0c8d02a2 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -53,7 +53,7 @@ impl PaletteId { } /// This holds loading of static sprites and palettes. -pub struct StaticSpriteLoader { +pub struct SpriteLoader { static_palette_map: HashMap>, static_sprite_map: HashMap>, } @@ -159,7 +159,7 @@ impl SpriteVram { } } -impl StaticSpriteLoader { +impl SpriteLoader { fn create_sprite_no_insert( palette_map: &mut HashMap>, sprite: &'static Sprite, @@ -245,7 +245,7 @@ impl StaticSpriteLoader { } } -impl Default for StaticSpriteLoader { +impl Default for SpriteLoader { fn default() -> Self { Self::new() } diff --git a/agb/src/display/object/unmanaged.rs b/agb/src/display/object/unmanaged.rs index 3a9db454..42f609fa 100644 --- a/agb/src/display/object/unmanaged.rs +++ b/agb/src/display/object/unmanaged.rs @@ -2,4 +2,4 @@ mod attributes; mod object; pub use attributes::AffineMode; -pub use object::{OAMIterator, OAMSlot, UnmanagedOAM, UnmanagedObject}; +pub use object::{OamIterator, OamSlot, OamUnmanaged, ObjectUnmanaged}; diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 982d4eb3..30387b21 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -5,7 +5,8 @@ use alloc::vec::Vec; use crate::display::{ object::{ - affine::AffineMatrixVram, sprites::SpriteVram, AffineMatrix, OBJECT_ATTRIBUTE_MEMORY, + affine::AffineMatrixVram, sprites::SpriteVram, AffineMatrixInstance, + OBJECT_ATTRIBUTE_MEMORY, }, Priority, }; @@ -20,24 +21,24 @@ struct OamFrameModifyables { affine_matrix_count: u32, } -pub struct UnmanagedOAM<'gba> { +pub struct OamUnmanaged<'gba> { phantom: PhantomData<&'gba ()>, frame_data: UnsafeCell, previous_frame_sprites: Vec, } -pub struct OAMIterator<'oam> { +pub struct OamIterator<'oam> { index: usize, frame_data: &'oam UnsafeCell, } -pub struct OAMSlot<'oam> { +pub struct OamSlot<'oam> { slot: usize, frame_data: &'oam UnsafeCell, } -impl OAMSlot<'_> { - pub fn set(&mut self, object: &UnmanagedObject) { +impl OamSlot<'_> { + pub fn set(&mut self, object: &ObjectUnmanaged) { let mut attributes = object.attributes; // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. let frame_data = unsafe { &mut *self.frame_data.get() }; @@ -72,8 +73,8 @@ impl OAMSlot<'_> { } } -impl<'oam> Iterator for OAMIterator<'oam> { - type Item = OAMSlot<'oam>; +impl<'oam> Iterator for OamIterator<'oam> { + type Item = OamSlot<'oam>; fn next(&mut self) -> Option { let idx = self.index; @@ -82,7 +83,7 @@ impl<'oam> Iterator for OAMIterator<'oam> { if idx >= 128 { None } else { - Some(OAMSlot { + Some(OamSlot { slot: idx, frame_data: self.frame_data, }) @@ -90,7 +91,7 @@ impl<'oam> Iterator for OAMIterator<'oam> { } } -impl Drop for OAMIterator<'_> { +impl Drop for OamIterator<'_> { fn drop(&mut self) { let last_written = unsafe { &*self.frame_data.get() }.up_to; @@ -105,8 +106,8 @@ impl Drop for OAMIterator<'_> { } } -impl UnmanagedOAM<'_> { - pub fn iter(&mut self) -> OAMIterator<'_> { +impl OamUnmanaged<'_> { + pub fn iter(&mut self) -> OamIterator<'_> { let frame_data = self.frame_data.get_mut(); frame_data.up_to = -1; frame_data.frame = frame_data.frame.wrapping_add(1); @@ -120,7 +121,7 @@ impl UnmanagedOAM<'_> { &mut self.previous_frame_sprites, ); - OAMIterator { + OamIterator { index: 0, frame_data: &self.frame_data, } @@ -136,13 +137,13 @@ impl UnmanagedOAM<'_> { } #[derive(Debug, Clone)] -pub struct UnmanagedObject { +pub struct ObjectUnmanaged { attributes: Attributes, sprite: SpriteVram, affine_matrix: Option, } -impl UnmanagedObject { +impl ObjectUnmanaged { #[must_use] pub fn new(sprite: SpriteVram) -> Self { let sprite_location = sprite.location(); @@ -226,7 +227,7 @@ impl UnmanagedObject { self } - pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrix) -> &mut Self { + pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self { let vram = affine_matrix.vram(); self.affine_matrix = Some(vram); From e7caff3776fedc41caeffa01f2ddac725b6df5a4 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 4 Apr 2023 17:38:57 +0100 Subject: [PATCH 21/39] use new names in games --- .../hyperspace-roll/src/battle/display.rs | 14 +++--- examples/hyperspace-roll/src/customise.rs | 8 ++-- examples/hyperspace-roll/src/graphics.rs | 12 ++--- examples/hyperspace-roll/src/lib.rs | 4 +- .../the-hat-chooses-the-wizard/src/enemies.rs | 18 ++++---- .../the-hat-chooses-the-wizard/src/lib.rs | 14 +++--- examples/the-purple-night/src/lib.rs | 46 +++++++++---------- 7 files changed, 58 insertions(+), 58 deletions(-) diff --git a/examples/hyperspace-roll/src/battle/display.rs b/examples/hyperspace-roll/src/battle/display.rs index 973d7e79..44c6da2a 100644 --- a/examples/hyperspace-roll/src/battle/display.rs +++ b/examples/hyperspace-roll/src/battle/display.rs @@ -1,4 +1,4 @@ -use agb::display::object::{OAMManager, Object}; +use agb::display::object::{OamManager, Object}; use agb::rng; use alloc::vec; use alloc::vec::Vec; @@ -39,7 +39,7 @@ pub struct BattleScreenDisplay<'a> { const HEALTH_BAR_WIDTH: usize = 48; impl<'a> BattleScreenDisplay<'a> { - pub fn new(obj: &'a OAMManager, current_battle_state: &CurrentBattleState) -> Self { + pub fn new(obj: &'a OamManager, current_battle_state: &CurrentBattleState) -> Self { let mut misc_sprites = vec![]; let player_x = 12; let player_y = 8; @@ -189,7 +189,7 @@ impl<'a> BattleScreenDisplay<'a> { pub fn update( &mut self, - obj: &'a OAMManager, + obj: &'a OamManager, current_battle_state: &CurrentBattleState, ) -> Vec { for (i, player_shield) in self.objs.player_shield.iter_mut().enumerate() { @@ -279,7 +279,7 @@ impl<'a> BattleScreenDisplay<'a> { actions_to_apply } - pub fn add_action(&mut self, action: Action, obj: &'a OAMManager, sfx: &mut Sfx) { + pub fn add_action(&mut self, action: Action, obj: &'a OamManager, sfx: &mut Sfx) { play_sound_for_action_start(&action, sfx); self.animations @@ -309,7 +309,7 @@ impl<'a> EnemyAttackDisplay<'a> { } } - pub fn update(&mut self, attack: &Option, obj: &'a OAMManager) { + pub fn update(&mut self, attack: &Option, obj: &'a OamManager) { if let Some(attack) = attack { self.face.show().set_sprite( obj.get_vram_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), @@ -350,7 +350,7 @@ enum AnimationUpdateState { } impl<'a> AnimationStateHolder<'a> { - fn for_action(a: Action, obj: &'a OAMManager) -> Self { + fn for_action(a: Action, obj: &'a OamManager) -> Self { let state = match a { Action::PlayerActivateShield { amount, .. } => { AnimationState::PlayerActivateShield { amount, frame: 0 } @@ -383,7 +383,7 @@ impl<'a> AnimationStateHolder<'a> { fn update( &mut self, objs: &mut BattleScreenDisplayObjects<'a>, - obj: &'a OAMManager, + obj: &'a OamManager, current_battle_state: &CurrentBattleState, ) -> AnimationUpdateState { match &mut self.state { diff --git a/examples/hyperspace-roll/src/customise.rs b/examples/hyperspace-roll/src/customise.rs index 5e02580d..f3c7a3ca 100644 --- a/examples/hyperspace-roll/src/customise.rs +++ b/examples/hyperspace-roll/src/customise.rs @@ -1,6 +1,6 @@ use agb::{ display::{ - object::{OAMManager, Object}, + object::{OamManager, Object}, tiled::{RegularMap, TiledMap}, HEIGHT, WIDTH, }, @@ -91,7 +91,7 @@ fn move_net_position_ud(idx: usize, direction: Tri) -> usize { } } -fn create_dice_display<'a>(gfx: &'a OAMManager, dice: &'_ PlayerDice) -> Vec> { +fn create_dice_display<'a>(gfx: &'a OamManager, dice: &'_ PlayerDice) -> Vec> { let mut objects = Vec::new(); for (idx, dice) in dice.dice.iter().enumerate() { let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(dice.faces[1])); @@ -105,7 +105,7 @@ fn create_dice_display<'a>(gfx: &'a OAMManager, dice: &'_ PlayerDice) -> Vec(gfx: &'a OAMManager, die: &'_ Die, modified: &[usize]) -> Vec> { +fn create_net<'a>(gfx: &'a OamManager, die: &'_ Die, modified: &[usize]) -> Vec> { let mut objects = Vec::new(); for (idx, &face) in die.faces.iter().enumerate() { let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(face)); @@ -139,7 +139,7 @@ fn upgrade_position(idx: usize) -> (u32, u32) { ) } -fn create_upgrade_objects<'a>(gfx: &'a OAMManager, upgrades: &[Face]) -> Vec> { +fn create_upgrade_objects<'a>(gfx: &'a OamManager, upgrades: &[Face]) -> Vec> { let mut objects = Vec::new(); for (idx, &upgrade) in upgrades.iter().enumerate() { let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(upgrade)); diff --git a/examples/hyperspace-roll/src/graphics.rs b/examples/hyperspace-roll/src/graphics.rs index 88bad3ee..1ae79cc0 100644 --- a/examples/hyperspace-roll/src/graphics.rs +++ b/examples/hyperspace-roll/src/graphics.rs @@ -1,5 +1,5 @@ use agb::{ - display::object::{OAMManager, Object, Sprite, Tag}, + display::object::{OamManager, Object, Sprite, Tag}, fixnum::Vector2D, }; use alloc::vec::Vec; @@ -141,7 +141,7 @@ pub struct HealthBar<'a> { } impl<'a> HealthBar<'a> { - pub fn new(pos: Vector2D, max: usize, obj: &'a OAMManager) -> Self { + pub fn new(pos: Vector2D, max: usize, obj: &'a OamManager) -> Self { assert_eq!(max % 8, 0); let sprites = (0..(max / 8)) @@ -157,7 +157,7 @@ impl<'a> HealthBar<'a> { Self { max, sprites } } - pub fn set_value(&mut self, new_value: usize, obj: &'a OAMManager) { + pub fn set_value(&mut self, new_value: usize, obj: &'a OamManager) { assert!(new_value <= self.max); for (i, sprite) in self.sprites.iter_mut().enumerate() { @@ -195,7 +195,7 @@ pub struct FractionDisplay<'a> { } impl<'a> FractionDisplay<'a> { - pub fn new(pos: Vector2D, digits: usize, obj: &'a OAMManager) -> Self { + pub fn new(pos: Vector2D, digits: usize, obj: &'a OamManager) -> Self { let mut sprites = Vec::with_capacity(digits * 2 + 1); for i in 0..digits { @@ -222,7 +222,7 @@ impl<'a> FractionDisplay<'a> { } } - pub fn set_value(&mut self, current: usize, max: usize, obj: &'a OAMManager) { + pub fn set_value(&mut self, current: usize, max: usize, obj: &'a OamManager) { if self.current_current == current && self.current_max == max { return; } @@ -261,7 +261,7 @@ impl<'a> NumberDisplay<'a> { } } - pub fn set_value(&mut self, new_value: Option, obj: &'a OAMManager) { + pub fn set_value(&mut self, new_value: Option, obj: &'a OamManager) { if self.value == new_value { return; } diff --git a/examples/hyperspace-roll/src/lib.rs b/examples/hyperspace-roll/src/lib.rs index ff0bc020..62c2dc95 100644 --- a/examples/hyperspace-roll/src/lib.rs +++ b/examples/hyperspace-roll/src/lib.rs @@ -12,7 +12,7 @@ #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -use agb::display::object::OAMManager; +use agb::display::object::OamManager; use agb::display::tiled::{TileFormat, TiledMap, VRamManager}; use agb::display::Priority; use agb::interrupt::VBlank; @@ -90,7 +90,7 @@ pub struct PlayerDice { } struct Agb<'a> { - obj: OAMManager<'a>, + obj: OamManager<'a>, vblank: VBlank, star_background: StarBackground<'a>, vram: VRamManager, diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index 9acef75a..104b863e 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -2,7 +2,7 @@ use crate::TAG_MAP; use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use agb::{ - display::object::{OAMManager, Tag}, + display::object::{OamManager, Tag}, fixnum::Vector2D, }; @@ -35,11 +35,11 @@ pub enum EnemyUpdateState { } impl<'a> Enemy<'a> { - pub fn new_slime(object: &'a OAMManager, start_pos: Vector2D) -> Self { + pub fn new_slime(object: &'a OamManager, start_pos: Vector2D) -> Self { Enemy::Slime(Slime::new(object, start_pos + (0, 1).into())) } - pub fn new_snail(object: &'a OAMManager, start_pos: Vector2D) -> Self { + pub fn new_snail(object: &'a OamManager, start_pos: Vector2D) -> Self { Enemy::Snail(Snail::new(object, start_pos)) } @@ -52,7 +52,7 @@ impl<'a> Enemy<'a> { pub fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -94,7 +94,7 @@ struct EnemyInfo<'a> { impl<'a> EnemyInfo<'a> { fn new( - object: &'a OAMManager, + object: &'a OamManager, start_pos: Vector2D, collision: Vector2D, ) -> Self { @@ -135,7 +135,7 @@ pub struct Slime<'a> { } impl<'a> Slime<'a> { - fn new(object: &'a OAMManager, start_pos: Vector2D) -> Self { + fn new(object: &'a OamManager, start_pos: Vector2D) -> Self { let slime = Slime { enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()), state: SlimeState::Idle, @@ -146,7 +146,7 @@ impl<'a> Slime<'a> { fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -257,7 +257,7 @@ pub struct Snail<'a> { } impl<'a> Snail<'a> { - fn new(object: &'a OAMManager, start_pos: Vector2D) -> Self { + fn new(object: &'a OamManager, start_pos: Vector2D) -> Self { let snail = Snail { enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()), state: SnailState::Idle(0), @@ -272,7 +272,7 @@ impl<'a> Snail<'a> { fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, level: &Level, player_pos: Vector2D, hat_state: HatState, diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index 57117e3d..3f9337c9 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -8,7 +8,7 @@ extern crate alloc; use agb::{ display::{ - object::{Graphics, OAMManager, Object, Tag, TagMap}, + object::{Graphics, OamManager, Object, Tag, TagMap}, tiled::{ InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet, TileSetting, TiledMap, VRamManager, @@ -124,7 +124,7 @@ pub struct Entity<'a> { } impl<'a> Entity<'a> { - pub fn new(object: &'a OAMManager, collision_mask: Vector2D) -> Self { + pub fn new(object: &'a OamManager, collision_mask: Vector2D) -> Self { let mut dummy_object = object.add_object_static_sprite(WALKING.sprite(0)); dummy_object.set_priority(Priority::P1); Entity { @@ -347,7 +347,7 @@ fn ping_pong(i: i32, n: i32) -> i32 { } impl<'a> Player<'a> { - fn new(controller: &'a OAMManager, start_position: Vector2D) -> Self { + fn new(controller: &'a OamManager, start_position: Vector2D) -> Self { let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); @@ -381,7 +381,7 @@ impl<'a> Player<'a> { fn update_frame( &mut self, input: &ButtonController, - controller: &'a OAMManager, + controller: &'a OamManager, timer: i32, level: &Level, enemies: &[enemies::Enemy], @@ -615,7 +615,7 @@ enum UpdateState { impl<'a, 'b> PlayingLevel<'a, 'b> { fn open_level( level: &'a Level, - object_control: &'a OAMManager, + object_control: &'a OamManager, background: &'a mut InfiniteScrolledMap<'b>, foreground: &'a mut InfiniteScrolledMap<'b>, input: ButtonController, @@ -676,7 +676,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { self.player.wizard.sprite.set_priority(Priority::P0); } - fn dead_update(&mut self, controller: &'a OAMManager) -> bool { + fn dead_update(&mut self, controller: &'a OamManager) -> bool { self.timer += 1; let frame = PLAYER_DEATH.animation_sprite(self.timer as usize / 8); @@ -695,7 +695,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { &mut self, sfx_player: &mut SfxPlayer, vram: &mut VRamManager, - controller: &'a OAMManager, + controller: &'a OamManager, ) -> UpdateState { self.timer += 1; self.input.update(); diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 9d08bf28..4b68b1d0 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -14,7 +14,7 @@ use alloc::{boxed::Box, vec::Vec}; use agb::{ display::{ - object::{Graphics, OAMManager, Object, Sprite, Tag, TagMap}, + object::{Graphics, OamManager, Object, Sprite, Tag, TagMap}, tiled::{ InfiniteScrolledMap, RegularBackgroundSize, TileFormat, TileSet, TileSetting, VRamManager, @@ -164,7 +164,7 @@ struct Entity<'a> { } impl<'a> Entity<'a> { - fn new(object_controller: &'a OAMManager, collision_mask: Rect) -> Self { + fn new(object_controller: &'a OamManager, collision_mask: Rect) -> Self { let mut sprite = object_controller.add_object_static_sprite(LONG_SWORD_IDLE.sprite(0)); sprite.set_priority(Priority::P1); Entity { @@ -532,7 +532,7 @@ struct Player<'a> { } impl<'a> Player<'a> { - fn new(object_controller: &'a OAMManager<'a>) -> Player { + fn new(object_controller: &'a OamManager<'a>) -> Player { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), @@ -558,7 +558,7 @@ impl<'a> Player<'a> { fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, buttons: &ButtonController, level: &Level, sfx: &mut sfx::Sfx, @@ -814,7 +814,7 @@ impl BatData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -947,7 +947,7 @@ impl SlimeData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1076,7 +1076,7 @@ impl MiniFlameData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1205,7 +1205,7 @@ impl EmuData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1378,7 +1378,7 @@ impl EnemyData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1399,7 +1399,7 @@ struct Enemy<'a> { } impl<'a> Enemy<'a> { - fn new(object_controller: &'a OAMManager, enemy_data: EnemyData) -> Self { + fn new(object_controller: &'a OamManager, enemy_data: EnemyData) -> Self { let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); let sprite = enemy_data.sprite(); @@ -1413,7 +1413,7 @@ impl<'a> Enemy<'a> { fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -1444,7 +1444,7 @@ impl ParticleData { fn update<'a>( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1532,7 +1532,7 @@ struct Particle<'a> { impl<'a> Particle<'a> { fn new( - object_controller: &'a OAMManager, + object_controller: &'a OamManager, particle_data: ParticleData, position: Vector2D, ) -> Self { @@ -1551,7 +1551,7 @@ impl<'a> Particle<'a> { fn update( &mut self, - controller: &'a OAMManager, + controller: &'a OamManager, player: &Player, level: &Level, ) -> UpdateInstruction { @@ -1578,7 +1578,7 @@ impl<'a> BossState<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a OAMManager, + object_controller: &'a OamManager, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1613,7 +1613,7 @@ struct FollowingBoss<'a> { } impl<'a> FollowingBoss<'a> { - fn new(object_controller: &'a OAMManager, position: Vector2D) -> Self { + fn new(object_controller: &'a OamManager, position: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()), @@ -1628,7 +1628,7 @@ impl<'a> FollowingBoss<'a> { gone: false, } } - fn update(&mut self, controller: &'a OAMManager, player: &Player) { + fn update(&mut self, controller: &'a OamManager, player: &Player) { let difference = player.entity.position - self.entity.position; self.timer += 1; @@ -1697,7 +1697,7 @@ enum BossInstruction { } impl<'a> Boss<'a> { - fn new(object_controller: &'a OAMManager, screen_coords: Vector2D) -> Self { + fn new(object_controller: &'a OamManager, screen_coords: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()), @@ -1716,7 +1716,7 @@ impl<'a> Boss<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a OAMManager, + object_controller: &'a OamManager, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1820,7 +1820,7 @@ impl<'a> Boss<'a> { self.entity .commit_with_size(offset + shake, (32, 32).into()); } - fn explode(&self, enemies: &mut Arena>, object_controller: &'a OAMManager) { + fn explode(&self, enemies: &mut Arena>, object_controller: &'a OamManager) { for _ in 0..(6 - self.health) { let x_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; let y_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; @@ -1894,7 +1894,7 @@ impl<'a> Game<'a> { fn advance_frame( &mut self, - object_controller: &'a OAMManager, + object_controller: &'a OamManager, vram: &mut VRamManager, sfx: &mut sfx::Sfx, ) -> GameStatus { @@ -2108,7 +2108,7 @@ impl<'a> Game<'a> { } } - fn load_enemies(&mut self, object_controller: &'a OAMManager) { + fn load_enemies(&mut self, object_controller: &'a OamManager) { if self.slime_load < self.level.slime_spawns.len() { for (idx, slime_spawn) in self .level @@ -2178,7 +2178,7 @@ impl<'a> Game<'a> { vram.set_background_palettes(&modified_palettes); } - fn new(object: &'a OAMManager, level: Level<'a>, start_at_boss: bool) -> Self { + fn new(object: &'a OamManager<'a>, level: Level<'a>, start_at_boss: bool) -> Self { let mut player = Player::new(object); let mut offset = (8, 8).into(); if start_at_boss { From e690dda0f52a8529f3d45387571ab886573af4fa Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 4 Apr 2023 17:46:57 +0100 Subject: [PATCH 22/39] add test that my ordering code is right --- agb/src/display/object/managed.rs | 41 +++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 2d1b6a45..52b733ef 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -55,6 +55,7 @@ impl Store { } } + #[cfg(test)] fn is_all_ordered_right(&self) -> bool { let mut previous_z = i32::MIN; let mut current_index = self.first_z.get(); @@ -429,3 +430,43 @@ impl Object<'_> { self } } + +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + + use crate::{display::object::Graphics, include_aseprite}; + + use super::*; + + const TEST_SPRITES: &Graphics = include_aseprite!("examples/gfx/tall.aseprite"); + + const TEST_SPRITE: &Sprite = &TEST_SPRITES.sprites()[0]; + + #[test_case] + fn test_always_ordered(gba: &mut crate::Gba) { + let managed = gba.display.object.get_managed(); + + let sprite = managed.get_vram_sprite(TEST_SPRITE); + + let mut objects = Vec::new(); + for _ in 0..200 { + let obj = managed.add_object(sprite.clone()); + objects.push(obj); + } + + for modification_number in 0..10_000 { + let index_to_modify = (crate::rng::gen() as usize) % objects.len(); + let modify_to = crate::rng::gen(); + objects[index_to_modify].set_z(modify_to); + + assert!( + managed.object_store.is_all_ordered_right(), + "objects are unordered after {} modifications. Modified {} to {}.", + modification_number + 1, + index_to_modify, + modify_to + ); + } + } +} From cdc2eed01d4926e455ad173c48cb8d93c0ebabe0 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 5 Apr 2023 17:31:53 +0100 Subject: [PATCH 23/39] unsafe arena added (may remove later) --- agb/Cargo.toml | 1 - agb/src/arena.rs | 77 ++++++ agb/src/display/object/managed.rs | 22 +- agb/src/lib.rs | 2 + book/games/pong/Cargo.lock | 101 +++++-- examples/combo/Cargo.lock | 42 ++- examples/hyperspace-roll/Cargo.lock | 32 ++- .../the-hat-chooses-the-wizard/Cargo.lock | 42 ++- examples/the-purple-night/Cargo.lock | 38 +-- mgba-test-runner/Cargo.lock | 12 +- tools/Cargo.lock | 259 +++++++++++++----- 11 files changed, 436 insertions(+), 192 deletions(-) create mode 100644 agb/src/arena.rs diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 70797e43..02cdc17b 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -30,7 +30,6 @@ agb_fixnum = { version = "0.13.0", path = "../agb-fixnum" } bare-metal = "1" modular-bitfield = "0.11" rustc-hash = { version = "1", default-features = false } -slotmap = { version = "1.0", default-features = false } [package.metadata.docs.rs] default-target = "thumbv6m-none-eabi" diff --git a/agb/src/arena.rs b/agb/src/arena.rs new file mode 100644 index 00000000..976f44bb --- /dev/null +++ b/agb/src/arena.rs @@ -0,0 +1,77 @@ +use core::{alloc::Allocator, mem::ManuallyDrop}; + +use alloc::{alloc::Global, vec::Vec}; + +union ArenaItem { + free: Option, + occupied: ManuallyDrop, +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub struct ArenaKey(usize); + +pub struct Arena { + tip: Option, + data: Vec, A>, + inserted: usize, +} + +impl Arena { + pub const fn new() -> Self { + Self::new_in(Global) + } +} + +impl Arena { + pub const fn new_in(alloc: A) -> Self { + Self { + tip: None, + data: Vec::new_in(alloc), + inserted: 0, + } + } + + pub unsafe fn insert(&mut self, value: T) -> ArenaKey { + self.inserted += 1; + match self.tip { + Some(tip) => { + self.tip = self.data[tip.0].free; + self.data[tip.0].occupied = ManuallyDrop::new(value); + tip + } + None => { + self.data.push(ArenaItem { + occupied: ManuallyDrop::new(value), + }); + ArenaKey(self.data.len() - 1) + } + } + } + + pub unsafe fn remove(&mut self, key: ArenaKey) { + self.inserted = self + .inserted + .checked_sub(1) + .expect("removed more items than exist in here!"); + + unsafe { + core::mem::ManuallyDrop::::drop(&mut self.data[key.0].occupied); + } + + self.data[key.0].free = self.tip; + self.tip = Some(key); + } + + pub unsafe fn get(&self, key: ArenaKey) -> &T { + &self.data[key.0].occupied + } +} + +impl Drop for Arena { + fn drop(&mut self) { + assert_eq!( + self.inserted, 0, + "must remove all elements from arena before dropping it!" + ); + } +} diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 52b733ef..440c30b4 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -1,16 +1,18 @@ use core::cell::{Cell, UnsafeCell}; use agb_fixnum::Vector2D; -use slotmap::{new_key_type, SlotMap}; -use crate::display::Priority; +use crate::{ + arena::{Arena, ArenaKey}, + display::Priority, +}; use super::{ AffineMatrixInstance, AffineMode, OamUnmanaged, ObjectUnmanaged, Sprite, SpriteLoader, SpriteVram, }; -new_key_type! {struct ObjectKey; } +type ObjectKey = ArenaKey; #[derive(Clone, Copy)] struct Ordering { @@ -25,12 +27,12 @@ struct ObjectItem { } struct Store { - store: UnsafeCell>, + store: UnsafeCell>, first_z: Cell>, } struct StoreIterator<'store> { - store: &'store slotmap::SlotMap, + store: &'store Arena, current: Option, } @@ -38,7 +40,7 @@ impl<'store> Iterator for StoreIterator<'store> { type Item = &'store ObjectItem; fn next(&mut self) -> Option { - let to_output = &self.store[self.current?]; + let to_output = unsafe { self.store.get(self.current?) }; self.current = to_output.z_order.get().next; Some(to_output) } @@ -84,7 +86,7 @@ impl Store { }; let idx = { let data = unsafe { &mut *self.store.get() }; - data.insert(object_item) + unsafe { data.insert(object_item) } }; if let Some(first) = self.first_z.get() { @@ -115,11 +117,11 @@ impl Store { remove_from_linked_list(self, object); let data = unsafe { &mut *self.store.get() }; - data.remove(object); + unsafe { data.remove(object) }; } fn get_object(&self, key: ObjectKey) -> &ObjectItem { - &(unsafe { &*self.store.get() }[key]) + unsafe { (*self.store.get()).get(key) } } } @@ -133,7 +135,7 @@ impl OamManager<'_> { pub(crate) fn new() -> Self { Self { object_store: Store { - store: UnsafeCell::new(SlotMap::with_key()), + store: UnsafeCell::new(Arena::new()), first_z: Cell::new(None), }, sprite_loader: UnsafeCell::new(SpriteLoader::new()), diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 90bd7166..98f19eb9 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -181,6 +181,8 @@ pub mod syscall; /// Interactions with the internal timers pub mod timer; +pub(crate) mod arena; + pub use {agb_alloc::ExternalAllocator, agb_alloc::InternalAllocator}; #[cfg(not(any(test, feature = "testing")))] diff --git a/book/games/pong/Cargo.lock b/book/games/pong/Cargo.lock index 16d18409..30fa6f0e 100644 --- a/book/games/pong/Cargo.lock +++ b/book/games/pong/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.8", + "syn 2.0.13", "toml", ] @@ -55,7 +55,7 @@ version = "0.13.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -65,7 +65,7 @@ dependencies = [ "hound", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -176,7 +176,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a62391ecb864cf12ed06b2af4eda2e609b97657950d6a8f06841b17726ab253" dependencies = [ - "hashbrown", + "hashbrown 0.11.2", "ttf-parser", ] @@ -200,6 +200,12 @@ dependencies = [ "ahash", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hound" version = "3.5.0" @@ -222,10 +228,20 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.140" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "log" @@ -236,6 +252,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "miniz_oxide" version = "0.3.7" @@ -349,9 +371,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -373,22 +395,31 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", ] [[package]] @@ -410,9 +441,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -421,11 +452,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -451,3 +507,12 @@ name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] diff --git a/examples/combo/Cargo.lock b/examples/combo/Cargo.lock index d44a3230..07f739b9 100644 --- a/examples/combo/Cargo.lock +++ b/examples/combo/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", - "slotmap", ] [[package]] @@ -46,7 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.8", + "syn 2.0.13", "toml", ] @@ -56,7 +55,7 @@ version = "0.13.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -66,7 +65,7 @@ dependencies = [ "hound", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -287,9 +286,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libflate" @@ -424,9 +423,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -460,29 +459,29 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", @@ -498,15 +497,6 @@ dependencies = [ "serde", ] -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -526,9 +516,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", diff --git a/examples/hyperspace-roll/Cargo.lock b/examples/hyperspace-roll/Cargo.lock index 84a8548d..c30998d2 100644 --- a/examples/hyperspace-roll/Cargo.lock +++ b/examples/hyperspace-roll/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", - "slotmap", ] [[package]] @@ -46,7 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.8", + "syn 2.0.13", "toml", ] @@ -56,7 +55,7 @@ version = "0.13.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -66,7 +65,7 @@ dependencies = [ "hound", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -247,9 +246,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "log" @@ -372,9 +371,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -396,22 +395,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -425,6 +424,7 @@ dependencies = [ [[package]] <<<<<<< HEAD +<<<<<<< HEAD ======= name = "slotmap" version = "1.0.6" @@ -436,6 +436,8 @@ dependencies = [ [[package]] >>>>>>> ff9ab890 (fix games) +======= +>>>>>>> bd1a51df (unsafe arena added (may remove later)) name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -454,9 +456,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", diff --git a/examples/the-hat-chooses-the-wizard/Cargo.lock b/examples/the-hat-chooses-the-wizard/Cargo.lock index 1ebec2ad..7b76ddc4 100644 --- a/examples/the-hat-chooses-the-wizard/Cargo.lock +++ b/examples/the-hat-chooses-the-wizard/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", - "slotmap", ] [[package]] @@ -46,7 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.8", + "syn 2.0.13", "toml", ] @@ -56,7 +55,7 @@ version = "0.13.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -66,7 +65,7 @@ dependencies = [ "hound", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -246,9 +245,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "log" @@ -371,9 +370,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -401,29 +400,29 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa", "ryu", @@ -439,15 +438,6 @@ dependencies = [ "serde", ] -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -467,9 +457,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", diff --git a/examples/the-purple-night/Cargo.lock b/examples/the-purple-night/Cargo.lock index d90f423c..54c82041 100644 --- a/examples/the-purple-night/Cargo.lock +++ b/examples/the-purple-night/Cargo.lock @@ -26,7 +26,6 @@ dependencies = [ "bitflags 2.0.2", "modular-bitfield", "rustc-hash", - "slotmap", ] [[package]] @@ -46,7 +45,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.8", + "syn 2.0.13", "toml", ] @@ -56,7 +55,7 @@ version = "0.13.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -66,7 +65,7 @@ dependencies = [ "hound", "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -264,9 +263,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libflate" @@ -401,9 +400,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -431,22 +430,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "serde" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] @@ -458,15 +457,6 @@ dependencies = [ "serde", ] -[[package]] -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -486,9 +476,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", diff --git a/mgba-test-runner/Cargo.lock b/mgba-test-runner/Cargo.lock index bb4c6046..c5c60c26 100644 --- a/mgba-test-runner/Cargo.lock +++ b/mgba-test-runner/Cargo.lock @@ -89,9 +89,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" dependencies = [ "bytemuck", "byteorder", @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "memchr" @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.2" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", diff --git a/tools/Cargo.lock b/tools/Cargo.lock index a2508c2c..5541597a 100644 --- a/tools/Cargo.lock +++ b/tools/Cargo.lock @@ -11,6 +11,46 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -58,25 +98,31 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.13" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c911b090850d79fc64fe9ea01e28e465f65e821e08813ced95bced72f7a8a9b" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", "bitflags", "clap_lex", - "is-terminal", "strsim", - "termcolor", ] [[package]] name = "clap_lex" -version = "0.3.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "codespan-reporting" @@ -89,16 +135,31 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.3" +name = "concolor-override" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cxx" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -108,9 +169,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -118,35 +179,35 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] name = "cxxbridge-flags" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.13", ] [[package]] name = "errno" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -179,9 +240,9 @@ checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -203,9 +264,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -213,25 +274,25 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" dependencies = [ "hermit-abi", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -245,9 +306,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "link-cplusplus" @@ -260,9 +321,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "log" @@ -304,17 +365,11 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -330,16 +385,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -367,9 +422,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" dependencies = [ "proc-macro2", "quote", @@ -435,6 +490,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -528,11 +589,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -541,7 +602,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -550,13 +620,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -565,36 +650,72 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -602,10 +723,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] -name = "winnow" -version = "0.4.0" +name = "windows_x86_64_msvc" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deac0939bd6e4f24ab5919fbf751c97a8cfc8543bb083a305ed5c0c10bb241d1" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] From 78eebdd27020cd4e0d6f54b1696c9aa0570c17d0 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 5 Apr 2023 18:28:30 +0100 Subject: [PATCH 24/39] add combo to workspace --- .vscode/agb.code-workspace | 85 ++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/.vscode/agb.code-workspace b/.vscode/agb.code-workspace index f7b0fd40..1ef7a26a 100644 --- a/.vscode/agb.code-workspace +++ b/.vscode/agb.code-workspace @@ -1,43 +1,46 @@ { - "folders": [ - { - "path": "../agb" - }, - { - "path": "../agb-sound-converter" - }, - { - "path": "../agb-macros" - }, - { - "path": "../agb-image-converter" - }, - { - "path": "../agb-fixnum" - }, - { - "path": "../examples/the-purple-night" - }, - { - "path": "../examples/the-hat-chooses-the-wizard" - }, - { - "path": "../examples/hyperspace-roll" - }, - { - "path": "../.github" - }, - { - "path": "../template" - }, - { - "path": "../book" - }, - { - "path": "../mgba-test-runner" - }, - { - "path": "../tools" - } - ] + "folders": [ + { + "path": "../agb" + }, + { + "path": "../agb-sound-converter" + }, + { + "path": "../agb-macros" + }, + { + "path": "../agb-image-converter" + }, + { + "path": "../agb-fixnum" + }, + { + "path": "../examples/the-purple-night" + }, + { + "path": "../examples/the-hat-chooses-the-wizard" + }, + { + "path": "../examples/hyperspace-roll" + }, + { + "path": "../.github" + }, + { + "path": "../template" + }, + { + "path": "../book" + }, + { + "path": "../mgba-test-runner" + }, + { + "path": "../tools" + }, + { + "path": "../examples/combo" + } + ] } From c84e81299b91211e6d4d58b06d34cd3f2363d4b9 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 5 Apr 2023 18:35:45 +0100 Subject: [PATCH 25/39] improve the migration --- agb/examples/chicken.rs | 18 ++-- agb/examples/sprites.rs | 14 +-- agb/src/display/mod.rs | 6 +- agb/src/display/object.rs | 2 +- agb/src/display/object/managed.rs | 16 +-- book/games/pong/src/main.rs | 2 +- examples/hyperspace-roll/src/battle.rs | 4 +- .../hyperspace-roll/src/battle/display.rs | 53 +++++---- examples/hyperspace-roll/src/customise.rs | 24 ++--- examples/hyperspace-roll/src/graphics.rs | 36 +++---- examples/hyperspace-roll/src/lib.rs | 4 +- .../the-hat-chooses-the-wizard/src/enemies.rs | 34 +++--- .../the-hat-chooses-the-wizard/src/lib.rs | 36 +++---- examples/the-purple-night/src/lib.rs | 102 +++++++++--------- 14 files changed, 172 insertions(+), 179 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index e7905802..0ea1d435 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -4,7 +4,7 @@ use agb::{ display::tiled::{TileFormat, TileSet, TileSetting, TiledMap}, display::{ - object::{OamManager, Object, Size, Sprite}, + object::{OamManaged, Object, Size, Sprite}, palette16::Palette16, tiled::RegularBackgroundSize, HEIGHT, WIDTH, @@ -76,9 +76,9 @@ fn main(mut gba: agb::Gba) -> ! { let object = gba.display.object.get_managed(); - let sprite = object.get_vram_sprite(&CHICKEN_SPRITES[0]); + let sprite = object.get_sprite(&CHICKEN_SPRITES[0]); let mut chicken = Character { - object: object.add_object(sprite), + object: object.object(sprite), position: Vector2D { x: (6 * 8) << 8, y: ((7 * 8) - 4) << 8, @@ -145,7 +145,7 @@ fn main(mut gba: agb::Gba) -> ! { fn update_chicken_object( chicken: &'_ mut Character<'_>, - gfx: &OamManager, + gfx: &OamManaged, state: State, frame_count: u32, ) { @@ -158,19 +158,19 @@ fn update_chicken_object( State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { chicken.object.set_sprite( - gfx.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), + gfx.get_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), ); } else { chicken .object - .set_sprite(gfx.get_vram_sprite(&CHICKEN_SPRITES[0])); + .set_sprite(gfx.get_sprite(&CHICKEN_SPRITES[0])); } } State::Upwards => {} State::Flapping => { - chicken.object.set_sprite( - gfx.get_vram_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)]), - ); + chicken + .object + .set_sprite(gfx.get_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)])); } } diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 776ecafd..255512a5 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -5,7 +5,7 @@ extern crate alloc; use agb::display::{ affine::AffineMatrix, - object::{self, Graphics, OamManager, Sprite, TagMap}, + object::{self, Graphics, OamManaged, Sprite, TagMap}, }; use agb::fixnum::num; use agb_fixnum::Num; @@ -20,7 +20,7 @@ const GRAPHICS: &Graphics = agb::include_aseprite!( const SPRITES: &[Sprite] = GRAPHICS.sprites(); const TAG_MAP: &TagMap = GRAPHICS.tags(); -fn all_sprites(gfx: &OamManager, rotation_speed: Num) { +fn all_sprites(gfx: &OamManaged, rotation_speed: Num) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); @@ -31,7 +31,7 @@ fn all_sprites(gfx: &OamManager, rotation_speed: Num) { for y in 0..9 { for x in 0..14 { - let mut obj = gfx.add_object_static_sprite(&SPRITES[0]); + let mut obj = gfx.object_sprite(&SPRITES[0]); obj.set_affine_matrix(matrix.clone()); obj.show_affine(object::AffineMode::Affine); obj.set_position((x * 16 + 8, y * 16 + 8).into()); @@ -68,14 +68,14 @@ fn all_sprites(gfx: &OamManager, rotation_speed: Num) { image %= SPRITES.len(); for (i, obj) in objs.iter_mut().enumerate() { let this_image = (image + i) % SPRITES.len(); - obj.set_sprite(gfx.get_vram_sprite(&SPRITES[this_image])); + obj.set_sprite(gfx.get_sprite(&SPRITES[this_image])); } } gfx.commit(); } } -fn all_tags(gfx: &OamManager) { +fn all_tags(gfx: &OamManaged) { let mut input = agb::input::ButtonController::new(); let mut objs = Vec::new(); @@ -85,7 +85,7 @@ fn all_tags(gfx: &OamManager) { let sprite = v.sprite(0); let (size_x, size_y) = sprite.size().to_width_height(); let (size_x, size_y) = (size_x as i32, size_y as i32); - let mut obj = gfx.add_object_static_sprite(sprite); + let mut obj = gfx.object_sprite(sprite); obj.show(); obj.set_position((x * 32 + 16 - size_x / 2, y * 32 + 16 - size_y / 2).into()); objs.push((obj, v)); @@ -110,7 +110,7 @@ fn all_tags(gfx: &OamManager) { if count % 5 == 0 { image += 1; for (obj, tag) in objs.iter_mut() { - obj.set_sprite(gfx.get_vram_sprite(tag.animation_sprite(image))); + obj.set_sprite(gfx.get_sprite(tag.animation_sprite(image))); } gfx.commit(); } diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 8971814c..bc6f6d16 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -6,7 +6,7 @@ use video::Video; use self::{ blend::Blend, - object::{initilise_oam, OamManager, OamUnmanaged, SpriteLoader}, + object::{initilise_oam, OamManaged, OamUnmanaged, SpriteLoader}, window::Windows, }; @@ -89,9 +89,9 @@ impl ObjectDistribution { (OamUnmanaged::new(), SpriteLoader::new()) } - pub fn get_managed(&mut self) -> OamManager<'_> { + pub fn get_managed(&mut self) -> OamManaged<'_> { unsafe { initilise_oam() }; - OamManager::new() + OamManaged::new() } } diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 5406ecbb..281b8253 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -8,7 +8,7 @@ pub use sprites::{ }; pub use affine::AffineMatrixInstance; -pub use managed::{OamManager, Object}; +pub use managed::{OamManaged, Object}; pub use unmanaged::{AffineMode, OamIterator, OamSlot, OamUnmanaged, ObjectUnmanaged}; use super::DISPLAY_CONTROL; diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 440c30b4..5e0f3815 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -125,13 +125,13 @@ impl Store { } } -pub struct OamManager<'gba> { +pub struct OamManaged<'gba> { object_store: Store, sprite_loader: UnsafeCell, unmanaged: UnsafeCell>, } -impl OamManager<'_> { +impl OamManaged<'_> { pub(crate) fn new() -> Self { Self { object_store: Store { @@ -172,20 +172,20 @@ impl OamManager<'_> { } } - pub fn add_object(&self, sprite: SpriteVram) -> Object<'_> { + pub fn object(&self, sprite: SpriteVram) -> Object<'_> { self.object_store .insert_object(ObjectUnmanaged::new(sprite)) } - pub fn get_vram_sprite(&self, sprite: &'static Sprite) -> SpriteVram { + pub fn get_sprite(&self, sprite: &'static Sprite) -> SpriteVram { // safety: not reentrant unsafe { self.do_work_with_sprite_loader(|sprite_loader| sprite_loader.get_vram_sprite(sprite)) } } - pub fn add_object_static_sprite(&self, sprite: &'static Sprite) -> Object<'_> { - self.add_object(self.get_vram_sprite(sprite)) + pub fn object_sprite(&self, sprite: &'static Sprite) -> Object<'_> { + self.object(self.get_sprite(sprite)) } } @@ -449,11 +449,11 @@ mod tests { fn test_always_ordered(gba: &mut crate::Gba) { let managed = gba.display.object.get_managed(); - let sprite = managed.get_vram_sprite(TEST_SPRITE); + let sprite = managed.get_sprite(TEST_SPRITE); let mut objects = Vec::new(); for _ in 0..200 { - let obj = managed.add_object(sprite.clone()); + let obj = managed.object(sprite.clone()); objects.push(obj); } diff --git a/book/games/pong/src/main.rs b/book/games/pong/src/main.rs index 73916baa..8e7182f2 100644 --- a/book/games/pong/src/main.rs +++ b/book/games/pong/src/main.rs @@ -32,7 +32,7 @@ const BALL: &Tag = GRAPHICS.tags().get("Ball"); #[agb::entry] fn main(mut gba: agb::Gba) -> ! { // Get the OAM manager - let object = gba.display.object.get(); + let object = gba.display.object.get_managed(); // Create an object with the ball sprite let mut ball = object.object_sprite(BALL.sprite(0)); diff --git a/examples/hyperspace-roll/src/battle.rs b/examples/hyperspace-roll/src/battle.rs index cce4cd80..0068a39e 100644 --- a/examples/hyperspace-roll/src/battle.rs +++ b/examples/hyperspace-roll/src/battle.rs @@ -494,7 +494,7 @@ pub(crate) fn battle_screen( let obj = &agb.obj; - let mut select_box_obj = agb.obj.add_object_static_sprite(SELECT_BOX.sprite(0)); + let mut select_box_obj = agb.obj.object_sprite(SELECT_BOX.sprite(0)); select_box_obj.show(); let num_dice = player_dice.dice.len(); @@ -587,7 +587,7 @@ pub(crate) fn battle_screen( .set_x(selected_die as u16 * 40 + 28 - 4) .set_sprite( agb.obj - .get_vram_sprite(SELECT_BOX.animation_sprite(counter / 10)), + .get_sprite(SELECT_BOX.animation_sprite(counter / 10)), ); agb.star_background.update(); diff --git a/examples/hyperspace-roll/src/battle/display.rs b/examples/hyperspace-roll/src/battle/display.rs index 44c6da2a..f5328704 100644 --- a/examples/hyperspace-roll/src/battle/display.rs +++ b/examples/hyperspace-roll/src/battle/display.rs @@ -1,4 +1,4 @@ -use agb::display::object::{OamManager, Object}; +use agb::display::object::{OamManaged, Object}; use agb::rng; use alloc::vec; use alloc::vec::Vec; @@ -39,7 +39,7 @@ pub struct BattleScreenDisplay<'a> { const HEALTH_BAR_WIDTH: usize = 48; impl<'a> BattleScreenDisplay<'a> { - pub fn new(obj: &'a OamManager, current_battle_state: &CurrentBattleState) -> Self { + pub fn new(obj: &'a OamManaged, current_battle_state: &CurrentBattleState) -> Self { let mut misc_sprites = vec![]; let player_x = 12; let player_y = 8; @@ -52,8 +52,8 @@ impl<'a> BattleScreenDisplay<'a> { Ship::PilotedShip }); - let mut player_obj = obj.add_object_static_sprite(player_sprite); - let mut enemy_obj = obj.add_object_static_sprite(enemy_sprite); + let mut player_obj = obj.object_sprite(player_sprite); + let mut enemy_obj = obj.object_sprite(enemy_sprite); player_obj.set_x(player_x).set_y(player_y).set_z(1).show(); enemy_obj.set_x(enemy_x).set_y(player_y).set_z(1).show(); @@ -66,7 +66,7 @@ impl<'a> BattleScreenDisplay<'a> { .faces_to_render() .enumerate() .map(|(i, (face, _))| { - let mut die_obj = obj.add_object_static_sprite(FACE_SPRITES.sprite_for_face(face)); + let mut die_obj = obj.object_sprite(FACE_SPRITES.sprite_for_face(face)); die_obj.set_y(120).set_x(i as u16 * 40 + 28).show(); @@ -89,7 +89,7 @@ impl<'a> BattleScreenDisplay<'a> { let player_shield: Vec<_> = (0..5) .map(|i| { - let mut shield_obj = obj.add_object_static_sprite(shield_sprite); + let mut shield_obj = obj.object_sprite(shield_sprite); shield_obj .set_x(player_x + 18 + 11 * i) .set_y(player_y) @@ -101,7 +101,7 @@ impl<'a> BattleScreenDisplay<'a> { let enemy_shield: Vec<_> = (0..5) .map(|i| { - let mut shield_obj = obj.add_object_static_sprite(shield_sprite); + let mut shield_obj = obj.object_sprite(shield_sprite); shield_obj .set_x(enemy_x - 16 - 11 * i) .set_y(player_y) @@ -146,9 +146,8 @@ impl<'a> BattleScreenDisplay<'a> { let enemy_attack_display = (0..2) .map(|i| { - let mut attack_obj = obj.add_object_static_sprite( - ENEMY_ATTACK_SPRITES.sprite_for_attack(EnemyAttackType::Attack), - ); + let mut attack_obj = obj + .object_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(EnemyAttackType::Attack)); let attack_obj_position = (120, 56 + 32 * i).into(); attack_obj.set_position(attack_obj_position).hide(); @@ -189,14 +188,14 @@ impl<'a> BattleScreenDisplay<'a> { pub fn update( &mut self, - obj: &'a OamManager, + obj: &'a OamManaged, current_battle_state: &CurrentBattleState, ) -> Vec { for (i, player_shield) in self.objs.player_shield.iter_mut().enumerate() { if i < current_battle_state.player.shield_count as usize { player_shield .show() - .set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); + .set_sprite(obj.get_sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -206,7 +205,7 @@ impl<'a> BattleScreenDisplay<'a> { if i < current_battle_state.enemy.shield_count as usize { player_shield .show() - .set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); + .set_sprite(obj.get_sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -250,7 +249,7 @@ impl<'a> BattleScreenDisplay<'a> { .zip(current_battle_state.rolled_dice.faces_to_render()) .zip(self.objs.dice_cooldowns.iter_mut()) { - die_obj.set_sprite(obj.get_vram_sprite(FACE_SPRITES.sprite_for_face(current_face))); + die_obj.set_sprite(obj.get_sprite(FACE_SPRITES.sprite_for_face(current_face))); if let Some(cooldown) = cooldown { cooldown_healthbar @@ -279,7 +278,7 @@ impl<'a> BattleScreenDisplay<'a> { actions_to_apply } - pub fn add_action(&mut self, action: Action, obj: &'a OamManager, sfx: &mut Sfx) { + pub fn add_action(&mut self, action: Action, obj: &'a OamManaged, sfx: &mut Sfx) { play_sound_for_action_start(&action, sfx); self.animations @@ -309,10 +308,10 @@ impl<'a> EnemyAttackDisplay<'a> { } } - pub fn update(&mut self, attack: &Option, obj: &'a OamManager) { + pub fn update(&mut self, attack: &Option, obj: &'a OamManaged) { if let Some(attack) = attack { self.face.show().set_sprite( - obj.get_vram_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), + obj.get_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), ); self.cooldown .set_value((attack.cooldown * 48 / attack.max_cooldown) as usize, obj); @@ -350,27 +349,27 @@ enum AnimationUpdateState { } impl<'a> AnimationStateHolder<'a> { - fn for_action(a: Action, obj: &'a OamManager) -> Self { + fn for_action(a: Action, obj: &'a OamManaged) -> Self { let state = match a { Action::PlayerActivateShield { amount, .. } => { AnimationState::PlayerActivateShield { amount, frame: 0 } } Action::PlayerShoot { .. } => AnimationState::PlayerShoot { - bullet: obj.add_object_static_sprite(BULLET_SPRITE), + bullet: obj.object_sprite(BULLET_SPRITE), x: 64, }, Action::PlayerDisrupt { .. } => AnimationState::PlayerDisrupt { - bullet: obj.add_object_static_sprite(DISRUPT_BULLET), + bullet: obj.object_sprite(DISRUPT_BULLET), x: 64, }, Action::PlayerHeal { .. } => AnimationState::PlayerHeal {}, Action::PlayerBurstShield { .. } => AnimationState::PlayerBurstShield { frame: 0 }, Action::PlayerSendBurstShield { .. } => AnimationState::PlayerSendBurstShield { - bullet: obj.add_object_static_sprite(BURST_BULLET), + bullet: obj.object_sprite(BURST_BULLET), x: 64, }, Action::EnemyShoot { .. } => AnimationState::EnemyShoot { - bullet: obj.add_object_static_sprite(BULLET_SPRITE), + bullet: obj.object_sprite(BULLET_SPRITE), x: 175, }, Action::EnemyShield { amount, .. } => AnimationState::EnemyShield { amount, frame: 0 }, @@ -383,7 +382,7 @@ impl<'a> AnimationStateHolder<'a> { fn update( &mut self, objs: &mut BattleScreenDisplayObjects<'a>, - obj: &'a OamManager, + obj: &'a OamManaged, current_battle_state: &CurrentBattleState, ) -> AnimationUpdateState { match &mut self.state { @@ -414,7 +413,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_player_shields..*amount { objs.player_shield[i as usize] .show() - .set_sprite(obj.get_vram_sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.get_sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -445,7 +444,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_enemy_shields..*amount { objs.enemy_shield[i as usize] .show() - .set_sprite(obj.get_vram_sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.get_sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -468,7 +467,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationState::PlayerBurstShield { frame } => { if *frame < 10 { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.get_vram_sprite(SHIELD.sprite(*frame / 2))); + shield.set_sprite(obj.get_sprite(SHIELD.sprite(*frame / 2))); } *frame += 1; @@ -476,7 +475,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationUpdateState::Continue } else { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.get_vram_sprite(SHIELD.sprite(0))); + shield.set_sprite(obj.get_sprite(SHIELD.sprite(0))); } AnimationUpdateState::RemoveWithAction(self.action.clone()) diff --git a/examples/hyperspace-roll/src/customise.rs b/examples/hyperspace-roll/src/customise.rs index f3c7a3ca..ea7c57a8 100644 --- a/examples/hyperspace-roll/src/customise.rs +++ b/examples/hyperspace-roll/src/customise.rs @@ -1,6 +1,6 @@ use agb::{ display::{ - object::{OamManager, Object}, + object::{OamManaged, Object}, tiled::{RegularMap, TiledMap}, HEIGHT, WIDTH, }, @@ -91,10 +91,10 @@ fn move_net_position_ud(idx: usize, direction: Tri) -> usize { } } -fn create_dice_display<'a>(gfx: &'a OamManager, dice: &'_ PlayerDice) -> Vec> { +fn create_dice_display<'a>(gfx: &'a OamManaged, dice: &'_ PlayerDice) -> Vec> { let mut objects = Vec::new(); for (idx, dice) in dice.dice.iter().enumerate() { - let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(dice.faces[1])); + let mut obj = gfx.object_sprite(FACE_SPRITES.sprite_for_face(dice.faces[1])); obj.set_x((idx as i32 * 32 - 24 / 2 + 20) as u16); obj.set_y(16 - 24 / 2); @@ -105,10 +105,10 @@ fn create_dice_display<'a>(gfx: &'a OamManager, dice: &'_ PlayerDice) -> Vec(gfx: &'a OamManager, die: &'_ Die, modified: &[usize]) -> Vec> { +fn create_net<'a>(gfx: &'a OamManaged, die: &'_ Die, modified: &[usize]) -> Vec> { let mut objects = Vec::new(); for (idx, &face) in die.faces.iter().enumerate() { - let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(face)); + let mut obj = gfx.object_sprite(FACE_SPRITES.sprite_for_face(face)); let (x, y) = screen_position_for_index(idx); obj.set_x((x - 24 / 2) as u16); obj.set_y((y - 24 / 2) as u16); @@ -119,7 +119,7 @@ fn create_net<'a>(gfx: &'a OamManager, die: &'_ Die, modified: &[usize]) -> Vec< } for &m in modified.iter().chain(core::iter::once(&3)) { - let mut obj = gfx.add_object_static_sprite(MODIFIED_BOX); + let mut obj = gfx.object_sprite(MODIFIED_BOX); let (x, y) = screen_position_for_index(m); obj.set_x((x - 32 / 2) as u16); obj.set_y((y - 32 / 2) as u16); @@ -139,10 +139,10 @@ fn upgrade_position(idx: usize) -> (u32, u32) { ) } -fn create_upgrade_objects<'a>(gfx: &'a OamManager, upgrades: &[Face]) -> Vec> { +fn create_upgrade_objects<'a>(gfx: &'a OamManaged, upgrades: &[Face]) -> Vec> { let mut objects = Vec::new(); for (idx, &upgrade) in upgrades.iter().enumerate() { - let mut obj = gfx.add_object_static_sprite(FACE_SPRITES.sprite_for_face(upgrade)); + let mut obj = gfx.object_sprite(FACE_SPRITES.sprite_for_face(upgrade)); let (x, y) = upgrade_position(idx); obj.set_x((x - 24 / 2) as u16); obj.set_y((y - 24 / 2) as u16); @@ -180,13 +180,13 @@ pub(crate) fn customise_screen( let mut input = agb::input::ButtonController::new(); - let mut select_box = agb.obj.add_object_static_sprite(SELECT_BOX.sprite(0)); + let mut select_box = agb.obj.object_sprite(SELECT_BOX.sprite(0)); select_box.show(); - let mut selected_dice = agb.obj.add_object_static_sprite(SELECTED_BOX); + let mut selected_dice = agb.obj.object_sprite(SELECTED_BOX); selected_dice.hide(); - let mut selected_face = agb.obj.add_object_static_sprite(SELECTED_BOX); + let mut selected_face = agb.obj.object_sprite(SELECTED_BOX); selected_face.hide(); agb.sfx.frame(); @@ -340,7 +340,7 @@ pub(crate) fn customise_screen( select_box.set_sprite( agb.obj - .get_vram_sprite(SELECT_BOX.animation_sprite(counter / 10)), + .get_sprite(SELECT_BOX.animation_sprite(counter / 10)), ); agb.star_background.update(); diff --git a/examples/hyperspace-roll/src/graphics.rs b/examples/hyperspace-roll/src/graphics.rs index 1ae79cc0..640c5157 100644 --- a/examples/hyperspace-roll/src/graphics.rs +++ b/examples/hyperspace-roll/src/graphics.rs @@ -1,5 +1,5 @@ use agb::{ - display::object::{OamManager, Object, Sprite, Tag}, + display::object::{OamManaged, Object, Sprite, Tag}, fixnum::Vector2D, }; use alloc::vec::Vec; @@ -141,12 +141,12 @@ pub struct HealthBar<'a> { } impl<'a> HealthBar<'a> { - pub fn new(pos: Vector2D, max: usize, obj: &'a OamManager) -> Self { + pub fn new(pos: Vector2D, max: usize, obj: &'a OamManaged) -> Self { assert_eq!(max % 8, 0); let sprites = (0..(max / 8)) .map(|i| { - let mut health_object = obj.add_object_static_sprite(SMALL_SPRITES.red_bar(0)); + let mut health_object = obj.object_sprite(SMALL_SPRITES.red_bar(0)); health_object .set_position(pos + (i as i32 * 8, 0).into()) .show(); @@ -157,18 +157,16 @@ impl<'a> HealthBar<'a> { Self { max, sprites } } - pub fn set_value(&mut self, new_value: usize, obj: &'a OamManager) { + pub fn set_value(&mut self, new_value: usize, obj: &'a OamManaged) { assert!(new_value <= self.max); for (i, sprite) in self.sprites.iter_mut().enumerate() { if (i + 1) * 8 < new_value { - sprite.set_sprite(obj.get_vram_sprite(SMALL_SPRITES.red_bar(0))); + sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(0))); } else if i * 8 < new_value { - sprite.set_sprite( - obj.get_vram_sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8))), - ); + sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8)))); } else { - sprite.set_sprite(obj.get_vram_sprite(SMALL_SPRITES.red_bar(8))); + sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(8))); } } } @@ -195,22 +193,22 @@ pub struct FractionDisplay<'a> { } impl<'a> FractionDisplay<'a> { - pub fn new(pos: Vector2D, digits: usize, obj: &'a OamManager) -> Self { + pub fn new(pos: Vector2D, digits: usize, obj: &'a OamManaged) -> Self { let mut sprites = Vec::with_capacity(digits * 2 + 1); for i in 0..digits { - let mut left_digit = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); + let mut left_digit = obj.object_sprite(SMALL_SPRITES.number(0)); left_digit.set_position(pos + (i as i32 * 4, 0).into()); sprites.push(left_digit); - let mut right_digit = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); + let mut right_digit = obj.object_sprite(SMALL_SPRITES.number(0)); right_digit.set_position(pos + (i as i32 * 4 + digits as i32 * 4 + 7, 0).into()); sprites.push(right_digit); } - let mut slash = obj.add_object_static_sprite(SMALL_SPRITES.slash()); + let mut slash = obj.object_sprite(SMALL_SPRITES.slash()); slash.set_position(pos + (digits as i32 * 4 + 1, 0).into()); sprites.push(slash); @@ -222,7 +220,7 @@ impl<'a> FractionDisplay<'a> { } } - pub fn set_value(&mut self, current: usize, max: usize, obj: &'a OamManager) { + pub fn set_value(&mut self, current: usize, max: usize, obj: &'a OamManaged) { if self.current_current == current && self.current_max == max { return; } @@ -235,13 +233,13 @@ impl<'a> FractionDisplay<'a> { current /= 10; let current_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 2]; current_value_sprite - .set_sprite(obj.get_vram_sprite(SMALL_SPRITES.number(current_value_digit as u32))); + .set_sprite(obj.get_sprite(SMALL_SPRITES.number(current_value_digit as u32))); let max_value_digit = max % 10; max /= 10; let max_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 1]; max_value_sprite - .set_sprite(obj.get_vram_sprite(SMALL_SPRITES.number(max_value_digit as u32))); + .set_sprite(obj.get_sprite(SMALL_SPRITES.number(max_value_digit as u32))); } } } @@ -261,7 +259,7 @@ impl<'a> NumberDisplay<'a> { } } - pub fn set_value(&mut self, new_value: Option, obj: &'a OamManager) { + pub fn set_value(&mut self, new_value: Option, obj: &'a OamManaged) { if self.value == new_value { return; } @@ -272,7 +270,7 @@ impl<'a> NumberDisplay<'a> { if let Some(mut new_value) = new_value { if new_value == 0 { - let mut zero_object = obj.add_object_static_sprite(SMALL_SPRITES.number(0)); + let mut zero_object = obj.object_sprite(SMALL_SPRITES.number(0)); zero_object.show().set_position(self.position); self.objects.push(zero_object); @@ -285,7 +283,7 @@ impl<'a> NumberDisplay<'a> { new_value /= 10; let mut current_value_obj = - obj.add_object_static_sprite(SMALL_SPRITES.number(current_value_digit)); + obj.object_sprite(SMALL_SPRITES.number(current_value_digit)); current_value_obj .show() diff --git a/examples/hyperspace-roll/src/lib.rs b/examples/hyperspace-roll/src/lib.rs index 62c2dc95..055c1e04 100644 --- a/examples/hyperspace-roll/src/lib.rs +++ b/examples/hyperspace-roll/src/lib.rs @@ -12,7 +12,7 @@ #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] -use agb::display::object::OamManager; +use agb::display::object::OamManaged; use agb::display::tiled::{TileFormat, TiledMap, VRamManager}; use agb::display::Priority; use agb::interrupt::VBlank; @@ -90,7 +90,7 @@ pub struct PlayerDice { } struct Agb<'a> { - obj: OamManager<'a>, + obj: OamManaged<'a>, vblank: VBlank, star_background: StarBackground<'a>, vram: VRamManager, diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index 104b863e..45f05104 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -2,7 +2,7 @@ use crate::TAG_MAP; use super::{sfx::SfxPlayer, Entity, FixedNumberType, HatState, Level}; use agb::{ - display::object::{OamManager, Tag}, + display::object::{OamManaged, Tag}, fixnum::Vector2D, }; @@ -35,11 +35,11 @@ pub enum EnemyUpdateState { } impl<'a> Enemy<'a> { - pub fn new_slime(object: &'a OamManager, start_pos: Vector2D) -> Self { + pub fn new_slime(object: &'a OamManaged, start_pos: Vector2D) -> Self { Enemy::Slime(Slime::new(object, start_pos + (0, 1).into())) } - pub fn new_snail(object: &'a OamManager, start_pos: Vector2D) -> Self { + pub fn new_snail(object: &'a OamManaged, start_pos: Vector2D) -> Self { Enemy::Snail(Snail::new(object, start_pos)) } @@ -52,7 +52,7 @@ impl<'a> Enemy<'a> { pub fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -94,7 +94,7 @@ struct EnemyInfo<'a> { impl<'a> EnemyInfo<'a> { fn new( - object: &'a OamManager, + object: &'a OamManaged, start_pos: Vector2D, collision: Vector2D, ) -> Self { @@ -135,7 +135,7 @@ pub struct Slime<'a> { } impl<'a> Slime<'a> { - fn new(object: &'a OamManager, start_pos: Vector2D) -> Self { + fn new(object: &'a OamManaged, start_pos: Vector2D) -> Self { let slime = Slime { enemy_info: EnemyInfo::new(object, start_pos, (14u16, 14u16).into()), state: SlimeState::Idle, @@ -146,7 +146,7 @@ impl<'a> Slime<'a> { fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -161,7 +161,7 @@ impl<'a> Slime<'a> { let offset = (timer / 16) as usize; let frame = SLIME_IDLE.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -201,7 +201,7 @@ impl<'a> Slime<'a> { self.state = SlimeState::Idle; } else { let frame = SLIME_JUMP.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -227,7 +227,7 @@ impl<'a> Slime<'a> { } let frame = SLIME_SPLAT.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -257,7 +257,7 @@ pub struct Snail<'a> { } impl<'a> Snail<'a> { - fn new(object: &'a OamManager, start_pos: Vector2D) -> Self { + fn new(object: &'a OamManaged, start_pos: Vector2D) -> Self { let snail = Snail { enemy_info: EnemyInfo::new(object, start_pos, (16u16, 16u16).into()), state: SnailState::Idle(0), @@ -272,7 +272,7 @@ impl<'a> Snail<'a> { fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, level: &Level, player_pos: Vector2D, hat_state: HatState, @@ -298,7 +298,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_IDLE.animation_sprite(0); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); if player_has_collided { @@ -318,7 +318,7 @@ impl<'a> Snail<'a> { self.enemy_info.entity.velocity = (0, 0).into(); let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -340,7 +340,7 @@ impl<'a> Snail<'a> { let offset = (timer - time) as usize / 8; let frame = SNAIL_MOVE.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -374,7 +374,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); @@ -403,7 +403,7 @@ impl<'a> Snail<'a> { return UpdateState::Remove; }; - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index 3f9337c9..05841ec1 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -8,7 +8,7 @@ extern crate alloc; use agb::{ display::{ - object::{Graphics, OamManager, Object, Tag, TagMap}, + object::{Graphics, OamManaged, Object, Tag, TagMap}, tiled::{ InfiniteScrolledMap, PartialUpdateStatus, RegularBackgroundSize, TileFormat, TileSet, TileSetting, TiledMap, VRamManager, @@ -124,8 +124,8 @@ pub struct Entity<'a> { } impl<'a> Entity<'a> { - pub fn new(object: &'a OamManager, collision_mask: Vector2D) -> Self { - let mut dummy_object = object.add_object_static_sprite(WALKING.sprite(0)); + pub fn new(object: &'a OamManaged, collision_mask: Vector2D) -> Self { + let mut dummy_object = object.object_sprite(WALKING.sprite(0)); dummy_object.set_priority(Priority::P1); Entity { sprite: dummy_object, @@ -347,15 +347,15 @@ fn ping_pong(i: i32, n: i32) -> i32 { } impl<'a> Player<'a> { - fn new(controller: &'a OamManager, start_position: Vector2D) -> Self { + fn new(controller: &'a OamManaged, start_position: Vector2D) -> Self { let mut wizard = Entity::new(controller, (6_u16, 14_u16).into()); let mut hat = Entity::new(controller, (6_u16, 6_u16).into()); wizard .sprite - .set_sprite(controller.get_vram_sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.get_sprite(HAT_SPIN_1.sprite(0))); hat.sprite - .set_sprite(controller.get_vram_sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.get_sprite(HAT_SPIN_1.sprite(0))); wizard.sprite.show(); hat.sprite.show(); @@ -381,7 +381,7 @@ impl<'a> Player<'a> { fn update_frame( &mut self, input: &ButtonController, - controller: &'a OamManager, + controller: &'a OamManaged, timer: i32, level: &Level, enemies: &[enemies::Enemy], @@ -460,7 +460,7 @@ impl<'a> Player<'a> { self.wizard_frame = offset as u8; let frame = WALKING.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -470,7 +470,7 @@ impl<'a> Player<'a> { self.wizard_frame = 5; let frame = JUMPING.animation_sprite(0); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.wizard.sprite.set_sprite(sprite); } else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 { @@ -485,7 +485,7 @@ impl<'a> Player<'a> { self.wizard_frame = 0; let frame = FALLING.animation_sprite(offset); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -512,13 +512,13 @@ impl<'a> Player<'a> { self.wizard.sprite.set_hflip(true); self.hat .sprite - .set_sprite(controller.get_vram_sprite(hat_base_tile.sprite(5))); + .set_sprite(controller.get_sprite(hat_base_tile.sprite(5))); } agb::input::Tri::Positive => { self.wizard.sprite.set_hflip(false); self.hat .sprite - .set_sprite(controller.get_vram_sprite(hat_base_tile.sprite(0))); + .set_sprite(controller.get_sprite(hat_base_tile.sprite(0))); } _ => {} } @@ -544,7 +544,7 @@ impl<'a> Player<'a> { let hat_sprite_offset = (timer / hat_sprite_divider) as usize; self.hat.sprite.set_sprite( - controller.get_vram_sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), + controller.get_sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), ); if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() { @@ -577,7 +577,7 @@ impl<'a> Player<'a> { } HatState::WizardTowards => { self.hat.sprite.set_sprite( - controller.get_vram_sprite(hat_base_tile.animation_sprite(timer as usize / 2)), + controller.get_sprite(hat_base_tile.animation_sprite(timer as usize / 2)), ); let distance_vector = self.hat.position - self.wizard.position + hat_resting_position; @@ -615,7 +615,7 @@ enum UpdateState { impl<'a, 'b> PlayingLevel<'a, 'b> { fn open_level( level: &'a Level, - object_control: &'a OamManager, + object_control: &'a OamManaged, background: &'a mut InfiniteScrolledMap<'b>, foreground: &'a mut InfiniteScrolledMap<'b>, input: ButtonController, @@ -676,11 +676,11 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { self.player.wizard.sprite.set_priority(Priority::P0); } - fn dead_update(&mut self, controller: &'a OamManager) -> bool { + fn dead_update(&mut self, controller: &'a OamManaged) -> bool { self.timer += 1; let frame = PLAYER_DEATH.animation_sprite(self.timer as usize / 8); - let sprite = controller.get_vram_sprite(frame); + let sprite = controller.get_sprite(frame); self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); self.player.wizard.position += self.player.wizard.velocity; @@ -695,7 +695,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { &mut self, sfx_player: &mut SfxPlayer, vram: &mut VRamManager, - controller: &'a OamManager, + controller: &'a OamManaged, ) -> UpdateState { self.timer += 1; self.input.update(); diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 4b68b1d0..de8b0bea 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -14,7 +14,7 @@ use alloc::{boxed::Box, vec::Vec}; use agb::{ display::{ - object::{Graphics, OamManager, Object, Sprite, Tag, TagMap}, + object::{Graphics, OamManaged, Object, Sprite, Tag, TagMap}, tiled::{ InfiniteScrolledMap, RegularBackgroundSize, TileFormat, TileSet, TileSetting, VRamManager, @@ -164,8 +164,8 @@ struct Entity<'a> { } impl<'a> Entity<'a> { - fn new(object_controller: &'a OamManager, collision_mask: Rect) -> Self { - let mut sprite = object_controller.add_object_static_sprite(LONG_SWORD_IDLE.sprite(0)); + fn new(object_controller: &'a OamManaged, collision_mask: Rect) -> Self { + let mut sprite = object_controller.object_sprite(LONG_SWORD_IDLE.sprite(0)); sprite.set_priority(Priority::P1); Entity { sprite, @@ -532,12 +532,12 @@ struct Player<'a> { } impl<'a> Player<'a> { - fn new(object_controller: &'a OamManager<'a>) -> Player { + fn new(object_controller: &'a OamManaged<'_>) -> Player<'a> { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()), ); - let s = object_controller.get_vram_sprite(LONG_SWORD_IDLE.sprite(0)); + let s = object_controller.get_sprite(LONG_SWORD_IDLE.sprite(0)); entity.sprite.set_sprite(s); entity.sprite.show(); entity.position = (144, 0).into(); @@ -558,7 +558,7 @@ impl<'a> Player<'a> { fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, buttons: &ButtonController, level: &Level, sfx: &mut sfx::Sfx, @@ -590,11 +590,11 @@ impl<'a> Player<'a> { self.entity.velocity.x += self.sword.ground_walk_force() * x as i32; if self.entity.velocity.x.abs() > Number::new(1) / 10 { let sprite = controller - .get_vram_sprite(self.sword.walk_animation(self.sprite_offset)); + .get_sprite(self.sword.walk_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } else { let sprite = controller - .get_vram_sprite(self.sword.idle_animation(self.sprite_offset)); + .get_sprite(self.sword.idle_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } @@ -614,8 +614,7 @@ impl<'a> Player<'a> { let frame = self.sword.attack_frame(*a); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; let tag = self.sword.attack_tag(); - let sprite = - controller.get_vram_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.ground_attack_hurtbox(frame); @@ -629,8 +628,7 @@ impl<'a> Player<'a> { let frame = self.sword.hold_frame(); self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32; let tag = self.sword.attack_tag(); - let sprite = - controller.get_vram_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if *a == 0 { self.attack_timer = AttackTimer::Idle; @@ -655,8 +653,7 @@ impl<'a> Player<'a> { 2 }; let tag = self.sword.jump_tag(); - let sprite = - controller.get_vram_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if x != Tri::Zero { @@ -678,8 +675,7 @@ impl<'a> Player<'a> { *a -= 1; let frame = self.sword.jump_attack_frame(*a); let tag = self.sword.jump_attack_tag(); - let sprite = - controller.get_vram_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.air_attack_hurtbox(frame); @@ -814,7 +810,7 @@ impl BatData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -842,7 +838,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 8); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -877,7 +873,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -904,7 +900,7 @@ impl BatData { BatState::Dead => { const BAT_DEAD: &Tag = TAG_MAP.get("bat dead"); let sprite = BAT_DEAD.sprite(0); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -947,7 +943,7 @@ impl SlimeData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -972,7 +968,7 @@ impl SlimeData { const IDLE: &Tag = TAG_MAP.get("slime idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1012,7 +1008,7 @@ impl SlimeData { const CHASE: &Tag = TAG_MAP.get("Slime jump"); let sprite = CHASE.sprite(frame as usize); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1042,7 +1038,7 @@ impl SlimeData { if *count < 5 * 4 { const DEATH: &Tag = TAG_MAP.get("Slime death"); let sprite = DEATH.sprite(*count as usize / 4); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); *count += 1; @@ -1076,7 +1072,7 @@ impl MiniFlameData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1110,7 +1106,7 @@ impl MiniFlameData { } } else { let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 8); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into(); @@ -1158,7 +1154,7 @@ impl MiniFlameData { } let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); } MiniFlameState::Dead => { @@ -1170,7 +1166,7 @@ impl MiniFlameData { const DEATH: &Tag = TAG_MAP.get("angry boss dead"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 12); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1205,7 +1201,7 @@ impl EmuData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1231,7 +1227,7 @@ impl EmuData { const IDLE: &Tag = TAG_MAP.get("emu - idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); if (entity.position.y - player.entity.position.y).abs() < 10.into() { @@ -1278,7 +1274,7 @@ impl EmuData { const WALK: &Tag = TAG_MAP.get("emu-walk"); let sprite = WALK.sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); let gravity: Number = 1.into(); @@ -1333,7 +1329,7 @@ impl EmuData { const DEATH: &Tag = TAG_MAP.get("emu - die"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 4); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1378,7 +1374,7 @@ impl EnemyData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, level: &Level, @@ -1399,11 +1395,11 @@ struct Enemy<'a> { } impl<'a> Enemy<'a> { - fn new(object_controller: &'a OamManager, enemy_data: EnemyData) -> Self { + fn new(object_controller: &'a OamManaged, enemy_data: EnemyData) -> Self { let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); let sprite = enemy_data.sprite(); - let sprite = object_controller.get_vram_sprite(sprite); + let sprite = object_controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); entity.sprite.show(); @@ -1413,7 +1409,7 @@ impl<'a> Enemy<'a> { fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, player: &Player, level: &Level, sfx: &mut sfx::Sfx, @@ -1444,7 +1440,7 @@ impl ParticleData { fn update<'a>( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, entity: &mut Entity<'a>, player: &Player, _level: &Level, @@ -1457,7 +1453,7 @@ impl ParticleData { const DUST: &Tag = TAG_MAP.get("dust"); let sprite = DUST.sprite(*frame as usize / 3); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1471,7 +1467,7 @@ impl ParticleData { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1497,7 +1493,7 @@ impl ParticleData { ParticleData::BossHealer(frame, target) => { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1532,7 +1528,7 @@ struct Particle<'a> { impl<'a> Particle<'a> { fn new( - object_controller: &'a OamManager, + object_controller: &'a OamManaged, particle_data: ParticleData, position: Vector2D, ) -> Self { @@ -1551,7 +1547,7 @@ impl<'a> Particle<'a> { fn update( &mut self, - controller: &'a OamManager, + controller: &'a OamManaged, player: &Player, level: &Level, ) -> UpdateInstruction { @@ -1578,7 +1574,7 @@ impl<'a> BossState<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a OamManager, + object_controller: &'a OamManaged, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1613,7 +1609,7 @@ struct FollowingBoss<'a> { } impl<'a> FollowingBoss<'a> { - fn new(object_controller: &'a OamManager, position: Vector2D) -> Self { + fn new(object_controller: &'a OamManaged, position: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()), @@ -1628,7 +1624,7 @@ impl<'a> FollowingBoss<'a> { gone: false, } } - fn update(&mut self, controller: &'a OamManager, player: &Player) { + fn update(&mut self, controller: &'a OamManaged, player: &Player) { let difference = player.entity.position - self.entity.position; self.timer += 1; @@ -1661,7 +1657,7 @@ impl<'a> FollowingBoss<'a> { const BOSS: &Tag = TAG_MAP.get("happy boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = controller.get_vram_sprite(sprite); + let sprite = controller.get_sprite(sprite); self.entity.sprite.set_sprite(sprite); @@ -1697,7 +1693,7 @@ enum BossInstruction { } impl<'a> Boss<'a> { - fn new(object_controller: &'a OamManager, screen_coords: Vector2D) -> Self { + fn new(object_controller: &'a OamManaged, screen_coords: Vector2D) -> Self { let mut entity = Entity::new( object_controller, Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()), @@ -1716,7 +1712,7 @@ impl<'a> Boss<'a> { fn update( &mut self, enemies: &mut Arena>, - object_controller: &'a OamManager, + object_controller: &'a OamManaged, player: &Player, sfx: &mut sfx::Sfx, ) -> BossInstruction { @@ -1797,7 +1793,7 @@ impl<'a> Boss<'a> { const BOSS: &Tag = TAG_MAP.get("Boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = object_controller.get_vram_sprite(sprite); + let sprite = object_controller.get_sprite(sprite); self.entity.sprite.set_sprite(sprite); @@ -1820,7 +1816,7 @@ impl<'a> Boss<'a> { self.entity .commit_with_size(offset + shake, (32, 32).into()); } - fn explode(&self, enemies: &mut Arena>, object_controller: &'a OamManager) { + fn explode(&self, enemies: &mut Arena>, object_controller: &'a OamManaged) { for _ in 0..(6 - self.health) { let x_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; let y_offset: Number = Number::from_raw(rng::gen()).rem_euclid(2.into()) - 1; @@ -1894,7 +1890,7 @@ impl<'a> Game<'a> { fn advance_frame( &mut self, - object_controller: &'a OamManager, + object_controller: &'a OamManaged, vram: &mut VRamManager, sfx: &mut sfx::Sfx, ) -> GameStatus { @@ -2108,7 +2104,7 @@ impl<'a> Game<'a> { } } - fn load_enemies(&mut self, object_controller: &'a OamManager) { + fn load_enemies(&mut self, object_controller: &'a OamManaged) { if self.slime_load < self.level.slime_spawns.len() { for (idx, slime_spawn) in self .level @@ -2178,7 +2174,7 @@ impl<'a> Game<'a> { vram.set_background_palettes(&modified_palettes); } - fn new(object: &'a OamManager<'a>, level: Level<'a>, start_at_boss: bool) -> Self { + fn new(object: &'a OamManaged<'a>, level: Level<'a>, start_at_boss: bool) -> Self { let mut player = Player::new(object); let mut offset = (8, 8).into(); if start_at_boss { From c6084582479143e67a7e65bda73d5f99476c65c7 Mon Sep 17 00:00:00 2001 From: Corwin Date: Wed, 5 Apr 2023 19:29:29 +0100 Subject: [PATCH 26/39] start on writing docs. A long way to go... --- agb/src/display/mod.rs | 1 - agb/src/display/object.rs | 13 +++- agb/src/display/object/affine.rs | 8 ++ agb/src/display/object/sprites.rs | 2 +- agb/src/display/object/sprites/sprite.rs | 1 + .../object/sprites/sprite_allocator.rs | 74 +++++++++++++------ .../display/object/unmanaged/attributes.rs | 3 + agb/src/display/palette16.rs | 1 - 8 files changed, 77 insertions(+), 26 deletions(-) diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index bc6f6d16..0d6dd6d2 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -16,7 +16,6 @@ pub mod bitmap3; pub mod bitmap4; /// Test logo of agb. pub mod example_logo; -/// Implements sprites. pub mod object; /// Palette type. pub mod palette16; diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index 281b8253..dfecf718 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -1,10 +1,21 @@ +#![warn(missing_docs)] +//! # Sprites and objects +//! +//! There are two implementations of objects depending on how you want to make +//! your game. There is the *Managed* and *Unmanaged* systems, given by +//! [OamManaged] and [OamUnmanaged] respectively. The managed Oam is easier to +//! use and has built in support for setting the `z` coordinate. The unmanaged +//! Oam is simpler and more efficient with the tradeoff that it is slightly +//! harder to integrate into your games depending on how they are architectured. + mod affine; mod managed; mod sprites; mod unmanaged; pub use sprites::{ - include_aseprite, DynamicSprite, Graphics, Size, Sprite, SpriteLoader, SpriteVram, Tag, TagMap, + include_aseprite, DynamicSprite, Graphics, PaletteVram, Size, Sprite, SpriteLoader, SpriteVram, + Tag, TagMap, }; pub use affine::AffineMatrixInstance; diff --git a/agb/src/display/object/affine.rs b/agb/src/display/object/affine.rs index 703ab160..f10b9007 100644 --- a/agb/src/display/object/affine.rs +++ b/agb/src/display/object/affine.rs @@ -14,6 +14,11 @@ struct AffineMatrixData { #[derive(Debug, Clone)] pub(crate) struct AffineMatrixVram(Rc); +/// An affine matrix that can be used on objects. It is just in time copied to +/// vram, so you can have as many as you like of these but you can only use up +/// to 16 in one frame. They are reference counted (Cloning is cheap) and +/// immutable, if you want to change a matrix you must make a new one and set it +/// on all your objects. #[derive(Debug, Clone)] pub struct AffineMatrixInstance { location: AffineMatrixVram, @@ -21,6 +26,9 @@ pub struct AffineMatrixInstance { impl AffineMatrixInstance { #[must_use] + /// Creates an instance of an affine matrix from its object form. Check out + /// the docs for [AffineMatrix][crate::display::affine::AffineMatrix] to see + /// how you can use them to create effects. pub fn new(affine_matrix: AffineMatrixObject) -> AffineMatrixInstance { AffineMatrixInstance { location: AffineMatrixVram(Rc::new(AffineMatrixData { diff --git a/agb/src/display/object/sprites.rs b/agb/src/display/object/sprites.rs index c384be6a..d9b3d9cf 100644 --- a/agb/src/display/object/sprites.rs +++ b/agb/src/display/object/sprites.rs @@ -4,4 +4,4 @@ mod sprite_allocator; const BYTES_PER_TILE_4BPP: usize = 32; pub use sprite::{include_aseprite, Graphics, Size, Sprite, Tag, TagMap}; -pub use sprite_allocator::{DynamicSprite, SpriteLoader, SpriteVram}; +pub use sprite_allocator::{DynamicSprite, PaletteVram, SpriteLoader, SpriteVram}; diff --git a/agb/src/display/object/sprites/sprite.rs b/agb/src/display/object/sprites/sprite.rs index 49774897..6751c47d 100644 --- a/agb/src/display/object/sprites/sprite.rs +++ b/agb/src/display/object/sprites/sprite.rs @@ -28,6 +28,7 @@ impl Sprite { } #[must_use] + /// Gives the size of the sprite pub fn size(&self) -> Size { self.size } diff --git a/agb/src/display/object/sprites/sprite_allocator.rs b/agb/src/display/object/sprites/sprite_allocator.rs index 0c8d02a2..ae28f908 100644 --- a/agb/src/display/object/sprites/sprite_allocator.rs +++ b/agb/src/display/object/sprites/sprite_allocator.rs @@ -87,14 +87,17 @@ impl Drop for PaletteVramData { } } -#[derive(Debug)] +/// A palette in vram, this is reference counted so it is cheap to Clone. +#[derive(Debug, Clone)] pub struct PaletteVram { data: Rc, } impl PaletteVram { - fn new(palette: &Palette16) -> Option { - let allocated = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout()) }?; + /// Attempts to allocate a new palette in sprite vram + pub fn new(palette: &Palette16) -> Result { + let allocated = unsafe { PALETTE_ALLOCATOR.alloc(Palette16::layout()) } + .ok_or(LoaderError::PaletteFull)?; unsafe { allocated @@ -103,7 +106,7 @@ impl PaletteVram { .copy_from_nonoverlapping(palette.colours.as_ptr(), palette.colours.len()); } - Some(PaletteVram { + Ok(PaletteVram { data: Rc::new(PaletteVramData { location: Location::from_palette_ptr(allocated), }), @@ -124,20 +127,37 @@ impl Drop for SpriteVramData { } } +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum LoaderError { + SpriteFull, + PaletteFull, +} + +/// A sprite that is currently loaded into vram. +/// +/// This is referenced counted such that clones of this are cheap and can be +/// reused between objects. When nothing references the sprite it gets +/// deallocated from vram. +/// +/// You can create one of these either via the [DynamicSprite] interface, which +/// allows you to generate sprites at run time, or via a [SpriteLoader] (or +/// [OamManaged][super::super::OamManaged]). #[derive(Clone, Debug)] pub struct SpriteVram { data: Rc, } impl SpriteVram { - fn new(data: &[u8], size: Size, palette: PaletteVram) -> Option { - let allocated = unsafe { SPRITE_ALLOCATOR.alloc(size.layout()) }?; + fn new(data: &[u8], size: Size, palette: PaletteVram) -> Result { + let allocated = + unsafe { SPRITE_ALLOCATOR.alloc(size.layout()) }.ok_or(LoaderError::SpriteFull)?; unsafe { allocated .as_ptr() .copy_from_nonoverlapping(data.as_ptr(), data.len()); } - Some(SpriteVram { + Ok(SpriteVram { data: Rc::new(SpriteVramData { location: Location::from_sprite_ptr(allocated), size, @@ -163,19 +183,19 @@ impl SpriteLoader { fn create_sprite_no_insert( palette_map: &mut HashMap>, sprite: &'static Sprite, - ) -> Option<(Weak, SpriteVram)> { + ) -> Result<(Weak, SpriteVram), LoaderError> { let palette = Self::try_get_vram_palette_asoc(palette_map, sprite.palette)?; let sprite = SpriteVram::new(sprite.data, sprite.size, palette)?; - Some((Rc::downgrade(&sprite.data), sprite)) + Ok((Rc::downgrade(&sprite.data), sprite)) } fn try_get_vram_palette_asoc( palette_map: &mut HashMap>, palette: &'static Palette16, - ) -> Option { + ) -> Result { let id = PaletteId::from_static_palette(palette); - Some(match palette_map.entry(id) { + Ok(match palette_map.entry(id) { crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { Some(data) => PaletteVram { data }, None => { @@ -192,12 +212,16 @@ impl SpriteLoader { }) } - pub fn try_get_vram_sprite(&mut self, sprite: &'static Sprite) -> Option { + /// Attempts to get a sprite + pub fn try_get_vram_sprite( + &mut self, + sprite: &'static Sprite, + ) -> Result { // check if we already have the sprite in vram let id = SpriteId::from_static_sprite(sprite); - Some(match self.static_sprite_map.entry(id) { + Ok(match self.static_sprite_map.entry(id) { crate::hash_map::Entry::Occupied(mut entry) => match entry.get().upgrade() { Some(data) => SpriteVram { data }, None => { @@ -216,18 +240,24 @@ impl SpriteLoader { }) } - pub fn try_get_vram_palette(&mut self, palette: &'static Palette16) -> Option { + /// Attempts to allocate a static palette + pub fn try_get_vram_palette( + &mut self, + palette: &'static Palette16, + ) -> Result { Self::try_get_vram_palette_asoc(&mut self.static_palette_map, palette) } + /// Allocates a sprite to vram, panics if it cannot fit. pub fn get_vram_sprite(&mut self, sprite: &'static Sprite) -> SpriteVram { self.try_get_vram_sprite(sprite) - .expect("no free sprite slots") + .expect("cannot create sprite") } + /// Allocates a palette to vram, panics if it cannot fit. pub fn get_vram_palette(&mut self, palette: &'static Palette16) -> PaletteVram { self.try_get_vram_palette(palette) - .expect("no free palette slots") + .expect("cannot create sprite") } pub(crate) fn new() -> Self { @@ -237,6 +267,9 @@ impl SpriteLoader { } } + /// Remove internal references to sprites that no longer exist in vram. If + /// you neglect calling this, memory will leak over time in relation to the + /// total number of different sprites used. It will not leak vram. pub fn garbage_collect(&mut self) { self.static_sprite_map .retain(|_, v| Weak::strong_count(v) != 0); @@ -277,18 +310,15 @@ impl DynamicSprite<'_> { DynamicSprite { data, size } } - #[must_use] /// Tries to copy the sprite to vram to be used to set object sprites. - /// Returns None if there is no room in sprite vram. - pub fn try_vram(&self, palette: PaletteVram) -> Option { + pub fn try_vram(&self, palette: PaletteVram) -> Result { SpriteVram::new(self.data, self.size, palette) } #[must_use] /// Tries to copy the sprite to vram to be used to set object sprites. - /// Panics if there is no room in sprite vram. + /// Panics if it cannot be allocated. pub fn to_vram(&self, palette: PaletteVram) -> SpriteVram { - self.try_vram(palette) - .expect("No slot for sprite available") + self.try_vram(palette).expect("cannot create sprite") } } diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index 7223d6ae..2ae52059 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -26,8 +26,11 @@ impl Default for Attributes { } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +/// The affine mode pub enum AffineMode { + /// Normal affine, this is where the area of the affine is equal to the sprite size Affine = 1, + /// Double affine, this is where the area of the affine is double that of the sprite AffineDouble = 3, } diff --git a/agb/src/display/palette16.rs b/agb/src/display/palette16.rs index 449eb31c..7296b5bd 100644 --- a/agb/src/display/palette16.rs +++ b/agb/src/display/palette16.rs @@ -14,7 +14,6 @@ impl Palette16 { // Clippy bug: claims that index is only used in recursion. I can't reproduce in // other examples, even just copy pasting this struct and impl into a blank project :/ - #[allow(clippy::only_used_in_recursion)] pub fn update_colour(&mut self, index: usize, colour: u16) { self.colours[index] = colour; } From 4f19d6c2402abb586d8cf8b5256a4e17b3d4e157 Mon Sep 17 00:00:00 2001 From: Corwin Date: Thu, 6 Apr 2023 17:16:01 +0100 Subject: [PATCH 27/39] forbid slot drop and move slot on set --- agb/src/display/object/managed.rs | 2 +- agb/src/display/object/unmanaged/object.rs | 75 ++++++++++++++++++---- examples/hyperspace-roll/Cargo.lock | 21 +----- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 5e0f3815..0194cfa4 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -158,7 +158,7 @@ impl OamManaged<'_> { // safety: commit is not reentrant let unmanaged = unsafe { &mut *self.unmanaged.get() }; - for (object, mut slot) in unsafe { self.object_store.iter() } + for (object, slot) in unsafe { self.object_store.iter() } .map(|item| unsafe { &*item.object.get() }) .filter(|object| object.is_visible()) .zip(unmanaged.iter()) diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index 30387b21..f0d4e1d2 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -15,7 +15,6 @@ use super::attributes::{AffineMode, Attributes}; #[derive(Default, Debug)] struct OamFrameModifyables { - up_to: i32, this_frame_sprites: Vec, frame: u32, affine_matrix_count: u32, @@ -32,17 +31,42 @@ pub struct OamIterator<'oam> { frame_data: &'oam UnsafeCell, } +/// A slot in Oam that you can write to. Note that you must call [OamSlot::set] +/// or else it is a bug and will panic when dropped. pub struct OamSlot<'oam> { slot: usize, frame_data: &'oam UnsafeCell, } +impl Drop for OamSlot<'_> { + #[track_caller] + fn drop(&mut self) { + panic!("Dropping an OamSlot is a bug in your code. Use the slot by calling set (this consumes the slot) or don't obtain one. See documentation for notes on potential pitfalls.") + } +} + impl OamSlot<'_> { - pub fn set(&mut self, object: &ObjectUnmanaged) { + /// Set the slot in OAM to contain the sprite given. + pub fn set(mut self, object: &ObjectUnmanaged) { let mut attributes = object.attributes; // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. let frame_data = unsafe { &mut *self.frame_data.get() }; + Self::handle_affine(&mut attributes, frame_data, object); + self.set_bytes(attributes.bytes()); + + frame_data.this_frame_sprites.push(object.sprite.clone()); + + // don't call the drop implementation. + // okay as none of the fields we have have drop implementations. + core::mem::forget(self); + } + + fn handle_affine( + attributes: &mut Attributes, + frame_data: &mut OamFrameModifyables, + object: &ObjectUnmanaged, + ) { if let Some(affine_matrix) = &object.affine_matrix { if affine_matrix.frame_count() != frame_data.frame { affine_matrix.set_frame_count(frame_data.frame); @@ -57,12 +81,6 @@ impl OamSlot<'_> { attributes.set_affine_matrix(affine_matrix.location() as u16); } - - self.set_bytes(attributes.bytes()); - - frame_data.this_frame_sprites.push(object.sprite.clone()); - - frame_data.up_to = self.slot as i32; } fn set_bytes(&mut self, bytes: [u8; 6]) { @@ -93,9 +111,7 @@ impl<'oam> Iterator for OamIterator<'oam> { impl Drop for OamIterator<'_> { fn drop(&mut self) { - let last_written = unsafe { &*self.frame_data.get() }.up_to; - - let number_writen = (last_written + 1) as usize; + let number_writen = self.index; for idx in number_writen..128 { unsafe { @@ -109,7 +125,6 @@ impl Drop for OamIterator<'_> { impl OamUnmanaged<'_> { pub fn iter(&mut self) -> OamIterator<'_> { let frame_data = self.frame_data.get_mut(); - frame_data.up_to = -1; frame_data.frame = frame_data.frame.wrapping_add(1); frame_data.affine_matrix_count = 0; @@ -252,3 +267,39 @@ impl ObjectUnmanaged { self } } + +#[cfg(test)] +mod tests { + use crate::{ + display::object::{Graphics, Tag}, + include_aseprite, + }; + + use super::*; + + #[test_case] + fn object_usage(gba: &mut crate::Gba) { + const GRAPHICS: &Graphics = include_aseprite!( + "../examples/the-purple-night/gfx/objects.aseprite", + "../examples/the-purple-night/gfx/boss.aseprite" + ); + + const BOSS: &Tag = GRAPHICS.tags().get("Boss"); + + let (mut gfx, mut loader) = gba.display.object.get_unmanaged(); + + { + let mut slotter = gfx.iter(); + + let slot_a = slotter.next().unwrap(); + let slot_b = slotter.next().unwrap(); + + let mut obj = ObjectUnmanaged::new(loader.get_vram_sprite(BOSS.sprite(2))); + + obj.show(); + + slot_b.set(&obj); + slot_a.set(&obj); + } + } +} diff --git a/examples/hyperspace-roll/Cargo.lock b/examples/hyperspace-roll/Cargo.lock index c30998d2..36c5d3b4 100644 --- a/examples/hyperspace-roll/Cargo.lock +++ b/examples/hyperspace-roll/Cargo.lock @@ -23,7 +23,7 @@ dependencies = [ "agb_macros", "agb_sound_converter", "bare-metal", - "bitflags 2.0.2", + "bitflags 2.1.0", "modular-bitfield", "rustc-hash", ] @@ -113,9 +113,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" [[package]] name = "bytemuck" @@ -423,21 +423,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -<<<<<<< HEAD -======= -name = "slotmap" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" -dependencies = [ - "version_check", -] - -[[package]] ->>>>>>> ff9ab890 (fix games) -======= ->>>>>>> bd1a51df (unsafe arena added (may remove later)) name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" From f59e4ad32228a354fe77e4098d1e2b26e96dd5aa Mon Sep 17 00:00:00 2001 From: Corwin Date: Fri, 7 Apr 2023 01:40:27 +0100 Subject: [PATCH 28/39] various micro optimisations --- .../display/object/unmanaged/attributes.rs | 33 ++++---- agb/src/display/object/unmanaged/object.rs | 82 +++++++++++-------- 2 files changed, 64 insertions(+), 51 deletions(-) diff --git a/agb/src/display/object/unmanaged/attributes.rs b/agb/src/display/object/unmanaged/attributes.rs index 2ae52059..2ae0d948 100644 --- a/agb/src/display/object/unmanaged/attributes.rs +++ b/agb/src/display/object/unmanaged/attributes.rs @@ -35,23 +35,26 @@ pub enum AffineMode { } impl Attributes { - pub fn bytes(self) -> [u8; 6] { + pub fn write(self, ptr: *mut u16) { let mode = self.a0.object_mode(); - let attrs = match mode { - ObjectMode::Normal => [ - self.a0.into_bytes(), - self.a1s.into_bytes(), - self.a2.into_bytes(), - ], - _ => [ - self.a0.into_bytes(), - self.a1a.into_bytes(), - self.a2.into_bytes(), - ], - }; + unsafe { + let attrs = core::mem::transmute::<_, [u16; 3]>(match mode { + ObjectMode::Normal => [ + self.a0.into_bytes(), + self.a1s.into_bytes(), + self.a2.into_bytes(), + ], + _ => [ + self.a0.into_bytes(), + self.a1a.into_bytes(), + self.a2.into_bytes(), + ], + }); - // Safety: length and alignment are the same, and every possible value is valid - unsafe { core::mem::transmute(attrs) } + ptr.add(0).write_volatile(attrs[0]); + ptr.add(1).write_volatile(attrs[1]); + ptr.add(2).write_volatile(attrs[2]); + } } pub fn is_visible(self) -> bool { diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index f0d4e1d2..fb1c42de 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -13,11 +13,12 @@ use crate::display::{ use super::attributes::{AffineMode, Attributes}; -#[derive(Default, Debug)] +#[derive(Debug)] struct OamFrameModifyables { this_frame_sprites: Vec, frame: u32, affine_matrix_count: u32, + previous_index: usize, } pub struct OamUnmanaged<'gba> { @@ -47,60 +48,62 @@ impl Drop for OamSlot<'_> { impl OamSlot<'_> { /// Set the slot in OAM to contain the sprite given. - pub fn set(mut self, object: &ObjectUnmanaged) { - let mut attributes = object.attributes; - // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. - let frame_data = unsafe { &mut *self.frame_data.get() }; - - Self::handle_affine(&mut attributes, frame_data, object); - self.set_bytes(attributes.bytes()); - - frame_data.this_frame_sprites.push(object.sprite.clone()); + #[inline(always)] + pub fn set(self, object: &ObjectUnmanaged) { + self.set_inner(object); // don't call the drop implementation. // okay as none of the fields we have have drop implementations. core::mem::forget(self); } + /// By writing these as two separate functions, one inlined and one not, the + /// compiler doesn't have to copy around the slot structure while still + /// keeping move semantics. This is slightly faster in benchmarks. + #[inline(never)] + fn set_inner(&self, object: &ObjectUnmanaged) { + let mut attributes = object.attributes; + // SAFETY: This function is not reentrant and we currently hold a mutable borrow of the [UnmanagedOAM]. + let frame_data = unsafe { &mut *self.frame_data.get() }; + + if let Some(affine_matrix) = &object.affine_matrix { + Self::handle_affine(&mut attributes, frame_data, affine_matrix); + } + attributes.write(unsafe { (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(self.slot * 4) }); + + frame_data.this_frame_sprites.push(object.sprite.clone()); + } + fn handle_affine( attributes: &mut Attributes, frame_data: &mut OamFrameModifyables, - object: &ObjectUnmanaged, + affine_matrix: &AffineMatrixVram, ) { - if let Some(affine_matrix) = &object.affine_matrix { - if affine_matrix.frame_count() != frame_data.frame { - affine_matrix.set_frame_count(frame_data.frame); - assert!( - frame_data.affine_matrix_count <= 32, - "too many affine matricies in one frame" - ); - affine_matrix.set_location(frame_data.affine_matrix_count); - frame_data.affine_matrix_count += 1; - affine_matrix.write_to_location(OBJECT_ATTRIBUTE_MEMORY); - } - - attributes.set_affine_matrix(affine_matrix.location() as u16); + if affine_matrix.frame_count() != frame_data.frame { + affine_matrix.set_frame_count(frame_data.frame); + assert!( + frame_data.affine_matrix_count <= 32, + "too many affine matricies in one frame" + ); + affine_matrix.set_location(frame_data.affine_matrix_count); + frame_data.affine_matrix_count += 1; + affine_matrix.write_to_location(OBJECT_ATTRIBUTE_MEMORY); } - } - fn set_bytes(&mut self, bytes: [u8; 6]) { - unsafe { - let address = (OBJECT_ATTRIBUTE_MEMORY as *mut u8).add(self.slot * 8); - address.copy_from_nonoverlapping(bytes.as_ptr(), bytes.len()); - } + attributes.set_affine_matrix(affine_matrix.location() as u16); } } impl<'oam> Iterator for OamIterator<'oam> { type Item = OamSlot<'oam>; + #[inline(always)] fn next(&mut self) -> Option { let idx = self.index; - self.index += 1; - - if idx >= 128 { + if idx == 128 { None } else { + self.index += 1; Some(OamSlot { slot: idx, frame_data: self.frame_data, @@ -112,13 +115,15 @@ impl<'oam> Iterator for OamIterator<'oam> { impl Drop for OamIterator<'_> { fn drop(&mut self) { let number_writen = self.index; + let last_frame_written = unsafe { &mut (*self.frame_data.get()).previous_index }; - for idx in number_writen..128 { + for idx in number_writen..*last_frame_written { unsafe { let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(idx * 4); ptr.write_volatile(0b10 << 8); } } + *last_frame_written = number_writen; } } @@ -130,7 +135,7 @@ impl OamUnmanaged<'_> { // We drain the previous frame sprites here to reuse the Vecs allocation and remove the now unused sprites. // Any sprites currently being shown will now be put in the new Vec. - self.previous_frame_sprites.drain(..); + self.previous_frame_sprites.clear(); core::mem::swap( &mut frame_data.this_frame_sprites, &mut self.previous_frame_sprites, @@ -144,7 +149,12 @@ impl OamUnmanaged<'_> { pub(crate) fn new() -> Self { Self { - frame_data: Default::default(), + frame_data: UnsafeCell::new(OamFrameModifyables { + this_frame_sprites: Vec::new(), + frame: 0, + affine_matrix_count: 0, + previous_index: 0, + }), phantom: PhantomData, previous_frame_sprites: Default::default(), } From 6417570a5158df15217668b7a379501162740115 Mon Sep 17 00:00:00 2001 From: Corwin Date: Fri, 21 Apr 2023 17:14:51 +0100 Subject: [PATCH 29/39] add docs --- agb/src/display/object/managed.rs | 51 ++++++++++++--- agb/src/display/object/unmanaged/object.rs | 72 ++++++++++++++++++++-- 2 files changed, 110 insertions(+), 13 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 0194cfa4..f950a3f5 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -125,6 +125,15 @@ impl Store { } } +/// OAM that manages z ordering and commit all visible objects in one call. This +/// is simpler to use than the [`OamUnmanaged`], but is less performant +/// depending on how objects are stored. +/// +/// Use this if: +/// * You don't want to handle z ordering. +/// * You don't want to deal with the complexity of committing all objects during vblank. +/// +/// Otherwise I'd recommend using [`OamUnmanaged`]. pub struct OamManaged<'gba> { object_store: Store, sprite_loader: UnsafeCell, @@ -154,6 +163,8 @@ impl OamManaged<'_> { c(sprite_loader) } + /// Commits all the visible objects. Call during vblank to make changes made + /// to objects visible. pub fn commit(&self) { // safety: commit is not reentrant let unmanaged = unsafe { &mut *self.unmanaged.get() }; @@ -172,11 +183,13 @@ impl OamManaged<'_> { } } + /// Creates an object from the sprite in vram. pub fn object(&self, sprite: SpriteVram) -> Object<'_> { self.object_store .insert_object(ObjectUnmanaged::new(sprite)) } + /// Creates a sprite in vram from a static sprite from [`include_aseprite`][crate::include_aseprite]. pub fn get_sprite(&self, sprite: &'static Sprite) -> SpriteVram { // safety: not reentrant unsafe { @@ -184,11 +197,13 @@ impl OamManaged<'_> { } } + /// Creates a sprite in vram and uses it to make an object from a static sprite from [`include_aseprite`][crate::include_aseprite]. pub fn object_sprite(&self, sprite: &'static Sprite) -> Object<'_> { self.object(self.get_sprite(sprite)) } } +/// A managed object used with the [`OamManaged`] interface. pub struct Object<'controller> { me: ObjectKey, store: &'controller Store, @@ -291,6 +306,13 @@ fn move_after(store: &Store, source: ObjectKey, after_this: ObjectKey) { } impl Object<'_> { + /// Sets the z position of an object. This is not a GBA concept. It causes + /// the order of rendering to be different, thus changing whether objects + /// are rendered above eachother. + /// + /// Negative z is more towards the outside and positive z is further into + /// the screen => an object with a more *negative* z is drawn on top of an + /// object with a more *positive* z. pub fn set_z(&mut self, z_index: i32) -> &mut Self { let my_object = &self.store.get_object(self.me); @@ -350,11 +372,14 @@ impl Object<'_> { } #[must_use] + /// Checks whether the object is not marked as hidden. Note that it could be + /// off screen or completely transparent and still claimed to be visible. pub fn is_visible(&self) -> bool { // safety: only have one of these, doesn't modify slotmap unsafe { self.object_shared() }.is_visible() } + /// Display the sprite in Normal mode. pub fn show(&mut self) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().show() }; @@ -362,6 +387,7 @@ impl Object<'_> { self } + /// Display the sprite in Affine mode. pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().show_affine(affine_mode) }; @@ -369,6 +395,7 @@ impl Object<'_> { self } + /// Sets the horizontal flip, note that this only has a visible affect in Normal mode. pub fn set_hflip(&mut self, flip: bool) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_hflip(flip) }; @@ -376,6 +403,7 @@ impl Object<'_> { self } + /// Sets the vertical flip, note that this only has a visible affect in Normal mode. pub fn set_vflip(&mut self, flip: bool) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_vflip(flip) }; @@ -383,13 +411,7 @@ impl Object<'_> { self } - pub fn set_x(&mut self, x: u16) -> &mut Self { - // safety: only have one of these, doesn't modify slotmap - unsafe { self.object().set_x(x) }; - - self - } - + /// Sets the priority of the object relative to the backgrounds priority. pub fn set_priority(&mut self, priority: Priority) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_priority(priority) }; @@ -397,6 +419,9 @@ impl Object<'_> { self } + /// Changes the sprite mode to be hidden, can be changed to Normal or Affine + /// modes using [`show`][Object::show] and + /// [`show_affine`][Object::show_affine] respectively. pub fn hide(&mut self) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().hide() }; @@ -404,6 +429,15 @@ impl Object<'_> { self } + /// Sets the x position of the object. + pub fn set_x(&mut self, x: u16) -> &mut Self { + // safety: only have one of these, doesn't modify slotmap + unsafe { self.object().set_x(x) }; + + self + } + + /// Sets the y position of the object. pub fn set_y(&mut self, y: u16) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_y(y) }; @@ -411,6 +445,7 @@ impl Object<'_> { self } + /// Sets the position of the object. pub fn set_position(&mut self, position: Vector2D) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_position(position) }; @@ -418,6 +453,7 @@ impl Object<'_> { self } + /// Sets the affine matrix. This only has an affect in Affine mode. pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_affine_matrix(affine_matrix) }; @@ -425,6 +461,7 @@ impl Object<'_> { self } + /// Sets the current sprite for the object. pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { // safety: only have one of these, doesn't modify slotmap unsafe { self.object().set_sprite(sprite) }; diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index fb1c42de..fc9fd4ac 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -21,12 +21,51 @@ struct OamFrameModifyables { previous_index: usize, } +/// This handles the unmanaged oam system which gives more control to the OAM slots. +/// This is utilised by calling the iter function and writing objects to those slots. pub struct OamUnmanaged<'gba> { phantom: PhantomData<&'gba ()>, frame_data: UnsafeCell, previous_frame_sprites: Vec, } +/// The iterator over the OAM slots. Dropping this will finalise the frame. To +/// use, iterate over and write to each slot. +/// +/// For example, it could look like this: +/// +/// ```no_run +/// # #![no_main] +/// # #![no_std] +/// use agb::display::object::{OamIterator, ObjectUnmanaged}; +/// +/// fn write_to_oam(oam_iterator: OamIterator, objects: &[ObjectUnmanaged]) { +/// for (object, slot) in objects.iter().zip(oam_iterator) { +/// slot.set(&object); +/// } +/// } +/// ``` +/// +/// # Pitfalls +/// You *must* use each OamSlot you obtain, this can be an issue if instead of +/// the above you write +/// +/// ```no_run +/// # #![no_main] +/// # #![no_std] +/// use agb::display::object::{OamIterator, ObjectUnmanaged}; +/// +/// fn write_to_oam(oam_iterator: OamIterator, objects: &[ObjectUnmanaged]) { +/// for (slot, object) in oam_iterator.iter().zip(objects.iter()) { +/// slot.set(&object); +/// } +/// } +/// ``` +/// +/// This will panic if called because when you run out of objects the zip will +/// have already grabbed the next OamSlot before realising there are no more +/// objects. + pub struct OamIterator<'oam> { index: usize, frame_data: &'oam UnsafeCell, @@ -34,6 +73,8 @@ pub struct OamIterator<'oam> { /// A slot in Oam that you can write to. Note that you must call [OamSlot::set] /// or else it is a bug and will panic when dropped. +/// +/// See [`OamIterator`] for potential pitfalls. pub struct OamSlot<'oam> { slot: usize, frame_data: &'oam UnsafeCell, @@ -128,6 +169,7 @@ impl Drop for OamIterator<'_> { } impl OamUnmanaged<'_> { + /// Returns the OamSlot iterator for this frame. pub fn iter(&mut self) -> OamIterator<'_> { let frame_data = self.frame_data.get_mut(); frame_data.frame = frame_data.frame.wrapping_add(1); @@ -162,6 +204,8 @@ impl OamUnmanaged<'_> { } #[derive(Debug, Clone)] +/// An object to be used by the [`OamUnmanaged`] system. Changes made here are +/// reflected when set to an OamSlot using [`OamSlot::set`]. pub struct ObjectUnmanaged { attributes: Attributes, sprite: SpriteVram, @@ -170,6 +214,7 @@ pub struct ObjectUnmanaged { impl ObjectUnmanaged { #[must_use] + /// Creates an unmanaged object from a sprite in vram. pub fn new(sprite: SpriteVram) -> Self { let sprite_location = sprite.location(); let palette_location = sprite.palette_location(); @@ -188,16 +233,20 @@ impl ObjectUnmanaged { } #[must_use] + /// Checks whether the object is not marked as hidden. Note that it could be + /// off screen or completely transparent and still claimed to be visible. pub fn is_visible(&self) -> bool { self.attributes.is_visible() } + /// Display the sprite in Normal mode. pub fn show(&mut self) -> &mut Self { self.attributes.show(); self } + /// Display the sprite in Affine mode. pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self { assert!( self.affine_matrix.is_some(), @@ -209,42 +258,51 @@ impl ObjectUnmanaged { self } + /// Sets the horizontal flip, note that this only has a visible affect in Normal mode. pub fn set_hflip(&mut self, flip: bool) -> &mut Self { self.attributes.set_hflip(flip); self } + /// Sets the vertical flip, note that this only has a visible affect in Normal mode. pub fn set_vflip(&mut self, flip: bool) -> &mut Self { self.attributes.set_vflip(flip); self } - pub fn set_x(&mut self, x: u16) -> &mut Self { - self.attributes.set_x(x); - - self - } - + /// Sets the priority of the object relative to the backgrounds priority. pub fn set_priority(&mut self, priority: Priority) -> &mut Self { self.attributes.set_priority(priority); self } + /// Changes the sprite mode to be hidden, can be changed to Normal or Affine + /// modes using [`show`][ObjectUnmanaged::show] and + /// [`show_affine`][ObjectUnmanaged::show_affine] respectively. pub fn hide(&mut self) -> &mut Self { self.attributes.hide(); self } + /// Sets the x position of the object. + pub fn set_x(&mut self, x: u16) -> &mut Self { + self.attributes.set_x(x); + + self + } + + /// Sets the y position of the object. pub fn set_y(&mut self, y: u16) -> &mut Self { self.attributes.set_y(y); self } + /// Sets the position of the object. pub fn set_position(&mut self, position: Vector2D) -> &mut Self { self.set_y(position.y.rem_euclid(1 << 9) as u16); self.set_x(position.x.rem_euclid(1 << 9) as u16); @@ -252,6 +310,7 @@ impl ObjectUnmanaged { self } + /// Sets the affine matrix. This only has an affect in Affine mode. pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self { let vram = affine_matrix.vram(); self.affine_matrix = Some(vram); @@ -269,6 +328,7 @@ impl ObjectUnmanaged { self } + /// Sets the current sprite for the object. pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self { self.set_sprite_attributes(&sprite); From 7e1f996d9086ad26622887b7355765a39e2ce20e Mon Sep 17 00:00:00 2001 From: Corwin Date: Sat, 22 Apr 2023 00:40:06 +0100 Subject: [PATCH 30/39] correct doc test --- agb/src/display/object/unmanaged/object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index fc9fd4ac..fb5a42ef 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -56,7 +56,7 @@ pub struct OamUnmanaged<'gba> { /// use agb::display::object::{OamIterator, ObjectUnmanaged}; /// /// fn write_to_oam(oam_iterator: OamIterator, objects: &[ObjectUnmanaged]) { -/// for (slot, object) in oam_iterator.iter().zip(objects.iter()) { +/// for (slot, object) in oam_iterator.zip(objects.iter()) { /// slot.set(&object); /// } /// } From 14c7028aa06b28ac5fe4a90ddc43693e8228afd0 Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 23 Apr 2023 18:55:23 +0100 Subject: [PATCH 31/39] reverse change of name --- agb/examples/chicken.rs | 12 ++--- agb/examples/sprites.rs | 4 +- agb/src/display/object/managed.rs | 6 +-- examples/hyperspace-roll/src/battle.rs | 5 +- .../hyperspace-roll/src/battle/display.rs | 16 +++--- examples/hyperspace-roll/src/customise.rs | 5 +- examples/hyperspace-roll/src/graphics.rs | 11 ++-- .../the-hat-chooses-the-wizard/src/enemies.rs | 16 +++--- .../the-hat-chooses-the-wizard/src/lib.rs | 20 +++---- examples/the-purple-night/src/lib.rs | 54 +++++++++---------- 10 files changed, 70 insertions(+), 79 deletions(-) diff --git a/agb/examples/chicken.rs b/agb/examples/chicken.rs index 0ea1d435..9d56ce68 100644 --- a/agb/examples/chicken.rs +++ b/agb/examples/chicken.rs @@ -76,7 +76,7 @@ fn main(mut gba: agb::Gba) -> ! { let object = gba.display.object.get_managed(); - let sprite = object.get_sprite(&CHICKEN_SPRITES[0]); + let sprite = object.sprite(&CHICKEN_SPRITES[0]); let mut chicken = Character { object: object.object(sprite), position: Vector2D { @@ -157,20 +157,18 @@ fn update_chicken_object( match state { State::Ground => { if chicken.velocity.x.abs() > 1 << 4 { - chicken.object.set_sprite( - gfx.get_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)]), - ); - } else { chicken .object - .set_sprite(gfx.get_sprite(&CHICKEN_SPRITES[0])); + .set_sprite(gfx.sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 1, 3, 10)])); + } else { + chicken.object.set_sprite(gfx.sprite(&CHICKEN_SPRITES[0])); } } State::Upwards => {} State::Flapping => { chicken .object - .set_sprite(gfx.get_sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)])); + .set_sprite(gfx.sprite(&CHICKEN_SPRITES[frame_ranger(frame_count, 4, 5, 5)])); } } diff --git a/agb/examples/sprites.rs b/agb/examples/sprites.rs index 255512a5..161e4ed8 100644 --- a/agb/examples/sprites.rs +++ b/agb/examples/sprites.rs @@ -68,7 +68,7 @@ fn all_sprites(gfx: &OamManaged, rotation_speed: Num) { image %= SPRITES.len(); for (i, obj) in objs.iter_mut().enumerate() { let this_image = (image + i) % SPRITES.len(); - obj.set_sprite(gfx.get_sprite(&SPRITES[this_image])); + obj.set_sprite(gfx.sprite(&SPRITES[this_image])); } } gfx.commit(); @@ -110,7 +110,7 @@ fn all_tags(gfx: &OamManaged) { if count % 5 == 0 { image += 1; for (obj, tag) in objs.iter_mut() { - obj.set_sprite(gfx.get_sprite(tag.animation_sprite(image))); + obj.set_sprite(gfx.sprite(tag.animation_sprite(image))); } gfx.commit(); } diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index f950a3f5..b55c1806 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -190,7 +190,7 @@ impl OamManaged<'_> { } /// Creates a sprite in vram from a static sprite from [`include_aseprite`][crate::include_aseprite]. - pub fn get_sprite(&self, sprite: &'static Sprite) -> SpriteVram { + pub fn sprite(&self, sprite: &'static Sprite) -> SpriteVram { // safety: not reentrant unsafe { self.do_work_with_sprite_loader(|sprite_loader| sprite_loader.get_vram_sprite(sprite)) @@ -199,7 +199,7 @@ impl OamManaged<'_> { /// Creates a sprite in vram and uses it to make an object from a static sprite from [`include_aseprite`][crate::include_aseprite]. pub fn object_sprite(&self, sprite: &'static Sprite) -> Object<'_> { - self.object(self.get_sprite(sprite)) + self.object(self.sprite(sprite)) } } @@ -486,7 +486,7 @@ mod tests { fn test_always_ordered(gba: &mut crate::Gba) { let managed = gba.display.object.get_managed(); - let sprite = managed.get_sprite(TEST_SPRITE); + let sprite = managed.sprite(TEST_SPRITE); let mut objects = Vec::new(); for _ in 0..200 { diff --git a/examples/hyperspace-roll/src/battle.rs b/examples/hyperspace-roll/src/battle.rs index 0068a39e..bbde2c45 100644 --- a/examples/hyperspace-roll/src/battle.rs +++ b/examples/hyperspace-roll/src/battle.rs @@ -585,10 +585,7 @@ pub(crate) fn battle_screen( select_box_obj .set_y(120 - 4) .set_x(selected_die as u16 * 40 + 28 - 4) - .set_sprite( - agb.obj - .get_sprite(SELECT_BOX.animation_sprite(counter / 10)), - ); + .set_sprite(agb.obj.sprite(SELECT_BOX.animation_sprite(counter / 10))); agb.star_background.update(); agb.sfx.frame(); diff --git a/examples/hyperspace-roll/src/battle/display.rs b/examples/hyperspace-roll/src/battle/display.rs index f5328704..fbc1cd88 100644 --- a/examples/hyperspace-roll/src/battle/display.rs +++ b/examples/hyperspace-roll/src/battle/display.rs @@ -195,7 +195,7 @@ impl<'a> BattleScreenDisplay<'a> { if i < current_battle_state.player.shield_count as usize { player_shield .show() - .set_sprite(obj.get_sprite(SHIELD.sprite(0))); + .set_sprite(obj.sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -205,7 +205,7 @@ impl<'a> BattleScreenDisplay<'a> { if i < current_battle_state.enemy.shield_count as usize { player_shield .show() - .set_sprite(obj.get_sprite(SHIELD.sprite(0))); + .set_sprite(obj.sprite(SHIELD.sprite(0))); } else { player_shield.hide(); } @@ -249,7 +249,7 @@ impl<'a> BattleScreenDisplay<'a> { .zip(current_battle_state.rolled_dice.faces_to_render()) .zip(self.objs.dice_cooldowns.iter_mut()) { - die_obj.set_sprite(obj.get_sprite(FACE_SPRITES.sprite_for_face(current_face))); + die_obj.set_sprite(obj.sprite(FACE_SPRITES.sprite_for_face(current_face))); if let Some(cooldown) = cooldown { cooldown_healthbar @@ -311,7 +311,7 @@ impl<'a> EnemyAttackDisplay<'a> { pub fn update(&mut self, attack: &Option, obj: &'a OamManaged) { if let Some(attack) = attack { self.face.show().set_sprite( - obj.get_sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), + obj.sprite(ENEMY_ATTACK_SPRITES.sprite_for_attack(attack.attack_type())), ); self.cooldown .set_value((attack.cooldown * 48 / attack.max_cooldown) as usize, obj); @@ -413,7 +413,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_player_shields..*amount { objs.player_shield[i as usize] .show() - .set_sprite(obj.get_sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -444,7 +444,7 @@ impl<'a> AnimationStateHolder<'a> { for i in current_enemy_shields..*amount { objs.enemy_shield[i as usize] .show() - .set_sprite(obj.get_sprite(SHIELD.sprite(3 - *frame / 2))); + .set_sprite(obj.sprite(SHIELD.sprite(3 - *frame / 2))); } } else { return AnimationUpdateState::RemoveWithAction(self.action.clone()); @@ -467,7 +467,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationState::PlayerBurstShield { frame } => { if *frame < 10 { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.get_sprite(SHIELD.sprite(*frame / 2))); + shield.set_sprite(obj.sprite(SHIELD.sprite(*frame / 2))); } *frame += 1; @@ -475,7 +475,7 @@ impl<'a> AnimationStateHolder<'a> { AnimationUpdateState::Continue } else { for shield in objs.player_shield.iter_mut() { - shield.set_sprite(obj.get_sprite(SHIELD.sprite(0))); + shield.set_sprite(obj.sprite(SHIELD.sprite(0))); } AnimationUpdateState::RemoveWithAction(self.action.clone()) diff --git a/examples/hyperspace-roll/src/customise.rs b/examples/hyperspace-roll/src/customise.rs index ea7c57a8..4c6a5aec 100644 --- a/examples/hyperspace-roll/src/customise.rs +++ b/examples/hyperspace-roll/src/customise.rs @@ -338,10 +338,7 @@ pub(crate) fn customise_screen( break; } - select_box.set_sprite( - agb.obj - .get_sprite(SELECT_BOX.animation_sprite(counter / 10)), - ); + select_box.set_sprite(agb.obj.sprite(SELECT_BOX.animation_sprite(counter / 10))); agb.star_background.update(); let _ = agb::rng::gen(); diff --git a/examples/hyperspace-roll/src/graphics.rs b/examples/hyperspace-roll/src/graphics.rs index 640c5157..4f05521b 100644 --- a/examples/hyperspace-roll/src/graphics.rs +++ b/examples/hyperspace-roll/src/graphics.rs @@ -162,11 +162,11 @@ impl<'a> HealthBar<'a> { for (i, sprite) in self.sprites.iter_mut().enumerate() { if (i + 1) * 8 < new_value { - sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(0))); + sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(0))); } else if i * 8 < new_value { - sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8)))); + sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(8 - (new_value - i * 8)))); } else { - sprite.set_sprite(obj.get_sprite(SMALL_SPRITES.red_bar(8))); + sprite.set_sprite(obj.sprite(SMALL_SPRITES.red_bar(8))); } } } @@ -233,13 +233,12 @@ impl<'a> FractionDisplay<'a> { current /= 10; let current_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 2]; current_value_sprite - .set_sprite(obj.get_sprite(SMALL_SPRITES.number(current_value_digit as u32))); + .set_sprite(obj.sprite(SMALL_SPRITES.number(current_value_digit as u32))); let max_value_digit = max % 10; max /= 10; let max_value_sprite = &mut self.sprites[(self.digits - i) * 2 - 1]; - max_value_sprite - .set_sprite(obj.get_sprite(SMALL_SPRITES.number(max_value_digit as u32))); + max_value_sprite.set_sprite(obj.sprite(SMALL_SPRITES.number(max_value_digit as u32))); } } } diff --git a/examples/the-hat-chooses-the-wizard/src/enemies.rs b/examples/the-hat-chooses-the-wizard/src/enemies.rs index 45f05104..d7904ae5 100644 --- a/examples/the-hat-chooses-the-wizard/src/enemies.rs +++ b/examples/the-hat-chooses-the-wizard/src/enemies.rs @@ -161,7 +161,7 @@ impl<'a> Slime<'a> { let offset = (timer / 16) as usize; let frame = SLIME_IDLE.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -201,7 +201,7 @@ impl<'a> Slime<'a> { self.state = SlimeState::Idle; } else { let frame = SLIME_JUMP.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -227,7 +227,7 @@ impl<'a> Slime<'a> { } let frame = SLIME_SPLAT.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); } @@ -298,7 +298,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_IDLE.animation_sprite(0); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); if player_has_collided { @@ -318,7 +318,7 @@ impl<'a> Snail<'a> { self.enemy_info.entity.velocity = (0, 0).into(); let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -340,7 +340,7 @@ impl<'a> Snail<'a> { let offset = (timer - time) as usize / 8; let frame = SNAIL_MOVE.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); @@ -374,7 +374,7 @@ impl<'a> Snail<'a> { } let frame = SNAIL_EMERGE.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); @@ -403,7 +403,7 @@ impl<'a> Snail<'a> { return UpdateState::Remove; }; - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.enemy_info.entity.sprite.set_sprite(sprite); self.enemy_info.entity.velocity = (0, 0).into(); diff --git a/examples/the-hat-chooses-the-wizard/src/lib.rs b/examples/the-hat-chooses-the-wizard/src/lib.rs index baac7792..5a03e89c 100644 --- a/examples/the-hat-chooses-the-wizard/src/lib.rs +++ b/examples/the-hat-chooses-the-wizard/src/lib.rs @@ -353,9 +353,9 @@ impl<'a> Player<'a> { wizard .sprite - .set_sprite(controller.get_sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.sprite(HAT_SPIN_1.sprite(0))); hat.sprite - .set_sprite(controller.get_sprite(HAT_SPIN_1.sprite(0))); + .set_sprite(controller.sprite(HAT_SPIN_1.sprite(0))); wizard.sprite.show(); hat.sprite.show(); @@ -460,7 +460,7 @@ impl<'a> Player<'a> { self.wizard_frame = offset as u8; let frame = WALKING.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -470,7 +470,7 @@ impl<'a> Player<'a> { self.wizard_frame = 5; let frame = JUMPING.animation_sprite(0); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.wizard.sprite.set_sprite(sprite); } else if self.wizard.velocity.y > FixedNumberType::new(1) / 16 { @@ -485,7 +485,7 @@ impl<'a> Player<'a> { self.wizard_frame = 0; let frame = FALLING.animation_sprite(offset); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.wizard.sprite.set_sprite(sprite); } @@ -512,13 +512,13 @@ impl<'a> Player<'a> { self.wizard.sprite.set_hflip(true); self.hat .sprite - .set_sprite(controller.get_sprite(hat_base_tile.sprite(5))); + .set_sprite(controller.sprite(hat_base_tile.sprite(5))); } agb::input::Tri::Positive => { self.wizard.sprite.set_hflip(false); self.hat .sprite - .set_sprite(controller.get_sprite(hat_base_tile.sprite(0))); + .set_sprite(controller.sprite(hat_base_tile.sprite(0))); } _ => {} } @@ -544,7 +544,7 @@ impl<'a> Player<'a> { let hat_sprite_offset = (timer / hat_sprite_divider) as usize; self.hat.sprite.set_sprite( - controller.get_sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), + controller.sprite(hat_base_tile.animation_sprite(hat_sprite_offset)), ); if self.hat_slow_counter < 30 && self.hat.velocity.magnitude() < 2.into() { @@ -577,7 +577,7 @@ impl<'a> Player<'a> { } HatState::WizardTowards => { self.hat.sprite.set_sprite( - controller.get_sprite(hat_base_tile.animation_sprite(timer as usize / 2)), + controller.sprite(hat_base_tile.animation_sprite(timer as usize / 2)), ); let distance_vector = self.hat.position - self.wizard.position + hat_resting_position; @@ -680,7 +680,7 @@ impl<'a, 'b> PlayingLevel<'a, 'b> { self.timer += 1; let frame = PLAYER_DEATH.animation_sprite(self.timer as usize / 8); - let sprite = controller.get_sprite(frame); + let sprite = controller.sprite(frame); self.player.wizard.velocity += (0.into(), FixedNumberType::new(1) / 32).into(); self.player.wizard.position += self.player.wizard.velocity; diff --git a/examples/the-purple-night/src/lib.rs b/examples/the-purple-night/src/lib.rs index 27b2aecc..ea667825 100644 --- a/examples/the-purple-night/src/lib.rs +++ b/examples/the-purple-night/src/lib.rs @@ -524,7 +524,7 @@ struct Player<'a> { impl<'a> Player<'a> { fn new(object_controller: &'a OamManaged<'_>) -> Player<'a> { let mut entity = Entity::new(object_controller, Rect::new((0, 1).into(), (5, 10).into())); - let s = object_controller.get_sprite(LONG_SWORD_IDLE.sprite(0)); + let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0)); entity.sprite.set_sprite(s); entity.sprite.show(); entity.position = (144, 0).into(); @@ -576,12 +576,12 @@ impl<'a> Player<'a> { self.entity.sprite.set_hflip(self.facing == Tri::Negative); self.entity.velocity.x += self.sword.ground_walk_force() * x as i32; if self.entity.velocity.x.abs() > Number::new(1) / 10 { - let sprite = controller - .get_sprite(self.sword.walk_animation(self.sprite_offset)); + let sprite = + controller.sprite(self.sword.walk_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } else { - let sprite = controller - .get_sprite(self.sword.idle_animation(self.sprite_offset)); + let sprite = + controller.sprite(self.sword.idle_animation(self.sprite_offset)); self.entity.sprite.set_sprite(sprite); } @@ -601,7 +601,7 @@ impl<'a> Player<'a> { let frame = self.sword.attack_frame(*a); self.fudge_factor.x = (self.sword.fudge(frame) * self.facing as i32).into(); let tag = self.sword.attack_tag(); - let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.ground_attack_hurtbox(frame); @@ -615,7 +615,7 @@ impl<'a> Player<'a> { let frame = self.sword.hold_frame(); self.fudge_factor.x = (self.sword.fudge(frame) * self.facing as i32).into(); let tag = self.sword.attack_tag(); - let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if *a == 0 { self.attack_timer = AttackTimer::Idle; @@ -640,7 +640,7 @@ impl<'a> Player<'a> { 2 }; let tag = self.sword.jump_tag(); - let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); if x != Tri::Zero { @@ -662,7 +662,7 @@ impl<'a> Player<'a> { *a -= 1; let frame = self.sword.jump_attack_frame(*a); let tag = self.sword.jump_attack_tag(); - let sprite = controller.get_sprite(tag.animation_sprite(frame as usize)); + let sprite = controller.sprite(tag.animation_sprite(frame as usize)); self.entity.sprite.set_sprite(sprite); hurtbox = self.sword.air_attack_hurtbox(frame); @@ -827,7 +827,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 8); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -862,7 +862,7 @@ impl BatData { } let sprite = BAT_IDLE.sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -889,7 +889,7 @@ impl BatData { BatState::Dead => { const BAT_DEAD: &Tag = TAG_MAP.get("bat dead"); let sprite = BAT_DEAD.sprite(0); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -957,7 +957,7 @@ impl SlimeData { const IDLE: &Tag = TAG_MAP.get("slime idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -997,7 +997,7 @@ impl SlimeData { const CHASE: &Tag = TAG_MAP.get("Slime jump"); let sprite = CHASE.sprite(frame as usize); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1027,7 +1027,7 @@ impl SlimeData { if *count < 5 * 4 { const DEATH: &Tag = TAG_MAP.get("Slime death"); let sprite = DEATH.sprite(*count as usize / 4); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); *count += 1; @@ -1095,7 +1095,7 @@ impl MiniFlameData { } } else { let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 8); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); entity.velocity = (0.into(), Number::new(-1) / Number::new(4)).into(); @@ -1143,7 +1143,7 @@ impl MiniFlameData { } let sprite = ANGRY.animation_sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); } MiniFlameState::Dead => { @@ -1155,7 +1155,7 @@ impl MiniFlameData { const DEATH: &Tag = TAG_MAP.get("angry boss dead"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 12); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1216,7 +1216,7 @@ impl EmuData { const IDLE: &Tag = TAG_MAP.get("emu - idle"); let sprite = IDLE.sprite(self.sprite_offset as usize / 16); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); if (entity.position.y - player.entity.position.y).abs() < 10.into() { @@ -1263,7 +1263,7 @@ impl EmuData { const WALK: &Tag = TAG_MAP.get("emu-walk"); let sprite = WALK.sprite(self.sprite_offset as usize / 2); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); let gravity: Number = 1.into(); @@ -1318,7 +1318,7 @@ impl EmuData { const DEATH: &Tag = TAG_MAP.get("emu - die"); let sprite = DEATH.animation_sprite(self.sprite_offset as usize / 4); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); self.sprite_offset += 1; @@ -1388,7 +1388,7 @@ impl<'a> Enemy<'a> { let mut entity = Entity::new(object_controller, enemy_data.collision_mask()); let sprite = enemy_data.sprite(); - let sprite = object_controller.get_sprite(sprite); + let sprite = object_controller.sprite(sprite); entity.sprite.set_sprite(sprite); entity.sprite.show(); @@ -1442,7 +1442,7 @@ impl ParticleData { const DUST: &Tag = TAG_MAP.get("dust"); let sprite = DUST.sprite(*frame as usize / 3); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1456,7 +1456,7 @@ impl ParticleData { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1482,7 +1482,7 @@ impl ParticleData { ParticleData::BossHealer(frame, target) => { const HEALTH: &Tag = TAG_MAP.get("Heath"); let sprite = HEALTH.animation_sprite(*frame as usize / 3); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); entity.sprite.set_sprite(sprite); @@ -1640,7 +1640,7 @@ impl<'a> FollowingBoss<'a> { const BOSS: &Tag = TAG_MAP.get("happy boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = controller.get_sprite(sprite); + let sprite = controller.sprite(sprite); self.entity.sprite.set_sprite(sprite); @@ -1773,7 +1773,7 @@ impl<'a> Boss<'a> { const BOSS: &Tag = TAG_MAP.get("Boss"); let sprite = BOSS.animation_sprite(frame as usize); - let sprite = object_controller.get_sprite(sprite); + let sprite = object_controller.sprite(sprite); self.entity.sprite.set_sprite(sprite); From 94a8e2d4c3994cea3d8a846d26725542a73b5a0f Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 23 Apr 2023 20:41:42 +0100 Subject: [PATCH 32/39] add deprecation for old names --- agb/src/display/mod.rs | 7 +++++++ agb/src/display/object.rs | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/agb/src/display/mod.rs b/agb/src/display/mod.rs index 0d6dd6d2..e91ac9d6 100644 --- a/agb/src/display/mod.rs +++ b/agb/src/display/mod.rs @@ -92,6 +92,13 @@ impl ObjectDistribution { unsafe { initilise_oam() }; OamManaged::new() } + + /// The old name for [`get_managed`][ObjectDistribution::get_managed] kept around for easier migration. + /// This will be removed in a future release. + #[deprecated = "use get_managed to get the managed oam instead"] + pub fn get(&mut self) -> OamManaged<'_> { + self.get_managed() + } } #[non_exhaustive] diff --git a/agb/src/display/object.rs b/agb/src/display/object.rs index dfecf718..64c13b76 100644 --- a/agb/src/display/object.rs +++ b/agb/src/display/object.rs @@ -26,6 +26,11 @@ use super::DISPLAY_CONTROL; const OBJECT_ATTRIBUTE_MEMORY: *mut u16 = 0x0700_0000 as *mut u16; +#[deprecated = "use OamManaged directly instead"] +/// The old name for [`OamManaged`] kept around for easier migration. +/// This will be removed in a future release. +pub type ObjectController<'a> = OamManaged<'a>; + pub(super) unsafe fn initilise_oam() { for i in 0..128 { let ptr = (OBJECT_ATTRIBUTE_MEMORY).add(i * 4); From 8ab539acbb3eee64e72fdb21875a49ef3cf573ee Mon Sep 17 00:00:00 2001 From: Corwin Date: Sun, 23 Apr 2023 20:43:09 +0100 Subject: [PATCH 33/39] add changelog entries --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eee11ee8..1b3a04cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - You can now import aseprite files directly (in addition to the already supported png and bmp files) when importing background tiles. +- New additional unmanaged object API for interacting with a more straightforward manner with the underlying hardware. ### Changed - Importing background tiles has been improved. You no longer need to use `include_gfx!` with the toml file. Instead, use `include_background_gfx`. See the documentation for usage. +- Moved the existing object API to be the OamManaged API. The old names persist with deprecated notices on them. ## [0.14.0] - 2023/04/11 From e78a6624ff185b6270f7cd52907c9d02c8af3c57 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 20:32:28 +0100 Subject: [PATCH 34/39] add amplitude --- examples/amplitude/.cargo/config.toml | 14 + examples/amplitude/Cargo.lock | 407 ++++++++++++++++++++++++ examples/amplitude/Cargo.toml | 20 ++ examples/amplitude/README.md | 72 +++++ examples/amplitude/gba.ld | 117 +++++++ examples/amplitude/gba_mb.ld | 115 +++++++ examples/amplitude/gfx/circles.aseprite | Bin 0 -> 713 bytes examples/amplitude/gfx/numbers.aseprite | Bin 0 -> 1100 bytes examples/amplitude/gfx/saw.aseprite | Bin 0 -> 736 bytes examples/amplitude/rust-toolchain.toml | 3 + examples/amplitude/src/lib.rs | 373 ++++++++++++++++++++++ examples/amplitude/src/main.rs | 10 + 12 files changed, 1131 insertions(+) create mode 100644 examples/amplitude/.cargo/config.toml create mode 100644 examples/amplitude/Cargo.lock create mode 100644 examples/amplitude/Cargo.toml create mode 100644 examples/amplitude/README.md create mode 100644 examples/amplitude/gba.ld create mode 100644 examples/amplitude/gba_mb.ld create mode 100644 examples/amplitude/gfx/circles.aseprite create mode 100644 examples/amplitude/gfx/numbers.aseprite create mode 100644 examples/amplitude/gfx/saw.aseprite create mode 100644 examples/amplitude/rust-toolchain.toml create mode 100644 examples/amplitude/src/lib.rs create mode 100644 examples/amplitude/src/main.rs diff --git a/examples/amplitude/.cargo/config.toml b/examples/amplitude/.cargo/config.toml new file mode 100644 index 00000000..b3276236 --- /dev/null +++ b/examples/amplitude/.cargo/config.toml @@ -0,0 +1,14 @@ +[unstable] +build-std = ["core", "alloc"] +build-std-features = ["compiler-builtins-mem"] + +[build] +target = "thumbv4t-none-eabi" + +[target.thumbv4t-none-eabi] +rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"] +runner = "mgba-qt" + +[target.armv4t-none-eabi] +rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"] +runner = "mgba-qt" diff --git a/examples/amplitude/Cargo.lock b/examples/amplitude/Cargo.lock new file mode 100644 index 00000000..dd6fd268 --- /dev/null +++ b/examples/amplitude/Cargo.lock @@ -0,0 +1,407 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "agb" +version = "0.14.0" +dependencies = [ + "agb_fixnum", + "agb_hashmap", + "agb_image_converter", + "agb_macros", + "agb_sound_converter", + "bare-metal", + "bitflags 2.1.0", + "modular-bitfield", + "rustc-hash", +] + +[[package]] +name = "agb_fixnum" +version = "0.14.0" +dependencies = [ + "agb_macros", +] + +[[package]] +name = "agb_hashmap" +version = "0.14.0" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "agb_image_converter" +version = "0.14.0" +dependencies = [ + "asefile", + "fontdue", + "image", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "agb_macros" +version = "0.14.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "agb_sound_converter" +version = "0.14.0" +dependencies = [ + "hound", + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "amplitude" +version = "0.1.0" +dependencies = [ + "agb", +] + +[[package]] +name = "asefile" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a71de7aecd2d0a76ec90fde2c443d12667c737d92de76bd187f101eca37891" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "flate2", + "image", + "log", + "nohash", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide 0.6.2", +] + +[[package]] +name = "fontdue" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc" +dependencies = [ + "hashbrown", + "ttf-parser", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hound" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1" + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-iter", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "nohash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca" + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/examples/amplitude/Cargo.toml b/examples/amplitude/Cargo.toml new file mode 100644 index 00000000..470d0c45 --- /dev/null +++ b/examples/amplitude/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "amplitude" +version = "0.1.0" +authors = [""] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +agb = { version = "0.14.0", path = "../../agb" } + +[profile.dev] +opt-level = 2 +debug = true + +[profile.release] +panic = "abort" +lto = true +debug = true +codegen-units = 1 diff --git a/examples/amplitude/README.md b/examples/amplitude/README.md new file mode 100644 index 00000000..2d488275 --- /dev/null +++ b/examples/amplitude/README.md @@ -0,0 +1,72 @@ +# AGBRS template + +## A basic template example for agb projects + +This makes getting started with a new project for the Game Boy Advance in rust really simple, by providing +all the boiler plate files for you. + +## Building + +### Prerequisites + +You will need the following installed in order to build and run this project: + +* A recent version of `rustup`. See the [rust website](https://www.rust-lang.org/tools/install) for instructions for your operating system +* `arm-none-eabi-binutils` for assembling and linking + * Windows: [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads). + Make sure you select "Add path to environment variable" during the install + * Debian and derivatives (e.g. Ubuntu, raspberry pi OS, linux mint): `sudo apt install binutils-arm-none-eabi` + * Arch linux and derivatives: `sudo pacman -S arm-none-eabi-binutils` + +You will also want to install an emulator. The best support in agb is with [mgba](https://mgba.io), with +`println!` support via `agb::println!` but any emulator should work. You'll get the best experience if +`mgba-qt` is in your `PATH`. + +If you want to run your game on real hardware, you will also need to install `agb-gbafix` which you can do after installing +rust with the following: `cargo install agb-gbafix`. This is not required if you are only running your game in an emulator. + +### Running in an emulator + +Once you have the prerequisites installed, you should be able to build using + +```sh +cargo build +``` + +or in release mode (recommended for the final version to ship to players) + +```sh +cargo build --release +``` + +The resulting file will be in `target/thumbv4t-none-eabi/debug/` or `target/thumbv4t-none-eabi/release/` depending on +whether you did a release or debug build. + +If you have `mgba-qt` in your path, you will be able to run your game with + +```sh +cargo run +``` + +or in release mode + +```sh +cargo run --release +``` + +## Starting development + +You can find the documentation for agb [here](https://docs.rs/agb/latest/agb/). + +You may also want to change the package name and version in `Cargo.toml` before you start. + +## Shipping a .gba file for real hardware + +To make a game run on real hardware, you will need to convert the built file into a file suitable for +running on the real thing. + +First build the binary in release mode using the instructions above, then do the following: + +```sh +agb-gbafix target/thumbv4t-none-eabi/release/ -o .gba +``` \ No newline at end of file diff --git a/examples/amplitude/gba.ld b/examples/amplitude/gba.ld new file mode 100644 index 00000000..4190e45b --- /dev/null +++ b/examples/amplitude/gba.ld @@ -0,0 +1,117 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) + +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + +MEMORY { + ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K + iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K + rom (rx) : ORIGIN = 0x08000000, LENGTH = 32M +} + +__text_start = ORIGIN(rom); + +INPUT (agb.a) + +SECTIONS { + . = __text_start; + + + .text : { + KEEP(*(.crt0)); + *(.crt0 .crt0*); + *(.text .text*); + . = ALIGN(4); + } > rom + __text_end = .; + + .rodata : { + *(.rodata .rodata.*); + . = ALIGN(4); + } > rom + + __iwram_rom_start = .; + .iwram : { + __iwram_data_start = ABSOLUTE(.); + + *(.iwram .iwram.*); + . = ALIGN(4); + + *(.text_iwram .text_iwram.*); + . = ALIGN(4); + + __iwram_data_end = ABSOLUTE(.); + } > iwram AT>rom + + . = __iwram_rom_start + (__iwram_data_end - __iwram_data_start); + + __ewram_rom_start = .; + .ewram : { + __ewram_data_start = ABSOLUTE(.); + + *(.ewram .ewram.*); + . = ALIGN(4); + + *(.data .data.*); + . = ALIGN(4); + + __ewram_data_end = ABSOLUTE(.); + } > ewram AT>rom + + .bss : { + *(.bss .bss.*); + . = ALIGN(4); + __iwram_end = ABSOLUTE(.); + } > iwram + + __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; + __iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2; + + __ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start; + __ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2; + + .shstrtab : { + *(.shstrtab) + } + + /* debugging sections */ + /* Stabs */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .debug_ranges 0 : { *(.debug_ranges) } + + /* discard anything not already mentioned */ + /DISCARD/ : { *(*) } +} \ No newline at end of file diff --git a/examples/amplitude/gba_mb.ld b/examples/amplitude/gba_mb.ld new file mode 100644 index 00000000..0f3ef446 --- /dev/null +++ b/examples/amplitude/gba_mb.ld @@ -0,0 +1,115 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(__start) +EXTERN(__RUST_INTERRUPT_HANDLER) + +EXTERN(__agbabi_memset) +EXTERN(__agbabi_memcpy) + +MEMORY { + ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K + iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K +} + +__text_start = ORIGIN(ewram); + +INPUT (agb.a) + +SECTIONS { + . = __text_start; + + .text : { + KEEP(*(.crt0)); + *(.crt0 .crt0*); + *(.text .text*); + . = ALIGN(4); + } > rom + __text_end = .; + + .rodata : { + *(.rodata .rodata.*); + . = ALIGN(4); + } > ewram + + __iwram_rom_start = .; + .iwram : { + __iwram_data_start = ABSOLUTE(.); + + *(.iwram .iwram.*); + . = ALIGN(4); + + *(.text_iwram .text_iwram.*); + . = ALIGN(4); + + __iwram_data_end = ABSOLUTE(.); + } > iwram AT>ewram + + . = __iwram_rom_start + (__iwram_data_end - __iwram_data_start); + + __ewram_rom_start = .; + .ewram : { + __ewram_data_start = ABSOLUTE(.); + + *(.ewram .ewram.*); + . = ALIGN(4); + + *(.data .data.*); + . = ALIGN(4); + + __ewram_data_end = ABSOLUTE(.); + } > ewram AT>ewram + + .bss : { + *(.bss .bss.*); + . = ALIGN(4); + __iwram_end = ABSOLUTE(.); + } > iwram + + __iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start; + __iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2; + + __ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start; + __ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2; + + .shstrtab : { + *(.shstrtab) + } + + /* debugging sections */ + /* Stabs */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + + .debug_ranges 0 : { *(.debug_ranges) } + + /* discard anything not already mentioned */ + /DISCARD/ : { *(*) } +} \ No newline at end of file diff --git a/examples/amplitude/gfx/circles.aseprite b/examples/amplitude/gfx/circles.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..3282aaca20b95da459b3883472b175bcd5076545 GIT binary patch literal 713 zcmcJNODIH97{|Xc!`QJBQcjZW%uMnKjY!E@FpH7MqcLRCh!81xOeqNq#V`vSJ2p1T z!h)4#?O7e_g}&lRJ2P`35y5vcA0w z!xOWxHyDJ?J}(@pnuA*|7d&tB!_)CSxVTaW=lUHw$O3G}cUMeU1S>!vcf_$q8DC^E zMH5E^u|o|nq%cCqaM2nI6v!u>WMauAkTjyQjU;7-Uxk9$rM`14FsPgroTMxM#&2a6 zZ=lO!ZjrcKY*N)X94Mocs6N{I`yIxf8jbopCMR`ecs^rE_Of;|TL)hb!dJ7$DUJcL tp7lg(dFu7aVr~HAx0f*fc-55FjZRnPO0{psv(UD>6nl)NNYvRAc?SmvtJnYl literal 0 HcmV?d00001 diff --git a/examples/amplitude/gfx/numbers.aseprite b/examples/amplitude/gfx/numbers.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..d839e9de9542688a7b481af71303e7e0062f44a6 GIT binary patch literal 1100 zcmcJMJxBs!9LE3GshJvmN!|5<7X^t}o)MB^UqqWrK?KpDR!~tvMpR1~LCDn*tp(BG zRD)Yxk}SCNkodh$Es&jIT>1 zjuojK_kUc+=$=U$RH^}hYK&q@MQU(yyo-|fSrIfdM7CyAmdHC=4~^}%Koh5Srnkq? zlW|rTdcCYN<6?TY42^{bper4X&~Seh)X`81^*Y%4X}=G;zdj3XnVg0ux-8JaCbq_R zbO=4DWn51UtB;hicUjfS3Vn7233Y5YMWjPCxd#o16ytyOPEugn9n3R^?=y1*fE%y`M%7Y#=OlrA9U}%nAiGTpFTV^ zJ}vZIezut}_ju~Dn9@CY_O?RoN1Mh)@>osj{yew)CpU3fQNFthSsv#|av0yb^BNSe zWx>ea?ZzLoD?vKwR0JA+HK%-eXy;GaBj<9TwT_;K44L0=x*`hlMO@)&<;L>zCwqJ+ L809JSU`~Ai(x}!S literal 0 HcmV?d00001 diff --git a/examples/amplitude/gfx/saw.aseprite b/examples/amplitude/gfx/saw.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..a92d5c2602f2ac5ee972b7deadaf2ba83ec0ebac GIT binary patch literal 736 zcmcJMT}V@57{{N}HnoWCqJ<)dy+n-ennMzqDf9_hAsP$oVjJ3)OAK?&Vr0<=hC~Jx zQ3iEUP}IT}5oCB#*i969Du6a+2trl6KYT(9I z^)Osl4TtZ=2{reJiQF_U)I1&Z<}Fs%mZuBcf+R6)v%>$CG^(U!N$fI^aPK= z-Q90s`>=+A7t`?h>t9e)BE!jt@I?Or?2JZXo4*Cd*A2ouUN6k-Y=`N@BRF(%3mn|< z(NPAl3-c-(hF}8paYr0$l<`FtQ#5fz5IfZHLJA{v3NBh>fdcu2lT0j`1d>LStB|B@ zaIR30QrQ|hwC8}Tc5_ql&xfCTm8(r!Lw)yX9BTGd^Xq0yVSn(-$8>1XQMK5Sy*us5 zJ7ZGbrJR-9BfSZG)nm(QT^4KGaPFmYk5}}HnHo(d&h!jC@xME}>sIE%lH|zb=98(b z=FQVT)<$Y(Mzt+49*#FZ@R<#nbeZqehlS>=hAzFnY~*V$we6EDbUPlLdf#8(XC3~Q gzwbPrtKE0Ro=ZI2p#z)Z6Gn$+UjJ@X?R7?e10f>O-2eap literal 0 HcmV?d00001 diff --git a/examples/amplitude/rust-toolchain.toml b/examples/amplitude/rust-toolchain.toml new file mode 100644 index 00000000..6e1f5327 --- /dev/null +++ b/examples/amplitude/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["rust-src", "clippy", "rustfmt"] diff --git a/examples/amplitude/src/lib.rs b/examples/amplitude/src/lib.rs new file mode 100644 index 00000000..6a19fa27 --- /dev/null +++ b/examples/amplitude/src/lib.rs @@ -0,0 +1,373 @@ +#![no_std] +#![no_main] +#![cfg_attr(test, feature(custom_test_frameworks))] +#![cfg_attr(test, reexport_test_harness_main = "test_main")] +#![cfg_attr(test, test_runner(agb::test_runner::test_runner))] + +extern crate alloc; + +use agb::{ + display::{ + self, + affine::{self, AffineMatrix}, + object::{ + AffineMatrixInstance, AffineMode, Graphics, OamIterator, ObjectUnmanaged, Sprite, + SpriteLoader, SpriteVram, Tag, TagMap, + }, + palette16::Palette16, + }, + fixnum::{num, Num, Vector2D}, + include_aseprite, + input::{Button, ButtonController}, + rng, +}; +use alloc::{boxed::Box, collections::VecDeque, vec::Vec}; + +type Number = Num; + +struct Saw { + object: ObjectUnmanaged, + position: Vector2D, + angle: Number, + rotation_speed: Number, +} + +enum Colour { + Red, + Blue, +} + +struct Circle { + colour: Colour, + position: Vector2D, +} + +#[derive(Clone)] +struct SpriteCache { + saw: SpriteVram, + blue: SpriteVram, + red: SpriteVram, + numbers: Box<[SpriteVram]>, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum DrawDirection { + Left, + Right, +} + +fn draw_number( + mut number: u32, + position: Vector2D, + oam: &mut OamIterator, + direction: DrawDirection, + sprite_cache: &SpriteCache, +) { + let mut digits = Vec::new(); + if number == 0 { + digits.push(0); + } + + while number != 0 { + digits.push(number % 10); + number /= 10; + } + + let mut current_position = if direction == DrawDirection::Right { + position + (4 * (digits.len() - 1) as i32, 0).into() + } else { + position + }; + + for digit in digits { + let mut obj = ObjectUnmanaged::new(sprite_cache.numbers[digit as usize].clone()); + obj.show().set_position(current_position); + + if let Some(slot) = oam.next() { + slot.set(&obj); + } + + current_position -= (4, 0).into(); + } +} + +impl SpriteCache { + fn new(loader: &mut SpriteLoader) -> Self { + const SPRITES: &Graphics = include_aseprite!( + "gfx/circles.aseprite", + "gfx/saw.aseprite", + "gfx/numbers.aseprite" + ); + + const NUMBERS: &Tag = SPRITES.tags().get("numbers"); + const BLUE_CIRCLE: &Sprite = SPRITES.tags().get("Blue").sprite(0); + const RED_CIRCLE: &Sprite = SPRITES.tags().get("Red").sprite(0); + const SAW: &Sprite = SPRITES.tags().get("Saw").sprite(0); + + Self { + saw: loader.get_vram_sprite(SAW), + blue: loader.get_vram_sprite(BLUE_CIRCLE), + red: loader.get_vram_sprite(RED_CIRCLE), + numbers: (0..10) + .map(|x| NUMBERS.sprite(x)) + .map(|x| loader.get_vram_sprite(x)) + .collect::>() + .into_boxed_slice(), + } + } +} + +struct Game { + settings: FinalisedSettings, + circles: VecDeque, + saws: VecDeque, + head_position: Vector2D, + phase_time: Number, + input: ButtonController, + energy: Number, + frame_since_last_saw: i32, + alive_frames: u32, +} + +enum GameState { + Continue, + Loss(u32), +} + +impl Game { + fn from_settings(settings: Settings) -> Self { + let finalised = settings.to_finalised_settings(); + + let mut circles = VecDeque::with_capacity(finalised.number_of_circles); + for idx in 0..finalised.number_of_circles { + circles.push_back(Circle { + colour: Colour::Red, + position: Vector2D::new( + finalised.speed * idx as i32 - 4, + settings.head_start_position.y, + ), + }) + } + + Game { + energy: 100.into(), + input: agb::input::ButtonController::new(), + settings: finalised, + circles, + saws: VecDeque::new(), + head_position: settings.head_start_position, + phase_time: 0.into(), + frame_since_last_saw: 0, + alive_frames: 0, + } + } + + fn frame(&mut self, sprite_cache: &SpriteCache) -> GameState { + self.input.update(); + + let (height, colour) = if self.input.is_pressed(Button::A) { + (self.settings.wave_height_ability, Colour::Blue) + } else { + (self.settings.wave_height_normal, Colour::Red) + }; + + let next_phase_time = self.phase_time + self.settings.phase_speed; + + let this_frame_y_delta = next_phase_time.cos() - self.phase_time.cos(); + self.phase_time = next_phase_time % num!(1.); + let this_frame_y_delta = this_frame_y_delta * height; + self.head_position.y += this_frame_y_delta; + + // update circles + for circle in self.circles.iter_mut() { + circle.position.x -= self.settings.speed; + } + + self.circles.pop_front(); + + // generate circle + let circle = Circle { + colour, + position: self.head_position, + }; + + self.circles.push_back(circle); + + // update saws + check for death + let mut saw_has_hit_head = false; + let mut number_of_saws_to_pop = 0; + for (idx, saw) in self.saws.iter_mut().enumerate() { + saw.position.x -= self.settings.speed; + if saw.position.x < (-32).into() { + number_of_saws_to_pop = idx + 1; + } + saw.angle += saw.rotation_speed; + + let angle_affine_matrix = AffineMatrix::from_rotation(saw.angle); + + saw.object.set_affine_matrix(AffineMatrixInstance::new( + angle_affine_matrix.to_object_wrapping(), + )); + saw.object.show_affine(AffineMode::Affine); + + saw.object + .set_position(saw.position.floor() - (16, 16).into()); + + if (saw.position - self.head_position).magnitude_squared() + < ((16 + 4) * (16 + 4)).into() + { + saw_has_hit_head = true; + } + } + + // destroy saws + for _ in 0..number_of_saws_to_pop { + self.saws.pop_front(); + } + + // create saw + self.frame_since_last_saw -= 1; + if self.frame_since_last_saw <= 0 { + self.frame_since_last_saw = self.settings.frames_between_saws; + let mut rotation_direction = rng::gen().signum(); + if rotation_direction == 0 { + rotation_direction = 1; + } + + let rotation_magnitude = + Number::from_raw(rng::gen().abs() % (1 << 8)) % num!(0.02) + num!(0.005); + + let rotation_speed = rotation_magnitude * rotation_direction; + let saw = Saw { + object: ObjectUnmanaged::new(sprite_cache.saw.clone()), + position: (300, rng::gen().rem_euclid(display::HEIGHT)).into(), + angle: 0.into(), + rotation_speed, + }; + + self.saws.push_back(saw); + } + + self.alive_frames += 1; + + let out_of_bounds_death = self.head_position.y.floor() < -4 + || (self.head_position.y + 1).floor() > display::HEIGHT + 4; + + if saw_has_hit_head || out_of_bounds_death { + GameState::Loss(self.alive_frames) + } else { + GameState::Continue + } + } + + fn render(&self, oam: &mut OamIterator, sprite_cache: &SpriteCache) { + for saw in self.saws.iter() { + if let Some(slot) = oam.next() { + slot.set(&saw.object); + } + } + + for circle in self.circles.iter() { + if let Some(slot) = oam.next() { + let mut object = ObjectUnmanaged::new(match circle.colour { + Colour::Red => sprite_cache.red.clone(), + Colour::Blue => sprite_cache.blue.clone(), + }); + + object + .show() + .set_position(circle.position.floor() - (4, 4).into()); + + slot.set(&object); + } + } + } +} + +struct Settings { + phase_speed: Number, + frames_between_saws: i32, + speed: Number, + head_start_position: Vector2D, + wave_height_normal: Number, + wave_height_ability: Number, +} + +impl Settings { + fn to_finalised_settings(&self) -> FinalisedSettings { + FinalisedSettings { + number_of_circles: ((self.head_start_position.x + 4) / self.speed + 1) + .floor() + .try_into() + .expect("number should be positive"), + speed: self.speed, + phase_speed: self.phase_speed, + frames_between_saws: self.frames_between_saws, + wave_height_ability: self.wave_height_ability, + wave_height_normal: self.wave_height_normal, + } + } +} + +struct FinalisedSettings { + wave_height_normal: Number, + wave_height_ability: Number, + phase_speed: Number, + frames_between_saws: i32, + speed: Number, + number_of_circles: usize, +} + +pub fn main(mut gba: agb::Gba) -> ! { + let (mut unmanaged, mut sprites) = gba.display.object.get_unmanaged(); + let sprite_cache = SpriteCache::new(&mut sprites); + + let (_background, mut vram) = gba.display.video.tiled0(); + + vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]); + + let vblank = agb::interrupt::VBlank::get(); + + let mut max_score = 0; + + loop { + let mut game = Game::from_settings(Settings { + phase_speed: num!(0.02), + frames_between_saws: 60, + speed: num!(1.), + head_start_position: (40, 100).into(), + wave_height_normal: 20.into(), + wave_height_ability: 5.into(), + }); + loop { + let state = game.frame(&sprite_cache); + if game.alive_frames > max_score { + max_score = game.alive_frames; + } + vblank.wait_for_vblank(); + let oam_frame = &mut unmanaged.iter(); + draw_number( + max_score, + (display::WIDTH - 4, 1).into(), + oam_frame, + DrawDirection::Left, + &sprite_cache, + ); + draw_number( + game.alive_frames, + (1, 1).into(), + oam_frame, + DrawDirection::Right, + &sprite_cache, + ); + game.render(oam_frame, &sprite_cache); + + if let GameState::Loss(score) = state { + for _ in 0..30 { + vblank.wait_for_vblank(); + } + break; + } + } + } +} diff --git a/examples/amplitude/src/main.rs b/examples/amplitude/src/main.rs new file mode 100644 index 00000000..2bd74c7c --- /dev/null +++ b/examples/amplitude/src/main.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] +#![cfg_attr(test, feature(custom_test_frameworks))] +#![cfg_attr(test, reexport_test_harness_main = "test_main")] +#![cfg_attr(test, test_runner(agb::test_runner::test_runner))] + +#[agb::entry] +fn main(mut gba: agb::Gba) -> ! { + amplitude::main(gba) +} From d44461610d776581f9000632e376f61b55e99706 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 20:39:25 +0100 Subject: [PATCH 35/39] go back to tab spacing --- .vscode/agb.code-workspace | 88 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/.vscode/agb.code-workspace b/.vscode/agb.code-workspace index 3cd3604a..b0575aac 100644 --- a/.vscode/agb.code-workspace +++ b/.vscode/agb.code-workspace @@ -1,49 +1,49 @@ { - "folders": [ - { - "path": "../agb" - }, - { - "path": "../agb-sound-converter" - }, - { - "path": "../agb-macros" - }, - { - "path": "../agb-image-converter" - }, - { - "path": "../agb-fixnum" - }, - { - "path": "../examples/the-purple-night" - }, - { - "path": "../examples/the-hat-chooses-the-wizard" - }, - { - "path": "../examples/hyperspace-roll" - }, - { - "path": "../.github" - }, - { - "path": "../template" - }, - { - "path": "../book" - }, - { - "path": "../mgba-test-runner" - }, - { - "path": "../tools" - }, - { - "path": "../examples/combo" + "folders": [ + { + "path": "../agb" + }, + { + "path": "../agb-sound-converter" + }, + { + "path": "../agb-macros" + }, + { + "path": "../agb-image-converter" + }, + { + "path": "../agb-fixnum" + }, + { + "path": "../examples/the-purple-night" + }, + { + "path": "../examples/the-hat-chooses-the-wizard" + }, + { + "path": "../examples/hyperspace-roll" + }, + { + "path": "../.github" + }, + { + "path": "../template" + }, + { + "path": "../book" + }, + { + "path": "../mgba-test-runner" + }, + { + "path": "../tools" + }, + { + "path": "../examples/combo" }, { "path": "../agb-hashmap" - } - ] + } + ] } \ No newline at end of file From 4ea75bc06430ae50a29fb355f36fa3e4c5e8b113 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 20:57:18 +0100 Subject: [PATCH 36/39] add amplitude to combo rom --- examples/combo/Cargo.lock | 8 ++++++++ examples/combo/Cargo.toml | 1 + examples/combo/gfx/amplitude.png | Bin 0 -> 1844 bytes examples/combo/gfx/games.aseprite | Bin 10151 -> 0 bytes examples/combo/gfx/hyperspace.png | Bin 3758 -> 2588 bytes examples/combo/src/lib.rs | 12 +++++++++--- 6 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 examples/combo/gfx/amplitude.png delete mode 100644 examples/combo/gfx/games.aseprite diff --git a/examples/combo/Cargo.lock b/examples/combo/Cargo.lock index e623a679..b39df590 100644 --- a/examples/combo/Cargo.lock +++ b/examples/combo/Cargo.lock @@ -85,6 +85,13 @@ dependencies = [ "version_check", ] +[[package]] +name = "amplitude" +version = "0.1.0" +dependencies = [ + "agb", +] + [[package]] name = "asefile" version = "0.3.5" @@ -167,6 +174,7 @@ name = "combo" version = "0.1.0" dependencies = [ "agb", + "amplitude", "hyperspace-roll", "the-hat-chooses-the-wizard", "the-purple-night", diff --git a/examples/combo/Cargo.toml b/examples/combo/Cargo.toml index d27c394e..e9d93b33 100644 --- a/examples/combo/Cargo.toml +++ b/examples/combo/Cargo.toml @@ -10,6 +10,7 @@ agb = { version = "0.14.0", path = "../../agb" } the-purple-night = { path = "../the-purple-night" } the-hat-chooses-the-wizard = { path = "../the-hat-chooses-the-wizard" } hyperspace-roll = { path = "../hyperspace-roll" } +amplitude = { path = "../amplitude" } [profile.dev] diff --git a/examples/combo/gfx/amplitude.png b/examples/combo/gfx/amplitude.png new file mode 100644 index 0000000000000000000000000000000000000000..2086f1cbb6fa3b34f1fc414ad109571bc4619db7 GIT binary patch literal 1844 zcmV-42g~@0P)j^nud_(}gM7ljad7zPE@ z;;xj^TFVzE+*<2l7^cqy3Hb!|K6-^so;0rpl3NYxzd*|uqgNAU~K&029(yi>pJyw#Oin%iN~t+f$qAl|Mc=;RI2Td z68SEfhFkhT8~vw$Jzq+x_xs(ptx`%VH{dUEptaT#Vq5>|CCXB{Me}H)zB805O`uIL zVfu}c*(ZTj@w(DcLo5$A1-E%LZQIgD!y!_IHlqwd0mexp1zJ{+EHxNFD7VyaU;{Z= z-MPL*8`{d_%rUd9Lyv!Vui zwTG58`(&ClV^+LCYps3KJbeuK%c$k}{_{Qic&d3clxNI}6zI{J!X=X9)ildmJYXtD zpf^pR-Dw8gGHN+IalQ}^F;1+Qg&c}yNjO=&#YysjB}zC|?3RP`g*ZrS?Pxd)>!F)R zBfku^(br_}5it9LG8QMMjX>jljf_D445Rmp08;t zB=Pdtj&;gF&o;z%tCZ$#p%Btcn<_3(CcENebtgOdl%e0al!K=!nn#oGB}{BddQI>5 z`^blLzH?}{TfZ9j851APqse8;fr|^Z+J`_RF*62GA=a)DXrvT&sMWS@z6XW4g0QPE z>7kLB|E7oLx@q?{P5Dkaa1jAl6!)K=mIJw1E!93nnwA5_@VS7RmIGBG(8xuQo1W1_ zBNwx`MB9oU8d*4gg{aI)2sCnW@C=JAuP4yRf}CMd#};)A!$6>s3ptz{-OtkhN@{sP zpplo+#dup0Xk=<>nTLIn+j&w5UvwD)o5|4MA=+hZMK_{2lB5#h2C$pEfuj2 z&KxN!Yri4;=lu2crM0%Nq^3geH2pLj?TIsL0r@%aCO_(FnrBg=gBk6_ZlB2I9vTPv z?`A=uH*;JyB$>9qRKE{mu*0SgHv+w4Iy`bu{@L&;Edsr6v! znQ87xaH<>QBnPearrJ!MPVS~cTyT43_I5t2Tl+MRhVp!>Ti11Rm_n>a%yQ>dl4GAV zv+V9{cXQ~im?$0KnPz^xXZt_4NL%`c6;8gU`=i+xDvO6DzCmHREf;jGq^qtUMohIS|ZQXA1Xq)xbd>fj$sSGL6W7=Fq2gvL@3@{|7>kfYwf|gp1p3vPm<- zO0aVhA;x)bN-;S@J)8%H)KZj&+q$7DH~J0KWVu4qZ=gW59HX#iBx^Dw9A{o>4L?G^#?NQI+3;rr$u}sL-g26==#c%14DpRel0Zc}5ke z(5T8yDateIL4`(DZj@)#gbIzS5NK3|K%*)I8dV|Cs0x8bRR}byLZDF<0*$H=XjFwj zqbdX%RUyzpG=W+qUwg`KQ-f%eRzLUteD- z-e52Z?o)^)x~}U#z50o@`pI$Iw&q*%WYIN_Kwpl@uH^Xje3@q|Ko4C%sl?ON&gQu5 zx}rO20*zn#Pj7mX5)tT{X|3Py_iTAy{ey+p>9UUiu*8ft_VvfjqfvA7ko*zo+AMX< zJCqn}T8`QTj9vffO-o%wniYYrPoceUCj0!hxvcktfcx9|5n-=E*flizcmbM`*#x7S{K z@3q%n=_t&8J)4vSr_H*^l6%oz4heK{iEI^7*e1^ zFt}$N!LDH~f;!|{wydDC9>HrVX$Xdeha>3geHuZc>rDhJ_U%KE=M#b;m)wY8R_;#- z-i-c17mhSw#gg8)AU8-OkQN~H5$*_agf&7L;fs((m?AV0jtD`79YPJ^g^)rRA#|3w z=yV7RgaRTT5spYk#3C{gfrvCjl*N*yEz1%7>q00jTfOY~*@W}4TlN-jTefWFmVdv} zeg0RGC9}9xC@I<<7l5iuUA|oJ#B#mmCswaorLXtpe$&LW?+)~*>Mbv@Z~FLxdO3AD zp)~wiVs6uXiZ)S;tBfuxH+MgM2-uiCJ^OwPba(Bz+eD#wuJOIkWRkAkK6Fr}yLLO4 z17Wv5GL6%D9>E8h3k1KhJw5;XXw9zGeBC{}ghep=qw~nP`@&qis=h=%xE?9~>D_wQ zPUI`k-Y1Slm?wOL!NGejEkd6agT*Bpit7>76byWITE9`ZVrX{Eu#l7c*^z3vzvm=6}Bm*2~1I+!|%_W?(}a=IjjbH>qyz z!P$>+-uaPZ!9_1ep9fm9(C^rqbK0InU4pA1Mts62O2 z2=}H;O5NI92u2oARV2g#Vh~ZW)(m!`m@}m^`3&K`VkMN24)B^~XM*~rVKIHOp#cNP zXmz7bP_(g~)$>-;r7BZTtlE$w>lu+;1oPo>UGw^EI6!+xJS%>yh! z%b@TP5tqzv%9FCj+x&yg60aQ-a;7y`(Vv=dfMPDjDIM#wGF-ZZQzXfB8l3(aqJ2ig zB{_|iUFE3na@D;b(5*r=c;z?Pc@UhbCZ8a0AHS$zV&>f_ZC}mr%0yeV9-&NXGZL4& zu=I7jFwe!9uPa~3_5Sh(%Az@mndI|wJ=M3iWBFvAv{>79bH zt>6r1GHjQ=I}0C-Rp3mkVLr)2dH#)RwO##% zwkp1_k;XHCX087~ffAs7#Jqyuhmw#kyw#v2rCLpbzH>xSqZ#U*?ONpo1G zYle97CHaowTAna%cQ(spyIqs4*-OhKfj+4&!|Zg#3i^t(+r3IMfJQ5@FqZBuf;${tNaCck| zl5HVIEDBq}*uwfYYVt%`YqtNW%kcWflqqn0g)71N;n_{*G@!?q({f1p z?civ4PHd!i7Q)w(&-m^{j|IUj;XFl&iVj1iCNajFNCmE311PUBH7;zq6V}COVpk}* z1vfVVdSWT?!d&hqmQ+%k<*8fHXb19osz*R6(zUF5EY~qO14sLS+XnBE^Ghp68oU76 zDb|#liUry`$DV$dH3X9R#OPP_M zyN2!ux}Z4w(*bXVZ!iJ{Dgd85bIQQ=;in3Y03fK0gd#F`DcsO#67gv7k4+Qq_A^H@%{>fW7wR8 zLFuskwk#~gfq?4vW9`i%c9E~4v*v_@oc6Vgw|;Tfmpr3iR`Ro_xrx=Y=NR=|D`_$v zfSO4v9JiX2(0eiY3|fV%aTzp?YJL8oHYK^ZBAek)QQ-DjYSF8}aD>31(E+BEJ-?ej zG+wS!b7~j)d;r&d53T!llKm7mjdkIwF6>Op6FZzw5AM2Um;;ZjR$Js<&L=kf-bHn} z#cm=&or@+eTx13mq6VMr4|E@_YQLB~q&j|6Q0DaRW=)0QjLSmty|8{@t?%k_S#5^{xD=jZ z)3ogwddtvdA8Te{XrN8$lE8Abx2(p`@l0q-D#-J=U`VpA#Hnq=RtjrYbheFk11oJ< z*e`qfr}h6ye3#u7waJf3XQsr-=BXkuENZ|O+^KWoQ~d{QpuF4Ak(di=xvBI=Qc!MA zyd^iwycV+`LiJQ9zRDuvoD}&|=Ta;yDS2UWL91O=v-*zR!SuAWrM1TBJv(*4?nnF?9wM!N(#?R~nfob66C2o!^%4&fqiE5TaHONG#W{#I44A1kUi&EH2K zRTGcPbOSPs%99u2xU+FXEioZ_0>}6cY7f*atGVJ9t{+M31&&%V<}tC!Q~k&!THzh@ zu=yq&5X_o#%Ig6F({e)ks~M(oBD*Jfw=Rx7q3LJDicBwP;gZTUo>^ZNeK&U^8!zu$ z`iRu|(?{~gKr%Tl40PCHN@W7PQ}en z89}_~SKJ!$(oVb9DfXk4sCexEBBLMZgOE9(rcWPcCke#?8NV4Yqa;p5{jV&>hY}Me zRw{99!DiW=z7sVei18gfYO=W+qY%N}v%+Sx#z!f==dlG?ljuLB__o__<`L}=lBK3E zF7-qUFi$QfC8luX4hzu1boczYky_Fn>2bb~UG5Ll>W^{O}`rBgAO>&sQv- zOOqf;9nHXjHMSir3&9gg0xQt|={CV&aQi+H2mc$Qna`?I0`JAjwZZO{whwwF@>)FR z^b1RuJZX|)rK!5HCGb&lyA+UuE=ZSoD{z_E`q+mi6#WKUD6ji-3U*>lfEV1v1gAuI zR@}gITkXiq?NwUoFFTXgl~Zl+1%waZnirnYWb(0mAh7)*S~kW;&EK+?=Ru5f@Zr~{ znSsLSLi?XmxbLtH~vct_K5OFIQVGhG1@!Ov(vBMn5vN)v#UQU z8|DjfHMw`}IwE~)fviGhmJ;aAJVY8DrXCYdYlH@yroaGJ=7t(zZClAeQbItYVVZT! zauVdvkXAb9WL5-nr3~7;&GYy{%Mz>rcNJ#uqXV-d3oS-qcZQ zc4QEzh8j4S_$!D7z!z1ELjFsyI=9@xd-L-uz}!YZ`5{)WV!qyWZSqojxBCsUdhj2E znG;Sub8j*CzVXSbE$+n}9(<@E-0g2UHiR9@KHO+)mY70naZ=cjS{&nE0k!I$U8`*; z_wac^Zd`CDB`2gCLn4|D#S49Pvf|qtq6YI53I#x=Nke-Xet-K5)fusoB;=e`_>fS-+DELqtATN<$!4miy^RIKhQvYJvGNx&h;=OExV##V&(6%0mx$-m%F7JX3? zm|n52QBph!GOJ$K+oI$S;q3s8;+3R(XkKbG5B+IUzVjl^^#}%Wnb^m@SMZBHXFsh* zThCe(cBMP`e=&*8ZJ@_rNN`@hlTt^pOCs1G4x!+-#RZZNGi4tzV)Axk@G*xS#rHol z!0S>?TabSSC(X(Wl((*9jKLK7+D-_f`1IlA^E`aYDO4MiJea3kPi= zBQeuu?xYy3zJw33*-@PTks*3BS@s>&52057t=3ZqQ%cZsv@&9_ddWobyXLSGPjq;JVdvRiLFmECz^h>9oSqkAFJE+js9W=Uz5T0+h z_~1_fFMecCK_eRt5qR!A2UuQ%o=+VE<(3fYa2T?5a1XPe4gAxkU(+&~%~071k28Gy zqsq=sf%Ax4skZesQV5qxJSPl~^L+fN zsHbOZgh}tc=8jO>m_P9ID!x$DeFj z$>XJOfMZ=wb~eqONhD+wP?T5krJ8VAd9_#n3VU7et{9==Jo!16X?~S>;|zRZPIzBP z9uKL(=H7RKP-byD!Ejzrw1sX#B6?o`fI!xh1fNTu0v&a?LB$a!PK&;~Pv-j!yQBi8zUcWGsCLB7|IrbjJaDCsXE6{1|w}#^%djuE{GS7{ULbm z#&rkxl`U;lKVK@$V-HWg2koQKZ4&b?l%-*f{0@-=?1kSuowT7u8Bx|U7YI@OK49%u z`W5APu{7e3D7-xy#tUN=A8{g~)Q;UeYN3{&(QyhZzRNh7Upvcv-iR*GK_>70ssd2u zL;Pvo9rIJuzkVJZCF#D)8M&DQM+nb{>PKc(GW!8l8tmML{YPewINs%hO>3f@|FS;^ zEF?I&z+hQ_j=Kluk1P>{CAzjpyDADP6`9p&5oBNERs7DVyroe#?*{akwfT3(p z-~bx+iDSBh8K=TW8VIidFTRs9>Yt=C?3mfFtM86Hzk|;_j_`l|1QBEx3)3}$htzW3 zr&@Sj?~5#Dr-Rh=qwo!*w|bu|RC$2XhW=OQ2F7_RQtuflu~5_h1nLEzRl^bO)Y@A6 z85Ea>UZ*Amv%*B!t@WnAn8%K$F7ojruxdvzkg*`&>GR`%sdxU&Ec=je`{??StPOPs z5&6}!ol33r+oeVavv{w8KvLa8Tge1z9l>mf6j`fwZx1ejpQN-1FNJuFSjo>8etelr z+Y~-BXYBq8a4$o?bHCP0O|Te$KS&@YUw*ot?nKMerm{|ugXo4CA&7) z#;Zr3zX8Zdv!Yj^Q*d{r0cl}i+YP<88wieoiB`6ki)7b!y_)!_@N*I6Y- zBJe^4Z;LRxAq1Rmh-)3<3~B0;WWLioHdqrqD8=_~hcs3h#di5oqW*_osJ<1AH+D)o zvs-47r91NYkf~q5RHN&~o7mDo(jm&A_N`OR)&-rg)xo zdLBLFJG!p2=g#S10lZkL(XhR-QD>|&b0!W@q*K-kWM|Q+*H`rHj)WH|> zO}Ur$H;xi7P8n&9Bn`HUzEx=ESoctTf>oWh`xk=+c9bZHZHq)m5)7t51#Z7WnAm;k zc}$2rs4q{#Y-E+qDV$dEJ706GXWXL7LGfE2VCVU^^_lwuVa-7HZY|G|&<*s=7eS~= zjzVZxf^zld3+pNczqNCoPG-j-Q^cle$N`qv81?7YFo&Nze>a&cu+k)vhRrzB}Y1=rlV-H zLeAg|swdE2{S1p|UnU9=-wuf{)Xd?;9JHD)?)EE~uf?h50USUC?otFeWU&KeY@$co znlvJx*Zw73xG!z&$FwL>udPO|ub-j7F{=)*qbM1KP%Y!S8yJLb7U#huo5rs1<))(^ zXM{XnvhXWP_vot0mVXtUco4M2!@j+Aq9Nv(N0@D;v$4b?l;TQn#Flm$@G+4lWmwvY z4!h(`-{~C7e%D9t`=(Rjfa`&!Rp(|%bkc!QXiT)*b~Ttj@+c%xhd zJ74kD8tt`rKSN`>QK~{-Ef8!-Re9pGz|+OdvpYxDMyA0{n98ng?L~aMeN&)(?=2(% zi7sSSZ*S%b)-1|p6s;ip6kNd|h~u3+!q7`gU8~#=G{Y87kYfwBF2l@&{cn= zTMi>8Ir4T~erA>r*u^*E*=v)9d(XdSYq5{m2>`3rm z1(EPCKOvA3l<~k58NLePX=I9$84P9hN(??lf)N)?0`5zx(PhR)sO2eeK)(I|RUJx5 zpa~ZxwVc-1qrK) zz@i7O09l=pj;nD93#(1S)=;YS5%%+l`-4lxkKf@@vD~vMZhwqBtci4tgSYho$@(WK zQC(7arfu&~K~0Sx%Yur{y@JG7d7r%z_$h==KeZM67+oDS&4)*F_z)wvKF29eta8%` zr%RyCfviy3mTULDhAAe16YnWfc4G5>C!MuISY0>;(i=Emq|~BAv4^=fNObyBIdJ9| z%eD>_d_3H-6*!-?hBQ4N6{-s`tFb5*Z~CO;rxI2fu4lhtto#VooQ3oBRGA?GEdFeV5uK=e8U8Gl*WA&#Ps9>Shki zaGD}gYrn2+R(y`crP4%YTQDrzz!ffx)KO7aX9SAwNRN3Bz-f zQ--<#uaQ9^CtsIZ+%rqK<^Zl})CHTdvme~*pDxYDg3~`YLyQZMUL0@X?Erv8PGigB zM?6_Wm&1Y|7fm?shm6KNUHHBPA_emp38M` z#0YPOyg<(o-0lw&fgyZGy}PDxDA}9P{WWY#m^wL{RBWn2|0~)4XM0Q2L_^wP^op&I z3X#xm+akz-zi)1Ds3u0-iHdw7b%L9UusRJD8Ap*OC~0B8w+NJs)Zw&MZg^2AE*a+a zQ{BQ^=cp6AwqX*|f2Y)2bEG4EfMl|G7(Z9+%q_mvI@Y$a^J$JO23B|KX|k3xdoE#E<>q;7$}3U<^9SMa6@3acKG}HB4pR(F2|{YL#kNMPD1*cW|J~12#3* z=t<6>u`*Y+jI__a3$omt#Mb(LoYO3K2A&~CIvi}nnvnHBbH z6H?XIR>zR}Fu^dbpe%_1CF~&1;Uv!S7VCseAT|q5-`bid0^{E(1#~v0POz~l$!A6= zIrDrH9HiDRnnYbv+YSOJc)a*}yz;fZ7fH3DNpwy_OW6jYs$GqKv_-Mo-m_8Tl8He6z0JJo{>72t_O6nIj|L9 zwK(c|)2)4kF~C&rq@VOR>D9+;qHAP}Nk6LTOvYJPF*fZ_lGy3vdXOkNH{a`aWxKD z%>>6s2r4VYyN@hRf4R_B>?PmRUKrwWZ4X-fEH-`K_mvqElTgzqd;0*w7_Bl`{3ipR znXGn)e`+fcf!E%hX$5>@C%!2^Odg&YcOq1|l?uSR*^|ShEH7!8CZ`1AMNsk8nm)JU zS5DorGdw4NS3XJJNU0m$3G$PtT8>p=g>AuZ>nP@X(?Ym1gHA%Dov&UcRP_!-qX->k zNcSbhU@;kuP)#NW$w)OP>XNBJ6r*= zrQnVR`OY-Vo$MCu=2!uTusaKT%;BDOsTm%uG#@n|uI@dPz{8y$D}o@;FM~}eCo~5a z2_4VY=5166Lsn7DYkL7<$Tmuygde$@N?2?%@ebmCIJ#A-GqlqBzKmZZ37XQq_xU0k z1isM2oSXkx*%>jQ66{4Zy5AelWA>1L)F@!@-c0lVfbJJnj*6TAq{d{@2m^G0&qh zd4%I9u)*8NP5P zpc?0@LWX4FQUuQJUUA9r`v`a>98OJ59wim;TUA+>H_b-;haZ?sie5>=GCqdXhTCae5BMIIVkN8vEB7gS#+cAB>jmffH`E4{d?WMYkp&Aj!Y8jJ( zz5hv%5Oe~8!LAy9D8+fAnIN#>+mmL6g|)>5A*KsCNw|^mUadvN_xH`Uz9+LuR*0y7 zXGo|%3WHj57Nwu`BO;In7 z)D!E4^^&oS9R-f-i{*h`EC0%wsJ(wFDq}c|EQslZZliM=OANQ?VEXdBQ= zIc(~rykl&?4J4LgWf>&$`~`6%7kAz3Sk-KvoF;=m%s0lp>zenxLW`E)y^pPK9sT;h z-%g)n2IjQ_M~(LCLPa1oW{jg+Q;SO;HR~jF6d)_;-3f56aemxqHIZh7&6y3(#5T9~ zxbKffx;zufQvjuuDV2Y-*26|H`f9|In|uDjZESfEs8?F{bfQZYwHL<|)z^7$J0G!_ zfn|nt0fmkmNkl-jlx1=4{#C@CR$&#I%Al0;-00{l#l3$5ZlRdMef3wzK-}rM`t-@R zTyb!85jXzVfvhqLb|*3!Z2gP_>1N=x6xKkOOfe@p!Li#1IdF0Q>y5pl?BL^rhuN%# zyn^@LzDhOyLwj`7qSN9BCH+y)>m$vR%l~RPU1On!ylq(hUG)wJxlc?fY$H|W{N()w z6zptMI@uaJ);^sy;rX?DN^7%fZZ3ALS4kL8sTvVe@mrU&ES7R-uuL6Q5IsMKZcZYv zkIOg=&x}Sl*UsV9a`;ih+=O3fI?pVvg-kNT*R^jo$Sl%Sg(0K#Q-TbuO!r#OX};aX zXrEFQlxhDl@r!Zso3H0Hj;DFV`*Qg-V_$f3Q6AWu~?`!_?$-3}q`Kon@hZAoo^ zU~^V#IzInpa8(R$`Cpp!-UeLyqujIfxS z$VoWJ92M1=r?n*Q;F$R+Gh75KFI_UrYS9eelikOS;7dF8{?aZ|_2$Sc9&+dq2oT58 zukV^Y?<9wtW`#_Dmfm8OlZ_kGf-z|F*Jwd#0e@>})CpHrcXcL`%0sj*UWFw8EqL2F7{|4H4y3GOrG_3$S*c+T4hCkdWh=YF zE8Ez{W*E4GfuUs?TcF?-mNoEz5=HfgEX$HDz54&2KF`wFku~S%*RMXTzPr3+0FdF& zsVe}2hUYW+I@=#6tcf8IIvV(Hon0?SI@ZbK;+OL7ta0D4cz>5d%Au+-rWVmr_c%Lk zD%V-I+D}lYo+J^WRfA!39gWA)k%Z8Q(2X?a6^$y#5}sKiDF;v~4o8o<@}@o{w+S6! zbVx#IL};dXylDEyzrVCP9Z`5@jQ!!NAii6>D$05JDd*6|VJ3bSmrVRLa(nqEV8=@m z5jt6f$L6%f>3{7kf*xoAggz2TD#FX0%@&NYZ25I~X)?_alGXvLZD4W|=uJ@3s|}Osqp>(mT*4gw?(1<$g)%OfrA7uN3{F%BgMj%2@#^iTrk|08#899Q&W*q>UVkIP?a7yqb#;2|o1wiNe3@S#3i<2n zw#@$iCSD_2lPACjZ+rJc^JSiY`5_~5w%+e%Te(aj#QAD9DuJ(P zYJV>@J7vuK`%iqCi>Bd4v68!~P77eR9laXR6E0y|VdND}#aS{Hx^hnIPQU+nE4RM> z-Z925K3t1{Ew$!lSugppf3p8ifEv8a>r0plM%H?gC3;P}?b;%QXa&Cicq=4UdHuZ; z0sHys*q3wh;kv%tEWfE2>&bHT6%Hrx6@N`_QjRaP+2da;#@N-(T*z9{G+V8BSzdg& zHYshl*lu_=m-_tk3X$6m-zi7nE1Fv6&}DwTDXwnjoAQn^#;!BAJ6&z3*NfFR;ri9h zysez)r(e6pvCD764~uNZ*m1@0&A$(D+8VEoLKf1BruAFy;JF-4HiK!&R(r-gE`Qr~ z4z4ej11qkmCy*Xf4#t>C))9Wn5o6{(@@huTS2VT8a_Czf@qXQw7saaH+H17AzFQ;V z*bI6R_v9p|?Y3j5aTavjP0naH8TkYXpP$VZVY{OBYRUOJ4`M!a%fQNUjF6pTbA4b1 z?oP${DO01_$4x$uYagN2p>K9l8Gl3Hpn}HiV#%F9g4H{RmZRMDi5!ATnz6tC6l0?b z_#?Q7WQDAnxLb+M!e(eqh9O_2Zw)ZPACSVqL$V^r5csxP%mIHhP3rVcWmDyS6?w;~ zm*2R5;}JVQRjEnet!=a2>rb*-)Rr}!Np=5t@1+hNlFgK{ZEKImOh_z_C4WC+{#1qh zL#P&82coZS)VW`0<%d2b%hqRu`K~te%DgCVr&u4-+KyZQlzJ-bpXzlK!#e0U+0{1o z#nU=ZNGPk10^o#xLdSl64l|v@ zn)=<6#WX^Pf6B~7vA8TX*?+TPcf^LoG?3d`IhxjwVdGDIzpVqy;wbt} z8q6Dov3Iw*qF)Yect@r|-+sDjiL18$u%g>kLVYNy)V8gVwoPrvbrQeE$yek48!LKToj$$FLyqF(3)YWpdw)8p3F90We`5uZ zf^vig2?C)3Dd;o`2a*?8;>CXZ9A<%@$|OcVp#vFMam&$?#XrYi*#jG9jPn9Ll}VDM z8-*21-moz;rgDW3FVIINlOj=}b(W|Y8{@n{Pi2xPIiXE0QPCRXyg*lFlFSb~NlWOw zxawz#%I2Z2pz{Jfm4BHIj=Sxgj0~I4Ew6cu9-N{Fs7wD->GU&g3$K7^L?s1)k z3^|HecTWT{&Dzz1ta+it&o&I`sEb#OEKwN13U98cVA(jj&u&t`jD;Wr$hS1ZR z53K zLdpS1kz9lZ5z*@wA$FcDLgRre-~*%_#Nqi&zRvcC32QPA#Mib<7U^-J{&m?|;*IBmOPf(|EfzTAtc$^ycLZekTIgNQmqsobdXEsD=@-_#1#TExsj}rvF z9M1540e?u37foN;_m@_u+rqP5g@e7$1rWM{puRJ`$0}`3r;~3m2pu@ZVb-dL%NwCX%OC+7g?)LGa=_-$*v<}{7k?&xI?;67^*yeuEOGp76LUF2=Nw#L zEC*IBa249d50S{BN5x4}ClhO~ys5W2on8(^=x!V~Eu54iW41auNy?r0qz*)A3YbhE zO(aMoLQ_CKTgB(8PmD%{rb**eX`Vpm9fT%MY7famXg~@qGy_QCOlZtN0wI8u1CRnK z2Y(<1ga)KA5<2$$IQIGjbC5%WGXy5YBjo^R#EqLR&7KTsuxgcdVYoTGvQr3ZiS-3DT!#Zrs zZs8p^%XZiV2JXVZ!YON50}VSYWev*(a%4q+mi1%H@_C;;yN>M08t2RBqa#@zU0wo$ zB*TBYZU7QB#bivk^WCAtmKcJ9?hVDuI=@}Cbgq+*XIrLpEPp*VE=EHLIan#QDU04A z>Nr2`TCMYZx$B@!4H<+(vkFEvi)eJ*I|dJFI($6d^HTNih26X7* zFpzJ*Nj}!d6y7Gw& z8nHtfjTDOHT~v1=6f~lvcMr*Wn?8z2VQ0ODo?#n;e0QL)uG?Y+I&^1res@k1bm)qf z9Te3Z0)G^A1u85m?EJ;2p!*mjBGF>!FFpnB;&Aw5K)eu3$bq+e6YytWODux6V`yLS zFn{)qzGY0?A#D?Mux1cTfgv%_&Wffe=!i&isHNoFbTG7-=(8XdhpZ>{~ z>>fV5wFha5KzD=A9~y8HbiUpd@YiHhJ&c%%ajXU9pM9OB7@ia*!~CL>DBkpn=E7(+U7C zW`9eJJvzkuYBem8uV`v7XgEd8yNA!kVnVux7qcZjjMX#&m2F3(4s?f0m~yndqA9sb zrh?|Ksi^7fk9)fJ@4j zQzBMQrulMNtX3B{wWDw&=wsyF@@hujS2VT8a_E~K@&0&P zUCfqZZ%?Xb@h~azs2OQQzb7YwHf;x|aTZM5bxvzH8TtfDF|CwMY@3tya#0lPNfh&; z6$2~f7$KazSsYkF?RG``A(K?u<$tb_=e2iecIX>UQpTZgghQk3*`o0N2v*}5nvQad z6FCT6n6Z2K+{aE85{~E|k|ko*_%7$oM9nDcWN`T^L(2ds_#;W-;UQTPV-P~Cnalw{ zngU8?hX@vnB`)qekt6+ON#pMPG;E@uhx1?xw) z1D(`_cFv1`V+BcylpPuwASh@gDbnc_Ix?AEk&WHcuQ)3VRA*u|6Lcg4D}E_eGXG!s z79PP(G2SZ-RA-V5x>LxhUSXg*lOf zHff;yj0Zu-bB&N%k`GsY17}4he~sd&eNTzj#UhHKvAct~6Oo`1#2xGuB1wUQMv?*r zjU)vM8c7NiG?El)3Od>>DH57A1&#Ua$O-2-n1@D^B7aSXMg|KC8c7NibX;I}))5qR zSegnHyR(j9rw~aBbZ8_g1}A6?QqAe(wZf1CNs7TaG}0p)-6F`r=+H<~SS|&&-yZ=W z%SLpYyxs4Qq>z-85_YfZQ?y)l3a)a*kYX~X+xhO$VN2S~NAa7g0{sgws|&wyDD4bd)qGdq+z zE|@~SC8MBg=<4ntvq^{k0Z<*!WnbC%ccrFPYJawCu;V|IQ;sy4borJ|1XEyQ#p-*31=p{K@BWT*ht`w|?>N{rVc42lH*Xo!>rRk?} z@C>vlMB4Ka#~;K(UxLyZLik6T$hV7kS4mnzm#kG z+kbfcaA;#xh3mevGOhir;bD4|4jsRmIecllr_jhmN()hk7*wdD3kd1ZJ|`y~z2dQ9 zNDB}xwl=b>nz!W5p^rz;j^4DwBpsUb-P(`?6k+uI650tlYH2mg!A6jc1nphT7S*OI z=)QCcBjGIM2;Cq0Sn?Z)o~Q9)^A6-yi+?6<*Yv!7d{%|f#$S^CSi;4s@h*f=nd`gd zz={R2rFzWq6GN+F1FBb*CDXl9S?fa%(sA^jxeJPyv|uMg-_nGdX1b+nZ*8y9aEhg> zk_HaV3CM(tRZVn3_F9-UO@jmq-&s5+r@hoeJ)51JM8zFqo}Efqb~z3G#N(#5aev4i zT58W4L5r#)rfVK6lY=>BbJem9VDozYGU*VQOdqv2X>LH9D+IwTHM{8E&1SP${9bz^ zi&>UN4$Z2^(5D-z84QAH&XPx?k;NS&Rp_%-#nkh}XsMdP`C+2@eD2mRB}NV{4s&oa z;;hsqiNVU6tyXHu9D{Xe5$ROvqJMevP9^Bt<0*#zEiDh_(Cl5_#1W0SEKU9vQjJc@s{lM*p+XPht|NRBiPYF{Xk&t!J0u6X$KLr9D$Ii`SG^g$hX-8 z?Dt1ljn2dkkbU0*!2W&;?|)kWfXiw2eOt|kUI059l&z~1wKP&_h!`_)whh3)rI^?= z<~rKP=vfk~=JzU7Sg9ERw%haL729su3%XWVoHRL!!+F=xB+L*;3Jql);e%DZgHI>l zm+~zG>&IJa!)G6R-*RFt^K_q#&SG)DwGTNsdDR}zxv!xaHywe;kblDk!(Tq@yJe_w ziXXC{l~~#BlIgUJL$1%ubI5)=wVYz_&|F@*R?`~8mLgVY?a+uJruQ+T=As-}-``Km za3Tkg>asFvLCvp6001FqRt*-`jvd-(Ik+ePh^DDLy-e;ygH)dPLBbtqIhH zAn^l0<<{l4qy=U9{usSUF95hch1X5y`+tjXMRY4CSuf2&LIU=` zv>eW-FSCb%gZrYIgR;M_`fXIfq=fI@K_%Jj4fdqu6J$S6+WcwHquI6+I_ z`ogqR$V#Vnu00nns}5EjCW0dFo;N;bkZ!Zb^L&ye^`wOkxGs3!&O*^B;?AMj34k&t zriGc}NNV9y#cOsog$<~11%D(d@Gr?CNr8e!k^%+Y1{?idDz10@A1=@p4a16U1poj5 M07*qoM6N<$f{9f%aR2}S diff --git a/examples/combo/src/lib.rs b/examples/combo/src/lib.rs index 0092078a..42c9180c 100644 --- a/examples/combo/src/lib.rs +++ b/examples/combo/src/lib.rs @@ -21,6 +21,7 @@ pub enum Game { TheHatChoosesTheWizard, ThePurpleNight, HyperspaceRoll, + Amplitude, } impl Game { @@ -29,6 +30,7 @@ impl Game { Game::TheHatChoosesTheWizard => the_hat_chooses_the_wizard::main(gba), Game::ThePurpleNight => the_purple_night::main(gba), Game::HyperspaceRoll => hyperspace_roll::main(gba), + Game::Amplitude => amplitude::main(gba), } } @@ -37,6 +39,7 @@ impl Game { 0 => Game::TheHatChoosesTheWizard, 1 => Game::ThePurpleNight, 2 => Game::HyperspaceRoll, + 3 => Game::Amplitude, _ => unreachable!("game out of index in an unreachable manner"), } } @@ -46,7 +49,8 @@ include_background_gfx!( games, "121105", hat => "gfx/hat.png", purple => "gfx/purple.png", - hyperspace => "gfx/hyperspace.png" + hyperspace => "gfx/hyperspace.png", + amplitude => "gfx/amplitude.png" ); fn get_game(gba: &mut agb::Gba) -> Game { @@ -58,13 +62,15 @@ fn get_game(gba: &mut agb::Gba) -> Game { let hat = TileSet::new(games::hat.tiles, TileFormat::FourBpp); let purple = TileSet::new(games::purple.tiles, TileFormat::FourBpp); let hyperspace = TileSet::new(games::hyperspace.tiles, TileFormat::FourBpp); + let amplitude = TileSet::new(games::amplitude.tiles, TileFormat::FourBpp); - let tiles = [hat, purple, hyperspace]; + let tiles = [hat, purple, hyperspace, amplitude]; let palette_assignments = &[ games::hat.palette_assignments, games::purple.palette_assignments, games::hyperspace.palette_assignments, + games::amplitude.palette_assignments, ]; vram.set_background_palettes(games::PALETTES); @@ -79,7 +85,7 @@ fn get_game(gba: &mut agb::Gba) -> Game { let y = pos.y.rem_euclid(20); let x = pos.x.rem_euclid(30); - let game = (pos.x).rem_euclid(90) as usize / 30; + let game = (pos.x).rem_euclid(tiles.len() as i32 * 30) as usize / 30; let tile_id = (y * 30 + x) as usize; ( &tiles[game], From d54560f3628a475ee90a19421e10a0043613ca73 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 21:00:26 +0100 Subject: [PATCH 37/39] fix warnings --- examples/amplitude/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/amplitude/src/lib.rs b/examples/amplitude/src/lib.rs index 6a19fa27..65eb9ff4 100644 --- a/examples/amplitude/src/lib.rs +++ b/examples/amplitude/src/lib.rs @@ -9,10 +9,10 @@ extern crate alloc; use agb::{ display::{ self, - affine::{self, AffineMatrix}, + affine::AffineMatrix, object::{ AffineMatrixInstance, AffineMode, Graphics, OamIterator, ObjectUnmanaged, Sprite, - SpriteLoader, SpriteVram, Tag, TagMap, + SpriteLoader, SpriteVram, Tag, }, palette16::Palette16, }, @@ -124,7 +124,6 @@ struct Game { head_position: Vector2D, phase_time: Number, input: ButtonController, - energy: Number, frame_since_last_saw: i32, alive_frames: u32, } @@ -150,7 +149,6 @@ impl Game { } Game { - energy: 100.into(), input: agb::input::ButtonController::new(), settings: finalised, circles, @@ -362,7 +360,7 @@ pub fn main(mut gba: agb::Gba) -> ! { ); game.render(oam_frame, &sprite_cache); - if let GameState::Loss(score) = state { + if let GameState::Loss(_) = state { for _ in 0..30 { vblank.wait_for_vblank(); } From 929eef1569ad5f4d2dc48429eaaa6ab5c5663878 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 21:01:04 +0100 Subject: [PATCH 38/39] add deny --- examples/amplitude/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/amplitude/src/lib.rs b/examples/amplitude/src/lib.rs index 65eb9ff4..cf5b8c6a 100644 --- a/examples/amplitude/src/lib.rs +++ b/examples/amplitude/src/lib.rs @@ -3,6 +3,7 @@ #![cfg_attr(test, feature(custom_test_frameworks))] #![cfg_attr(test, reexport_test_harness_main = "test_main")] #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] +#![deny(clippy::all)] extern crate alloc; From 409798c3bf1d3cb7134a480f7daba0db309325e7 Mon Sep 17 00:00:00 2001 From: Corwin Date: Tue, 25 Apr 2023 21:07:31 +0100 Subject: [PATCH 39/39] use fancy way of getting and setting slots --- examples/amplitude/src/lib.rs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/examples/amplitude/src/lib.rs b/examples/amplitude/src/lib.rs index cf5b8c6a..8c145abb 100644 --- a/examples/amplitude/src/lib.rs +++ b/examples/amplitude/src/lib.rs @@ -63,7 +63,7 @@ fn draw_number( oam: &mut OamIterator, direction: DrawDirection, sprite_cache: &SpriteCache, -) { +) -> Option<()> { let mut digits = Vec::new(); if number == 0 { digits.push(0); @@ -84,12 +84,12 @@ fn draw_number( let mut obj = ObjectUnmanaged::new(sprite_cache.numbers[digit as usize].clone()); obj.show().set_position(current_position); - if let Some(slot) = oam.next() { - slot.set(&obj); - } + oam.next()?.set(&obj); current_position -= (4, 0).into(); } + + Some(()) } impl SpriteCache { @@ -259,27 +259,25 @@ impl Game { } } - fn render(&self, oam: &mut OamIterator, sprite_cache: &SpriteCache) { + fn render(&self, oam: &mut OamIterator, sprite_cache: &SpriteCache) -> Option<()> { for saw in self.saws.iter() { - if let Some(slot) = oam.next() { - slot.set(&saw.object); - } + oam.next()?.set(&saw.object); } for circle in self.circles.iter() { - if let Some(slot) = oam.next() { - let mut object = ObjectUnmanaged::new(match circle.colour { - Colour::Red => sprite_cache.red.clone(), - Colour::Blue => sprite_cache.blue.clone(), - }); + let mut object = ObjectUnmanaged::new(match circle.colour { + Colour::Red => sprite_cache.red.clone(), + Colour::Blue => sprite_cache.blue.clone(), + }); - object - .show() - .set_position(circle.position.floor() - (4, 4).into()); + object + .show() + .set_position(circle.position.floor() - (4, 4).into()); - slot.set(&object); - } + oam.next()?.set(&object); } + + Some(()) } }