diff --git a/Cargo.lock b/Cargo.lock index 636cb5a..cbf1630 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2669,11 +2669,13 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hexlab" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b912e78d292803bc279aec3a4e2a0cdd0e0ac1540bcdc5d0f32cbfe9e4d234dc" +checksum = "94f91bb99b0ef58661cc4c18e9bab7bba9e66a3d3047fec9af76e31381647442" dependencies = [ - "bevy", + "bevy_reflect", + "bevy_utils", + "glam", "hexx", "rand", "thiserror 2.0.6", diff --git a/Cargo.toml b/Cargo.toml index 0db5788..63fcbd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ tracing = { version = "0.1", features = [ "release_max_level_warn", ] } hexx = { version = "0.19", features = ["bevy_reflect", "grid"] } -hexlab = { version = "0.3", features = ["bevy"] } +hexlab = { version = "0.4", features = ["bevy_reflect"] } bevy-inspector-egui = { version = "0.28", optional = true } bevy_egui = { version = "0.31", optional = true } thiserror = "2.0" diff --git a/justfile b/justfile index 56e184f..2d1d43f 100644 --- a/justfile +++ b/justfile @@ -17,4 +17,3 @@ web-dev: # Run web release web-release: trunk serve --release --no-default-features - diff --git a/src/floor/events.rs b/src/floor/events.rs index 38e2988..cae8f22 100644 --- a/src/floor/events.rs +++ b/src/floor/events.rs @@ -1,27 +1,45 @@ use bevy::prelude::*; -use crate::maze::components::MazeConfig; +use super::components::Floor; -#[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, -} - -#[derive(Debug, Reflect, Event, Default)] +#[derive(Debug, Clone, Copy, Reflect, Event, Default, PartialEq, Eq)] pub enum TransitionFloor { #[default] Ascend, Descend, } + +impl TransitionFloor { + pub fn into_direction(&self) -> f32 { + self.into() + } + + pub fn opposite(&self) -> Self { + match self { + Self::Ascend => Self::Descend, + Self::Descend => Self::Ascend, + } + } + + pub fn next_floor_num(&self, floor: &Floor) -> u8 { + match self { + Self::Ascend => *floor.increased(), + Self::Descend => *floor.decreased(), + } + } +} + +impl From for f32 { + fn from(value: TransitionFloor) -> Self { + f32::from(&value) + } +} + +impl From<&TransitionFloor> for f32 { + fn from(value: &TransitionFloor) -> Self { + match value { + TransitionFloor::Ascend => -1., + TransitionFloor::Descend => 1., + } + } +} diff --git a/src/floor/mod.rs b/src/floor/mod.rs index 23e4944..7cb7527 100644 --- a/src/floor/mod.rs +++ b/src/floor/mod.rs @@ -1,11 +1,14 @@ pub mod components; pub mod events; +pub mod resources; mod systems; use bevy::prelude::*; use events::TransitionFloor; +use resources::HighestFloor; pub(super) fn plugin(app: &mut App) { app.add_event::() + .insert_resource(HighestFloor(1)) .add_plugins(systems::plugin); } diff --git a/src/floor/resources.rs b/src/floor/resources.rs new file mode 100644 index 0000000..80e447c --- /dev/null +++ b/src/floor/resources.rs @@ -0,0 +1,5 @@ +use bevy::prelude::*; + +#[derive(Debug, Default, Reflect, Resource, PartialEq, Eq)] +#[reflect(Resource)] +pub struct HighestFloor(pub u8); diff --git a/src/floor/systems/clear_events.rs b/src/floor/systems/clear_events.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/floor/systems/mod.rs b/src/floor/systems/mod.rs index 43c50b2..7bdf6e1 100644 --- a/src/floor/systems/mod.rs +++ b/src/floor/systems/mod.rs @@ -1,3 +1,4 @@ +mod clear_events; mod despawn; mod movement; mod spawn; diff --git a/src/floor/systems/movement.rs b/src/floor/systems/movement.rs index 795e985..1b57a67 100644 --- a/src/floor/systems/movement.rs +++ b/src/floor/systems/movement.rs @@ -39,11 +39,16 @@ pub(super) fn handle_floor_transition_events( next_query: Query>, mut event_reader: EventReader, ) { + let is_moving = maze_query + .iter() + .any(|(_, _, movement_state)| movement_state.is_some()); + + if is_moving { + return; + } + for event in event_reader.read() { - let direction = match event { - TransitionFloor::Ascend => -1., - TransitionFloor::Descend => 1., - }; + let direction = event.into(); let Some((current_entity, current_y)) = get_floor_info(&maze_query, ¤t_query) else { continue; @@ -64,6 +69,7 @@ pub(super) fn handle_floor_transition_events( } update_current_next_floor(&mut commands, current_entity, next_entity); + break; } } diff --git a/src/floor/systems/spawn.rs b/src/floor/systems/spawn.rs index 083f61b..03d2721 100644 --- a/src/floor/systems/spawn.rs +++ b/src/floor/systems/spawn.rs @@ -1,7 +1,8 @@ use crate::{ floor::{ - components::{CurrentFloor, Floor}, + components::{CurrentFloor, Floor, MovementState}, events::TransitionFloor, + resources::HighestFloor, }, maze::events::SpawnMaze, }; @@ -10,23 +11,31 @@ use bevy::prelude::*; pub(super) fn spawn_floor( mut commands: Commands, query: Query<&mut Floor, With>, + movement_state_query: Query>, mut event_reader: EventReader, + mut highest_floor: ResMut, ) { let Ok(floor) = query.get_single() else { return; }; - for event in event_reader.read() { - let floor = match event { - TransitionFloor::Ascend => *floor.increased(), - TransitionFloor::Descend => *floor.decreased(), - }; + let is_moving = movement_state_query + .iter() + .any(|movement_state| movement_state.is_some()); + if is_moving { + return; + } - if floor == 1 { + for event in event_reader.read() { + let floor = event.next_floor_num(floor); + + if floor == 1 && *event == TransitionFloor::Descend { warn!("Cannot descend below floor 1"); return; } + highest_floor.0 = highest_floor.0.max(floor); + info!("Creating level for floor {}", floor); commands.trigger(SpawnMaze { floor, ..default() }); diff --git a/src/maze/components.rs b/src/maze/components.rs index fccb97b..6dbb5d1 100644 --- a/src/maze/components.rs +++ b/src/maze/components.rs @@ -1,6 +1,6 @@ use crate::floor::components::Floor; -use super::{errors::MazeConfigError, GlobalMazeConfig}; +use super::GlobalMazeConfig; use bevy::prelude::*; use hexlab::HexMaze; use hexx::{Hex, HexLayout, HexOrientation}; @@ -22,7 +22,7 @@ pub struct Wall; #[derive(Debug, Reflect, Component, Clone)] #[reflect(Component)] pub struct MazeConfig { - pub radius: u32, + pub radius: u16, pub start_pos: Hex, pub end_pos: Hex, pub seed: u64, @@ -31,19 +31,21 @@ pub struct MazeConfig { impl MazeConfig { fn new( - radius: u32, + radius: u16, orientation: HexOrientation, seed: Option, global_conig: &GlobalMazeConfig, - ) -> Result { + ) -> Self { let seed = seed.unwrap_or_else(|| thread_rng().gen()); let mut rng = StdRng::seed_from_u64(seed); - let start_pos = generate_pos(radius, &mut rng)?; - let end_pos = generate_pos(radius, &mut rng)?; + let start_pos = generate_pos(radius, &mut rng); + let end_pos = generate_pos(radius, &mut rng); - info!("Start pos: (q={}, r={})", start_pos.x, start_pos.y); - info!("End pos: (q={}, r={})", end_pos.x, end_pos.y); + info!( + "Start pos: (q={}, r={}). End pos: (q={}, r={})", + start_pos.x, start_pos.y, end_pos.x, end_pos.y + ); let layout = HexLayout { orientation, @@ -51,23 +53,13 @@ impl MazeConfig { ..default() }; - Ok(Self { + Self { radius, start_pos, end_pos, seed, layout, - }) - } - - pub fn new_unchecked( - radius: u32, - orientation: HexOrientation, - seed: Option, - global_conig: &GlobalMazeConfig, - ) -> Self { - Self::new(radius, orientation, seed, global_conig) - .expect("Failed to create MazeConfig with supposedly safe values") + } } pub fn update(&mut self, global_conig: &GlobalMazeConfig) { @@ -77,14 +69,14 @@ impl MazeConfig { impl Default for MazeConfig { fn default() -> Self { - Self::new_unchecked(7, HexOrientation::Flat, None, &GlobalMazeConfig::default()) + Self::new(8, HexOrientation::Flat, None, &GlobalMazeConfig::default()) } } -fn generate_pos(radius: u32, rng: &mut R) -> Result { - let radius = i32::try_from(radius)?; - Ok(Hex::new( +fn generate_pos(radius: u16, rng: &mut R) -> Hex { + let radius = radius as i32; + Hex::new( rng.gen_range(-radius..radius), rng.gen_range(-radius..radius), - )) + ) } diff --git a/src/maze/errors.rs b/src/maze/errors.rs index 87e36a6..f287bdc 100644 --- a/src/maze/errors.rs +++ b/src/maze/errors.rs @@ -14,7 +14,7 @@ pub enum MazeError { #[error("Floor {0} not found")] FloorNotFound(u8), #[error("Failed to generate maze with config: {radius}, seed: {seed}")] - GenerationFailed { radius: u32, seed: u64 }, + GenerationFailed { radius: u16, seed: u64 }, #[error("Invalid tile entity: {0:?}")] TileNotFound(bevy::prelude::Entity), #[error("Failed to create maze assets")] @@ -32,7 +32,7 @@ impl MazeError { Self::ConfigurationError(msg.into()) } - pub const fn generation_failed(radius: u32, seed: u64) -> Self { + pub const fn generation_failed(radius: u16, seed: u64) -> Self { Self::GenerationFailed { radius, seed } } } diff --git a/src/maze/triggers/spawn.rs b/src/maze/triggers/spawn.rs index 493fefe..235025f 100644 --- a/src/maze/triggers/spawn.rs +++ b/src/maze/triggers/spawn.rs @@ -38,7 +38,12 @@ pub(super) fn spawn_maze( } }; - let y_offset = (floor - 1) * FLOOR_Y_OFFSET; + let y_offset = match *floor { + 1 => 0, + _ => FLOOR_Y_OFFSET, + } as f32; + + // (floor - 1) * FLOOR_Y_OFFSET let entity = commands .spawn(( @@ -46,7 +51,7 @@ pub(super) fn spawn_maze( Maze(maze.clone()), Floor(*floor), config.clone(), - Transform::from_translation(Vec3::ZERO.with_y(y_offset as f32)), + Transform::from_translation(Vec3::ZERO.with_y(y_offset)), Visibility::Visible, )) .insert_if(CurrentFloor, || *floor == 1) diff --git a/src/player/systems/ascend.rs b/src/player/systems/ascend.rs index c46bf8c..fd95198 100644 --- a/src/player/systems/ascend.rs +++ b/src/player/systems/ascend.rs @@ -7,16 +7,17 @@ use bevy::prelude::*; pub(super) fn ascend_player( query: Query<&CurrentPosition, With>, - maze_config_query: Query<&MazeConfig, With>, + maze_config: Query<&MazeConfig, With>, mut event_writer: EventWriter, ) { - let Ok(config) = maze_config_query.get_single() else { + let Ok(config) = maze_config.get_single() else { warn!("Failed to get maze configuration for current floor - cannot ascend player"); return; }; for current_hex in query.iter() { if current_hex.0 == config.end_pos { + dbg!("Ascend"); event_writer.send(TransitionFloor::Ascend); return; } diff --git a/src/player/systems/descend.rs b/src/player/systems/descend.rs index f885263..be8b4f5 100644 --- a/src/player/systems/descend.rs +++ b/src/player/systems/descend.rs @@ -11,10 +11,10 @@ use crate::{ pub(super) fn descend_player( query: Query<&CurrentPosition, With>, - maze_config_query: Query<(&MazeConfig, &Floor), With>, + maze_config: Query<(&MazeConfig, &Floor), With>, mut event_writer: EventWriter, ) { - let Ok((config, floor)) = maze_config_query.get_single() else { + let Ok((config, floor)) = maze_config.get_single() else { warn!("Failed to get maze configuration for current floor - cannot descend player"); return; }; diff --git a/src/theme/palette/mod.rs b/src/theme/palette/mod.rs index 960cca2..8a3de0a 100644 --- a/src/theme/palette/mod.rs +++ b/src/theme/palette/mod.rs @@ -11,10 +11,12 @@ pub const HEADER_TEXT: Color = Color::srgb(0.867, 0.827, 0.412); pub const NODE_BACKGROUND: Color = Color::srgb(0.286, 0.478, 0.773); +const MAX_COLOR_VALUE: f32 = 255.; + pub(super) const fn rgb_u8(red: u8, green: u8, blue: u8) -> Color { Color::srgb(scale(red), scale(green), scale(blue)) } const fn scale(value: u8) -> f32 { - value as f32 / 255. + value as f32 / MAX_COLOR_VALUE }