refactor(maze): remove triggers and observers

This commit is contained in:
Kristofers Solo 2025-01-06 11:30:00 +02:00
parent b64930ed9e
commit 77407f7a90
12 changed files with 86 additions and 92 deletions

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
floor::components::{CurrentFloor, Floor}, floor::components::{CurrentFloor, Floor},
maze::{components::MazeConfig, events::RespawnMaze, GlobalMazeConfig}, maze::{commands::RespawnMaze, components::MazeConfig, GlobalMazeConfig},
player::events::RespawnPlayer, player::events::RespawnPlayer,
screens::Screen, screens::Screen,
}; };
@ -72,10 +72,11 @@ pub fn maze_controls_ui(world: &mut World) {
// Handle updates // Handle updates
if changed { if changed {
maze_config.update(&global_config); maze_config.update(&global_config);
world.trigger(RespawnMaze { RespawnMaze {
floor: floor_value, floor: floor_value,
config: maze_config, config: maze_config,
}); }
.apply(world);
world.trigger(RespawnPlayer); world.trigger(RespawnPlayer);
} }
} }

View File

@ -6,7 +6,7 @@ use crate::{
events::TransitionFloor, events::TransitionFloor,
resources::HighestFloor, resources::HighestFloor,
}, },
maze::{components::MazeConfig, events::SpawnMaze}, maze::{commands::SpawnMaze, components::MazeConfig},
}; };
pub(super) fn spawn_floor( pub(super) fn spawn_floor(
@ -21,7 +21,7 @@ pub(super) fn spawn_floor(
for event in event_reader.read() { for event in event_reader.read() {
if current_floor.0 == 1 && *event == TransitionFloor::Descend { if current_floor.0 == 1 && *event == TransitionFloor::Descend {
warn!("Cannot descend below floor 1"); info!("Cannot descend below floor 1");
return; return;
} }
@ -30,7 +30,7 @@ pub(super) fn spawn_floor(
info!("Creating level for floor {}", target_floor); info!("Creating level for floor {}", target_floor);
commands.trigger(SpawnMaze { commands.queue(SpawnMaze {
floor: target_floor, floor: target_floor,
config: MazeConfig { config: MazeConfig {
start_pos: config.end_pos, start_pos: config.end_pos,

49
src/maze/commands.rs Normal file
View File

@ -0,0 +1,49 @@
use super::{
components::MazeConfig,
systems::{despawn::despawn_maze, respawn::respawn_maze, spawn::spawn_maze},
};
use bevy::{ecs::system::RunSystemOnce, prelude::*};
#[derive(Debug, Reflect)]
pub struct SpawnMaze {
pub floor: u8,
pub config: MazeConfig,
}
#[derive(Debug, Reflect)]
pub struct RespawnMaze {
pub floor: u8,
pub config: MazeConfig,
}
#[derive(Debug, Reflect)]
pub struct DespawnMaze {
pub floor: u8,
}
impl Default for SpawnMaze {
fn default() -> Self {
Self {
floor: 1,
config: MazeConfig::default(),
}
}
}
impl Command for SpawnMaze {
fn apply(self, world: &mut World) {
let _ = world.run_system_once_with(self, spawn_maze);
}
}
impl Command for RespawnMaze {
fn apply(self, world: &mut World) {
let _ = world.run_system_once_with(self, respawn_maze);
}
}
impl Command for DespawnMaze {
fn apply(self, world: &mut World) {
let _ = world.run_system_once_with(self, despawn_maze);
}
}

View File

@ -1,28 +0,0 @@
use super::components::MazeConfig;
use bevy::prelude::*;
#[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,
}
impl Default for SpawnMaze {
fn default() -> Self {
Self {
floor: 1,
config: MazeConfig::default(),
}
}
}

View File

@ -1,25 +1,19 @@
mod assets; mod assets;
pub mod commands;
pub mod components; pub mod components;
pub mod errors; pub mod errors;
pub mod events;
pub mod resources; pub mod resources;
mod systems; mod systems;
mod triggers;
use bevy::{ecs::system::RunSystemOnce, prelude::*}; use bevy::prelude::*;
use components::HexMaze; use commands::SpawnMaze;
use events::{DespawnMaze, RespawnMaze, SpawnMaze};
pub use resources::GlobalMazeConfig; pub use resources::GlobalMazeConfig;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.init_resource::<GlobalMazeConfig>() app.init_resource::<GlobalMazeConfig>()
.add_event::<SpawnMaze>() .add_plugins(systems::plugin);
.add_event::<RespawnMaze>()
.add_event::<DespawnMaze>()
.register_type::<HexMaze>()
.add_plugins((systems::plugin, triggers::plugin));
} }
pub fn spawn_level_command(world: &mut World) { pub fn spawn_level_command(world: &mut World) {
let _ = world.run_system_once(systems::setup::setup); SpawnMaze::default().apply(world);
} }

View File

@ -2,18 +2,17 @@
//! //!
//! Module handles the cleanup of maze entities when they need to be removed, //! Module handles the cleanup of maze entities when they need to be removed,
//! ensuring proper cleanup of both the maze and all its child entities. //! ensuring proper cleanup of both the maze and all its child entities.
use crate::{floor::components::Floor, maze::events::DespawnMaze}; use crate::{floor::components::Floor, maze::commands::DespawnMaze};
use bevy::prelude::*; use bevy::prelude::*;
/// Despawns a maze and all its associated entities for a given floor. /// Despawns a maze and all its associated entities for a given floor.
pub fn despawn_maze( pub fn despawn_maze(
trigger: Trigger<DespawnMaze>, In(DespawnMaze { floor }): In<DespawnMaze>,
mut commands: Commands, mut commands: Commands,
query: Query<(Entity, &Floor)>, query: Query<(Entity, &Floor)>,
) { ) {
let DespawnMaze { floor } = trigger.event(); match query.iter().find(|(_, f)| f.0 == floor) {
match query.iter().find(|(_, f)| f.0 == *floor) {
Some((entity, _)) => commands.entity(entity).despawn_recursive(), Some((entity, _)) => commands.entity(entity).despawn_recursive(),
_ => warn!("Floor {} not found for removal", floor), _ => warn!("Floor {} not found for removal", floor),
} }

View File

@ -1,4 +1,7 @@
pub mod setup; pub mod common;
pub mod despawn;
pub mod respawn;
pub mod spawn;
use bevy::prelude::*; use bevy::prelude::*;

View File

@ -3,14 +3,15 @@
//! Module provides the ability to regenerate mazes for existing floors, //! Module provides the ability to regenerate mazes for existing floors,
//! maintaining the same floor entity but replacing its internal maze structure. //! maintaining the same floor entity but replacing its internal maze structure.
use super::{common::generate_maze, spawn::spawn_maze_tiles};
use crate::{ use crate::{
floor::components::Floor, floor::components::Floor,
maze::{assets::MazeAssets, errors::MazeError, events::RespawnMaze, GlobalMazeConfig}, maze::{assets::MazeAssets, commands::RespawnMaze, errors::MazeError, GlobalMazeConfig},
}; };
use bevy::prelude::*; use bevy::prelude::*;
use hexlab::Maze; use hexlab::Maze;
use super::{common::generate_maze, spawn::spawn_maze_tiles};
/// Respawns a maze for an existing floor with a new configuration. /// Respawns a maze for an existing floor with a new configuration.
/// ///
/// # Behavior: /// # Behavior:
@ -20,19 +21,17 @@ use hexlab::Maze;
/// - Spawns new maze tiles /// - Spawns new maze tiles
/// - Updates the floor's configuration /// - Updates the floor's configuration
pub fn respawn_maze( pub fn respawn_maze(
trigger: Trigger<RespawnMaze>, In(RespawnMaze { floor, config }): In<RespawnMaze>,
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
mut maze_query: Query<(Entity, &Floor, &mut Maze)>, mut maze_query: Query<(Entity, &Floor, &mut Maze)>,
global_config: Res<GlobalMazeConfig>, global_config: Res<GlobalMazeConfig>,
) { ) {
let RespawnMaze { floor, config } = trigger.event();
let (entity, _, mut maze) = match maze_query let (entity, _, mut maze) = match maze_query
.iter_mut() .iter_mut()
.find(|(_, f, _)| f.0 == *floor) .find(|(_, f, _)| f.0 == floor)
.ok_or(MazeError::FloorNotFound(*floor)) .ok_or(MazeError::FloorNotFound(floor))
{ {
Ok((entity, floor, maze)) => (entity, floor, maze), Ok((entity, floor, maze)) => (entity, floor, maze),
Err(e) => { Err(e) => {
@ -41,7 +40,7 @@ pub fn respawn_maze(
} }
}; };
*maze = match generate_maze(config) { *maze = match generate_maze(&config) {
Ok(generated_maze) => generated_maze, Ok(generated_maze) => generated_maze,
Err(e) => { Err(e) => {
warn!("Failed to update floor ({floor}). {e}"); warn!("Failed to update floor ({floor}). {e}");
@ -56,7 +55,7 @@ pub fn respawn_maze(
entity, entity,
&maze, &maze,
&assets, &assets,
config, &config,
&global_config, &global_config,
); );

View File

@ -1,6 +0,0 @@
use crate::maze::events::SpawnMaze;
use bevy::prelude::*;
pub fn setup(mut commands: Commands) {
commands.trigger(SpawnMaze::default());
}

View File

@ -11,8 +11,8 @@ use crate::{
}, },
maze::{ maze::{
assets::MazeAssets, assets::MazeAssets,
commands::SpawnMaze,
components::{HexMaze, MazeConfig, Tile, Wall}, components::{HexMaze, MazeConfig, Tile, Wall},
events::SpawnMaze,
resources::GlobalMazeConfig, resources::GlobalMazeConfig,
}, },
screens::Screen, screens::Screen,
@ -26,7 +26,7 @@ use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6};
/// Spawns a new maze for the specified floor on [`SpawnMaze`] event. /// Spawns a new maze for the specified floor on [`SpawnMaze`] event.
pub fn spawn_maze( pub fn spawn_maze(
trigger: Trigger<SpawnMaze>, In(SpawnMaze { floor, config }): In<SpawnMaze>,
mut commands: Commands, mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
@ -34,14 +34,12 @@ pub fn spawn_maze(
global_config: Res<GlobalMazeConfig>, global_config: Res<GlobalMazeConfig>,
mut event_writer: EventWriter<TransitionFloor>, mut event_writer: EventWriter<TransitionFloor>,
) { ) {
let SpawnMaze { floor, config } = trigger.event(); if maze_query.iter().any(|(_, f, _)| f.0 == floor) {
info!("Floor {} already exists, skipping creation", floor);
if maze_query.iter().any(|(_, f, _)| f.0 == *floor) {
warn!("Floor {} already exists, skipping creation", floor);
return; return;
} }
let maze = match generate_maze(config) { let maze = match generate_maze(&config) {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
error!("Failed to generate maze for floor {floor}: {:?}", e); error!("Failed to generate maze for floor {floor}: {:?}", e);
@ -50,7 +48,7 @@ pub fn spawn_maze(
}; };
// Calculate vertical offset based on floor number // Calculate vertical offset based on floor number
let y_offset = match *floor { let y_offset = match floor {
1 => 0, // Ground/Initial floor (floor 1) is at y=0 1 => 0, // Ground/Initial floor (floor 1) is at y=0
_ => FLOOR_Y_OFFSET, // Other floors are offset vertically _ => FLOOR_Y_OFFSET, // Other floors are offset vertically
} as f32; } as f32;
@ -60,13 +58,13 @@ pub fn spawn_maze(
Name::new(format!("Floor {}", floor)), Name::new(format!("Floor {}", floor)),
HexMaze, HexMaze,
maze.clone(), maze.clone(),
Floor(*floor), Floor(floor),
config.clone(), config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset)), Transform::from_translation(Vec3::ZERO.with_y(y_offset)),
Visibility::Visible, Visibility::Visible,
StateScoped(Screen::Gameplay), StateScoped(Screen::Gameplay),
)) ))
.insert_if(CurrentFloor, || *floor == 1) // Only floor 1 gets CurrentFloor .insert_if(CurrentFloor, || floor == 1) // Only floor 1 gets CurrentFloor
.id(); .id();
let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config); let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config);
@ -75,12 +73,12 @@ pub fn spawn_maze(
entity, entity,
&maze, &maze,
&assets, &assets,
config, &config,
&global_config, &global_config,
); );
// TODO: find a better way to handle double event indirection // TODO: find a better way to handle double event indirection
if *floor != 1 { if floor != 1 {
event_writer.send(TransitionFloor::Ascend); event_writer.send(TransitionFloor::Ascend);
} }
} }

View File

@ -1,15 +0,0 @@
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);
}