mirror of
https://github.com/kristoferssolo/maze-ascension.git
synced 2025-10-21 19:20:34 +00:00
Merge pull request #11 from kristoferssolo/feature/floors
This commit is contained in:
commit
ead980b7fe
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@ -27,9 +27,7 @@ jobs:
|
||||
sweep-cache: true
|
||||
- name: Run tests
|
||||
run: |
|
||||
cargo test --locked --workspace --all-features --all-targets
|
||||
# Workaround for https://github.com/rust-lang/cargo/issues/6669
|
||||
cargo test --locked --workspace --all-features --doc
|
||||
cargo test --locked --workspace --no-default-features
|
||||
# Run clippy lints.
|
||||
clippy:
|
||||
name: Clippy
|
||||
|
||||
210
Cargo.lock
generated
210
Cargo.lock
generated
@ -2299,6 +2299,21 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@ -2306,6 +2321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2314,6 +2330,17 @@ version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
@ -2333,6 +2360,53 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuzzy-matcher"
|
||||
version = "0.3.7"
|
||||
@ -2571,6 +2645,12 @@ dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
@ -2595,11 +2675,14 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "hexlab"
|
||||
version = "0.3.0"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b912e78d292803bc279aec3a4e2a0cdd0e0ac1540bcdc5d0f32cbfe9e4d234dc"
|
||||
checksum = "7bd7c21f4e2c11d40473d1ae673905f4deae3b12104fa6d70eeef9ef385aceb6"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_reflect",
|
||||
"bevy_utils",
|
||||
"glam",
|
||||
"hexx",
|
||||
"rand",
|
||||
"thiserror 2.0.6",
|
||||
@ -3083,7 +3166,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "maze-ascension"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bevy",
|
||||
@ -3093,6 +3176,10 @@ dependencies = [
|
||||
"hexx",
|
||||
"log",
|
||||
"rand",
|
||||
"rstest",
|
||||
"rstest_reuse",
|
||||
"strum",
|
||||
"test-log",
|
||||
"thiserror 2.0.6",
|
||||
"tracing",
|
||||
]
|
||||
@ -3749,6 +3836,12 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "piper"
|
||||
version = "0.2.4"
|
||||
@ -4032,6 +4125,12 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "renderdoc-sys"
|
||||
version = "1.1.0"
|
||||
@ -4067,12 +4166,62 @@ version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"rstest_macros",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"glob",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_reuse"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"rand",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.42"
|
||||
@ -4086,6 +4235,12 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "rustybuzz"
|
||||
version = "0.14.1"
|
||||
@ -4158,6 +4313,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
|
||||
|
||||
[[package]]
|
||||
name = "send_wrapper"
|
||||
version = "0.6.0"
|
||||
@ -4318,6 +4479,28 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svg_fmt"
|
||||
version = "0.4.4"
|
||||
@ -4401,6 +4584,27 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-log"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93"
|
||||
dependencies = [
|
||||
"test-log-macros",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test-log-macros"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "maze-ascension"
|
||||
authors = ["Kristofers Solo <dev@kristofers.xyz>"]
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -18,12 +18,19 @@ 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.5", features = ["bevy"] }
|
||||
bevy-inspector-egui = { version = "0.28", optional = true }
|
||||
bevy_egui = { version = "0.31", optional = true }
|
||||
thiserror = "2.0"
|
||||
anyhow = "1"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = "0.23"
|
||||
rstest_reuse = "0.7"
|
||||
test-log = { version = "0.2.16", default-features = false, features = [
|
||||
"trace",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = [
|
||||
@ -54,6 +61,8 @@ dev_native = [
|
||||
too_many_arguments = "allow"
|
||||
# Queries that access many components may trigger this lint.
|
||||
type_complexity = "allow"
|
||||
nursery = { level = "warn", priority = -1 }
|
||||
unwrap_used = "warn"
|
||||
|
||||
|
||||
# Compile with Performance Optimizations:
|
||||
|
||||
1
justfile
1
justfile
@ -17,4 +17,3 @@ web-dev:
|
||||
# Run web release
|
||||
web-release:
|
||||
trunk serve --release --no-default-features
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
//! A high-level way to load collections of asset handles as resources.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub(super) fn plugin(app: &mut App) {
|
||||
app.init_resource::<ResourceHandles>();
|
||||
@ -51,7 +50,9 @@ fn load_resource_assets(world: &mut World) {
|
||||
world.resource_scope(|world, mut resource_handles: Mut<ResourceHandles>| {
|
||||
world.resource_scope(|world, assets: Mut<AssetServer>| {
|
||||
for _ in 0..resource_handles.waiting.len() {
|
||||
let (handle, insert_fn) = resource_handles.waiting.pop_front().unwrap();
|
||||
let Some((handle, insert_fn)) = resource_handles.waiting.pop_front() else {
|
||||
continue;
|
||||
};
|
||||
if assets.is_loaded_with_dependencies(&handle) {
|
||||
insert_fn(world, &handle);
|
||||
resource_handles.finished.push(handle);
|
||||
|
||||
3
src/constants.rs
Normal file
3
src/constants.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub const MOVEMENT_THRESHOLD: f32 = 0.01;
|
||||
pub const WALL_OVERLAP_MODIFIER: f32 = 1.25;
|
||||
pub const FLOOR_Y_OFFSET: u8 = 100;
|
||||
@ -12,7 +12,7 @@ use hexx::{Hex, HexOrientation};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
pub(crate) fn maze_controls_ui(world: &mut World) {
|
||||
pub fn maze_controls_ui(world: &mut World) {
|
||||
if world.get_resource::<MazePluginLoaded>().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
mod maze_controls;
|
||||
|
||||
pub(crate) use maze_controls::maze_controls_ui;
|
||||
pub use maze_controls::maze_controls_ui;
|
||||
|
||||
@ -1,19 +1,59 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Debug, Reflect, Component)]
|
||||
#[derive(Debug, Reflect, Component, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct Floor(pub u8);
|
||||
|
||||
#[derive(Debug, Reflect, Component)]
|
||||
#[reflect(Component)]
|
||||
pub struct TargetFloor(pub u8);
|
||||
pub struct CurrentFloor;
|
||||
|
||||
#[derive(Debug, Reflect, Component)]
|
||||
#[reflect(Component)]
|
||||
pub struct CurrentFloor;
|
||||
pub struct NextFloor;
|
||||
|
||||
#[derive(Debug, Reflect, Component, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct FloorYTarget(pub f32);
|
||||
|
||||
impl Default for Floor {
|
||||
fn default() -> Self {
|
||||
Self(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Floor {
|
||||
pub const fn increased(&self) -> Self {
|
||||
Self(self.0.saturating_add(1))
|
||||
}
|
||||
|
||||
pub fn decreased(&self) -> Self {
|
||||
Self(self.0.saturating_sub(1).max(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case(0, 1)]
|
||||
#[case(1, 2)]
|
||||
#[case(254, 255)]
|
||||
#[case(255, 255)]
|
||||
fn increase(#[case] input: u8, #[case] expected: u8) {
|
||||
let floor = Floor(input);
|
||||
assert_eq!(*floor.increased(), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(0, 1)] // clamps to 1
|
||||
#[case(1, 1)] // clamps to 1
|
||||
#[case(2, 1)]
|
||||
#[case(255, 254)]
|
||||
fn decrease(#[case] input: u8, #[case] expected: u8) {
|
||||
let floor = Floor(input);
|
||||
assert_eq!(*floor.decreased(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +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, Clone, Copy, Reflect, Event, Default, PartialEq, Eq)]
|
||||
pub enum TransitionFloor {
|
||||
#[default]
|
||||
Ascend,
|
||||
Descend,
|
||||
}
|
||||
|
||||
#[derive(Debug, Reflect, Event)]
|
||||
pub struct RespawnFloor {
|
||||
pub floor: u8,
|
||||
pub config: MazeConfig,
|
||||
impl TransitionFloor {
|
||||
pub fn into_direction(&self) -> f32 {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub const 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Reflect, Event)]
|
||||
pub struct DespawnFloor {
|
||||
pub floor: u8,
|
||||
impl From<TransitionFloor> for f32 {
|
||||
fn from(value: TransitionFloor) -> Self {
|
||||
Self::from(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TransitionFloor> for f32 {
|
||||
fn from(value: &TransitionFloor) -> Self {
|
||||
match value {
|
||||
TransitionFloor::Ascend => -1.,
|
||||
TransitionFloor::Descend => 1.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +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) {}
|
||||
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
5
src/floor/resources.rs
Normal file
@ -0,0 +1,5 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Debug, Default, Reflect, Resource, PartialEq, Eq)]
|
||||
#[reflect(Resource)]
|
||||
pub struct HighestFloor(pub u8);
|
||||
0
src/floor/systems/clear_events.rs
Normal file
0
src/floor/systems/clear_events.rs
Normal file
3
src/floor/systems/despawn.rs
Normal file
3
src/floor/systems/despawn.rs
Normal file
@ -0,0 +1,3 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub const fn despawn_floor(mut _commands: Commands) {}
|
||||
@ -1 +1,23 @@
|
||||
mod clear_events;
|
||||
mod despawn;
|
||||
mod movement;
|
||||
mod spawn;
|
||||
|
||||
use crate::maze::MazePluginLoaded;
|
||||
use bevy::prelude::*;
|
||||
use despawn::despawn_floor;
|
||||
use movement::{handle_floor_transition_events, move_floors};
|
||||
use spawn::spawn_floor;
|
||||
|
||||
pub(super) fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(
|
||||
spawn_floor,
|
||||
despawn_floor,
|
||||
handle_floor_transition_events,
|
||||
move_floors.after(handle_floor_transition_events),
|
||||
)
|
||||
.run_if(resource_exists::<MazePluginLoaded>),
|
||||
);
|
||||
}
|
||||
|
||||
79
src/floor/systems/movement.rs
Normal file
79
src/floor/systems/movement.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use crate::{
|
||||
constants::{FLOOR_Y_OFFSET, MOVEMENT_THRESHOLD},
|
||||
floor::{
|
||||
components::{CurrentFloor, FloorYTarget, NextFloor},
|
||||
events::TransitionFloor,
|
||||
},
|
||||
maze::components::HexMaze,
|
||||
player::components::{MovementSpeed, Player},
|
||||
};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub fn move_floors(
|
||||
mut commands: Commands,
|
||||
mut maze_query: Query<
|
||||
(Entity, &mut Transform, &FloorYTarget),
|
||||
(With<HexMaze>, With<FloorYTarget>),
|
||||
>,
|
||||
player_query: Query<&MovementSpeed, With<Player>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let speed = player_query.get_single().map_or(100., |s| s.0);
|
||||
let movement_distance = speed * time.delta_secs();
|
||||
for (entity, mut transform, movement_state) in maze_query.iter_mut() {
|
||||
let delta = movement_state.0 - transform.translation.y;
|
||||
if delta.abs() > MOVEMENT_THRESHOLD {
|
||||
let movement = delta.signum() * movement_distance.min(delta.abs());
|
||||
transform.translation.y += movement;
|
||||
} else {
|
||||
transform.translation.y = movement_state.0;
|
||||
commands.entity(entity).remove::<FloorYTarget>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_floor_transition_events(
|
||||
mut commands: Commands,
|
||||
mut maze_query: Query<(Entity, &Transform, Option<&FloorYTarget>), With<HexMaze>>,
|
||||
current_query: Query<Entity, With<CurrentFloor>>,
|
||||
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 = event.into();
|
||||
|
||||
let Some(current_entity) = current_query.get_single().ok() else {
|
||||
continue;
|
||||
};
|
||||
let Some(next_entity) = next_query.get_single().ok() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for (entity, transforms, movement_state) in maze_query.iter_mut() {
|
||||
let target_y = (FLOOR_Y_OFFSET as f32).mul_add(direction, transforms.translation.y);
|
||||
if movement_state.is_none() {
|
||||
commands.entity(entity).insert(FloorYTarget(target_y));
|
||||
}
|
||||
}
|
||||
|
||||
update_current_next_floor(&mut commands, current_entity, next_entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fn update_current_next_floor(commands: &mut Commands, current_entity: Entity, next_entity: Entity) {
|
||||
commands.entity(current_entity).remove::<CurrentFloor>();
|
||||
commands
|
||||
.entity(next_entity)
|
||||
.remove::<NextFloor>()
|
||||
.insert(CurrentFloor);
|
||||
}
|
||||
35
src/floor/systems/spawn.rs
Normal file
35
src/floor/systems/spawn.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::{
|
||||
floor::{
|
||||
components::{CurrentFloor, Floor, FloorYTarget},
|
||||
events::TransitionFloor,
|
||||
resources::HighestFloor,
|
||||
},
|
||||
maze::events::SpawnMaze,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub(super) fn spawn_floor(
|
||||
mut commands: Commands,
|
||||
query: Query<&mut Floor, (With<CurrentFloor>, Without<FloorYTarget>)>,
|
||||
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 = 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() });
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,12 @@
|
||||
mod asset_tracking;
|
||||
pub mod asset_tracking;
|
||||
pub mod audio;
|
||||
pub mod constants;
|
||||
#[cfg(feature = "dev")]
|
||||
mod dev_tools;
|
||||
pub mod dev_tools;
|
||||
pub mod floor;
|
||||
pub mod maze;
|
||||
pub mod player;
|
||||
mod screens;
|
||||
pub mod screens;
|
||||
pub mod theme;
|
||||
|
||||
use bevy::{
|
||||
|
||||
@ -1,25 +1,33 @@
|
||||
use super::resources::GlobalMazeConfig;
|
||||
use bevy::prelude::*;
|
||||
use crate::{
|
||||
constants::WALL_OVERLAP_MODIFIER,
|
||||
theme::{palette::rose_pine::RosePine, prelude::ColorScheme},
|
||||
};
|
||||
use bevy::{prelude::*, utils::HashMap};
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
const WALL_OVERLAP_MODIFIER: f32 = 1.25;
|
||||
const HEX_SIDES: u32 = 6;
|
||||
const WHITE_EMISSION_INTENSITY: f32 = 10.;
|
||||
|
||||
pub(crate) struct MazeAssets {
|
||||
pub(crate) hex_mesh: Handle<Mesh>,
|
||||
pub(crate) wall_mesh: Handle<Mesh>,
|
||||
pub(crate) hex_material: Handle<StandardMaterial>,
|
||||
pub(crate) wall_material: Handle<StandardMaterial>,
|
||||
pub struct MazeAssets {
|
||||
pub hex_mesh: Handle<Mesh>,
|
||||
pub wall_mesh: Handle<Mesh>,
|
||||
pub hex_material: Handle<StandardMaterial>,
|
||||
pub wall_material: Handle<StandardMaterial>,
|
||||
pub custom_materials: HashMap<RosePine, Handle<StandardMaterial>>,
|
||||
}
|
||||
|
||||
impl MazeAssets {
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
meshes: &mut ResMut<Assets<Mesh>>,
|
||||
materials: &mut ResMut<Assets<StandardMaterial>>,
|
||||
global_config: &GlobalMazeConfig,
|
||||
) -> MazeAssets {
|
||||
MazeAssets {
|
||||
) -> Self {
|
||||
let custom_materials = RosePine::iter()
|
||||
.map(|color| (color, materials.add(color.to_standart_material())))
|
||||
.collect();
|
||||
Self {
|
||||
hex_mesh: meshes.add(generate_hex_mesh(
|
||||
global_config.hex_size,
|
||||
global_config.height,
|
||||
@ -30,6 +38,7 @@ impl MazeAssets {
|
||||
)),
|
||||
hex_material: materials.add(white_material()),
|
||||
wall_material: materials.add(Color::BLACK),
|
||||
custom_materials,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -53,9 +62,8 @@ fn generate_square_mesh(depth: f32, wall_size: f32) -> Mesh {
|
||||
Mesh::from(rectangular_prism).rotated_by(rotation)
|
||||
}
|
||||
|
||||
fn white_material() -> StandardMaterial {
|
||||
pub fn white_material() -> StandardMaterial {
|
||||
StandardMaterial {
|
||||
base_color: Color::WHITE,
|
||||
emissive: LinearRgba::new(
|
||||
WHITE_EMISSION_INTENSITY,
|
||||
WHITE_EMISSION_INTENSITY,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
use crate::floor::components::Floor;
|
||||
|
||||
use super::{errors::MazeConfigError, GlobalMazeConfig};
|
||||
use super::GlobalMazeConfig;
|
||||
use bevy::prelude::*;
|
||||
use hexlab::HexMaze;
|
||||
use hexlab::Maze;
|
||||
use hexx::{Hex, HexLayout, HexOrientation};
|
||||
use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng};
|
||||
|
||||
#[derive(Debug, Reflect, Component)]
|
||||
#[reflect(Component)]
|
||||
#[require(MazeConfig, Floor)]
|
||||
pub struct Maze(pub HexMaze);
|
||||
#[require(MazeConfig, Floor, Maze)]
|
||||
pub struct HexMaze;
|
||||
|
||||
#[derive(Debug, Reflect, Component)]
|
||||
#[reflect(Component)]
|
||||
@ -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),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
@ -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 fn generation_failed(radius: u32, seed: u64) -> Self {
|
||||
pub const fn generation_failed(radius: u16, seed: u64) -> Self {
|
||||
Self::GenerationFailed { radius, seed }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use super::components::MazeConfig;
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Debug, Reflect, Event)]
|
||||
pub struct SpawnMaze {
|
||||
@ -18,3 +17,12 @@ pub struct RespawnMaze {
|
||||
pub struct DespawnMaze {
|
||||
pub floor: u8,
|
||||
}
|
||||
|
||||
impl Default for SpawnMaze {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
floor: 1,
|
||||
config: MazeConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ mod systems;
|
||||
mod triggers;
|
||||
|
||||
use bevy::{ecs::system::RunSystemOnce, prelude::*};
|
||||
use components::Maze;
|
||||
use components::HexMaze;
|
||||
use events::{DespawnMaze, RespawnMaze, SpawnMaze};
|
||||
pub use resources::{GlobalMazeConfig, MazePluginLoaded};
|
||||
|
||||
@ -16,7 +16,7 @@ pub(super) fn plugin(app: &mut App) {
|
||||
.add_event::<SpawnMaze>()
|
||||
.add_event::<RespawnMaze>()
|
||||
.add_event::<DespawnMaze>()
|
||||
.register_type::<Maze>()
|
||||
.register_type::<HexMaze>()
|
||||
.add_plugins((systems::plugin, triggers::plugin));
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::maze::{components::MazeConfig, events::SpawnMaze};
|
||||
use crate::maze::events::SpawnMaze;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub(crate) fn setup(mut commands: Commands) {
|
||||
let config = MazeConfig::default();
|
||||
commands.trigger(SpawnMaze { floor: 1, config });
|
||||
pub fn setup(mut commands: Commands) {
|
||||
commands.trigger(SpawnMaze::default());
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@ use crate::maze::{
|
||||
components::MazeConfig,
|
||||
errors::{MazeError, MazeResult},
|
||||
};
|
||||
use hexlab::{GeneratorType, HexMaze, MazeBuilder};
|
||||
use hexlab::prelude::*;
|
||||
|
||||
pub(crate) fn generate_maze(config: &MazeConfig) -> MazeResult<HexMaze> {
|
||||
pub fn generate_maze(config: &MazeConfig) -> MazeResult<Maze> {
|
||||
MazeBuilder::new()
|
||||
.with_radius(config.radius)
|
||||
.with_seed(config.seed)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
pub mod common;
|
||||
mod despawn;
|
||||
mod respawn;
|
||||
mod spawn;
|
||||
pub mod spawn;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use despawn::despawn_maze;
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
use super::{common::generate_maze, spawn::spawn_maze_tiles};
|
||||
use crate::{
|
||||
floor::components::Floor,
|
||||
maze::{
|
||||
assets::MazeAssets, components::Maze, errors::MazeError, events::RespawnMaze,
|
||||
GlobalMazeConfig,
|
||||
},
|
||||
maze::{assets::MazeAssets, errors::MazeError, events::RespawnMaze, GlobalMazeConfig},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use hexlab::Maze;
|
||||
|
||||
pub(super) fn respawn_maze(
|
||||
trigger: Trigger<RespawnMaze>,
|
||||
@ -30,7 +28,7 @@ pub(super) fn respawn_maze(
|
||||
}
|
||||
};
|
||||
|
||||
maze.0 = match generate_maze(config) {
|
||||
*maze = match generate_maze(config) {
|
||||
Ok(generated_maze) => generated_maze,
|
||||
Err(e) => {
|
||||
warn!("Failed to update floor ({floor}). {e}");
|
||||
@ -43,7 +41,7 @@ pub(super) fn respawn_maze(
|
||||
spawn_maze_tiles(
|
||||
&mut commands,
|
||||
entity,
|
||||
&maze.0,
|
||||
&maze,
|
||||
&assets,
|
||||
config,
|
||||
&global_config,
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
use super::common::generate_maze;
|
||||
use crate::{
|
||||
floor::components::{CurrentFloor, Floor},
|
||||
constants::FLOOR_Y_OFFSET,
|
||||
floor::components::{CurrentFloor, Floor, NextFloor},
|
||||
maze::{
|
||||
assets::MazeAssets,
|
||||
components::{Maze, MazeConfig, Tile, Wall},
|
||||
components::{HexMaze, MazeConfig, Tile, Wall},
|
||||
events::SpawnMaze,
|
||||
resources::GlobalMazeConfig,
|
||||
},
|
||||
theme::palette::rose_pine::RosePine,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use hexlab::prelude::*;
|
||||
use hexlab::prelude::{Tile as HexTile, *};
|
||||
use hexx::HexOrientation;
|
||||
use std::f32::consts::{FRAC_PI_2, FRAC_PI_3, FRAC_PI_6};
|
||||
|
||||
use super::common::generate_maze;
|
||||
|
||||
pub(super) fn spawn_maze(
|
||||
trigger: Trigger<SpawnMaze>,
|
||||
mut commands: Commands,
|
||||
@ -23,23 +24,39 @@ pub(super) fn spawn_maze(
|
||||
global_config: Res<GlobalMazeConfig>,
|
||||
) {
|
||||
let SpawnMaze { floor, config } = trigger.event();
|
||||
|
||||
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 maze = match generate_maze(config) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
error!("Failed to generate maze for floor {floor}: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let y_offset = match *floor {
|
||||
1 => 0,
|
||||
_ => FLOOR_Y_OFFSET,
|
||||
} as f32;
|
||||
|
||||
// (floor - 1) * FLOOR_Y_OFFSET
|
||||
|
||||
let entity = commands
|
||||
.spawn((
|
||||
Name::new(format!("Floor {}", floor)),
|
||||
Maze(maze.clone()),
|
||||
HexMaze,
|
||||
maze.clone(),
|
||||
Floor(*floor),
|
||||
CurrentFloor, // TODO: remove
|
||||
config.clone(),
|
||||
Transform::from_translation(Vec3::ZERO),
|
||||
Transform::from_translation(Vec3::ZERO.with_y(y_offset)),
|
||||
Visibility::Visible,
|
||||
))
|
||||
.insert_if(CurrentFloor, || *floor == 1)
|
||||
.insert_if(NextFloor, || *floor != 1)
|
||||
.id();
|
||||
|
||||
let assets = MazeAssets::new(&mut meshes, &mut materials, &global_config);
|
||||
@ -53,10 +70,10 @@ pub(super) fn spawn_maze(
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn spawn_maze_tiles(
|
||||
pub fn spawn_maze_tiles(
|
||||
commands: &mut Commands,
|
||||
parent_entity: Entity,
|
||||
maze: &HexMaze,
|
||||
maze: &Maze,
|
||||
assets: &MazeAssets,
|
||||
maze_config: &MazeConfig,
|
||||
global_config: &GlobalMazeConfig,
|
||||
@ -81,12 +98,26 @@ pub(super) fn spawn_single_hex_tile(
|
||||
HexOrientation::Flat => Quat::from_rotation_y(FRAC_PI_6), // 30 degrees rotation
|
||||
};
|
||||
|
||||
let material = match tile.pos() {
|
||||
pos if pos == maze_config.start_pos => assets
|
||||
.custom_materials
|
||||
.get(&RosePine::Pine)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
pos if pos == maze_config.end_pos => assets
|
||||
.custom_materials
|
||||
.get(&RosePine::Love)
|
||||
.cloned()
|
||||
.unwrap_or_default(),
|
||||
_ => assets.hex_material.clone(),
|
||||
};
|
||||
|
||||
parent
|
||||
.spawn((
|
||||
Name::new(format!("Hex {}", tile)),
|
||||
Tile,
|
||||
Mesh3d(assets.hex_mesh.clone()),
|
||||
MeshMaterial3d(assets.hex_material.clone()),
|
||||
MeshMaterial3d(material),
|
||||
Transform::from_translation(world_pos).with_rotation(rotation),
|
||||
))
|
||||
.with_children(|parent| spawn_walls(parent, assets, tile.walls(), global_config));
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::theme::palette::rose_pine::PINE;
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::theme::{palette::rose_pine::RosePine, prelude::ColorScheme};
|
||||
|
||||
pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh {
|
||||
Mesh::from(Capsule3d {
|
||||
radius,
|
||||
@ -9,9 +10,10 @@ pub(super) fn generate_pill_mesh(radius: f32, half_length: f32) -> Mesh {
|
||||
}
|
||||
|
||||
pub(super) fn blue_material() -> StandardMaterial {
|
||||
let color = RosePine::Pine;
|
||||
StandardMaterial {
|
||||
base_color: PINE,
|
||||
emissive: PINE.to_linear() * 3.,
|
||||
base_color: color.to_color(),
|
||||
emissive: color.to_linear_rgba() * 3.,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::{
|
||||
floor::components::CurrentFloor,
|
||||
maze::components::{Maze, MazeConfig},
|
||||
maze::components::MazeConfig,
|
||||
player::components::{CurrentPosition, MovementTarget, Player},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use hexlab::prelude::*;
|
||||
use hexx::{EdgeDirection, HexOrientation};
|
||||
|
||||
pub(super) fn player_input(
|
||||
@ -24,7 +25,7 @@ pub(super) fn player_input(
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(tile) = maze.0.get_tile(current_pos) else {
|
||||
let Some(tile) = maze.get(current_pos) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
|
||||
@ -1,18 +1,22 @@
|
||||
mod input;
|
||||
mod movement;
|
||||
pub mod setup;
|
||||
mod vertical_transition;
|
||||
|
||||
use crate::maze::MazePluginLoaded;
|
||||
use bevy::prelude::*;
|
||||
use input::player_input;
|
||||
use movement::player_movement;
|
||||
|
||||
use crate::maze::MazePluginLoaded;
|
||||
use vertical_transition::handle_floor_transition;
|
||||
|
||||
pub(super) fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
(player_input, player_movement)
|
||||
.chain()
|
||||
(
|
||||
player_input,
|
||||
player_movement.after(player_input),
|
||||
handle_floor_transition,
|
||||
)
|
||||
.run_if(resource_exists::<MazePluginLoaded>),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::{
|
||||
constants::MOVEMENT_THRESHOLD,
|
||||
floor::components::CurrentFloor,
|
||||
maze::components::MazeConfig,
|
||||
player::components::{CurrentPosition, MovementSpeed, MovementTarget, Player},
|
||||
@ -48,7 +49,7 @@ pub(super) fn player_movement(
|
||||
}
|
||||
|
||||
fn should_complete_movement(current_pos: Vec3, target_pos: Vec3) -> bool {
|
||||
(target_pos - current_pos).length() < 0.1
|
||||
(target_pos - current_pos).length() < MOVEMENT_THRESHOLD
|
||||
}
|
||||
|
||||
fn update_position(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::player::events::SpawnPlayer;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub(crate) fn setup(mut commands: Commands) {
|
||||
pub fn setup(mut commands: Commands) {
|
||||
commands.trigger(SpawnPlayer);
|
||||
}
|
||||
|
||||
37
src/player/systems/vertical_transition.rs
Normal file
37
src/player/systems/vertical_transition.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
floor::{
|
||||
components::{CurrentFloor, Floor},
|
||||
events::TransitionFloor,
|
||||
},
|
||||
maze::components::MazeConfig,
|
||||
player::components::{CurrentPosition, Player},
|
||||
};
|
||||
|
||||
pub fn handle_floor_transition(
|
||||
player_query: Query<&CurrentPosition, With<Player>>,
|
||||
maze_query: Query<(&MazeConfig, &Floor), With<CurrentFloor>>,
|
||||
mut event_writer: EventWriter<TransitionFloor>,
|
||||
) {
|
||||
let Ok((config, floor)) = maze_query.get_single() else {
|
||||
warn!("Failed to get maze configuration for current floor - cannot ascend/descend player.");
|
||||
return;
|
||||
};
|
||||
|
||||
for current_hex in player_query.iter() {
|
||||
// Check for ascending
|
||||
if current_hex.0 == config.end_pos {
|
||||
dbg!("Ascending");
|
||||
event_writer.send(TransitionFloor::Ascend);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for descending
|
||||
if current_hex.0 == config.start_pos && floor.0 != 1 {
|
||||
dbg!("Descending");
|
||||
event_writer.send(TransitionFloor::Descend);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ fn continue_to_title_screen(mut next_screen: ResMut<NextState<Screen>>) {
|
||||
next_screen.set(Screen::Title);
|
||||
}
|
||||
|
||||
fn all_assets_loaded(
|
||||
const fn all_assets_loaded(
|
||||
interaction_assets: Option<Res<InteractionAssets>>,
|
||||
credits_music: Option<Res<CreditsMusic>>,
|
||||
gameplay_music: Option<Res<GameplayMusic>>,
|
||||
|
||||
@ -101,7 +101,7 @@ impl UiImageFadeInOut {
|
||||
let fade = self.fade_duration / self.total_duration;
|
||||
|
||||
// Regular trapezoid-shaped graph, flat at the top with alpha = 1.0.
|
||||
((1.0 - (2.0 * t - 1.0).abs()) / fade).min(1.0)
|
||||
((1.0 - 2.0f32.mul_add(t, -1.0).abs()) / fade).min(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
104
src/theme/colorscheme.rs
Normal file
104
src/theme/colorscheme.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use bevy::prelude::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// A trait for types that can be converted to a Bevy `Color`.
|
||||
///
|
||||
/// Implementing this trait allows a type to be easily converted to various Bevy color types.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bevy::prelude::*;
|
||||
/// use maze_ascension::theme::prelude::ColorScheme;
|
||||
///
|
||||
/// struct MyColor(u8, u8, u8);
|
||||
///
|
||||
/// impl ColorScheme for MyColor {
|
||||
/// fn to_color(&self) -> Color {
|
||||
/// Color::srgb(
|
||||
/// self.0 as f32 / 255.0,
|
||||
/// self.1 as f32 / 255.0,
|
||||
/// self.2 as f32 / 255.0
|
||||
/// )
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let my_color = MyColor(255, 0, 0);
|
||||
/// let bevy_color: Color = my_color.to_color();
|
||||
/// assert_eq!(bevy_color, Color::srgb(1., 0., 0.));
|
||||
/// ```
|
||||
pub trait ColorScheme {
|
||||
/// Converts the implementing type to a Bevy `Color`.
|
||||
fn to_color(&self) -> Color;
|
||||
|
||||
/// Converts the implementing type to a Bevy `LinearRgba`.
|
||||
///
|
||||
/// This method provides a default implementation based on `to_color()`.
|
||||
fn to_linear_rgba(&self) -> LinearRgba {
|
||||
self.to_color().to_linear()
|
||||
}
|
||||
|
||||
/// Converts the implementing type to a Bevy `StandardMaterial`.
|
||||
///
|
||||
/// This method provides a default implementation that sets the emissive color.
|
||||
fn to_standart_material(&self) -> StandardMaterial {
|
||||
StandardMaterial {
|
||||
emissive: self.to_linear_rgba(),
|
||||
..default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type that implements `From` traits for types implementing `ColorScheme`.
|
||||
///
|
||||
/// This wrapper allows for easy conversion from `ColorScheme` types to Bevy color types.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bevy::prelude::*;
|
||||
/// use maze_ascension::theme::prelude::{ColorScheme, ColorSchemeWrapper};
|
||||
///
|
||||
/// struct MyColor(u8, u8, u8);
|
||||
///
|
||||
/// impl ColorScheme for MyColor {
|
||||
/// fn to_color(&self) -> Color {
|
||||
/// Color::srgb(
|
||||
/// self.0 as f32 / 255.0,
|
||||
/// self.1 as f32 / 255.0,
|
||||
/// self.2 as f32 / 255.0
|
||||
/// )
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let my_color = MyColor(0, 255, 0);
|
||||
/// let wrapper = ColorSchemeWrapper(my_color);
|
||||
/// let bevy_color: Color = wrapper.into();
|
||||
/// assert_eq!(bevy_color, Color::srgb(0., 1., 0.));
|
||||
/// ```
|
||||
pub struct ColorSchemeWrapper<T: ColorScheme>(pub T);
|
||||
|
||||
impl<T: ColorScheme> From<T> for ColorSchemeWrapper<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ColorScheme> Deref for ColorSchemeWrapper<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ColorScheme> From<ColorSchemeWrapper<T>> for Color {
|
||||
fn from(value: ColorSchemeWrapper<T>) -> Self {
|
||||
value.to_color()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ColorScheme> From<ColorSchemeWrapper<T>> for LinearRgba {
|
||||
fn from(value: ColorSchemeWrapper<T>) -> Self {
|
||||
value.to_linear_rgba()
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
// Unused utilities may trigger this lints undesirably.
|
||||
|
||||
mod colorscheme;
|
||||
pub mod interaction;
|
||||
pub mod palette;
|
||||
mod widgets;
|
||||
@ -9,6 +10,7 @@ mod widgets;
|
||||
#[allow(unused_imports)]
|
||||
pub mod prelude {
|
||||
pub use super::{
|
||||
colorscheme::{ColorScheme, ColorSchemeWrapper},
|
||||
interaction::{InteractionPalette, OnPress},
|
||||
palette as ui_palette,
|
||||
widgets::{Containers as _, Widgets as _},
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -1,18 +1,45 @@
|
||||
use super::rgb_u8;
|
||||
use crate::theme::prelude::ColorScheme;
|
||||
use bevy::prelude::*;
|
||||
use strum::EnumIter;
|
||||
|
||||
pub const BASE: Color = rgb_u8(25, 23, 36);
|
||||
pub const SURFACE: Color = rgb_u8(31, 29, 46);
|
||||
pub const OVERLAY: Color = rgb_u8(38, 35, 58);
|
||||
pub const MUTED: Color = rgb_u8(110, 106, 134);
|
||||
pub const SUBTLE: Color = rgb_u8(144, 140, 170);
|
||||
pub const TEXT: Color = rgb_u8(224, 222, 244);
|
||||
pub const LOVE: Color = rgb_u8(235, 111, 146);
|
||||
pub const GOLD: Color = rgb_u8(246, 193, 119);
|
||||
pub const ROSE: Color = rgb_u8(235, 188, 186);
|
||||
pub const PINE: Color = rgb_u8(49, 116, 143);
|
||||
pub const FOAM: Color = rgb_u8(156, 207, 216);
|
||||
pub const IRIS: Color = rgb_u8(196, 167, 231);
|
||||
pub const HIGHLIGHT_LOW: Color = rgb_u8(33, 32, 46);
|
||||
pub const HIGHLIGHT_MED: Color = rgb_u8(64, 61, 82);
|
||||
pub const HIGHLIGHT_HIGH: Color = rgb_u8(82, 79, 103);
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
|
||||
pub enum RosePine {
|
||||
Base,
|
||||
Surface,
|
||||
Overlay,
|
||||
Muted,
|
||||
Subtle,
|
||||
Text,
|
||||
Love,
|
||||
Gold,
|
||||
Rose,
|
||||
Pine,
|
||||
Foam,
|
||||
Iris,
|
||||
HighlightLow,
|
||||
HighlightMed,
|
||||
HighlightHigh,
|
||||
}
|
||||
|
||||
impl ColorScheme for RosePine {
|
||||
fn to_color(&self) -> Color {
|
||||
match self {
|
||||
Self::Base => rgb_u8(25, 23, 36),
|
||||
Self::Surface => rgb_u8(31, 29, 46),
|
||||
Self::Overlay => rgb_u8(38, 35, 58),
|
||||
Self::Muted => rgb_u8(110, 106, 134),
|
||||
Self::Subtle => rgb_u8(144, 140, 170),
|
||||
Self::Text => rgb_u8(224, 222, 244),
|
||||
Self::Love => rgb_u8(235, 111, 146),
|
||||
Self::Gold => rgb_u8(246, 193, 119),
|
||||
Self::Rose => rgb_u8(235, 188, 186),
|
||||
Self::Pine => rgb_u8(49, 116, 143),
|
||||
Self::Foam => rgb_u8(156, 207, 216),
|
||||
Self::Iris => rgb_u8(196, 167, 231),
|
||||
Self::HighlightLow => rgb_u8(33, 32, 46),
|
||||
Self::HighlightMed => rgb_u8(64, 61, 82),
|
||||
Self::HighlightHigh => rgb_u8(82, 79, 103),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user