diff --git a/Cargo.lock b/Cargo.lock index 9162696..efe3c3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3083,7 +3083,7 @@ dependencies = [ [[package]] name = "maze-ascension" -version = "0.2.0" +version = "0.2.2" dependencies = [ "anyhow", "bevy", diff --git a/Cargo.toml b/Cargo.toml index 199dd56..ed2c0c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "maze-ascension" authors = ["Kristofers Solo "] -version = "0.2.0" +version = "0.2.2" edition = "2021" [dependencies] diff --git a/src/dev_tools/ui/maze_controls.rs b/src/dev_tools/ui/maze_controls.rs index 5767ca9..95002cb 100644 --- a/src/dev_tools/ui/maze_controls.rs +++ b/src/dev_tools/ui/maze_controls.rs @@ -1,7 +1,7 @@ use crate::{ floor::components::{CurrentFloor, Floor}, - maze::{components::MazeConfig, events::MazeEvent, GlobalMazeConfig, MazePluginLoaded}, - player::events::PlayerEvent, + maze::{components::MazeConfig, events::RespawnMaze, GlobalMazeConfig, MazePluginLoaded}, + player::events::RespawnPlayer, }; use bevy::{prelude::*, window::PrimaryWindow}; use bevy_egui::{ @@ -58,16 +58,11 @@ pub(crate) fn maze_controls_ui(world: &mut World) { // Handle updates if changed { maze_config.update(&global_config); - - if let Some(mut event_writer) = world.get_resource_mut::>() { - event_writer.send(MazeEvent::Recreate { - floor: floor_value, - config: maze_config, - }); - } - if let Some(mut event_writer) = world.get_resource_mut::>() { - event_writer.send(PlayerEvent::Respawn); - } + world.trigger(RespawnMaze { + floor: floor_value, + config: maze_config, + }); + world.trigger(RespawnPlayer); } } }); diff --git a/src/floor/events.rs b/src/floor/events.rs new file mode 100644 index 0000000..d9bd31c --- /dev/null +++ b/src/floor/events.rs @@ -0,0 +1,20 @@ +use bevy::prelude::*; + +use crate::maze::components::MazeConfig; + +#[derive(Debug, Reflect, Event)] +pub struct SpawnFloor { + pub floor: u8, + pub config: MazeConfig, +} + +#[derive(Debug, Reflect, Event)] +pub struct RespawnFloor { + pub floor: u8, + pub config: MazeConfig, +} + +#[derive(Debug, Reflect, Event)] +pub struct DespawnFloor { + pub floor: u8, +} diff --git a/src/floor/mod.rs b/src/floor/mod.rs index f6e6b6a..b071bcf 100644 --- a/src/floor/mod.rs +++ b/src/floor/mod.rs @@ -1,6 +1,7 @@ pub mod components; +pub mod events; mod systems; use bevy::prelude::*; -pub(super) fn plugin(app: &mut App) {} +pub(super) fn plugin(_app: &mut App) {} diff --git a/src/floor/systems/mod.rs b/src/floor/systems/mod.rs index 38d4d38..8b13789 100644 --- a/src/floor/systems/mod.rs +++ b/src/floor/systems/mod.rs @@ -1 +1 @@ -mod spawn; + diff --git a/src/floor/systems/spawn.rs b/src/floor/systems/spawn.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/lib.rs b/src/lib.rs index 98a0536..3dcce5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,6 +62,7 @@ impl Plugin for AppPlugin { screens::plugin, theme::plugin, maze::plugin, + floor::plugin, player::plugin, )); diff --git a/src/maze/events.rs b/src/maze/events.rs index a860e0d..24278ae 100644 --- a/src/maze/events.rs +++ b/src/maze/events.rs @@ -2,9 +2,19 @@ use bevy::prelude::*; use super::components::MazeConfig; -#[derive(Debug, Event)] -pub enum MazeEvent { - Create { floor: u8, config: MazeConfig }, - Recreate { floor: u8, config: MazeConfig }, - Remove { floor: u8 }, +#[derive(Debug, Reflect, Event)] +pub struct SpawnMaze { + pub floor: u8, + pub config: MazeConfig, +} + +#[derive(Debug, Reflect, Event)] +pub struct RespawnMaze { + pub floor: u8, + pub config: MazeConfig, +} + +#[derive(Debug, Reflect, Event)] +pub struct DespawnMaze { + pub floor: u8, } diff --git a/src/maze/mod.rs b/src/maze/mod.rs index 3731cb3..16f2541 100644 --- a/src/maze/mod.rs +++ b/src/maze/mod.rs @@ -4,17 +4,20 @@ pub mod errors; pub mod events; pub mod resources; mod systems; +mod triggers; use bevy::{ecs::system::RunSystemOnce, prelude::*}; use components::Maze; -use events::MazeEvent; +use events::{DespawnMaze, RespawnMaze, SpawnMaze}; pub use resources::{GlobalMazeConfig, MazePluginLoaded}; pub(super) fn plugin(app: &mut App) { app.init_resource::() - .add_event::() + .add_event::() + .add_event::() + .add_event::() .register_type::() - .add_plugins(systems::plugin); + .add_plugins((systems::plugin, triggers::plugin)); } pub fn spawn_level_command(world: &mut World) { diff --git a/src/maze/systems/event_handler.rs b/src/maze/systems/event_handler.rs deleted file mode 100644 index 65f0430..0000000 --- a/src/maze/systems/event_handler.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::{spawn::spawn_floor, update::update_floor}; -use crate::{ - floor::components::Floor, - maze::{components::Maze, events::MazeEvent, GlobalMazeConfig}, -}; -use bevy::prelude::*; - -pub(crate) fn handle_maze_events( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, - mut event_reader: EventReader, - mut maze_query: Query<(Entity, &Floor, &mut Maze)>, - global_config: Res, -) { - for event in event_reader.read() { - match event { - MazeEvent::Create { floor, config } => { - if maze_query.iter().any(|(_, f, _)| f.0 == *floor) { - warn!("Floor {} already exists, skipping creation", floor); - continue; - } - spawn_floor( - &mut commands, - &mut meshes, - &mut materials, - *floor, - config, - &global_config, - ); - } - MazeEvent::Recreate { floor, config } => { - let result = update_floor( - &mut commands, - &mut meshes, - &mut materials, - &mut maze_query, - *floor, - config, - &global_config, - ); - if let Err(e) = result { - warn!("Failed to update floor {}: {}", floor, e); - } - } - MazeEvent::Remove { floor } => { - match maze_query.iter().find(|(_, f, _)| f.0 == *floor) { - Some((entity, _, _)) => commands.entity(entity).despawn_recursive(), - _ => warn!("Floor {} not found for removal", floor), - } - } - } - } -} diff --git a/src/maze/systems/mod.rs b/src/maze/systems/mod.rs index a67e90a..554515c 100644 --- a/src/maze/systems/mod.rs +++ b/src/maze/systems/mod.rs @@ -1,12 +1,5 @@ -pub mod common; -pub mod event_handler; pub mod setup; -pub mod spawn; -pub mod update; use bevy::prelude::*; -use event_handler::handle_maze_events; -pub(super) fn plugin(app: &mut App) { - app.add_systems(Update, handle_maze_events); -} +pub(super) fn plugin(_app: &mut App) {} diff --git a/src/maze/systems/setup.rs b/src/maze/systems/setup.rs index bde8c12..c9dd708 100644 --- a/src/maze/systems/setup.rs +++ b/src/maze/systems/setup.rs @@ -1,7 +1,7 @@ -use crate::maze::{components::MazeConfig, events::MazeEvent}; +use crate::maze::{components::MazeConfig, events::SpawnMaze}; use bevy::prelude::*; -pub(crate) fn setup(mut event_writer: EventWriter) { +pub(crate) fn setup(mut commands: Commands) { let config = MazeConfig::default(); - event_writer.send(MazeEvent::Create { floor: 1, config }); + commands.trigger(SpawnMaze { floor: 1, config }); } diff --git a/src/maze/systems/update.rs b/src/maze/systems/update.rs deleted file mode 100644 index 415887a..0000000 --- a/src/maze/systems/update.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::{common::generate_maze, spawn::spawn_maze_tiles}; -use crate::{ - floor::components::Floor, - maze::{ - assets::MazeAssets, - components::{Maze, MazeConfig}, - errors::{MazeError, MazeResult}, - GlobalMazeConfig, - }, -}; -use bevy::prelude::*; - -pub(super) fn update_floor( - commands: &mut Commands, - meshes: &mut ResMut>, - materials: &mut ResMut>, - maze_query: &mut Query<(Entity, &Floor, &mut Maze)>, - floor: u8, - maze_config: &MazeConfig, - global_config: &GlobalMazeConfig, -) -> MazeResult<()> { - let (entity, _, mut maze) = maze_query - .iter_mut() - .find(|(_, f, _)| f.0 == floor) - .ok_or(MazeError::FloorNotFound(floor))?; - - maze.0 = generate_maze(maze_config)?; - - commands.entity(entity).despawn_descendants(); - let assets = MazeAssets::new(meshes, materials, global_config); - spawn_maze_tiles( - commands, - entity, - &maze.0, - &assets, - maze_config, - global_config, - ); - - commands.entity(entity).insert(maze_config.clone()); - - Ok(()) -} diff --git a/src/maze/systems/common.rs b/src/maze/triggers/common.rs similarity index 86% rename from src/maze/systems/common.rs rename to src/maze/triggers/common.rs index 457f0e0..6716a09 100644 --- a/src/maze/systems/common.rs +++ b/src/maze/triggers/common.rs @@ -4,7 +4,7 @@ use crate::maze::{ }; use hexlab::{GeneratorType, HexMaze, MazeBuilder}; -pub(super) fn generate_maze(config: &MazeConfig) -> MazeResult { +pub(crate) fn generate_maze(config: &MazeConfig) -> MazeResult { MazeBuilder::new() .with_radius(config.radius) .with_seed(config.seed) diff --git a/src/maze/triggers/despawn.rs b/src/maze/triggers/despawn.rs new file mode 100644 index 0000000..e478c19 --- /dev/null +++ b/src/maze/triggers/despawn.rs @@ -0,0 +1,14 @@ +use crate::{floor::components::Floor, maze::events::DespawnMaze}; +use bevy::prelude::*; + +pub(super) fn despawn_maze( + trigger: Trigger, + mut commands: Commands, + query: Query<(Entity, &Floor)>, +) { + let DespawnMaze { floor } = trigger.event(); + match query.iter().find(|(_, f)| f.0 == *floor) { + Some((entity, _)) => commands.entity(entity).despawn_recursive(), + _ => warn!("Floor {} not found for removal", floor), + } +} diff --git a/src/maze/triggers/mod.rs b/src/maze/triggers/mod.rs new file mode 100644 index 0000000..657f989 --- /dev/null +++ b/src/maze/triggers/mod.rs @@ -0,0 +1,15 @@ +pub mod common; +mod despawn; +mod respawn; +mod spawn; + +use bevy::prelude::*; +use despawn::despawn_maze; +use respawn::respawn_maze; +use spawn::spawn_maze; + +pub(super) fn plugin(app: &mut App) { + app.add_observer(spawn_maze) + .add_observer(respawn_maze) + .add_observer(despawn_maze); +} diff --git a/src/maze/triggers/respawn.rs b/src/maze/triggers/respawn.rs new file mode 100644 index 0000000..5daf94a --- /dev/null +++ b/src/maze/triggers/respawn.rs @@ -0,0 +1,53 @@ +use super::{common::generate_maze, spawn::spawn_maze_tiles}; +use crate::{ + floor::components::Floor, + maze::{ + assets::MazeAssets, components::Maze, errors::MazeError, events::RespawnMaze, + GlobalMazeConfig, + }, +}; +use bevy::prelude::*; + +pub(super) fn respawn_maze( + trigger: Trigger, + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + mut maze_query: Query<(Entity, &Floor, &mut Maze)>, + global_config: Res, +) { + let RespawnMaze { floor, config } = trigger.event(); + + let (entity, _, mut maze) = match maze_query + .iter_mut() + .find(|(_, f, _)| f.0 == *floor) + .ok_or(MazeError::FloorNotFound(*floor)) + { + Ok((entity, floor, maze)) => (entity, floor, maze), + Err(e) => { + warn!("Failed to update floor ({floor}). {e}"); + return; + } + }; + + maze.0 = match generate_maze(config) { + Ok(generated_maze) => generated_maze, + Err(e) => { + warn!("Failed to update floor ({floor}). {e}"); + return; + } + }; + + commands.entity(entity).despawn_descendants(); + let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config); + spawn_maze_tiles( + &mut commands, + entity, + &maze.0, + &assets, + config, + &global_config, + ); + + commands.entity(entity).insert(config.clone()); +} diff --git a/src/maze/systems/spawn.rs b/src/maze/triggers/spawn.rs similarity index 77% rename from src/maze/systems/spawn.rs rename to src/maze/triggers/spawn.rs index a69c69d..af25d4a 100644 --- a/src/maze/systems/spawn.rs +++ b/src/maze/triggers/spawn.rs @@ -3,6 +3,7 @@ use crate::{ maze::{ assets::MazeAssets, components::{Maze, MazeConfig, Tile, Wall}, + events::SpawnMaze, resources::GlobalMazeConfig, }, }; @@ -13,30 +14,43 @@ use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6}; use super::common::generate_maze; -pub(super) fn spawn_floor( - commands: &mut Commands, - meshes: &mut ResMut>, - materials: &mut ResMut>, - floor: u8, - maze_config: &MazeConfig, - global_config: &GlobalMazeConfig, +pub(super) fn spawn_maze( + trigger: Trigger, + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + maze_query: Query<(Entity, &Floor, &Maze)>, + global_config: Res, ) { - let maze = generate_maze(maze_config).expect("Failed to generate maze during spawn"); + let SpawnMaze { floor, config } = trigger.event(); + if maze_query.iter().any(|(_, f, _)| f.0 == *floor) { + warn!("Floor {} already exists, skipping creation", floor); + return; + } + + let maze = generate_maze(config).expect("Failed to generate maze during spawn"); let entity = commands .spawn(( Name::new(format!("Floor {}", floor)), Maze(maze.clone()), - Floor(floor), + Floor(*floor), CurrentFloor, // TODO: remove - maze_config.clone(), + config.clone(), Transform::from_translation(Vec3::ZERO), Visibility::Visible, )) .id(); - let assets = MazeAssets::new(meshes, materials, global_config); - spawn_maze_tiles(commands, entity, &maze, &assets, maze_config, global_config); + let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config); + spawn_maze_tiles( + &mut commands, + entity, + &maze, + &assets, + config, + &global_config, + ); } pub(super) fn spawn_maze_tiles( diff --git a/src/player/events.rs b/src/player/events.rs index 7bcda24..953c8c9 100644 --- a/src/player/events.rs +++ b/src/player/events.rs @@ -1,8 +1,10 @@ use bevy::prelude::*; #[derive(Debug, Event)] -pub enum PlayerEvent { - Spawn, - Respawn, - Despawn, -} +pub struct SpawnPlayer; + +#[derive(Debug, Event)] +pub struct RespawnPlayer; + +#[derive(Debug, Event)] +pub struct DespawnPlayer; diff --git a/src/player/mod.rs b/src/player/mod.rs index 53d6129..480168d 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -2,15 +2,18 @@ mod assets; pub mod components; pub mod events; mod systems; +mod triggers; use bevy::{ecs::system::RunSystemOnce, prelude::*}; use components::Player; -use events::PlayerEvent; +use events::{DespawnPlayer, RespawnPlayer, SpawnPlayer}; pub(super) fn plugin(app: &mut App) { app.register_type::() - .add_event::() - .add_plugins(systems::plugin); + .add_event::() + .add_event::() + .add_event::() + .add_plugins((triggers::plugin, systems::plugin)); } pub fn spawn_player_command(world: &mut World) { diff --git a/src/player/systems/despawn.rs b/src/player/systems/despawn.rs deleted file mode 100644 index f1ad992..0000000 --- a/src/player/systems/despawn.rs +++ /dev/null @@ -1,9 +0,0 @@ -use bevy::prelude::*; - -use crate::player::components::Player; - -pub(super) fn despawn_players(commands: &mut Commands, query: &Query>) { - for entity in query.iter() { - commands.entity(entity).despawn_recursive(); - } -} diff --git a/src/player/systems/event_handler.rs b/src/player/systems/event_handler.rs deleted file mode 100644 index cc7de49..0000000 --- a/src/player/systems/event_handler.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{ - floor::components::CurrentFloor, - maze::{components::MazeConfig, GlobalMazeConfig}, - player::{components::Player, events::PlayerEvent}, -}; -use bevy::prelude::*; - -use super::{despawn::despawn_players, respawn::respawn_player, spawn::spawn_player}; - -pub(crate) fn handle_player_events( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, - mut event_reader: EventReader, - maze_config_query: Query<&MazeConfig, With>, - player_query: Query>, - global_config: Res, -) { - for event in event_reader.read() { - match event { - PlayerEvent::Spawn => { - let Ok(maze_config) = maze_config_query.get_single() else { - warn!( - "Failed to get maze configuration for current floor - cannot spawn player" - ); - return; - }; - spawn_player( - &mut commands, - &mut meshes, - &mut materials, - maze_config, - &global_config, - ); - } - PlayerEvent::Respawn => { - let Ok(maze_config) = maze_config_query.get_single() else { - warn!("Failed to get maze configuration for current floor - cannot respawn player"); - return; - }; - respawn_player( - &mut commands, - &mut meshes, - &mut materials, - &player_query, - maze_config, - &global_config, - ); - } - PlayerEvent::Despawn => despawn_players(&mut commands, &player_query), - } - } -} diff --git a/src/player/systems/mod.rs b/src/player/systems/mod.rs index aeb03be..7d69b84 100644 --- a/src/player/systems/mod.rs +++ b/src/player/systems/mod.rs @@ -1,23 +1,18 @@ -mod despawn; -mod event_handler; mod input; mod movement; -mod respawn; pub mod setup; -mod spawn; use bevy::prelude::*; -use event_handler::handle_player_events; use input::player_input; use movement::player_movement; +use crate::maze::MazePluginLoaded; + pub(super) fn plugin(app: &mut App) { app.add_systems( Update, - ( - player_input, - player_movement.after(player_input), - handle_player_events, - ), + (player_input, player_movement) + .chain() + .run_if(resource_exists::), ); } diff --git a/src/player/systems/respawn.rs b/src/player/systems/respawn.rs deleted file mode 100644 index 002eca5..0000000 --- a/src/player/systems/respawn.rs +++ /dev/null @@ -1,18 +0,0 @@ -use super::{despawn::despawn_players, spawn::spawn_player}; -use crate::{ - maze::{components::MazeConfig, GlobalMazeConfig}, - player::components::Player, -}; -use bevy::prelude::*; - -pub(super) fn respawn_player( - commands: &mut Commands, - meshes: &mut ResMut>, - materials: &mut ResMut>, - query: &Query>, - maze_config: &MazeConfig, - global_config: &GlobalMazeConfig, -) { - despawn_players(commands, query); - spawn_player(commands, meshes, materials, maze_config, global_config); -} diff --git a/src/player/systems/setup.rs b/src/player/systems/setup.rs index 94287fa..84d879c 100644 --- a/src/player/systems/setup.rs +++ b/src/player/systems/setup.rs @@ -1,6 +1,6 @@ -use crate::player::events::PlayerEvent; +use crate::player::events::SpawnPlayer; use bevy::prelude::*; -pub(crate) fn setup(mut event_writer: EventWriter) { - event_writer.send(PlayerEvent::Spawn); +pub(crate) fn setup(mut commands: Commands) { + commands.trigger(SpawnPlayer); } diff --git a/src/player/triggers/despawn.rs b/src/player/triggers/despawn.rs new file mode 100644 index 0000000..417b572 --- /dev/null +++ b/src/player/triggers/despawn.rs @@ -0,0 +1,12 @@ +use crate::player::{components::Player, events::DespawnPlayer}; +use bevy::prelude::*; + +pub(super) fn despawn_players( + _trigger: Trigger, + mut commands: Commands, + query: Query>, +) { + for entity in query.iter() { + commands.entity(entity).despawn_recursive(); + } +} diff --git a/src/player/triggers/mod.rs b/src/player/triggers/mod.rs new file mode 100644 index 0000000..fc1d3dc --- /dev/null +++ b/src/player/triggers/mod.rs @@ -0,0 +1,14 @@ +mod despawn; +mod respawn; +mod spawn; + +use bevy::prelude::*; +use despawn::despawn_players; +use respawn::respawn_player; +use spawn::spawn_player; + +pub(super) fn plugin(app: &mut App) { + app.add_observer(spawn_player) + .add_observer(respawn_player) + .add_observer(despawn_players); +} diff --git a/src/player/triggers/respawn.rs b/src/player/triggers/respawn.rs new file mode 100644 index 0000000..6c83923 --- /dev/null +++ b/src/player/triggers/respawn.rs @@ -0,0 +1,7 @@ +use crate::player::events::{DespawnPlayer, RespawnPlayer, SpawnPlayer}; +use bevy::prelude::*; + +pub(super) fn respawn_player(_trigger: Trigger, mut commands: Commands) { + commands.trigger(DespawnPlayer); + commands.trigger(SpawnPlayer); +} diff --git a/src/player/systems/spawn.rs b/src/player/triggers/spawn.rs similarity index 66% rename from src/player/systems/spawn.rs rename to src/player/triggers/spawn.rs index e28b667..5c77171 100644 --- a/src/player/systems/spawn.rs +++ b/src/player/triggers/spawn.rs @@ -1,19 +1,25 @@ use crate::{ + floor::components::CurrentFloor, maze::{components::MazeConfig, GlobalMazeConfig}, player::{ assets::{blue_material, generate_pill_mesh}, components::{CurrentPosition, Player}, + events::SpawnPlayer, }, }; use bevy::prelude::*; pub(super) fn spawn_player( - commands: &mut Commands, - meshes: &mut ResMut>, - materials: &mut ResMut>, - maze_config: &MazeConfig, - global_config: &GlobalMazeConfig, + _trigger: Trigger, + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + maze_config_query: Query<&MazeConfig, With>, + global_config: Res, ) { + let Ok(maze_config) = maze_config_query.get_single() else { + return; + }; let player_radius = global_config.hex_size * 0.5; let player_height = player_radius * 3.5; diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index 84945f8..2e802a0 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -9,10 +9,7 @@ use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen}; pub(super) fn plugin(app: &mut App) { app.add_systems( OnEnter(Screen::Gameplay), - ( - spawn_level_command, - spawn_player_command.after(spawn_level_command), - ), + (spawn_level_command, spawn_player_command).chain(), ); app.load_resource::();