add app_root_dir helper function. integrate with all examples

This commit is contained in:
Gered 2024-09-03 00:00:07 -04:00
parent 74d7f2b22c
commit 0cbd5366fd
8 changed files with 92 additions and 38 deletions

View file

@ -58,11 +58,11 @@ fn main() -> Result<()> {
let mut volume = 1.0; let mut volume = 1.0;
let sounds = [ let sounds = [
load_and_convert_wav(Path::new("./assets/pickup-coin.wav"), system.res.audio.spec())?, load_and_convert_wav(system.app_root_dir.join("./assets/pickup-coin.wav").as_path(), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/powerup.wav"), system.res.audio.spec())?, load_and_convert_wav(system.app_root_dir.join("./assets/powerup.wav").as_path(), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/explosion.wav"), system.res.audio.spec())?, load_and_convert_wav(system.app_root_dir.join("./assets/explosion.wav").as_path(), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/jump.wav"), system.res.audio.spec())?, load_and_convert_wav(system.app_root_dir.join("./assets/jump.wav").as_path(), system.res.audio.spec())?,
load_and_convert_wav(Path::new("./assets/laser-shoot.wav"), system.res.audio.spec())?, load_and_convert_wav(system.app_root_dir.join("./assets/laser-shoot.wav").as_path(), system.res.audio.spec())?,
]; ];
let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS]; let mut statuses = [AudioChannelStatus { size: 0, position: 0, playing: false }; NUM_CHANNELS];

View file

@ -1,5 +1,3 @@
use std::path::Path;
use anyhow::Result; use anyhow::Result;
use ggdt::prelude::*; use ggdt::prelude::*;
@ -26,7 +24,8 @@ fn main() -> Result<()> {
let font = BitmaskFont::new_vga_font()?; let font = BitmaskFont::new_vga_font()?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; let (balls_bmp, balls_palette) =
IndexedBitmap::load_pcx_file(system.app_root_dir.join("./assets/balls.pcx").as_path())?;
system.res.palette = balls_palette.clone(); system.res.palette = balls_palette.clone();
let mut sprites = Vec::<IndexedBitmap>::new(); let mut sprites = Vec::<IndexedBitmap>::new();

View file

@ -25,7 +25,8 @@ impl Game {
pub fn new(mut system: System<DosLike>) -> Result<Self> { pub fn new(mut system: System<DosLike>) -> Result<Self> {
let font = BitmaskFont::new_vga_font()?; let font = BitmaskFont::new_vga_font()?;
let (balls_bmp, balls_palette) = IndexedBitmap::load_pcx_file(Path::new("./assets/balls.pcx"))?; let (balls_bmp, balls_palette) =
IndexedBitmap::load_pcx_file(system.app_root_dir.join("./assets/balls.pcx").as_path())?;
system.res.palette = balls_palette.clone(); system.res.palette = balls_palette.clone();
let mut sprites = Vec::new(); let mut sprites = Vec::new();

View file

@ -1,5 +1,4 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use anyhow::Result; use anyhow::Result;
@ -91,17 +90,20 @@ impl AppContext<Standard> for GameContext {
impl GameContext { impl GameContext {
pub fn new(system: System<Standard>) -> Result<Self> { pub fn new(system: System<Standard>) -> Result<Self> {
let palette = load_palette(Path::new("./assets/db16.pal"))?; let palette = load_palette(system.app_root_dir.join("./assets/db16.pal").as_path())?;
let font = load_font(Path::new("./assets/dp.fnt"))?; let font = load_font(system.app_root_dir.join("./assets/dp.fnt").as_path())?;
let small_font = load_font(Path::new("./assets/small.fnt"))?; let small_font = load_font(system.app_root_dir.join("./assets/small.fnt").as_path())?;
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); let tiles = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/tiles.pcx").as_path())?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); let green_slime =
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/green_slime.pcx").as_path())?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); let blue_slime =
Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/blue_slime.pcx").as_path())?);
let orange_slime =
Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/orange_slime.pcx").as_path())?);
let tilemap = TileMap::load_from(Path::new("./assets/arena.map.json"))?; let tilemap = TileMap::load_from(system.app_root_dir.join("./assets/arena.map.json").as_path())?;
let entities = Entities::new(); let entities = Entities::new();
let component_systems = ComponentSystems::new(); let component_systems = ComponentSystems::new();

View file

@ -1,7 +1,6 @@
extern crate core; extern crate core;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -108,30 +107,36 @@ impl AppContext<DosLike> for Game {
impl Game { impl Game {
pub fn new(mut system: System<DosLike>) -> Result<Self> { pub fn new(mut system: System<DosLike>) -> Result<Self> {
let palette = load_palette(Path::new("./assets/db16.pal"))?; let palette = load_palette(system.app_root_dir.join("./assets/db16.pal").as_path())?;
system.res.palette = palette.clone(); system.res.palette = palette.clone();
let font = load_font(Path::new("./assets/dp.fnt"))?; let font = load_font(system.app_root_dir.join("./assets/dp.fnt").as_path())?;
let tiles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/tiles.pcx"))?); let tiles = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/tiles.pcx").as_path())?);
let hero_male = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_male.pcx"))?); let hero_male =
let hero_female = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/hero_female.pcx"))?); Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/hero_male.pcx").as_path())?);
let green_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/green_slime.pcx"))?); let hero_female =
let blue_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/blue_slime.pcx"))?); Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/hero_female.pcx").as_path())?);
let orange_slime = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/orange_slime.pcx"))?); let green_slime =
let fist = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/fist.pcx"))?); Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/green_slime.pcx").as_path())?);
let sword = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/sword.pcx"))?); let blue_slime =
let particles = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/particles.pcx"))?); Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/blue_slime.pcx").as_path())?);
let items = Rc::new(load_bitmap_atlas_autogrid(Path::new("./assets/items.pcx"))?); let orange_slime =
Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/orange_slime.pcx").as_path())?);
let fist = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/fist.pcx").as_path())?);
let sword = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/sword.pcx").as_path())?);
let particles =
Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/particles.pcx").as_path())?);
let items = Rc::new(load_bitmap_atlas_autogrid(system.app_root_dir.join("./assets/items.pcx").as_path())?);
let mut ui = load_bitmap_atlas(Path::new("./assets/ui.pcx"))?; let mut ui = load_bitmap_atlas(system.app_root_dir.join("./assets/ui.pcx").as_path())?;
ui.add(Rect::new(0, 0, 16, 16))?; ui.add(Rect::new(0, 0, 16, 16))?;
ui.add(Rect::new(16, 0, 16, 16))?; ui.add(Rect::new(16, 0, 16, 16))?;
for i in 0..8 { for i in 0..8 {
ui.add(Rect::new(i * 8, 16, 8, 8))?; ui.add(Rect::new(i * 8, 16, 8, 8))?;
} }
let tilemap = TileMap::load_from(Path::new("./assets/title_screen.map.json"))?; let tilemap = TileMap::load_from(system.app_root_dir.join("./assets/title_screen.map.json").as_path())?;
let entities = Entities::new(); let entities = Entities::new();
let component_systems = ComponentSystems::new(); let component_systems = ComponentSystems::new();

View file

@ -1,5 +1,3 @@
use std::path::Path;
use ggdt::prelude::*; use ggdt::prelude::*;
use crate::entities::*; use crate::entities::*;
@ -88,7 +86,13 @@ impl AppState<Game> for MainMenuState {
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
match new_state { match new_state {
State::Pending | State::Resume => { State::Pending | State::Resume => {
init_everything(context, Path::new("./assets/title_screen.map.json"), 0.2, 1.0, 32); init_everything(
context,
context.core.system.app_root_dir.join("./assets/title_screen.map.json").as_path(),
0.2,
1.0,
32,
);
} }
State::TransitionIn => { State::TransitionIn => {
self.fade = 0.0; self.fade = 0.0;
@ -219,7 +223,13 @@ impl AppState<Game> for GamePlayState {
fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) { fn state_change(&mut self, new_state: State, old_state: State, context: &mut Game) {
match new_state { match new_state {
State::Pending => { State::Pending => {
init_everything(context, Path::new("./assets/arena.map.json"), 0.5, 2.0, 100); init_everything(
context,
context.core.system.app_root_dir.join("./assets/arena.map.json").as_path(),
0.5,
2.0,
100,
);
spawn_player_randomly(&mut context.core); spawn_player_randomly(&mut context.core);
} }
State::TransitionIn => { State::TransitionIn => {

View file

@ -1,6 +1,7 @@
use thiserror::Error; use thiserror::Error;
use crate::audio::AudioError; use crate::audio::AudioError;
use crate::utils::app_root_dir;
mod event; mod event;
mod framebuffer; mod framebuffer;
@ -51,6 +52,9 @@ pub enum SystemError {
#[error("SystemResources error: {0}")] #[error("SystemResources error: {0}")]
SystemResourcesError(#[from] SystemResourcesError), SystemResourcesError(#[from] SystemResourcesError),
#[error("System I/O error")]
IOError(#[from] std::io::Error),
} }
/// Builder for configuring and constructing an instance of [`System`]. /// Builder for configuring and constructing an instance of [`System`].
@ -195,6 +199,8 @@ impl SystemBuilder {
let event_pump = SystemEventPump::from(sdl_event_pump); let event_pump = SystemEventPump::from(sdl_event_pump);
let app_root_dir = app_root_dir()?;
Ok(System { Ok(System {
sdl_context, sdl_context,
sdl_audio_subsystem, sdl_audio_subsystem,
@ -202,6 +208,7 @@ impl SystemBuilder {
sdl_timer_subsystem, sdl_timer_subsystem,
res: system_resources, res: system_resources,
event_pump, event_pump,
app_root_dir,
vsync: self.vsync, vsync: self.vsync,
target_framerate: self.target_framerate, target_framerate: self.target_framerate,
target_framerate_delta: None, target_framerate_delta: None,
@ -231,6 +238,8 @@ where
pub res: SystemResType, pub res: SystemResType,
pub event_pump: SystemEventPump, pub event_pump: SystemEventPump,
pub app_root_dir: std::path::PathBuf,
} }
impl<SystemResType> std::fmt::Debug for System<SystemResType> impl<SystemResType> std::fmt::Debug for System<SystemResType>

View file

@ -1,5 +1,8 @@
use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::{ReadBytesExt, WriteBytesExt};
use std::io::{Error, SeekFrom}; use std::{
io::{Error, SeekFrom},
path::PathBuf,
};
/// Provides a convenience method for determining the total size of a stream. This is provided /// Provides a convenience method for determining the total size of a stream. This is provided
/// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable. /// as a temporary alternative to [std::io::Seek::stream_len] which is currently marked unstable.
@ -34,3 +37,28 @@ pub trait WriteType {
fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), Self::ErrorType>; fn write<T: WriteBytesExt>(&self, writer: &mut T) -> Result<(), Self::ErrorType>;
} }
/// Returns the application root directory (the directory that the application executable is
/// located in).
///
/// First tries to automatically detect this from the `CARGO_MANIFEST_DIR` environment variable,
/// if present, to catch scenarios where the application is running from a Cargo workspace as a
/// sub-project/binary within that workspace. In such a case, the application root directory is
/// the same as that sub-project/binary's `Cargo.toml`.
///
/// If `CARGO_MANIFEST_DIR` is not present, then an attempt is made to determine the application
/// root directory from the running executable's location.
///
/// If this fails for some reason, then this returns the current working directory.
pub fn app_root_dir() -> Result<PathBuf, Error> {
if let Some(manifest_path) = std::env::var_os("CARGO_MANIFEST_DIR") {
return Ok(PathBuf::from(manifest_path));
}
let mut exe_path = std::env::current_exe()?.canonicalize()?;
if exe_path.pop() {
return Ok(exe_path);
}
std::env::current_dir()
}