mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
Merge pull request #35 from kristoferssolo/feture/score
This commit is contained in:
commit
d01e987b89
58
Cargo.lock
generated
58
Cargo.lock
generated
@ -1409,7 +1409,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
@ -1813,7 +1813,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rangemap",
|
"rangemap",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"rustybuzz",
|
"rustybuzz",
|
||||||
"self_cell",
|
"self_cell",
|
||||||
"swash",
|
"swash",
|
||||||
@ -1925,6 +1925,18 @@ version = "2.6.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deprecate-until"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a3767f826efbbe5a5ae093920b58b43b01734202be697e1354914e862e8e704"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"semver",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -2644,15 +2656,16 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hexlab"
|
name = "hexlab"
|
||||||
version = "0.5.3"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7d2fbc6c41965686841aa5ea0e1af448730d0902274e49251c7d1fb7c78fffb9"
|
checksum = "e247b21d6885136e4a910e1e6fac755d8dc56112c40ebf1062582f0644d62c62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
"glam",
|
"glam",
|
||||||
"hexx",
|
"hexx",
|
||||||
|
"pathfinding",
|
||||||
"rand",
|
"rand",
|
||||||
"thiserror 2.0.6",
|
"thiserror 2.0.6",
|
||||||
]
|
]
|
||||||
@ -2894,6 +2907,15 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "integer-sqrt"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-kit-sys"
|
name = "io-kit-sys"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -3228,7 +3250,7 @@ dependencies = [
|
|||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"pp-rs",
|
"pp-rs",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"spirv",
|
"spirv",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
@ -3249,7 +3271,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.5",
|
"regex-syntax 0.8.5",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"tracing",
|
"tracing",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
@ -3763,6 +3785,20 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathfinding"
|
||||||
|
version = "4.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "301ad6aa19104eeb9af172b3d6a4ab8a5ea26234890baf2fcb1cbbc3f05f674b"
|
||||||
|
dependencies = [
|
||||||
|
"deprecate-until",
|
||||||
|
"indexmap",
|
||||||
|
"integer-sqrt",
|
||||||
|
"num-traits",
|
||||||
|
"rustc-hash 2.1.0",
|
||||||
|
"thiserror 2.0.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@ -4184,6 +4220,12 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -5224,7 +5266,7 @@ dependencies = [
|
|||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
@ -5266,7 +5308,7 @@ dependencies = [
|
|||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"rustc-hash",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
|
|||||||
@ -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.5", features = ["bevy"] }
|
hexlab = { version = "0.6", features = ["bevy", "pathfinding"] }
|
||||||
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"
|
||||||
|
|||||||
@ -3,3 +3,12 @@ pub const WALL_OVERLAP_MODIFIER: f32 = 1.25;
|
|||||||
pub const FLOOR_Y_OFFSET: u8 = 200;
|
pub const FLOOR_Y_OFFSET: u8 = 200;
|
||||||
pub const MOVEMENT_COOLDOWN: f32 = 1.0; // one second cooldown
|
pub const MOVEMENT_COOLDOWN: f32 = 1.0; // one second cooldown
|
||||||
pub const TITLE: &str = "Maze Ascension: The Labyrinth of Echoes";
|
pub const TITLE: &str = "Maze Ascension: The Labyrinth of Echoes";
|
||||||
|
|
||||||
|
// Base score constants
|
||||||
|
pub const BASE_FLOOR_SCORE: usize = 1000;
|
||||||
|
pub const BASE_TIME_SCORE: usize = 100;
|
||||||
|
|
||||||
|
// Floor progression constants
|
||||||
|
pub const FLOOR_DIFFICULTY_MULTIPLIER: f32 = 1.2; // Higher floors are exponentially harder
|
||||||
|
pub const MIN_TIME_MULTIPLIER: f32 = 0.1; // Minimum score multiplier for time
|
||||||
|
pub const TIME_REFERENCE_SECONDS: f32 = 60.0; // Reference time for score calculation
|
||||||
|
|||||||
@ -3,9 +3,11 @@ pub mod components;
|
|||||||
mod systems;
|
mod systems;
|
||||||
|
|
||||||
use bevy::{ecs::system::RunSystemOnce, prelude::*};
|
use bevy::{ecs::system::RunSystemOnce, prelude::*};
|
||||||
|
use components::IdleTimer;
|
||||||
|
|
||||||
pub(super) fn plugin(app: &mut App) {
|
pub(super) fn plugin(app: &mut App) {
|
||||||
app.add_plugins(systems::plugin);
|
app.register_type::<IdleTimer>()
|
||||||
|
.add_plugins(systems::plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_hint_command(world: &mut World) {
|
pub fn spawn_hint_command(world: &mut World) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@ pub mod hint;
|
|||||||
pub mod maze;
|
pub mod maze;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod screens;
|
pub mod screens;
|
||||||
|
pub mod stats;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
@ -69,6 +70,7 @@ impl Plugin for AppPlugin {
|
|||||||
floor::plugin,
|
floor::plugin,
|
||||||
player::plugin,
|
player::plugin,
|
||||||
hint::plugin,
|
hint::plugin,
|
||||||
|
stats::plugin,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Enable dev tools for dev builds.
|
// Enable dev tools for dev builds.
|
||||||
|
|||||||
@ -95,14 +95,6 @@ impl MazeConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO
|
|
||||||
// 3928551514041614914
|
|
||||||
// (4, 0)
|
|
||||||
|
|
||||||
// FROM
|
|
||||||
// 7365371276044996661
|
|
||||||
// ()
|
|
||||||
|
|
||||||
impl Default for MazeConfig {
|
impl Default for MazeConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
//! The screen state for the main gameplay.
|
//! The screen state for the main gameplay.
|
||||||
|
|
||||||
use crate::player::spawn_player_command;
|
use crate::{
|
||||||
use crate::screens::Screen;
|
hint::spawn_hint_command, maze::spawn_level_command, player::spawn_player_command,
|
||||||
use crate::{hint::spawn_hint_command, maze::spawn_level_command};
|
screens::Screen, stats::spawn_stats_command,
|
||||||
|
};
|
||||||
|
|
||||||
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
use bevy::{input::common_conditions::input_just_pressed, prelude::*};
|
||||||
|
|
||||||
@ -13,6 +14,7 @@ pub(super) fn plugin(app: &mut App) {
|
|||||||
spawn_level_command,
|
spawn_level_command,
|
||||||
spawn_player_command,
|
spawn_player_command,
|
||||||
spawn_hint_command,
|
spawn_hint_command,
|
||||||
|
spawn_stats_command,
|
||||||
)
|
)
|
||||||
.chain(),
|
.chain(),
|
||||||
);
|
);
|
||||||
|
|||||||
21
src/stats/components.rs
Normal file
21
src/stats/components.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct FloorDisplay;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct HighestFloorDisplay;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct ScoreDisplay;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct FloorTimerDisplay;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Reflect, Component)]
|
||||||
|
#[reflect(Component)]
|
||||||
|
pub struct TotalTimerDisplay;
|
||||||
22
src/stats/container.rs
Normal file
22
src/stats/container.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
pub trait StatsContainer {
|
||||||
|
fn ui_stats(&mut self) -> EntityCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatsContainer for Commands<'_, '_> {
|
||||||
|
fn ui_stats(&mut self) -> EntityCommands {
|
||||||
|
self.spawn((
|
||||||
|
Name::new("Stats Root"),
|
||||||
|
Node {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
top: Val::Px(10.),
|
||||||
|
right: Val::Px(10.),
|
||||||
|
row_gap: Val::Px(8.),
|
||||||
|
align_items: AlignItems::End,
|
||||||
|
flex_direction: FlexDirection::Column,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/stats/mod.rs
Normal file
18
src/stats/mod.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pub mod components;
|
||||||
|
pub mod container;
|
||||||
|
pub mod resources;
|
||||||
|
mod systems;
|
||||||
|
|
||||||
|
use bevy::{ecs::system::RunSystemOnce, prelude::*};
|
||||||
|
use resources::{FloorTimer, Score, TotalTimer};
|
||||||
|
|
||||||
|
pub(super) fn plugin(app: &mut App) {
|
||||||
|
app.init_resource::<Score>()
|
||||||
|
.init_resource::<TotalTimer>()
|
||||||
|
.init_resource::<FloorTimer>()
|
||||||
|
.add_plugins(systems::plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_stats_command(world: &mut World) {
|
||||||
|
let _ = world.run_system_once(systems::spawn::spawn_stats);
|
||||||
|
}
|
||||||
31
src/stats/resources.rs
Normal file
31
src/stats/resources.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Reflect, Resource, Deref, DerefMut)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct Score(pub usize);
|
||||||
|
|
||||||
|
#[derive(Debug, Reflect, Resource, Deref, DerefMut)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct TotalTimer(pub Timer);
|
||||||
|
|
||||||
|
#[derive(Debug, Reflect, Resource, Deref, DerefMut)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct FloorTimer(pub Timer);
|
||||||
|
|
||||||
|
impl Default for TotalTimer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(init_timer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FloorTimer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(init_timer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_timer() -> Timer {
|
||||||
|
Timer::new(Duration::MAX, TimerMode::Once)
|
||||||
|
}
|
||||||
28
src/stats/systems/common.rs
Normal file
28
src/stats/systems/common.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
pub fn format_duration_adaptive(seconds: f32) -> String {
|
||||||
|
let total_millis = (seconds * 1000.0) as u64;
|
||||||
|
let millis = total_millis % 1000;
|
||||||
|
let total_seconds = total_millis / 1000;
|
||||||
|
let seconds = total_seconds % 60;
|
||||||
|
let total_minutes = total_seconds / 60;
|
||||||
|
let minutes = total_minutes % 60;
|
||||||
|
let total_hours = total_minutes / 60;
|
||||||
|
let hours = total_hours % 24;
|
||||||
|
let days = total_hours / 24;
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
if days > 0 {
|
||||||
|
result.push_str(&format!("{}d ", days));
|
||||||
|
}
|
||||||
|
if hours > 0 || days > 0 {
|
||||||
|
result.push_str(&format!("{:02}:", hours));
|
||||||
|
}
|
||||||
|
if minutes > 0 || hours > 0 || days > 0 {
|
||||||
|
result.push_str(&format!("{:02}:", minutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always show at least seconds and milliseconds
|
||||||
|
result.push_str(&format!("{:02}.{:03}", seconds, millis));
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
39
src/stats/systems/floor.rs
Normal file
39
src/stats/systems/floor.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
floor::{
|
||||||
|
components::{CurrentFloor, Floor},
|
||||||
|
resources::HighestFloor,
|
||||||
|
},
|
||||||
|
stats::components::{FloorDisplay, HighestFloorDisplay},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn update_floor_display(
|
||||||
|
floor_query: Query<&Floor, With<CurrentFloor>>,
|
||||||
|
mut text_query: Query<&mut Text, With<FloorDisplay>>,
|
||||||
|
) {
|
||||||
|
let Ok(floor) = floor_query.get_single() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(mut text) = text_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!("Floor: {}", floor.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_highest_floor_display(
|
||||||
|
hightes_floor: Res<HighestFloor>,
|
||||||
|
mut text_query: Query<&mut Text, With<HighestFloorDisplay>>,
|
||||||
|
) {
|
||||||
|
if !hightes_floor.is_changed() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Ok(mut text) = text_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!("Highest Floor: {}", hightes_floor.0);
|
||||||
|
}
|
||||||
33
src/stats/systems/floor_timer.rs
Normal file
33
src/stats/systems/floor_timer.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
floor::resources::HighestFloor,
|
||||||
|
stats::{components::FloorTimerDisplay, resources::FloorTimer},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::common::format_duration_adaptive;
|
||||||
|
|
||||||
|
pub fn update_floor_timer(
|
||||||
|
mut floor_timer: ResMut<FloorTimer>,
|
||||||
|
time: Res<Time>,
|
||||||
|
hightes_floor: Res<HighestFloor>,
|
||||||
|
) {
|
||||||
|
floor_timer.tick(time.delta());
|
||||||
|
if hightes_floor.is_changed() {
|
||||||
|
floor_timer.0.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_floor_timer_display(
|
||||||
|
mut text_query: Query<&mut Text, With<FloorTimerDisplay>>,
|
||||||
|
floor_timer: Res<FloorTimer>,
|
||||||
|
) {
|
||||||
|
let Ok(mut text) = text_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!(
|
||||||
|
"Floor Timer: {}",
|
||||||
|
format_duration_adaptive(floor_timer.0.elapsed_secs())
|
||||||
|
);
|
||||||
|
}
|
||||||
31
src/stats/systems/mod.rs
Normal file
31
src/stats/systems/mod.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
mod common;
|
||||||
|
mod floor;
|
||||||
|
mod floor_timer;
|
||||||
|
mod reset;
|
||||||
|
mod score;
|
||||||
|
pub mod spawn;
|
||||||
|
mod total_timer;
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use floor::{update_floor_display, update_highest_floor_display};
|
||||||
|
use floor_timer::{update_floor_timer, update_floor_timer_display};
|
||||||
|
use reset::reset_timers;
|
||||||
|
use score::{update_score, update_score_display};
|
||||||
|
use total_timer::{update_total_timer, update_total_timer_display};
|
||||||
|
|
||||||
|
use crate::screens::Screen;
|
||||||
|
|
||||||
|
pub(super) fn plugin(app: &mut App) {
|
||||||
|
app.add_systems(OnEnter(Screen::Gameplay), reset_timers)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
(update_score, update_score_display).chain(),
|
||||||
|
(update_floor_timer, update_floor_timer_display).chain(),
|
||||||
|
(update_total_timer, update_total_timer_display).chain(),
|
||||||
|
update_floor_display,
|
||||||
|
update_highest_floor_display,
|
||||||
|
)
|
||||||
|
.run_if(in_state(Screen::Gameplay)),
|
||||||
|
);
|
||||||
|
}
|
||||||
13
src/stats/systems/reset.rs
Normal file
13
src/stats/systems/reset.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::stats::resources::{FloorTimer, Score, TotalTimer};
|
||||||
|
|
||||||
|
pub fn reset_timers(
|
||||||
|
mut floor_timer: ResMut<FloorTimer>,
|
||||||
|
mut total_timer: ResMut<TotalTimer>,
|
||||||
|
mut score: ResMut<Score>,
|
||||||
|
) {
|
||||||
|
floor_timer.reset();
|
||||||
|
total_timer.reset();
|
||||||
|
score.0 = 0;
|
||||||
|
}
|
||||||
47
src/stats/systems/score.rs
Normal file
47
src/stats/systems/score.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
constants::{
|
||||||
|
BASE_FLOOR_SCORE, FLOOR_DIFFICULTY_MULTIPLIER, MIN_TIME_MULTIPLIER, TIME_REFERENCE_SECONDS,
|
||||||
|
},
|
||||||
|
floor::resources::HighestFloor,
|
||||||
|
stats::{
|
||||||
|
components::ScoreDisplay,
|
||||||
|
resources::{FloorTimer, Score},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn update_score(
|
||||||
|
mut score: ResMut<Score>,
|
||||||
|
hightes_floor: Res<HighestFloor>,
|
||||||
|
floor_timer: Res<FloorTimer>,
|
||||||
|
) {
|
||||||
|
if !hightes_floor.is_changed() || hightes_floor.is_added() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
score.0 = calculate_score(hightes_floor.0, floor_timer.elapsed_secs());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_score_display(
|
||||||
|
mut text_query: Query<&mut Text, With<ScoreDisplay>>,
|
||||||
|
score: Res<Score>,
|
||||||
|
) {
|
||||||
|
let Ok(mut text) = text_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!("Score: {}", score.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_score(floor_number: u8, completion_time: f32) -> usize {
|
||||||
|
// Calculate base floor score with exponential scaling
|
||||||
|
let floor_multiplier = (floor_number as f32).powf(FLOOR_DIFFICULTY_MULTIPLIER);
|
||||||
|
let base_score = BASE_FLOOR_SCORE as f32 * floor_multiplier;
|
||||||
|
|
||||||
|
// Calculate time multiplier (decreases as time increases)
|
||||||
|
let time_factor = 1. / (1. + (completion_time / TIME_REFERENCE_SECONDS));
|
||||||
|
let time_multiplier = time_factor.max(MIN_TIME_MULTIPLIER);
|
||||||
|
|
||||||
|
(base_score * time_multiplier) as usize
|
||||||
|
}
|
||||||
25
src/stats/systems/spawn.rs
Normal file
25
src/stats/systems/spawn.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
screens::Screen,
|
||||||
|
stats::{
|
||||||
|
components::{
|
||||||
|
FloorDisplay, FloorTimerDisplay, HighestFloorDisplay, ScoreDisplay, TotalTimerDisplay,
|
||||||
|
},
|
||||||
|
container::StatsContainer,
|
||||||
|
},
|
||||||
|
theme::widgets::Widgets,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn spawn_stats(mut commands: Commands) {
|
||||||
|
commands
|
||||||
|
.ui_stats()
|
||||||
|
.insert(StateScoped(Screen::Gameplay))
|
||||||
|
.with_children(|parent| {
|
||||||
|
parent.stats("Floor: 1", FloorDisplay);
|
||||||
|
parent.stats("Highest Floor: 1", HighestFloorDisplay);
|
||||||
|
parent.stats("Score: 0", ScoreDisplay);
|
||||||
|
parent.stats("Floor Timer", FloorTimerDisplay);
|
||||||
|
parent.stats("Total Timer", TotalTimerDisplay);
|
||||||
|
});
|
||||||
|
}
|
||||||
23
src/stats/systems/total_timer.rs
Normal file
23
src/stats/systems/total_timer.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
|
use crate::stats::{components::TotalTimerDisplay, resources::TotalTimer};
|
||||||
|
|
||||||
|
use super::common::format_duration_adaptive;
|
||||||
|
|
||||||
|
pub fn update_total_timer(mut total_timer: ResMut<TotalTimer>, time: Res<Time>) {
|
||||||
|
total_timer.tick(time.delta());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_total_timer_display(
|
||||||
|
mut text_query: Query<&mut Text, With<TotalTimerDisplay>>,
|
||||||
|
total_timer: Res<TotalTimer>,
|
||||||
|
) {
|
||||||
|
let Ok(mut text) = text_query.get_single_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
text.0 = format!(
|
||||||
|
"Total Timer: {}",
|
||||||
|
format_duration_adaptive(total_timer.0.elapsed_secs())
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@ pub mod components;
|
|||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod palette;
|
pub mod palette;
|
||||||
mod systems;
|
mod systems;
|
||||||
mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|||||||
@ -19,6 +19,8 @@ pub trait Widgets {
|
|||||||
|
|
||||||
/// Spawn a simple text label.
|
/// Spawn a simple text label.
|
||||||
fn label(&mut self, text: impl Into<String>) -> EntityCommands;
|
fn label(&mut self, text: impl Into<String>) -> EntityCommands;
|
||||||
|
|
||||||
|
fn stats(&mut self, text: impl Into<String>, bundle: impl Bundle) -> EntityCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SpawnUi> Widgets for T {
|
impl<T: SpawnUi> Widgets for T {
|
||||||
@ -107,6 +109,21 @@ impl<T: SpawnUi> Widgets for T {
|
|||||||
));
|
));
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stats(&mut self, text: impl Into<String>, bundle: impl Bundle) -> EntityCommands {
|
||||||
|
let text = text.into();
|
||||||
|
let entity = self.spawn_ui((
|
||||||
|
Name::new(text.clone()),
|
||||||
|
Text(text),
|
||||||
|
TextFont {
|
||||||
|
font_size: 24.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
bundle,
|
||||||
|
TextColor(RosePineDawn::Text.to_color()),
|
||||||
|
));
|
||||||
|
entity
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An extension trait for spawning UI containers.
|
/// An extension trait for spawning UI containers.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user