mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
feat(floor): add NextFloor component
This commit is contained in:
parent
f8ea1edd87
commit
6c07bc0670
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {}
|
||||||
|
|||||||
@ -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>),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
59
src/floor/systems/movement.rs
Normal file
59
src/floor/systems/movement.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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>>,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user