diff --git a/Cargo.lock b/Cargo.lock index d60e648..b5c5dcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,6 +180,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arboard" +version = "3.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" +dependencies = [ + "clipboard-win", + "core-graphics", + "image", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "parking_lot", + "windows-sys 0.48.0", + "x11rb", +] + [[package]] name = "arrayref" version = "0.3.8" @@ -304,6 +322,50 @@ dependencies = [ "bevy_internal", ] +[[package]] +name = "bevy-inspector-egui" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac12a22e5de801323bc5bc344949de086b9b8db02c34e109f128ffd41514e5d" +dependencies = [ + "bevy-inspector-egui-derive", + "bevy_app", + "bevy_asset", + "bevy_color", + "bevy_core", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_egui", + "bevy_hierarchy", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_reflect", + "bevy_render", + "bevy_state", + "bevy_time", + "bevy_utils", + "bevy_window", + "bytemuck", + "egui", + "fuzzy-matcher", + "image", + "pretty-type-name", + "smallvec", + "winit", +] + +[[package]] +name = "bevy-inspector-egui-derive" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f3be3ba88a25445c0c10684709b1ccd07e37f5f6b5d1b8dcf11d34548f1d61" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "bevy_a11y" version = "0.14.1" @@ -579,6 +641,28 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "bevy_egui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b8c164da1303ac3e6dc8d1a649be3e4399f12fd132f7665f3d018884236f9c" +dependencies = [ + "arboard", + "bevy", + "bytemuck", + "crossbeam-channel", + "egui", + "js-sys", + "log", + "thread_local", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webbrowser", + "wgpu-types", + "winit", +] + [[package]] name = "bevy_encase_derive" version = "0.14.1" @@ -818,6 +902,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "bevy_prototype_lyon" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a59b46da5bccc6d86c6047cfc81b8e9027f5b98e24b8721e8e1453c1d05371" +dependencies = [ + "bevy", + "lyon_algorithms", + "lyon_tessellation", + "svgtypes", +] + [[package]] name = "bevy_ptr" version = "0.14.1" @@ -1368,6 +1464,15 @@ dependencies = [ "libloading 0.8.5", ] +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1482,10 +1587,20 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.6" +name = "core-foundation" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -1494,7 +1609,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types", "libc", @@ -1507,7 +1622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -1643,12 +1758,43 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "ecolor" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775cfde491852059e386c4e1deb4aef381c617dc364184c6f6afee99b87c402b" +dependencies = [ + "bytemuck", + "emath", +] + +[[package]] +name = "egui" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53eafabcce0cb2325a59a98736efe0bf060585b437763f8c476957fb274bb974" +dependencies = [ + "ahash", + "emath", + "epaint", + "nohash-hasher", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "emath" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1fe0049ce51d0fb414d029e668dd72eb30bc2b739bf34296ed97bd33df544f3" +dependencies = [ + "bytemuck", +] + [[package]] name = "encase" version = "0.8.0" @@ -1681,6 +1827,28 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "epaint" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32af8da821bd4f43f2c137e295459ee2e1661d87ca8779dfa0eaf45d870e20f" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "epaint_default_fonts", + "nohash-hasher", + "parking_lot", +] + +[[package]] +name = "epaint_default_fonts" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "483440db0b7993cf77a20314f08311dbe95675092405518c0677aa08c151a3ea" + [[package]] name = "equivalent" version = "1.0.1" @@ -1707,6 +1875,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + [[package]] name = "euclid" version = "0.22.10" @@ -1801,6 +1975,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "fnv" version = "1.0.7" @@ -1834,6 +2014,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -1868,6 +2057,15 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + [[package]] name = "gethostname" version = "0.4.3" @@ -1910,7 +2108,7 @@ version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb5e8d912059b33b463831c16b838d15c4772d584ce332e4a80f6dffdae2bc1" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "inotify 0.10.2", "io-kit-sys", "js-sys", @@ -2137,6 +2335,36 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hexx" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c40cfb11c06c0b7051c2c0df030c57c65921db962ee2b8e89de218bb749f173" +dependencies = [ + "bevy_reflect", + "glam", + "serde", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.25.2" @@ -2147,6 +2375,7 @@ dependencies = [ "byteorder-lite", "num-traits", "png", + "tiff", ] [[package]] @@ -2261,6 +2490,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.69" @@ -2316,6 +2551,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2365,6 +2609,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.2" @@ -2414,6 +2664,48 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "lyon_algorithms" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3bca95f9a4955b3e4a821fbbcd5edfbd9be2a9a50bb5758173e5358bfb4c623" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edecfb8d234a2b0be031ab02ebcdd9f3b9ee418fb35e265f7a540a48d197bff9" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c08a606c7a59638d6c6aa18ac91a06aa9fb5f765a7efb27e6a4da58700740d7" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579d42360a4b09846eff2feef28f538696c7d6c7439bfa65874ff3cbe0951b2c" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + [[package]] name = "mach2" version = "0.4.2" @@ -2606,6 +2898,12 @@ dependencies = [ "libc", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -2692,6 +2990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -3137,6 +3436,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +[[package]] +name = "pretty-type-name" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f73cdaf19b52e6143685c3606206e114a4dfa969d6b14ec3894c88eb38bd4b" + [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -3452,6 +3757,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "slab" version = "0.4.9" @@ -3537,6 +3848,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20e16a0f46cf5fd675563ef54f26e83e20f2366bcf027bcb3cc3ed2b98aaf2ca" +[[package]] +name = "svgtypes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71499ff2d42f59d26edb21369a308ede691421f79ebc0f001e2b1fd3a7c9e52" +dependencies = [ + "kurbo", + "siphasher", +] + [[package]] name = "syn" version = "1.0.109" @@ -3597,9 +3918,12 @@ dependencies = [ [[package]] name = "the-labyrinth-of-echoes" -version = "0.0.1" +version = "0.0.4" dependencies = [ "bevy", + "bevy-inspector-egui", + "bevy_prototype_lyon", + "hexx", "log", "rand", "tracing", @@ -3635,6 +3959,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "tiny-skia" version = "0.11.4" @@ -3797,12 +4132,27 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -3821,6 +4171,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.10.0" @@ -4036,6 +4397,7 @@ checksum = "43676fe2daf68754ecf1d72026e4e6c15483198b5d24e888b74d3f22f887a148" dependencies = [ "dlib", "log", + "once_cell", "pkg-config", ] @@ -4059,6 +4421,30 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5f07fb9bc8de2ddfe6b24a71a75430673fd679e568c48b52716cef1cfae923" +dependencies = [ + "block2", + "core-foundation 0.10.0", + "home", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "wgpu" version = "0.20.1" @@ -4570,7 +4956,7 @@ dependencies = [ "calloop", "cfg_aliases 0.2.1", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", diff --git a/Cargo.toml b/Cargo.toml index 272de97..6d70049 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "the-labyrinth-of-echoes" authors = ["Kristofers Solo "] -version = "0.0.1" +version = "0.0.4" edition = "2021" [dependencies] @@ -17,6 +17,10 @@ tracing = { version = "0.1", features = [ "max_level_debug", "release_max_level_warn", ] } +hexx = { version = "0.18", features = ["bevy_reflect", "grid"] } +bevy_prototype_lyon = "0.12" +bevy-inspector-egui = { version = "0.27", optional = true } + [features] default = [ @@ -27,6 +31,7 @@ dev = [ # Improve compile times for dev builds by linking Bevy as a dynamic library. "bevy/dynamic_linking", "bevy/bevy_dev_tools", + "bevy-inspector-egui", ] dev_native = [ "dev", @@ -35,6 +40,7 @@ dev_native = [ # Enable embedded asset hot reloading for native dev builds. "bevy/embedded_watcher", ] +demo = [] # Idiomatic Bevy code often triggers these lints, and the CI workflow treats them as errors. diff --git a/justfile b/justfile new file mode 100644 index 0000000..56e184f --- /dev/null +++ b/justfile @@ -0,0 +1,20 @@ +# Default recipe +default: + @just --list + +# Run native dev +native-dev: + RUST_BACKTRACE=full cargo run + +# Run native release +native-release: + cargo run --release --no-default-features + +# Run web dev +web-dev: + RUST_BACKTRACE=full trunk serve + +# Run web release +web-release: + trunk serve --release --no-default-features + diff --git a/src/dev_tools.rs b/src/dev_tools.rs index 7f387db..1b78f7e 100644 --- a/src/dev_tools.rs +++ b/src/dev_tools.rs @@ -9,6 +9,8 @@ use bevy::{ prelude::*, }; +use bevy_inspector_egui::quick::WorldInspectorPlugin; + use crate::screens::Screen; pub(super) fn plugin(app: &mut App) { @@ -17,6 +19,7 @@ pub(super) fn plugin(app: &mut App) { // Toggle the debug overlay for UI. app.add_plugins(DebugUiPlugin); + app.add_plugins(WorldInspectorPlugin::default()); app.add_systems( Update, toggle_debug_ui.run_if(input_just_pressed(TOGGLE_KEY)), diff --git a/src/lib.rs b/src/lib.rs index ce0492c..be714f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,11 @@ mod asset_tracking; pub mod audio; +#[cfg(feature = "demo")] mod demo; #[cfg(feature = "dev")] mod dev_tools; +#[cfg(not(feature = "demo"))] +mod maze; mod screens; mod theme; @@ -37,7 +40,7 @@ impl Plugin for AppPlugin { }) .set(WindowPlugin { primary_window: Window { - title: "The Labyrinth Of Echoes".to_string(), + title: "The Labyrinth of Echoes".to_string(), canvas: Some("#bevy".to_string()), fit_canvas_to_parent: true, prevent_default_event_handling: true, @@ -48,7 +51,7 @@ impl Plugin for AppPlugin { }) .set(AudioPlugin { global_volume: GlobalVolume { - volume: Volume::new(0.3), + volume: Volume::new(0.), }, ..default() }), @@ -57,7 +60,10 @@ impl Plugin for AppPlugin { // Add other plugins. app.add_plugins(( asset_tracking::plugin, + #[cfg(feature = "demo")] demo::plugin, + #[cfg(not(feature = "demo"))] + maze::plugin::MazePlugin, screens::plugin, theme::plugin, )); @@ -84,7 +90,10 @@ enum AppSet { fn spawn_camera(mut commands: Commands) { commands.spawn(( Name::new("Camera"), - Camera2dBundle::default(), + Camera3dBundle { + transform: Transform::from_xyz(0., 300., 300.).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }, // Render all UI to this camera. // Not strictly necessary since we only use one camera, // but if we don't use this component, our UI will disappear as soon diff --git a/src/maze/grid.rs b/src/maze/grid.rs new file mode 100644 index 0000000..babf67b --- /dev/null +++ b/src/maze/grid.rs @@ -0,0 +1,220 @@ +use bevy::{ + color::palettes::css::{BLACK, GREEN, RED}, + pbr::wireframe::{WireframeConfig, WireframePlugin}, + prelude::*, + utils::hashbrown::HashMap, +}; +use bevy_prototype_lyon::{ + draw::{Fill, Stroke}, + entity::ShapeBundle, + path::PathBuilder, + plugin::ShapePlugin, +}; +use hexx::{EdgeDirection, Hex}; +use rand::{prelude::SliceRandom, rngs::ThreadRng, thread_rng}; + +use super::{ + resource::{Layout, MazeConfig, HEX_SIZE}, + tile::{Tile, TileBundle, Walls}, +}; + +pub(super) fn plugin(app: &mut App) { + app.add_plugins((ShapePlugin, WireframePlugin)); + app.init_resource::(); + app.init_resource::(); + app.insert_resource(WireframeConfig { + global: false, + ..default() + }); +} + +pub(super) fn _spawn_hex_grid(mut commands: Commands, config: Res) { + let radius = config.radius as i32; + + for q in -radius..=radius { + let r1 = (-radius).max(-q - radius); + let r2 = radius.min(-q + radius); + for r in r1..=r2 { + let tile = Tile::new(q, r); + commands.spawn(( + Name::new(format!("Tile {}", &tile.to_string())), + TileBundle { + hex: tile, + ..default() + }, + )); + } + } +} + +pub(super) fn _generate_maze( + mut commands: Commands, + query: Query<(Entity, &Tile, &Walls)>, + config: Res, +) { + let mut tiles = query + .into_iter() + .map(|(entity, tile, walls)| (tile.hex, (entity, tile.clone(), walls.clone()))) + .collect(); + + let mut rng = thread_rng(); + _recursive_maze(&mut tiles, config.start_pos, &mut rng); + + for (entity, tile, walls) in tiles.values() { + commands + .entity(*entity) + .insert(tile.clone()) + .insert(walls.clone()); + } +} + +fn _recursive_maze( + tiles: &mut HashMap, + current_hex: Hex, + rng: &mut ThreadRng, +) { + { + let (_, tile, _) = tiles.get_mut(¤t_hex).unwrap(); + tile._visit(); + } + + let mut directions = EdgeDirection::ALL_DIRECTIONS; + directions.shuffle(rng); + + for direction in directions.into_iter() { + let neighbor_hex = current_hex + direction; + if let Some((_, neighbor_tile, _)) = tiles.get(&neighbor_hex) { + if !neighbor_tile.visited { + _remove_wall_between(tiles, current_hex, neighbor_hex, direction); + _recursive_maze(tiles, neighbor_hex, rng); + } + } + } +} + +fn _remove_wall_between( + tiles: &mut HashMap, + current_hex: Hex, + neighbor_hex: Hex, + direction: EdgeDirection, +) { + { + let (_, _, walls) = tiles.get_mut(¤t_hex).unwrap(); + walls.0[direction.index() as usize] = false; + } + { + let (_, _, walls) = tiles.get_mut(&neighbor_hex).unwrap(); + walls.0[direction.const_neg().index() as usize] = false; + } +} + +fn _add_hex_tile( + commands: &mut Commands, + position: Vec3, + size: f32, + tile: &Tile, + walls: &Walls, + fill_color: Color, + layout: &Layout, +) { + let hex_points = tile + .hex + .all_vertices() + .into_iter() + .map(|v| { + let mut layout = layout.clone(); + layout.origin = position.xy(); + layout.hex_size = Vec2::splat(size); + layout.hex_to_world_pos(v.origin + v.direction) + }) + .collect::>(); + + let mut path_builder = PathBuilder::new(); + path_builder.move_to(hex_points[0]); + for point in &hex_points[1..] { + path_builder.line_to(*point); + } + path_builder.close(); + let hexagon = path_builder.build(); + + // Create the hexagon fill + commands + .spawn(( + ShapeBundle { + path: hexagon, + spatial: SpatialBundle { + transform: Transform::from_xyz(position.x, position.y, 0.), + ..default() + }, + ..default() + }, + Fill::color(fill_color), + )) + .with_children(|p| { + p.spawn(Text2dBundle { + text: Text { + sections: vec![TextSection { + value: tile.to_string(), + style: TextStyle { + font_size: 16., + color: Color::BLACK, + ..default() + }, + }], + ..default() + }, + transform: Transform::from_xyz(position.x * 2., position.y * 2., 1.), + ..default() + }); + }); + + // Draw walls + for direction in EdgeDirection::iter() { + let idx = direction.index() as usize; + if walls[idx] { + let start = hex_points[idx]; + let end = hex_points[(idx + 1) % 6]; + let mut line_builder = PathBuilder::new(); + line_builder.move_to(start); + line_builder.line_to(end); + let line = line_builder.build(); + + commands.spawn(( + ShapeBundle { + path: line, + spatial: SpatialBundle { + transform: Transform::from_xyz(position.x, position.y, 1.), + ..default() + }, + ..default() + }, + Stroke::new(BLACK, 2.), + )); + } + } +} + +pub(super) fn _render_maze( + mut commands: Commands, + query: Query<(&Tile, &mut Walls)>, + layout: Res, + config: Res, +) { + for (tile, walls) in query.iter() { + let world_pos = layout.hex_to_world_pos(tile.hex).extend(0.); + let fill_color = match tile.hex { + pos if pos == config.start_pos => GREEN.into(), + pos if pos == config.end_pos => RED.into(), + _ => Color::srgb(0.8, 0.8, 0.8), + }; + _add_hex_tile( + &mut commands, + world_pos, + HEX_SIZE, + tile, + walls, + fill_color, + &layout, + ); + } +} diff --git a/src/maze/mod.rs b/src/maze/mod.rs new file mode 100644 index 0000000..c3266d1 --- /dev/null +++ b/src/maze/mod.rs @@ -0,0 +1,11 @@ +use bevy::{ecs::world::Command, prelude::*}; +use plugin::MazePlugin; +pub mod grid; +pub mod plugin; +pub mod prism; +pub mod resource; +pub mod tile; + +pub fn spawn_grid(world: &mut World) { + MazePlugin.apply(world); +} diff --git a/src/maze/plugin.rs b/src/maze/plugin.rs new file mode 100644 index 0000000..d59f509 --- /dev/null +++ b/src/maze/plugin.rs @@ -0,0 +1,29 @@ +use bevy::{ + ecs::{system::RunSystemOnce, world::Command}, + prelude::*, +}; + +use super::{grid, prism}; + +#[derive(Default)] +pub(crate) struct MazePlugin; + +impl Plugin for MazePlugin { + fn build(&self, app: &mut App) { + app.add_plugins(prism::plugin); + app.add_plugins(grid::plugin); + // app.insert_resource(AmbientLight { + // brightness: f32::MAX, + // color: Color::WHITE, + // }); + } +} + +impl Command for MazePlugin { + fn apply(self, world: &mut World) { + // world.run_system_once(spawn_hex_grid); + // world.run_system_once(generate_maze); + // world.run_system_once(render_maze); + world.run_system_once(prism::setup); + } +} diff --git a/src/maze/prism.rs b/src/maze/prism.rs new file mode 100644 index 0000000..0310a0a --- /dev/null +++ b/src/maze/prism.rs @@ -0,0 +1,146 @@ +use bevy::prelude::*; +use core::f32; +use std::f32::consts::{FRAC_PI_2, FRAC_PI_3}; + +use super::{ + resource::{Layout, MazeConfig, HEX_SIZE}, + tile::Tile, +}; + +pub(super) fn plugin(_app: &mut App) {} +const WALL_SIZE: f32 = 1.0; + +pub(super) fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + config: Res, + layout: Res, +) { + let radius = config.radius as i32; + + let assets = create_base_assets(&mut meshes, &mut materials, &config); + // spawn_single_hex_tile(&mut commands, &assets, &config); + commands + .spawn(( + Name::new("Floor"), + SpatialBundle { + transform: Transform::from_translation(Vec3::ZERO), + ..default() + }, + )) + .with_children(|parent| { + for q in -radius..=radius { + let r1 = (-radius).max(-q - radius); + let r2 = radius.min(-q + radius); + for r in r1..=r2 { + let tile = Tile::new(q, r); + spawn_single_hex_tile(parent, &tile, &layout, &assets, &config); + } + } + }); +} + +fn spawn_single_hex_tile( + parent: &mut ChildBuilder, + tile: &Tile, + layout: &Res, + assets: &MazeAssets, + config: &Res, +) { + let pos = tile.to_vec3(layout); + parent + .spawn(( + Name::new(format!("Hex {}", &tile.to_string())), + PbrBundle { + mesh: assets.hex_mesh.clone(), + material: assets.hex_material.clone(), + transform: Transform::from_translation(pos), + ..default() + }, + )) + .with_children(|parent| spawn_walls(parent, assets, config)); +} + +fn spawn_walls(parent: &mut ChildBuilder, asstets: &MazeAssets, config: &Res) { + let y_offset = config.height / 2.; + let z_rotation = Quat::from_rotation_z(-FRAC_PI_2); + + for i in 0..6 { + let wall_angle = FRAC_PI_3 * i as f32; + + let x_offset = (HEX_SIZE - WALL_SIZE) * f32::cos(wall_angle); + let z_offset = (HEX_SIZE - WALL_SIZE) * f32::sin(wall_angle); + let pos = Vec3::new(x_offset, y_offset, z_offset); + + let x_rotation = Quat::from_rotation_x(wall_angle + FRAC_PI_2); + let final_rotation = z_rotation * x_rotation; + + spawn_single_wall(parent, asstets, final_rotation, pos); + } +} + +fn spawn_single_wall( + parent: &mut ChildBuilder, + asstets: &MazeAssets, + rotation: Quat, + offset: Vec3, +) { + parent.spawn(( + Name::new("Wall"), + PbrBundle { + mesh: asstets.wall_mesh.clone(), + material: asstets.wall_material.clone(), + transform: Transform::from_translation(offset).with_rotation(rotation), + ..default() + }, + )); +} + +fn create_base_assets( + meshes: &mut ResMut>, + materials: &mut ResMut>, + config: &Res, +) -> MazeAssets { + MazeAssets { + hex_mesh: meshes.add(generate_hex_mesh(HEX_SIZE, config.height)), + wall_mesh: meshes.add(generate_square_mesh(HEX_SIZE)), + hex_material: materials.add(white_material()), + wall_material: materials.add(Color::BLACK), + } +} + +fn generate_hex_mesh(radius: f32, depth: f32) -> Mesh { + let hexagon = RegularPolygon { + sides: 6, + circumcircle: Circle::new(radius), + }; + let prism_shape = Extrusion::new(hexagon, depth); + let rotation = Quat::from_rotation_x(FRAC_PI_2); + + Mesh::from(prism_shape).rotated_by(rotation) +} + +fn generate_square_mesh(depth: f32) -> Mesh { + let square = Rectangle::new(WALL_SIZE, WALL_SIZE); + let rectangular_prism = Extrusion::new(square, depth); + let rotation = Quat::from_rotation_x(FRAC_PI_2); + + Mesh::from(rectangular_prism).rotated_by(rotation) +} + +fn white_material() -> StandardMaterial { + let val = 10.; + StandardMaterial { + base_color: Color::WHITE, + emissive: LinearRgba::new(val, val, val, val), + ..default() + } +} + +struct MazeAssets { + hex_mesh: Handle, + wall_mesh: Handle, + hex_material: Handle, + wall_material: Handle, +} diff --git a/src/maze/resource.rs b/src/maze/resource.rs new file mode 100644 index 0000000..a3c5326 --- /dev/null +++ b/src/maze/resource.rs @@ -0,0 +1,51 @@ +use bevy::prelude::*; +use hexx::{Hex, HexLayout, HexOrientation}; +use rand::{thread_rng, Rng}; + +pub(crate) const HEX_SIZE: f32 = 6.; + +#[derive(Debug, Reflect, Resource)] +#[reflect(Resource)] +pub struct MazeConfig { + pub radius: u32, + pub height: f32, + pub start_pos: Hex, + pub end_pos: Hex, +} + +impl Default for MazeConfig { + fn default() -> Self { + let mut rng = thread_rng(); + let radius = 11; + let start_pos = Hex::new( + rng.gen_range(-radius..radius), + rng.gen_range(-radius..radius), + ); + let end_pos = Hex::new( + rng.gen_range(-radius..radius), + rng.gen_range(-radius..radius), + ); + debug!("Start pos: ({},{})", start_pos.x, start_pos.y); + debug!("End pos: ({},{})", end_pos.x, end_pos.y); + Self { + radius: radius as u32, + height: 20., + start_pos, + end_pos, + } + } +} + +#[derive(Debug, Reflect, Resource, Deref, DerefMut, Clone)] +#[reflect(Resource)] +pub struct Layout(pub HexLayout); + +impl FromWorld for Layout { + fn from_world(_world: &mut World) -> Self { + Self(HexLayout { + orientation: HexOrientation::Pointy, + hex_size: Vec2::splat(HEX_SIZE), + ..default() + }) + } +} diff --git a/src/maze/tile.rs b/src/maze/tile.rs new file mode 100644 index 0000000..7c75af7 --- /dev/null +++ b/src/maze/tile.rs @@ -0,0 +1,55 @@ +use std::fmt::Display; + +use bevy::prelude::*; +use hexx::{Hex, HexLayout}; + +#[derive(Debug, Reflect, Component, Default, PartialEq, Eq, Hash, Clone)] +#[reflect(Component)] +pub struct Tile { + pub hex: Hex, + pub visited: bool, +} + +#[derive(Debug, Reflect, Component, Deref, DerefMut, Clone)] +#[reflect(Component)] +pub struct Walls(pub [bool; 6]); + +#[derive(Debug, Reflect, Bundle, Default)] +pub struct TileBundle { + pub hex: Tile, + pub walls: Walls, +} + +impl Tile { + pub fn new(q: i32, r: i32) -> Self { + Self { + hex: Hex::new(q, r), + visited: false, + } + } + + pub fn _visit(&mut self) { + self.visited = true; + } + + pub fn to_vec2(&self, layout: &HexLayout) -> Vec2 { + layout.hex_to_world_pos(self.hex) + } + + pub fn to_vec3(&self, layout: &HexLayout) -> Vec3 { + let pos = self.to_vec2(layout); + Vec3::new(pos.x, 0., pos.y) + } +} + +impl Display for Tile { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({},{})", self.hex.x, self.hex.y) + } +} + +impl Default for Walls { + fn default() -> Self { + Self([true; 6]) + } +} diff --git a/src/screens/gameplay.rs b/src/screens/gameplay.rs index 0640462..f932d17 100644 --- a/src/screens/gameplay.rs +++ b/src/screens/gameplay.rs @@ -2,10 +2,11 @@ use bevy::{input::common_conditions::input_just_pressed, prelude::*}; -use crate::{ - asset_tracking::LoadResource, audio::Music, demo::level::spawn_level as spawn_level_command, - screens::Screen, -}; +#[cfg(feature = "demo")] +use crate::demo::level::spawn_level as spawn_level_command; +#[cfg(not(feature = "demo"))] +use crate::maze::spawn_grid as spawn_level_command; +use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen}; pub(super) fn plugin(app: &mut App) { app.add_systems(OnEnter(Screen::Gameplay), spawn_level); diff --git a/src/screens/loading.rs b/src/screens/loading.rs index e6eb5ca..1f2b3aa 100644 --- a/src/screens/loading.rs +++ b/src/screens/loading.rs @@ -4,7 +4,6 @@ use bevy::prelude::*; use crate::{ - demo::player::PlayerAssets, screens::{credits::CreditsMusic, gameplay::GameplayMusic, Screen}, theme::{interaction::InteractionAssets, prelude::*}, }; @@ -35,13 +34,9 @@ fn continue_to_title_screen(mut next_screen: ResMut>) { } fn all_assets_loaded( - player_assets: Option>, interaction_assets: Option>, credits_music: Option>, gameplay_music: Option>, ) -> bool { - player_assets.is_some() - && interaction_assets.is_some() - && credits_music.is_some() - && gameplay_music.is_some() + interaction_assets.is_some() && credits_music.is_some() && gameplay_music.is_some() } diff --git a/src/screens/mod.rs b/src/screens/mod.rs index 49bdc53..13606fd 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -24,8 +24,9 @@ pub(super) fn plugin(app: &mut App) { /// The game's main screen states. #[derive(States, Debug, Hash, PartialEq, Eq, Clone, Default)] pub enum Screen { - #[default] + #[cfg_attr(not(feature = "dev"), default)] Splash, + #[cfg_attr(feature = "dev", default)] Loading, Title, Credits,