fix(floor): multiple floor create

This commit is contained in:
Kristofers Solo 2024-12-27 11:49:22 +02:00
parent 433a3ce5e8
commit 4635b0f134
16 changed files with 112 additions and 69 deletions

8
Cargo.lock generated
View File

@ -2669,11 +2669,13 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]] [[package]]
name = "hexlab" name = "hexlab"
version = "0.3.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b912e78d292803bc279aec3a4e2a0cdd0e0ac1540bcdc5d0f32cbfe9e4d234dc" checksum = "94f91bb99b0ef58661cc4c18e9bab7bba9e66a3d3047fec9af76e31381647442"
dependencies = [ dependencies = [
"bevy", "bevy_reflect",
"bevy_utils",
"glam",
"hexx", "hexx",
"rand", "rand",
"thiserror 2.0.6", "thiserror 2.0.6",

View File

@ -18,7 +18,7 @@ tracing = { version = "0.1", features = [
"release_max_level_warn", "release_max_level_warn",
] } ] }
hexx = { version = "0.19", features = ["bevy_reflect", "grid"] } 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-inspector-egui = { version = "0.28", optional = true }
bevy_egui = { version = "0.31", optional = true } bevy_egui = { version = "0.31", optional = true }
thiserror = "2.0" thiserror = "2.0"

View File

@ -17,4 +17,3 @@ web-dev:
# Run web release # Run web release
web-release: web-release:
trunk serve --release --no-default-features trunk serve --release --no-default-features

View File

@ -1,27 +1,45 @@
use bevy::prelude::*; use bevy::prelude::*;
use crate::maze::components::MazeConfig; use super::components::Floor;
#[derive(Debug, Reflect, Event)] #[derive(Debug, Clone, Copy, Reflect, Event, Default, PartialEq, Eq)]
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)]
pub enum TransitionFloor { pub enum TransitionFloor {
#[default] #[default]
Ascend, Ascend,
Descend, 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<TransitionFloor> 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.,
}
}
}

View File

@ -1,11 +1,14 @@
pub mod components; pub mod components;
pub mod events; pub mod events;
pub mod resources;
mod systems; mod systems;
use bevy::prelude::*; use bevy::prelude::*;
use events::TransitionFloor; use events::TransitionFloor;
use resources::HighestFloor;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.add_event::<TransitionFloor>() app.add_event::<TransitionFloor>()
.insert_resource(HighestFloor(1))
.add_plugins(systems::plugin); .add_plugins(systems::plugin);
} }

5
src/floor/resources.rs Normal file
View File

@ -0,0 +1,5 @@
use bevy::prelude::*;
#[derive(Debug, Default, Reflect, Resource, PartialEq, Eq)]
#[reflect(Resource)]
pub struct HighestFloor(pub u8);

View File

View File

@ -1,3 +1,4 @@
mod clear_events;
mod despawn; mod despawn;
mod movement; mod movement;
mod spawn; mod spawn;

View File

@ -39,11 +39,16 @@ pub(super) fn handle_floor_transition_events(
next_query: Query<Entity, With<NextFloor>>, next_query: Query<Entity, With<NextFloor>>,
mut event_reader: EventReader<TransitionFloor>, mut event_reader: EventReader<TransitionFloor>,
) { ) {
let is_moving = maze_query
.iter()
.any(|(_, _, movement_state)| movement_state.is_some());
if is_moving {
return;
}
for event in event_reader.read() { for event in event_reader.read() {
let direction = match event { let direction = event.into();
TransitionFloor::Ascend => -1.,
TransitionFloor::Descend => 1.,
};
let Some((current_entity, current_y)) = get_floor_info(&maze_query, &current_query) else { let Some((current_entity, current_y)) = get_floor_info(&maze_query, &current_query) else {
continue; continue;
@ -64,6 +69,7 @@ pub(super) fn handle_floor_transition_events(
} }
update_current_next_floor(&mut commands, current_entity, next_entity); update_current_next_floor(&mut commands, current_entity, next_entity);
break;
} }
} }

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
floor::{ floor::{
components::{CurrentFloor, Floor}, components::{CurrentFloor, Floor, MovementState},
events::TransitionFloor, events::TransitionFloor,
resources::HighestFloor,
}, },
maze::events::SpawnMaze, maze::events::SpawnMaze,
}; };
@ -10,23 +11,31 @@ use bevy::prelude::*;
pub(super) fn spawn_floor( pub(super) fn spawn_floor(
mut commands: Commands, mut commands: Commands,
query: Query<&mut Floor, With<CurrentFloor>>, query: Query<&mut Floor, With<CurrentFloor>>,
movement_state_query: Query<Option<&MovementState>>,
mut event_reader: EventReader<TransitionFloor>, mut event_reader: EventReader<TransitionFloor>,
mut highest_floor: ResMut<HighestFloor>,
) { ) {
let Ok(floor) = query.get_single() else { let Ok(floor) = query.get_single() else {
return; return;
}; };
for event in event_reader.read() { let is_moving = movement_state_query
let floor = match event { .iter()
TransitionFloor::Ascend => *floor.increased(), .any(|movement_state| movement_state.is_some());
TransitionFloor::Descend => *floor.decreased(), 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"); warn!("Cannot descend below floor 1");
return; return;
} }
highest_floor.0 = highest_floor.0.max(floor);
info!("Creating level for floor {}", floor); info!("Creating level for floor {}", floor);
commands.trigger(SpawnMaze { floor, ..default() }); commands.trigger(SpawnMaze { floor, ..default() });

View File

@ -1,6 +1,6 @@
use crate::floor::components::Floor; use crate::floor::components::Floor;
use super::{errors::MazeConfigError, GlobalMazeConfig}; use super::GlobalMazeConfig;
use bevy::prelude::*; use bevy::prelude::*;
use hexlab::HexMaze; use hexlab::HexMaze;
use hexx::{Hex, HexLayout, HexOrientation}; use hexx::{Hex, HexLayout, HexOrientation};
@ -22,7 +22,7 @@ pub struct Wall;
#[derive(Debug, Reflect, Component, Clone)] #[derive(Debug, Reflect, Component, Clone)]
#[reflect(Component)] #[reflect(Component)]
pub struct MazeConfig { pub struct MazeConfig {
pub radius: u32, pub radius: u16,
pub start_pos: Hex, pub start_pos: Hex,
pub end_pos: Hex, pub end_pos: Hex,
pub seed: u64, pub seed: u64,
@ -31,19 +31,21 @@ pub struct MazeConfig {
impl MazeConfig { impl MazeConfig {
fn new( fn new(
radius: u32, radius: u16,
orientation: HexOrientation, orientation: HexOrientation,
seed: Option<u64>, seed: Option<u64>,
global_conig: &GlobalMazeConfig, global_conig: &GlobalMazeConfig,
) -> Result<Self, MazeConfigError> { ) -> Self {
let seed = seed.unwrap_or_else(|| thread_rng().gen()); let seed = seed.unwrap_or_else(|| thread_rng().gen());
let mut rng = StdRng::seed_from_u64(seed); let mut rng = StdRng::seed_from_u64(seed);
let start_pos = generate_pos(radius, &mut rng)?; let start_pos = generate_pos(radius, &mut rng);
let end_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!(
info!("End pos: (q={}, r={})", end_pos.x, end_pos.y); "Start pos: (q={}, r={}). End pos: (q={}, r={})",
start_pos.x, start_pos.y, end_pos.x, end_pos.y
);
let layout = HexLayout { let layout = HexLayout {
orientation, orientation,
@ -51,23 +53,13 @@ impl MazeConfig {
..default() ..default()
}; };
Ok(Self { Self {
radius, radius,
start_pos, start_pos,
end_pos, end_pos,
seed, seed,
layout, layout,
}) }
}
pub fn new_unchecked(
radius: u32,
orientation: HexOrientation,
seed: Option<u64>,
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) { pub fn update(&mut self, global_conig: &GlobalMazeConfig) {
@ -77,14 +69,14 @@ impl MazeConfig {
impl Default for MazeConfig { impl Default for MazeConfig {
fn default() -> Self { fn default() -> Self {
Self::new_unchecked(7, HexOrientation::Flat, None, &GlobalMazeConfig::default()) Self::new(8, HexOrientation::Flat, None, &GlobalMazeConfig::default())
} }
} }
fn generate_pos<R: Rng>(radius: u32, rng: &mut R) -> Result<Hex, MazeConfigError> { fn generate_pos<R: Rng>(radius: u16, rng: &mut R) -> Hex {
let radius = i32::try_from(radius)?; let radius = radius as i32;
Ok(Hex::new( Hex::new(
rng.gen_range(-radius..radius), rng.gen_range(-radius..radius),
rng.gen_range(-radius..radius), rng.gen_range(-radius..radius),
)) )
} }

View File

@ -14,7 +14,7 @@ pub enum MazeError {
#[error("Floor {0} not found")] #[error("Floor {0} not found")]
FloorNotFound(u8), FloorNotFound(u8),
#[error("Failed to generate maze with config: {radius}, seed: {seed}")] #[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:?}")] #[error("Invalid tile entity: {0:?}")]
TileNotFound(bevy::prelude::Entity), TileNotFound(bevy::prelude::Entity),
#[error("Failed to create maze assets")] #[error("Failed to create maze assets")]
@ -32,7 +32,7 @@ impl MazeError {
Self::ConfigurationError(msg.into()) 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 } Self::GenerationFailed { radius, seed }
} }
} }

View File

@ -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 let entity = commands
.spawn(( .spawn((
@ -46,7 +51,7 @@ pub(super) fn spawn_maze(
Maze(maze.clone()), Maze(maze.clone()),
Floor(*floor), Floor(*floor),
config.clone(), config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset as f32)), Transform::from_translation(Vec3::ZERO.with_y(y_offset)),
Visibility::Visible, Visibility::Visible,
)) ))
.insert_if(CurrentFloor, || *floor == 1) .insert_if(CurrentFloor, || *floor == 1)

View File

@ -7,16 +7,17 @@ use bevy::prelude::*;
pub(super) fn ascend_player( pub(super) fn ascend_player(
query: Query<&CurrentPosition, With<Player>>, query: Query<&CurrentPosition, With<Player>>,
maze_config_query: Query<&MazeConfig, With<CurrentFloor>>, maze_config: Query<&MazeConfig, With<CurrentFloor>>,
mut event_writer: EventWriter<TransitionFloor>, mut event_writer: EventWriter<TransitionFloor>,
) { ) {
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"); warn!("Failed to get maze configuration for current floor - cannot ascend player");
return; return;
}; };
for current_hex in query.iter() { for current_hex in query.iter() {
if current_hex.0 == config.end_pos { if current_hex.0 == config.end_pos {
dbg!("Ascend");
event_writer.send(TransitionFloor::Ascend); event_writer.send(TransitionFloor::Ascend);
return; return;
} }

View File

@ -11,10 +11,10 @@ use crate::{
pub(super) fn descend_player( pub(super) fn descend_player(
query: Query<&CurrentPosition, With<Player>>, query: Query<&CurrentPosition, With<Player>>,
maze_config_query: Query<(&MazeConfig, &Floor), With<CurrentFloor>>, maze_config: Query<(&MazeConfig, &Floor), With<CurrentFloor>>,
mut event_writer: EventWriter<TransitionFloor>, mut event_writer: EventWriter<TransitionFloor>,
) { ) {
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"); warn!("Failed to get maze configuration for current floor - cannot descend player");
return; return;
}; };

View File

@ -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); 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 { pub(super) const fn rgb_u8(red: u8, green: u8, blue: u8) -> Color {
Color::srgb(scale(red), scale(green), scale(blue)) Color::srgb(scale(red), scale(green), scale(blue))
} }
const fn scale(value: u8) -> f32 { const fn scale(value: u8) -> f32 {
value as f32 / 255. value as f32 / MAX_COLOR_VALUE
} }