Merge pull request #27 from kristoferssolo/refactor

This commit is contained in:
Kristofers Solo 2025-01-06 12:43:20 +02:00 committed by GitHub
commit a698495c06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 146 additions and 161 deletions

2
Cargo.lock generated
View File

@ -3129,7 +3129,7 @@ dependencies = [
[[package]]
name = "maze-ascension"
version = "1.0.0"
version = "1.0.1"
dependencies = [
"anyhow",
"bevy",

View File

@ -1,7 +1,7 @@
[package]
name = "maze-ascension"
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
version = "1.0.0"
version = "1.0.1"
edition = "2021"
[dependencies]

View File

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

View File

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

View File

@ -9,5 +9,5 @@ pub(super) fn plugin(app: &mut App) {
}
pub fn spawn_hint_command(world: &mut World) {
let _ = world.run_system_once(systems::setup::setup);
let _ = world.run_system_once(systems::spawn::spawn_hints);
}

View File

@ -1,5 +1,5 @@
mod check;
pub mod setup;
pub mod spawn;
use bevy::prelude::*;
use check::check_player_hints;

View File

@ -5,7 +5,7 @@ use crate::hint::{
components::{Hint, IdleTimer},
};
pub fn setup(mut commands: Commands, hint_assets: Res<HintAssets>) {
pub fn spawn_hints(mut commands: Commands, hint_assets: Res<HintAssets>) {
commands.spawn((
Name::new("Movement hint"),
Hint::Movement,

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;
pub mod commands;
pub mod components;
pub mod errors;
pub mod events;
pub mod resources;
mod systems;
mod triggers;
use bevy::{ecs::system::RunSystemOnce, prelude::*};
use components::HexMaze;
use events::{DespawnMaze, RespawnMaze, SpawnMaze};
use bevy::prelude::*;
use commands::SpawnMaze;
pub use resources::GlobalMazeConfig;
pub(super) fn plugin(app: &mut App) {
app.init_resource::<GlobalMazeConfig>()
.add_event::<SpawnMaze>()
.add_event::<RespawnMaze>()
.add_event::<DespawnMaze>()
.register_type::<HexMaze>()
.add_plugins((systems::plugin, triggers::plugin));
.add_plugins(systems::plugin);
}
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,
//! 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::*;
/// Despawns a maze and all its associated entities for a given floor.
pub fn despawn_maze(
trigger: Trigger<DespawnMaze>,
In(DespawnMaze { floor }): In<DespawnMaze>,
mut commands: Commands,
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(),
_ => 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::*;

View File

@ -3,14 +3,15 @@
//! Module provides the ability to regenerate mazes for existing floors,
//! maintaining the same floor entity but replacing its internal maze structure.
use super::{common::generate_maze, spawn::spawn_maze_tiles};
use crate::{
floor::components::Floor,
maze::{assets::MazeAssets, errors::MazeError, events::RespawnMaze, GlobalMazeConfig},
maze::{assets::MazeAssets, commands::RespawnMaze, errors::MazeError, GlobalMazeConfig},
};
use bevy::prelude::*;
use hexlab::Maze;
use super::{common::generate_maze, spawn::spawn_maze_tiles};
/// Respawns a maze for an existing floor with a new configuration.
///
/// # Behavior:
@ -20,19 +21,17 @@ use hexlab::Maze;
/// - Spawns new maze tiles
/// - Updates the floor's configuration
pub fn respawn_maze(
trigger: Trigger<RespawnMaze>,
In(RespawnMaze { floor, config }): In<RespawnMaze>,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut maze_query: Query<(Entity, &Floor, &mut Maze)>,
global_config: Res<GlobalMazeConfig>,
) {
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))
.find(|(_, f, _)| f.0 == floor)
.ok_or(MazeError::FloorNotFound(floor))
{
Ok((entity, floor, maze)) => (entity, floor, maze),
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,
Err(e) => {
warn!("Failed to update floor ({floor}). {e}");
@ -56,7 +55,7 @@ pub fn respawn_maze(
entity,
&maze,
&assets,
config,
&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::{
assets::MazeAssets,
commands::SpawnMaze,
components::{HexMaze, MazeConfig, Tile, Wall},
events::SpawnMaze,
resources::GlobalMazeConfig,
},
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.
pub fn spawn_maze(
trigger: Trigger<SpawnMaze>,
In(SpawnMaze { floor, config }): In<SpawnMaze>,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
@ -34,14 +34,12 @@ pub fn spawn_maze(
global_config: Res<GlobalMazeConfig>,
mut event_writer: EventWriter<TransitionFloor>,
) {
let SpawnMaze { floor, config } = trigger.event();
if maze_query.iter().any(|(_, f, _)| f.0 == *floor) {
warn!("Floor {} already exists, skipping creation", floor);
if maze_query.iter().any(|(_, f, _)| f.0 == floor) {
info!("Floor {} already exists, skipping creation", floor);
return;
}
let maze = match generate_maze(config) {
let maze = match generate_maze(&config) {
Ok(m) => m,
Err(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
let y_offset = match *floor {
let y_offset = match floor {
1 => 0, // Ground/Initial floor (floor 1) is at y=0
_ => FLOOR_Y_OFFSET, // Other floors are offset vertically
} as f32;
@ -60,13 +58,13 @@ pub fn spawn_maze(
Name::new(format!("Floor {}", floor)),
HexMaze,
maze.clone(),
Floor(*floor),
Floor(floor),
config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset)),
Visibility::Visible,
StateScoped(Screen::Gameplay),
))
.insert_if(CurrentFloor, || *floor == 1) // Only floor 1 gets CurrentFloor
.insert_if(CurrentFloor, || floor == 1) // Only floor 1 gets CurrentFloor
.id();
let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config);
@ -75,12 +73,12 @@ pub fn spawn_maze(
entity,
&maze,
&assets,
config,
&config,
&global_config,
);
// TODO: find a better way to handle double event indirection
if *floor != 1 {
if floor != 1 {
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);
}

30
src/player/commands.rs Normal file
View File

@ -0,0 +1,30 @@
use bevy::{ecs::system::RunSystemOnce, prelude::*};
use super::systems::{despawn::despawn_players, respawn::respawn_player, spawn::spawn_player};
#[derive(Debug, Reflect)]
pub struct SpawnPlayer;
#[derive(Debug, Reflect)]
pub struct RespawnPlayer;
#[derive(Debug, Reflect)]
pub struct DespawnPlayer;
impl Command for SpawnPlayer {
fn apply(self, world: &mut World) {
let _ = world.run_system_once(spawn_player);
}
}
impl Command for RespawnPlayer {
fn apply(self, world: &mut World) {
let _ = world.run_system_once(respawn_player);
}
}
impl Command for DespawnPlayer {
fn apply(self, world: &mut World) {
let _ = world.run_system_once(despawn_players);
}
}

View File

@ -1,10 +0,0 @@
use bevy::prelude::*;
#[derive(Debug, Event)]
pub struct SpawnPlayer;
#[derive(Debug, Event)]
pub struct RespawnPlayer;
#[derive(Debug, Event)]
pub struct DespawnPlayer;

View File

@ -1,25 +1,20 @@
mod assets;
pub mod commands;
pub mod components;
pub mod events;
mod systems;
mod triggers;
use assets::PlayerAssets;
use bevy::{ecs::system::RunSystemOnce, prelude::*};
use components::Player;
use events::{DespawnPlayer, RespawnPlayer, SpawnPlayer};
use crate::asset_tracking::LoadResource;
pub(super) fn plugin(app: &mut App) {
app.register_type::<Player>()
.load_resource::<PlayerAssets>()
.add_event::<SpawnPlayer>()
.add_event::<RespawnPlayer>()
.add_event::<DespawnPlayer>()
.add_plugins((triggers::plugin, systems::plugin));
.add_plugins(systems::plugin);
}
pub fn spawn_player_command(world: &mut World) {
let _ = world.run_system_once(systems::setup::setup);
let _ = world.run_system_once(systems::spawn::spawn_player);
}

View File

@ -0,0 +1,8 @@
use crate::player::components::Player;
use bevy::prelude::*;
pub fn despawn_players(mut commands: Commands, query: Query<Entity, With<Player>>) {
for entity in query.iter() {
commands.entity(entity).despawn_recursive();
}
}

View File

@ -1,7 +1,9 @@
pub mod despawn;
mod input;
mod movement;
pub mod setup;
pub mod respawn;
mod sound_effect;
pub mod spawn;
mod vertical_transition;
use crate::{screens::Screen, AppSet};

View File

@ -0,0 +1,7 @@
use crate::player::commands::{DespawnPlayer, SpawnPlayer};
use bevy::prelude::*;
pub fn respawn_player(mut commands: Commands) {
commands.queue(DespawnPlayer);
commands.queue(SpawnPlayer);
}

View File

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

View File

@ -4,14 +4,12 @@ use crate::{
player::{
assets::{blue_material, generate_pill_mesh},
components::{CurrentPosition, Player},
events::SpawnPlayer,
},
screens::Screen,
};
use bevy::prelude::*;
pub(super) fn spawn_player(
_trigger: Trigger<SpawnPlayer>,
pub fn spawn_player(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,

View File

@ -1,12 +0,0 @@
use crate::player::{components::Player, events::DespawnPlayer};
use bevy::prelude::*;
pub(super) fn despawn_players(
_trigger: Trigger<DespawnPlayer>,
mut commands: Commands,
query: Query<Entity, With<Player>>,
) {
for entity in query.iter() {
commands.entity(entity).despawn_recursive();
}
}

View File

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

View File

@ -1,7 +0,0 @@
use crate::player::events::{DespawnPlayer, RespawnPlayer, SpawnPlayer};
use bevy::prelude::*;
pub(super) fn respawn_player(_trigger: Trigger<RespawnPlayer>, mut commands: Commands) {
commands.trigger(DespawnPlayer);
commands.trigger(SpawnPlayer);
}