Compare commits
No commits in common. "f75da62ef9f2d5c620e352f9a4a842764ac388f5" and "c59f8242c92abd2eb9643185825c6c2520eeb394" have entirely different histories.
f75da62ef9
...
c59f8242c9
37
CHANGELOG.md
|
@ -1,40 +1,3 @@
|
|||
# git
|
||||
|
||||
- Implement cruise control
|
||||
- New logo: a white O, diagonal ring around it, and neon pink glow
|
||||
- New dashboard icons: rotation stabiliser, radioactivity warning
|
||||
|
||||
# v0.9.0
|
||||
|
||||
- Implement game menu
|
||||
- Implement achievements
|
||||
- Implement death screen, showing cause of death and death poem
|
||||
- Add better textures for Venus, Io, Europa, Ganymede, Callisto
|
||||
|
||||
# v0.8.5
|
||||
|
||||
- Implement flashlight
|
||||
- Implement power drain (for flashlight only, for now)
|
||||
- Redesign HUD with fallout4esque bars and car dashboard warning lights
|
||||
- Add a more balanced cruising vehicle near starting position
|
||||
- Add much better Jupiter texture
|
||||
- Fix collision sounds
|
||||
|
||||
# v0.8.4
|
||||
|
||||
- Fix star positions in map when zooming far out and moving around
|
||||
|
||||
# v0.8.3
|
||||
|
||||
- Implement "outfly.toml" configuration file
|
||||
- Implement dynamic planet/moon locations based on real time
|
||||
- Change hud color to neon red/pink
|
||||
- Add textures for the remaining 7 planets
|
||||
- Add point-of-interest markers for moons/planets
|
||||
- Add work-in-progress settlement in hollow asteroid
|
||||
- Fix jitter glitch in map mode when targeting far away objects
|
||||
- Remove point lights of bus stations for performance reasons
|
||||
|
||||
# v0.8.2
|
||||
|
||||
- Add speedometer and bigger reticule
|
||||
|
|
178
Cargo.lock
generated
|
@ -291,7 +291,8 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
|||
[[package]]
|
||||
name = "bevy"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65b9eadaacf8fe971331bc3f250f35c18bc9dace3f96b483062f38ac07e3a1b4"
|
||||
dependencies = [
|
||||
"bevy_dylib",
|
||||
"bevy_internal",
|
||||
|
@ -300,7 +301,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_a11y"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd8ef2795f7f5c816a4eda04834083eb5a92e8fef603bc21d2091c6e3b63621a"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"bevy_app",
|
||||
|
@ -311,14 +313,15 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_animation"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e553d68bc937586010ed2194ac66b751bc6238cf622b3ed5a86f4e1581e94509"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
"bevy_core",
|
||||
"bevy_ecs",
|
||||
"bevy_hierarchy",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_time",
|
||||
|
@ -329,7 +332,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_app"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab348a32e46d21c5d61794294a92d415a770d26c7ba8951830b127b40b53ccc4"
|
||||
dependencies = [
|
||||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
|
@ -344,7 +348,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_asset"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50028e0d4f28a9f6aab48f61b688ba2793141188f88cdc9aa6c2bca2cc02ad35"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-fs",
|
||||
|
@ -376,7 +381,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_asset_macros"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6617475908368418d815360148fdbb82f879dc255a70d2d7baa3766f0cd4bfd7"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
|
@ -387,13 +393,14 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_audio"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0f12495e230cd5cf59c6051cdd820c97d7fe4f0597d4d9c3240c62e9c65b485"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_transform",
|
||||
"bevy_utils",
|
||||
|
@ -403,11 +410,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_core"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12b0042f241ba7cd61487aadd8addfb56f7eeb662d713ac1577026704508fc6c"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_tasks",
|
||||
"bevy_utils",
|
||||
|
@ -417,7 +425,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_core_pipeline"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48b7a471cb8ba665f12f7a167faa5566c11386f5bfc77d2e10bfde22b179f7b3"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
|
@ -425,7 +434,7 @@ dependencies = [
|
|||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_transform",
|
||||
|
@ -438,7 +447,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_derive"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0e01f8343f391e2d6a63b368b82fb5b252ed43c8713fc87f9a8f2d59407dd00"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"quote",
|
||||
|
@ -448,7 +458,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_diagnostic"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1401cdccec7e49378d013dfb0ff62c251f85b3be19dcdf04cfd827f793d1ee9"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
|
@ -463,7 +474,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_dylib"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "922826e3b8f37c19836b49e18ceca662260cce87ab8faa4db6df8433903660cc"
|
||||
dependencies = [
|
||||
"bevy_internal",
|
||||
]
|
||||
|
@ -471,7 +483,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_ecs"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98e612a8e7962ead849e370f3a7e972b88df879ced05cd9dad6a0286d14650cf"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"bevy_ecs_macros",
|
||||
|
@ -490,7 +503,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_ecs_macros"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "807b5106c3410e58f4f523b55ea3c071e2a09e31e9510f3c22021c6a04732b5b"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
|
@ -501,7 +515,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_embedded_assets"
|
||||
version = "0.10.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy_embedded_assets.git?rev=bb925e7e5373c742c01e6e7aff04e92fdc07c095#bb925e7e5373c742c01e6e7aff04e92fdc07c095"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a4b0bfcdcbd0c59829415ae0756757d50dfdb0c8f324087b4a2daabb3971fbd"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"cargo-emit",
|
||||
|
@ -512,7 +527,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_encase_derive"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "887087a5e522d9f20733a84dd7e6e9ca04cd8fdfac659220ed87d675eebc83a7"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"encase_derive_impl",
|
||||
|
@ -521,7 +537,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_gizmos"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054df3550a9d423a961de65b459946ff23304f97f25af8a62c23f4259db8506d"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
|
@ -530,7 +547,7 @@ dependencies = [
|
|||
"bevy_ecs",
|
||||
"bevy_gizmos_macros",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_pbr",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
|
@ -542,7 +559,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_gizmos_macros"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abdcaf74d8cd34aa5c3293527e7a012826840886ad3496c1b963ed8b66b1619f"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
|
@ -553,7 +571,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_gltf"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21ecf404295055deb7fe037495891bc135ca10d46bc5b6c55f9ab7b7ebc61d31"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bevy_animation",
|
||||
|
@ -564,7 +583,7 @@ dependencies = [
|
|||
"bevy_ecs",
|
||||
"bevy_hierarchy",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_pbr",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
|
@ -582,7 +601,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_hierarchy"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbb3dfad24866a6713dafa3065a91c5cf5e355f6e1b191c25d704ae54185246c"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_core",
|
||||
|
@ -595,11 +615,12 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_input"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47f2b2b3df168c6ef661d25e09abf5bd4fecaacd400f27e5db650df1c3fa3a3b"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_utils",
|
||||
"smol_str",
|
||||
|
@ -609,7 +630,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_internal"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f58ec0ce77603df9474cde61f429126bfe06eb79094440e9141afb4217751c79"
|
||||
dependencies = [
|
||||
"bevy_a11y",
|
||||
"bevy_animation",
|
||||
|
@ -626,7 +648,7 @@ dependencies = [
|
|||
"bevy_hierarchy",
|
||||
"bevy_input",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_pbr",
|
||||
"bevy_ptr",
|
||||
"bevy_reflect",
|
||||
|
@ -646,7 +668,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_log"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5eea6c527fd828b7fef8d0f518167f27f405b904a16f227b644687d3f46a809"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"bevy_app",
|
||||
|
@ -661,7 +684,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_macro_utils"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb270c98a96243b29465139ed10bda2f675d00a11904f6588a5f7fc4774119c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -675,14 +699,6 @@ name = "bevy_math"
|
|||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f06daa26ffb82d90ba772256c0ba286f6c305c392f6976c9822717974805837c"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bevy_math"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"serde",
|
||||
|
@ -691,7 +707,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_mikktspace"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d7ef7f2a826d0b19f059035831ce00a5e930435cc53c61e045773d0483f67a"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
@ -699,14 +716,15 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_pbr"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b29c80269fa6db55c9e33701edd3ecb73d8866ca8cb814d49a9d3fb72531b6"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
"bevy_core_pipeline",
|
||||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_transform",
|
||||
|
@ -723,14 +741,16 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_ptr"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8050e2869fe341db6874203b5a01ff12673807a2c7c80cb829f6c7bea6997268"
|
||||
|
||||
[[package]]
|
||||
name = "bevy_reflect"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccbd7de21d586457a340a0962ad0747dc5098ff925eb6b27a918c4bdd8252f7b"
|
||||
dependencies = [
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_ptr",
|
||||
"bevy_reflect_derive",
|
||||
"bevy_utils",
|
||||
|
@ -745,7 +765,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_reflect_derive"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce33051bd49036d4a5a62aa3f2068672ec55f3ebe92aa0d003a341f15cc37ac"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
|
@ -757,7 +778,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_render"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b2c4b644c739c0b474b6f8f7b0bc68ac13d83b59688781e9a7753c52780177"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"bevy_app",
|
||||
|
@ -768,7 +790,7 @@ dependencies = [
|
|||
"bevy_encase_derive",
|
||||
"bevy_hierarchy",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_mikktspace",
|
||||
"bevy_reflect",
|
||||
"bevy_render_macros",
|
||||
|
@ -801,7 +823,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_render_macros"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "720b88406e786e378829b7d43c1ffb5300186912b99904d0d4d8ec6698a4f210"
|
||||
dependencies = [
|
||||
"bevy_macro_utils",
|
||||
"proc-macro2",
|
||||
|
@ -812,7 +835,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_scene"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3d2caa1bfe7542dbe2c62e1bcc10791ba181fb744d2fe6711d1d373354da7c"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
|
@ -831,7 +855,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_sprite"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cad1b555161f50e5d62b7fdf7ebeef1b24338aae7a88e51985da9553cd60ddf"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
|
@ -839,7 +864,7 @@ dependencies = [
|
|||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_transform",
|
||||
|
@ -856,7 +881,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_tasks"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f07fcc4969b357de143509925b39c9a2c56eaa8750828d97f319ca9ed41897cb"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-executor",
|
||||
|
@ -869,13 +895,14 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_text"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4e8456ae0bea7d6b7621e42c1c12bf66c0891381e62c948ab23920673ce611c"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"bevy_app",
|
||||
"bevy_asset",
|
||||
"bevy_ecs",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_sprite",
|
||||
|
@ -890,7 +917,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_time"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38ea5ae9fe7f56f555dbb05a88d34931907873e3f0c7dc426591839eef72fe3e"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
|
@ -903,12 +931,13 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_transform"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0d51a1f332cc00939d2f19ed6b909e5ed7037e39c7e25cc86930d79d432163e"
|
||||
dependencies = [
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"bevy_hierarchy",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -916,7 +945,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_ui"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bbc30be39cfbfa3a073b541d22aea43ab14452dea12d7411ce201df17ff7b1"
|
||||
dependencies = [
|
||||
"bevy_a11y",
|
||||
"bevy_app",
|
||||
|
@ -927,7 +957,7 @@ dependencies = [
|
|||
"bevy_hierarchy",
|
||||
"bevy_input",
|
||||
"bevy_log",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_render",
|
||||
"bevy_sprite",
|
||||
|
@ -943,7 +973,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_utils"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a9f845a985c00e0ee8dc2d8af3f417be925fb52aad4bda5b96e2e58a2b4d2eb"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bevy_utils_proc_macros",
|
||||
|
@ -961,7 +992,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_utils_proc_macros"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef158627f30503d5c18c20c60b444829f698d343516eeaf6eeee078c9a45163"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -971,13 +1003,14 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_window"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976202d2ed838176595b550ac654b15ae236e0178a6f19a94ca6d58f2a96ca60"
|
||||
dependencies = [
|
||||
"bevy_a11y",
|
||||
"bevy_app",
|
||||
"bevy_ecs",
|
||||
"bevy_input",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_reflect",
|
||||
"bevy_utils",
|
||||
"raw-window-handle",
|
||||
|
@ -987,7 +1020,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_winit"
|
||||
version = "0.13.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa66539aa93d8522b146bf82de429714ea6370a6061fc1f1ff7bcacd4e64c6c4"
|
||||
dependencies = [
|
||||
"accesskit_winit",
|
||||
"approx",
|
||||
|
@ -997,7 +1031,7 @@ dependencies = [
|
|||
"bevy_ecs",
|
||||
"bevy_hierarchy",
|
||||
"bevy_input",
|
||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
||||
"bevy_math",
|
||||
"bevy_tasks",
|
||||
"bevy_utils",
|
||||
"bevy_window",
|
||||
|
@ -1011,10 +1045,11 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_xpbd_3d"
|
||||
version = "0.4.2"
|
||||
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912#99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0425ea7361b9b27c2a382e0663deb42f41147eee60fb2b3d5fa7e42d363ea848"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_math 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bevy_math",
|
||||
"bevy_xpbd_derive",
|
||||
"derive_more",
|
||||
"fxhash",
|
||||
|
@ -1028,7 +1063,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "bevy_xpbd_derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912#99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e1ef1d5e328abe1b76df974245f78e17fd17867583883d5e77444c6a8223a64"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.52",
|
||||
|
@ -2767,7 +2803,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "outfly"
|
||||
version = "0.9.0"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"bevy",
|
||||
"bevy_embedded_assets",
|
||||
|
|
43
Cargo.toml
|
@ -10,7 +10,7 @@
|
|||
|
||||
[package]
|
||||
name = "outfly"
|
||||
version = "0.9.0"
|
||||
version = "0.8.2"
|
||||
edition = "2021"
|
||||
homepage = "https://codeberg.org/hut/outfly"
|
||||
repository = "https://codeberg.org/hut/outfly"
|
||||
|
@ -23,6 +23,15 @@ rust-version = "1.76.0"
|
|||
# For parsing the game definition file, src/data/defs.txt
|
||||
regex = "1"
|
||||
|
||||
# The bevy game engine, the basis for this game
|
||||
bevy = { version = "0.13.2", default-features = false, features = ["animation", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "jpeg", "multi-threaded", "png", "tonemapping_luts", "vorbis"]}
|
||||
|
||||
# For physics and collision handling
|
||||
bevy_xpbd_3d = { version = "0.4.2", default-features = false, features = ["3d", "f64", "parry-f64", "parallel", "async-collider"] }
|
||||
|
||||
# For embedding assets into the binary, creating a self-sufficient executable
|
||||
bevy_embedded_assets = { version = "0.10.2", optional = true }
|
||||
|
||||
# For seeded pseudo-random procedural generation of asteroids
|
||||
fastrand = "2.0"
|
||||
|
||||
|
@ -33,42 +42,14 @@ serde_yaml = "0.9"
|
|||
# For reading/writing the player's configuration file.
|
||||
toml_edit = { version = "0.22", features = ["serde"] }
|
||||
|
||||
[dependencies.bevy]
|
||||
# The bevy game engine, the basis for this game
|
||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||
version = "0.13.2"
|
||||
git = "https://codeberg.org/outfly/bevy.git"
|
||||
rev = "2076f102be15a88a5026e128851a8ee1ae9d8960"
|
||||
default-features = false
|
||||
features = ["animation", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit", "bevy_core_pipeline", "bevy_pbr", "bevy_gltf", "bevy_render", "bevy_text", "bevy_ui", "jpeg", "multi-threaded", "png", "tonemapping_luts", "vorbis"]
|
||||
|
||||
[dependencies.bevy_embedded_assets]
|
||||
# For embedding assets into the binary, creating a self-sufficient executable
|
||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||
version = "0.10.2"
|
||||
git = "https://codeberg.org/outfly/bevy_embedded_assets.git"
|
||||
rev = "bb925e7e5373c742c01e6e7aff04e92fdc07c095"
|
||||
optional = true
|
||||
|
||||
[dependencies.bevy_xpbd_3d]
|
||||
# For physics and collision handling
|
||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||
version = "0.4.2"
|
||||
git = "https://codeberg.org/outfly/bevy_xpbd.git"
|
||||
rev = "99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
||||
default-features = false
|
||||
features = ["3d", "f64", "parry-f64", "parallel", "async-collider"]
|
||||
|
||||
[build-dependencies]
|
||||
# NOTE: even though we use embed-resource for windows only, we can't move it into
|
||||
# a [target[...]build-dependencies] block because in case of cross-compiling, the
|
||||
# build.rs will be compiled for a different, non-windows target than the main executable.
|
||||
embed-resource = "1.6.3" # embedding of .exe metadata
|
||||
|
||||
[features]
|
||||
default = ["x11", "embed_assets"]
|
||||
mute_music = []
|
||||
dev_mode = []
|
||||
dev = ["dev_mode", "bevy/dynamic_linking", "bevy/file_watcher"]
|
||||
dev = ["dev_mode", "mute_music", "bevy/dynamic_linking", "bevy/file_watcher"]
|
||||
release_linux = ["x11", "wayland", "embed_assets"]
|
||||
release_windows = ["embed_assets"]
|
||||
wasm = ["bevy/webgl2"]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
This directory contains scripts and data files for building outfly for various operating systems.
|
||||
|
||||
# Dev Features
|
||||
|
||||
For development, it's recommended to use `--features dev`:
|
||||
|
@ -8,7 +6,7 @@ For development, it's recommended to use `--features dev`:
|
|||
cargo [run|build] --features dev
|
||||
```
|
||||
|
||||
This enables the following, but ONLY if you run it with `cargo run`:
|
||||
This enables the following:
|
||||
|
||||
- Mutes music by default (you can still unmute it)
|
||||
- Enables "dev mode", which changes the game slightly:
|
||||
|
@ -20,7 +18,7 @@ This enables the following, but ONLY if you run it with `cargo run`:
|
|||
|
||||
# pack.sh
|
||||
|
||||
The [pack.sh](build/pack.sh) script is used by the developer team to compile and pack release binaries into official packages.
|
||||
The [pack.sh](src/build/pack.sh) script is used by the developer team to compile and pack release binaries into official packages.
|
||||
|
||||
It could serve as a starting point for package maintainers or tinkerers.
|
||||
|
||||
|
@ -90,7 +88,7 @@ python -m http.server -d wasm
|
|||
|
||||
## Building release versions optimized for packaging
|
||||
|
||||
To build release versions optimized for final deployment, build with the following features: (see also [pack.sh](https://codeberg.org/hut/outfly/src/branch/main/build/pack.sh))
|
||||
To build release versions optimized for final deployment, build with the following features: (see also [pack.sh](https://codeberg.org/hut/outfly/src/branch/main/src/build/pack.sh))
|
||||
|
||||
```
|
||||
cargo build --release --no-default-features --features release_[linux|windows] [--target=$YOUR_TARGET]
|
22
LICENSE.md
|
@ -3,26 +3,7 @@
|
|||
- Source code: GPL Version 3.0
|
||||
- https://codeberg.org/hut/outfly
|
||||
- 3D models: Original art, placed under the Creative Commons CC0 License
|
||||
- Photographs of celestial bodies:
|
||||
- Mercury: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
||||
- Venus: [By Björn Jónsson, free to use, with attribution](https://bjj.mmedia.is/data/venus/venus.html)
|
||||
- To reduce the visibility of the ultraviolet features in Björn's original image, I applied GIMP's "Colors"→"Hue-Saturation" with Lightness=100 and Saturation=-20.
|
||||
- Exported as 80% quality JPEG
|
||||
- Earth: A simple addition of
|
||||
- [base image, by NASA, public domain](https://visibleearth.nasa.gov/images/74318/april-blue-marble-next-generation-w-topography)
|
||||
- [clouds, by NASA, public domain](https://visibleearth.nasa.gov/images/57747/blue-marble-clouds)
|
||||
- Mars: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
||||
- Jupiter: [By Björn Jónsson, CC BY 3.0](https://www.planetary.org/space-images/merged-cassini-and-juno)
|
||||
- Downscaled by 2x with LoHalo interpolation
|
||||
- Adjusted contrast with GIMP's "Colors"→"Curves", by pulling x=128/y=128 down to y=96.
|
||||
- Exported as 80% quality JPEG
|
||||
- Io: [By Björn Jónsson, free to use, with attribution](https://bjj.mmedia.is/data/io/io.html)
|
||||
- Europa: [By Björn Jónsson, free to use, with attribution](https://www.planetary.org/articles/0218-mapping-europa)
|
||||
- Ganymede: [By Björn Jónsson, free to use, with attribution](https://bjj.mmedia.is/data/ganymede/index.html)
|
||||
- Callisto: [By Björn Jónsson, free to use, with attribution](https://bjj.mmedia.is/data/callisto/index.html)
|
||||
- Saturn: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
||||
- Uranus: [By Askaniy, CC BY-SA 3.0](https://www.deviantart.com/askaniy/art/Uranus-Texture-Map-763551816)
|
||||
- Neptune: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
||||
- Photographs of celestial bodies: By NASA, public domain
|
||||
- Icon: Creative Commons CC0 License
|
||||
- Original sound files:
|
||||
- wakeup.ogg: Creative Commons CC0 License
|
||||
|
@ -40,7 +21,6 @@
|
|||
- https://pixabay.com/sound-effects/electric-fan-motor-blades-removed-13169
|
||||
- https://pixabay.com/sound-effects/whoosh-blow-flutter-shortwav-14678/
|
||||
- https://pixabay.com/sound-effects/dslr-camera-sounds-26117/
|
||||
- https://pixabay.com/sound-effects/beep-6-96243
|
||||
- Music: [Cinematic Cello](https://pixabay.com/music/build-up-scenes-cinematic-cello-115667) by [Aleksey Chistilin](https://pixabay.com/users/lexin_music-28841948/), [Pixabay Content License](https://pixabay.com/service/license-summary)
|
||||
- Star chart based on the [HYG Stellar database](https://github.com/astronexus/HYG-Database)
|
||||
- Font Yupiter-Regular.ttf is placed under the SIL OPEN FONT LICENSE Version 1.1 and is based on:
|
||||
|
|
60
README.md
|
@ -1,4 +1,14 @@
|
|||
![OutFly Screenshot](doc/images/screenshot3.jpg)
|
||||
```
|
||||
▄████████▄ + ███ + ▄█████████ ███ +
|
||||
███▀ ▀███ + + ███ ███▀ + ███ + +
|
||||
███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
||||
███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
||||
███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
||||
███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
||||
▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
||||
+ + + ███
|
||||
+ ▀████████████████████████████████████████████████████▀
|
||||
```
|
||||
|
||||
[Features](#features) • [Controls](#controls) • [Running OutFly](#running-outfly) • [Troubleshooting](#troubleshooting)
|
||||
|
||||
|
@ -18,6 +28,8 @@ This game aims to respect the player as much as possible. It doesn't waste your
|
|||
|
||||
Source code: https://codeberg.org/hut/outfly
|
||||
|
||||
![screenshot](doc/images/screenshot3.jpg)
|
||||
|
||||
# Features
|
||||
|
||||
- A beautiful, serene atmosphere with gorgeous views
|
||||
|
@ -29,30 +41,32 @@ Source code: https://codeberg.org/hut/outfly
|
|||
|
||||
# Controls
|
||||
|
||||
You can view these any time in game through the game menu (press Escape.)
|
||||
|
||||
- Space: Slow down, match velocity
|
||||
- E: Interact
|
||||
- F: Flashlight
|
||||
- F1: Show key bindings
|
||||
- Space: Slow down (or match velocity)
|
||||
- AWSD/Shift/Ctrl: Accelerate
|
||||
- R: Rotate (hold & move mouse)
|
||||
- E: Interact: Talk to people, enter vehicles
|
||||
- Q: Exit vehicle
|
||||
- M: Map
|
||||
- C: Camera
|
||||
- T: Cruise control
|
||||
- R: Rotate (hold + move mouse)
|
||||
- Y: Rotation stabilizer
|
||||
- AWSD/Shift/Ctrl: Move
|
||||
- J/K/U/L/I/O: Rotate
|
||||
- F11: Fullscreen
|
||||
- Tab: Toggle Augmented Reality
|
||||
- Augmented Reality only:
|
||||
- JKULIO: Mouseless camera rotation
|
||||
- Augmented Reality: (toggle with Tab)
|
||||
- Left click: Target objects
|
||||
- Right click: Zoom
|
||||
- Cheats:
|
||||
- G: Toggle cheats + invulnerability
|
||||
- V/B: Impossible acceleration
|
||||
- Shift+V/B: Extreme acceleration
|
||||
- Settings
|
||||
- Tab: Toggle HUD/AR
|
||||
- M: Toggle map
|
||||
- F: Toggle 3rd person view
|
||||
- Y: Toggle rotation stabilizer
|
||||
- F2: Toggle shadows
|
||||
- F3: Toggle sound effects
|
||||
- F4: Toggle music
|
||||
- F7: Restart game
|
||||
- F11: Toggle fullscreen
|
||||
- Cheats
|
||||
- G: Toggle god mode / cheats
|
||||
- V/B: Impossible acceleration forward/backward
|
||||
- Shift+V/B: Same as V/B, but a thousand times faster
|
||||
- C: Impossibly instant stopping
|
||||
- X: Teleport to target
|
||||
- Z: Stop
|
||||
|
||||
# Running OutFly
|
||||
## System Requirements
|
||||
|
@ -88,10 +102,6 @@ yay -S outfly-git
|
|||
No releases for these operating systems exist yet. For MacOS, you can build OutFly yourself using the instructions in [HACKING.md](https://codeberg.org/hut/outfly/src/branch/main/HACKING.md). Support for Android/iOS is planned for the future.
|
||||
|
||||
# Troubleshooting
|
||||
## I'm stuck inside another object
|
||||
|
||||
Try turning on God Mode and using one of the speed cheats, that should get you out.
|
||||
|
||||
## My GPU doesn't support Vulkan!
|
||||
|
||||
Try running outfly with the command-line option "--gl", with one of these commands:
|
||||
|
|
14
ROADMAP.md
|
@ -91,14 +91,12 @@ We are not quite there yet, but this is what I'm aiming for:
|
|||
|
||||
- The player can survive higher g-forces than a contemporary human, though this can be explained through better equipment, drugs, and/or genetic engineering.
|
||||
- The position of the planets/moons is the one of the present real time of the player, even though the game takes place hundreds of years in the future. I could simulate the future positions, but I think it's an extra layer of awesomeness if the positions of the celestial objects in the game mirroring the ones of reality. And nobody would ever notice the discrepancy.
|
||||
- P.S. First I have to find a way to actually calculate the current real positions of the moons...
|
||||
|
||||
# Game systems
|
||||
|
||||
A variety of relatively simple game systems should interact with each other to create emergent gameplay and interesting game mechanics.
|
||||
|
||||
- Free movement in space
|
||||
- Everything orbits around Planets/Sun
|
||||
- Collision with other actors
|
||||
- Augmented Reality overlay
|
||||
- Targeting objects
|
||||
|
@ -109,13 +107,6 @@ A variety of relatively simple game systems should interact with each other to c
|
|||
- G-forces and equipment/organ damage
|
||||
- Death, and survival
|
||||
|
||||
# Quest Ideas
|
||||
|
||||
- Pizza Delivery
|
||||
- Charting the major moonlets
|
||||
- Killing somebody (perhaps one of the cultists)
|
||||
- Some overarching quest for which you need the support/friendship of everybody else
|
||||
|
||||
# Challenges
|
||||
|
||||
- How to tell a deep story with permadeath without getting repetitive?
|
||||
|
@ -190,11 +181,6 @@ Items:
|
|||
- Pizzeria Clippy
|
||||
- Bus Station Clippys
|
||||
|
||||
## Other life forms
|
||||
|
||||
- Space dogs + cats. Definitely space dogs + cats.
|
||||
- Life forms that have adapted to the vacuum of space, perhaps some gigantic space-radiation-eating monsters with light-eating leaf-like appendages that move slowly
|
||||
|
||||
## Places
|
||||
|
||||
![Game Map](doc/images/map.svg)
|
||||
|
|
|
@ -27,7 +27,7 @@ fn ring_density(radius: f32) -> f32 {
|
|||
let thebe_inner: f32 = 129.0;
|
||||
let thebe_outer: f32 = 229.0;
|
||||
let metis_notch_center: f32 = 128.0;
|
||||
let metis_notch_width: f32 = 0.1;
|
||||
let metis_notch_width: f32 = 0.6;
|
||||
|
||||
let halo_brightness: f32 = 0.75;
|
||||
let main_brightness: f32 = 1.0;
|
||||
|
@ -41,7 +41,7 @@ fn ring_density(radius: f32) -> f32 {
|
|||
} else if (radius >= main_inner && radius <= main_outer) {
|
||||
var metis_notch_effect: f32 = 1.0;
|
||||
if (radius > metis_notch_center - metis_notch_width * 0.5 && radius < metis_notch_center + metis_notch_width * 0.5) {
|
||||
metis_notch_effect = 0.8 * (1.0 - smooth_edge(metis_notch_center - metis_notch_width * 0.5, metis_notch_center + metis_notch_width * 0.5, radius));
|
||||
metis_notch_effect = 0.5 * (1.0 - smooth_edge(metis_notch_center - metis_notch_width * 0.5, metis_notch_center + metis_notch_width * 0.5, radius));
|
||||
}
|
||||
density = main_brightness * metis_notch_effect * smooth_edge(main_inner, main_outer, radius);
|
||||
} else {
|
||||
|
|
Before Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 296 B |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 247 B |
BIN
assets/sprites/reticule.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/sprites/reticule2.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/sprites/reticule3.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 430 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 770 KiB |
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 928 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 403 KiB |
Before Width: | Height: | Size: 2 MiB |
Before Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 33 KiB |
2
build.rs
|
@ -12,6 +12,6 @@ fn main() {
|
|||
let target = std::env::var("TARGET").unwrap();
|
||||
if target.contains("windows") {
|
||||
println!("cargo:warning=Embedding Windows Icon");
|
||||
embed_resource::compile("build/windows/icon.rc");
|
||||
embed_resource::compile("src/build/windows/icon.rc");
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 22 KiB |
331
src/actor.rs
|
@ -10,14 +10,19 @@
|
|||
//
|
||||
// This module manages the internal states of individual characters,
|
||||
// such as their resources, the damage they receive, and interactions
|
||||
// between characters and with vehicles.
|
||||
// between characters and with vehicles. It also handles cheats.
|
||||
//
|
||||
// This module should never handle any visual aspects directly.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use crate::prelude::*;
|
||||
use bevy_xpbd_3d::plugins::sync;
|
||||
use bevy::scene::SceneInstance;
|
||||
use bevy::math::DVec3;
|
||||
use crate::{actor, audio, camera, chat, commands, effects, hud, nature, var, world};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const CENTER_WORLD_ON_PLAYER: bool = true;
|
||||
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
||||
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
||||
const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
||||
|
@ -25,24 +30,40 @@ const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
|||
pub struct ActorPlugin;
|
||||
impl Plugin for ActorPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(PreUpdate, (
|
||||
handle_player_death,
|
||||
));
|
||||
app.add_systems(FixedUpdate, (
|
||||
update_physics_lifeforms,
|
||||
update_power,
|
||||
handle_wants_maxrotation,
|
||||
handle_wants_maxvelocity,
|
||||
handle_gforce,
|
||||
));
|
||||
app.add_systems(PostUpdate, handle_gforce
|
||||
.after(PhysicsSet::Sync)
|
||||
.after(sync::position_to_transform));
|
||||
app.add_systems(Update, (
|
||||
handle_input.run_if(in_control),
|
||||
handle_input,
|
||||
handle_collisions,
|
||||
handle_damage,
|
||||
handle_cheats,
|
||||
));
|
||||
app.add_systems(PostUpdate, (
|
||||
handle_vehicle_enter_exit,
|
||||
update_id2pos,
|
||||
));
|
||||
app.add_event::<VehicleEnterExitEvent>();
|
||||
app.add_event::<PlayerDiesEvent>();
|
||||
app.insert_resource(Id2Pos(HashMap::new()));
|
||||
|
||||
if CENTER_WORLD_ON_PLAYER {
|
||||
// Disable bevy_xpbd's position->transform sync function
|
||||
app.insert_resource(sync::SyncConfig {
|
||||
position_to_transform: true,
|
||||
transform_to_position: false,
|
||||
});
|
||||
// Add own position->transform sync function
|
||||
app.add_systems(PostUpdate, position_to_transform
|
||||
.after(sync::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,20 +72,19 @@ pub enum DamageType {
|
|||
Unknown,
|
||||
Mental,
|
||||
Trauma,
|
||||
GForce,
|
||||
Asphyxiation,
|
||||
Depressurization,
|
||||
//Poison,
|
||||
//Radiation,
|
||||
//Freeze,
|
||||
//Burn,
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlayerDiesEvent(pub DamageType);
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct VehicleEnterExitEvent {
|
||||
vehicle: Entity,
|
||||
driver: Entity,
|
||||
name: Option<String>,
|
||||
is_entering: bool,
|
||||
is_player: bool
|
||||
}
|
||||
|
@ -122,16 +142,15 @@ impl Default for ExperiencesGForce { fn default() -> Self { Self {
|
|||
}}}
|
||||
|
||||
#[derive(Component)] pub struct Player; // Attached to the suit of the player
|
||||
#[derive(Component)] pub struct PlayerCollider; // Attached to the collider of the suit of the player
|
||||
#[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle
|
||||
#[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view
|
||||
#[derive(Component)] pub struct JustNowEnteredVehicle;
|
||||
#[derive(Component)] pub struct ActorEnteringVehicle;
|
||||
#[derive(Component)] pub struct ActorVehicleBeingEntered;
|
||||
#[derive(Component)] pub struct PlayersFlashLight;
|
||||
#[derive(Component)] pub struct WantsMaxRotation(pub f64);
|
||||
#[derive(Component)] pub struct WantsMaxVelocity(pub f64);
|
||||
#[derive(Component)] pub struct Identifier(pub String);
|
||||
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct LifeForm {
|
||||
|
@ -189,58 +208,21 @@ impl Default for Engine {
|
|||
#[derive(Component)]
|
||||
pub struct Suit {
|
||||
pub oxygen: f32,
|
||||
pub power: f32,
|
||||
pub oxygen_max: f32,
|
||||
pub power_max: f32,
|
||||
pub integrity: f32, // [0.0 - 1.0]
|
||||
}
|
||||
impl Default for Suit { fn default() -> Self { SUIT_SIMPLE } }
|
||||
|
||||
const SUIT_SIMPLE: Suit = Suit {
|
||||
power: 1e5,
|
||||
power_max: 1e5,
|
||||
oxygen: nature::OXY_D,
|
||||
oxygen_max: nature::OXY_D,
|
||||
integrity: 1.0,
|
||||
};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Battery {
|
||||
pub power: f32, // Watt-seconds
|
||||
pub capacity: f32, // Watt-seconds
|
||||
pub reactor: f32, // Watt (production)
|
||||
}
|
||||
|
||||
impl Default for Battery {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
power: 10e3 * 3600.0,
|
||||
capacity: 10e3 * 3600.0, // 10kWh
|
||||
reactor: 2000e3, // 2MW
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_power(
|
||||
time: Res<Time>,
|
||||
mut settings: ResMut<Settings>,
|
||||
mut q_battery: Query<(&mut Battery, Option<&Player>)>,
|
||||
mut q_flashlight: Query<&mut Visibility, With<PlayersFlashLight>>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
let d = time.delta_seconds();
|
||||
for (mut battery, player) in &mut q_battery {
|
||||
if player.is_some() && settings.flashlight_active {
|
||||
battery.power -= 4000000.0 * d;
|
||||
if battery.power <= 0.0 {
|
||||
settings.flashlight_active = false;
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
for mut flashlight_vis in &mut q_flashlight {
|
||||
*flashlight_vis = Visibility::Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
battery.power = (battery.power + battery.reactor * d)
|
||||
.clamp(0.0, battery.capacity);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_physics_lifeforms(
|
||||
time: Res<Time>,
|
||||
mut query: Query<(&mut LifeForm, &mut HitPoints, &mut Suit, &LinearVelocity)>,
|
||||
|
@ -289,15 +271,14 @@ pub fn update_physics_lifeforms(
|
|||
pub fn handle_input(
|
||||
mut commands: Commands,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<Settings>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
q_talker: Query<(&chat::Talker, &Transform), (Without<actor::Player>, Without<Camera>)>,
|
||||
player: Query<Entity, With<actor::Player>>,
|
||||
q_camera: Query<&Transform, With<Camera>>,
|
||||
mut q_flashlight: Query<&mut Visibility, With<PlayersFlashLight>>,
|
||||
q_vehicles: Query<(Entity, &Actor, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>,
|
||||
q_vehicles: Query<(Entity, &Transform), (With<actor::Vehicle>, Without<actor::Player>, Without<Camera>)>,
|
||||
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
||||
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||
q_player_drives: Query<Entity, With<PlayerDrivesThis>>,
|
||||
) {
|
||||
if q_camera.is_empty() || player.is_empty() {
|
||||
|
@ -320,20 +301,16 @@ pub fn handle_input(
|
|||
}
|
||||
// Entering Vehicles
|
||||
if q_player_drives.is_empty() {
|
||||
let objects: Vec<((Entity, &Actor), &Transform)> = q_vehicles
|
||||
let objects: Vec<(Entity, &Transform)> = q_vehicles
|
||||
.iter()
|
||||
.map(|(entity, actor, transform)| ((entity, actor), transform))
|
||||
.collect();
|
||||
if let (Some((entity, actor)), dist) =
|
||||
camera::find_closest_target::<(Entity, &Actor)>(objects, camtrans)
|
||||
{
|
||||
if let (Some(entity), dist) = camera::find_closest_target::<Entity>(objects, camtrans) {
|
||||
if dist <= MAX_INTERACT_DISTANCE {
|
||||
commands.entity(entity).insert(ActorVehicleBeingEntered);
|
||||
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
||||
ew_vehicle.send(VehicleEnterExitEvent{
|
||||
vehicle: entity,
|
||||
driver: player_entity,
|
||||
name: actor.name.clone(),
|
||||
is_entering: q_player_drives.is_empty(),
|
||||
is_player: true,
|
||||
});
|
||||
|
@ -349,37 +326,21 @@ pub fn handle_input(
|
|||
ew_vehicle.send(VehicleEnterExitEvent{
|
||||
vehicle: vehicle_entity,
|
||||
driver: player_entity,
|
||||
name: None,
|
||||
is_entering: false,
|
||||
is_player: true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if keyboard_input.just_pressed(settings.key_flashlight) {
|
||||
for mut flashlight_vis in &mut q_flashlight {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
if *flashlight_vis == Visibility::Hidden {
|
||||
*flashlight_vis = Visibility::Visible;
|
||||
settings.flashlight_active = true;
|
||||
} else {
|
||||
*flashlight_vis = Visibility::Hidden;
|
||||
settings.flashlight_active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if keyboard_input.just_pressed(settings.key_cruise_control) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
settings.cruise_control_active ^= true;
|
||||
else if keyboard_input.just_pressed(settings.key_restart) {
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(PlayerDiesEvent(DamageType::Mental));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_vehicle_enter_exit(
|
||||
mut commands: Commands,
|
||||
mut settings: ResMut<Settings>,
|
||||
mut er_vehicle: EventReader<VehicleEnterExitEvent>,
|
||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||
mut q_playerflashlight: Query<&mut Visibility, (With<PlayersFlashLight>, Without<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
|
||||
mut q_drivers: Query<(Entity, &mut Visibility, Option<&Collider>), (Without<ActorVehicleBeingEntered>, With<ActorEnteringVehicle>)>,
|
||||
mut q_vehicles: Query<(Entity, &mut Vehicle, &mut Visibility), (With<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
|
@ -405,14 +366,6 @@ pub fn handle_vehicle_enter_exit(
|
|||
commands.entity(driver).insert(JustNowEnteredVehicle);
|
||||
commands.entity(vehicle).insert(PlayerCamera);
|
||||
commands.entity(vehicle).insert(PlayerDrivesThis);
|
||||
if let Ok(mut flashlight_vis) = q_playerflashlight.get_single_mut() {
|
||||
*flashlight_vis = Visibility::Hidden;
|
||||
settings.flashlight_active = false;
|
||||
}
|
||||
if let Some(vehicle_name) = &event.name {
|
||||
ew_achievement.send(game::AchievementEvent
|
||||
::RideVehicle(vehicle_name.clone()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Exiting Vehicle
|
||||
|
@ -436,15 +389,18 @@ pub fn handle_vehicle_enter_exit(
|
|||
fn handle_collisions(
|
||||
mut collision_event_reader: EventReader<CollisionStarted>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
q_player: Query<Entity, With<PlayerCollider>>,
|
||||
q_player: Query<(Entity, Option<&Player>), With<PlayerCamera>>,
|
||||
mut q_player_lifeform: Query<(&mut LifeForm, &mut Suit), With<Player>>,
|
||||
) {
|
||||
if let (Ok(player), Ok((mut lifeform, mut suit))) = (q_player.get_single(), q_player_lifeform.get_single_mut()) {
|
||||
if let (Ok((player, player_maybe)), Ok((mut lifeform, mut suit))) = (q_player.get_single(), q_player_lifeform.get_single_mut()) {
|
||||
for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
|
||||
if *entity1 == player || *entity2 == player {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash));
|
||||
lifeform.adrenaline_jolt += 0.1;
|
||||
suit.integrity = (suit.integrity - 0.03).max(0.0);
|
||||
|
||||
if player_maybe.is_some() {
|
||||
suit.integrity -= 0.03;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -454,10 +410,11 @@ fn handle_wants_maxrotation(
|
|||
//time: Res<Time>,
|
||||
mut query: Query<(&mut AngularVelocity, &Engine, &WantsMaxRotation)>,
|
||||
) {
|
||||
let epsilon = 0.0001;
|
||||
//let d = time.delta_seconds();
|
||||
for (mut v_ang, engine, maxrot) in &mut query {
|
||||
let total = v_ang.0.length();
|
||||
if total <= maxrot.0 + EPSILON {
|
||||
if total <= maxrot.0 + epsilon {
|
||||
if total > maxrot.0 {
|
||||
v_ang.0 = DVec3::splat(0.0);
|
||||
}
|
||||
|
@ -474,9 +431,10 @@ fn handle_wants_maxvelocity(
|
|||
mut query: Query<(&mut LinearVelocity, &Engine, &WantsMaxVelocity)>,
|
||||
) {
|
||||
let dt = time.delta_seconds();
|
||||
let epsilon = 0.0001;
|
||||
for (mut v, engine, maxv) in &mut query {
|
||||
let total = v.0.length();
|
||||
if total <= maxv.0 + EPSILON {
|
||||
if total <= maxv.0 + epsilon {
|
||||
if total > maxv.0 {
|
||||
v.0 = DVec3::splat(0.0);
|
||||
}
|
||||
|
@ -488,16 +446,73 @@ fn handle_wants_maxvelocity(
|
|||
let avg_thrust = (engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
|
||||
let acceleration = (avg_thrust * dt) as f64 * -v.0;
|
||||
v.0 += acceleration;
|
||||
if v.0.length() + EPSILON < acceleration.length() {
|
||||
if v.0.length() + epsilon < acceleration.length() {
|
||||
v.0 = DVec3::splat(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_player_death(
|
||||
mut cmd: Commands,
|
||||
mut er_playerdies: EventReader<PlayerDiesEvent>,
|
||||
q_scenes: Query<(Entity, &SceneInstance), With<world::DespawnOnPlayerDeath>>,
|
||||
q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>,
|
||||
ew_spawn: EventWriter<commands::SpawnEvent>,
|
||||
mut scene_spawner: ResMut<SceneSpawner>,
|
||||
mut active_asteroids: ResMut<world::ActiveAsteroids>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||
mut log: ResMut<hud::Log>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
) {
|
||||
for death in er_playerdies.read() {
|
||||
if settings.god_mode {
|
||||
return;
|
||||
}
|
||||
settings.reset_player_settings();
|
||||
active_asteroids.0.clear();
|
||||
for entity in &q_noscenes {
|
||||
cmd.entity(entity).despawn();
|
||||
}
|
||||
for (entity, sceneinstance) in &q_scenes {
|
||||
cmd.entity(entity).despawn();
|
||||
scene_spawner.despawn_instance(**sceneinstance);
|
||||
}
|
||||
log.clear();
|
||||
//cmd.run_system(commands::load_defs); // why is it so complicated to get SystemId?
|
||||
|
||||
match death.0 {
|
||||
DamageType::Mental => {
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::BLACK),
|
||||
duration: 4.0,
|
||||
});
|
||||
}
|
||||
DamageType::Asphyxiation => {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::BLACK),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
DamageType::Trauma | _ => {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
commands::load_defs(ew_spawn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_damage(
|
||||
mut ew_playerdies: EventWriter<game::PlayerDiesEvent>,
|
||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||
mut q_hp: Query<(&mut HitPoints, Option<&Player>), Changed<HitPoints>>,
|
||||
settings: Res<Settings>,
|
||||
settings: Res<var::Settings>,
|
||||
) {
|
||||
for (mut hp, player_maybe) in &mut q_hp {
|
||||
if player_maybe.is_some() {
|
||||
|
@ -505,7 +520,7 @@ fn handle_damage(
|
|||
hp.current -= hp.damage;
|
||||
}
|
||||
if hp.current <= 0.0 {
|
||||
ew_playerdies.send(game::PlayerDiesEvent(hp.damagetype));
|
||||
ew_playerdies.send(PlayerDiesEvent(hp.damagetype));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -530,7 +545,7 @@ fn handle_gforce(
|
|||
}
|
||||
if gforce.gforce > gforce.damage_threshold {
|
||||
hp.damage += (gforce.gforce - gforce.damage_threshold).powf(2.0) / 3000.0;
|
||||
hp.damagetype = DamageType::GForce;
|
||||
hp.damagetype = DamageType::Trauma;
|
||||
}
|
||||
|
||||
if gforce.visual_effect > 0.0001 {
|
||||
|
@ -544,3 +559,119 @@ fn handle_gforce(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_id2pos(
|
||||
mut id2pos: ResMut<Id2Pos>,
|
||||
q_id: Query<(&Position, &Identifier)>,
|
||||
) {
|
||||
id2pos.0.clear();
|
||||
for (pos, id) in &q_id {
|
||||
id2pos.0.insert(id.0.clone(), pos.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_cheats(
|
||||
key_input: Res<ButtonInput<KeyCode>>,
|
||||
mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||
mut q_life: Query<(&mut actor::LifeForm, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
||||
q_target: Query<(&Transform, &Position, &LinearVelocity), (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
||||
mut ew_playerdies: EventWriter<actor::PlayerDiesEvent>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if q_player.is_empty() || q_life.is_empty() {
|
||||
return;
|
||||
}
|
||||
let (trans, mut pos, mut v) = q_player.get_single_mut().unwrap();
|
||||
let (mut lifeform, mut gforce) = q_life.get_single_mut().unwrap();
|
||||
let boost = if key_input.pressed(KeyCode::ShiftLeft) {
|
||||
1e6
|
||||
} else {
|
||||
1e3
|
||||
};
|
||||
if key_input.just_pressed(settings.key_cheat_god_mode) {
|
||||
settings.god_mode ^= true;
|
||||
if settings.god_mode {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle));
|
||||
}
|
||||
}
|
||||
if !settings.god_mode && !settings.dev_mode {
|
||||
return;
|
||||
}
|
||||
|
||||
if key_input.just_pressed(settings.key_cheat_stop) {
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
v.0 = DVec3::ZERO;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_speed) {
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, boost));
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_speed_backward) {
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
v.0 += DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, -boost));
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_teleport) {
|
||||
if let Ok((transform, target_pos, target_v)) = q_target.get_single() {
|
||||
let offset: DVec3 = 4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3();
|
||||
pos.0 = **target_pos + offset;
|
||||
*v = target_v.clone();
|
||||
}
|
||||
}
|
||||
|
||||
if !settings.dev_mode {
|
||||
return;
|
||||
}
|
||||
|
||||
if key_input.just_pressed(settings.key_cheat_pizza) {
|
||||
if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) {
|
||||
pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview1) {
|
||||
if let Some(target) = id2pos.0.get(&"busstopclippy2".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview2) {
|
||||
if let Some(target) = id2pos.0.get(&"busstopclippy3".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_zero) {
|
||||
lifeform.adrenaline = 0.0;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_mid) {
|
||||
lifeform.adrenaline = 0.5;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_max) {
|
||||
lifeform.adrenaline = 1.0;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_die) {
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(actor::PlayerDiesEvent(actor::DamageType::Trauma));
|
||||
}
|
||||
}
|
||||
|
||||
// An extension of bevy_xpbd_3d::plugins::position_to_transform that adjusts
|
||||
// the rendering position to center entities at the player camera.
|
||||
// This avoids rendering glitches when very far away from the origin.
|
||||
pub fn position_to_transform(
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
mut q_trans: Query<(&'static mut Transform, &'static Position, &'static Rotation), Without<Parent>>,
|
||||
) {
|
||||
if let Ok(player_pos) = q_player.get_single() {
|
||||
for (mut transform, pos, rot) in &mut q_trans {
|
||||
transform.translation = Vec3::new(
|
||||
(pos.x - player_pos.x) as f32,
|
||||
(pos.y - player_pos.y) as f32,
|
||||
(pos.z - player_pos.z) as f32,
|
||||
);
|
||||
transform.rotation = rot.as_quat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
327
src/audio.rs
|
@ -12,22 +12,34 @@
|
|||
|
||||
use bevy::prelude::*;
|
||||
use bevy::audio::{PlaybackMode, Volume};
|
||||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use crate::{camera, var};
|
||||
|
||||
const ASSET_CLICK: &str = "sounds/click-button-140881-crop.ogg";
|
||||
const ASSET_SWITCH: &str = "sounds/typosonic-typing-192811-crop.ogg";
|
||||
const ASSET_WOOSH: &str = "sounds/woosh.ogg";
|
||||
const ASSET_ZOOM: &str = "sounds/zoom.ogg";
|
||||
const ASSET_INCOMING_MESSAGE: &str = "sounds/connect.ogg";
|
||||
const ASSET_PING: &str = "sounds/connect.ogg";
|
||||
const ASSET_CONNECT: &str = "sounds/connect.ogg";
|
||||
const ASSET_BGM: &str = "music/Aleksey Chistilin - Cinematic Cello.ogg";
|
||||
const ASSET_THRUSTER: &str = "sounds/thruster.ogg";
|
||||
const ASSET_ROCKET: &str = "sounds/rocket.ogg";
|
||||
const ASSET_ION: &str = "sounds/ion.ogg";
|
||||
const ASSET_WAKEUP: &str = "sounds/wakeup.ogg";
|
||||
const ASSET_BIKESTART: &str = "sounds/bikestart.ogg";
|
||||
const ASSET_CRASH: &str = "sounds/crash.ogg";
|
||||
const ASSET_ELECTRICMOTOR: &str = "sounds/electricmotor.ogg";
|
||||
|
||||
pub struct AudioPlugin;
|
||||
impl Plugin for AudioPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, respawn_sinks.run_if(on_event::<RespawnSinksEvent>()));
|
||||
app.add_systems(Update, toggle_bgm);
|
||||
app.add_systems(Update, play_zoom_sfx);
|
||||
app.add_systems(Update, pause_all.run_if(on_event::<PauseAllSfxEvent>()));
|
||||
app.add_systems(PostUpdate, play_sfx);
|
||||
app.add_systems(PostUpdate, update_music);
|
||||
app.add_event::<PlaySfxEvent>();
|
||||
app.add_event::<PauseAllSfxEvent>();
|
||||
app.add_event::<ToggleMusicEvent>();
|
||||
app.add_event::<RespawnSinksEvent>();
|
||||
app.insert_resource(ZoomTimer(
|
||||
Timer::from_seconds(0.09, TimerMode::Repeating)));
|
||||
}
|
||||
|
@ -35,48 +47,177 @@ impl Plugin for AudioPlugin {
|
|||
|
||||
#[derive(Resource)] pub struct ZoomTimer(Timer);
|
||||
|
||||
const PATHS: &[(SfxType, Sfx, &str)] = &[
|
||||
(SfxType::BGM, Sfx::BGM, "music/Aleksey Chistilin - Cinematic Cello.ogg"),
|
||||
(SfxType::LoopSfx, Sfx::ElectricMotor, "sounds/electricmotor.ogg"),
|
||||
(SfxType::LoopSfx, Sfx::Ion, "sounds/ion.ogg"),
|
||||
(SfxType::LoopSfx, Sfx::Rocket, "sounds/rocket.ogg"),
|
||||
(SfxType::LoopSfx, Sfx::Thruster, "sounds/thruster.ogg"),
|
||||
(SfxType::OneOff, Sfx::Achieve, "sounds/achieve.ogg"),
|
||||
(SfxType::OneOff, Sfx::Click, "sounds/click-button-140881-crop.ogg"),
|
||||
(SfxType::OneOff, Sfx::Connect, "sounds/connect.ogg"),
|
||||
(SfxType::OneOff, Sfx::Crash, "sounds/crash.ogg"),
|
||||
(SfxType::OneOff, Sfx::EnterVehicle, "sounds/bikestart.ogg"),
|
||||
(SfxType::OneOff, Sfx::IncomingChatMessage, "sounds/connect.ogg"),
|
||||
(SfxType::OneOff, Sfx::Ping, "sounds/connect.ogg"),
|
||||
(SfxType::OneOff, Sfx::Switch, "sounds/typosonic-typing-192811-crop.ogg"),
|
||||
(SfxType::OneOff, Sfx::WakeUp, "sounds/wakeup.ogg"),
|
||||
(SfxType::OneOff, Sfx::Woosh, "sounds/woosh.ogg"),
|
||||
(SfxType::OneOff, Sfx::Zoom, "sounds/zoom.ogg"),
|
||||
];
|
||||
|
||||
#[derive(Component, PartialEq, Hash, Eq, Copy, Clone)]
|
||||
pub enum Sfx {
|
||||
Achieve,
|
||||
BGM,
|
||||
Click,
|
||||
Connect,
|
||||
Crash,
|
||||
ElectricMotor,
|
||||
EnterVehicle,
|
||||
IncomingChatMessage,
|
||||
Ion,
|
||||
Ping,
|
||||
Rocket,
|
||||
Click,
|
||||
Switch,
|
||||
Thruster,
|
||||
WakeUp,
|
||||
Woosh,
|
||||
Zoom,
|
||||
Ping,
|
||||
Connect,
|
||||
EnterVehicle,
|
||||
Crash,
|
||||
WakeUp,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlaySfxEvent(pub Sfx);
|
||||
#[derive(Event)] pub struct ToggleMusicEvent();
|
||||
#[derive(Component)] pub struct ComponentBGM;
|
||||
#[derive(Component)] pub struct ComponentThrusterSound;
|
||||
#[derive(Component)] pub struct ComponentRocketSound;
|
||||
#[derive(Component)] pub struct ComponentIonSound;
|
||||
#[derive(Component)] pub struct ComponentElectricMotorSound;
|
||||
#[derive(Component)] struct SoundBGM(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundClick(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundSwitch(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundWoosh(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundZoom(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundIncomingMessage(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundPing(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundConnect(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundBikeStart(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundCrash(Handle<AudioSource>);
|
||||
#[derive(Resource)] pub struct SoundWakeUp(Handle<AudioSource>);
|
||||
|
||||
pub fn setup(
|
||||
mut commands: Commands,
|
||||
settings: Res<var::Settings>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
commands.spawn((
|
||||
ComponentBGM,
|
||||
AudioBundle {
|
||||
source: SoundBGM(asset_server.load(ASSET_BGM)).0.clone(),
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
paused: settings.hud_active || settings.mute_music,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
commands.spawn((
|
||||
ComponentThrusterSound,
|
||||
AudioBundle {
|
||||
source: asset_server.load(ASSET_THRUSTER),
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.0),
|
||||
paused: true,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
commands.spawn((
|
||||
ComponentRocketSound,
|
||||
AudioBundle {
|
||||
source: asset_server.load(ASSET_ROCKET),
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.0),
|
||||
paused: true,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
commands.spawn((
|
||||
ComponentIonSound,
|
||||
AudioBundle {
|
||||
source: asset_server.load(ASSET_ION),
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.0),
|
||||
paused: true,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
commands.spawn((
|
||||
ComponentElectricMotorSound,
|
||||
AudioBundle {
|
||||
source: asset_server.load(ASSET_ELECTRICMOTOR),
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.5),
|
||||
paused: true,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
commands.insert_resource(SoundClick(asset_server.load(ASSET_CLICK)));
|
||||
commands.insert_resource(SoundSwitch(asset_server.load(ASSET_SWITCH)));
|
||||
commands.insert_resource(SoundWoosh(asset_server.load(ASSET_WOOSH)));
|
||||
commands.insert_resource(SoundZoom(asset_server.load(ASSET_ZOOM)));
|
||||
commands.insert_resource(SoundIncomingMessage(asset_server.load(ASSET_INCOMING_MESSAGE)));
|
||||
commands.insert_resource(SoundPing(asset_server.load(ASSET_PING)));
|
||||
commands.insert_resource(SoundConnect(asset_server.load(ASSET_CONNECT)));
|
||||
commands.insert_resource(SoundBikeStart(asset_server.load(ASSET_BIKESTART)));
|
||||
commands.insert_resource(SoundCrash(asset_server.load(ASSET_CRASH)));
|
||||
commands.insert_resource(SoundWakeUp(asset_server.load(ASSET_WAKEUP)));
|
||||
}
|
||||
|
||||
pub fn toggle_bgm(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut evwriter_toggle: EventWriter<ToggleMusicEvent>,
|
||||
mut evwriter_sfx: EventWriter<PlaySfxEvent>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_toggle_music) {
|
||||
settings.mute_music ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_toggle_sfx) {
|
||||
settings.mute_sfx ^= true;
|
||||
evwriter_sfx.send(PlaySfxEvent(Sfx::Click));
|
||||
evwriter_toggle.send(ToggleMusicEvent());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_sfx(
|
||||
mut commands: Commands,
|
||||
settings: Res<var::Settings>,
|
||||
mut events_sfx: EventReader<PlaySfxEvent>,
|
||||
sound_click: Res<SoundClick>,
|
||||
sound_switch: Res<SoundSwitch>,
|
||||
sound_woosh: Res<SoundWoosh>,
|
||||
sound_zoom: Res<SoundZoom>,
|
||||
sound_incoming_message: Res<SoundIncomingMessage>,
|
||||
sound_ping: Res<SoundPing>,
|
||||
sound_connect: Res<SoundConnect>,
|
||||
sound_bikestart: Res<SoundBikeStart>,
|
||||
sound_crash: Res<SoundCrash>,
|
||||
sound_wakeup: Res<SoundWakeUp>,
|
||||
) {
|
||||
if settings.mute_sfx && !events_sfx.is_empty() {
|
||||
events_sfx.clear();
|
||||
}
|
||||
for sfx in events_sfx.read() {
|
||||
match sfx.0 {
|
||||
Sfx::None => { continue; }
|
||||
_ => {}
|
||||
}
|
||||
commands.spawn(AudioBundle {
|
||||
source: match sfx.0 {
|
||||
Sfx::Switch => sound_switch.0.clone(),
|
||||
Sfx::Click => sound_click.0.clone(),
|
||||
Sfx::Woosh => sound_woosh.0.clone(),
|
||||
Sfx::Zoom => sound_zoom.0.clone(),
|
||||
Sfx::IncomingChatMessage => sound_incoming_message.0.clone(),
|
||||
Sfx::Ping => sound_ping.0.clone(),
|
||||
Sfx::Connect => sound_connect.0.clone(),
|
||||
Sfx::EnterVehicle => sound_bikestart.0.clone(),
|
||||
Sfx::Crash => sound_crash.0.clone(),
|
||||
Sfx::WakeUp => sound_wakeup.0.clone(),
|
||||
Sfx::None => sound_ping.0.clone(),
|
||||
},
|
||||
settings: PlaybackSettings::DESPAWN,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str2sfx(sfx_label: &str) -> Sfx {
|
||||
return match sfx_label {
|
||||
"achieve" => Sfx::Achieve,
|
||||
"switch" => Sfx::Switch,
|
||||
"click" => Sfx::Click,
|
||||
"woosh" => Sfx::Woosh,
|
||||
|
@ -85,111 +226,19 @@ pub fn str2sfx(sfx_label: &str) -> Sfx {
|
|||
"ping" => Sfx::Ping,
|
||||
"connect" => Sfx::Connect,
|
||||
"entervehicle" => Sfx::EnterVehicle,
|
||||
"crash" => Sfx::Ping,
|
||||
_ => Sfx::Click,
|
||||
"crash" => Sfx::Crash,
|
||||
_ => Sfx::None,
|
||||
};
|
||||
}
|
||||
|
||||
pub enum SfxType {
|
||||
BGM,
|
||||
LoopSfx,
|
||||
OneOff,
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlaySfxEvent(pub Sfx);
|
||||
#[derive(Event)] pub struct PauseAllSfxEvent;
|
||||
#[derive(Event)] pub struct RespawnSinksEvent;
|
||||
#[derive(Event)] pub struct ToggleMusicEvent();
|
||||
#[derive(Resource)] pub struct Sounds(HashMap<Sfx, Handle<AudioSource>>);
|
||||
|
||||
pub fn setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut ew_respawnsinks: EventWriter<RespawnSinksEvent>,
|
||||
) {
|
||||
let mut map = HashMap::new();
|
||||
for (_, sfx, path) in PATHS {
|
||||
let source = asset_server.load(*path);
|
||||
map.insert(*sfx, source.clone());
|
||||
}
|
||||
commands.insert_resource(Sounds(map));
|
||||
ew_respawnsinks.send(RespawnSinksEvent);
|
||||
}
|
||||
|
||||
pub fn respawn_sinks(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
settings: Res<var::Settings>,
|
||||
q_audiosinks: Query<Entity, (With<AudioSink>, With<Sfx>)>,
|
||||
) {
|
||||
for sink in &q_audiosinks {
|
||||
commands.entity(sink).despawn();
|
||||
}
|
||||
for (sfxtype, sfx, path) in PATHS {
|
||||
let source = asset_server.load(*path);
|
||||
match sfxtype {
|
||||
SfxType::BGM => {
|
||||
commands.spawn((
|
||||
*sfx,
|
||||
AudioBundle {
|
||||
source,
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
paused: settings.mute_music,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
},
|
||||
SfxType::LoopSfx => {
|
||||
commands.spawn((
|
||||
*sfx,
|
||||
AudioBundle {
|
||||
source,
|
||||
settings: PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.0),
|
||||
paused: true,
|
||||
..default()
|
||||
},
|
||||
},
|
||||
));
|
||||
},
|
||||
SfxType::OneOff => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_sfx(
|
||||
mut commands: Commands,
|
||||
settings: Res<var::Settings>,
|
||||
mut events_sfx: EventReader<PlaySfxEvent>,
|
||||
sounds: Res<Sounds>,
|
||||
) {
|
||||
if settings.mute_sfx && !events_sfx.is_empty() {
|
||||
events_sfx.clear();
|
||||
}
|
||||
for sfx in events_sfx.read() {
|
||||
if let Some(source) = sounds.0.get(&sfx.0) {
|
||||
commands.spawn(AudioBundle {
|
||||
source: source.clone(),
|
||||
settings: PlaybackSettings::DESPAWN,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_music(
|
||||
mut events: EventReader<ToggleMusicEvent>,
|
||||
q_audiosinks: Query<(&AudioSink, &Sfx)>,
|
||||
bgm_controller: Query<&AudioSink, With<ComponentBGM>>,
|
||||
settings: Res<var::Settings>,
|
||||
) {
|
||||
if !events.is_empty() {
|
||||
events.clear();
|
||||
for (bgm_sink, sfx) in &q_audiosinks {
|
||||
if *sfx != Sfx::BGM {
|
||||
continue;
|
||||
}
|
||||
if let Ok(bgm_sink) = bgm_controller.get_single() {
|
||||
if settings.mute_music {
|
||||
bgm_sink.pause();
|
||||
}
|
||||
|
@ -218,11 +267,3 @@ pub fn play_zoom_sfx(
|
|||
*last_zoom_level = mapcam.target_zoom_level;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pause_all(
|
||||
q_audiosinks: Query<&AudioSink, With<Sfx>>,
|
||||
) {
|
||||
for sink in &q_audiosinks {
|
||||
sink.pause();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,7 @@
|
|||
# + + + ███
|
||||
# + ▀████████████████████████████████████████████████████▀
|
||||
#
|
||||
# This script generates the file /src/data/stars.in.
|
||||
#
|
||||
# You do not have to run it unless you wish to change the stars.in file.
|
||||
#
|
||||
# It requires the following file in the extra/ directory:
|
||||
# This script requires the following file in the extra/ directory:
|
||||
# https://github.com/astronexus/HYG-Database/blob/cbd21013d2bb89732b893be357a6f41836dbe614/hyg/CURRENT/hygdata_v41.csv
|
||||
|
||||
import csv
|
|
@ -13,5 +13,5 @@
|
|||
|
||||
rootdir="${1:-}"
|
||||
install -Dm755 "target/release/outfly" "$rootdir/usr/bin/outfly"
|
||||
install -Dm644 "build/linux/outfly.png" "$rootdir/usr/share/pixmaps/outfly.png"
|
||||
install -Dm644 "build/linux/outfly.desktop" "$rootdir/usr/share/applications/outfly.desktop"
|
||||
install -Dm644 "src/build/linux/outfly.png" "$rootdir/usr/share/pixmaps/outfly.png"
|
||||
install -Dm644 "src/build/linux/outfly.desktop" "$rootdir/usr/share/applications/outfly.desktop"
|
BIN
src/build/linux/outfly.png
Normal file
After Width: | Height: | Size: 10 KiB |
|
@ -10,26 +10,17 @@
|
|||
# + ▀████████████████████████████████████████████████████▀
|
||||
#
|
||||
# A script to package release binaries + README.md into zip files.
|
||||
# Usage: cd outfly; build/pack.sh [-b]
|
||||
# Usage: cd outfly; src/build/pack.sh [-b]
|
||||
# Options: -b: cross-compile targets before packing
|
||||
|
||||
set -e
|
||||
|
||||
function check_uncommitted_assets() {
|
||||
( cd assets; [ $(git status --porcelain . | wc -l) -gt 0 ] )
|
||||
}
|
||||
|
||||
if check_uncommitted_assets; then
|
||||
echo "Please make sure there are no uncommited files in the 'asset' directory before packing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$1" == "-b" ]; then
|
||||
cargo build --release --target=x86_64-unknown-linux-gnu --no-default-features --features release_linux
|
||||
cargo build --release --target=x86_64-pc-windows-gnu --no-default-features --features release_windows
|
||||
fi
|
||||
|
||||
VERSION="$(sed -nr 's/^\s*version\s*=\s*"(.*)"\s*$/\1/p' Cargo.toml | head -n1)"
|
||||
VERSION="$(sed -nr 's/^\s*version\s*=\s*"(.*)"\s*$/\1/p' Cargo.toml)"
|
||||
test -z "$VERSION" && echo 'Error: Could not extract version from Cargo.toml' && exit
|
||||
echo "Extracted version from Cargo.toml: $VERSION"
|
||||
|
BIN
src/build/windows/outfly.ico
Normal file
After Width: | Height: | Size: 27 KiB |
179
src/camera.rs
|
@ -19,10 +19,11 @@ use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
|
|||
use bevy::core_pipeline::tonemapping::Tonemapping;
|
||||
use bevy::pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap};
|
||||
use bevy::transform::TransformSystem;
|
||||
use bevy::math::{DVec3, DQuat};
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use bevy_xpbd_3d::plugins::sync;
|
||||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI as PI64;
|
||||
use crate::{actor, audio, hud, var};
|
||||
|
||||
pub const INITIAL_ZOOM_LEVEL: f64 = 10.0;
|
||||
|
||||
|
@ -31,31 +32,19 @@ pub struct CameraPlugin;
|
|||
impl Plugin for CameraPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup_camera);
|
||||
app.add_systems(Update, handle_input.run_if(in_control));
|
||||
app.add_systems(Update, update_map_only_object_visibility.run_if(alive));
|
||||
app.add_systems(Update, handle_input);
|
||||
app.add_systems(Update, update_map_only_object_visibility);
|
||||
app.add_systems(Update, manage_player_actor.after(handle_input));
|
||||
app.add_systems(PostUpdate, sync_camera_to_player
|
||||
.after(PhysicsSet::Sync)
|
||||
.after(apply_input_to_player)
|
||||
.before(TransformSystem::TransformPropagate));
|
||||
app.add_systems(PostUpdate, update_mapcam_center
|
||||
.before(sync::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform));
|
||||
app.add_systems(Update, update_map_camera.run_if(in_control));
|
||||
app.add_systems(Update, update_fov.run_if(alive));
|
||||
app.add_systems(PreUpdate, apply_input_to_player);
|
||||
app.add_systems(Update, update_map_camera);
|
||||
app.add_systems(Update, update_fov);
|
||||
app.add_systems(PostUpdate, apply_input_to_player
|
||||
.after(PhysicsSet::Sync)
|
||||
.before(TransformSystem::TransformPropagate));
|
||||
app.insert_resource(MapCam::default());
|
||||
|
||||
// To center the renderer origin on the player camera,
|
||||
// 1. Disable bevy_xpbd's position->transform sync function
|
||||
app.insert_resource(sync::SyncConfig {
|
||||
position_to_transform: true,
|
||||
transform_to_position: false,
|
||||
});
|
||||
// 2. Add own position->transform sync function
|
||||
app.add_systems(PostUpdate, position_to_transform
|
||||
.after(sync::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +64,6 @@ pub struct MapCam {
|
|||
pub offset_x: f64,
|
||||
pub offset_z: f64,
|
||||
pub center: DVec3,
|
||||
pub center_on_entity: Option<Entity>,
|
||||
}
|
||||
impl Default for MapCam {
|
||||
fn default() -> Self {
|
||||
|
@ -83,12 +71,11 @@ impl Default for MapCam {
|
|||
initialized: false,
|
||||
zoom_level: 2.0,
|
||||
target_zoom_level: INITIAL_ZOOM_LEVEL,
|
||||
pitch: PI * 0.3,
|
||||
pitch: PI64 * 0.3,
|
||||
yaw: 0.0,
|
||||
offset_x: 0.0,
|
||||
offset_z: 0.0,
|
||||
center: DVec3::new(0.0, 0.0, 0.0),
|
||||
center_on_entity: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +109,7 @@ pub fn setup_camera(
|
|||
shadows_enabled: settings.shadows_sun,
|
||||
..default()
|
||||
},
|
||||
transform: Transform::from_rotation(Quat::from_rotation_y(PI32/2.0)),
|
||||
transform: Transform::from_rotation(Quat::from_rotation_y(PI/2.0)),
|
||||
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||
num_cascades: 4,
|
||||
minimum_distance: 0.1,
|
||||
|
@ -149,16 +136,14 @@ pub fn sync_camera_to_player(
|
|||
let (actor, player_transform) = q_playercam.get_single().unwrap();
|
||||
|
||||
// Rotation
|
||||
let rotation = player_transform.rotation * Quat::from_array([0.0, -1.0, 0.0, 0.0]);
|
||||
camera_transform.rotation = player_transform.rotation * Quat::from_array([0.0, -1.0, 0.0, 0.0]);
|
||||
|
||||
// Translation
|
||||
if settings.third_person {
|
||||
camera_transform.translation = player_transform.translation + rotation * (actor.camdistance * Vec3::new(0.0, 0.2, 1.0));
|
||||
camera_transform.rotation = rotation * Quat::from_euler(EulerRot::XYZ, -0.02, 0.0, 0.0);
|
||||
camera_transform.translation = player_transform.translation + camera_transform.rotation * (actor.camdistance * Vec3::new(0.0, 0.2, 1.0));
|
||||
}
|
||||
else {
|
||||
camera_transform.translation = player_transform.translation;
|
||||
camera_transform.rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,8 +151,8 @@ pub fn update_map_camera(
|
|||
settings: Res<var::Settings>,
|
||||
mut mapcam: ResMut<MapCam>,
|
||||
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_playercam: Query<(Entity, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
q_target: Query<(Entity, &Transform), (With<hud::IsTargeted>, Without<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_playercam: Query<&Transform, (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
q_target: Query<&Transform, (With<hud::IsTargeted>, Without<Camera>, Without<actor::PlayerCamera>)>,
|
||||
q_target_changed: Query<(), Changed<hud::IsTargeted>>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
mut er_mousewheel: EventReader<MouseWheel>,
|
||||
|
@ -177,13 +162,12 @@ pub fn update_map_camera(
|
|||
return;
|
||||
}
|
||||
let mut camera_transform = q_camera.get_single_mut().unwrap();
|
||||
let (player_entity, player_trans) = q_playercam.get_single().unwrap();
|
||||
let (target_entity, target_trans) = if let Ok(target) = q_target.get_single() {
|
||||
let player_transform = q_playercam.get_single().unwrap();
|
||||
let target = if let Ok(target) = q_target.get_single() {
|
||||
target
|
||||
} else {
|
||||
(player_entity, player_trans)
|
||||
player_transform
|
||||
};
|
||||
mapcam.center_on_entity = Some(target_entity);
|
||||
|
||||
// Get mouse movement
|
||||
let mut mouse_delta = Vec2::ZERO;
|
||||
|
@ -193,9 +177,10 @@ pub fn update_map_camera(
|
|||
// NOTE: we need to subtract a bit from PI/2, otherwise the "up"
|
||||
// direction parameter for the Transform.look_at function is ambiguous
|
||||
// at the extreme values and the orientation will flicker back/forth.
|
||||
let min_zoom: f64 = target_trans.scale.x as f64 * 2.0;
|
||||
let epsilon = 0.001;
|
||||
let min_zoom: f64 = target.scale.x as f64 * 2.0;
|
||||
let max_zoom: f64 = 17e18; // at this point, camera starts glitching
|
||||
mapcam.pitch = (mapcam.pitch + mouse_delta.y as f64 / 180.0 * settings.mouse_sensitivity as f64).clamp(-PI / 2.0 + EPSILON, PI / 2.0 - EPSILON);
|
||||
mapcam.pitch = (mapcam.pitch + mouse_delta.y as f64 / 180.0 * settings.mouse_sensitivity as f64).clamp(-PI64 / 2.0 + epsilon, PI64 / 2.0 - epsilon);
|
||||
mapcam.yaw += mouse_delta.x as f64 / 180.0 * settings.mouse_sensitivity as f64;
|
||||
|
||||
// Reset movement offset if target changes
|
||||
|
@ -226,9 +211,9 @@ pub fn update_map_camera(
|
|||
|
||||
// Update zoom level
|
||||
if !mapcam.initialized {
|
||||
let factor: f64 = if target_trans == player_trans { 7.0 } else { 1.0 };
|
||||
mapcam.target_zoom_level *= target_trans.scale.x as f64 * factor;
|
||||
mapcam.zoom_level *= target_trans.scale.x as f64 * factor;
|
||||
let factor: f64 = if target == player_transform { 7.0 } else { 1.0 };
|
||||
mapcam.target_zoom_level *= target.scale.x as f64 * factor;
|
||||
mapcam.zoom_level *= target.scale.x as f64 * factor;
|
||||
mapcam.initialized = true;
|
||||
}
|
||||
let mut change_zoom: f64 = 0.0;
|
||||
|
@ -247,7 +232,8 @@ pub fn update_map_camera(
|
|||
|
||||
// Update point of view
|
||||
let pov_rotation = DQuat::from_euler(EulerRot::XYZ, 0.0, mapcam.yaw as f64, mapcam.pitch as f64);
|
||||
let point_of_view = pov_rotation * (mapcam.zoom_level as f64 * DVec3::new(1.0, 0.0, 0.0));
|
||||
let offset = DVec3::new(mapcam.offset_x, 0.0, mapcam.offset_z);
|
||||
let point_of_view = offset + pov_rotation * (mapcam.zoom_level as f64 * DVec3::new(1.0, 0.0, 0.0));
|
||||
|
||||
// Update movement offset
|
||||
let mut direction = pov_rotation * DVec3::new(offset_x, 0.0, offset_z);
|
||||
|
@ -259,24 +245,9 @@ pub fn update_map_camera(
|
|||
mapcam.offset_z += 0.01 * (direction.z * mapcam.zoom_level);
|
||||
|
||||
// Apply updates to camera
|
||||
camera_transform.translation = point_of_view.as_vec3();
|
||||
camera_transform.look_at(Vec3::ZERO, Vec3::Y);
|
||||
}
|
||||
|
||||
pub fn update_mapcam_center(
|
||||
mut mapcam: ResMut<MapCam>,
|
||||
settings: Res<var::Settings>,
|
||||
q_pos: Query<&Position>,
|
||||
) {
|
||||
if !settings.map_active {
|
||||
return;
|
||||
}
|
||||
if let Some(entity) = mapcam.center_on_entity {
|
||||
if let Ok(pos) = q_pos.get(entity) {
|
||||
let offset = DVec3::new(mapcam.offset_x, 0.0, mapcam.offset_z);
|
||||
mapcam.center = **pos + offset;
|
||||
}
|
||||
}
|
||||
mapcam.center = target.translation.as_dvec3() + offset;
|
||||
camera_transform.translation = target.translation + point_of_view.as_vec3();
|
||||
camera_transform.look_at(mapcam.center.as_vec3(), Vec3::Y);
|
||||
}
|
||||
|
||||
pub fn update_fov(
|
||||
|
@ -305,19 +276,33 @@ pub fn update_fov(
|
|||
|
||||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
settings: Res<var::Settings>,
|
||||
mut q_light: Query<&mut DirectionalLight>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
mut mapcam: ResMut<MapCam>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_game: EventWriter<GameEvent>,
|
||||
mut ew_updateoverlays: EventWriter<hud::UpdateOverlayVisibility>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_camera) {
|
||||
ew_game.send(GameEvent::SetThirdPerson(Toggle));
|
||||
settings.third_person ^= true;
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_shadows) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
settings.shadows_sun ^= true;
|
||||
for mut light in &mut q_light {
|
||||
light.shadows_enabled = settings.shadows_sun;
|
||||
}
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_map) {
|
||||
ew_game.send(GameEvent::SetMap(Toggle));
|
||||
settings.map_active ^= true;
|
||||
if settings.map_active {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Woosh));
|
||||
}
|
||||
*mapcam = MapCam::default();
|
||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_rotation_stabilizer) {
|
||||
ew_game.send(GameEvent::SetRotationStabilizer(Toggle));
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
settings.rotation_stabilizer_active ^= true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -366,7 +351,10 @@ pub fn apply_input_to_player(
|
|||
windows: Query<&Window, With<PrimaryWindow>>,
|
||||
mut mouse_events: EventReader<MouseMotion>,
|
||||
key_input: Res<ButtonInput<KeyCode>>,
|
||||
q_audiosinks: Query<(&audio::Sfx, &AudioSink)>,
|
||||
thruster_sound_controller: Query<&AudioSink, With<audio::ComponentThrusterSound>>,
|
||||
rocket_sound_controller: Query<&AudioSink, With<audio::ComponentRocketSound>>,
|
||||
ion_sound_controller: Query<&AudioSink, With<audio::ComponentIonSound>>,
|
||||
electricmotor_sound_controller: Query<&AudioSink, With<audio::ComponentElectricMotorSound>>,
|
||||
q_target: Query<&LinearVelocity, (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
||||
mut q_playercam: Query<(
|
||||
&Transform,
|
||||
|
@ -377,7 +365,7 @@ pub fn apply_input_to_player(
|
|||
Option<&actor::PlayerDrivesThis>,
|
||||
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||
) {
|
||||
if settings.map_active || !settings.in_control() {
|
||||
if settings.map_active {
|
||||
return;
|
||||
}
|
||||
let dt = time.delta_seconds();
|
||||
|
@ -405,7 +393,7 @@ pub fn apply_input_to_player(
|
|||
if let Ok((player_transform, mut engine, mut angularvelocity, mut v, mut torque, bike)) = q_playercam.get_single_mut() {
|
||||
// Handle key input
|
||||
if focused {
|
||||
if key_input.pressed(settings.key_forward) || settings.cruise_control_active {
|
||||
if key_input.pressed(settings.key_forward) {
|
||||
axis_input.z += 1.2;
|
||||
}
|
||||
if key_input.pressed(settings.key_back) {
|
||||
|
@ -429,10 +417,6 @@ pub fn apply_input_to_player(
|
|||
axis_input += 1.0 * DVec3::from(player_transform.rotation.inverse() * stop_direction.as_vec3());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if settings.cruise_control_active {
|
||||
axis_input.z += 1.2;
|
||||
}
|
||||
}
|
||||
// In typical games we would normalize the input vector so that diagonal movement is as
|
||||
// fast as forward or sideways movement. But here, we merely clamp each direction to an
|
||||
|
@ -514,9 +498,8 @@ pub fn apply_input_to_player(
|
|||
}
|
||||
}
|
||||
|
||||
let slowrot = settings.rotation_stabilizer_active || key_input.pressed(settings.key_stop);
|
||||
let angular_slowdown: f64 = if slowrot {
|
||||
(2.0 - engine.reaction_wheels.powf(0.05).clamp(1.001, 1.1)) as f64
|
||||
let angular_slowdown: f64 = if settings.rotation_stabilizer_active {
|
||||
(2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
@ -539,13 +522,8 @@ pub fn apply_input_to_player(
|
|||
}
|
||||
}
|
||||
|
||||
let mut sinks: HashMap<audio::Sfx, &AudioSink> = HashMap::new();
|
||||
for (sfx, sink) in &q_audiosinks {
|
||||
sinks.insert(*sfx, sink);
|
||||
}
|
||||
|
||||
// Play sound effects
|
||||
if let Some(sink) = sinks.get(&audio::Sfx::ElectricMotor) {
|
||||
if let Ok(sink) = electricmotor_sound_controller.get_single() {
|
||||
let volume = sink.volume();
|
||||
let speed = sink.speed();
|
||||
let action = pitch_yaw_rot.length_squared().powf(0.2) * 0.0005;
|
||||
|
@ -563,14 +541,14 @@ pub fn apply_input_to_player(
|
|||
}
|
||||
}
|
||||
let sinks = vec![
|
||||
(1.2, actor::EngineType::Monopropellant, sinks.get(&audio::Sfx::Thruster)),
|
||||
(1.0, actor::EngineType::Rocket, sinks.get(&audio::Sfx::Rocket)),
|
||||
(1.4, actor::EngineType::Ion, sinks.get(&audio::Sfx::Ion)),
|
||||
(1.2, actor::EngineType::Monopropellant, thruster_sound_controller.get_single()),
|
||||
(1.0, actor::EngineType::Rocket, rocket_sound_controller.get_single()),
|
||||
(1.4, actor::EngineType::Ion, ion_sound_controller.get_single()),
|
||||
];
|
||||
let seconds_to_max_vol = 0.05;
|
||||
let seconds_to_min_vol = 0.05;
|
||||
for sink_data in sinks {
|
||||
if let (vol_boost, engine_type, Some(sink)) = sink_data {
|
||||
if let (vol_boost, engine_type, Ok(sink)) = sink_data {
|
||||
if settings.mute_sfx {
|
||||
sink.pause();
|
||||
}
|
||||
|
@ -605,7 +583,7 @@ pub fn update_map_only_object_visibility(
|
|||
q_camera: Query<&Transform, With<Camera>>,
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
mut q_onlyinmap: Query<(&mut Visibility, &ShowOnlyInMap), Without<Camera>>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
) {
|
||||
if q_camera.is_empty() || q_player.is_empty() {
|
||||
return;
|
||||
|
@ -650,7 +628,7 @@ pub fn find_closest_target<TargetSpecifier>(
|
|||
// not on the player mesh but on the camera, which doesn't have a position.
|
||||
let (angular_diameter, angle, distance) = calc_angular_diameter_known_target_vector(
|
||||
trans, camera_transform, &target_vector);
|
||||
if angle <= angular_diameter.clamp(0.01, PI32) {
|
||||
if angle <= angular_diameter.clamp(0.01, PI) {
|
||||
// It's in the field of view!
|
||||
//commands.entity(entity).insert(IsTargeted);
|
||||
let distance_to_surface = distance - trans.scale.x;
|
||||
|
@ -694,30 +672,3 @@ pub fn calc_angular_diameter(
|
|||
.normalize_or_zero();
|
||||
return calc_angular_diameter_known_target_vector(target, camera, &target_vector);
|
||||
}
|
||||
|
||||
// An extension of bevy_xpbd_3d::plugins::position_to_transform that adjusts
|
||||
// the rendering position to center entities at the player camera.
|
||||
// This avoids rendering glitches when very far away from the origin.
|
||||
pub fn position_to_transform(
|
||||
mapcam: Res<MapCam>,
|
||||
settings: Res<var::Settings>,
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
mut q_trans: Query<(&'static mut Transform, &'static Position, &'static Rotation), Without<Parent>>,
|
||||
) {
|
||||
let center: DVec3 = if settings.map_active {
|
||||
mapcam.center
|
||||
} else if let Ok(player_pos) = q_player.get_single() {
|
||||
**player_pos
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (mut transform, pos, rot) in &mut q_trans {
|
||||
transform.translation = Vec3::new(
|
||||
(pos.x - center.x) as f32,
|
||||
(pos.y - center.y) as f32,
|
||||
(pos.z - center.z) as f32,
|
||||
);
|
||||
transform.rotation = rot.as_quat();
|
||||
}
|
||||
}
|
||||
|
|
27
src/chat.rs
|
@ -11,8 +11,9 @@
|
|||
// This module loads the chat definitions from the YAML files
|
||||
// and manages the flow of conversations.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{actor, audio, effects, hud, var, world};
|
||||
use bevy::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use serde_yaml::Value;
|
||||
use serde::Deserialize;
|
||||
|
@ -618,7 +619,6 @@ pub fn handle_new_conversations(
|
|||
mut er_conv: EventReader<StartConversationEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_chatevent: EventWriter<ChatEvent>,
|
||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||
chatdb: Res<ChatDB>,
|
||||
q_chats: Query<&Chat>,
|
||||
time: Res<Time>,
|
||||
|
@ -630,9 +630,6 @@ pub fn handle_new_conversations(
|
|||
}
|
||||
match (*chatdb).get_chat_by_id(&event.talker.chat_name) {
|
||||
Ok(chat_id) => {
|
||||
if let Some(name) = &event.talker.name {
|
||||
ew_achievement.send(game::AchievementEvent::TalkTo(name.clone()));
|
||||
}
|
||||
let mut chat = Chat {
|
||||
internal_id: chat_id,
|
||||
position: vec![0],
|
||||
|
@ -705,9 +702,6 @@ pub fn handle_chat_events(
|
|||
hud::LogLevel::Info => {
|
||||
log.info(message.into());
|
||||
}
|
||||
hud::LogLevel::Achievement => {
|
||||
log.add(message.into(), "".into(), hud::LogLevel::Achievement);
|
||||
}
|
||||
hud::LogLevel::Warning => {
|
||||
log.warning(message.into());
|
||||
}
|
||||
|
@ -797,9 +791,8 @@ pub fn handle_chat_scripts(
|
|||
mut q_player: Query<(&mut actor::Actor, &mut actor::Suit, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
||||
mut q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
mut ew_effect: EventWriter<effects::SpawnEffectEvent>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
) {
|
||||
for script in er_chatscript.read() {
|
||||
// Parse the script string
|
||||
|
@ -846,7 +839,6 @@ pub fn handle_chat_scripts(
|
|||
error!("Invalid parameter for command `{}`: `{}`", name, param1);
|
||||
}
|
||||
"repairsuit" => {
|
||||
ew_achievement.send(game::AchievementEvent::RepairSuit);
|
||||
for (_, mut suit, _) in q_player.iter_mut() {
|
||||
suit.integrity = 1.0;
|
||||
}
|
||||
|
@ -879,21 +871,18 @@ pub fn handle_chat_scripts(
|
|||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::CYAN),
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeIn(Color::CYAN),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
"cryofadeout" => {
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeOut(Color::CYAN),
|
||||
ew_effect.send(effects::SpawnEffectEvent {
|
||||
class: effects::Effects::FadeOut(Color::CYAN),
|
||||
duration: 5.1,
|
||||
});
|
||||
}
|
||||
"drinkpizza" => {
|
||||
ew_achievement.send(game::AchievementEvent::DrinkPizza);
|
||||
}
|
||||
_ => {
|
||||
error!("Error, undefined chat script {name}");
|
||||
}
|
||||
|
|
|
@ -74,6 +74,10 @@
|
|||
- Micros? What's that?:
|
||||
- Micrometeorites. Those tiny 混蛋 that fly right through you, leaving holes in your suit. And your body.
|
||||
- goto: help
|
||||
- What year is this?:
|
||||
- Oh, is your Augmented Reality deactivated?
|
||||
- Push the TAB button, your space suit's AR will show you the date and time.
|
||||
- goto: help
|
||||
- Why am I here?:
|
||||
- That's a very philosophical question.
|
||||
- I don't know.
|
||||
|
@ -86,7 +90,7 @@
|
|||
- I'm here mostly for the view and the peace.
|
||||
- Just look at Jupiter, it's mesmerizing, isn't it?
|
||||
- So far away from everything, nobody expects anything from you.
|
||||
- If you want, you can take my sports cruiser for a ride. It's right over there.
|
||||
- If you want, you can take my sports racing capsule MeteorAceGT™ for a ride. It's right over there.
|
||||
- It rides like a punch in the face, don't hurt yourself, ok?
|
||||
- You're too kind!:
|
||||
- Ah, don't mention it!
|
||||
|
@ -206,31 +210,27 @@
|
|||
- if: $knows-menu
|
||||
I'd like a Suspicious Spacefunghi:
|
||||
- Coming right up your feeding tube!
|
||||
- script: drinkpizza
|
||||
- system: Received Suspicious Spacefunghi pizza smoothie
|
||||
- goto: served
|
||||
- if: $knows-menu
|
||||
I'd like a Daring Durian:
|
||||
- Coming right up your feeding tube!
|
||||
- script: drinkpizza
|
||||
- system: Received Daring Durian pizza smoothie
|
||||
- goto: served
|
||||
- if: $knows-menu
|
||||
I'd like an Artichoke Apple Pie pizza:
|
||||
- Coming right up your feeding tube!
|
||||
- script: drinkpizza
|
||||
- system: Received Artichoke Apple Pie pizza smoothie
|
||||
- goto: served
|
||||
- if: $knows-pineapple
|
||||
I'd like a pineapple pizza:
|
||||
- Coming right up your feeding tube!
|
||||
- script: drinkpizza
|
||||
- system: Received pineapple pizza smoothie
|
||||
- goto: served
|
||||
- if: $knows-coffee
|
||||
I'd like a cup of that legendary Old Earth Soykaf, please:
|
||||
I'd like a cup of that legendary Old Earth Coffee, please:
|
||||
- Coming right up your feeding tube!
|
||||
- system: Received Old Earth Soykaf
|
||||
- system: Received Old Earth Coffee
|
||||
- goto: served
|
||||
- Surprise me.:
|
||||
- Hmm...
|
||||
|
@ -257,7 +257,7 @@
|
|||
- goto: served
|
||||
- Got any coffee?:
|
||||
- Your suit should have a coffee dispenser built right into it.
|
||||
- Naturally, it's not as good as my legendary Old Earth Soykaf!
|
||||
- Naturally, it's not as good as my legendary Old Earth Coffee!
|
||||
- set: knows-coffee
|
||||
- goto: non-pizza
|
||||
- Can't think of anything right now.:
|
||||
|
@ -285,30 +285,16 @@
|
|||
---
|
||||
|
||||
|
||||
- chat: Ash
|
||||
- Oh, hello!
|
||||
- Look, I'm very busy right now constructing this place.
|
||||
- We can talk more once this is done.
|
||||
- See you around.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
- chat: River
|
||||
- Welcome to our little oasis!
|
||||
- This will be great once it's finished.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
- chat: generic_questions_serenity
|
||||
- Where are we?:
|
||||
- Inside Jupiter's rings, obviously.
|
||||
- We're about 150,000km away from the gas giant.
|
||||
- This region is called Serenity by its inhabitants, due to the relative safety from Jupiter's magnetic field and the micros.
|
||||
- goto: generic_questions
|
||||
- What time is it?:
|
||||
- Oh, is your Augmented Reality deactivated?
|
||||
- Push the TAB button, your space suit's AR will show you the date and time.
|
||||
- goto: generic_questions
|
||||
- I think I'm good for now.: []
|
||||
|
||||
|
||||
|
|
|
@ -13,21 +13,19 @@
|
|||
extern crate regex;
|
||||
use bevy::prelude::*;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
||||
use crate::prelude::*;
|
||||
use crate::{actor, camera, chat, hud, nature, shading, skeleton, var, world};
|
||||
use regex::Regex;
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI as PI64;
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub const ID_EARTH: &str = "earth";
|
||||
pub const ID_SOL: &str = "sol";
|
||||
pub const ID_JUPITER: &str = "jupiter";
|
||||
|
||||
pub struct CmdPlugin;
|
||||
impl Plugin for CmdPlugin {
|
||||
pub struct CommandsPlugin;
|
||||
impl Plugin for CommandsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, load_defs);
|
||||
app.add_systems(Update, spawn_entities);
|
||||
app.add_systems(Update, process_mesh);
|
||||
app.add_systems(PreUpdate, hide_colliders
|
||||
.run_if(any_with_component::<NeedsSceneColliderRemoved>));
|
||||
app.add_event::<SpawnEvent>();
|
||||
|
@ -57,7 +55,6 @@ struct ParserState {
|
|||
model: Option<String>,
|
||||
model_scale: f32,
|
||||
rotation: Quat,
|
||||
axialtilt: f32,
|
||||
velocity: DVec3,
|
||||
angular_momentum: DVec3,
|
||||
pronoun: Option<String>,
|
||||
|
@ -71,7 +68,6 @@ struct ParserState {
|
|||
is_targeted_on_startup: bool,
|
||||
is_sun: bool,
|
||||
is_moon: bool,
|
||||
is_planet: bool,
|
||||
is_point_of_interest: bool,
|
||||
orbit_distance: Option<f64>,
|
||||
orbit_object_id: Option<String>,
|
||||
|
@ -113,7 +109,6 @@ impl Default for ParserState {
|
|||
model: None,
|
||||
model_scale: 1.0,
|
||||
rotation: Quat::IDENTITY,
|
||||
axialtilt: 0.0,
|
||||
velocity: DVec3::splat(0.0),
|
||||
angular_momentum: DVec3::new(0.03, 0.3, 0.09),
|
||||
pronoun: None,
|
||||
|
@ -127,7 +122,6 @@ impl Default for ParserState {
|
|||
is_targeted_on_startup: false,
|
||||
is_sun: false,
|
||||
is_moon: false,
|
||||
is_planet: false,
|
||||
is_point_of_interest: false,
|
||||
orbit_distance: None,
|
||||
orbit_object_id: None,
|
||||
|
@ -253,7 +247,7 @@ pub fn load_defs(
|
|||
["orbit", radius_str, phase_str] => {
|
||||
if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) {
|
||||
state.orbit_distance = Some(r);
|
||||
state.orbit_phase = Some(phase * PI * 2.0);
|
||||
state.orbit_phase = Some(phase * PI64 * 2.0);
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
|
@ -262,7 +256,7 @@ pub fn load_defs(
|
|||
}
|
||||
["orbit_phase_offset", value] => {
|
||||
if let Ok(value_float) = value.parse::<f64>() {
|
||||
let offset_radians = 2.0 * PI * value_float;
|
||||
let offset_radians = 2.0 * PI64 * value_float;
|
||||
if let Some(phase_radians) = state.orbit_phase {
|
||||
state.orbit_phase = Some(phase_radians + offset_radians);
|
||||
}
|
||||
|
@ -295,9 +289,6 @@ pub fn load_defs(
|
|||
["moon", "yes"] => {
|
||||
state.is_moon = true;
|
||||
}
|
||||
["planet", "yes"] => {
|
||||
state.is_planet = true;
|
||||
}
|
||||
["sun", "yes"] => {
|
||||
state.is_sun = true;
|
||||
}
|
||||
|
@ -335,7 +326,7 @@ pub fn load_defs(
|
|||
}
|
||||
["rotationx", rotation_x] => {
|
||||
if let Ok(rotation_x_float) = rotation_x.parse::<f32>() {
|
||||
state.rotation *= Quat::from_rotation_x(rotation_x_float.to_radians());
|
||||
state.rotation *= Quat::from_rotation_x(PI * rotation_x_float);
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
|
@ -344,7 +335,7 @@ pub fn load_defs(
|
|||
}
|
||||
["rotationy", rotation_y] => {
|
||||
if let Ok(rotation_y_float) = rotation_y.parse::<f32>() {
|
||||
state.rotation *= Quat::from_rotation_y(rotation_y_float.to_radians());
|
||||
state.rotation *= Quat::from_rotation_y(PI * rotation_y_float);
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
|
@ -353,17 +344,7 @@ pub fn load_defs(
|
|||
}
|
||||
["rotationz", rotation_z] => {
|
||||
if let Ok(rotation_z_float) = rotation_z.parse::<f32>() {
|
||||
state.rotation *= Quat::from_rotation_z(rotation_z_float.to_radians());
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
["axialtilt", rotation_y] => {
|
||||
if let Ok(rotation_y_float) = rotation_y.parse::<f32>() {
|
||||
state.rotation *= Quat::from_rotation_y(rotation_y_float.to_radians());
|
||||
state.axialtilt = rotation_y_float;
|
||||
state.rotation *= Quat::from_rotation_z(PI * rotation_z_float);
|
||||
}
|
||||
else {
|
||||
error!("Can't parse float: {line}");
|
||||
|
@ -531,14 +512,12 @@ fn spawn_entities(
|
|||
asset_server: Res<AssetServer>,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut materials_jupiter: ResMut<Assets<load::JupitersRing>>,
|
||||
mut id2pos: ResMut<game::Id2Pos>,
|
||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||
mut materials_jupiter: ResMut<Assets<shading::JupitersRing>>,
|
||||
mut id2pos: ResMut<actor::Id2Pos>,
|
||||
settings: Res<var::Settings>,
|
||||
) {
|
||||
for state_wrapper in er_spawn.read() {
|
||||
let state = &state_wrapper.0;
|
||||
let mut rotation = state.rotation;
|
||||
if state.class == DefClass::Actor {
|
||||
// Preprocessing
|
||||
let mut absolute_pos = if let Some(id) = &state.relative_to {
|
||||
|
@ -562,7 +541,6 @@ fn spawn_entities(
|
|||
if let Some(id) = &state.orbit_object_id {
|
||||
let mass = match id.as_str() {
|
||||
"jupiter" => nature::JUPITER_MASS,
|
||||
"sol" => nature::JUPITER_MASS,
|
||||
_ => {
|
||||
error!("Found no mass for object `{id}`");
|
||||
continue;
|
||||
|
@ -570,8 +548,7 @@ fn spawn_entities(
|
|||
};
|
||||
let orbital_period = nature::simple_orbital_period(mass, r);
|
||||
phase_radians += if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
||||
let now = epoch.as_secs_f64() + 614533234154.0; // random
|
||||
PI * 2.0 * (now % orbital_period) / orbital_period
|
||||
PI64 * 2.0 * (epoch.as_secs_f64() % orbital_period) / orbital_period
|
||||
} else {
|
||||
error!("Can't determine current time `{id}`");
|
||||
0.0
|
||||
|
@ -601,13 +578,13 @@ fn spawn_entities(
|
|||
actor.insert(world::DespawnOnPlayerDeath);
|
||||
actor.insert(actor::HitPoints::default());
|
||||
actor.insert(Position::from(absolute_pos));
|
||||
actor.insert(Rotation::from(state.rotation));
|
||||
if state.is_sphere {
|
||||
let sphere_texture_handle = if let Some(model) = &state.model {
|
||||
Some(asset_server.load(format!("textures/{}.jpg", model)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
rotation = Quat::from_rotation_x(-90f32.to_radians()) * rotation;
|
||||
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128));
|
||||
let sphere_material_handle = materials.add(StandardMaterial {
|
||||
base_color_texture: sphere_texture_handle,
|
||||
|
@ -626,9 +603,8 @@ fn spawn_entities(
|
|||
transform: Transform::from_scale(scale),
|
||||
..default()
|
||||
});
|
||||
load_asset(model.as_str(), &mut actor, &*asset_server);
|
||||
skeleton::load(model.as_str(), &mut actor, &*asset_server);
|
||||
}
|
||||
actor.insert(Rotation::from(rotation));
|
||||
|
||||
// Physics Parameters
|
||||
if state.has_physics {
|
||||
|
@ -699,7 +675,6 @@ fn spawn_entities(
|
|||
integrity: state.suit_integrity,
|
||||
..default()
|
||||
});
|
||||
actor.insert(actor::Battery::default());
|
||||
}
|
||||
if state.is_clickable {
|
||||
actor.insert(hud::IsClickable {
|
||||
|
@ -739,22 +714,15 @@ fn spawn_entities(
|
|||
pronoun: state.pronoun.clone(),
|
||||
talking_speed: 1.0,
|
||||
});
|
||||
if let Some(name) = &state.name {
|
||||
achievement_tracker.all_people.insert(name.clone());
|
||||
}
|
||||
}
|
||||
if state.is_vehicle {
|
||||
actor.insert(actor::Vehicle::default());
|
||||
if let Some(name) = &state.name {
|
||||
achievement_tracker.all_vehicles.insert(name.clone());
|
||||
}
|
||||
}
|
||||
if state.is_vehicle
|
||||
|| state.is_suited
|
||||
|| state.thrust_forward > 0.0
|
||||
|| state.thrust_sideways > 0.0
|
||||
|| state.thrust_back > 0.0
|
||||
|| state.reaction_wheels > 0.0
|
||||
if state.is_vehicle || state.is_suited
|
||||
|| state.thrust_forward > 0.0
|
||||
|| state.thrust_sideways > 0.0
|
||||
|| state.thrust_back > 0.0
|
||||
|| state.reaction_wheels > 0.0
|
||||
{
|
||||
actor.insert(actor::Engine {
|
||||
thrust_forward: state.thrust_forward,
|
||||
|
@ -769,32 +737,6 @@ fn spawn_entities(
|
|||
if let Some(_) = state.ar_model {
|
||||
actor.insert(hud::AugmentedRealityOverlayBroadcaster);
|
||||
}
|
||||
if state.is_player {
|
||||
actor.with_children(|builder| {
|
||||
builder.spawn((
|
||||
world::DespawnOnPlayerDeath,
|
||||
actor::PlayersFlashLight,
|
||||
SpotLightBundle {
|
||||
transform: Transform {
|
||||
translation: Vec3::new(0.0, 0.0, 1.0),
|
||||
rotation: Quat::from_rotation_y(180f32.to_radians()),
|
||||
..default()
|
||||
},
|
||||
spot_light: SpotLight {
|
||||
intensity: 40_000_000.0, // lumens
|
||||
color: Color::WHITE,
|
||||
shadows_enabled: true,
|
||||
inner_angle: PI32 / 8.0 * 0.85,
|
||||
outer_angle: PI32 / 4.0,
|
||||
range: 2000.0,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
}
|
||||
));
|
||||
});
|
||||
}
|
||||
actor_entity = actor.id();
|
||||
}
|
||||
|
||||
|
@ -811,10 +753,10 @@ fn spawn_entities(
|
|||
NotShadowCaster,
|
||||
NotShadowReceiver,
|
||||
));
|
||||
load_asset(ar_asset_name, &mut entitycmd, &*asset_server);
|
||||
skeleton::load(ar_asset_name, &mut entitycmd, &*asset_server);
|
||||
}
|
||||
|
||||
if state.is_point_of_interest || state.is_moon || state.is_planet {
|
||||
if state.is_point_of_interest {
|
||||
let mut entitycmd = commands.spawn((
|
||||
hud::PointOfInterestMarker(actor_entity),
|
||||
world::DespawnOnPlayerDeath,
|
||||
|
@ -826,23 +768,15 @@ fn spawn_entities(
|
|||
NotShadowCaster,
|
||||
NotShadowReceiver,
|
||||
));
|
||||
let model = if state.is_point_of_interest {
|
||||
"point_of_interest"
|
||||
} else if state.is_planet {
|
||||
"marker_planets"
|
||||
} else {
|
||||
"marker_satellites"
|
||||
};
|
||||
load_asset(model, &mut entitycmd, &*asset_server);
|
||||
skeleton::load("point_of_interest", &mut entitycmd, &*asset_server);
|
||||
}
|
||||
|
||||
if state.has_ring {
|
||||
let ring_radius = state.model_scale * (nature::JUPITER_RING_RADIUS / nature::JUPITER_RADIUS) as f32;
|
||||
commands.spawn((
|
||||
world::DespawnOnPlayerDeath,
|
||||
MaterialMeshBundle {
|
||||
mesh: meshes.add(Mesh::from(Cylinder::new(ring_radius, 1.0))),
|
||||
material: materials_jupiter.add(load::JupitersRing {
|
||||
mesh: meshes.add(Mesh::from(Cylinder::new(nature::JUPITER_RING_RADIUS as f32, 1.0))),
|
||||
material: materials_jupiter.add(shading::JupitersRing {
|
||||
alpha_mode: AlphaMode::Blend,
|
||||
ring_radius: nature::JUPITER_RING_RADIUS as f32,
|
||||
jupiter_radius: nature::JUPITER_RADIUS as f32,
|
||||
|
@ -851,7 +785,8 @@ fn spawn_entities(
|
|||
..default()
|
||||
},
|
||||
Position::new(absolute_pos),
|
||||
Rotation::from(Quat::from_rotation_z(-state.axialtilt.to_radians())),
|
||||
Rotation::from(Quat::IDENTITY),
|
||||
//Rotation::from(Quat::from_rotation_x(-0.3f32.to_radians())),
|
||||
NotShadowCaster,
|
||||
NotShadowReceiver,
|
||||
));
|
||||
|
@ -867,38 +802,3 @@ pub fn hide_colliders(mut q_mesh: Query<(&mut Visibility, &Name), (Added<Visibil
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_mesh(
|
||||
mut commands: Commands,
|
||||
mut q_mesh: Query<(Entity, &Name, &Parent), Added<Handle<Mesh>>>,
|
||||
q_parents: Query<(Option<&Parent>, Option<&actor::Player>, Option<&NotShadowCaster>, Option<&NotShadowReceiver>)>,
|
||||
) {
|
||||
// Add "PlayerCollider" component to the player's collider mesh entity
|
||||
for (child_entity, child_name, child_parent) in &mut q_mesh {
|
||||
// get the root parent
|
||||
let mut get_parent = q_parents.get(child_parent.get());
|
||||
while let Ok((parent_maybe, _, _, _)) = get_parent {
|
||||
if let Some(parent) = parent_maybe {
|
||||
get_parent = q_parents.get(parent.get());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok((_, player, noshadowcast, noshadowrecv)) = get_parent {
|
||||
let childcmd = &mut commands.entity(child_entity);
|
||||
|
||||
// If the root parent is the player, add PlayerCollider to the collider mesh
|
||||
if player.is_some() && child_name.as_str() == "Collider" {
|
||||
childcmd.insert(actor::PlayerCollider);
|
||||
}
|
||||
|
||||
if noshadowcast.is_some() {
|
||||
childcmd.insert(NotShadowCaster);
|
||||
}
|
||||
if noshadowrecv.is_some() {
|
||||
childcmd.insert(NotShadowReceiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
107
src/common.rs
|
@ -1,107 +0,0 @@
|
|||
// ▄████████▄ + ███ + ▄█████████ ███ +
|
||||
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
||||
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
||||
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
||||
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
||||
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
||||
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
||||
// + + + ███
|
||||
// + ▀████████████████████████████████████████████████████▀
|
||||
//
|
||||
// Various common functions and constants
|
||||
|
||||
use bevy::prelude::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub use bevy::math::{DVec3, DQuat};
|
||||
pub use std::f32::consts::PI as PI32;
|
||||
pub use std::f64::consts::PI;
|
||||
|
||||
pub const GAME_NAME: &str = "OutFly";
|
||||
pub const CONF_FILE: &str = "outfly.toml";
|
||||
pub const FONT: &str = "fonts/Yupiter-Regular.ttf";
|
||||
|
||||
pub const EPSILON32: f32 = 1e-9;
|
||||
pub const EPSILON: f64 = 1e-9;
|
||||
|
||||
pub const COLOR_BODY: &str = "#888888"; // For simple text
|
||||
pub const COLOR_DIM: &str = "#666666"; // For darker, less important text
|
||||
pub const COLOR_PRIMARY: &str = "#BE1251"; // The "branding" color
|
||||
pub const COLOR_SECONDARY: &str = "#CCCCCC"; // For accents
|
||||
pub const COLOR_SUCCESS: &str = "#F0D50C"; // For positive outcomes
|
||||
pub const COLOR_DANGER: &str = "#CCCCCC"; // For critical situations
|
||||
pub const COLOR_WARNING: &str = "#F0D50C"; // For warnings
|
||||
|
||||
#[inline]
|
||||
pub fn bool2vis(boolean: bool) -> Visibility {
|
||||
if boolean {
|
||||
Visibility::Inherited
|
||||
} else {
|
||||
Visibility::Hidden
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style_fullscreen() -> Style {
|
||||
Style {
|
||||
width: Val::Vw(100.0),
|
||||
height: Val::Vh(100.0),
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn style_centered() -> Style {
|
||||
Style {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
..default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alive(settings: Res<Settings>) -> bool {
|
||||
return settings.alive;
|
||||
}
|
||||
|
||||
pub fn in_control(settings: Res<Settings>) -> bool {
|
||||
return settings.in_control();
|
||||
}
|
||||
|
||||
pub fn in_shadow(
|
||||
light_source_pos: DVec3,
|
||||
light_source_r: f64,
|
||||
shadow_caster_pos: DVec3,
|
||||
shadow_caster_r: f64,
|
||||
camera_pos: DVec3
|
||||
) -> bool {
|
||||
if light_source_r < shadow_caster_r {
|
||||
error!("common::in_shadow only works if light_source_r > shadow_caster_r");
|
||||
return false;
|
||||
}
|
||||
|
||||
let direction = (shadow_caster_pos - light_source_pos).normalize();
|
||||
let shadow_caster_to_player = camera_pos - shadow_caster_pos;
|
||||
|
||||
// Calculate the distance to the shadow caster if the player was projected
|
||||
// onto the line of the shadow, with positive numbers = in the shadow,
|
||||
// negative numbers = in between light source and shadow caster.
|
||||
let projection_length = shadow_caster_to_player.dot(direction);
|
||||
if projection_length < 0.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let projection = projection_length * direction;
|
||||
|
||||
// Calculate the vector to the closest point along the shadow line
|
||||
let closest_vector = shadow_caster_to_player - projection;
|
||||
|
||||
let distance_between_light_and_caster = (shadow_caster_pos - light_source_pos).length();
|
||||
let max_distance = shadow_caster_r + ((shadow_caster_r - light_source_r) / distance_between_light_and_caster) * projection_length;
|
||||
|
||||
return closest_vector.length() < max_distance;
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
Though I heard
|
||||
Everyone goes this road
|
||||
Eventually
|
||||
I didn't expect that
|
||||
I'd be on it yesterday or today
|
||||
- Ariwara no Narihira, 880
|
||||
|
||||
A flower of Sponge cucumber blooms
|
||||
Phlegm gets caught
|
||||
In the dead's throat
|
||||
- Masaoka Shiki, 1902
|
||||
|
||||
There is no death;
|
||||
there is no life.
|
||||
Indeed, the skies are cloudless
|
||||
And the river waters clear.
|
||||
- Toshimoto, Taiheiki
|
||||
|
||||
I wish to die
|
||||
in spring, beneath
|
||||
the cherry blossoms,
|
||||
while the springtime moon
|
||||
is full.
|
||||
- Saigyo, 1190
|
||||
|
||||
Illusion appears, illusion ceases
|
||||
The biggest illusion among all is our body
|
||||
Once a pacified heart finds its place
|
||||
There's no such body to look for
|
||||
- Zheng Ting, 621
|
||||
|
||||
Empty-handed I entered the world
|
||||
Barefoot I leave it.
|
||||
My coming, my going —
|
||||
Two simple happenings
|
||||
That got entangled.
|
||||
- Kozan Ichikyo, 1360
|
||||
|
||||
Bury me when I die
|
||||
beneath a wine barrel
|
||||
in a tavern.
|
||||
With luck
|
||||
the cask will leak.
|
||||
- Moriya Sen'an 1838
|
||||
|
||||
I wish I could enjoy,
|
||||
the rest of Spring,
|
||||
as the cherry blossoms are yet in bloom,
|
||||
in spite of the spring breeze
|
||||
which is attempting to blow off all their petals.
|
||||
– Asano Naganori, 1701
|
||||
|
||||
To be saved from the chasm
|
||||
why cling to the cliff?
|
||||
Clouds floating low never know where the breezes will blow them.
|
||||
- Sengai Gibon, 1837
|
||||
|
||||
Flash of steel stills me;
|
||||
calmness mirrors the ocean;
|
||||
I await the waves.
|
||||
*
|
||||
The death of blossoms;
|
||||
is not something to grieve on;
|
||||
but the way of things.
|
||||
- Asakura Sōteki, 1555
|
||||
|
||||
The glory and prosperity of my life was as good as a single cup of sake.
|
||||
My life of forty-nine years is passed like a dream.
|
||||
I know not what life is; nor death.
|
||||
Both Heaven and Hell are left behind.
|
||||
I stand in the moonlit dawn; free from clouds of attachment.
|
||||
- Uesugi Kenshin, 1578
|
||||
|
||||
My whole life long I've sharpened my sword
|
||||
And now, face to face with death
|
||||
I unsheathe it, and lo-
|
||||
The blade is broken-
|
||||
Alas!
|
||||
– Dairin Soto, 1568
|
||||
|
||||
I borrow moonlight
|
||||
for this journey of a
|
||||
million miles
|
||||
- Saikaku, 1730
|
||||
|
||||
Not knowing
|
||||
that my body lies
|
||||
upon Mount Kamo's rocks,
|
||||
my love
|
||||
awaits me.
|
||||
- Kakinomoto no Hitomaro, 707
|
||||
|
||||
Overtaken by darkness
|
||||
I will lodge under
|
||||
the boughs of a tree.
|
||||
Flowers alone
|
||||
host me tonight.
|
||||
- Taira no Tadanori, 1184
|
||||
|
||||
Like a rotten log
|
||||
half-buried in the ground-
|
||||
my life, which
|
||||
has not flowered, comes
|
||||
to this sad end.
|
||||
- Minamoto no Yorimasa, 1180
|
||||
|
||||
Had I not known
|
||||
that I was dead
|
||||
already
|
||||
I would have mourned
|
||||
my loss of life.
|
||||
- Ota Dokan, 1486
|
||||
|
||||
Since I was born
|
||||
I have to die,
|
||||
and so ...
|
||||
- Kisei, 1764
|
||||
|
||||
Till now I thought
|
||||
that death befell
|
||||
the untalented alone.
|
||||
If those with talent, too, must die
|
||||
surely they make a better manure?
|
||||
- Morikawa Kyoriku, 1715
|
||||
|
||||
One leaf lets go, and
|
||||
then another takes
|
||||
the wind.
|
||||
- Ransetsu, 1707
|
||||
|
||||
I cleansed the mirror
|
||||
of my heart - now it reflects
|
||||
the moon.
|
||||
- Renseki, 1789
|
||||
|
||||
Let them bloom or
|
||||
let them die-it's all the same:
|
||||
cherry trees on Mount Yoshino.
|
||||
- Rekisen, 1834
|
||||
|
||||
Depths of cold
|
||||
unfathomable
|
||||
ocean roar.
|
||||
- Kasenjo, 1776
|
||||
|
||||
A tune of non-being
|
||||
Filling the void:
|
||||
Spring sun
|
||||
Snow whiteness
|
||||
Bright clouds
|
||||
Clear wind.
|
||||
- Daido Ichi'i, 1370
|
||||
|
||||
I raise the mirror of my life
|
||||
Up to my face: sixty years.
|
||||
With a swing I smash the reflection-
|
||||
The world as usual
|
||||
All in its place.
|
||||
- Taigen Sofu, 1555
|
||||
|
||||
I cast the brush aside-
|
||||
from here on I'll speak to the moon
|
||||
face to face.
|
||||
- Koha, 1897
|
||||
|
||||
The joy of dewdrops
|
||||
in the grass as they
|
||||
turn back to vapor.
|
||||
- Koraku, 1837
|
||||
|
||||
The foam on the last water
|
||||
has dissolved
|
||||
my mind is clear.
|
||||
- Mitoku, 1669
|
||||
|
||||
Still tied to the world,
|
||||
I cool off and lose
|
||||
my form.
|
||||
- Ozui, 1783
|
||||
|
||||
Festival of Souls:
|
||||
yesterday I hosted them
|
||||
today I am a guest ...
|
||||
- Sofu, 1891
|
||||
|
||||
Spitting blood
|
||||
clears up reality
|
||||
and dream alike.
|
||||
- Sunao, 1926
|
||||
|
||||
I go back
|
||||
to the void where frost and snow
|
||||
won't bother me.
|
||||
- Tojaku, 1799
|
||||
|
||||
My life was
|
||||
lunacy until
|
||||
this moonlit night.
|
||||
- Tokugen, 1647
|
||||
|
||||
The owner of the cherry blossoms
|
||||
turns to compost
|
||||
for the trees.
|
||||
- Utsu, 1863
|
|
@ -1,24 +1,22 @@
|
|||
Space: Slow down, match velocity
|
||||
E: Interact
|
||||
F: Flashlight
|
||||
Space: Slow down (or match velocity)
|
||||
AWSD/Shift/Ctrl: Movement
|
||||
R: Rotate (hold + move mouse)
|
||||
E: Interact: Talk to people, enter vehicles
|
||||
Q: Exit vehicle
|
||||
M: Map
|
||||
C: Camera
|
||||
T: Cruise control
|
||||
R: Rotate (hold + move mouse)
|
||||
Tab: Toggle HUD + Augmented Reality
|
||||
Left click: Target objects [AUGMENTED REALITY ONLY]
|
||||
Right click: Zoom [AUGMENTED REALITY ONLY]
|
||||
F: 3rd person view
|
||||
Y: Rotation stabilizer
|
||||
AWSD/Shift/Ctrl: Move
|
||||
J/K/U/L/I/O: Rotate
|
||||
F11: Fullscreen
|
||||
Tab: Toggle Augmented Reality
|
||||
|
||||
Augmented Reality only:
|
||||
Left click: Target objects
|
||||
Right click: Zoom
|
||||
|
||||
Cheats:
|
||||
G: Toggle cheats + invulnerability
|
||||
V/B: Impossible acceleration
|
||||
Shift+V/B: Extreme acceleration
|
||||
X: Teleport to target
|
||||
Z: Stop
|
||||
F2: Toggle shadows
|
||||
F3: Toggle sound effects
|
||||
F4: Toggle music
|
||||
F7: Restart game
|
||||
F11: Toggle fullscreen
|
||||
JKULIO: Mouseless camera rotation
|
||||
G: Toggle god mode + cheats [CHEAT]
|
||||
V/B: Impossible acceleration forward/backward [CHEAT]
|
||||
Shift+V/B: Same as V/B, but a thousand times faster [CHEAT]
|
||||
C: Impossibly instant stopping [CHEAT]
|
||||
X: Teleport to target [CHEAT]
|
||||
|
|
|
@ -11,18 +11,17 @@
|
|||
// This module manages visual effects.
|
||||
|
||||
use bevy::prelude::*;
|
||||
use crate::prelude::*;
|
||||
use crate::{camera, var};
|
||||
|
||||
pub struct VisualPlugin;
|
||||
pub struct EffectsPlugin;
|
||||
|
||||
impl Plugin for VisualPlugin {
|
||||
impl Plugin for EffectsPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup.after(menu::setup).after(hud::setup));
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Startup, spawn_effects.after(setup).after(camera::setup_camera));
|
||||
app.add_systems(Update, spawn_effects);
|
||||
app.add_systems(Update, update_fadein);
|
||||
app.add_systems(Update, update_fadeout);
|
||||
app.add_systems(Update, play_animations);
|
||||
// Blackout disabled for now
|
||||
//app.add_systems(Update, update_blackout);
|
||||
app.add_event::<SpawnEffectEvent>();
|
||||
|
@ -94,7 +93,14 @@ pub fn spawn_effects(
|
|||
},
|
||||
FadeIn,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
style: Style {
|
||||
width: Val::Vw(100.0),
|
||||
height: Val::Vh(100.0),
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
..default()
|
||||
},
|
||||
background_color: color.into(),
|
||||
..default()
|
||||
},
|
||||
|
@ -109,7 +115,14 @@ pub fn spawn_effects(
|
|||
},
|
||||
FadeOut,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
style: Style {
|
||||
width: Val::Vw(100.0),
|
||||
height: Val::Vh(100.0),
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(0.0),
|
||||
left: Val::Px(0.0),
|
||||
..default()
|
||||
},
|
||||
background_color: color.with_a(0.0).into(),
|
||||
..default()
|
||||
},
|
||||
|
@ -152,16 +165,6 @@ pub fn update_fadeout(
|
|||
}
|
||||
}
|
||||
|
||||
fn play_animations(
|
||||
mut players: Query<&mut AnimationPlayer, Added<AnimationPlayer>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
for mut player in &mut players {
|
||||
let animation = asset_server.load("models/suit_v2/suit_v2.glb#Animation0");
|
||||
player.play(animation.clone()).repeat();
|
||||
}
|
||||
}
|
||||
|
||||
// Blackout disabled for now
|
||||
//pub fn update_blackout(
|
||||
// mut q_effect: Query<&mut BackgroundColor, With<BlackOutOverlay>>,
|
429
src/game.rs
|
@ -1,429 +0,0 @@
|
|||
// ▄████████▄ + ███ + ▄█████████ ███ +
|
||||
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
||||
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
||||
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
||||
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
||||
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
||||
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
||||
// + + + ███
|
||||
// + ▀████████████████████████████████████████████████████▀
|
||||
//
|
||||
// This module handles player input, and coordinates interplay between other modules
|
||||
|
||||
use crate::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy::pbr::ExtendedMaterial;
|
||||
use bevy::scene::SceneInstance;
|
||||
use bevy::window::{Window, WindowMode, PrimaryWindow};
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct GamePlugin;
|
||||
impl Plugin for GamePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, handle_cheats.run_if(in_control));
|
||||
app.add_systems(Update, debug);
|
||||
app.add_systems(PostUpdate, handle_game_event);
|
||||
app.add_systems(PreUpdate, handle_player_death);
|
||||
app.add_systems(PostUpdate, update_id2pos);
|
||||
app.add_systems(Update, handle_achievement_event.run_if(on_event::<AchievementEvent>()));
|
||||
app.add_systems(Update, check_achievements);
|
||||
app.insert_resource(Id2Pos(HashMap::new()));
|
||||
app.insert_resource(var::AchievementTracker::default());
|
||||
app.insert_resource(AchievementCheckTimer(
|
||||
Timer::from_seconds(1.0, TimerMode::Repeating)));
|
||||
app.add_event::<PlayerDiesEvent>();
|
||||
app.add_event::<GameEvent>();
|
||||
app.add_event::<AchievementEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType);
|
||||
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||
#[derive(Resource)] pub struct AchievementCheckTimer(pub Timer);
|
||||
|
||||
#[derive(Event)]
|
||||
pub enum AchievementEvent {
|
||||
RepairSuit,
|
||||
TalkTo(String),
|
||||
RideVehicle(String),
|
||||
DrinkPizza,
|
||||
InJupitersShadow,
|
||||
FindEarth,
|
||||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub enum GameEvent {
|
||||
SetAR(Turn),
|
||||
SetMusic(Turn),
|
||||
SetSound(Turn),
|
||||
SetMap(Turn),
|
||||
SetFullscreen(Turn),
|
||||
SetMenu(Turn),
|
||||
SetThirdPerson(Turn),
|
||||
SetRotationStabilizer(Turn),
|
||||
SetShadows(Turn),
|
||||
Achievement(String),
|
||||
}
|
||||
|
||||
pub enum Turn {
|
||||
On,
|
||||
Off,
|
||||
Toggle,
|
||||
}
|
||||
|
||||
impl Turn {
|
||||
pub fn to_bool(&self, current_state: bool) -> bool {
|
||||
match self {
|
||||
Turn::On => true,
|
||||
Turn::Off => false,
|
||||
Turn::Toggle => !current_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_game_event(
|
||||
mut settings: ResMut<Settings>,
|
||||
mut er_game: EventReader<GameEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_updateoverlays: EventWriter<hud::UpdateOverlayVisibility>,
|
||||
mut ew_updatemenu: EventWriter<menu::UpdateMenuEvent>,
|
||||
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
|
||||
mut q_window: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut q_light: Query<&mut DirectionalLight>,
|
||||
mut mapcam: ResMut<camera::MapCam>,
|
||||
mut log: ResMut<hud::Log>,
|
||||
opt: Res<var::CommandLineOptions>,
|
||||
) {
|
||||
for event in er_game.read() {
|
||||
match event {
|
||||
GameEvent::SetAR(turn) => {
|
||||
settings.hud_active = turn.to_bool(settings.hud_active);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||
}
|
||||
GameEvent::SetMusic(turn) => {
|
||||
// TODO invert "mute_music" to "music_active"
|
||||
settings.mute_music = turn.to_bool(settings.mute_music);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
}
|
||||
GameEvent::SetSound(turn) => {
|
||||
// TODO invert "mute_sfx" to "sfx_active"
|
||||
settings.mute_sfx = turn.to_bool(settings.mute_sfx);
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
}
|
||||
GameEvent::SetMap(turn) => {
|
||||
settings.map_active = turn.to_bool(settings.map_active);
|
||||
if settings.map_active {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Woosh));
|
||||
}
|
||||
*mapcam = camera::MapCam::default();
|
||||
ew_updateoverlays.send(hud::UpdateOverlayVisibility);
|
||||
}
|
||||
GameEvent::SetFullscreen(turn) => {
|
||||
for mut window in &mut q_window {
|
||||
let current_state = window.mode != WindowMode::Windowed;
|
||||
window.mode = match turn.to_bool(current_state) {
|
||||
true => opt.window_mode_fullscreen,
|
||||
false => WindowMode::Windowed
|
||||
};
|
||||
}
|
||||
}
|
||||
GameEvent::SetMenu(turn) => {
|
||||
settings.menu_active = turn.to_bool(settings.menu_active);
|
||||
ew_updatemenu.send(menu::UpdateMenuEvent);
|
||||
}
|
||||
GameEvent::SetThirdPerson(turn) => {
|
||||
settings.third_person = turn.to_bool(settings.third_person);
|
||||
}
|
||||
GameEvent::SetRotationStabilizer(turn) => {
|
||||
settings.rotation_stabilizer_active
|
||||
= turn.to_bool(settings.rotation_stabilizer_active);
|
||||
}
|
||||
GameEvent::SetShadows(turn) => {
|
||||
settings.shadows_sun = turn.to_bool(settings.shadows_sun);
|
||||
for mut light in &mut q_light {
|
||||
light.shadows_enabled = settings.shadows_sun;
|
||||
}
|
||||
}
|
||||
GameEvent::Achievement(name) => {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Achieve));
|
||||
log.add(format!("Achievement accomplished: {name}!"),
|
||||
"".to_string(), hud::LogLevel::Achievement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_player_death(
|
||||
mut cmd: Commands,
|
||||
mut er_playerdies: EventReader<PlayerDiesEvent>,
|
||||
q_scenes: Query<(Entity, &SceneInstance), With<world::DespawnOnPlayerDeath>>,
|
||||
q_noscenes: Query<Entity, (With<world::DespawnOnPlayerDeath>, Without<SceneInstance>)>,
|
||||
mut scene_spawner: ResMut<SceneSpawner>,
|
||||
mut active_asteroids: ResMut<world::ActiveAsteroids>,
|
||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||
mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>,
|
||||
mut log: ResMut<hud::Log>,
|
||||
mut settings: ResMut<Settings>,
|
||||
) {
|
||||
for death in er_playerdies.read() {
|
||||
if settings.god_mode {
|
||||
return;
|
||||
}
|
||||
settings.reset_player_settings();
|
||||
active_asteroids.0.clear();
|
||||
for entity in &q_noscenes {
|
||||
cmd.entity(entity).despawn();
|
||||
}
|
||||
for (entity, sceneinstance) in &q_scenes {
|
||||
cmd.entity(entity).despawn();
|
||||
scene_spawner.despawn_instance(**sceneinstance);
|
||||
}
|
||||
log.clear();
|
||||
|
||||
match death.0 {
|
||||
actor::DamageType::Depressurization => {
|
||||
settings.death_cause = "Depressurization".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::BLACK),
|
||||
duration: 4.0,
|
||||
});
|
||||
}
|
||||
actor::DamageType::Mental => {
|
||||
settings.death_cause = "Brain Damage".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::BLACK),
|
||||
duration: 4.0,
|
||||
});
|
||||
}
|
||||
actor::DamageType::Asphyxiation => {
|
||||
settings.death_cause = "Suffocation".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::BLACK),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
actor::DamageType::Trauma => {
|
||||
settings.death_cause = "Trauma".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
actor::DamageType::GForce => {
|
||||
settings.death_cause = "Trauma from excessive g forces".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
settings.death_cause = "Unknown".to_string();
|
||||
ew_effect.send(visual::SpawnEffectEvent {
|
||||
class: visual::Effects::FadeIn(Color::MAROON),
|
||||
duration: 1.0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ew_deathscreen.send(menu::DeathScreenEvent::Show);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_cheats(
|
||||
key_input: Res<ButtonInput<KeyCode>>,
|
||||
mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||
mut q_life: Query<(&mut actor::LifeForm, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
||||
q_target: Query<(&Transform, &Position, Option<&LinearVelocity>), (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||
mut settings: ResMut<Settings>,
|
||||
id2pos: Res<Id2Pos>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
) {
|
||||
if q_player.is_empty() || q_life.is_empty() {
|
||||
return;
|
||||
}
|
||||
let (trans, mut pos, mut v) = q_player.get_single_mut().unwrap();
|
||||
let (mut lifeform, mut gforce) = q_life.get_single_mut().unwrap();
|
||||
let boost = if key_input.pressed(KeyCode::ShiftLeft) {
|
||||
1e6
|
||||
} else {
|
||||
1e3
|
||||
};
|
||||
if key_input.just_pressed(settings.key_cheat_god_mode) {
|
||||
settings.god_mode ^= true;
|
||||
if settings.god_mode {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::EnterVehicle));
|
||||
}
|
||||
}
|
||||
if !settings.god_mode && !settings.dev_mode {
|
||||
return;
|
||||
}
|
||||
|
||||
if key_input.just_pressed(settings.key_cheat_stop) {
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
v.0 = DVec3::ZERO;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_speed) || key_input.pressed(settings.key_cheat_speed_backward) {
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
let sign = if key_input.pressed(settings.key_cheat_speed) { 1.0 } else { -1.0 };
|
||||
let dv = DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, sign * boost));
|
||||
let current_speed = v.0.length();
|
||||
let next_speed = (v.0 + dv).length();
|
||||
let avg_speed = (current_speed + next_speed) / 2.0;
|
||||
let inv_lorentz = nature::inverse_lorentz_factor(avg_speed.clamp(0.0, nature::C));
|
||||
v.0 = v.0 + inv_lorentz * dv;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_teleport) {
|
||||
if let Ok((transform, target_pos, target_v)) = q_target.get_single() {
|
||||
let offset: DVec3 = 4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3();
|
||||
pos.0 = **target_pos + offset;
|
||||
if let Some(target_v) = target_v {
|
||||
*v = target_v.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !settings.dev_mode {
|
||||
return;
|
||||
}
|
||||
|
||||
if key_input.just_pressed(settings.key_cheat_pizza) {
|
||||
if let Some(target) = id2pos.0.get(&"pizzeria".to_string()) {
|
||||
pos.0 = *target + DVec3::new(-60.0, 0.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview1) {
|
||||
if let Some(target) = id2pos.0.get(&"busstopclippy2".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_farview2) {
|
||||
if let Some(target) = id2pos.0.get(&"busstopclippy3".to_string()) {
|
||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||
gforce.ignore_gforce_seconds = 1.0;
|
||||
}
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_zero) {
|
||||
lifeform.adrenaline = 0.0;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_mid) {
|
||||
lifeform.adrenaline = 0.5;
|
||||
}
|
||||
if key_input.pressed(settings.key_cheat_adrenaline_max) {
|
||||
lifeform.adrenaline = 1.0;
|
||||
}
|
||||
if key_input.just_pressed(settings.key_cheat_die) {
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(PlayerDiesEvent(actor::DamageType::Trauma));
|
||||
}
|
||||
}
|
||||
|
||||
fn update_id2pos(
|
||||
mut id2pos: ResMut<Id2Pos>,
|
||||
q_id: Query<(&Position, &actor::Identifier)>,
|
||||
) {
|
||||
id2pos.0.clear();
|
||||
for (pos, id) in &q_id {
|
||||
id2pos.0.insert(id.0.clone(), pos.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn debug(
|
||||
settings: Res<var::Settings>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>>,
|
||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
||||
) {
|
||||
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyP) {
|
||||
for (entity, _name, mesh) in &materials {
|
||||
dbg!(mesh);
|
||||
let mut entity = commands.entity(entity);
|
||||
entity.remove::<Handle<StandardMaterial>>();
|
||||
let material = extended_materials.add(load::AsteroidSurface::material());
|
||||
entity.insert(material);
|
||||
}
|
||||
}
|
||||
if settings.dev_mode && keyboard_input.just_pressed(KeyCode::KeyN) {
|
||||
achievement_tracker.achieve_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_achievement_event(
|
||||
mut er_achievement: EventReader<AchievementEvent>,
|
||||
mut ew_game: EventWriter<GameEvent>,
|
||||
mut tracker: ResMut<var::AchievementTracker>,
|
||||
) {
|
||||
for event in er_achievement.read() {
|
||||
match event {
|
||||
AchievementEvent::RepairSuit => {
|
||||
if !tracker.repair_suit {
|
||||
ew_game.send(GameEvent::Achievement("Repair Your Suit".into()));
|
||||
}
|
||||
tracker.repair_suit = true;
|
||||
}
|
||||
AchievementEvent::InJupitersShadow => {
|
||||
if !tracker.in_jupiters_shadow {
|
||||
ew_game.send(GameEvent::Achievement("Eclipse the sun with Jupiter".into()));
|
||||
}
|
||||
tracker.in_jupiters_shadow = true;
|
||||
}
|
||||
AchievementEvent::DrinkPizza => {
|
||||
if !tracker.drink_a_pizza {
|
||||
ew_game.send(GameEvent::Achievement("Enjoy A Pizza".into()));
|
||||
}
|
||||
tracker.drink_a_pizza = true;
|
||||
}
|
||||
AchievementEvent::FindEarth => {
|
||||
if !tracker.find_earth {
|
||||
ew_game.send(GameEvent::Achievement("Find Earth".into()));
|
||||
}
|
||||
tracker.find_earth = true;
|
||||
}
|
||||
AchievementEvent::RideVehicle(name) => {
|
||||
tracker.vehicles_ridden.insert(name.clone());
|
||||
let len = tracker.vehicles_ridden.len();
|
||||
let total = tracker.all_vehicles.len();
|
||||
if !tracker.ride_every_vehicle && len == total {
|
||||
tracker.ride_every_vehicle = true;
|
||||
ew_game.send(GameEvent::Achievement("Ride Every Vehicle".into()));
|
||||
}
|
||||
}
|
||||
AchievementEvent::TalkTo(name) => {
|
||||
tracker.people_talked_to.insert(name.clone());
|
||||
let len = tracker.people_talked_to.len();
|
||||
let total = tracker.all_people.len();
|
||||
if !tracker.talk_to_everyone && len == total {
|
||||
tracker.talk_to_everyone = true;
|
||||
ew_game.send(GameEvent::Achievement("Talk To Everyone".into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_achievements(
|
||||
time: Res<Time>,
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
id2pos: Res<Id2Pos>,
|
||||
mut ew_achievement: EventWriter<AchievementEvent>,
|
||||
mut timer: ResMut<AchievementCheckTimer>,
|
||||
) {
|
||||
if !timer.0.tick(time.delta()).just_finished() { return; }
|
||||
let pos_player = if let Ok(pos) = q_player.get_single() { pos } else { return; };
|
||||
let pos_sun = if let Some(pos) = id2pos.0.get("sol") { pos } else { return; };
|
||||
let pos_jupiter = if let Some(pos) = id2pos.0.get("jupiter") { pos } else { return; };
|
||||
|
||||
let shadowed = in_shadow(*pos_sun, nature::SOL_RADIUS,
|
||||
*pos_jupiter, nature::JUPITER_RADIUS, **pos_player);
|
||||
|
||||
if shadowed {
|
||||
ew_achievement.send(AchievementEvent::InJupitersShadow);
|
||||
}
|
||||
}
|
433
src/hud.rs
|
@ -10,56 +10,46 @@
|
|||
//
|
||||
// This module manages the heads-up display and augmented reality overlays.
|
||||
|
||||
use crate::prelude::*;
|
||||
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
||||
use crate::{actor, audio, camera, chat, nature, skeleton, var};
|
||||
use bevy::prelude::*;
|
||||
use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
|
||||
use bevy::transform::TransformSystem;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub const DASHBOARD_ICON_SIZE: f32 = 64.0;
|
||||
pub const HUD_REFRESH_TIME: f32 = 0.1;
|
||||
pub const FONT: &str = "fonts/Yupiter-Regular.ttf";
|
||||
pub const LOG_MAX_TIME_S: f64 = 30.0;
|
||||
pub const LOG_MAX_ROWS: usize = 30;
|
||||
pub const LOG_MAX: usize = LOG_MAX_ROWS;
|
||||
pub const MAX_CHOICES: usize = 10;
|
||||
pub const SPEEDOMETER_WIDTH: f32 = 20.0;
|
||||
pub const SPEEDOMETER_WIDTH: f32 = 40.0;
|
||||
pub const SPEEDOMETER_HEIGHT: f32 = 10.0;
|
||||
pub const AMBIENT_LIGHT: f32 = 0.0; // Space is DARK
|
||||
pub const AMBIENT_LIGHT_AR: f32 = 20.0;
|
||||
pub const AMBIENT_LIGHT_AR: f32 = 30.0;
|
||||
//pub const REPLY_NUMBERS: [char; 10] = ['❶', '❷', '❸', '❹', '❺', '❻', '❼', '❽', '❾', '⓿'];
|
||||
//pub const REPLY_NUMBERS: [char; 10] = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩'];
|
||||
pub const REPLY_NUMBERS: [char; 10] = ['➀', '➁', '➂', '➃', '➄', '➅', '➆', '➇', '➈', '➉'];
|
||||
|
||||
pub const DASHBOARD_DEF: &[(Dashboard, &str)] = &[
|
||||
(Dashboard::Flashlight, "highbeams"),
|
||||
(Dashboard::Leak, "leak"),
|
||||
(Dashboard::RotationStabiliser, "rotation_stabiliser"),
|
||||
(Dashboard::CruiseControl, "cruise_control"),
|
||||
(Dashboard::Radioactivity, "radioactivity"),
|
||||
];
|
||||
|
||||
pub struct HudPlugin;
|
||||
impl Plugin for HudPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, (
|
||||
update_hud,
|
||||
update_dashboard,
|
||||
update_speedometer,
|
||||
update_gauges,
|
||||
handle_input.run_if(in_control),
|
||||
handle_input,
|
||||
handle_target_event,
|
||||
));
|
||||
app.add_systems(PostUpdate, (
|
||||
update_overlay_visibility,
|
||||
update_ar_overlays
|
||||
.after(camera::position_to_transform)
|
||||
.after(actor::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform),
|
||||
update_poi_overlays
|
||||
.after(camera::position_to_transform)
|
||||
.after(actor::position_to_transform)
|
||||
.in_set(sync::SyncSet::PositionToTransform),
|
||||
update_target_selectagon
|
||||
.after(PhysicsSet::Sync)
|
||||
|
@ -90,30 +80,12 @@ impl Plugin for HudPlugin {
|
|||
#[derive(Component)] struct Reticule;
|
||||
#[derive(Component)] struct Speedometer;
|
||||
#[derive(Component)] struct Speedometer2;
|
||||
#[derive(Component)] struct GaugeLength(f32);
|
||||
#[derive(Component)] pub struct ToggleableHudElement;
|
||||
#[derive(Component)] pub struct ToggleableHudMapElement;
|
||||
#[derive(Component)] struct Selectagon;
|
||||
#[derive(Component)] pub struct IsTargeted;
|
||||
#[derive(Component)] pub struct PointOfInterestMarker(pub Entity);
|
||||
|
||||
#[derive(Component, Debug, Copy, Clone)]
|
||||
pub enum Dashboard {
|
||||
Leak,
|
||||
Flashlight,
|
||||
RotationStabiliser,
|
||||
CruiseControl,
|
||||
Radioactivity,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, Copy, Clone)]
|
||||
enum Gauge {
|
||||
Health,
|
||||
Power,
|
||||
Oxygen,
|
||||
//Integrity,
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct AugmentedRealityState {
|
||||
pub overlays_visible: bool,
|
||||
|
@ -129,7 +101,6 @@ pub struct AugmentedRealityOverlay {
|
|||
struct FPSUpdateTimer(Timer);
|
||||
|
||||
pub enum LogLevel {
|
||||
Achievement,
|
||||
Always,
|
||||
Warning,
|
||||
//Error,
|
||||
|
@ -224,9 +195,9 @@ impl Log {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn setup(
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
settings: Res<Settings>,
|
||||
settings: Res<var::Settings>,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut ew_updateoverlays: EventWriter<UpdateOverlayVisibility>,
|
||||
) {
|
||||
|
@ -242,12 +213,6 @@ pub fn setup(
|
|||
color: settings.hud_color_subtitles,
|
||||
..default()
|
||||
};
|
||||
let style_fps = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_fps,
|
||||
color: settings.hud_color_fps,
|
||||
..default()
|
||||
};
|
||||
let style_console = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_console,
|
||||
|
@ -274,9 +239,24 @@ pub fn setup(
|
|||
};
|
||||
|
||||
// Add Statistics HUD
|
||||
let version = &settings.version;
|
||||
let mut bundle_fps = TextBundle::from_sections([
|
||||
TextSection::new("", style), // Target
|
||||
TextSection::new("", style_fps), // Frames per second
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new(format!(" OutFlyOS v{version} ⚡ "), style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new(" ☣ ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("\n氧 OXYGEN ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("\nProximity 警告 ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("\nSuit Integrity ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("\nVitals ", style.clone()),
|
||||
TextSection::new("", style.clone()),
|
||||
TextSection::new("", style.clone()), // Speed
|
||||
TextSection::new("", style.clone()), // Target
|
||||
]).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::VMin(2.0),
|
||||
|
@ -325,7 +305,13 @@ pub fn setup(
|
|||
let reticule_handle: Handle<Image> = asset_server.load("sprites/reticule4.png");
|
||||
commands.spawn((
|
||||
NodeBundle {
|
||||
style: style_centered(),
|
||||
style: Style {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
..default()
|
||||
},
|
||||
visibility,
|
||||
..default()
|
||||
},
|
||||
|
@ -345,139 +331,6 @@ pub fn setup(
|
|||
));
|
||||
});
|
||||
|
||||
// HP/O2/Suit Integrity/Power Gauges
|
||||
let gauges_handle: Handle<Image> = asset_server.load("sprites/gauge_horizontal.png");
|
||||
let gauges = [
|
||||
("sprites/gauge_heart.png", Gauge::Health),
|
||||
("sprites/gauge_battery.png", Gauge::Power),
|
||||
("sprites/gauge_o2.png", Gauge::Oxygen),
|
||||
//("sprites/gauge_suit.png", Gauge::Integrity),
|
||||
];
|
||||
let icon_size = 24.0;
|
||||
let gauge_bar_padding_left = 4.0;
|
||||
for (i, (sprite, gauge)) in gauges.iter().enumerate() {
|
||||
let bar_length = if i == 0 { 32.0*8.0 } else { 32.0*5.0 };
|
||||
|
||||
// The bar with variable width
|
||||
commands.spawn((
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(30.0),
|
||||
height: Val::Percent(100.0),
|
||||
bottom: Val::Px(20.0 + 24.0 * i as f32),
|
||||
left: Val::VMin(2.0),
|
||||
align_items: AlignItems::End,
|
||||
overflow: Overflow::clip(),
|
||||
..default()
|
||||
},
|
||||
visibility,
|
||||
..default()
|
||||
},
|
||||
ToggleableHudElement,
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Px(118.0),
|
||||
height: Val::Px(10.0),
|
||||
bottom: Val::Px(8.0),
|
||||
left: Val::Px(gauge_bar_padding_left + icon_size),
|
||||
..Default::default()
|
||||
},
|
||||
visibility,
|
||||
background_color: settings.hud_color.into(),
|
||||
..Default::default()
|
||||
},
|
||||
gauge.clone(),
|
||||
GaugeLength(bar_length),
|
||||
));
|
||||
});
|
||||
|
||||
// The decorator sprites surrounding the bar
|
||||
commands.spawn((
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(30.0),
|
||||
height: Val::Percent(100.0),
|
||||
bottom: Val::Px(20.0 + 24.0 * i as f32),
|
||||
left: Val::VMin(2.0),
|
||||
align_items: AlignItems::End,
|
||||
overflow: Overflow::clip(),
|
||||
..default()
|
||||
},
|
||||
visibility,
|
||||
..default()
|
||||
},
|
||||
ToggleableHudElement,
|
||||
)).with_children(|builder| {
|
||||
// The gauge symbol
|
||||
builder.spawn((
|
||||
ImageBundle {
|
||||
image: UiImage::new(asset_server.load(sprite.to_string())),
|
||||
style: Style {
|
||||
width: Val::Px(icon_size),
|
||||
height: Val::Px(icon_size),
|
||||
..Default::default()
|
||||
},
|
||||
visibility,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
// The gauge bar border
|
||||
builder.spawn((
|
||||
ImageBundle {
|
||||
image: UiImage::new(gauges_handle.clone()),
|
||||
style: Style {
|
||||
width: Val::Px(bar_length),
|
||||
height: Val::Px(10.0),
|
||||
bottom: Val::Px(8.0),
|
||||
left: Val::Px(gauge_bar_padding_left),
|
||||
..Default::default()
|
||||
},
|
||||
visibility,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Car-Dashboard-Style icons
|
||||
let style_dashboard = Style {
|
||||
width: Val::Px(DASHBOARD_ICON_SIZE),
|
||||
height: Val::Px(DASHBOARD_ICON_SIZE),
|
||||
..Default::default()
|
||||
};
|
||||
commands.spawn((
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(30.0),
|
||||
height: Val::Percent(100.0),
|
||||
bottom: Val::Px(40.0 + icon_size * gauges.len() as f32),
|
||||
left: Val::VMin(4.0),
|
||||
align_items: AlignItems::End,
|
||||
overflow: Overflow::clip(),
|
||||
..default()
|
||||
},
|
||||
visibility,
|
||||
..default()
|
||||
},
|
||||
ToggleableHudElement,
|
||||
)).with_children(|builder| {
|
||||
for (component, filename) in DASHBOARD_DEF {
|
||||
builder.spawn((
|
||||
*component,
|
||||
ImageBundle {
|
||||
image: UiImage::new(asset_server.load(
|
||||
format!("sprites/dashboard_{}.png", filename))),
|
||||
style: style_dashboard.clone(),
|
||||
visibility: Visibility::Hidden,
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
// Add Speedometer
|
||||
let speedometer_handle: Handle<Image> = asset_server.load("sprites/speedometer.png");
|
||||
commands.spawn((
|
||||
|
@ -485,7 +338,6 @@ pub fn setup(
|
|||
style: Style {
|
||||
width: Val::VMin(0.0),
|
||||
height: Val::Percent(100.0),
|
||||
left: Val::Vw(100.0 - SPEEDOMETER_WIDTH),
|
||||
align_items: AlignItems::End,
|
||||
overflow: Overflow::clip(),
|
||||
..default()
|
||||
|
@ -500,7 +352,7 @@ pub fn setup(
|
|||
ImageBundle {
|
||||
image: UiImage::new(speedometer_handle),
|
||||
style: Style {
|
||||
width: Val::Vw(SPEEDOMETER_WIDTH),
|
||||
width: Val::VMin(SPEEDOMETER_WIDTH),
|
||||
height: Val::VMin(SPEEDOMETER_HEIGHT),
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -514,7 +366,6 @@ pub fn setup(
|
|||
style: Style {
|
||||
width: Val::VMin(0.0),
|
||||
height: Val::Percent(100.0),
|
||||
left: Val::Vw(100.0 - SPEEDOMETER_WIDTH),
|
||||
align_items: AlignItems::End,
|
||||
overflow: Overflow::clip(),
|
||||
..default()
|
||||
|
@ -529,7 +380,7 @@ pub fn setup(
|
|||
ImageBundle {
|
||||
image: UiImage::new(speedometer_handle),
|
||||
style: Style {
|
||||
width: Val::Vw(SPEEDOMETER_WIDTH),
|
||||
width: Val::VMin(SPEEDOMETER_WIDTH),
|
||||
height: Val::VMin(SPEEDOMETER_HEIGHT),
|
||||
..Default::default()
|
||||
},
|
||||
|
@ -538,12 +389,11 @@ pub fn setup(
|
|||
));
|
||||
});
|
||||
let mut bundle_speedometer_text = TextBundle::from_sections([
|
||||
TextSection::new("", style_speedometer.clone()), // speed relative to target
|
||||
TextSection::new("", style_speedometer.clone()), // speed relative to target
|
||||
TextSection::new("", style_speedometer.clone()), // speed relative to orbit
|
||||
]).with_style(Style {
|
||||
position_type: PositionType::Absolute,
|
||||
left: Val::Vw(100.0 - SPEEDOMETER_WIDTH + 2.0),
|
||||
left: Val::VMin(2.0),
|
||||
bottom: Val::VMin(4.0),
|
||||
..default()
|
||||
}).with_text_justify(JustifyText::Left);
|
||||
|
@ -605,100 +455,41 @@ pub fn setup(
|
|||
// Selectagon
|
||||
let mut entitycmd = commands.spawn((
|
||||
Selectagon,
|
||||
NotShadowCaster,
|
||||
NotShadowReceiver,
|
||||
SpatialBundle {
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
load_asset("selectagon", &mut entitycmd, &*asset_server);
|
||||
skeleton::load("selectagon", &mut entitycmd, &*asset_server);
|
||||
|
||||
ew_updateoverlays.send(UpdateOverlayVisibility);
|
||||
}
|
||||
|
||||
fn update_dashboard(
|
||||
timer: ResMut<FPSUpdateTimer>,
|
||||
mut q_dashboard: Query<(&mut Visibility, &Dashboard)>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
q_player: Query<(&actor::Suit, &Position), With<actor::Player>>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
if !settings.hud_active || !timer.0.just_finished() {
|
||||
return;
|
||||
}
|
||||
let player = q_player.get_single();
|
||||
if player.is_err() { return; }
|
||||
let (suit, pos) = player.unwrap();
|
||||
|
||||
for (mut vis, icon) in &mut q_dashboard {
|
||||
*vis = bool2vis(match icon {
|
||||
Dashboard::Flashlight => {
|
||||
settings.flashlight_active
|
||||
}
|
||||
Dashboard::Leak => {
|
||||
suit.integrity < 0.5
|
||||
}
|
||||
Dashboard::RotationStabiliser => {
|
||||
!settings.rotation_stabilizer_active
|
||||
}
|
||||
Dashboard::CruiseControl => {
|
||||
settings.cruise_control_active
|
||||
}
|
||||
Dashboard::Radioactivity => {
|
||||
if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
|
||||
pos_jupiter.distance(pos.0) < 140_000_000.0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn update_speedometer(
|
||||
timer: ResMut<FPSUpdateTimer>,
|
||||
settings: Res<Settings>,
|
||||
q_camera: Query<&LinearVelocity, With<actor::PlayerCamera>>,
|
||||
q_player: Query<&actor::ExperiencesGForce, With<actor::Player>>,
|
||||
q_target: Query<&LinearVelocity, With<IsTargeted>>,
|
||||
mut q_speedometer: Query<&mut Style, (With<Speedometer>, Without<Speedometer2>)>,
|
||||
mut q_speedometer2: Query<&mut Style, (With<Speedometer2>, Without<Speedometer>)>,
|
||||
mut q_node_speed: Query<&mut Text, With<NodeSpeedometerText>>,
|
||||
) {
|
||||
if !settings.hud_active || !timer.0.just_finished() {
|
||||
return;
|
||||
}
|
||||
if let Ok(cam_v) = q_camera.get_single() {
|
||||
let speed = cam_v.length();
|
||||
|
||||
let speedometer_split = 5_000.0;
|
||||
if let Ok(mut speedometer) = q_speedometer.get_single_mut() {
|
||||
let custom_c = speedometer_split;
|
||||
let fraction = nature::inverse_lorentz_factor_custom_c((custom_c - speed).clamp(0.0, custom_c), custom_c).clamp(0.0, 1.0) as f32;
|
||||
let fraction = nature::lorenz_factor_custom_c((custom_c - speed).clamp(0.0, custom_c), custom_c).clamp(0.0, 1.0) as f32;
|
||||
let wid = (fraction * SPEEDOMETER_WIDTH).clamp(0.0, 100.0);
|
||||
speedometer.width = Val::Vw(wid);
|
||||
speedometer.width = Val::VMin(wid);
|
||||
}
|
||||
if let Ok(mut speedometer2) = q_speedometer2.get_single_mut() {
|
||||
let custom_c = nature::C - speedometer_split;
|
||||
let fraction = nature::inverse_lorentz_factor_custom_c((custom_c - speed + speedometer_split).clamp(0.0, custom_c), custom_c).clamp(0.0, 1.0) as f32;
|
||||
let fraction = nature::lorenz_factor_custom_c((custom_c - speed + speedometer_split).clamp(0.0, custom_c), custom_c).clamp(0.0, 1.0) as f32;
|
||||
let wid = (fraction * SPEEDOMETER_WIDTH).clamp(0.0, 100.0);
|
||||
speedometer2.width = Val::Vw(wid);
|
||||
speedometer2.width = Val::VMin(wid);
|
||||
}
|
||||
if let Ok(mut speed_text) = q_node_speed.get_single_mut() {
|
||||
// G forces
|
||||
speed_text.sections[0].value = if let Ok(gforce) = q_player.get_single() {
|
||||
if gforce.gforce > 0.0001 {
|
||||
format!("{:.1}g\n", gforce.gforce)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
// Velocity relative to target
|
||||
speed_text.sections[1].value = if let Ok(target_v) = q_target.get_single() {
|
||||
speed_text.sections[0].value = if let Ok(target_v) = q_target.get_single() {
|
||||
let delta_v = (target_v.0 - cam_v.0).length();
|
||||
if delta_v > 0.0001 {
|
||||
format!("Δv {}\n", nature::readable_speed(delta_v))
|
||||
|
@ -708,9 +499,7 @@ fn update_speedometer(
|
|||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
// "Absolute velocity", or velocity relative to orbit
|
||||
speed_text.sections[2].value = if speed > 0.0001 {
|
||||
speed_text.sections[1].value = if speed > 0.0001 {
|
||||
nature::readable_speed(speed)
|
||||
} else {
|
||||
"".to_string()
|
||||
|
@ -719,40 +508,11 @@ fn update_speedometer(
|
|||
}
|
||||
}
|
||||
|
||||
fn update_gauges(
|
||||
timer: ResMut<FPSUpdateTimer>,
|
||||
q_player: Query<(&actor::HitPoints, &actor::Suit, &actor::Battery), With<actor::Player>>,
|
||||
mut q_gauges: Query<(&mut Style, &mut BackgroundColor, &Gauge, &GaugeLength)>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
if !settings.hud_active || !timer.0.just_finished() {
|
||||
return;
|
||||
}
|
||||
let player = q_player.get_single();
|
||||
if player.is_err() { return; }
|
||||
let (hp, suit, battery) = player.unwrap();
|
||||
|
||||
for (mut style, mut bg, gauge, len) in &mut q_gauges {
|
||||
let value: f32 = match gauge {
|
||||
Gauge::Health => hp.current / hp.max,
|
||||
Gauge::Oxygen => (suit.oxygen / suit.oxygen_max).powf(0.5),
|
||||
//Gauge::Integrity => suit.integrity,
|
||||
Gauge::Power => battery.power / battery.capacity,
|
||||
};
|
||||
if value < 0.5 {
|
||||
*bg = settings.hud_color_alert.into();
|
||||
}
|
||||
else {
|
||||
*bg = settings.hud_color.into();
|
||||
}
|
||||
style.width = Val::Px(len.0 * value);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_hud(
|
||||
diagnostics: Res<DiagnosticsStore>,
|
||||
time: Res<Time>,
|
||||
mut log: ResMut<Log>,
|
||||
player: Query<(&actor::HitPoints, &actor::Suit, &actor::ExperiencesGForce), With<actor::Player>>,
|
||||
q_camera: Query<(&Position, &LinearVelocity), With<actor::PlayerCamera>>,
|
||||
mut timer: ResMut<FPSUpdateTimer>,
|
||||
q_choices: Query<&chat::Choice>,
|
||||
|
@ -761,21 +521,60 @@ fn update_hud(
|
|||
mut q_node_console: Query<&mut Text, (With<NodeConsole>, Without<NodeHud>, Without<NodeChoiceText>)>,
|
||||
mut q_node_choice: Query<&mut Text, (With<NodeChoiceText>, Without<NodeHud>, Without<NodeConsole>)>,
|
||||
mut q_node_currentline: Query<&mut Text, (With<NodeCurrentChatLine>, Without<NodeHud>, Without<NodeConsole>, Without<NodeChoiceText>)>,
|
||||
settings: Res<Settings>,
|
||||
query_all_actors: Query<&actor::Actor>,
|
||||
settings: Res<var::Settings>,
|
||||
q_target: Query<(&IsClickable, Option<&Position>, Option<&LinearVelocity>), With<IsTargeted>>,
|
||||
) {
|
||||
// TODO only when hud is actually on
|
||||
if timer.0.tick(time.delta()).just_finished() || log.needs_rerendering {
|
||||
let q_camera_result = q_camera.get_single();
|
||||
let player = player.get_single();
|
||||
let mut freshest_line: f64 = 0.0;
|
||||
if settings.hud_active && q_camera_result.is_ok() {
|
||||
if player.is_ok() && q_camera_result.is_ok() {
|
||||
let (hp, suit, gforce) = player.unwrap();
|
||||
let (pos, _) = q_camera_result.unwrap();
|
||||
for mut text in &mut q_node_hud {
|
||||
text.sections[0].value = format!("2524-03-12 03:02");
|
||||
if let Some(fps) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) {
|
||||
if let Some(value) = fps.smoothed() {
|
||||
// Update the value of the second section
|
||||
text.sections[1].value = format!("{value:.0}");
|
||||
text.sections[4].value = format!("{value:.0}");
|
||||
}
|
||||
}
|
||||
let power = suit.power / suit.power_max * 100.0;
|
||||
text.sections[2].value = format!("{power:}%");
|
||||
let oxy_percent = suit.oxygen / suit.oxygen_max * 100.0;
|
||||
|
||||
// the remaining oxygen hud info ignores leaking suits from low integrity
|
||||
if suit.oxygen > nature::OXY_H {
|
||||
let oxy_hour = suit.oxygen / nature::OXY_H;
|
||||
text.sections[7].value = format!("{oxy_percent:.1}% [lasts {oxy_hour:.1} hours]");
|
||||
text.sections[7].style.color = settings.hud_color;
|
||||
} else {
|
||||
let oxy_min = suit.oxygen / nature::OXY_M;
|
||||
text.sections[7].value = format!("{oxy_percent:.1}% [lasts {oxy_min:.1} min]");
|
||||
text.sections[7].style.color = settings.hud_color_alert;
|
||||
}
|
||||
//let adrenaline = lifeform.adrenaline * 990.0 + 10.0;
|
||||
//text.sections[11].value = format!("{adrenaline:.0}pg/mL");
|
||||
let vitals = 100.0 * hp.current / hp.max;
|
||||
text.sections[13].value = format!("{vitals:.0}%");
|
||||
if vitals < 50.0 {
|
||||
text.sections[13].style.color = settings.hud_color_alert;
|
||||
} else {
|
||||
text.sections[13].style.color = settings.hud_color;
|
||||
}
|
||||
let all_actors = query_all_actors.iter().len();
|
||||
text.sections[9].value = format!("{all_actors:.0}");
|
||||
let integrity = suit.integrity * 100.0;
|
||||
if integrity < 50.0 {
|
||||
text.sections[11].style.color = settings.hud_color_alert;
|
||||
text.sections[11].value = format!("{integrity:.0}% [LEAKING]");
|
||||
} else {
|
||||
text.sections[11].style.color = settings.hud_color;
|
||||
text.sections[11].value = format!("{integrity:.0}%");
|
||||
}
|
||||
//text.sections[17].value = format!("{speed_readable}/s / {kmh:.0}km/h / {gforce:.1}g");
|
||||
|
||||
// Target display
|
||||
let dist_scalar: f64;
|
||||
|
@ -809,11 +608,23 @@ fn update_hud(
|
|||
}
|
||||
}
|
||||
|
||||
// let dev_speed = if settings.dev_mode {
|
||||
// let x = pos.x;
|
||||
// let y = pos.y;
|
||||
// let z = pos.z;
|
||||
// format!("\n{x:.0}\n{y:.0}\n{z:.0}")
|
||||
// } else {
|
||||
// "".to_string()
|
||||
// };
|
||||
let dev_speed = "";
|
||||
let gforce = gforce.gforce;
|
||||
text.sections[14].value = format!("\n{gforce:.1}g{dev_speed}");
|
||||
|
||||
if target_multiple {
|
||||
text.sections[0].value = "ERROR: MULTIPLE TARGETS\n\n".to_string();
|
||||
text.sections[15].value = "\n\nERROR: MULTIPLE TARGETS".to_string();
|
||||
}
|
||||
else if target_error {
|
||||
text.sections[0].value = "ERROR: FAILED TO AQUIRE TARGET\n\n".to_string();
|
||||
text.sections[15].value = "\n\nERROR: FAILED TO AQUIRE TARGET".to_string();
|
||||
}
|
||||
else if let Ok((clickable, _, _)) = q_target.get_single() {
|
||||
let distance = if dist_scalar.is_nan() {
|
||||
|
@ -829,10 +640,10 @@ fn update_hud(
|
|||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
text.sections[0].value = format!("Target: {target_name}\n{pronoun}Distance: {distance}\n\n");
|
||||
text.sections[15].value = format!("\n\nTarget: {target_name}\n{pronoun}Distance: {distance}");
|
||||
}
|
||||
else {
|
||||
text.sections[0].value = "".to_string();
|
||||
text.sections[15].value = "".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -866,7 +677,6 @@ fn update_hud(
|
|||
} else {
|
||||
|msg: &&Message| { match msg.level {
|
||||
LogLevel::Always => true,
|
||||
LogLevel::Achievement => true,
|
||||
_ => false
|
||||
}}
|
||||
};
|
||||
|
@ -882,7 +692,6 @@ fn update_hud(
|
|||
let opacity: f32 = (freshness.powf(1.5) as f32).clamp(0.0, 1.0);
|
||||
freshest_line = freshest_line.max(freshness);
|
||||
chat.sections[row].style.color = match msg.level {
|
||||
LogLevel::Achievement => settings.hud_color_console_achievement,
|
||||
LogLevel::Warning => settings.hud_color_console_warn,
|
||||
LogLevel::Info => settings.hud_color_console_system,
|
||||
_ => settings.hud_color_console,
|
||||
|
@ -959,16 +768,26 @@ fn update_hud(
|
|||
fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mouse_input: Res<ButtonInput<MouseButton>>,
|
||||
settings: Res<Settings>,
|
||||
mut settings: ResMut<var::Settings>,
|
||||
mut log: ResMut<Log>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_togglemusic: EventWriter<audio::ToggleMusicEvent>,
|
||||
mut ew_target: EventWriter<TargetEvent>,
|
||||
mut ew_game: EventWriter<GameEvent>,
|
||||
mut ew_updateoverlays: EventWriter<UpdateOverlayVisibility>,
|
||||
q_objects: Query<(Entity, &Transform), (With<IsClickable>, Without<IsTargeted>, Without<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
||||
q_camera: Query<&Transform, With<Camera>>,
|
||||
) {
|
||||
if keyboard_input.just_pressed(settings.key_help) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
for line in include_str!("data/keybindings.in").trim().lines().rev() {
|
||||
log.add(line.to_string(), "".to_string(), LogLevel::Always);
|
||||
}
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_togglehud) {
|
||||
ew_game.send(GameEvent::SetAR(Turn::Toggle));
|
||||
settings.hud_active ^= true;
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||
ew_togglemusic.send(audio::ToggleMusicEvent());
|
||||
ew_updateoverlays.send(UpdateOverlayVisibility);
|
||||
}
|
||||
if settings.hud_active && mouse_input.just_pressed(settings.key_selectobject) {
|
||||
if let Ok(camtrans) = q_camera.get_single() {
|
||||
|
@ -985,12 +804,10 @@ fn handle_input(
|
|||
|
||||
fn handle_target_event(
|
||||
mut commands: Commands,
|
||||
settings: Res<Settings>,
|
||||
settings: Res<var::Settings>,
|
||||
mut er_target: EventReader<TargetEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||
q_target: Query<Entity, With<IsTargeted>>,
|
||||
q_ids: Query<&actor::Identifier>,
|
||||
) {
|
||||
let mut play_sfx = false;
|
||||
|
||||
|
@ -1002,12 +819,6 @@ fn handle_target_event(
|
|||
if let Some(entity) = target {
|
||||
commands.entity(*entity).insert(IsTargeted);
|
||||
play_sfx = true;
|
||||
|
||||
if let Ok(id) = q_ids.get(*entity) {
|
||||
if id.0 == cmd::ID_EARTH {
|
||||
ew_achievement.send(game::AchievementEvent::FindEarth);
|
||||
}
|
||||
}
|
||||
}
|
||||
if play_sfx && !settings.mute_sfx {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
|
@ -1017,7 +828,7 @@ fn handle_target_event(
|
|||
}
|
||||
|
||||
fn update_target_selectagon(
|
||||
settings: Res<Settings>,
|
||||
settings: Res<var::Settings>,
|
||||
mut q_selectagon: Query<(&mut Transform, &mut Visibility), (With<Selectagon>, Without<IsTargeted>, Without<Camera>)>,
|
||||
q_target: Query<&Transform, (With<IsTargeted>, Without<Camera>, Without<Selectagon>)>,
|
||||
q_camera: Query<&Transform, (With<Camera>, Without<IsTargeted>, Without<Selectagon>)>,
|
||||
|
@ -1035,7 +846,7 @@ fn update_target_selectagon(
|
|||
}
|
||||
selectagon_trans.translation = target_trans.translation;
|
||||
selectagon_trans.scale = target_trans.scale;
|
||||
selectagon_trans.look_at(camera_trans.translation, camera_trans.up().into());
|
||||
selectagon_trans.look_at(camera_trans.translation, target_trans.up().into());
|
||||
|
||||
// Enlarge Selectagon to a minimum angular diameter
|
||||
let (angular_diameter, _, _) = camera::calc_angular_diameter(
|
||||
|
@ -1057,7 +868,7 @@ fn update_target_selectagon(
|
|||
fn update_ar_overlays (
|
||||
q_owners: Query<(Entity, &Transform, &Visibility), (With<AugmentedRealityOverlayBroadcaster>, Without<AugmentedRealityOverlay>)>,
|
||||
mut q_overlays: Query<(&mut Transform, &mut Visibility, &mut AugmentedRealityOverlay)>,
|
||||
settings: ResMut<Settings>,
|
||||
settings: ResMut<var::Settings>,
|
||||
mut state: ResMut<AugmentedRealityState>,
|
||||
) {
|
||||
let (need_activate, need_clean, need_update);
|
||||
|
@ -1094,7 +905,7 @@ fn update_poi_overlays (
|
|||
mut q_marker: Query<(&mut Transform, &PointOfInterestMarker)>,
|
||||
q_parent: Query<&Transform, Without<PointOfInterestMarker>>,
|
||||
q_camera: Query<&Transform, (With<Camera>, Without<PointOfInterestMarker>)>,
|
||||
settings: ResMut<Settings>,
|
||||
settings: ResMut<var::Settings>,
|
||||
) {
|
||||
if !settings.hud_active || !settings.map_active || q_camera.is_empty() {
|
||||
return;
|
||||
|
@ -1123,7 +934,7 @@ fn update_overlay_visibility(
|
|||
q_target: Query<&IsTargeted, (Without<Camera>, Without<Selectagon>, Without<PointOfInterestMarker>, Without<ToggleableHudElement>)>,
|
||||
mut ambient_light: ResMut<AmbientLight>,
|
||||
er_target: EventReader<UpdateOverlayVisibility>,
|
||||
settings: Res<Settings>,
|
||||
settings: Res<var::Settings>,
|
||||
) {
|
||||
if er_target.is_empty() {
|
||||
return;
|
||||
|
|
94
src/main.rs
|
@ -11,35 +11,25 @@
|
|||
// This module initializes the game, handles command-line arguments,
|
||||
// and manages window-related key bindings.
|
||||
|
||||
pub mod actor;
|
||||
pub mod audio;
|
||||
pub mod camera;
|
||||
pub mod chat;
|
||||
pub mod cmd;
|
||||
pub mod common;
|
||||
pub mod game;
|
||||
pub mod hud;
|
||||
pub mod load;
|
||||
pub mod menu;
|
||||
#[allow(dead_code)]
|
||||
pub mod nature;
|
||||
pub mod var;
|
||||
pub mod visual;
|
||||
pub mod world;
|
||||
mod actor;
|
||||
mod audio;
|
||||
mod camera;
|
||||
mod chat;
|
||||
mod commands;
|
||||
mod effects;
|
||||
mod hud;
|
||||
mod shading;
|
||||
mod skeleton;
|
||||
mod var;
|
||||
mod world;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{actor, audio, camera, chat, cmd, common, game, hud,
|
||||
load, menu, nature, var, visual, world};
|
||||
pub use crate::common::*;
|
||||
pub use crate::var::Settings;
|
||||
pub use crate::load::load_asset;
|
||||
pub use game::{GameEvent, Turn};
|
||||
pub use game::Turn::Toggle;
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
mod nature;
|
||||
|
||||
use bevy::window::{Window, WindowMode, PrimaryWindow, CursorGrabMode};
|
||||
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
|
||||
use bevy::prelude::*;
|
||||
use bevy::pbr::ExtendedMaterial;
|
||||
use std::env;
|
||||
|
||||
const HELP: &str = "./outfly [options]
|
||||
|
@ -56,7 +46,7 @@ Note: borderless fullscreen is the default, but it crashes on some systems.";
|
|||
|
||||
fn main() {
|
||||
let prefs = var::load_prefs();
|
||||
let mut opt = var::CommandLineOptions {
|
||||
let mut opt = CommandLineOptions {
|
||||
window_mode_fullscreen: prefs.get_fullscreen_mode(),
|
||||
window_mode_initial: prefs.get_window_mode(),
|
||||
use_gl: prefs.render_mode_is_gl(),
|
||||
|
@ -127,6 +117,7 @@ impl Plugin for OutFlyPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup);
|
||||
app.add_systems(Update, handle_input);
|
||||
app.add_systems(Update, debug);
|
||||
app.insert_resource(var::Settings::default());
|
||||
app.insert_resource(var::GameVars::default());
|
||||
app.add_plugins((
|
||||
|
@ -137,37 +128,72 @@ impl Plugin for OutFlyPlugin {
|
|||
audio::AudioPlugin,
|
||||
camera::CameraPlugin,
|
||||
chat::ChatPlugin,
|
||||
cmd::CmdPlugin,
|
||||
game::GamePlugin,
|
||||
menu::MenuPlugin,
|
||||
visual::VisualPlugin,
|
||||
commands::CommandsPlugin,
|
||||
effects::EffectsPlugin,
|
||||
hud::HudPlugin,
|
||||
load::LoadPlugin,
|
||||
shading::ShadingPlugin,
|
||||
skeleton::SkeletonPlugin,
|
||||
world::WorldPlugin,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct CommandLineOptions {
|
||||
window_mode_fullscreen: WindowMode,
|
||||
window_mode_initial: WindowMode,
|
||||
use_gl: bool,
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
opt: Res<var::CommandLineOptions>,
|
||||
opt: Res<CommandLineOptions>,
|
||||
) {
|
||||
for mut window in &mut windows {
|
||||
window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||
window.cursor.visible = false;
|
||||
window.mode = opt.window_mode_initial;
|
||||
window.title = common::GAME_NAME.to_string();
|
||||
window.title = "OutFly".to_string();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
settings: Res<var::Settings>,
|
||||
opt: Res<CommandLineOptions>,
|
||||
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
|
||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_game: EventWriter<game::GameEvent>,
|
||||
) {
|
||||
if keyboard_input.pressed(settings.key_exit) {
|
||||
app_exit_events.send(bevy::app::AppExit);
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_fullscreen) {
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
ew_game.send(game::GameEvent::SetFullscreen(game::Turn::Toggle));
|
||||
for mut window in &mut windows {
|
||||
window.mode = if window.mode == WindowMode::Windowed {
|
||||
opt.window_mode_fullscreen
|
||||
} else {
|
||||
WindowMode::Windowed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debug(
|
||||
settings: Res<var::Settings>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut commands: Commands,
|
||||
mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, shading::AsteroidSurface>>>,
|
||||
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
||||
) {
|
||||
if settings.dev_mode && keyboard_input.pressed(KeyCode::KeyP) {
|
||||
for (entity, _name, mesh) in &materials {
|
||||
dbg!(mesh);
|
||||
let mut entity = commands.entity(entity);
|
||||
entity.remove::<Handle<StandardMaterial>>();
|
||||
let material = extended_materials.add(shading::AsteroidSurface::material());
|
||||
entity.insert(material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
548
src/menu.rs
|
@ -1,548 +0,0 @@
|
|||
// ▄████████▄ + ███ + ▄█████████ ███ +
|
||||
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
||||
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
||||
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
||||
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
||||
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
||||
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
||||
// + + + ███
|
||||
// + ▀████████████████████████████████████████████████████▀
|
||||
//
|
||||
// This plugin manages game menus and the player death screen
|
||||
|
||||
use crate::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
use fastrand;
|
||||
|
||||
pub const POEMS: &str = &include_str!("data/deathpoems.in");
|
||||
|
||||
pub struct MenuPlugin;
|
||||
impl Plugin for MenuPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Startup, setup.after(hud::setup));
|
||||
app.add_systems(PreUpdate, show_deathscreen.run_if(on_event::<DeathScreenEvent>()));
|
||||
app.add_systems(Update, handle_deathscreen_input);
|
||||
app.add_systems(PostUpdate, update_menu
|
||||
.after(game::handle_game_event)
|
||||
.run_if(on_event::<UpdateMenuEvent>()));
|
||||
app.add_systems(Update, handle_input.run_if(alive));
|
||||
app.insert_resource(DeathScreenInputDelayTimer(
|
||||
Timer::from_seconds(1.0, TimerMode::Once)));
|
||||
app.insert_resource(MenuState::default());
|
||||
app.add_event::<DeathScreenEvent>();
|
||||
app.add_event::<UpdateMenuEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)] pub struct DeathScreenInputDelayTimer(pub Timer);
|
||||
#[derive(Component)] pub struct MenuElement;
|
||||
#[derive(Component)] pub struct MenuTopLevel;
|
||||
#[derive(Component)] pub struct MenuAchievements;
|
||||
#[derive(Component)] pub struct DeathScreenElement;
|
||||
#[derive(Component)] pub struct DeathText;
|
||||
#[derive(Event)] pub struct UpdateMenuEvent;
|
||||
#[derive(Event, PartialEq)] pub enum DeathScreenEvent { Show, Hide }
|
||||
|
||||
pub const MENUDEF: &[(&str, MenuAction)] = &[
|
||||
("", MenuAction::ToggleMap),
|
||||
("", MenuAction::ToggleAR),
|
||||
("", MenuAction::ToggleSound),
|
||||
("", MenuAction::ToggleMusic),
|
||||
("", MenuAction::ToggleCamera),
|
||||
("Toggle Fullscreen [F11]", MenuAction::ToggleFullscreen),
|
||||
("", MenuAction::ToggleShadows),
|
||||
("Take Off Helmet", MenuAction::Restart),
|
||||
("Quit", MenuAction::Quit),
|
||||
];
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum MenuAction {
|
||||
ToggleMap,
|
||||
ToggleAR,
|
||||
ToggleSound,
|
||||
ToggleMusic,
|
||||
ToggleCamera,
|
||||
ToggleFullscreen,
|
||||
ToggleShadows,
|
||||
Restart,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn setup(
|
||||
mut commands: Commands,
|
||||
asset_server: Res<AssetServer>,
|
||||
achievement_tracker: Res<var::AchievementTracker>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
commands.spawn((
|
||||
DeathScreenElement,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
background_color: Color::BLACK.into(),
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
let font_handle = asset_server.load(FONT);
|
||||
let style_death = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_deathtext,
|
||||
color: settings.hud_color_death,
|
||||
..default()
|
||||
};
|
||||
let style_death_poem = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_deathpoem,
|
||||
color: settings.hud_color_deathpoem,
|
||||
..default()
|
||||
};
|
||||
let style_death_subtext = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_deathsubtext,
|
||||
color: settings.hud_color_death,
|
||||
..default()
|
||||
};
|
||||
let style_death_subsubtext = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_deathsubtext * 0.8,
|
||||
color: settings.hud_color_death,
|
||||
..default()
|
||||
};
|
||||
let style_death_achievements = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_death_achievements,
|
||||
color: settings.hud_color_death_achievements,
|
||||
..default()
|
||||
};
|
||||
commands.spawn((
|
||||
DeathScreenElement,
|
||||
NodeBundle {
|
||||
style: style_centered(),
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
DeathText,
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection::new("", style_death_poem),
|
||||
TextSection::new("You are dead.\n", style_death),
|
||||
TextSection::new("Cause: ", style_death_subtext.clone()),
|
||||
TextSection::new("Unknown", style_death_subtext),
|
||||
TextSection::new("", style_death_achievements),
|
||||
TextSection::new("\n\n\n\nPress E to begin anew.", style_death_subsubtext),
|
||||
],
|
||||
justify: JustifyText::Center,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
let style_menu = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_hud,
|
||||
color: settings.hud_color,
|
||||
..default()
|
||||
};
|
||||
|
||||
let sections: Vec<TextSection> = Vec::from_iter(MENUDEF.iter().map(|(label, _)|
|
||||
TextSection::new(label.to_string() + "\n", style_menu.clone())
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: style_fullscreen(),
|
||||
background_color: Color::rgba(0.0, 0.0, 0.0, 0.8).into(),
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::SpaceAround,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
MenuTopLevel,
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections,
|
||||
justify: JustifyText::Center,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
let style_achievement_header = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_achievement_header,
|
||||
color: settings.hud_color_achievement_header,
|
||||
..default()
|
||||
};
|
||||
let style_achievement = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_achievement,
|
||||
color: settings.hud_color_achievement,
|
||||
..default()
|
||||
};
|
||||
let achievement_count = achievement_tracker.to_bool_vec().len();
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
left: Val::Percent(2.0),
|
||||
top: Val::Percent(2.0),
|
||||
align_items: AlignItems::Start,
|
||||
justify_content: JustifyContent::Start,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
let mut sections = vec![
|
||||
TextSection::new("Achievements\n", style_achievement_header.clone())
|
||||
];
|
||||
sections.extend(Vec::from_iter((0..achievement_count).map(|_|
|
||||
TextSection::new("", style_achievement.clone())
|
||||
)));
|
||||
builder.spawn((
|
||||
MenuAchievements,
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections,
|
||||
justify: JustifyText::Left,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
let keybindings = include_str!("data/keybindings.in");
|
||||
let style_keybindings = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_keybindings,
|
||||
color: settings.hud_color_keybindings,
|
||||
..default()
|
||||
};
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(96.0),
|
||||
height: Val::Percent(96.0),
|
||||
left: Val::Percent(2.0),
|
||||
top: Val::Percent(2.0),
|
||||
align_items: AlignItems::Start,
|
||||
justify_content: JustifyContent::End,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection::new("Controls\n", style_achievement_header),
|
||||
TextSection::new(keybindings, style_keybindings)
|
||||
],
|
||||
justify: JustifyText::Right,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
|
||||
let style_version = TextStyle {
|
||||
font: font_handle.clone(),
|
||||
font_size: settings.font_size_version,
|
||||
color: settings.hud_color_version,
|
||||
..default()
|
||||
};
|
||||
|
||||
commands.spawn((
|
||||
MenuElement,
|
||||
NodeBundle {
|
||||
style: Style {
|
||||
width: Val::Percent(96.0),
|
||||
height: Val::Percent(96.0),
|
||||
left: Val::Percent(2.0),
|
||||
top: Val::Percent(2.0),
|
||||
align_items: AlignItems::End,
|
||||
justify_content: JustifyContent::End,
|
||||
..default()
|
||||
},
|
||||
visibility: Visibility::Hidden,
|
||||
..default()
|
||||
},
|
||||
)).with_children(|builder| {
|
||||
builder.spawn((
|
||||
TextBundle {
|
||||
text: Text {
|
||||
sections: vec![
|
||||
TextSection::new(format!("{} {}", GAME_NAME,
|
||||
settings.version.as_str()), style_version),
|
||||
],
|
||||
justify: JustifyText::Right,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn show_deathscreen(
|
||||
mut er_deathscreen: EventReader<DeathScreenEvent>,
|
||||
mut q_vis: Query<&mut Visibility, With<DeathScreenElement>>,
|
||||
mut q_text: Query<&mut Text, With<DeathText>>,
|
||||
mut ew_pausesfx: EventWriter<audio::PauseAllSfxEvent>,
|
||||
mut ew_game: EventWriter<GameEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||
mut ew_respawn: EventWriter<world::RespawnEvent>,
|
||||
mut ew_respawnaudiosinks: EventWriter<audio::RespawnSinksEvent>,
|
||||
mut timer: ResMut<DeathScreenInputDelayTimer>,
|
||||
mut menustate: ResMut<MenuState>,
|
||||
mut settings: ResMut<Settings>,
|
||||
achievement_tracker: Res<var::AchievementTracker>,
|
||||
) {
|
||||
for event in er_deathscreen.read() {
|
||||
let show = *event == DeathScreenEvent::Show;
|
||||
for mut vis in &mut q_vis {
|
||||
*vis = bool2vis(show);
|
||||
}
|
||||
settings.deathscreen_active = show;
|
||||
settings.alive = !show;
|
||||
|
||||
if show {
|
||||
timer.0.reset();
|
||||
*menustate = MenuState::default();
|
||||
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
||||
ew_pausesfx.send(audio::PauseAllSfxEvent);
|
||||
if let Ok(mut text) = q_text.get_single_mut() {
|
||||
let poems: Vec<&str> = POEMS.split("\n\n").collect();
|
||||
if poems.len() > 0 {
|
||||
let poem_index = fastrand::usize(..poems.len());
|
||||
let poem = poems[poem_index].to_string();
|
||||
text.sections[0].value = poem + "\n\n\n\n";
|
||||
}
|
||||
text.sections[3].value = settings.death_cause.clone();
|
||||
text.sections[4].value = achievement_tracker.to_summary();
|
||||
}
|
||||
} else {
|
||||
ew_respawnaudiosinks.send(audio::RespawnSinksEvent);
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||
ew_effect.send(visual::SpawnEffectEvent { class: visual::Effects::FadeIn(Color::BLACK), duration: 0.3 });
|
||||
ew_respawn.send(world::RespawnEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_deathscreen_input(
|
||||
time: Res<Time>,
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut timer: ResMut<DeathScreenInputDelayTimer>,
|
||||
mut ew_deathscreen: EventWriter<DeathScreenEvent>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
if !settings.deathscreen_active || !timer.0.tick(time.delta()).finished() {
|
||||
return;
|
||||
}
|
||||
if keyboard_input.pressed(settings.key_interact) {
|
||||
ew_deathscreen.send(DeathScreenEvent::Hide);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Debug, Default)]
|
||||
pub struct MenuState {
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
pub fn update_menu(
|
||||
mut q_text: Query<&mut Text, With<MenuTopLevel>>,
|
||||
mut q_achievement_text: Query<&mut Text, (With<MenuAchievements>, Without<MenuTopLevel>)>,
|
||||
mut q_vis: Query<&mut Visibility, With<menu::MenuElement>>,
|
||||
achievement_tracker: Res<var::AchievementTracker>,
|
||||
menustate: Res<MenuState>,
|
||||
settings: Res<Settings>,
|
||||
) {
|
||||
for mut vis in &mut q_vis {
|
||||
*vis = bool2vis(settings.menu_active);
|
||||
}
|
||||
fn bool2string(boolean: bool) -> String {
|
||||
if boolean { "On" } else { "Off" }.to_string()
|
||||
}
|
||||
|
||||
let bools = achievement_tracker.to_bool_vec();
|
||||
let rendered = achievement_tracker.to_textsections();
|
||||
if let Ok(mut text) = q_achievement_text.get_single_mut() {
|
||||
for i in 0..text.sections.len() - 1 {
|
||||
text.sections[i + 1].style.color = if bools[i] {
|
||||
settings.hud_color_achievement_accomplished
|
||||
} else {
|
||||
settings.hud_color_achievement
|
||||
};
|
||||
text.sections[i + 1].value = rendered[i].clone();
|
||||
}
|
||||
}
|
||||
if let Ok(mut text) = q_text.get_single_mut() {
|
||||
for i in 0..text.sections.len() {
|
||||
if menustate.cursor == i {
|
||||
text.sections[i].style.color = settings.hud_color_subtitles;
|
||||
} else {
|
||||
text.sections[i].style.color = settings.hud_color;
|
||||
}
|
||||
|
||||
match MENUDEF[i].1 {
|
||||
MenuAction::ToggleSound => {
|
||||
let onoff = bool2string(!settings.mute_sfx);
|
||||
text.sections[i].value = format!("Sound: {onoff}\n");
|
||||
}
|
||||
MenuAction::ToggleMusic => {
|
||||
let onoff = bool2string(!settings.mute_music);
|
||||
text.sections[i].value = format!("Music: {onoff}\n");
|
||||
}
|
||||
MenuAction::ToggleAR => {
|
||||
let onoff = bool2string(settings.hud_active);
|
||||
text.sections[i].value = format!("Augmented Reality: {onoff} [TAB]\n");
|
||||
}
|
||||
MenuAction::ToggleMap => {
|
||||
let onoff = bool2string(settings.map_active);
|
||||
text.sections[i].value = format!("Map: {onoff} [M]\n");
|
||||
}
|
||||
MenuAction::ToggleCamera => {
|
||||
let onoff = if settings.third_person {
|
||||
"3rd Person"
|
||||
} else {
|
||||
"1st Person"
|
||||
};
|
||||
text.sections[i].value = format!("Camera: {onoff} [C]\n");
|
||||
}
|
||||
MenuAction::ToggleShadows => {
|
||||
let onoff = if settings.shadows_sun {
|
||||
"Flashlight + Sun"
|
||||
} else {
|
||||
"Flashlight Only"
|
||||
};
|
||||
text.sections[i].value = format!("Shadows: {onoff}\n");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_input(
|
||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||
mut settings: ResMut<Settings>,
|
||||
mut menustate: ResMut<MenuState>,
|
||||
mut app_exit_events: ResMut<Events<bevy::app::AppExit>>,
|
||||
mut ew_game: EventWriter<game::GameEvent>,
|
||||
mut ew_playerdies: EventWriter<game::PlayerDiesEvent>,
|
||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||
mut ew_updatemenu: EventWriter<UpdateMenuEvent>,
|
||||
) {
|
||||
let last_menu_entry = MENUDEF.len() - 1;
|
||||
|
||||
if keyboard_input.just_pressed(settings.key_menu)
|
||||
|| keyboard_input.just_pressed(settings.key_vehicle) && settings.menu_active
|
||||
{
|
||||
ew_game.send(GameEvent::SetMenu(Toggle));
|
||||
}
|
||||
if !settings.menu_active {
|
||||
return;
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_forward)
|
||||
|| keyboard_input.just_pressed(settings.key_mouseup)
|
||||
|| keyboard_input.just_pressed(KeyCode::ArrowUp)
|
||||
{
|
||||
menustate.cursor = if menustate.cursor == 0 {
|
||||
last_menu_entry
|
||||
} else {
|
||||
menustate.cursor.saturating_sub(1)
|
||||
};
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_back)
|
||||
|| keyboard_input.just_pressed(settings.key_mousedown)
|
||||
|| keyboard_input.just_pressed(KeyCode::ArrowDown)
|
||||
{
|
||||
menustate.cursor = if menustate.cursor == last_menu_entry {
|
||||
0
|
||||
} else {
|
||||
menustate.cursor.saturating_add(1)
|
||||
};
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
}
|
||||
if keyboard_input.just_pressed(settings.key_interact)
|
||||
|| keyboard_input.just_pressed(KeyCode::Enter)
|
||||
{
|
||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
|
||||
match MENUDEF[menustate.cursor].1 {
|
||||
MenuAction::ToggleMap => {
|
||||
ew_game.send(GameEvent::SetMap(Toggle));
|
||||
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::ToggleAR => {
|
||||
ew_game.send(GameEvent::SetAR(Toggle));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::ToggleMusic => {
|
||||
ew_game.send(GameEvent::SetMusic(Toggle));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::ToggleSound => {
|
||||
ew_game.send(GameEvent::SetSound(Toggle));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::ToggleCamera => {
|
||||
ew_game.send(GameEvent::SetThirdPerson(Toggle));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::ToggleFullscreen => {
|
||||
ew_game.send(GameEvent::SetFullscreen(Toggle));
|
||||
},
|
||||
MenuAction::ToggleShadows => {
|
||||
ew_game.send(GameEvent::SetShadows(Toggle));
|
||||
ew_updatemenu.send(UpdateMenuEvent);
|
||||
},
|
||||
MenuAction::Restart => {
|
||||
settings.god_mode = false;
|
||||
ew_playerdies.send(game::PlayerDiesEvent(actor::DamageType::Depressurization));
|
||||
},
|
||||
MenuAction::Quit => {
|
||||
app_exit_events.send(bevy::app::AppExit);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@
|
|||
//
|
||||
// This module manages the messy, impure parts of our universe.
|
||||
|
||||
use crate::prelude::*;
|
||||
use bevy::math::DVec3;
|
||||
use std::f64::consts::PI as PI64;
|
||||
|
||||
pub const OXYGEN_USE_KG_PER_S: f32 = 1e-5;
|
||||
pub const OXY_S: f32 = OXYGEN_USE_KG_PER_S;
|
||||
|
@ -27,8 +28,6 @@ pub const G: f64 = 6.6743015e-11; // Gravitational constant in Nm²/kg²
|
|||
pub const SOL_RADIUS: f64 = 696_300_000.0;
|
||||
pub const JUPITER_RADIUS: f64 = 71_492_000.0;
|
||||
pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0;
|
||||
|
||||
pub const SOL_MASS: f64 = 1.9885e30;
|
||||
pub const JUPITER_MASS: f64 = 1.8982e27;
|
||||
|
||||
// Each star's values: (x, y, z, magnitude, color index, distance, name)
|
||||
|
@ -76,7 +75,7 @@ pub fn ring_density(radius: f32) -> f32 {
|
|||
let thebe_inner: f32 = 129.0;
|
||||
let thebe_outer: f32 = 229.0;
|
||||
let metis_notch_center: f32 = 128.0;
|
||||
let metis_notch_width: f32 = 0.1;
|
||||
let metis_notch_width: f32 = 0.6;
|
||||
|
||||
let halo_brightness: f32 = 0.75;
|
||||
let main_brightness: f32 = 1.0;
|
||||
|
@ -90,7 +89,7 @@ pub fn ring_density(radius: f32) -> f32 {
|
|||
} else if radius >= main_inner && radius <= main_outer {
|
||||
let mut metis_notch_effect: f32 = 1.0;
|
||||
if radius > metis_notch_center - metis_notch_width * 0.5 && radius < metis_notch_center + metis_notch_width * 0.5 {
|
||||
metis_notch_effect = 0.8 * (1.0 - smooth_edge(metis_notch_center - metis_notch_width * 0.5, metis_notch_center + metis_notch_width * 0.5, radius));
|
||||
metis_notch_effect = 0.5 * (1.0 - smooth_edge(metis_notch_center - metis_notch_width * 0.5, metis_notch_center + metis_notch_width * 0.5, radius));
|
||||
}
|
||||
density = main_brightness * metis_notch_effect * smooth_edge(main_inner, main_outer, radius);
|
||||
} else {
|
||||
|
@ -138,24 +137,16 @@ pub fn readable_speed(speed: f64) -> String {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lorentz_factor(speed: f64) -> f64 {
|
||||
(1.0 - (speed.powf(2.0) / C.powf(2.0))).powf(-0.5)
|
||||
}
|
||||
|
||||
pub fn lorentz_factor_custom_c(speed: f64, c: f64) -> f64 {
|
||||
(1.0 - (speed.powf(2.0) / c.powf(2.0))).powf(-0.5)
|
||||
}
|
||||
|
||||
pub fn inverse_lorentz_factor(speed: f64) -> f64 {
|
||||
pub fn lorenz_factor(speed: f64) -> f64 {
|
||||
(1.0 - (speed.powf(2.0) / C.powf(2.0))).sqrt()
|
||||
}
|
||||
|
||||
pub fn inverse_lorentz_factor_custom_c(speed: f64, c: f64) -> f64 {
|
||||
pub fn lorenz_factor_custom_c(speed: f64, c: f64) -> f64 {
|
||||
(1.0 - (speed.powf(2.0) / c.powf(2.0))).sqrt()
|
||||
}
|
||||
|
||||
pub fn simple_orbital_period(mass: f64, distance: f64) -> f64 {
|
||||
return 2.0 * PI * (distance.powf(3.0) / (G * mass)).sqrt();
|
||||
return 2.0 * PI64 * (distance.powf(3.0) / (G * mass)).sqrt();
|
||||
}
|
||||
|
||||
pub fn phase_dist_to_coords(phase_radians: f64, distance: f64) -> DVec3 {
|
||||
|
|
|
@ -8,15 +8,14 @@
|
|||
// + + + ███
|
||||
// + ▀████████████████████████████████████████████████████▀
|
||||
//
|
||||
// This module manages asset loading.
|
||||
// This module manages graphics shaders.
|
||||
|
||||
use bevy::ecs::system::EntityCommands;
|
||||
use bevy::prelude::*;
|
||||
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
|
||||
use bevy::pbr::{ExtendedMaterial, MaterialExtension, OpaqueRendererMethod};
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct LoadPlugin;
|
||||
impl Plugin for LoadPlugin {
|
||||
pub struct ShadingPlugin;
|
||||
impl Plugin for ShadingPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_plugins(MaterialPlugin::<JupitersRing>::default());
|
||||
app.add_plugins(MaterialPlugin::<SkyBox>::default());
|
||||
|
@ -24,58 +23,6 @@ impl Plugin for LoadPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn asset_name_to_path(name: &str) -> &'static str {
|
||||
match name {
|
||||
"suitv2" => "models/suit_v2/suit_v2.glb#Scene0",
|
||||
"suit_ar_chefhat" => "models/suit_v2/ar_chefhat.glb#Scene0",
|
||||
"asteroid1" => "models/asteroid.glb#Scene0",
|
||||
"asteroid2" => "models/asteroid2.glb#Scene0",
|
||||
"asteroid_lum" => "models/asteroid_lum.glb#Scene0",
|
||||
"hollow_asteroid" => "models/hollow_asteroid.glb#Scene0",
|
||||
"moonlet" => "models/moonlet.glb#Scene0",
|
||||
"monolith" => "models/monolith_neon.glb#Scene0",
|
||||
"lightorb" => "models/lightorb.glb#Scene0",
|
||||
"orb_busstop" => "models/orb_busstop.glb#Scene0",
|
||||
"orb_busstop_dim" => "models/orb_busstop_dim.glb#Scene0",
|
||||
"crate" => "models/crate.glb#Scene0",
|
||||
"MeteorAceGT" => "models/MeteorAceGT.glb#Scene0",
|
||||
"cruiser" => "models/cruiser.glb#Scene0",
|
||||
"satellite" => "models/satellite.glb#Scene0",
|
||||
"pizzeria" => "models/pizzeria2.glb#Scene0",
|
||||
"pizzasign" => "models/pizzasign.glb#Scene0",
|
||||
"selectagon" => "models/selectagon.glb#Scene0",
|
||||
"orbitring" => "models/orbitring.glb#Scene0",
|
||||
"clippy" => "models/clippy/clippy.glb#Scene0",
|
||||
"clippy_ar" => "models/clippy/ar_happy.glb#Scene0",
|
||||
"whale" => "models/whale.glb#Scene0",
|
||||
"marker_satellites" => "models/marker_satellites.glb#Scene0",
|
||||
"marker_planets" => "models/marker_planets.glb#Scene0",
|
||||
"point_of_interest" => "models/point_of_interest.glb#Scene0",
|
||||
_ => "models/error.glb#Scene0",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_asset(
|
||||
name: &str,
|
||||
entity_commands: &mut EntityCommands,
|
||||
asset_server: &AssetServer,
|
||||
) {
|
||||
entity_commands.insert(load_scene_by_path(asset_name_to_path(name), asset_server));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn load_scene_by_path(
|
||||
path: &str,
|
||||
asset_server: &AssetServer
|
||||
) -> Handle<Scene> {
|
||||
let path_string = path.to_string();
|
||||
if let Some(handle) = asset_server.get_handle(&path_string) {
|
||||
handle
|
||||
} else {
|
||||
asset_server.load(&path_string)
|
||||
}
|
||||
}
|
||||
|
||||
// Jupiter's Ring
|
||||
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
|
||||
pub struct JupitersRing {
|
355
src/skeleton.rs
Normal file
|
@ -0,0 +1,355 @@
|
|||
// ▄████████▄ + ███ + ▄█████████ ███ +
|
||||
// ███▀ ▀███ + + ███ ███▀ + ███ + +
|
||||
// ███ + ███ ███ ███ █████████ ███ ███ ███ ███
|
||||
// ███ +███ ███ ███ ███ ███▐██████ ███ ███ ███
|
||||
// ███ + ███ ███+ ███ +███ ███ + ███ ███ + ███
|
||||
// ███▄ ▄███ ███▄ ███ ███ + ███ + ███ ███▄ ███
|
||||
// ▀████████▀ + ▀███████ ███▄ ███▄ ▀████ ▀███████
|
||||
// + + + ███
|
||||
// + ▀████████████████████████████████████████████████████▀
|
||||
//
|
||||
// This module manages model loading and animation.
|
||||
|
||||
use crate::world;
|
||||
use bevy::ecs::system::EntityCommands;
|
||||
use bevy::prelude::*;
|
||||
|
||||
pub struct SkeletonPlugin;
|
||||
impl Plugin for SkeletonPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(Update, animate_skeleton_parts);
|
||||
app.add_systems(Update, play_animations);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asset_name_to_path(name: &str) -> &'static str {
|
||||
match name {
|
||||
"suitv2" => "models/suit_v2/suit_v2.glb#Scene0",
|
||||
"suit_ar_chefhat" => "models/suit_v2/ar_chefhat.glb#Scene0",
|
||||
"asteroid1" => "models/asteroid.glb#Scene0",
|
||||
"asteroid2" => "models/asteroid2.glb#Scene0",
|
||||
"asteroid_lum" => "models/asteroid_lum.glb#Scene0",
|
||||
"moonlet" => "models/moonlet.glb#Scene0",
|
||||
"monolith" => "models/monolith_neon.glb#Scene0",
|
||||
"lightorb" => "models/lightorb.glb#Scene0",
|
||||
"orb_busstop" => "models/orb_busstop.glb#Scene0",
|
||||
"orb_busstop_dim" => "models/orb_busstop_dim.glb#Scene0",
|
||||
"MeteorAceGT" => "models/MeteorAceGT.glb#Scene0",
|
||||
"satellite" => "models/satellite.glb#Scene0",
|
||||
"pizzeria" => "models/pizzeria2.glb#Scene0",
|
||||
"pizzasign" => "models/pizzasign.glb#Scene0",
|
||||
"selectagon" => "models/selectagon.glb#Scene0",
|
||||
"orbitring" => "models/orbitring.glb#Scene0",
|
||||
"clippy" => "models/clippy/clippy.glb#Scene0",
|
||||
"clippy_ar" => "models/clippy/ar_happy.glb#Scene0",
|
||||
"whale" => "models/whale.glb#Scene0",
|
||||
"point_of_interest" => "models/point_of_interest.glb#Scene0",
|
||||
_ => "models/error.glb#Scene0",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skeleton_name_to_skeletondef(name: &str) -> Option<SkeletonDef> {
|
||||
// x: positive: left, negative: right
|
||||
// y: positive: upward, negative: downward
|
||||
// z: positive: forward, negative: backward
|
||||
match name {
|
||||
"suitv1" => Some(SkeletonDef::Human(HumanDef {
|
||||
collider: "models/suit_v1/collider.glb#Scene0".into(),
|
||||
base: "models/suit_v1/base.glb#Scene0".into(),
|
||||
limbs: vec![
|
||||
LimbDef {
|
||||
class: Limb::Head,
|
||||
path: "models/suit_v1/head.glb#Scene0".into(),
|
||||
pos: Vec3::new(0.0, 0.46, 0.0),
|
||||
..default()
|
||||
},
|
||||
LimbDef {
|
||||
class: Limb::UpperArmLeft,
|
||||
path: "models/suit_v1/upper_arm.glb#Scene0".into(),
|
||||
pos: Vec3::new(0.22, 0.3, 0.0),
|
||||
mirror: true,
|
||||
children: vec![LimbDef {
|
||||
class: Limb::LowerArmLeft,
|
||||
path: "models/suit_v1/lower_arm.glb#Scene0".into(),
|
||||
pos: Vec3::new(-0.33, 0.0, 0.0),
|
||||
..default()
|
||||
}],
|
||||
..default()
|
||||
},
|
||||
LimbDef {
|
||||
class: Limb::UpperArmRight,
|
||||
path: "models/suit_v1/upper_arm.glb#Scene0".into(),
|
||||
pos: Vec3::new(-0.22, 0.3, 0.0),
|
||||
children: vec![LimbDef {
|
||||
class: Limb::LowerArmRight,
|
||||
path: "models/suit_v1/lower_arm.glb#Scene0".into(),
|
||||
pos: Vec3::new(-0.33, 0.0, 0.0),
|
||||
..default()
|
||||
}],
|
||||
..default()
|
||||
},
|
||||
LimbDef {
|
||||
class: Limb::UpperLegLeft,
|
||||
path: "models/suit_v1/upper_leg.glb#Scene0".into(),
|
||||
pos: Vec3::new(0.15, -0.25, 0.1),
|
||||
mirror: true,
|
||||
children: vec![LimbDef {
|
||||
class: Limb::LowerLegLeft,
|
||||
path: "models/suit_v1/lower_leg.glb#Scene0".into(),
|
||||
pos: Vec3::new(0.0, -0.3, 0.0),
|
||||
..default()
|
||||
}],
|
||||
..default()
|
||||
},
|
||||
LimbDef {
|
||||
class: Limb::UpperLegRight,
|
||||
path: "models/suit_v1/upper_leg.glb#Scene0".into(),
|
||||
pos: Vec3::new(-0.15, -0.25, 0.1),
|
||||
children: vec![LimbDef {
|
||||
class: Limb::LowerLegRight,
|
||||
path: "models/suit_v1/lower_leg.glb#Scene0".into(),
|
||||
pos: Vec3::new(0.0, -0.3, 0.0),
|
||||
..default()
|
||||
}],
|
||||
..default()
|
||||
},
|
||||
],
|
||||
})),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)] pub struct SkeletonLimb;
|
||||
#[derive(Component)] pub struct MirroredLimb;
|
||||
pub enum SkeletonDef {
|
||||
Human(HumanDef)
|
||||
}
|
||||
|
||||
pub struct HumanDef {
|
||||
collider: String,
|
||||
base: String,
|
||||
limbs: Vec<LimbDef>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LimbDef {
|
||||
path: String,
|
||||
pos: Vec3,
|
||||
class: Limb,
|
||||
mirror: bool,
|
||||
children: Vec<LimbDef>,
|
||||
}
|
||||
|
||||
#[derive(Component, Default)]
|
||||
pub enum Limb {
|
||||
#[default]
|
||||
Base,
|
||||
Head,
|
||||
UpperArmRight,
|
||||
UpperArmLeft,
|
||||
LowerArmRight,
|
||||
LowerArmLeft,
|
||||
UpperLegRight,
|
||||
UpperLegLeft,
|
||||
LowerLegRight,
|
||||
LowerLegLeft,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub enum Animation {
|
||||
HumanFloat,
|
||||
}
|
||||
|
||||
pub fn load(
|
||||
name: &str,
|
||||
entity_commands: &mut EntityCommands,
|
||||
asset_server: &AssetServer,
|
||||
) {
|
||||
if let Some(skel) = skeleton_name_to_skeletondef(name) {
|
||||
match skel {
|
||||
SkeletonDef::Human(human) => {
|
||||
entity_commands.insert(load_scene_by_path(human.collider.as_str(), asset_server));
|
||||
entity_commands.with_children(|parent| {
|
||||
parent.spawn((
|
||||
Limb::Base,
|
||||
Animation::HumanFloat,
|
||||
world::DespawnOnPlayerDeath,
|
||||
SceneBundle {
|
||||
scene: load_scene_by_path(human.base.as_str(), asset_server),
|
||||
..default()
|
||||
}
|
||||
));
|
||||
for limb in human.limbs {
|
||||
let rot = if limb.mirror {
|
||||
Quat::from_rotation_y(180.0f32.to_radians())
|
||||
} else {
|
||||
Quat::IDENTITY
|
||||
};
|
||||
let mut parent_limb = parent.spawn((
|
||||
limb.class,
|
||||
Animation::HumanFloat,
|
||||
world::DespawnOnPlayerDeath,
|
||||
SceneBundle {
|
||||
scene: load_scene_by_path(limb.path.as_str(), asset_server),
|
||||
transform: Transform::from_translation(limb.pos).with_rotation(rot),
|
||||
..default()
|
||||
}
|
||||
));
|
||||
if limb.mirror {
|
||||
parent_limb.insert(MirroredLimb);
|
||||
}
|
||||
if !limb.children.is_empty() {
|
||||
parent_limb.with_children(|parent| {
|
||||
for child_limb in limb.children {
|
||||
let rot = if child_limb.mirror {
|
||||
Quat::from_rotation_y(180.0f32.to_radians())
|
||||
} else {
|
||||
Quat::IDENTITY
|
||||
};
|
||||
let mut entity_commands = parent.spawn((
|
||||
child_limb.class,
|
||||
Animation::HumanFloat,
|
||||
world::DespawnOnPlayerDeath,
|
||||
SceneBundle {
|
||||
scene: load_scene_by_path(child_limb.path.as_str(), asset_server),
|
||||
transform: Transform::from_translation(child_limb.pos).with_rotation(rot),
|
||||
..default()
|
||||
}
|
||||
));
|
||||
if child_limb.mirror {
|
||||
entity_commands.insert(MirroredLimb);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entity_commands.insert(load_scene_by_path(asset_name_to_path(name), asset_server));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn load_scene_by_path(
|
||||
path: &str,
|
||||
asset_server: &AssetServer
|
||||
) -> Handle<Scene> {
|
||||
let path_string = path.to_string();
|
||||
if let Some(handle) = asset_server.get_handle(&path_string) {
|
||||
handle
|
||||
} else {
|
||||
asset_server.load(&path_string)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _build_body(
|
||||
_name: String,
|
||||
mut _entity_commands: EntityCommands,
|
||||
) {
|
||||
}
|
||||
|
||||
pub fn animate_skeleton_parts(
|
||||
time: Res<Time>,
|
||||
mut q_limb: Query<(&mut Transform, &Limb, &Animation, Option<&MirroredLimb>)>,
|
||||
) {
|
||||
let t = time.elapsed_seconds();
|
||||
for (mut trans, limb, animation, mirror) in &mut q_limb {
|
||||
let mirror = mirror.is_some();
|
||||
match animation {
|
||||
Animation::HumanFloat =>
|
||||
animate_human_float(&mut trans, &limb, mirror, t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rot(trans: &mut Transform, x: f32, y: f32, z: f32) {
|
||||
trans.rotation = Quat::from_euler(
|
||||
EulerRot::XYZ,
|
||||
x.to_radians(),
|
||||
y.to_radians(),
|
||||
z.to_radians()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn animate_human_float(mut trans: &mut Transform, limb: &Limb, mirror: bool, t: f32) {
|
||||
// x: lean head forward/backward
|
||||
// y: close/open arms together in front of the torso
|
||||
// z: spread legs sidewards
|
||||
let m = {|angle| if mirror { 180f32 + angle } else { angle }};
|
||||
match limb {
|
||||
Limb::UpperArmRight => rot(&mut trans,
|
||||
0.0,
|
||||
m(40.0 + 5.0 * (t * 0.5).sin()),
|
||||
20.0 + 10.0 * (t * 0.5).sin(),
|
||||
),
|
||||
Limb::UpperArmLeft => rot(&mut trans,
|
||||
0.0,
|
||||
m(-(40.0 + 5.0 * (t * 0.5).sin())),
|
||||
20.0 + 10.0 * (t * 0.5).sin(),
|
||||
),
|
||||
Limb::LowerArmRight => rot(&mut trans,
|
||||
0.0,
|
||||
m(20.0),
|
||||
-20.0,
|
||||
),
|
||||
Limb::LowerArmLeft => rot(&mut trans,
|
||||
0.0,
|
||||
m(-20.0),
|
||||
-20.0,
|
||||
),
|
||||
Limb::UpperLegRight => rot(&mut trans,
|
||||
-30.0 + 10.0 * (t * 0.5).sin(),
|
||||
0.0,
|
||||
-20.0 + 2.5 * (t * 0.5).cos(),
|
||||
),
|
||||
Limb::UpperLegLeft => rot(&mut trans,
|
||||
-30.0 + 10.0 * (t * 0.5).sin(),
|
||||
0.0,
|
||||
20.0 - 2.5 * (t * 0.5).cos(),
|
||||
),
|
||||
Limb::LowerLegRight => rot(&mut trans,
|
||||
35.0 + 5.0 * (t * 0.5).sin(),
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
Limb::LowerLegLeft => rot(&mut trans,
|
||||
35.0 + 5.0 * (t * 0.5).sin(),
|
||||
0.0,
|
||||
0.0,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn play_animations(
|
||||
mut players: Query<&mut AnimationPlayer, Added<AnimationPlayer>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
for mut player in &mut players {
|
||||
let animation = asset_server.load("models/suit_v2/suit_v2.glb#Animation0");
|
||||
player.play(animation.clone()).repeat();
|
||||
}
|
||||
}
|
||||
|
||||
//fn play_animations(
|
||||
// players: Query<(Entity, &Parent, &AnimationPlayer), Added<AnimationPlayer>>,
|
||||
// q_parents: Query<(Entity, &Parent)>,
|
||||
// world: &World,
|
||||
//) {
|
||||
// for (entity, parent, player) in &players {
|
||||
// info!("Got player!");
|
||||
//// dbg!(world.inspect_entity(entity));
|
||||
//// let parent_entity = q_parents.get(parent.get());
|
||||
//// if let Ok((parent_entity, parent_parents)) = parent_entity {
|
||||
//// //dbg!(world.inspect_entity(parent_entity));
|
||||
//// let parent_entity = q_parents.get(parent_parents.get());
|
||||
//// if let Ok((parent_entity, parent_parents)) = parent_entity {
|
||||
//// dbg!(world.inspect_entity(parent_entity));
|
||||
//// }
|
||||
//// }
|
||||
// //dbg!(player);
|
||||
// //player.play(animations.0[0].clone_weak()).repeat();
|
||||
// }
|
||||
//}
|
|
@ -1,158 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="dashboard_cruise_control.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/dashboard_cruise_control.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="1.1697646"
|
||||
inkscape:cx="-291.08422"
|
||||
inkscape:cy="368.87765"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1765"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g227"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><marker
|
||||
style="overflow:visible"
|
||||
id="marker3"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="-22.00"
|
||||
inkscape:stockid="Concave triangle arrow"
|
||||
markerWidth="0.5"
|
||||
markerHeight="0.5"
|
||||
viewBox="0 0 1 1"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always"
|
||||
preserveAspectRatio="xMidYMid"><path
|
||||
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||
id="path3"
|
||||
transform="scale(0.7)" /></marker><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.14372881"
|
||||
y="-0.26839938"
|
||||
width="1.2874576"
|
||||
height="1.4443655"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.090000000000000024"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="2"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="0.99999999999999922"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="filter:url(#filter1)"><path
|
||||
style="fill:none;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1"
|
||||
inkscape:label="circle"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="35.964756"
|
||||
sodipodi:cy="38.081425"
|
||||
sodipodi:rx="19.068579"
|
||||
sodipodi:ry="19.068579"
|
||||
sodipodi:start="2.3561945"
|
||||
sodipodi:end="0.78539816"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 22.481235,51.564946 a 19.068579,19.068579 0 0 1 0,-26.967043 19.068579,19.068579 0 0 1 26.967042,0 19.068579,19.068579 0 0 1 0,26.967043"
|
||||
sodipodi:open="true" /><circle
|
||||
style="fill:#007e43;fill-opacity:1;stroke:none;stroke-width:0.318295;stroke-linecap:round;stroke-linejoin:round"
|
||||
id="path2"
|
||||
cx="35.983334"
|
||||
cy="38.100002"
|
||||
r="4.0741858" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 22.489583,51.593753 2.910416,-2.910418"
|
||||
id="path5"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 49.448277,51.564944 46.566668,48.683335"
|
||||
id="path6"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.754167,24.870834 25.4,27.516667"
|
||||
id="path7" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 49.477086,24.60625 -2.910417,2.910417"
|
||||
id="path8" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 16.933333,38.100002 h 4.233334"
|
||||
id="path9" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 55.033336,38.100002 H 50.800002"
|
||||
id="path10" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 35.983335,19.05 v 4.233334"
|
||||
id="path11" /><path
|
||||
style="fill:none;fill-opacity:1;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 35.983335,38.100002 -6.35,-6.35"
|
||||
id="path12" /><path
|
||||
style="fill:none;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker3)"
|
||||
id="circle2"
|
||||
inkscape:label="circle"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="35.983334"
|
||||
sodipodi:cy="38.100002"
|
||||
sodipodi:rx="25.4"
|
||||
sodipodi:ry="25.4"
|
||||
sodipodi:start="3.5255651"
|
||||
sodipodi:end="4.1364303"
|
||||
sodipodi:arc-type="arc"
|
||||
d="M 12.432864,28.584995 A 25.4,25.4 0 0 1 22.149502,16.797771"
|
||||
sodipodi:open="true" /></g></g></svg>
|
Before Width: | Height: | Size: 6.5 KiB |
|
@ -1,115 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="dashboard_highbeams.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/dashboard_highbeams.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="0.90142722"
|
||||
inkscape:cx="-154.19991"
|
||||
inkscape:cy="8.3201393"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1673"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="92"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="false" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="filter:url(#filter1)"><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.225001,44.450003 H 11.641667"
|
||||
id="path2-0-1-7"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="ray5" /><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.225001,39.158336 H 11.641667"
|
||||
id="path2-0-1"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="ray4" /><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.225001,33.866669 H 11.641667"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="ray3" /><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.225001,28.575002 H 11.641667"
|
||||
id="path2-0"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="ray2" /><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 22.225001,23.283335 H 11.641667"
|
||||
id="path2-0-9"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:label="ray1" /><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:3.43958;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 30.691669,19.050001 c -4.233334,11.641668 -4.233334,17.991668 0,29.633335 10.583333,0 26.608074,-4.283783 26.458334,-14.816667 C 57.002975,23.524523 41.275002,19.050001 30.691669,19.050001 Z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="ccsc"
|
||||
inkscape:label="lamp" /></g></g></svg>
|
Before Width: | Height: | Size: 4.7 KiB |
|
@ -1,121 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="dashboard_leak.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/dashboard_leak.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="4.6790582"
|
||||
inkscape:cx="149.17532"
|
||||
inkscape:cy="102.69161"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1765"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g227"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.1510532"
|
||||
y="-0.12537775"
|
||||
width="1.2709421"
|
||||
height="1.2236664"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.090000000000000024"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="2"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="0.99999999999999922"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="filter:url(#filter1)"><path
|
||||
style="font-variation-settings:'wght' 400;display:inline;fill:none;stroke:#a6720b;stroke-width:2.64583;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 50.800004,44.450002 c 0,0 12.7,-31.750001 -16.933335,-31.750001 -29.6333354,0 -16.933334,31.749995 -16.933334,31.749995 z"
|
||||
id="path3"
|
||||
sodipodi:nodetypes="cscc"
|
||||
inkscape:label="Helmet" /><path
|
||||
style="font-variation-settings:'wght' 400;display:none;fill:#be1251;fill-opacity:1;stroke:#be1251;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
d="m 45.73128,27.516669 -4.233333,8.466668 -7.455338,-7.65886 5.338672,9.775527 -8.466669,0.748606 6.350002,1.368061 -4.233335,7.809454 6.350002,-5.692788 0.843071,7.166799 1.273595,-7.166799 5.068722,6.349999 -2.952055,-6.349999 7.383041,3.63801 -7.383042,-5.754678 8.466668,-2.799589 -7.623804,-0.678689 z"
|
||||
id="path4"
|
||||
sodipodi:nodetypes="ccccccccccccccccc" /><path
|
||||
id="path6"
|
||||
style="font-variation-settings:'wght' 400;display:inline;fill:#a6720b;fill-opacity:1;stroke:none;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;paint-order:normal"
|
||||
d="m 45.905214,23.415628 2.248956,4.89479 0.264583,4.497917 -0.661455,0.396877 -2.513542,0.926044 -0.264583,-4.45091 -0.529167,4.45091 -4.762504,-0.264588 -1.322916,-3.96875 1.645173,-7.282649 -2.17434,7.282649 -6.792366,-4.10104 6.527783,4.630206 1.322916,3.439584 -3.175,2.38125 -3.107304,-0.59893 -0.332279,-3.898987 0.08113,3.832324 -3.256132,0.930176 3.082499,-0.599447 3.280937,0.906922 -0.542603,2.867525 -6.35,6.056995 22.754167,-0.765328 3.439586,-14.287499 -5.55625,1.852083 -0.529167,-4.7625 z m 4.233334,9.789584 4.101039,-0.926043 -2.381251,6.614583 -3.96875,-0.264584 -2.645831,-3.968748 2.778126,-0.926041 z m -11.244796,1.190622 1.5875,4.7625 -3.96875,-0.529166 v -1.852084 z m 0.529167,0 5.291669,0.264586 2.38125,4.233334 -5.820834,0.529166 z m 11.641667,6.350002 -1.058148,1.984376 h -8.731436 v -2.778128 l 6.350001,-0.529165 z m -15.08125,-1.058335 4.233333,0.529168 -0.264581,3.175002 -7.143752,-3e-6 z"
|
||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
|
||||
inkscape:label="split" /><path
|
||||
style="font-variation-settings:'wght' 400;display:none;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
|
||||
d="m 40.21667,57.150004 c 6.349999,-4.233334 14.816667,4.233333 21.166668,0"
|
||||
id="path13"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="font-variation-settings:'wght' 400;display:none;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
d="m 40.21667,60.227636 c 6.349999,-4.665132 14.816667,3.801535 21.166668,0"
|
||||
id="path14"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="font-variation-settings:'wght' 400;display:none;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||
d="m 40.21667,63.305268 c 6.349999,-5.096931 14.816667,3.369737 21.166668,0"
|
||||
id="path15"
|
||||
sodipodi:nodetypes="cc" /><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16.9333px;font-family:Yupiter;-inkscape-font-specification:'Yupiter, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#a6720b;fill-opacity:1;stroke:none;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal"
|
||||
x="16.933334"
|
||||
y="61.383339"
|
||||
id="text19"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan19"
|
||||
style="stroke-width:2.64583;stroke:none;fill:#a6720b;fill-opacity:1"
|
||||
x="16.933334"
|
||||
y="61.383339">LEAK</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 6.8 KiB |
|
@ -1,108 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="dashboard_radioactivity.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/dashboard_radioactivity.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.3395291"
|
||||
inkscape:cx="58.131356"
|
||||
inkscape:cy="123.31541"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1765"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g227"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.20615604"
|
||||
y="-0.23572759"
|
||||
width="1.4123121"
|
||||
height="1.4714552"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.090000000000000024"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="2"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="0.99999999999999922"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="filter:url(#filter1)"><path
|
||||
id="path1"
|
||||
style="fill:#a6720b;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="m 57.150003,33.866669 c 0,8.462523 -4.59159,16.258741 -11.992506,20.362487 l -9.229858,-16.645624 2.172363,-3.716863 z"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
id="path2"
|
||||
clip-path="none"
|
||||
style="fill:#a6720b;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="M 22.225001,54.030628 C 15.021112,49.871461 10.583334,42.185003 10.583334,33.866669 l 19.050001,0 2.007676,3.854952 z"
|
||||
sodipodi:nodetypes="ccccc" /><circle
|
||||
style="fill:none;fill-opacity:1;stroke:#a6720b;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle7"
|
||||
cx="33.866669"
|
||||
cy="33.866669"
|
||||
r="4.2333336" /><path
|
||||
id="path3"
|
||||
style="fill:#a6720b;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="m 22.225001,13.702709 c 7.203889,-4.1591673 16.079447,-4.1591669 23.283336,10e-7 l -9.525001,16.497784 -4.240635,-0.01264 z"
|
||||
sodipodi:nodetypes="ccccc" /><circle
|
||||
style="fill:none;fill-opacity:1;stroke:#a6720b;stroke-width:2.64583336;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4"
|
||||
cx="33.866669"
|
||||
cy="33.866669"
|
||||
r="22.408022" /></g></g></svg>
|
Before Width: | Height: | Size: 4 KiB |
|
@ -1,133 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="dashboard_rotation_stabiliser.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/dashboard_rotation_stabiliser.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="1.6542969"
|
||||
inkscape:cx="211.87249"
|
||||
inkscape:cy="197.9693"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1673"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="92"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g227"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><marker
|
||||
style="overflow:visible"
|
||||
id="ConcaveTriangle"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto-start-reverse"
|
||||
inkscape:stockid="Concave triangle arrow"
|
||||
markerWidth="0.5"
|
||||
markerHeight="0.5"
|
||||
viewBox="0 0 1 1"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always"
|
||||
preserveAspectRatio="xMidYMid"><path
|
||||
transform="scale(0.7)"
|
||||
d="M -2,-4 9,0 -2,4 c 2,-2.33 2,-5.66 0,-8 z"
|
||||
style="fill:context-stroke;fill-rule:evenodd;stroke:none"
|
||||
id="path7" /></marker><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.1605499"
|
||||
y="-0.1605499"
|
||||
width="1.3210998"
|
||||
height="1.3210998"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.090000000000000024"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="2"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="0.99999999999999922"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="filter:url(#filter1)"><circle
|
||||
style="fill:none;stroke:#007e43;stroke-width:2.64583336;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1"
|
||||
cx="33.848091"
|
||||
cy="33.848091"
|
||||
r="19.068579"
|
||||
inkscape:label="circle" /><path
|
||||
style="fill:none;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#ConcaveTriangle)"
|
||||
id="circle2"
|
||||
inkscape:label="circle"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="33.848091"
|
||||
sodipodi:cy="33.848091"
|
||||
sodipodi:rx="25.4"
|
||||
sodipodi:ry="25.4"
|
||||
sodipodi:start="2.6179939"
|
||||
sodipodi:end="3.8397244"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="M 11.851046,46.54809 A 25.4,25.4 0 0 1 14.390563,17.521285" /><path
|
||||
style="fill:none;stroke:#007e43;stroke-width:2.64583;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#ConcaveTriangle)"
|
||||
id="path2"
|
||||
inkscape:label="circle"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="33.848091"
|
||||
sodipodi:cy="33.848091"
|
||||
sodipodi:rx="25.4"
|
||||
sodipodi:ry="25.4"
|
||||
sodipodi:start="5.7595865"
|
||||
sodipodi:end="0.6981317"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="M 55.845136,21.148091 A 25.4,25.4 0 0 1 53.30562,50.174896" /></g></g></svg>
|
Before Width: | Height: | Size: 4.6 KiB |
|
@ -1,99 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_battery.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_o2.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="3.6057089"
|
||||
inkscape:cx="65.590431"
|
||||
inkscape:cy="83.340062"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="184"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:5.29166672;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 6.7381087,16.059295 4.5e-6,40.702599 54.2571138,-2.5e-5 -3e-6,-40.702598 h -7.398697 l -14.797395,2.4e-5 14.797397,-1.271963 -2e-6,-3.815891 H 41.265364 v 5.08783 l -14.797395,2.4e-5 -14.180835,2.4e-5 14.180837,-1.271963 -2e-6,-3.815891 H 14.136807 v 5.08783 z"
|
||||
id="path9"
|
||||
sodipodi:nodetypes="ccccccccccccccccc" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.758334,30.691669 13.758334,0"
|
||||
id="path10"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 20.637501,23.283335 0,14.816667"
|
||||
id="path11"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;fill-opacity:1;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 40.216669,30.691669 13.758334,0"
|
||||
id="path12"
|
||||
sodipodi:nodetypes="cc" /></g></svg>
|
Before Width: | Height: | Size: 3.8 KiB |
|
@ -1,90 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_fuel.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_fuel.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="1.2748106"
|
||||
inkscape:cx="174.53573"
|
||||
inkscape:cy="209.05066"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="184"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
id="path7"
|
||||
style="font-variation-settings:'wght' 400;fill:#be1251;fill-opacity:1;stroke:#be1251;stroke-width:3.15574;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 15.238993,4.762497 12.381998,7.6195116 V 56.188732 H 9.5250001 v 5.71403 H 43.808972 v -5.71403 H 40.951957 V 7.6195116 L 38.094967,4.762497 Z m 2.856996,2.8570146 h 17.141982 l 2.856996,2.8570144 V 21.904584 H 15.238993 V 10.476526 Z" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:#be1251;fill-opacity:1;stroke:#be1251;stroke-width:3.15574;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 40.951957,33.332643 h 4.285496 c 0,0 0.454708,18.221438 7.142509,22.856089 0.782728,0.542444 2.124714,0.608879 2.856987,0 5.958093,-4.954194 4.654007,-11.242735 3.600464,-18.919535 -0.865387,-6.306034 -4.427679,-11.345209 -6.086401,-17.49038 -1.542319,-5.714029 2.485937,-0.731247 2.485937,-0.731247 L 47.65952,8.0073253 c 2.889759,10.9514577 9.321511,25.8668957 10.434417,31.0393197 1.201936,5.58618 2.595107,15.215356 -4.210071,15.730237 -8.138417,0.615734 -7.527568,-24.30128 -7.527568,-24.30128 h -5.404341 z"
|
||||
id="path8"
|
||||
sodipodi:nodetypes="ccssssccssccc" /></g></svg>
|
Before Width: | Height: | Size: 3.7 KiB |
|
@ -1,92 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_heart.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_heart.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.5496212"
|
||||
inkscape:cx="86.483435"
|
||||
inkscape:cy="137.07919"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="184"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="false" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="text2"
|
||||
transform="matrix(2.66899,0,0,2.5086282,-27.690165,-683.1161)"><path
|
||||
style="color:#000000;-inkscape-font-specification:'Fira Code, @wght=700';fill:#be1251;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="m 27.81583,276.35504 q 1.93286,0 3.004718,1.30028 1.071858,1.28272 1.071858,3.05744 0,2.05585 -1.177287,4.32257 -1.159715,2.24915 -3.162861,4.79701 -1.985573,2.54786 -4.445576,5.46472 -2.460003,-2.91686 -4.463148,-5.44715 -1.985574,-2.54786 -3.162861,-4.79701 -1.159716,-2.26671 -1.159716,-4.34014 0,-1.77472 1.071859,-3.05744 1.089429,-1.30028 3.004717,-1.30028 1.827431,0 2.864147,0.93128 1.054287,0.93129 1.845002,2.65329 0.790715,-1.722 1.845002,-2.65329 1.054287,-0.93128 2.864146,-0.93128 z"
|
||||
id="path13" /><path
|
||||
style="color:#000000;-inkscape-font-specification:'Fira Code, @wght=700';fill:#be1251;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="m 18.398437,273.99414 c -1.811015,0 -3.650852,0.75572 -4.814453,2.14453 l -0.002,0.004 c -1.05803,1.26617 -1.621093,2.91197 -1.621093,4.57031 -1e-6,1.82236 0.511901,3.64507 1.417968,5.41602 0.0032,0.007 0.0065,0.013 0.0098,0.0195 0.867874,1.65803 2.000897,3.36656 3.392578,5.15234 0.0039,0.005 0.0078,0.009 0.01172,0.0137 1.353323,1.70947 2.856469,3.54357 4.509765,5.50391 0.943088,1.11697 2.664334,1.11697 3.607422,0 1.654323,-1.96155 3.153682,-3.8025 4.498047,-5.52734 l 0.002,-0.004 c 1.405851,-1.78852 2.542492,-3.50233 3.402344,-5.16992 0.917572,-1.7684 1.439453,-3.58511 1.439453,-5.4043 0,-1.65539 -0.560843,-3.29723 -1.615234,-4.5625 -1.157815,-1.40068 -3.004809,-2.15625 -4.820313,-2.15625 -1.846213,0.18715 -3.522589,1.53018 -4.710937,2.97852 -1.187313,-1.43868 -2.857445,-2.81289 -4.707032,-2.97852 z m 0,4.7207 c 0.817322,0 1.106937,0.16803 1.285157,0.32813 0.0052,0.004 0.0104,0.008 0.01563,0.0117 0.381287,0.3368 0.833252,0.93603 1.261718,1.86914 0.841986,1.83471 3.44903,1.83471 4.291016,0 0.428467,-0.93311 0.880432,-1.53234 1.261719,-1.86914 0.200576,-0.17718 0.50102,-0.33985 1.302734,-0.33985 0.759271,0 0.911972,0.1119 1.183594,0.44141 0.0033,0.004 0.0065,0.008 0.0098,0.0117 0.371113,0.44412 0.521484,0.83697 0.521484,1.54492 0,0.92 -0.259838,1.98227 -0.910156,3.23438 l -0.0039,0.006 c -0.68583,1.3301 -1.656277,2.81266 -2.919921,4.41993 -0.002,0.003 -0.0039,0.005 -0.0059,0.008 -0.768666,0.98634 -1.710763,2.11007 -2.591797,3.18555 -0.881453,-1.06982 -1.819164,-2.18974 -2.59375,-3.16797 -1.252425,-1.6071 -2.224352,-3.08967 -2.925781,-4.42774 -0.636897,-1.2475 -0.898437,-2.31784 -0.898437,-3.25781 0,-0.70714 0.153241,-1.09947 0.523437,-1.54297 0.287152,-0.34215 0.453178,-0.45508 1.193359,-0.45508 z"
|
||||
id="path14"
|
||||
sodipodi:nodetypes="sccscccccccccscscssccccccsccscccccccsccc" /></g></g></svg>
|
Before Width: | Height: | Size: 5.2 KiB |
|
@ -1,87 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1024"
|
||||
height="48"
|
||||
viewBox="0 0 270.93334 12.7"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_horizonal.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_horizontal.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="1.6326"
|
||||
inkscape:cx="392.93152"
|
||||
inkscape:cy="-30.319735"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1627"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="138"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;stroke:#be1251;stroke-width:1.5875;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 0.79375004,0 v 11.906251 l 269.34584996,0 V 0"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="cccc" /></g></svg>
|
Before Width: | Height: | Size: 2.8 KiB |
|
@ -1,91 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_o2.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_o2.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.5496212"
|
||||
inkscape:cx="84.52236"
|
||||
inkscape:cy="46.477492"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="184"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="font-size:25.4px;font-family:Yupiter;-inkscape-font-specification:'Yupiter, Normal';fill:#be1251;stroke:#be1251;stroke-width:2.68582;stroke-linejoin:round"
|
||||
d="m 34.743546,43.194762 q 0,1.692365 -1.167149,2.91787 l -1.867438,1.867438 q -1.167147,1.167148 -2.859513,1.167148 H 17.586467 q -1.692364,0 -2.859513,-1.167148 L 12.801159,46.054275 Q 11.63401,44.887127 11.63401,43.194762 V 13.082338 q 0,-1.692364 1.167149,-2.917868 L 14.668597,8.2970329 Q 15.835745,7.1298846 17.52811,7.1298846 h 11.321336 q 1.692366,0 2.859513,1.225506 l 1.867438,1.8674374 q 1.167149,1.167146 1.167149,2.85951 z m -7.644822,0.641932 q 2.334297,0 2.334297,-2.334297 V 14.774704 q 0,-2.334297 -2.334297,-2.334297 h -7.819893 q -2.334296,0 -2.334296,2.334297 v 26.727693 q 0,2.334297 2.334296,2.334297 z"
|
||||
id="text3"
|
||||
aria-label="O" /><path
|
||||
style="font-size:16.9333px;font-family:Yupiter;-inkscape-font-specification:'Yupiter, Normal';fill:#be1251;stroke:#be1251;stroke-width:2.68582;stroke-linejoin:round"
|
||||
d="m 40.582604,35.589275 q 0,-1.55619 1.556194,-1.55619 H 52.48749 q 1.128242,0 1.90634,0.816981 l 1.244956,1.244966 q 0.778097,0.778106 0.778097,1.906333 v 7.975506 q 0,1.128228 -0.778097,1.945231 l -1.244956,1.244967 q -0.778098,0.778082 -1.90634,0.778082 h -6.808351 q -1.556194,0 -1.556194,1.556213 v 5.446677 q 0,1.556189 1.556194,1.556189 h 9.181549 q 1.556195,0 1.556195,1.55619 v 0.427963 q 0,1.556189 -1.556195,1.556189 H 44.47309 q -0.583574,0 -1.050431,-0.194601 -0.427955,-0.155543 -0.855907,-0.544654 l -1.128243,-0.972615 q -0.855905,-0.817004 -0.855905,-1.945255 v -8.09222 q 0,-0.583551 0.194532,-1.011514 0.194533,-0.466858 0.622478,-0.89482 l 1.20605,-1.206045 q 0.778098,-0.778107 1.906339,-0.778107 h 6.847256 q 1.556195,0 1.556195,-1.55619 v -5.719003 q 0,-1.55619 -1.556195,-1.55619 h -9.220451 q -1.556196,0 -1.556196,-1.556213 z"
|
||||
id="text4"
|
||||
aria-label="2" /></g></svg>
|
Before Width: | Height: | Size: 4.4 KiB |
|
@ -1,95 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_suit.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_suit.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="0.6374053"
|
||||
inkscape:cx="76.089734"
|
||||
inkscape:cy="-262.78413"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="184"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 40.216669,8.4666671 h -12.7 l -2.116667,2.1166669 v 10.583334 l -8.466668,0 -4.233333,4.233334 v 14.816667 h 8.466667 v 21.166668 h 8.466667 V 52.91667 h 8.466667 v 8.466667 h 8.466667 V 40.216669 h 8.466668 V 25.400002 l -4.233334,-4.233334 -8.466667,0 V 10.583334 Z"
|
||||
id="path1"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccc" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="M 33.866669,23.283335 V 40.216669"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="font-variation-settings:'wght' 400;fill:none;stroke:#be1251;stroke-width:5.29167;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="M 25.400002,31.750002 H 42.333336"
|
||||
id="path3"
|
||||
sodipodi:nodetypes="cc" /></g></svg>
|
Before Width: | Height: | Size: 3.4 KiB |
|
@ -1,87 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="42"
|
||||
height="512"
|
||||
viewBox="0 0 11.1125 135.46667"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="gauge_vertical.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../assets/sprites/gauge_horizontal.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="3.2652"
|
||||
inkscape:cx="-50.686022"
|
||||
inkscape:cy="52.523582"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1627"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="138"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#00000000"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.18727821"
|
||||
y="-0.30100527"
|
||||
width="1.3832762"
|
||||
height="1.6020105"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.30000000000000004"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="3"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="1"
|
||||
k2="1.5"
|
||||
k3="1"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="font-variation-settings:'wght' 700;fill:none;stroke:#be1251;stroke-width:1.5875;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 0,134.67292 h 10.318751 l 0,-133.87916996 H 0"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="cccc" /></g></svg>
|
Before Width: | Height: | Size: 2.8 KiB |
|
@ -1,96 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="256"
|
||||
height="256"
|
||||
viewBox="0 0 67.733334 67.733334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
|
||||
sodipodi:docname="outfly.svg"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="../../build/linux/outfly.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#000000"
|
||||
bordercolor="#252525"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#000000"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.3395291"
|
||||
inkscape:cx="50.223782"
|
||||
inkscape:cy="56.635329"
|
||||
inkscape:window-width="2880"
|
||||
inkscape:window-height="1765"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:export-bgcolor="#000000ff"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="0"
|
||||
originy="0"
|
||||
spacingx="0.26458334"
|
||||
spacingy="0.26458334"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="8"
|
||||
dotted="false"
|
||||
gridanglex="30"
|
||||
gridanglez="30"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter1"
|
||||
inkscape:label="bloom"
|
||||
x="-0.28216198"
|
||||
y="-0.2827584"
|
||||
width="1.564324"
|
||||
height="1.5655168"><feConvolveMatrix
|
||||
order="1 1"
|
||||
kernelMatrix="1.0000000 "
|
||||
id="feConvolveMatrix1"
|
||||
result="result1"
|
||||
bias="0.090000000000000024"
|
||||
preserveAlpha="true"
|
||||
edgeMode="none"
|
||||
divisor="0" /><feGaussianBlur
|
||||
stdDeviation="6"
|
||||
id="feGaussianBlur1"
|
||||
in="SourceGraphic" /><feComposite
|
||||
id="feComposite1"
|
||||
operator="arithmetic"
|
||||
k1="0"
|
||||
k2="4.4249689690420553"
|
||||
k3="5.8365581191588785"
|
||||
k4="0"
|
||||
in2="result1" /></filter></defs><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><g
|
||||
id="g227"
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.96875;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1)"
|
||||
transform="matrix(0.79690616,0,0,0.79690616,6.8658634,6.7579172)"><circle
|
||||
style="fill:none;fill-opacity:1;stroke:#870b39;stroke-width:5.97623696;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path1"
|
||||
cx="33.848091"
|
||||
cy="33.848091"
|
||||
inkscape:label="circle"
|
||||
r="21.166666" /><path
|
||||
style="color:#000000;fill:#870b39;stroke:none;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
|
||||
d="M 60.443093,7.4564407 C 57.786988,4.8003353 44.50646,10.112546 39.19425,15.424757 h 5.31221 c 2.656106,0 5.976237,-3.320131 8.632343,-0.664026 C 55.794908,17.416837 49.818671,28.705284 39.19425,39.329706 28.569828,49.954128 17.28138,55.930366 14.625274,53.27426 11.969169,50.618155 15.2893,47.298022 15.420264,44.641917 V 39.329706 C 9.9770896,44.641917 4.6648787,57.922444 7.3209841,60.57855 9.9770899,63.234655 25.913723,57.922444 41.850355,41.985812 57.786988,26.049179 63.099199,10.112546 60.443093,7.4564407 Z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="sccsssccsss" /></g></g></svg>
|
Before Width: | Height: | Size: 3.7 KiB |
211
src/var.rs
|
@ -11,10 +11,9 @@
|
|||
// This module manages variables, settings, as well as evaluating
|
||||
// "if"-conditions in chats.
|
||||
|
||||
use crate::prelude::*;
|
||||
use bevy::window::WindowMode;
|
||||
use bevy::prelude::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use serde::Deserialize;
|
||||
use toml_edit::DocumentMut;
|
||||
use std::env;
|
||||
|
@ -37,7 +36,6 @@ pub struct Settings {
|
|||
pub dev_mode: bool,
|
||||
pub god_mode: bool,
|
||||
pub version: String,
|
||||
pub alive: bool,
|
||||
pub mute_sfx: bool,
|
||||
pub mute_music: bool,
|
||||
pub volume_sfx: u8,
|
||||
|
@ -48,48 +46,24 @@ pub struct Settings {
|
|||
pub zoom_fov: f32,
|
||||
pub zoom_sensitivity_factor: f32,
|
||||
pub font_size_hud: f32,
|
||||
pub font_size_fps: f32,
|
||||
pub font_size_conversations: f32,
|
||||
pub font_size_choices: f32,
|
||||
pub font_size_console: f32,
|
||||
pub font_size_speedometer: f32,
|
||||
pub font_size_deathtext: f32,
|
||||
pub font_size_deathsubtext: f32,
|
||||
pub font_size_deathpoem: f32,
|
||||
pub font_size_death_achievements: f32,
|
||||
pub font_size_achievement: f32,
|
||||
pub font_size_achievement_header: f32,
|
||||
pub font_size_keybindings: f32,
|
||||
pub font_size_version: f32,
|
||||
pub hud_color: Color,
|
||||
pub hud_color_fps: Color,
|
||||
pub hud_color_console: Color,
|
||||
pub hud_color_console_warn: Color,
|
||||
pub hud_color_console_system: Color,
|
||||
pub hud_color_console_achievement: Color,
|
||||
pub hud_color_alert: Color,
|
||||
pub hud_color_subtitles: Color,
|
||||
pub hud_color_choices: Color,
|
||||
pub hud_color_speedometer: Color,
|
||||
pub hud_color_deathpoem: Color,
|
||||
pub hud_color_achievement: Color,
|
||||
pub hud_color_achievement_header: Color,
|
||||
pub hud_color_achievement_accomplished: Color,
|
||||
pub hud_color_death: Color,
|
||||
pub hud_color_death_achievements: Color,
|
||||
pub hud_color_keybindings: Color,
|
||||
pub hud_color_version: Color,
|
||||
pub chat_speed: f32,
|
||||
pub flashlight_active: bool,
|
||||
pub hud_active: bool,
|
||||
pub map_active: bool,
|
||||
pub deathscreen_active: bool,
|
||||
pub menu_active: bool,
|
||||
pub death_cause: String,
|
||||
pub is_zooming: bool,
|
||||
pub third_person: bool,
|
||||
pub rotation_stabilizer_active: bool,
|
||||
pub cruise_control_active: bool,
|
||||
pub shadows_sun: bool,
|
||||
pub shadows_pointlights: bool,
|
||||
pub shadowmap_resolution: usize,
|
||||
|
@ -102,7 +76,8 @@ pub struct Settings {
|
|||
//pub key_map_zoom_out_wheel: MouseButton,
|
||||
//pub key_map_zoom_in_wheel: MouseButton,
|
||||
pub key_togglehud: KeyCode,
|
||||
pub key_menu: KeyCode,
|
||||
pub key_exit: KeyCode,
|
||||
pub key_restart: KeyCode,
|
||||
pub key_fullscreen: KeyCode,
|
||||
pub key_help: KeyCode,
|
||||
pub key_forward: KeyCode,
|
||||
|
@ -116,8 +91,7 @@ pub struct Settings {
|
|||
pub key_interact: KeyCode,
|
||||
pub key_vehicle: KeyCode,
|
||||
pub key_camera: KeyCode,
|
||||
pub key_flashlight: KeyCode,
|
||||
pub key_cruise_control: KeyCode,
|
||||
pub key_shadows: KeyCode,
|
||||
pub key_rotate: KeyCode,
|
||||
pub key_rotation_stabilizer: KeyCode,
|
||||
pub key_mouseup: KeyCode,
|
||||
|
@ -126,6 +100,8 @@ pub struct Settings {
|
|||
pub key_mouseright: KeyCode,
|
||||
pub key_rotateleft: KeyCode,
|
||||
pub key_rotateright: KeyCode,
|
||||
pub key_toggle_sfx: KeyCode,
|
||||
pub key_toggle_music: KeyCode,
|
||||
pub key_reply1: KeyCode,
|
||||
pub key_reply2: KeyCode,
|
||||
pub key_reply3: KeyCode,
|
||||
|
@ -152,20 +128,19 @@ pub struct Settings {
|
|||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
let dev_mode = cfg!(feature = "dev_mode") && env::var("CARGO").is_ok();
|
||||
let dev_mode = cfg!(feature = "dev_mode");
|
||||
let default_mute_sfx = false;
|
||||
let default_mute_music = dev_mode;
|
||||
let default_mute_music = cfg!(feature = "mute_music");
|
||||
let version = if let Some(version) = option_env!("CARGO_PKG_VERSION") {
|
||||
version.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
"13.37".to_string()
|
||||
};
|
||||
|
||||
Settings {
|
||||
dev_mode,
|
||||
god_mode: false,
|
||||
version,
|
||||
alive: true,
|
||||
mute_sfx: default_mute_sfx,
|
||||
mute_music: default_mute_music,
|
||||
volume_sfx: 100,
|
||||
|
@ -176,48 +151,24 @@ impl Default for Settings {
|
|||
zoom_fov: 15.0,
|
||||
zoom_sensitivity_factor: 0.25,
|
||||
font_size_hud: 24.0,
|
||||
font_size_fps: 14.0,
|
||||
font_size_conversations: 32.0,
|
||||
font_size_choices: 28.0,
|
||||
font_size_console: 20.0,
|
||||
font_size_speedometer: 34.0,
|
||||
font_size_deathtext: 64.0,
|
||||
font_size_deathsubtext: 32.0,
|
||||
font_size_deathpoem: 18.0,
|
||||
font_size_death_achievements: 24.0,
|
||||
font_size_achievement: 24.0,
|
||||
font_size_achievement_header: 32.0,
|
||||
font_size_keybindings: 20.0,
|
||||
font_size_version: 20.0,
|
||||
hud_color: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||
hud_color_fps: Color::hex("#181818").unwrap(),
|
||||
hud_color_console: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||
hud_color_console_achievement: Color::hex(COLOR_SUCCESS).unwrap(),
|
||||
hud_color_console_warn: Color::hex(COLOR_WARNING).unwrap(),
|
||||
hud_color_console_system: Color::hex(COLOR_SECONDARY).unwrap(),
|
||||
hud_color_alert: Color::hex(COLOR_SECONDARY).unwrap(),
|
||||
hud_color_subtitles: Color::hex(COLOR_SECONDARY).unwrap(),
|
||||
hud_color_choices: Color::hex(COLOR_BODY).unwrap(),
|
||||
hud_color_speedometer: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||
hud_color_deathpoem: Color::hex("#CC2200").unwrap(),
|
||||
hud_color_achievement: Color::hex(COLOR_DIM).unwrap(),
|
||||
hud_color_achievement_accomplished: Color::hex(COLOR_SUCCESS).unwrap(),
|
||||
hud_color_achievement_header: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||
hud_color_death: Color::hex(COLOR_SECONDARY).unwrap(),
|
||||
hud_color_death_achievements: Color::hex(COLOR_SECONDARY).unwrap(),
|
||||
hud_color_keybindings: Color::hex(COLOR_DIM).unwrap(),
|
||||
hud_color_version: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||
hud_color: Color::hex("#197F19").unwrap(),
|
||||
hud_color_console: Color::hex("#197F19").unwrap(),
|
||||
hud_color_console_warn: Color::hex("#FF4C4C").unwrap(),
|
||||
hud_color_console_system: Color::hex("#7F7F7F").unwrap(),
|
||||
hud_color_alert: Color::hex("#991752").unwrap(),
|
||||
hud_color_subtitles: Color::hex("#CCCCCC").unwrap(),
|
||||
hud_color_choices: Color::hex("#727272").unwrap(),
|
||||
hud_color_speedometer: Color::hex("#BE1251").unwrap(),
|
||||
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
|
||||
flashlight_active: false,
|
||||
hud_active: true,
|
||||
hud_active: false,
|
||||
map_active: false,
|
||||
deathscreen_active: false,
|
||||
menu_active: false,
|
||||
death_cause: "Unknown".to_string(),
|
||||
is_zooming: false,
|
||||
third_person: true,
|
||||
third_person: false,
|
||||
rotation_stabilizer_active: true,
|
||||
cruise_control_active: false,
|
||||
shadows_sun: true,
|
||||
shadows_pointlights: false,
|
||||
shadowmap_resolution: 2048,
|
||||
|
@ -230,7 +181,8 @@ impl Default for Settings {
|
|||
//key_map_zoom_out_wheel: KeyCode::Shift,
|
||||
//key_map_zoom_in_wheel: KeyCode::Shift,
|
||||
key_togglehud: KeyCode::Tab,
|
||||
key_menu: KeyCode::Escape,
|
||||
key_exit: KeyCode::Escape,
|
||||
key_restart: KeyCode::F7,
|
||||
key_fullscreen: KeyCode::F11,
|
||||
key_help: KeyCode::F1,
|
||||
key_forward: KeyCode::KeyW,
|
||||
|
@ -243,9 +195,8 @@ impl Default for Settings {
|
|||
key_stop: KeyCode::Space,
|
||||
key_interact: KeyCode::KeyE,
|
||||
key_vehicle: KeyCode::KeyQ,
|
||||
key_camera: KeyCode::KeyC,
|
||||
key_flashlight: KeyCode::KeyF,
|
||||
key_cruise_control: KeyCode::KeyT,
|
||||
key_camera: KeyCode::KeyF,
|
||||
key_shadows: KeyCode::F2,
|
||||
key_rotate: KeyCode::KeyR,
|
||||
key_rotation_stabilizer: KeyCode::KeyY,
|
||||
key_mouseup: KeyCode::KeyI,
|
||||
|
@ -254,6 +205,8 @@ impl Default for Settings {
|
|||
key_mouseright: KeyCode::KeyL,
|
||||
key_rotateleft: KeyCode::KeyU,
|
||||
key_rotateright: KeyCode::KeyO,
|
||||
key_toggle_sfx: KeyCode::F3,
|
||||
key_toggle_music: KeyCode::F4,
|
||||
key_reply1: KeyCode::Digit1,
|
||||
key_reply2: KeyCode::Digit2,
|
||||
key_reply3: KeyCode::Digit3,
|
||||
|
@ -265,7 +218,7 @@ impl Default for Settings {
|
|||
key_reply9: KeyCode::Digit9,
|
||||
key_reply10: KeyCode::Digit0,
|
||||
key_cheat_god_mode: KeyCode::KeyG,
|
||||
key_cheat_stop: KeyCode::KeyZ,
|
||||
key_cheat_stop: KeyCode::KeyC,
|
||||
key_cheat_speed: KeyCode::KeyV,
|
||||
key_cheat_speed_backward: KeyCode::KeyB,
|
||||
key_cheat_teleport: KeyCode::KeyX,
|
||||
|
@ -275,7 +228,7 @@ impl Default for Settings {
|
|||
key_cheat_adrenaline_zero: KeyCode::F5,
|
||||
key_cheat_adrenaline_mid: KeyCode::F6,
|
||||
key_cheat_adrenaline_max: KeyCode::F8,
|
||||
key_cheat_die: KeyCode::F4,
|
||||
key_cheat_die: KeyCode::KeyZ,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -293,9 +246,6 @@ impl Settings {
|
|||
self.rotation_stabilizer_active = default.rotation_stabilizer_active;
|
||||
self.third_person = default.third_person;
|
||||
self.is_zooming = default.is_zooming;
|
||||
self.flashlight_active = default.flashlight_active;
|
||||
self.cruise_control_active = default.cruise_control_active;
|
||||
self.map_active = default.map_active;
|
||||
}
|
||||
|
||||
pub fn get_reply_keys(&self) -> [KeyCode; 10] {
|
||||
|
@ -312,100 +262,6 @@ impl Settings {
|
|||
self.key_reply10,
|
||||
];
|
||||
}
|
||||
|
||||
pub fn in_control(&self) -> bool {
|
||||
return self.alive && !self.menu_active;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default, Debug)]
|
||||
pub struct AchievementTracker {
|
||||
pub repair_suit: bool,
|
||||
pub drink_a_pizza: bool,
|
||||
pub in_jupiters_shadow: bool,
|
||||
pub find_earth: bool,
|
||||
|
||||
pub ride_every_vehicle: bool,
|
||||
pub vehicles_ridden: HashSet<String>,
|
||||
pub all_vehicles: HashSet<String>,
|
||||
|
||||
pub talk_to_everyone: bool,
|
||||
pub people_talked_to: HashSet<String>,
|
||||
pub all_people: HashSet<String>,
|
||||
}
|
||||
|
||||
impl AchievementTracker {
|
||||
pub fn to_bool_vec(&self) -> Vec<bool> {
|
||||
vec![
|
||||
self.repair_suit,
|
||||
self.drink_a_pizza,
|
||||
self.ride_every_vehicle,
|
||||
self.talk_to_everyone,
|
||||
self.find_earth,
|
||||
self.in_jupiters_shadow,
|
||||
]
|
||||
}
|
||||
pub fn achieve_all(&mut self) {
|
||||
self.repair_suit = true;
|
||||
self.drink_a_pizza = true;
|
||||
self.ride_every_vehicle = true;
|
||||
self.talk_to_everyone = true;
|
||||
self.find_earth = true;
|
||||
self.in_jupiters_shadow = true;
|
||||
}
|
||||
pub fn to_textsections(&self) -> Vec<String> {
|
||||
fn collectible(current: usize, total: usize) -> String {
|
||||
if current < total {
|
||||
format!(" ({}/{})", current, total)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
let ride = collectible(self.vehicles_ridden.len(), self.all_vehicles.len());
|
||||
let talk = collectible(self.people_talked_to.len(), self.all_people.len());
|
||||
vec![
|
||||
"Repair Your Suit\n".to_string(),
|
||||
"Enjoy A Pizza\n".to_string(),
|
||||
format!("Ride Every Vehicle{ride}\n"),
|
||||
format!("Talk To Everyone{talk}\n"),
|
||||
"Find Earth\n".to_string(),
|
||||
"Eclipse The Sun With Jupiter\n".to_string(),
|
||||
]
|
||||
}
|
||||
pub fn to_overview(&self) -> Vec<(bool, String)> {
|
||||
vec![
|
||||
(self.repair_suit, "repair your suit".into()),
|
||||
(self.drink_a_pizza, "enjoy a pizza".into()),
|
||||
(self.ride_every_vehicle, "ride every vehicle".into()),
|
||||
(self.talk_to_everyone, "talk to everyone".into()),
|
||||
(self.find_earth, "find Earth".into()),
|
||||
(self.in_jupiters_shadow, "eclipse the Sun with Jupiter".into()),
|
||||
]
|
||||
}
|
||||
pub fn to_summary(&self) -> String {
|
||||
let list = self.to_overview();
|
||||
let count = list.iter().filter(|(achieved, _)| *achieved).count();
|
||||
if count == 0 {
|
||||
return "".to_string()
|
||||
}
|
||||
let mut summary = "\n\n\nYou managed to ".to_string();
|
||||
for (i, (_, text)) in list.iter().filter(|(achieved, _)| *achieved).enumerate() {
|
||||
summary += text.as_str();
|
||||
if i + 2 == count {
|
||||
summary += ", and ";
|
||||
}
|
||||
else if i + 1 == count {
|
||||
summary += " before you perished.";
|
||||
if count == list.len() {
|
||||
summary += "\nA truly astounding achievement, a glimmer in the void, before it all fades, into nothingness.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
summary += ", ";
|
||||
}
|
||||
}
|
||||
summary
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Deserialize, Debug, Default)]
|
||||
|
@ -443,17 +299,17 @@ fn file_is_readable(file_path: &str) -> bool {
|
|||
}
|
||||
|
||||
fn get_prefs_path() -> Option<String> {
|
||||
let test = CONF_FILE;
|
||||
let test = "outfly.toml";
|
||||
if file_is_readable(test) {
|
||||
return Some(test.to_string());
|
||||
}
|
||||
if let Ok(basedir) = env::var("XDG_CONFIG_HOME") {
|
||||
let test = basedir.to_string() + "/outfly/" + CONF_FILE;
|
||||
let test = basedir.to_string() + "/outfly/outfly.toml";
|
||||
if file_is_readable(test.as_str()) {
|
||||
return Some(test);
|
||||
}
|
||||
} else if let Ok(basedir) = env::var("HOME") {
|
||||
let test = basedir.to_string() + ".config/outfly/" + CONF_FILE;
|
||||
let test = basedir.to_string() + ".config/outfly/outfly.toml";
|
||||
if file_is_readable(test.as_str()) {
|
||||
return Some(test);
|
||||
}
|
||||
|
@ -693,10 +549,3 @@ impl GameVars {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct CommandLineOptions {
|
||||
pub window_mode_fullscreen: WindowMode,
|
||||
pub window_mode_initial: WindowMode,
|
||||
pub use_gl: bool,
|
||||
}
|
||||
|
|
45
src/world.rs
|
@ -10,13 +10,14 @@
|
|||
//
|
||||
// This module populates the world with stars and asteroids.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{actor, hud, nature, shading, skeleton};
|
||||
use bevy::prelude::*;
|
||||
use bevy::math::I64Vec3;
|
||||
use bevy::math::{DVec3, I64Vec3};
|
||||
use bevy::scene::{InstanceId, SceneInstance};
|
||||
use bevy::render::mesh::Indices;
|
||||
use bevy_xpbd_3d::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::f32::consts::PI;
|
||||
use fastrand;
|
||||
|
||||
const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds
|
||||
|
@ -26,7 +27,7 @@ const STARS_MAX_MAGNITUDE: f32 = 5.5; // max 7.0, see generate_starchart.py
|
|||
|
||||
const SKYBOX: bool = false;
|
||||
|
||||
const ASTEROID_SPAWN_STEP: f64 = 1000.0;
|
||||
const ASTEROID_SPAWN_STEP: f64 = 500.0;
|
||||
const ASTEROID_VIEW_RADIUS: f64 = 3000.0;
|
||||
|
||||
const ASSET_NAME_ASTEROID1: &str = "asteroid1";
|
||||
|
@ -38,24 +39,21 @@ impl Plugin for WorldPlugin {
|
|||
app.add_systems(Startup, setup);
|
||||
app.add_systems(PostUpdate, handle_despawn);
|
||||
app.add_systems(Update, spawn_despawn_asteroids);
|
||||
app.add_systems(Update, handle_respawn.run_if(on_event::<RespawnEvent>()));
|
||||
app.add_plugins(PhysicsPlugins::default());
|
||||
//app.add_plugins(PhysicsDebugPlugin::default());
|
||||
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
||||
app.insert_resource(AsteroidUpdateTimer(
|
||||
Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating)));
|
||||
app.insert_resource(ActiveAsteroids(HashMap::new()));
|
||||
app.add_event::<DespawnAsteroidEvent>();
|
||||
app.add_event::<RespawnEvent>();
|
||||
app.add_event::<DespawnEvent>();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)] struct AsteroidUpdateTimer(Timer);
|
||||
#[derive(Resource)] pub struct ActiveAsteroids(pub HashMap<I64Vec3, AsteroidData>);
|
||||
|
||||
#[derive(Component)] struct Asteroid;
|
||||
#[derive(Component)] pub struct Star;
|
||||
#[derive(Component)] pub struct DespawnOnPlayerDeath;
|
||||
#[derive(Event)] pub struct RespawnEvent;
|
||||
|
||||
pub struct AsteroidData {
|
||||
entity: Entity,
|
||||
|
@ -63,17 +61,20 @@ pub struct AsteroidData {
|
|||
}
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct DespawnAsteroidEvent {
|
||||
pub struct DespawnEvent {
|
||||
entity: Entity,
|
||||
sceneinstance: InstanceId,
|
||||
origin: I64Vec3,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Star;
|
||||
|
||||
pub fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
mut materials_skybox: ResMut<Assets<load::SkyBox>>,
|
||||
mut materials_skybox: ResMut<Assets<shading::SkyBox>>,
|
||||
) {
|
||||
// Generate starmap
|
||||
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(16, 16));
|
||||
|
@ -138,8 +139,6 @@ pub fn setup(
|
|||
},
|
||||
..default()
|
||||
},
|
||||
Position::from(pos_render),
|
||||
Rotation::from(Quat::IDENTITY),
|
||||
));
|
||||
starcount += 1;
|
||||
}
|
||||
|
@ -157,7 +156,7 @@ pub fn setup(
|
|||
}
|
||||
commands.spawn(MaterialMeshBundle {
|
||||
mesh: meshes.add(mesh),
|
||||
material: materials_skybox.add(load::SkyBox {}),
|
||||
material: materials_skybox.add(shading::SkyBox {}),
|
||||
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 0.0)),
|
||||
..default()
|
||||
});
|
||||
|
@ -169,11 +168,11 @@ fn spawn_despawn_asteroids(
|
|||
mut timer: ResMut<AsteroidUpdateTimer>,
|
||||
mut commands: Commands,
|
||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||
mut ew_despawn: EventWriter<DespawnAsteroidEvent>,
|
||||
mut ew_despawn: EventWriter<DespawnEvent>,
|
||||
mut db: ResMut<ActiveAsteroids>,
|
||||
q_asteroid: Query<(&Position, &SceneInstance), With<Asteroid>>,
|
||||
mut last_player_cell: Local<I64Vec3>,
|
||||
id2pos: Res<game::Id2Pos>,
|
||||
id2pos: Res<actor::Id2Pos>,
|
||||
asset_server: Res<AssetServer>,
|
||||
) {
|
||||
if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() {
|
||||
|
@ -217,7 +216,7 @@ fn spawn_despawn_asteroids(
|
|||
{
|
||||
if let Ok((pos, sceneinstance)) = q_asteroid.get(asteroid.entity) {
|
||||
if pos.0.distance(player.0) > 1000.0 {
|
||||
ew_despawn.send(DespawnAsteroidEvent {
|
||||
ew_despawn.send(DespawnEvent {
|
||||
entity: asteroid.entity,
|
||||
sceneinstance: **sceneinstance,
|
||||
origin: origin.clone(),
|
||||
|
@ -301,7 +300,7 @@ fn spawn_despawn_asteroids(
|
|||
AngularVelocity(DVec3::new(0.1, 0.1, 0.03)),
|
||||
LinearVelocity(DVec3::new(0.0, 0.0, 0.35)),
|
||||
Collider::sphere(1.0),
|
||||
Rotation::from(Quat::from_rotation_y(-PI32 / 3.)),
|
||||
Rotation::from(Quat::from_rotation_y(-PI / 3.)),
|
||||
Position::new(pos),
|
||||
hud::IsClickable {
|
||||
name: Some("Uncharted Rock".to_string()),
|
||||
|
@ -321,7 +320,7 @@ fn spawn_despawn_asteroids(
|
|||
},
|
||||
..default()
|
||||
});
|
||||
load_asset(model, &mut entity_commands, &*asset_server);
|
||||
skeleton::load(model, &mut entity_commands, &*asset_server);
|
||||
db.0.insert(origin, AsteroidData {
|
||||
entity: entity_commands.id(),
|
||||
//viewdistance: 99999999.0,
|
||||
|
@ -333,7 +332,7 @@ fn spawn_despawn_asteroids(
|
|||
|
||||
fn handle_despawn(
|
||||
mut commands: Commands,
|
||||
mut er_despawn: EventReader<DespawnAsteroidEvent>,
|
||||
mut er_despawn: EventReader<DespawnEvent>,
|
||||
mut db: ResMut<ActiveAsteroids>,
|
||||
mut scene_spawner: ResMut<SceneSpawner>,
|
||||
) {
|
||||
|
@ -343,11 +342,3 @@ fn handle_despawn(
|
|||
db.0.remove(&despawn.origin);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_respawn(
|
||||
ew_spawn: EventWriter<cmd::SpawnEvent>,
|
||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||
) {
|
||||
*achievement_tracker = var::AchievementTracker::default();
|
||||
cmd::load_defs(ew_spawn);
|
||||
}
|
||||
|
|