feat(floor): add NextFloor component

This commit is contained in:
Kristofers Solo 2024-12-22 18:25:40 +02:00
parent f8ea1edd87
commit 6c07bc0670
10 changed files with 136 additions and 26 deletions

View File

@ -8,6 +8,10 @@ pub struct Floor(pub u8);
#[reflect(Component)] #[reflect(Component)]
pub struct CurrentFloor; pub struct CurrentFloor;
#[derive(Debug, Reflect, Component)]
#[reflect(Component)]
pub struct NextFloor;
impl Default for Floor { impl Default for Floor {
fn default() -> Self { fn default() -> Self {
Self(1) Self(1)
@ -15,11 +19,11 @@ impl Default for Floor {
} }
impl Floor { impl Floor {
pub fn increase(&self) -> Self { pub fn increased(&self) -> Self {
Self(self.0.saturating_add(1)) Self(self.0.saturating_add(1))
} }
pub fn decrease(&self) -> Self { pub fn decreased(&self) -> Self {
Self(self.0.saturating_sub(1).max(1)) Self(self.0.saturating_sub(1).max(1))
} }
} }
@ -36,7 +40,7 @@ mod tests {
#[case(255, 255)] #[case(255, 255)]
fn increase(#[case] input: u8, #[case] expected: u8) { fn increase(#[case] input: u8, #[case] expected: u8) {
let floor = Floor(input); let floor = Floor(input);
assert_eq!(*floor.increase(), expected); assert_eq!(*floor.increased(), expected);
} }
#[rstest] #[rstest]
@ -46,6 +50,6 @@ mod tests {
#[case(255, 254)] #[case(255, 254)]
fn decrease(#[case] input: u8, #[case] expected: u8) { fn decrease(#[case] input: u8, #[case] expected: u8) {
let floor = Floor(input); let floor = Floor(input);
assert_eq!(*floor.decrease(), expected); assert_eq!(*floor.decreased(), expected);
} }
} }

View File

@ -1,3 +1,3 @@
use bevy::prelude::*; use bevy::prelude::*;
pub(super) fn despawn_level(mut commands: Commands) {} pub(super) fn despawn_floor(mut commands: Commands) {}

View File

@ -1,14 +1,18 @@
mod despawn; mod despawn;
mod movement;
mod spawn; mod spawn;
use crate::maze::MazePluginLoaded; use crate::maze::MazePluginLoaded;
use bevy::prelude::*; use bevy::prelude::*;
use despawn::despawn_level; use despawn::despawn_floor;
use movement::floor_movement;
use spawn::spawn_floor; use spawn::spawn_floor;
pub(super) fn plugin(app: &mut App) { pub(super) fn plugin(app: &mut App) {
app.add_systems( app.add_systems(
Update, Update,
(spawn_floor, despawn_level).run_if(resource_exists::<MazePluginLoaded>), (spawn_floor, despawn_floor, floor_movement)
.chain()
.run_if(resource_exists::<MazePluginLoaded>),
); );
} }

View File

@ -0,0 +1,59 @@
use bevy::prelude::*;
use crate::{
floor::{
components::{CurrentFloor, NextFloor},
events::TransitionFloor,
},
maze::{components::Maze, GlobalMazeConfig},
player::components::{MovementSpeed, Player},
};
pub(super) fn floor_movement(
mut commands: Commands,
mut maze_transforms: Query<(Entity, &mut Transform), With<Maze>>,
current_floor: Query<Entity, With<CurrentFloor>>,
next_floor: Query<Entity, With<NextFloor>>,
player_query: Query<&MovementSpeed, With<Player>>,
time: Res<Time>,
global_config: Res<GlobalMazeConfig>,
mut event_reader: EventReader<TransitionFloor>,
) {
let speed = player_query.get_single().map(|s| s.0).unwrap_or(100.);
let movement_distance = speed * time.delta_secs();
for event in event_reader.read() {
let (direction, target_y) = match event {
TransitionFloor::Ascend => (Vec3::Y, -global_config.height),
TransitionFloor::Descent => (Vec3::NEG_Y, global_config.height),
};
let movement = direction * movement_distance;
for (_, mut transform) in maze_transforms.iter_mut() {
transform.translation += movement;
}
let is_movement_complete = maze_transforms
.iter()
.any(|(_, t)| t.translation.y.abs() >= target_y.abs());
if is_movement_complete {
if let Ok(current_floor_entity) = current_floor.get_single() {
commands
.entity(current_floor_entity)
.remove::<CurrentFloor>();
}
if let Ok(next_floor_entity) = next_floor.get_single() {
if let Ok((entity, mut transform)) = maze_transforms.get_mut(next_floor_entity) {
transform.translation.y = 0.;
commands
.entity(entity)
.remove::<NextFloor>()
.insert(CurrentFloor);
}
}
}
}
}

View File

@ -1,5 +1,3 @@
use bevy::prelude::*;
use crate::{ use crate::{
floor::{ floor::{
components::{CurrentFloor, Floor}, components::{CurrentFloor, Floor},
@ -7,30 +5,30 @@ use crate::{
}, },
maze::events::SpawnMaze, maze::events::SpawnMaze,
}; };
use bevy::prelude::*;
pub(super) fn spawn_floor( pub(super) fn spawn_floor(
mut commands: Commands, mut commands: Commands,
query: Query<(Entity, &Floor), With<CurrentFloor>>, query: Query<&mut Floor, With<CurrentFloor>>,
mut event_reader: EventReader<TransitionFloor>, mut event_reader: EventReader<TransitionFloor>,
) { ) {
let Ok((entity, floor)) = query.get_single() else { let Ok(floor) = query.get_single() else {
return; return;
}; };
for event in event_reader.read() { for event in event_reader.read() {
dbg!(event);
let floor = match event { let floor = match event {
TransitionFloor::Ascend => *floor.increase(), TransitionFloor::Ascend => *floor.increased(),
TransitionFloor::Descent => *floor.decrease(), TransitionFloor::Descent => *floor.decreased(),
}; };
if floor == 1 { if floor == 1 {
warn!("Cannot descend below floor 1");
return; return;
} }
info!("Creating level for floor {}", floor); info!("Creating level for floor {}", floor);
commands.entity(entity).remove::<CurrentFloor>();
commands.trigger(SpawnMaze { floor, ..default() }); commands.trigger(SpawnMaze { floor, ..default() });
} }
} }

View File

@ -1,6 +1,53 @@
use crate::maze::events::SpawnMaze; use crate::{
floor::components::{CurrentFloor, Floor},
maze::{
assets::MazeAssets,
components::Maze,
events::SpawnMaze,
triggers::{
common::generate_maze,
spawn::{spawn_maze_tiles, FLOOR_Y_OFFSET},
},
GlobalMazeConfig,
},
};
use bevy::prelude::*; use bevy::prelude::*;
pub(crate) fn setup(mut commands: Commands) { pub(crate) fn setup(
commands.trigger(SpawnMaze::default()); mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
maze_query: Query<(Entity, &Floor, &Maze)>,
global_config: Res<GlobalMazeConfig>,
) {
let SpawnMaze { floor, config } = SpawnMaze::default();
if maze_query.iter().any(|(_, f, _)| f.0 == floor) {
warn!("Floor {} already exists, skipping creation", floor);
return;
}
let maze = generate_maze(&config).expect("Failed to generate maze during spawn");
let y_offset = (floor - 1) * FLOOR_Y_OFFSET;
let entity = commands
.spawn((
Name::new(format!("Floor {}", floor)),
Maze(maze.clone()),
Floor(floor),
CurrentFloor,
config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset as f32)),
Visibility::Visible,
))
.id();
let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config);
spawn_maze_tiles(
&mut commands,
entity,
&maze,
&assets,
&config,
&global_config,
);
} }

View File

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

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
floor::components::{CurrentFloor, Floor}, floor::components::{CurrentFloor, Floor, NextFloor},
maze::{ maze::{
assets::MazeAssets, assets::MazeAssets,
components::{Maze, MazeConfig, Tile, Wall}, components::{Maze, MazeConfig, Tile, Wall},
@ -14,7 +14,7 @@ use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6};
use super::common::generate_maze; use super::common::generate_maze;
const FLOOR_Y_OFFSET: u8 = 100; pub(crate) const FLOOR_Y_OFFSET: u8 = 100;
pub(super) fn spawn_maze( pub(super) fn spawn_maze(
trigger: Trigger<SpawnMaze>, trigger: Trigger<SpawnMaze>,
@ -38,7 +38,7 @@ pub(super) fn spawn_maze(
Name::new(format!("Floor {}", floor)), Name::new(format!("Floor {}", floor)),
Maze(maze.clone()), Maze(maze.clone()),
Floor(*floor), Floor(*floor),
CurrentFloor, // TODO: remove NextFloor,
config.clone(), config.clone(),
Transform::from_translation(Vec3::ZERO.with_y(y_offset as f32)), Transform::from_translation(Vec3::ZERO.with_y(y_offset as f32)),
Visibility::Visible, Visibility::Visible,
@ -56,7 +56,7 @@ pub(super) fn spawn_maze(
); );
} }
pub(super) fn spawn_maze_tiles( pub(crate) fn spawn_maze_tiles(
commands: &mut Commands, commands: &mut Commands,
parent_entity: Entity, parent_entity: Entity,
maze: &HexMaze, maze: &HexMaze,

View File

@ -1,10 +1,9 @@
use bevy::prelude::*;
use crate::{ use crate::{
floor::{components::CurrentFloor, events::TransitionFloor}, floor::{components::CurrentFloor, events::TransitionFloor},
maze::components::MazeConfig, maze::components::MazeConfig,
player::components::{CurrentPosition, Player}, player::components::{CurrentPosition, Player},
}; };
use bevy::prelude::*;
pub(super) fn ascend_player( pub(super) fn ascend_player(
query: Query<&CurrentPosition, With<Player>>, query: Query<&CurrentPosition, With<Player>>,

View File

@ -3,7 +3,6 @@ mod descend;
mod input; mod input;
mod movement; mod movement;
pub mod setup; pub mod setup;
mod transition_floor;
use crate::maze::MazePluginLoaded; use crate::maze::MazePluginLoaded;
use ascend::ascend_player; use ascend::ascend_player;