agb/examples/the-dungeon-puzzlers-lament/src/lib.rs
2024-04-20 16:18:42 +01:00

189 lines
5 KiB
Rust

#![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))]
use agb::{
display::{
object::{OamIterator, OamUnmanaged, SpriteLoader},
tiled::{RegularBackgroundSize, TileFormat, TiledMap, VRamManager},
Priority,
},
input::{Button, ButtonController},
interrupt::VBlank,
sound::mixer::Frequency,
};
use game::{Pausable, PauseSelection};
use sfx::Sfx;
extern crate alloc;
mod backgrounds;
mod level;
mod map;
mod resources;
mod sfx;
mod game;
mod save;
struct Agb<'gba> {
vblank: VBlank,
input: ButtonController,
loader: SpriteLoader,
sfx: Sfx<'gba>,
vram: VRamManager,
oam: OamUnmanaged<'gba>,
}
impl<'gba> Agb<'gba> {
fn frame<D, U, T, F>(&mut self, data: &mut D, update: U, render: F) -> T
where
U: FnOnce(
&mut D,
&ButtonController,
&mut SpriteLoader,
&mut Sfx<'gba>,
&mut VRamManager,
) -> T,
F: FnOnce(&D, &mut OamIterator, &mut SpriteLoader),
{
self.vblank.wait_for_vblank();
self.input.update();
{
render(data, &mut self.oam.iter(), &mut self.loader);
}
self.sfx.frame();
update(
data,
&self.input,
&mut self.loader,
&mut self.sfx,
&mut self.vram,
)
}
}
pub fn entry(mut gba: agb::Gba) -> ! {
let vblank = VBlank::get();
let _ = save::init_save(&mut gba);
let (tiled, mut vram) = gba.display.video.tiled0();
let mut ui_bg = tiled.background(
Priority::P0,
RegularBackgroundSize::Background32x32,
TileFormat::FourBpp,
);
let mut level_bg = tiled.background(
Priority::P1,
RegularBackgroundSize::Background32x32,
TileFormat::FourBpp,
);
let mut ending_bg = tiled.background(
Priority::P0,
RegularBackgroundSize::Background32x32,
TileFormat::FourBpp,
);
backgrounds::load_ending_page(&mut ending_bg, &mut vram);
ending_bg.commit(&mut vram);
backgrounds::load_palettes(&mut vram);
backgrounds::load_ui(&mut ui_bg, &mut vram);
ui_bg.commit(&mut vram);
ui_bg.set_visible(true);
let (unmanaged, sprite_loader) = gba.display.object.get_unmanaged();
let mut input = agb::input::ButtonController::new();
input.update();
if input.is_pressed(Button::START | Button::SELECT | Button::L | Button::R) {
let _ = save::save_max_level(&mut gba.save, 0);
}
let mut mixer = gba.mixer.mixer(Frequency::Hz32768);
let sfx = Sfx::new(&mut mixer);
let mut g = Agb {
vblank,
input,
loader: sprite_loader,
sfx,
vram,
oam: unmanaged,
};
let saved_level = save::load_max_level() as usize;
let mut current_level = saved_level;
let mut maximum_level = saved_level;
loop {
if current_level >= level::Level::num_levels() {
current_level = 0;
ui_bg.set_visible(false);
level_bg.set_visible(false);
ending_bg.set_visible(true);
loop {
if g.frame(
&mut (),
|_, input, _, _, _| input.is_just_pressed(Button::SELECT),
|_, _, _| {},
) {
break;
}
}
ui_bg.set_visible(true);
ending_bg.set_visible(false);
} else {
if current_level > maximum_level {
maximum_level = current_level;
let _ = save::save_max_level(&mut gba.save, maximum_level as u32);
}
let mut game = g.frame(
&mut (),
|_, _, loader, _, _| {
Pausable::new(current_level, maximum_level, &mut level_bg, loader)
},
|_, _, _| {},
);
loop {
if let Some(option) = g.frame(
&mut game,
|game, input, loader, sfx, vram| game.update(input, sfx, loader, vram),
|game, oam, loader| game.render(loader, oam),
) {
match option {
game::UpdateResult::MenuSelection(PauseSelection::Restart) => break,
game::UpdateResult::MenuSelection(PauseSelection::LevelSelect(level)) => {
current_level = level;
break;
}
game::UpdateResult::NextLevel => {
current_level += 1;
break;
}
}
}
}
}
}
}
#[cfg(test)]
#[agb::entry]
fn agb_test_main(gba: agb::Gba) -> ! {
loop {
// full implementation provided by the #[entry]
agb::syscall::halt();
}
}