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]]
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",

View File

@ -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"

View File

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

View File

@ -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<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 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::<TransitionFloor>()
.insert_resource(HighestFloor(1))
.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 movement;
mod spawn;

View File

@ -39,11 +39,16 @@ pub(super) fn handle_floor_transition_events(
next_query: Query<Entity, With<NextFloor>>,
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() {
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, &current_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;
}
}

View File

@ -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<CurrentFloor>>,
movement_state_query: Query<Option<&MovementState>>,
mut event_reader: EventReader<TransitionFloor>,
mut highest_floor: ResMut<HighestFloor>,
) {
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() });

View File

@ -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<u64>,
global_conig: &GlobalMazeConfig,
) -> Result<Self, MazeConfigError> {
) -> 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<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) {
@ -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<R: Rng>(radius: u32, rng: &mut R) -> Result<Hex, MazeConfigError> {
let radius = i32::try_from(radius)?;
Ok(Hex::new(
fn generate_pos<R: Rng>(radius: u16, rng: &mut R) -> Hex {
let radius = radius as i32;
Hex::new(
rng.gen_range(-radius..radius),
rng.gen_range(-radius..radius),
))
)
}

View File

@ -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 }
}
}

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
.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)

View File

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

View File

@ -11,10 +11,10 @@ use crate::{
pub(super) fn descend_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>,
) {
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;
};

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);
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
}