Compare commits
43 commits
83313c1a5f
...
4fecab5428
Author | SHA1 | Date | |
---|---|---|---|
yuni | 4fecab5428 | ||
yuni | 3429ca5ab7 | ||
yuni | 9f936989f3 | ||
yuni | 1ce864c746 | ||
yuni | 63beec86a2 | ||
yuni | 3f1dc27684 | ||
yuni | 0bbca303cb | ||
yuni | f7002fd064 | ||
yuni | 76bfdf0bfb | ||
yuni | 23c86e9c2f | ||
yuni | 28cf269907 | ||
yuni | cc3213788e | ||
yuni | 192d2e0fcb | ||
yuni | efbb44a9fc | ||
yuni | 099e935e3e | ||
yuni | 2a6e14aa90 | ||
yuni | 8d4ad64330 | ||
yuni | e2046380ea | ||
yuni | 60be58b0fa | ||
yuni | 224e0ce2c9 | ||
yuni | bfad39613e | ||
yuni | e9afeefb7d | ||
yuni | c9e38c7b29 | ||
yuni | 62a0387867 | ||
yuni | 91d19e94a0 | ||
yuni | 8a07e9cfb7 | ||
yuni | c56b5d6d74 | ||
yuni | bcba3d0945 | ||
yuni | 9b48112ee6 | ||
yuni | 8acbd4f33b | ||
yuni | 93e5ee26e4 | ||
yuni | 8621002931 | ||
yuni | 220ab340fb | ||
yuni | 87199f41db | ||
yuni | b0ac508d91 | ||
yuni | 14a22699bc | ||
yuni | 2bc86227a9 | ||
yuni | 61b63aefbe | ||
yuni | c7e3a9396e | ||
yuni | b62c5a6287 | ||
yuni | 42c1d3e191 | ||
yuni | 77b682a7c1 | ||
yuni | 6043d4a1b0 |
10
CHANGELOG.md
|
@ -1,8 +1,16 @@
|
||||||
# git
|
# v0.9.2
|
||||||
|
|
||||||
|
- Implement customizable player avatars
|
||||||
|
- Implement NPC AI that dynamically changes their facing direction
|
||||||
|
- Implement "template" shortcuts for defining world objects
|
||||||
|
- Add actual Jupiter sound recording, playing when Augmented Reality is off
|
||||||
|
|
||||||
|
# v0.9.1
|
||||||
|
|
||||||
- Implement cruise control
|
- Implement cruise control
|
||||||
- New logo: a white O, diagonal ring around it, and neon pink glow
|
- New logo: a white O, diagonal ring around it, and neon pink glow
|
||||||
- New dashboard icons: rotation stabiliser, radioactivity warning
|
- New dashboard icons: rotation stabiliser, radioactivity warning
|
||||||
|
- Allow zooming (right click) when Augmented Reality is disabled
|
||||||
|
|
||||||
# v0.9.0
|
# v0.9.0
|
||||||
|
|
||||||
|
|
126
Cargo.lock
generated
|
@ -291,7 +291,7 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy"
|
name = "bevy"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_dylib",
|
"bevy_dylib",
|
||||||
"bevy_internal",
|
"bevy_internal",
|
||||||
|
@ -300,7 +300,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_a11y"
|
name = "bevy_a11y"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit",
|
"accesskit",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
@ -311,14 +311,14 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_animation"
|
name = "bevy_animation"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
"bevy_core",
|
"bevy_core",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_time",
|
"bevy_time",
|
||||||
|
@ -329,7 +329,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_app"
|
name = "bevy_app"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
|
@ -344,7 +344,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_asset"
|
name = "bevy_asset"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-fs",
|
"async-fs",
|
||||||
|
@ -376,7 +376,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_asset_macros"
|
name = "bevy_asset_macros"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -387,13 +387,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_audio"
|
name = "bevy_audio"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_transform",
|
"bevy_transform",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
|
@ -403,11 +403,11 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_core"
|
name = "bevy_core"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_tasks",
|
"bevy_tasks",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
|
@ -417,7 +417,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_core_pipeline"
|
name = "bevy_core_pipeline"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
|
@ -425,7 +425,7 @@ dependencies = [
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_transform",
|
"bevy_transform",
|
||||||
|
@ -438,7 +438,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_derive"
|
name = "bevy_derive"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -448,7 +448,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_diagnostic"
|
name = "bevy_diagnostic"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_core",
|
"bevy_core",
|
||||||
|
@ -463,7 +463,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_dylib"
|
name = "bevy_dylib"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_internal",
|
"bevy_internal",
|
||||||
]
|
]
|
||||||
|
@ -471,7 +471,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ecs"
|
name = "bevy_ecs"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"bevy_ecs_macros",
|
"bevy_ecs_macros",
|
||||||
|
@ -490,7 +490,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ecs_macros"
|
name = "bevy_ecs_macros"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -501,7 +501,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_embedded_assets"
|
name = "bevy_embedded_assets"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy_embedded_assets.git?rev=bb925e7e5373c742c01e6e7aff04e92fdc07c095#bb925e7e5373c742c01e6e7aff04e92fdc07c095"
|
source = "git+https://codeberg.org/outfly/bevy_embedded_assets.git?rev=2696fcc0319e8660c50d4601e7d4e530cf0bb981#2696fcc0319e8660c50d4601e7d4e530cf0bb981"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"cargo-emit",
|
"cargo-emit",
|
||||||
|
@ -512,7 +512,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_encase_derive"
|
name = "bevy_encase_derive"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"encase_derive_impl",
|
"encase_derive_impl",
|
||||||
|
@ -521,7 +521,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_gizmos"
|
name = "bevy_gizmos"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
|
@ -530,7 +530,7 @@ dependencies = [
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_gizmos_macros",
|
"bevy_gizmos_macros",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_pbr",
|
"bevy_pbr",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
|
@ -542,7 +542,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_gizmos_macros"
|
name = "bevy_gizmos_macros"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -553,7 +553,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_gltf"
|
name = "bevy_gltf"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bevy_animation",
|
"bevy_animation",
|
||||||
|
@ -564,7 +564,7 @@ dependencies = [
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_pbr",
|
"bevy_pbr",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
|
@ -582,7 +582,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_hierarchy"
|
name = "bevy_hierarchy"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_core",
|
"bevy_core",
|
||||||
|
@ -595,11 +595,11 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_input"
|
name = "bevy_input"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
"smol_str",
|
"smol_str",
|
||||||
|
@ -609,7 +609,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_internal"
|
name = "bevy_internal"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_a11y",
|
"bevy_a11y",
|
||||||
"bevy_animation",
|
"bevy_animation",
|
||||||
|
@ -626,7 +626,7 @@ dependencies = [
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_input",
|
"bevy_input",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_pbr",
|
"bevy_pbr",
|
||||||
"bevy_ptr",
|
"bevy_ptr",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
|
@ -646,7 +646,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_log"
|
name = "bevy_log"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_log-sys",
|
"android_log-sys",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
@ -661,7 +661,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_macro_utils"
|
name = "bevy_macro_utils"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -682,7 +682,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_math"
|
name = "bevy_math"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -691,7 +691,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_mikktspace"
|
name = "bevy_mikktspace"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
]
|
]
|
||||||
|
@ -699,14 +699,14 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_pbr"
|
name = "bevy_pbr"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
"bevy_core_pipeline",
|
"bevy_core_pipeline",
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_transform",
|
"bevy_transform",
|
||||||
|
@ -723,14 +723,14 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ptr"
|
name = "bevy_ptr"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_reflect"
|
name = "bevy_reflect"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_ptr",
|
"bevy_ptr",
|
||||||
"bevy_reflect_derive",
|
"bevy_reflect_derive",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
|
@ -745,7 +745,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_reflect_derive"
|
name = "bevy_reflect_derive"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -757,7 +757,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_render"
|
name = "bevy_render"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
@ -768,7 +768,7 @@ dependencies = [
|
||||||
"bevy_encase_derive",
|
"bevy_encase_derive",
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_mikktspace",
|
"bevy_mikktspace",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render_macros",
|
"bevy_render_macros",
|
||||||
|
@ -801,7 +801,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_render_macros"
|
name = "bevy_render_macros"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -812,7 +812,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_scene"
|
name = "bevy_scene"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
|
@ -831,7 +831,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_sprite"
|
name = "bevy_sprite"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
|
@ -839,7 +839,7 @@ dependencies = [
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_transform",
|
"bevy_transform",
|
||||||
|
@ -856,7 +856,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_tasks"
|
name = "bevy_tasks"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
|
@ -869,13 +869,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_text"
|
name = "bevy_text"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_sprite",
|
"bevy_sprite",
|
||||||
|
@ -890,7 +890,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_time"
|
name = "bevy_time"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
|
@ -903,12 +903,12 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_transform"
|
name = "bevy_transform"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -916,7 +916,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ui"
|
name = "bevy_ui"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_a11y",
|
"bevy_a11y",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
@ -927,7 +927,7 @@ dependencies = [
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_input",
|
"bevy_input",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_render",
|
"bevy_render",
|
||||||
"bevy_sprite",
|
"bevy_sprite",
|
||||||
|
@ -943,7 +943,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_utils"
|
name = "bevy_utils"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"bevy_utils_proc_macros",
|
"bevy_utils_proc_macros",
|
||||||
|
@ -961,7 +961,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_utils_proc_macros"
|
name = "bevy_utils_proc_macros"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -971,13 +971,13 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_window"
|
name = "bevy_window"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_a11y",
|
"bevy_a11y",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_input",
|
"bevy_input",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
@ -987,7 +987,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_winit"
|
name = "bevy_winit"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960#2076f102be15a88a5026e128851a8ee1ae9d8960"
|
source = "git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b#e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accesskit_winit",
|
"accesskit_winit",
|
||||||
"approx",
|
"approx",
|
||||||
|
@ -997,7 +997,7 @@ dependencies = [
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_hierarchy",
|
"bevy_hierarchy",
|
||||||
"bevy_input",
|
"bevy_input",
|
||||||
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=2076f102be15a88a5026e128851a8ee1ae9d8960)",
|
"bevy_math 0.13.2 (git+https://codeberg.org/outfly/bevy.git?rev=e4dc13639106aa86826f6243d58f3209e1e94b1b)",
|
||||||
"bevy_tasks",
|
"bevy_tasks",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
"bevy_window",
|
"bevy_window",
|
||||||
|
@ -1011,7 +1011,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_xpbd_3d"
|
name = "bevy_xpbd_3d"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912#99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=b6a03d6ec41e409d56f6b876f654a14d0b33afa7#b6a03d6ec41e409d56f6b876f654a14d0b33afa7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_math 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bevy_math 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1028,7 +1028,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_xpbd_derive"
|
name = "bevy_xpbd_derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912#99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
source = "git+https://codeberg.org/outfly/bevy_xpbd.git?rev=b6a03d6ec41e409d56f6b876f654a14d0b33afa7#b6a03d6ec41e409d56f6b876f654a14d0b33afa7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.52",
|
"syn 2.0.52",
|
||||||
|
@ -2767,7 +2767,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "outfly"
|
name = "outfly"
|
||||||
version = "0.9.0"
|
version = "0.9.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy",
|
"bevy",
|
||||||
"bevy_embedded_assets",
|
"bevy_embedded_assets",
|
||||||
|
|
12
Cargo.toml
|
@ -10,10 +10,10 @@
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "outfly"
|
name = "outfly"
|
||||||
version = "0.9.0"
|
version = "0.9.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
homepage = "https://codeberg.org/hut/outfly"
|
homepage = "https://codeberg.org/outfly/outfly"
|
||||||
repository = "https://codeberg.org/hut/outfly"
|
repository = "https://codeberg.org/outfly/outfly"
|
||||||
categories = ["game", "aerospace", "simulation"]
|
categories = ["game", "aerospace", "simulation"]
|
||||||
keywords = ["game", "space", "3d"]
|
keywords = ["game", "space", "3d"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
@ -38,7 +38,7 @@ toml_edit = { version = "0.22", features = ["serde"] }
|
||||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
git = "https://codeberg.org/outfly/bevy.git"
|
git = "https://codeberg.org/outfly/bevy.git"
|
||||||
rev = "2076f102be15a88a5026e128851a8ee1ae9d8960"
|
rev = "e4dc13639106aa86826f6243d58f3209e1e94b1b"
|
||||||
default-features = false
|
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"]
|
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"]
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ features = ["animation", "bevy_asset", "bevy_audio", "bevy_scene", "bevy_winit",
|
||||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
git = "https://codeberg.org/outfly/bevy_embedded_assets.git"
|
git = "https://codeberg.org/outfly/bevy_embedded_assets.git"
|
||||||
rev = "bb925e7e5373c742c01e6e7aff04e92fdc07c095"
|
rev = "2696fcc0319e8660c50d4601e7d4e530cf0bb981"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.bevy_xpbd_3d]
|
[dependencies.bevy_xpbd_3d]
|
||||||
|
@ -55,7 +55,7 @@ optional = true
|
||||||
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
# We temporarily use a fork with a custom bug fix, see https://codeberg.org/outfly/bevy
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
git = "https://codeberg.org/outfly/bevy_xpbd.git"
|
git = "https://codeberg.org/outfly/bevy_xpbd.git"
|
||||||
rev = "99bca3f6a25b8c4e6ec6509e9e9b0e7bed565912"
|
rev = "b6a03d6ec41e409d56f6b876f654a14d0b33afa7"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["3d", "f64", "parry-f64", "parallel", "async-collider"]
|
features = ["3d", "f64", "parry-f64", "parallel", "async-collider"]
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Licenses of OutFly files
|
# Licenses of OutFly files
|
||||||
|
|
||||||
- Source code: GPL Version 3.0
|
- Source code: GPL Version 3.0
|
||||||
- https://codeberg.org/hut/outfly
|
- https://codeberg.org/outfly/outfly
|
||||||
- 3D models: Original art, placed under the Creative Commons CC0 License
|
- 3D models: Original art, placed under the Creative Commons CC0 License
|
||||||
- Photographs of celestial bodies:
|
- Photographs of celestial bodies:
|
||||||
- Mercury: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
- Mercury: [By Solar System Scope, CC BY 4.0](https://www.solarsystemscope.com/textures/)
|
||||||
|
@ -41,7 +41,11 @@
|
||||||
- https://pixabay.com/sound-effects/whoosh-blow-flutter-shortwav-14678/
|
- https://pixabay.com/sound-effects/whoosh-blow-flutter-shortwav-14678/
|
||||||
- https://pixabay.com/sound-effects/dslr-camera-sounds-26117/
|
- https://pixabay.com/sound-effects/dslr-camera-sounds-26117/
|
||||||
- https://pixabay.com/sound-effects/beep-6-96243
|
- 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)
|
- Music:
|
||||||
|
- JupiterRecording.ogg is an [actual Jupiter recording by NASA](https://archive.org/download/voyager-1-and-2-1990-jupiter-nasa-voyager-space-sounds-electronic), public domain.
|
||||||
|
- Processed by cutting out min 1:47-3:47 and applying a 10s linear crossfade at the end. Exported as ogg with quality=3, see [.kdenlive file](src/audio/JupiterRecording.kdenlive).
|
||||||
|
- The source file has been taken down, and generally, I can't find information on how exactly this was produced. Hoping it's not a hoax.
|
||||||
|
- [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)
|
- 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:
|
- Font Yupiter-Regular.ttf is placed under the SIL OPEN FONT LICENSE Version 1.1 and is based on:
|
||||||
- Noto Sans Symbols 2, Copyright 2022 The Noto Project Authors (https://github.com/notofonts/symbols)
|
- Noto Sans Symbols 2, Copyright 2022 The Noto Project Authors (https://github.com/notofonts/symbols)
|
||||||
|
|
63
README.md
|
@ -1,4 +1,4 @@
|
||||||
![OutFly Screenshot](doc/images/screenshot3.jpg)
|
![OutFly Screenshot](doc/branding/banner.jpg)
|
||||||
|
|
||||||
[Features](#features) • [Controls](#controls) • [Running OutFly](#running-outfly) • [Troubleshooting](#troubleshooting)
|
[Features](#features) • [Controls](#controls) • [Running OutFly](#running-outfly) • [Troubleshooting](#troubleshooting)
|
||||||
|
|
||||||
|
@ -16,43 +16,44 @@ Imagine a blend of [Fallout](https://en.wikipedia.org/wiki/Fallout_%28series%29)
|
||||||
|
|
||||||
This game aims to respect the player as much as possible. It doesn't waste your time: Despite the vastness of space, nothing takes too long. Speed cheats are active by default, allowing you to visit places you normally couldn't, without passing out from the g-forces. There are no anxiety-causing features (apart of, maybe, space itself), no loading screens, nothing to micromanage, not even save games. You can plunge into the game any time you feel like it, and it's up to you whether you just want to soak in the beautiful scenery, engage with the survival mechanics [still in development], or dive into the game story [still in development]. And finally, it's not just DRM-free but completely open source, allowing you to tinker on any part of the game to your liking.
|
This game aims to respect the player as much as possible. It doesn't waste your time: Despite the vastness of space, nothing takes too long. Speed cheats are active by default, allowing you to visit places you normally couldn't, without passing out from the g-forces. There are no anxiety-causing features (apart of, maybe, space itself), no loading screens, nothing to micromanage, not even save games. You can plunge into the game any time you feel like it, and it's up to you whether you just want to soak in the beautiful scenery, engage with the survival mechanics [still in development], or dive into the game story [still in development]. And finally, it's not just DRM-free but completely open source, allowing you to tinker on any part of the game to your liking.
|
||||||
|
|
||||||
Source code: https://codeberg.org/hut/outfly
|
Source code: https://codeberg.org/outfly/outfly
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- A beautiful, serene atmosphere with gorgeous views
|
- A beautiful, serene atmosphere with gorgeous views
|
||||||
- Racing vehicles, unique NPCs, cozy places, deadly environment
|
- Racing vehicles, unique NPCs, cozy places, deadly environment
|
||||||
- Accurate, clickable star chart. Can you spot the constellations?
|
- Accurate, clickable star chart. Can you spot the constellations?
|
||||||
- Cross platform, [free & open source](https://codeberg.org/hut/outfly) forever!
|
- Cross platform, [free & open source](https://codeberg.org/outfly/outfly) forever!
|
||||||
- Written in [Rust](https://www.rust-lang.org) with the [Bevy game engine](https://bevyengine.org)
|
- Written in [Rust](https://www.rust-lang.org) with the [Bevy game engine](https://bevyengine.org)
|
||||||
- Status: Early access, not much content
|
- Status: Early access, not much content
|
||||||
|
|
||||||
# Controls
|
# Controls
|
||||||
|
|
||||||
You can view these any time in game through the game menu (press Escape.)
|
Press **ESC** to view these any time from the in-game menu.
|
||||||
|
|
||||||
- Space: Slow down, match velocity
|
- **Space**: Slow down, match velocity
|
||||||
- E: Interact
|
- **E**: Interact
|
||||||
- F: Flashlight
|
- **F**: Flashlight
|
||||||
- Q: Exit vehicle
|
- **Q**: Exit vehicle
|
||||||
- M: Map
|
- **M**: Map
|
||||||
- C: Camera
|
- **C**: Camera
|
||||||
- T: Cruise control
|
- **T**: Cruise control
|
||||||
- R: Rotate (hold + move mouse)
|
- **R**: Rotate (hold + move mouse)
|
||||||
- Y: Rotation stabilizer
|
- **Y**: Rotation stabilizer
|
||||||
- AWSD/Shift/Ctrl: Move
|
- **AWSD/Shift/Ctrl**: Move
|
||||||
- J/K/U/L/I/O: Rotate
|
- **J/K/U/L/I/O**: Rotate
|
||||||
- F11: Fullscreen
|
- **F11**: Fullscreen
|
||||||
- Tab: Toggle Augmented Reality
|
- **Tab**: Toggle Augmented Reality
|
||||||
- Augmented Reality only:
|
- **Left click**: Target objects (in AR only)
|
||||||
- Left click: Target objects
|
- **Right click**: Zoom
|
||||||
- Right click: Zoom
|
|
||||||
- Cheats:
|
Cheats:
|
||||||
- G: Toggle cheats + invulnerability
|
|
||||||
- V/B: Impossible acceleration
|
- **G**: Toggle cheats + invulnerability
|
||||||
- Shift+V/B: Extreme acceleration
|
- **V/B**: Impossible acceleration
|
||||||
- X: Teleport to target
|
- **Shift+V/B**: Extreme acceleration
|
||||||
- Z: Stop
|
- **X**: Teleport to target
|
||||||
|
- **Z**: Stop
|
||||||
|
|
||||||
# Running OutFly
|
# Running OutFly
|
||||||
## System Requirements
|
## System Requirements
|
||||||
|
@ -63,7 +64,7 @@ You can view these any time in game through the game menu (press Escape.)
|
||||||
|
|
||||||
## Running on Linux
|
## Running on Linux
|
||||||
|
|
||||||
1. Download and unpack the latest release: https://codeberg.org/hut/outfly/releases
|
1. Download and unpack the latest release: https://codeberg.org/outfly/outfly/releases
|
||||||
2. Open a terminal and navigate to the directory where you unpacked outfly
|
2. Open a terminal and navigate to the directory where you unpacked outfly
|
||||||
3. If you are on ArchLinux, type the following commands. For other distributions, replace "pacman -S" with the distro's command to install packages. Also, the packages may be called slightly differently.
|
3. If you are on ArchLinux, type the following commands. For other distributions, replace "pacman -S" with the distro's command to install packages. Also, the packages may be called slightly differently.
|
||||||
|
|
||||||
|
@ -78,14 +79,16 @@ Alternatively, you can also install OutFly as a package, if your distribution ha
|
||||||
yay -S outfly-git
|
yay -S outfly-git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See also: [build instructions](https://codeberg.org/outfly/outfly/src/branch/main/build/README.md)
|
||||||
|
|
||||||
## Running on Windows
|
## Running on Windows
|
||||||
|
|
||||||
1. Download and unpack the latest release: https://codeberg.org/hut/outfly/releases
|
1. Download and unpack the latest release: https://codeberg.org/outfly/outfly/releases
|
||||||
2. Double-click on `OutFly.exe`
|
2. Double-click on `OutFly.exe`
|
||||||
|
|
||||||
## Running on MacOS / Android / iOS
|
## Running on MacOS / Android / iOS
|
||||||
|
|
||||||
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.
|
No releases for these operating systems exist yet. For MacOS, you can build OutFly yourself using the [build instructions](https://codeberg.org/outfly/outfly/src/branch/main/build/README.md). Support for Android/iOS is planned for the future.
|
||||||
|
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
## I'm stuck inside another object
|
## I'm stuck inside another object
|
||||||
|
@ -112,7 +115,7 @@ Try changing the full screen mode with the command line option "--fs-legacy":
|
||||||
cargo run -- --fs-legacy
|
cargo run -- --fs-legacy
|
||||||
```
|
```
|
||||||
|
|
||||||
If this doesn't work, please open an issue on https://codeberg.org/hut/outfly and provide as many details as you can, including the crash log.
|
If this doesn't work, please open an issue on https://codeberg.org/outfly/outfly and provide as many details as you can, including the crash log.
|
||||||
|
|
||||||
## Crash with "error while loading shared libraries"
|
## Crash with "error while loading shared libraries"
|
||||||
|
|
||||||
|
|
BIN
assets/models/suit_v2/ar_wings.glb
Normal file
BIN
assets/music/JupiterRecording.ogg
Normal file
|
@ -90,7 +90,7 @@ python -m http.server -d wasm
|
||||||
|
|
||||||
## Building release versions optimized for packaging
|
## 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/outfly/outfly/src/branch/main/build/pack.sh))
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo build --release --no-default-features --features release_[linux|windows] [--target=$YOUR_TARGET]
|
cargo build --release --no-default-features --features release_[linux|windows] [--target=$YOUR_TARGET]
|
||||||
|
|
|
@ -13,5 +13,5 @@
|
||||||
|
|
||||||
rootdir="${1:-}"
|
rootdir="${1:-}"
|
||||||
install -Dm755 "target/release/outfly" "$rootdir/usr/bin/outfly"
|
install -Dm755 "target/release/outfly" "$rootdir/usr/bin/outfly"
|
||||||
install -Dm644 "build/linux/outfly.png" "$rootdir/usr/share/pixmaps/outfly.png"
|
install -Dm644 "doc/branding/logo.png" "$rootdir/usr/share/pixmaps/outfly.png"
|
||||||
install -Dm644 "build/linux/outfly.desktop" "$rootdir/usr/share/applications/outfly.desktop"
|
install -Dm644 "build/linux/outfly.desktop" "$rootdir/usr/share/applications/outfly.desktop"
|
||||||
|
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
26
doc/branding/README.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# OutFly Branding
|
||||||
|
## Colors
|
||||||
|
|
||||||
|
- Primary color: #BE1251 <span style="color:#BE1251">(pink)</span>
|
||||||
|
- Secondary color: #CCCCCC <span style="color:#CCCCCC">(white)</span>
|
||||||
|
- Warning color: #F0D50C <span style="color:#F0D50C">(golden)</span>
|
||||||
|
|
||||||
|
## Logo
|
||||||
|
|
||||||
|
[![logo](logo.png)](logo.png)
|
||||||
|
|
||||||
|
based on the [SVG Version](logo.svg), exported with Inkscape 1.3.2
|
||||||
|
|
||||||
|
## Sticker
|
||||||
|
|
||||||
|
[![sticker](sticker.jpg)](sticker.jpg)
|
||||||
|
|
||||||
|
based on the [SVG Version](sticker.svg), exported with Inkscape 1.3.2
|
||||||
|
|
||||||
|
Sticker parameters:
|
||||||
|
|
||||||
|
- Printing service provider: https://www.flyeralarm.com/de/c/druckprodukte/aufkleber/
|
||||||
|
- Type: Outdoor sticker
|
||||||
|
- Format: Square, 5x5 cm
|
||||||
|
- Material: 65 micrometer, silver, iridescent
|
||||||
|
- Colors: 4/0-colored
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
BIN
doc/branding/logo.png
Normal file
After Width: | Height: | Size: 17 KiB |
|
@ -27,12 +27,12 @@
|
||||||
inkscape:deskcolor="#000000"
|
inkscape:deskcolor="#000000"
|
||||||
inkscape:document-units="px"
|
inkscape:document-units="px"
|
||||||
inkscape:zoom="2.3395291"
|
inkscape:zoom="2.3395291"
|
||||||
inkscape:cx="139.34428"
|
inkscape:cx="50.223782"
|
||||||
inkscape:cy="142.55005"
|
inkscape:cy="56.635329"
|
||||||
inkscape:window-width="2880"
|
inkscape:window-width="2880"
|
||||||
inkscape:window-height="1627"
|
inkscape:window-height="1765"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
inkscape:window-y="138"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
|
@ -56,10 +56,10 @@
|
||||||
style="color-interpolation-filters:sRGB"
|
style="color-interpolation-filters:sRGB"
|
||||||
id="filter1"
|
id="filter1"
|
||||||
inkscape:label="bloom"
|
inkscape:label="bloom"
|
||||||
x="-0.26431716"
|
x="-0.28216198"
|
||||||
y="-0.26431324"
|
y="-0.2827584"
|
||||||
width="1.5286343"
|
width="1.564324"
|
||||||
height="1.5286265"><feConvolveMatrix
|
height="1.5655168"><feConvolveMatrix
|
||||||
order="1 1"
|
order="1 1"
|
||||||
kernelMatrix="1.0000000 "
|
kernelMatrix="1.0000000 "
|
||||||
id="feConvolveMatrix1"
|
id="feConvolveMatrix1"
|
||||||
|
@ -84,13 +84,13 @@
|
||||||
id="g227"
|
id="g227"
|
||||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.96875;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1)"
|
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
|
transform="matrix(0.79690616,0,0,0.79690616,6.8658634,6.7579172)"><circle
|
||||||
style="fill:none;fill-opacity:1;stroke:#a90e47;stroke-width:5.97623696;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
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"
|
id="path1"
|
||||||
cx="33.848091"
|
cx="33.848091"
|
||||||
cy="33.848091"
|
cy="33.848091"
|
||||||
inkscape:label="circle"
|
inkscape:label="circle"
|
||||||
r="21.166666" /><path
|
r="21.166666" /><path
|
||||||
style="color:#000000;fill:#a90e47;stroke:none;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none;fill-opacity:1"
|
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"
|
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"
|
id="path2"
|
||||||
sodipodi:nodetypes="sccsssccsss" /></g></g></svg>
|
sodipodi:nodetypes="sccsssccsss" /></g></g></svg>
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
BIN
doc/branding/sticker.jpg
Normal file
After Width: | Height: | Size: 654 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
224
src/actor.rs
|
@ -14,9 +14,9 @@
|
||||||
//
|
//
|
||||||
// This module should never handle any visual aspects directly.
|
// This module should never handle any visual aspects directly.
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
pub const ENGINE_SPEED_FACTOR: f32 = 30.0;
|
||||||
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
const MAX_TRANSMISSION_DISTANCE: f32 = 100.0;
|
||||||
|
@ -25,23 +25,31 @@ const MAX_INTERACT_DISTANCE: f32 = 50.0;
|
||||||
pub struct ActorPlugin;
|
pub struct ActorPlugin;
|
||||||
impl Plugin for ActorPlugin {
|
impl Plugin for ActorPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(FixedUpdate, (
|
app.add_systems(
|
||||||
|
FixedUpdate,
|
||||||
|
(
|
||||||
update_physics_lifeforms,
|
update_physics_lifeforms,
|
||||||
update_power,
|
update_power,
|
||||||
handle_wants_maxrotation,
|
handle_wants_maxrotation,
|
||||||
handle_wants_maxvelocity,
|
handle_wants_maxvelocity,
|
||||||
));
|
handle_wants_lookat.run_if(alive),
|
||||||
app.add_systems(PostUpdate, handle_gforce
|
),
|
||||||
|
);
|
||||||
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
handle_gforce
|
||||||
.after(PhysicsSet::Sync)
|
.after(PhysicsSet::Sync)
|
||||||
.after(sync::position_to_transform));
|
.after(sync::position_to_transform),
|
||||||
app.add_systems(Update, (
|
);
|
||||||
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
handle_input.run_if(in_control),
|
handle_input.run_if(in_control),
|
||||||
handle_collisions,
|
handle_collisions,
|
||||||
handle_damage,
|
handle_damage,
|
||||||
));
|
),
|
||||||
app.add_systems(PostUpdate, (
|
);
|
||||||
handle_vehicle_enter_exit,
|
app.add_systems(PostUpdate, (handle_vehicle_enter_exit,));
|
||||||
));
|
|
||||||
app.add_event::<VehicleEnterExitEvent>();
|
app.add_event::<VehicleEnterExitEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +74,7 @@ pub struct VehicleEnterExitEvent {
|
||||||
driver: Entity,
|
driver: Entity,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
is_entering: bool,
|
is_entering: bool,
|
||||||
is_player: bool
|
is_player: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
@ -112,26 +120,43 @@ pub struct ExperiencesGForce {
|
||||||
pub last_linear_velocity: DVec3,
|
pub last_linear_velocity: DVec3,
|
||||||
pub ignore_gforce_seconds: f32,
|
pub ignore_gforce_seconds: f32,
|
||||||
}
|
}
|
||||||
impl Default for ExperiencesGForce { fn default() -> Self { Self {
|
impl Default for ExperiencesGForce {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
gforce: 0.0,
|
gforce: 0.0,
|
||||||
damage_threshold: 100.0,
|
damage_threshold: 100.0,
|
||||||
visual_effect_threshold: 20.0,
|
visual_effect_threshold: 20.0,
|
||||||
visual_effect: 0.0,
|
visual_effect: 0.0,
|
||||||
last_linear_velocity: DVec3::splat(0.0),
|
last_linear_velocity: DVec3::splat(0.0),
|
||||||
ignore_gforce_seconds: 0.0,
|
ignore_gforce_seconds: 0.0,
|
||||||
}}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)] pub struct Player; // Attached to the suit of the player
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct PlayerCollider; // Attached to the collider of the suit of the player
|
pub struct Player; // Attached to the suit of the player
|
||||||
#[derive(Component)] pub struct PlayerDrivesThis; // Attached to the entered vehicle
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct PlayerCamera; // Attached to the actor to use as point of view
|
pub struct PlayerCollider; // Attached to the collider of the suit of the player
|
||||||
#[derive(Component)] pub struct JustNowEnteredVehicle;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct ActorEnteringVehicle;
|
pub struct PlayerDrivesThis; // Attached to the entered vehicle
|
||||||
#[derive(Component)] pub struct ActorVehicleBeingEntered;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct PlayersFlashLight;
|
pub struct PlayerCamera; // Attached to the actor to use as point of view
|
||||||
#[derive(Component)] pub struct WantsMaxRotation(pub f64);
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct WantsMaxVelocity(pub f64);
|
pub struct JustNowEnteredVehicle;
|
||||||
#[derive(Component)] pub struct Identifier(pub String);
|
#[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 WantsToLookAt(pub String);
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Identifier(pub String);
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct LifeForm {
|
pub struct LifeForm {
|
||||||
|
@ -140,20 +165,28 @@ pub struct LifeForm {
|
||||||
pub adrenaline_baseline: f32,
|
pub adrenaline_baseline: f32,
|
||||||
pub adrenaline_jolt: f32,
|
pub adrenaline_jolt: f32,
|
||||||
}
|
}
|
||||||
impl Default for LifeForm { fn default() -> Self { Self {
|
impl Default for LifeForm {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
is_alive: true,
|
is_alive: true,
|
||||||
adrenaline: 0.3,
|
adrenaline: 0.3,
|
||||||
adrenaline_baseline: 0.3,
|
adrenaline_baseline: 0.3,
|
||||||
adrenaline_jolt: 0.0,
|
adrenaline_jolt: 0.0,
|
||||||
}}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Vehicle {
|
pub struct Vehicle {
|
||||||
stored_drivers_collider: Option<Collider>,
|
stored_drivers_collider: Option<Collider>,
|
||||||
}
|
}
|
||||||
impl Default for Vehicle { fn default() -> Self { Self {
|
impl Default for Vehicle {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
stored_drivers_collider: None,
|
stored_drivers_collider: None,
|
||||||
}}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum EngineType {
|
pub enum EngineType {
|
||||||
|
@ -192,7 +225,11 @@ pub struct Suit {
|
||||||
pub oxygen_max: f32,
|
pub oxygen_max: f32,
|
||||||
pub integrity: f32, // [0.0 - 1.0]
|
pub integrity: f32, // [0.0 - 1.0]
|
||||||
}
|
}
|
||||||
impl Default for Suit { fn default() -> Self { SUIT_SIMPLE } }
|
impl Default for Suit {
|
||||||
|
fn default() -> Self {
|
||||||
|
SUIT_SIMPLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const SUIT_SIMPLE: Suit = Suit {
|
const SUIT_SIMPLE: Suit = Suit {
|
||||||
oxygen: nature::OXY_D,
|
oxygen: nature::OXY_D,
|
||||||
|
@ -236,8 +273,7 @@ pub fn update_power(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
battery.power = (battery.power + battery.reactor * d)
|
battery.power = (battery.power + battery.reactor * d).clamp(0.0, battery.capacity);
|
||||||
.clamp(0.0, battery.capacity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,15 +285,15 @@ pub fn update_physics_lifeforms(
|
||||||
for (mut lifeform, mut hp, mut suit, velocity) in query.iter_mut() {
|
for (mut lifeform, mut hp, mut suit, velocity) in query.iter_mut() {
|
||||||
if lifeform.adrenaline_jolt.abs() > 1e-3 {
|
if lifeform.adrenaline_jolt.abs() > 1e-3 {
|
||||||
lifeform.adrenaline_jolt *= 0.99;
|
lifeform.adrenaline_jolt *= 0.99;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
lifeform.adrenaline_jolt = 0.0
|
lifeform.adrenaline_jolt = 0.0
|
||||||
}
|
}
|
||||||
let speed = velocity.length();
|
let speed = velocity.length();
|
||||||
if speed > 1000.0 {
|
if speed > 1000.0 {
|
||||||
lifeform.adrenaline += 0.001;
|
lifeform.adrenaline += 0.001;
|
||||||
}
|
}
|
||||||
lifeform.adrenaline = (lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
|
lifeform.adrenaline =
|
||||||
|
(lifeform.adrenaline - 0.0001 + lifeform.adrenaline_jolt * 0.01).clamp(0.0, 1.0);
|
||||||
|
|
||||||
let mut oxygen_drain = nature::OXY_S;
|
let mut oxygen_drain = nature::OXY_S;
|
||||||
let integr_threshold = 0.5;
|
let integr_threshold = 0.5;
|
||||||
|
@ -278,7 +314,7 @@ pub fn update_physics_lifeforms(
|
||||||
let drain_scaling = (2.0 - 2.0 * suit.integrity / integr_threshold).powf(4.0);
|
let drain_scaling = (2.0 - 2.0 * suit.integrity / integr_threshold).powf(4.0);
|
||||||
oxygen_drain += suit.oxygen * 0.01 * drain_scaling;
|
oxygen_drain += suit.oxygen * 0.01 * drain_scaling;
|
||||||
}
|
}
|
||||||
suit.oxygen = (suit.oxygen - oxygen_drain*d).clamp(0.0, suit.oxygen_max);
|
suit.oxygen = (suit.oxygen - oxygen_drain * d).clamp(0.0, suit.oxygen_max);
|
||||||
if suit.oxygen <= 0.0 {
|
if suit.oxygen <= 0.0 {
|
||||||
hp.damage += 1.0 * d;
|
hp.damage += 1.0 * d;
|
||||||
hp.damagetype = DamageType::Asphyxiation;
|
hp.damagetype = DamageType::Asphyxiation;
|
||||||
|
@ -294,7 +330,14 @@ pub fn handle_input(
|
||||||
player: Query<Entity, With<actor::Player>>,
|
player: Query<Entity, With<actor::Player>>,
|
||||||
q_camera: Query<&Transform, With<Camera>>,
|
q_camera: Query<&Transform, With<Camera>>,
|
||||||
mut q_flashlight: Query<&mut Visibility, With<PlayersFlashLight>>,
|
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, &Actor, &Transform),
|
||||||
|
(
|
||||||
|
With<actor::Vehicle>,
|
||||||
|
Without<actor::Player>,
|
||||||
|
Without<Camera>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
mut ew_conv: EventWriter<chat::StartConversationEvent>,
|
||||||
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
|
mut ew_vehicle: EventWriter<VehicleEnterExitEvent>,
|
||||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
|
@ -313,9 +356,12 @@ pub fn handle_input(
|
||||||
.map(|(talker, transform)| (talker.clone(), transform))
|
.map(|(talker, transform)| (talker.clone(), transform))
|
||||||
.collect();
|
.collect();
|
||||||
// TODO: replace Transform.translation with Position
|
// TODO: replace Transform.translation with Position
|
||||||
if let (Some(talker), dist) = camera::find_closest_target::<chat::Talker>(objects, camtrans) {
|
if let (Some(talker), dist) = camera::find_closest_target::<chat::Talker>(objects, camtrans)
|
||||||
|
{
|
||||||
if dist <= MAX_TRANSMISSION_DISTANCE {
|
if dist <= MAX_TRANSMISSION_DISTANCE {
|
||||||
ew_conv.send(chat::StartConversationEvent{talker: talker.clone()});
|
ew_conv.send(chat::StartConversationEvent {
|
||||||
|
talker: talker.clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Entering Vehicles
|
// Entering Vehicles
|
||||||
|
@ -330,7 +376,7 @@ pub fn handle_input(
|
||||||
if dist <= MAX_INTERACT_DISTANCE {
|
if dist <= MAX_INTERACT_DISTANCE {
|
||||||
commands.entity(entity).insert(ActorVehicleBeingEntered);
|
commands.entity(entity).insert(ActorVehicleBeingEntered);
|
||||||
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
||||||
ew_vehicle.send(VehicleEnterExitEvent{
|
ew_vehicle.send(VehicleEnterExitEvent {
|
||||||
vehicle: entity,
|
vehicle: entity,
|
||||||
driver: player_entity,
|
driver: player_entity,
|
||||||
name: actor.name.clone(),
|
name: actor.name.clone(),
|
||||||
|
@ -340,13 +386,14 @@ pub fn handle_input(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if keyboard_input.just_pressed(settings.key_vehicle) {
|
||||||
else if keyboard_input.just_pressed(settings.key_vehicle) {
|
|
||||||
// Exiting Vehicles
|
// Exiting Vehicles
|
||||||
for vehicle_entity in &q_player_drives {
|
for vehicle_entity in &q_player_drives {
|
||||||
commands.entity(vehicle_entity).insert(ActorVehicleBeingEntered);
|
commands
|
||||||
|
.entity(vehicle_entity)
|
||||||
|
.insert(ActorVehicleBeingEntered);
|
||||||
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
commands.entity(player_entity).insert(ActorEnteringVehicle);
|
||||||
ew_vehicle.send(VehicleEnterExitEvent{
|
ew_vehicle.send(VehicleEnterExitEvent {
|
||||||
vehicle: vehicle_entity,
|
vehicle: vehicle_entity,
|
||||||
driver: player_entity,
|
driver: player_entity,
|
||||||
name: None,
|
name: None,
|
||||||
|
@ -355,8 +402,7 @@ pub fn handle_input(
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
} else if keyboard_input.just_pressed(settings.key_flashlight) {
|
||||||
else if keyboard_input.just_pressed(settings.key_flashlight) {
|
|
||||||
for mut flashlight_vis in &mut q_flashlight {
|
for mut flashlight_vis in &mut q_flashlight {
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||||
if *flashlight_vis == Visibility::Hidden {
|
if *flashlight_vis == Visibility::Hidden {
|
||||||
|
@ -367,8 +413,7 @@ pub fn handle_input(
|
||||||
settings.flashlight_active = false;
|
settings.flashlight_active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if keyboard_input.just_pressed(settings.key_cruise_control) {
|
||||||
else if keyboard_input.just_pressed(settings.key_cruise_control) {
|
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Switch));
|
||||||
settings.cruise_control_active ^= true;
|
settings.cruise_control_active ^= true;
|
||||||
}
|
}
|
||||||
|
@ -379,9 +424,28 @@ pub fn handle_vehicle_enter_exit(
|
||||||
mut settings: ResMut<Settings>,
|
mut settings: ResMut<Settings>,
|
||||||
mut er_vehicle: EventReader<VehicleEnterExitEvent>,
|
mut er_vehicle: EventReader<VehicleEnterExitEvent>,
|
||||||
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
mut ew_achievement: EventWriter<game::AchievementEvent>,
|
||||||
mut q_playerflashlight: Query<&mut Visibility, (With<PlayersFlashLight>, Without<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
|
mut q_playerflashlight: Query<
|
||||||
mut q_drivers: Query<(Entity, &mut Visibility, Option<&Collider>), (Without<ActorVehicleBeingEntered>, With<ActorEnteringVehicle>)>,
|
&mut Visibility,
|
||||||
mut q_vehicles: Query<(Entity, &mut Vehicle, &mut Visibility), (With<ActorVehicleBeingEntered>, Without<ActorEnteringVehicle>)>,
|
(
|
||||||
|
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>,
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
) {
|
) {
|
||||||
for event in er_vehicle.read() {
|
for event in er_vehicle.read() {
|
||||||
|
@ -410,11 +474,11 @@ pub fn handle_vehicle_enter_exit(
|
||||||
settings.flashlight_active = false;
|
settings.flashlight_active = false;
|
||||||
}
|
}
|
||||||
if let Some(vehicle_name) = &event.name {
|
if let Some(vehicle_name) = &event.name {
|
||||||
ew_achievement.send(game::AchievementEvent
|
ew_achievement.send(game::AchievementEvent::RideVehicle(
|
||||||
::RideVehicle(vehicle_name.clone()));
|
vehicle_name.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Exiting Vehicle
|
// Exiting Vehicle
|
||||||
if let Some(collider) = &vehicle_component.stored_drivers_collider {
|
if let Some(collider) = &vehicle_component.stored_drivers_collider {
|
||||||
commands.entity(driver).insert(collider.clone());
|
commands.entity(driver).insert(collider.clone());
|
||||||
|
@ -439,7 +503,9 @@ fn handle_collisions(
|
||||||
q_player: Query<Entity, With<PlayerCollider>>,
|
q_player: Query<Entity, With<PlayerCollider>>,
|
||||||
mut q_player_lifeform: Query<(&mut LifeForm, &mut Suit), With<Player>>,
|
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), Ok((mut lifeform, mut suit))) =
|
||||||
|
(q_player.get_single(), q_player_lifeform.get_single_mut())
|
||||||
|
{
|
||||||
for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
|
for CollisionStarted(entity1, entity2) in collision_event_reader.read() {
|
||||||
if *entity1 == player || *entity2 == player {
|
if *entity1 == player || *entity2 == player {
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Crash));
|
||||||
|
@ -461,9 +527,9 @@ fn handle_wants_maxrotation(
|
||||||
if total > maxrot.0 {
|
if total > maxrot.0 {
|
||||||
v_ang.0 = DVec3::splat(0.0);
|
v_ang.0 = DVec3::splat(0.0);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
let angular_slowdown: f64 =
|
||||||
let angular_slowdown: f64 = (2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64;
|
(2.0 - engine.reaction_wheels.powf(0.05).clamp(1.001, 1.1)) as f64;
|
||||||
v_ang.0 *= angular_slowdown;
|
v_ang.0 *= angular_slowdown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,18 +546,41 @@ fn handle_wants_maxvelocity(
|
||||||
if total > maxv.0 {
|
if total > maxv.0 {
|
||||||
v.0 = DVec3::splat(0.0);
|
v.0 = DVec3::splat(0.0);
|
||||||
}
|
}
|
||||||
// already not moving
|
} else {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: respect engine parameters for different thrusts for different directions
|
// TODO: respect engine parameters for different thrusts for different directions
|
||||||
let avg_thrust = (engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
|
let avg_thrust =
|
||||||
|
(engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
|
||||||
let acceleration = (avg_thrust * dt) as f64 * -v.0;
|
let acceleration = (avg_thrust * dt) as f64 * -v.0;
|
||||||
v.0 += acceleration;
|
v.0 += acceleration;
|
||||||
if v.0.length() + EPSILON < acceleration.length() {
|
if v.0.length() + EPSILON < acceleration.length() {
|
||||||
v.0 = DVec3::splat(0.0);
|
v.0 = DVec3::splat(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_wants_lookat(
|
||||||
|
mut query: Query<(&Position, &mut Rotation, &Transform, &WantsToLookAt)>,
|
||||||
|
q_playercam: Query<&Position, With<PlayerCamera>>,
|
||||||
|
id2pos: Res<game::Id2Pos>,
|
||||||
|
) {
|
||||||
|
let player_pos = if let Ok(player_pos) = q_playercam.get_single() {
|
||||||
|
player_pos
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: use ExternalTorque rather than hard-resetting the rotation
|
||||||
|
for (pos, mut rot, trans, target_id) in &mut query {
|
||||||
|
let target_pos = if target_id.0 == cmd::ID_SPECIAL_PLAYERCAM {
|
||||||
|
player_pos
|
||||||
|
} else if let Some(target_pos) = id2pos.0.get(&target_id.0) {
|
||||||
|
target_pos
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
rot.0 = look_at_quat(**pos, *target_pos, trans.up().as_dvec3());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_damage(
|
fn handle_damage(
|
||||||
|
@ -507,8 +596,7 @@ fn handle_damage(
|
||||||
if hp.current <= 0.0 {
|
if hp.current <= 0.0 {
|
||||||
ew_playerdies.send(game::PlayerDiesEvent(hp.damagetype));
|
ew_playerdies.send(game::PlayerDiesEvent(hp.damagetype));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
hp.current -= hp.damage;
|
hp.current -= hp.damage;
|
||||||
}
|
}
|
||||||
hp.damage = 0.0;
|
hp.damage = 0.0;
|
||||||
|
@ -535,12 +623,12 @@ fn handle_gforce(
|
||||||
|
|
||||||
if gforce.visual_effect > 0.0001 {
|
if gforce.visual_effect > 0.0001 {
|
||||||
gforce.visual_effect *= 0.984;
|
gforce.visual_effect *= 0.984;
|
||||||
}
|
} else if gforce.visual_effect > 0.0 {
|
||||||
else if gforce.visual_effect > 0.0 {
|
|
||||||
gforce.visual_effect = 0.0;
|
gforce.visual_effect = 0.0;
|
||||||
}
|
}
|
||||||
if gforce.gforce > gforce.visual_effect_threshold {
|
if gforce.gforce > gforce.visual_effect_threshold {
|
||||||
gforce.visual_effect += (gforce.gforce - gforce.visual_effect_threshold).powf(2.0) / 300000.0
|
gforce.visual_effect +=
|
||||||
|
(gforce.gforce - gforce.visual_effect_threshold).powf(2.0) / 300000.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
129
src/audio.rs
|
@ -10,45 +10,80 @@
|
||||||
//
|
//
|
||||||
// This module manages sound effects and music.
|
// This module manages sound effects and music.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::audio::{PlaybackMode, Volume};
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use bevy::audio::{PlaybackMode, Volume};
|
||||||
|
use bevy::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub struct AudioPlugin;
|
pub struct AudioPlugin;
|
||||||
impl Plugin for AudioPlugin {
|
impl Plugin for AudioPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup);
|
app.add_systems(Startup, setup);
|
||||||
app.add_systems(Update, respawn_sinks.run_if(on_event::<RespawnSinksEvent>()));
|
app.add_systems(
|
||||||
app.add_systems(Update, play_zoom_sfx);
|
Update,
|
||||||
app.add_systems(Update, pause_all.run_if(on_event::<PauseAllSfxEvent>()));
|
(
|
||||||
app.add_systems(PostUpdate, play_sfx);
|
play_zoom_sfx,
|
||||||
app.add_systems(PostUpdate, update_music);
|
respawn_sinks.run_if(on_event::<RespawnSinksEvent>()),
|
||||||
|
pause_all.run_if(on_event::<PauseAllSfxEvent>()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
(
|
||||||
|
play_sfx,
|
||||||
|
toggle_music.run_if(on_event::<ToggleMusicEvent>()),
|
||||||
|
),
|
||||||
|
);
|
||||||
app.add_event::<PlaySfxEvent>();
|
app.add_event::<PlaySfxEvent>();
|
||||||
app.add_event::<PauseAllSfxEvent>();
|
app.add_event::<PauseAllSfxEvent>();
|
||||||
app.add_event::<ToggleMusicEvent>();
|
app.add_event::<ToggleMusicEvent>();
|
||||||
app.add_event::<RespawnSinksEvent>();
|
app.add_event::<RespawnSinksEvent>();
|
||||||
app.insert_resource(ZoomTimer(
|
app.insert_resource(ZoomTimer(Timer::from_seconds(0.09, TimerMode::Repeating)));
|
||||||
Timer::from_seconds(0.09, TimerMode::Repeating)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)] pub struct ZoomTimer(Timer);
|
#[derive(Resource)]
|
||||||
|
pub struct ZoomTimer(Timer);
|
||||||
|
|
||||||
const PATHS: &[(SfxType, Sfx, &str)] = &[
|
const PATHS: &[(SfxType, Sfx, &str)] = &[
|
||||||
(SfxType::BGM, Sfx::BGM, "music/Aleksey Chistilin - Cinematic Cello.ogg"),
|
(
|
||||||
(SfxType::LoopSfx, Sfx::ElectricMotor, "sounds/electricmotor.ogg"),
|
SfxType::BGM,
|
||||||
|
Sfx::BGM,
|
||||||
|
"music/Aleksey Chistilin - Cinematic Cello.ogg",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SfxType::BGMNoAR,
|
||||||
|
Sfx::BGMActualJupiterRecording,
|
||||||
|
"music/JupiterRecording.ogg",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SfxType::LoopSfx,
|
||||||
|
Sfx::ElectricMotor,
|
||||||
|
"sounds/electricmotor.ogg",
|
||||||
|
),
|
||||||
(SfxType::LoopSfx, Sfx::Ion, "sounds/ion.ogg"),
|
(SfxType::LoopSfx, Sfx::Ion, "sounds/ion.ogg"),
|
||||||
(SfxType::LoopSfx, Sfx::Rocket, "sounds/rocket.ogg"),
|
(SfxType::LoopSfx, Sfx::Rocket, "sounds/rocket.ogg"),
|
||||||
(SfxType::LoopSfx, Sfx::Thruster, "sounds/thruster.ogg"),
|
(SfxType::LoopSfx, Sfx::Thruster, "sounds/thruster.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Achieve, "sounds/achieve.ogg"),
|
(SfxType::OneOff, Sfx::Achieve, "sounds/achieve.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Click, "sounds/click-button-140881-crop.ogg"),
|
(
|
||||||
|
SfxType::OneOff,
|
||||||
|
Sfx::Click,
|
||||||
|
"sounds/click-button-140881-crop.ogg",
|
||||||
|
),
|
||||||
(SfxType::OneOff, Sfx::Connect, "sounds/connect.ogg"),
|
(SfxType::OneOff, Sfx::Connect, "sounds/connect.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Crash, "sounds/crash.ogg"),
|
(SfxType::OneOff, Sfx::Crash, "sounds/crash.ogg"),
|
||||||
(SfxType::OneOff, Sfx::EnterVehicle, "sounds/bikestart.ogg"),
|
(SfxType::OneOff, Sfx::EnterVehicle, "sounds/bikestart.ogg"),
|
||||||
(SfxType::OneOff, Sfx::IncomingChatMessage, "sounds/connect.ogg"),
|
(
|
||||||
|
SfxType::OneOff,
|
||||||
|
Sfx::IncomingChatMessage,
|
||||||
|
"sounds/connect.ogg",
|
||||||
|
),
|
||||||
(SfxType::OneOff, Sfx::Ping, "sounds/connect.ogg"),
|
(SfxType::OneOff, Sfx::Ping, "sounds/connect.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Switch, "sounds/typosonic-typing-192811-crop.ogg"),
|
(
|
||||||
|
SfxType::OneOff,
|
||||||
|
Sfx::Switch,
|
||||||
|
"sounds/typosonic-typing-192811-crop.ogg",
|
||||||
|
),
|
||||||
(SfxType::OneOff, Sfx::WakeUp, "sounds/wakeup.ogg"),
|
(SfxType::OneOff, Sfx::WakeUp, "sounds/wakeup.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Woosh, "sounds/woosh.ogg"),
|
(SfxType::OneOff, Sfx::Woosh, "sounds/woosh.ogg"),
|
||||||
(SfxType::OneOff, Sfx::Zoom, "sounds/zoom.ogg"),
|
(SfxType::OneOff, Sfx::Zoom, "sounds/zoom.ogg"),
|
||||||
|
@ -58,6 +93,7 @@ const PATHS: &[(SfxType, Sfx, &str)] = &[
|
||||||
pub enum Sfx {
|
pub enum Sfx {
|
||||||
Achieve,
|
Achieve,
|
||||||
BGM,
|
BGM,
|
||||||
|
BGMActualJupiterRecording,
|
||||||
Click,
|
Click,
|
||||||
Connect,
|
Connect,
|
||||||
Crash,
|
Crash,
|
||||||
|
@ -92,15 +128,21 @@ pub fn str2sfx(sfx_label: &str) -> Sfx {
|
||||||
|
|
||||||
pub enum SfxType {
|
pub enum SfxType {
|
||||||
BGM,
|
BGM,
|
||||||
|
BGMNoAR,
|
||||||
LoopSfx,
|
LoopSfx,
|
||||||
OneOff,
|
OneOff,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)] pub struct PlaySfxEvent(pub Sfx);
|
#[derive(Event)]
|
||||||
#[derive(Event)] pub struct PauseAllSfxEvent;
|
pub struct PlaySfxEvent(pub Sfx);
|
||||||
#[derive(Event)] pub struct RespawnSinksEvent;
|
#[derive(Event)]
|
||||||
#[derive(Event)] pub struct ToggleMusicEvent();
|
pub struct PauseAllSfxEvent;
|
||||||
#[derive(Resource)] pub struct Sounds(HashMap<Sfx, Handle<AudioSource>>);
|
#[derive(Event)]
|
||||||
|
pub struct RespawnSinksEvent;
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct ToggleMusicEvent();
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct Sounds(HashMap<Sfx, Handle<AudioSource>>);
|
||||||
|
|
||||||
pub fn setup(
|
pub fn setup(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
@ -135,12 +177,25 @@ pub fn respawn_sinks(
|
||||||
source,
|
source,
|
||||||
settings: PlaybackSettings {
|
settings: PlaybackSettings {
|
||||||
mode: PlaybackMode::Loop,
|
mode: PlaybackMode::Loop,
|
||||||
paused: settings.mute_music,
|
paused: settings.mute_music || !settings.hud_active,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
SfxType::BGMNoAR => {
|
||||||
|
commands.spawn((
|
||||||
|
*sfx,
|
||||||
|
AudioBundle {
|
||||||
|
source,
|
||||||
|
settings: PlaybackSettings {
|
||||||
|
mode: PlaybackMode::Loop,
|
||||||
|
paused: settings.mute_music || settings.hud_active,
|
||||||
|
..default()
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
SfxType::LoopSfx => {
|
SfxType::LoopSfx => {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
*sfx,
|
*sfx,
|
||||||
|
@ -154,8 +209,8 @@ pub fn respawn_sinks(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
}
|
||||||
SfxType::OneOff => ()
|
SfxType::OneOff => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,25 +234,21 @@ pub fn play_sfx(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_music(
|
pub fn toggle_music(q_audiosinks: Query<(&AudioSink, &Sfx)>, settings: Res<var::Settings>) {
|
||||||
mut events: EventReader<ToggleMusicEvent>,
|
|
||||||
q_audiosinks: Query<(&AudioSink, &Sfx)>,
|
|
||||||
settings: Res<var::Settings>,
|
|
||||||
) {
|
|
||||||
if !events.is_empty() {
|
|
||||||
events.clear();
|
|
||||||
for (bgm_sink, sfx) in &q_audiosinks {
|
for (bgm_sink, sfx) in &q_audiosinks {
|
||||||
if *sfx != Sfx::BGM {
|
let play = match *sfx {
|
||||||
|
Sfx::BGM => settings.hud_active,
|
||||||
|
Sfx::BGMActualJupiterRecording => !settings.hud_active,
|
||||||
|
_ => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if settings.mute_music {
|
};
|
||||||
|
if settings.mute_music || !play {
|
||||||
bgm_sink.pause();
|
bgm_sink.pause();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
bgm_sink.play();
|
bgm_sink.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn play_zoom_sfx(
|
pub fn play_zoom_sfx(
|
||||||
|
@ -211,7 +262,9 @@ pub fn play_zoom_sfx(
|
||||||
if !timer.0.tick(time.delta()).just_finished() {
|
if !timer.0.tick(time.delta()).just_finished() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if settings.map_active && (*last_zoom_level - mapcam.target_zoom_level).abs() > mapcam.target_zoom_level * 0.2 {
|
if settings.map_active
|
||||||
|
&& (*last_zoom_level - mapcam.target_zoom_level).abs() > mapcam.target_zoom_level * 0.2
|
||||||
|
{
|
||||||
if *last_zoom_level != 0.0 && mapcam.target_zoom_level != camera::INITIAL_ZOOM_LEVEL {
|
if *last_zoom_level != 0.0 && mapcam.target_zoom_level != camera::INITIAL_ZOOM_LEVEL {
|
||||||
ew_sfx.send(PlaySfxEvent(Sfx::Zoom));
|
ew_sfx.send(PlaySfxEvent(Sfx::Zoom));
|
||||||
}
|
}
|
||||||
|
@ -219,9 +272,7 @@ pub fn play_zoom_sfx(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pause_all(
|
pub fn pause_all(q_audiosinks: Query<&AudioSink, With<Sfx>>) {
|
||||||
q_audiosinks: Query<&AudioSink, With<Sfx>>,
|
|
||||||
) {
|
|
||||||
for sink in &q_audiosinks {
|
for sink in &q_audiosinks {
|
||||||
sink.pause();
|
sink.pause();
|
||||||
}
|
}
|
||||||
|
|
418
src/audio/JupiterRecording.kdenlive
Normal file
|
@ -0,0 +1,418 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<mlt LC_NUMERIC="C" producer="main_bin" root="/home/hut/repos/outfly/src/audio" version="7.24.0">
|
||||||
|
<profile colorspace="709" description="HD 1080p 25 fps" display_aspect_den="9" display_aspect_num="16" frame_rate_den="1" frame_rate_num="25" height="1080" progressive="1" sample_aspect_den="1" sample_aspect_num="1" width="1920"/>
|
||||||
|
<chain id="chain2" out="00:09:59.600">
|
||||||
|
<property name="length">14991</property>
|
||||||
|
<property name="eof">pause</property>
|
||||||
|
<property name="resource">/home/hut/repos/outfly/additional/music/VOYAGER 1 and 2 - 1990 - Jupiter - [NASA Voyager Space Sounds][Electronic].mp3</property>
|
||||||
|
<property name="mlt_service">avformat</property>
|
||||||
|
<property name="meta.media.nb_streams">1</property>
|
||||||
|
<property name="meta.media.0.stream.type">audio</property>
|
||||||
|
<property name="meta.media.0.codec.sample_fmt">fltp</property>
|
||||||
|
<property name="meta.media.0.codec.sample_rate">44100</property>
|
||||||
|
<property name="meta.media.0.codec.channels">2</property>
|
||||||
|
<property name="meta.media.0.codec.layout">stereo</property>
|
||||||
|
<property name="meta.media.0.codec.name">mp3float</property>
|
||||||
|
<property name="meta.media.0.codec.long_name">MP3 (MPEG audio layer 3)</property>
|
||||||
|
<property name="meta.media.0.codec.bit_rate">160000</property>
|
||||||
|
<property name="meta.attr.encoder.markup">Lavf53.21.0</property>
|
||||||
|
<property name="meta.attr.track.markup">1</property>
|
||||||
|
<property name="meta.attr.date.markup">1990</property>
|
||||||
|
<property name="meta.attr.title.markup">Jupiter</property>
|
||||||
|
<property name="meta.attr.artist.markup">VOYAGER 1 and 2</property>
|
||||||
|
<property name="meta.attr.album.markup">NASA Voyager Space Sounds</property>
|
||||||
|
<property name="meta.attr.comment.markup">The sounds used on these recordings were taken from NASA Voyager I & II. ... Jupiter, the fifth planet from the sun is the largest and most massive planet in the Solar System. In mass alone, it is three hundred times the mass of Earth. Jupiter is mostly composed of hydrogen and helium. The entire planet is made of gas, with no solid surface under the atmosphere. The pressures and temperatures deep in Jupiter are so high that gases form a gradual transition into liquids which are gradually compressed into a metallic "plasma" in which the molecules have been stripped of their outer electrons, The winds of Jupiter are a thousand meters per second relative to the rotating interior. Jupiter's magnetic field is four thousand times stronger than Earth's, and is tipped by 11° degrees of axis spin. This causes the magnetic field to wobble, which has a profound effect on trapped electronically charged particles. This plasma of charged particles is accelerated beyond the magnetosphere of Jupiter to speeds of tens of thousands of kilometres per second. It is these magnetic particle vibrations which generate some of the sounds you hear on this recording.</property>
|
||||||
|
<property name="meta.attr.genre.markup">Electronic</property>
|
||||||
|
<property name="seekable">1</property>
|
||||||
|
<property name="audio_index">0</property>
|
||||||
|
<property name="video_index">-1</property>
|
||||||
|
<property name="astream">0</property>
|
||||||
|
<property name="kdenlive:folderid">-1</property>
|
||||||
|
<property name="kdenlive:id">4</property>
|
||||||
|
<property name="kdenlive:clip_type">1</property>
|
||||||
|
<property name="kdenlive:file_size">11995402</property>
|
||||||
|
<property name="kdenlive:file_hash">abfa2a24311614b54ac4cd440263dd6a</property>
|
||||||
|
<property name="kdenlive:audio_max0">171</property>
|
||||||
|
<property name="kdenlive:monitorPosition">0</property>
|
||||||
|
</chain>
|
||||||
|
<producer id="producer0" in="00:00:00.000" out="00:22:00.000">
|
||||||
|
<property name="length">2147483647</property>
|
||||||
|
<property name="eof">continue</property>
|
||||||
|
<property name="resource">black</property>
|
||||||
|
<property name="aspect_ratio">1</property>
|
||||||
|
<property name="mlt_service">color</property>
|
||||||
|
<property name="kdenlive:playlistid">black_track</property>
|
||||||
|
<property name="mlt_image_format">rgba</property>
|
||||||
|
<property name="set.test_audio">0</property>
|
||||||
|
</producer>
|
||||||
|
<chain id="chain0" out="00:09:59.600">
|
||||||
|
<property name="length">14991</property>
|
||||||
|
<property name="eof">pause</property>
|
||||||
|
<property name="resource">/home/hut/repos/outfly/additional/music/VOYAGER 1 and 2 - 1990 - Jupiter - [NASA Voyager Space Sounds][Electronic].mp3</property>
|
||||||
|
<property name="mlt_service">avformat-novalidate</property>
|
||||||
|
<property name="seekable">1</property>
|
||||||
|
<property name="audio_index">0</property>
|
||||||
|
<property name="video_index">-1</property>
|
||||||
|
<property name="astream">0</property>
|
||||||
|
<property name="kdenlive:folderid">-1</property>
|
||||||
|
<property name="kdenlive:id">4</property>
|
||||||
|
<property name="kdenlive:clip_type">1</property>
|
||||||
|
<property name="kdenlive:file_size">11995402</property>
|
||||||
|
<property name="kdenlive:file_hash">abfa2a24311614b54ac4cd440263dd6a</property>
|
||||||
|
<property name="kdenlive:audio_max0">171</property>
|
||||||
|
<property name="meta.media.nb_streams">1</property>
|
||||||
|
<property name="meta.media.0.stream.type">audio</property>
|
||||||
|
<property name="meta.media.0.codec.sample_fmt">fltp</property>
|
||||||
|
<property name="meta.media.0.codec.sample_rate">44100</property>
|
||||||
|
<property name="meta.media.0.codec.channels">2</property>
|
||||||
|
<property name="meta.media.0.codec.layout">stereo</property>
|
||||||
|
<property name="meta.media.0.codec.name">mp3float</property>
|
||||||
|
<property name="meta.media.0.codec.long_name">MP3 (MPEG audio layer 3)</property>
|
||||||
|
<property name="meta.media.0.codec.bit_rate">160000</property>
|
||||||
|
<property name="meta.attr.encoder.markup">Lavf53.21.0</property>
|
||||||
|
<property name="meta.attr.track.markup">1</property>
|
||||||
|
<property name="meta.attr.date.markup">1990</property>
|
||||||
|
<property name="meta.attr.title.markup">Jupiter</property>
|
||||||
|
<property name="meta.attr.artist.markup">VOYAGER 1 and 2</property>
|
||||||
|
<property name="meta.attr.album.markup">NASA Voyager Space Sounds</property>
|
||||||
|
<property name="meta.attr.comment.markup">The sounds used on these recordings were taken from NASA Voyager I & II. ... Jupiter, the fifth planet from the sun is the largest and most massive planet in the Solar System. In mass alone, it is three hundred times the mass of Earth. Jupiter is mostly composed of hydrogen and helium. The entire planet is made of gas, with no solid surface under the atmosphere. The pressures and temperatures deep in Jupiter are so high that gases form a gradual transition into liquids which are gradually compressed into a metallic "plasma" in which the molecules have been stripped of their outer electrons, The winds of Jupiter are a thousand meters per second relative to the rotating interior. Jupiter's magnetic field is four thousand times stronger than Earth's, and is tipped by 11° degrees of axis spin. This causes the magnetic field to wobble, which has a profound effect on trapped electronically charged particles. This plasma of charged particles is accelerated beyond the magnetosphere of Jupiter to speeds of tens of thousands of kilometres per second. It is these magnetic particle vibrations which generate some of the sounds you hear on this recording.</property>
|
||||||
|
<property name="meta.attr.genre.markup">Electronic</property>
|
||||||
|
<property name="xml">was here</property>
|
||||||
|
<property name="mute_on_pause">0</property>
|
||||||
|
<property name="set.test_audio">0</property>
|
||||||
|
<property name="set.test_image">1</property>
|
||||||
|
</chain>
|
||||||
|
<playlist id="playlist0">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
<blank length="00:01:50.000"/>
|
||||||
|
<entry in="00:01:50.000" out="00:01:59.960" producer="chain0">
|
||||||
|
<property name="kdenlive:id">4</property>
|
||||||
|
<filter id="filter0" in="00:01:50.000" out="00:02:00.000">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="kdenlive_id">fadein</property>
|
||||||
|
<property name="gain">0</property>
|
||||||
|
<property name="end">1</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter1">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="kdenlive_id">gain</property>
|
||||||
|
<property name="gain">1</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
</filter>
|
||||||
|
</entry>
|
||||||
|
</playlist>
|
||||||
|
<playlist id="playlist1">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
</playlist>
|
||||||
|
<tractor id="tractor0" in="00:00:00.000" out="00:01:59.960">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
<property name="kdenlive:trackheight">141</property>
|
||||||
|
<property name="kdenlive:timeline_active">1</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
<property name="kdenlive:thumbs_format"/>
|
||||||
|
<property name="kdenlive:audio_rec"/>
|
||||||
|
<track hide="video" producer="playlist0"/>
|
||||||
|
<track hide="video" producer="playlist1"/>
|
||||||
|
<filter id="filter2">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter3">
|
||||||
|
<property name="channel">-1</property>
|
||||||
|
<property name="mlt_service">panner</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="start">0.5</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter4">
|
||||||
|
<property name="iec_scale">0</property>
|
||||||
|
<property name="mlt_service">audiolevel</property>
|
||||||
|
<property name="dbpeak">1</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
</tractor>
|
||||||
|
<chain id="chain1" out="00:09:59.600">
|
||||||
|
<property name="length">14991</property>
|
||||||
|
<property name="eof">pause</property>
|
||||||
|
<property name="resource">/home/hut/repos/outfly/additional/music/VOYAGER 1 and 2 - 1990 - Jupiter - [NASA Voyager Space Sounds][Electronic].mp3</property>
|
||||||
|
<property name="mlt_service">avformat-novalidate</property>
|
||||||
|
<property name="seekable">1</property>
|
||||||
|
<property name="audio_index">0</property>
|
||||||
|
<property name="video_index">-1</property>
|
||||||
|
<property name="astream">0</property>
|
||||||
|
<property name="kdenlive:folderid">-1</property>
|
||||||
|
<property name="kdenlive:id">4</property>
|
||||||
|
<property name="kdenlive:clip_type">1</property>
|
||||||
|
<property name="kdenlive:file_size">11995402</property>
|
||||||
|
<property name="kdenlive:file_hash">abfa2a24311614b54ac4cd440263dd6a</property>
|
||||||
|
<property name="kdenlive:audio_max0">171</property>
|
||||||
|
<property name="meta.media.nb_streams">1</property>
|
||||||
|
<property name="meta.media.0.stream.type">audio</property>
|
||||||
|
<property name="meta.media.0.codec.sample_fmt">fltp</property>
|
||||||
|
<property name="meta.media.0.codec.sample_rate">44100</property>
|
||||||
|
<property name="meta.media.0.codec.channels">2</property>
|
||||||
|
<property name="meta.media.0.codec.layout">stereo</property>
|
||||||
|
<property name="meta.media.0.codec.name">mp3float</property>
|
||||||
|
<property name="meta.media.0.codec.long_name">MP3 (MPEG audio layer 3)</property>
|
||||||
|
<property name="meta.media.0.codec.bit_rate">160000</property>
|
||||||
|
<property name="meta.attr.encoder.markup">Lavf53.21.0</property>
|
||||||
|
<property name="meta.attr.track.markup">1</property>
|
||||||
|
<property name="meta.attr.date.markup">1990</property>
|
||||||
|
<property name="meta.attr.title.markup">Jupiter</property>
|
||||||
|
<property name="meta.attr.artist.markup">VOYAGER 1 and 2</property>
|
||||||
|
<property name="meta.attr.album.markup">NASA Voyager Space Sounds</property>
|
||||||
|
<property name="meta.attr.comment.markup">The sounds used on these recordings were taken from NASA Voyager I & II. ... Jupiter, the fifth planet from the sun is the largest and most massive planet in the Solar System. In mass alone, it is three hundred times the mass of Earth. Jupiter is mostly composed of hydrogen and helium. The entire planet is made of gas, with no solid surface under the atmosphere. The pressures and temperatures deep in Jupiter are so high that gases form a gradual transition into liquids which are gradually compressed into a metallic "plasma" in which the molecules have been stripped of their outer electrons, The winds of Jupiter are a thousand meters per second relative to the rotating interior. Jupiter's magnetic field is four thousand times stronger than Earth's, and is tipped by 11° degrees of axis spin. This causes the magnetic field to wobble, which has a profound effect on trapped electronically charged particles. This plasma of charged particles is accelerated beyond the magnetosphere of Jupiter to speeds of tens of thousands of kilometres per second. It is these magnetic particle vibrations which generate some of the sounds you hear on this recording.</property>
|
||||||
|
<property name="meta.attr.genre.markup">Electronic</property>
|
||||||
|
<property name="xml">was here</property>
|
||||||
|
<property name="mute_on_pause">0</property>
|
||||||
|
<property name="set.test_audio">0</property>
|
||||||
|
<property name="set.test_image">1</property>
|
||||||
|
</chain>
|
||||||
|
<playlist id="playlist2">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
<entry in="00:01:47.280" out="00:03:47.240" producer="chain1">
|
||||||
|
<property name="kdenlive:id">4</property>
|
||||||
|
<filter id="filter5" in="00:03:37.240" out="00:03:47.280">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="kdenlive_id">fadeout</property>
|
||||||
|
<property name="gain">1</property>
|
||||||
|
<property name="end">0</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter6">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="kdenlive_id">gain</property>
|
||||||
|
<property name="gain">1</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
<property name="disable">0</property>
|
||||||
|
</filter>
|
||||||
|
</entry>
|
||||||
|
</playlist>
|
||||||
|
<playlist id="playlist3">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
</playlist>
|
||||||
|
<tractor id="tractor1" in="00:00:00.000" out="00:01:59.960">
|
||||||
|
<property name="kdenlive:audio_track">1</property>
|
||||||
|
<property name="kdenlive:trackheight">131</property>
|
||||||
|
<property name="kdenlive:timeline_active">1</property>
|
||||||
|
<property name="kdenlive:collapsed">0</property>
|
||||||
|
<property name="kdenlive:thumbs_format"/>
|
||||||
|
<property name="kdenlive:audio_rec"/>
|
||||||
|
<track hide="video" producer="playlist2"/>
|
||||||
|
<track hide="video" producer="playlist3"/>
|
||||||
|
<filter id="filter7">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter8">
|
||||||
|
<property name="channel">-1</property>
|
||||||
|
<property name="mlt_service">panner</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="start">0.5</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter9">
|
||||||
|
<property name="iec_scale">0</property>
|
||||||
|
<property name="mlt_service">audiolevel</property>
|
||||||
|
<property name="dbpeak">1</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
</tractor>
|
||||||
|
<tractor id="tractor2" in="00:00:00.000" out="00:01:59.960">
|
||||||
|
<property name="kdenlive:sequenceproperties.hasAudio">1</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.hasVideo">0</property>
|
||||||
|
<property name="kdenlive:clip_type">1</property>
|
||||||
|
<property name="kdenlive:duration">00:02:00.000</property>
|
||||||
|
<property name="kdenlive:maxduration">3000</property>
|
||||||
|
<property name="kdenlive:clipname">Sequence 1</property>
|
||||||
|
<property name="kdenlive:description"/>
|
||||||
|
<property name="kdenlive:uuid">{700cf7a6-dd54-4622-9e00-6df4c0862a67}</property>
|
||||||
|
<property name="kdenlive:producer_type">17</property>
|
||||||
|
<property name="kdenlive:id">3</property>
|
||||||
|
<property name="kdenlive:file_hash">7e34583bd15531251a720488f71ecb03</property>
|
||||||
|
<property name="kdenlive:folderid">2</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.activeTrack">1</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.audioTarget">1</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.disablepreview">0</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.documentuuid">{700cf7a6-dd54-4622-9e00-6df4c0862a67}</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.position">2999</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.scrollPos">0</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.tracks">4</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.tracksCount">2</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.verticalzoom">1</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.videoTarget">2</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.zonein">0</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.zoneout">75</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.zoom">10</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.groups">[
|
||||||
|
]
|
||||||
|
</property>
|
||||||
|
<property name="kdenlive:sequenceproperties.guides">[
|
||||||
|
]
|
||||||
|
</property>
|
||||||
|
<track producer="producer0"/>
|
||||||
|
<track producer="tractor0"/>
|
||||||
|
<track producer="tractor1"/>
|
||||||
|
<transition id="transition0">
|
||||||
|
<property name="a_track">0</property>
|
||||||
|
<property name="b_track">1</property>
|
||||||
|
<property name="mlt_service">mix</property>
|
||||||
|
<property name="kdenlive_id">mix</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="always_active">1</property>
|
||||||
|
<property name="accepts_blanks">1</property>
|
||||||
|
<property name="sum">1</property>
|
||||||
|
</transition>
|
||||||
|
<transition id="transition1">
|
||||||
|
<property name="a_track">0</property>
|
||||||
|
<property name="b_track">2</property>
|
||||||
|
<property name="mlt_service">mix</property>
|
||||||
|
<property name="kdenlive_id">mix</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="always_active">1</property>
|
||||||
|
<property name="accepts_blanks">1</property>
|
||||||
|
<property name="sum">1</property>
|
||||||
|
</transition>
|
||||||
|
<filter id="filter10">
|
||||||
|
<property name="window">75</property>
|
||||||
|
<property name="max_gain">20dB</property>
|
||||||
|
<property name="mlt_service">volume</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
<filter id="filter11">
|
||||||
|
<property name="channel">-1</property>
|
||||||
|
<property name="mlt_service">panner</property>
|
||||||
|
<property name="internal_added">237</property>
|
||||||
|
<property name="start">0.5</property>
|
||||||
|
<property name="disable">1</property>
|
||||||
|
</filter>
|
||||||
|
</tractor>
|
||||||
|
<playlist id="main_bin">
|
||||||
|
<property name="kdenlive:folder.-1.2">Sequences</property>
|
||||||
|
<property name="kdenlive:sequenceFolder">2</property>
|
||||||
|
<property name="kdenlive:docproperties.activetimeline">{700cf7a6-dd54-4622-9e00-6df4c0862a67}</property>
|
||||||
|
<property name="kdenlive:docproperties.audioChannels">2</property>
|
||||||
|
<property name="kdenlive:docproperties.binsort">0</property>
|
||||||
|
<property name="kdenlive:docproperties.browserurl">/home/hut/repos/pages/data/videos/cellmade/</property>
|
||||||
|
<property name="kdenlive:docproperties.compositing">1</property>
|
||||||
|
<property name="kdenlive:docproperties.documentid">1716424483385</property>
|
||||||
|
<property name="kdenlive:docproperties.enableTimelineZone">0</property>
|
||||||
|
<property name="kdenlive:docproperties.enableexternalproxy">0</property>
|
||||||
|
<property name="kdenlive:docproperties.enableproxy">0</property>
|
||||||
|
<property name="kdenlive:docproperties.externalproxyparams"/>
|
||||||
|
<property name="kdenlive:docproperties.generateimageproxy">0</property>
|
||||||
|
<property name="kdenlive:docproperties.generateproxy">0</property>
|
||||||
|
<property name="kdenlive:docproperties.guidesCategories">[
|
||||||
|
{
|
||||||
|
"color": "#9b59b6",
|
||||||
|
"comment": "Category 1",
|
||||||
|
"index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#3daee9",
|
||||||
|
"comment": "Category 2",
|
||||||
|
"index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#1abc9c",
|
||||||
|
"comment": "Category 3",
|
||||||
|
"index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#1cdc9a",
|
||||||
|
"comment": "Category 4",
|
||||||
|
"index": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#c9ce3b",
|
||||||
|
"comment": "Category 5",
|
||||||
|
"index": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#fdbc4b",
|
||||||
|
"comment": "Category 6",
|
||||||
|
"index": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#f39c1f",
|
||||||
|
"comment": "Category 7",
|
||||||
|
"index": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#f47750",
|
||||||
|
"comment": "Category 8",
|
||||||
|
"index": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "#da4453",
|
||||||
|
"comment": "Category 9",
|
||||||
|
"index": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
</property>
|
||||||
|
<property name="kdenlive:docproperties.kdenliveversion">24.02.2</property>
|
||||||
|
<property name="kdenlive:docproperties.opensequences">{700cf7a6-dd54-4622-9e00-6df4c0862a67}</property>
|
||||||
|
<property name="kdenlive:docproperties.previewextension"/>
|
||||||
|
<property name="kdenlive:docproperties.previewparameters"/>
|
||||||
|
<property name="kdenlive:docproperties.profile">atsc_1080p_25</property>
|
||||||
|
<property name="kdenlive:docproperties.proxyextension"/>
|
||||||
|
<property name="kdenlive:docproperties.proxyimageminsize">2000</property>
|
||||||
|
<property name="kdenlive:docproperties.proxyimagesize">800</property>
|
||||||
|
<property name="kdenlive:docproperties.proxyminsize">1000</property>
|
||||||
|
<property name="kdenlive:docproperties.proxyparams"/>
|
||||||
|
<property name="kdenlive:docproperties.proxyresize">640</property>
|
||||||
|
<property name="kdenlive:docproperties.rendercategory">Audio only</property>
|
||||||
|
<property name="kdenlive:docproperties.rendercustomquality">30</property>
|
||||||
|
<property name="kdenlive:docproperties.renderendguide">-1</property>
|
||||||
|
<property name="kdenlive:docproperties.renderexportaudio">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderfullcolorrange">0</property>
|
||||||
|
<property name="kdenlive:docproperties.rendermode">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderplay">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderpreview">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderprofile">OGG</property>
|
||||||
|
<property name="kdenlive:docproperties.renderrescale">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderrescaleheight">540</property>
|
||||||
|
<property name="kdenlive:docproperties.renderrescalewidth">960</property>
|
||||||
|
<property name="kdenlive:docproperties.renderspeed">1</property>
|
||||||
|
<property name="kdenlive:docproperties.renderstartguide">-1</property>
|
||||||
|
<property name="kdenlive:docproperties.rendertcoverlay">0</property>
|
||||||
|
<property name="kdenlive:docproperties.rendertctype">-1</property>
|
||||||
|
<property name="kdenlive:docproperties.rendertwopass">0</property>
|
||||||
|
<property name="kdenlive:docproperties.renderurl">/home/hut/repos/outfly/assets/music/JupiterRecording.ogg</property>
|
||||||
|
<property name="kdenlive:docproperties.seekOffset">30000</property>
|
||||||
|
<property name="kdenlive:docproperties.uuid">{700cf7a6-dd54-4622-9e00-6df4c0862a67}</property>
|
||||||
|
<property name="kdenlive:docproperties.version">1.1</property>
|
||||||
|
<property name="kdenlive:expandedFolders"/>
|
||||||
|
<property name="kdenlive:binZoom">4</property>
|
||||||
|
<property name="kdenlive:documentnotes"/>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.Album.markup">NASA Voyager Space Sounds</property>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.Artist.markup">VOYAGER 1 and 2</property>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.Genre.markup">Electronic</property>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.copyright.markup">Public Domain</property>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.title.markup">Jupiter Recording</property>
|
||||||
|
<property name="kdenlive:docmetadata.meta.attr.year.markup">1990</property>
|
||||||
|
<property name="xml_retain">1</property>
|
||||||
|
<entry in="00:00:00.000" out="00:09:59.600" producer="chain2"/>
|
||||||
|
<entry in="00:00:00.000" out="00:03:29.960" producer="tractor2"/>
|
||||||
|
</playlist>
|
||||||
|
<tractor id="tractor3" in="00:00:00.000" out="00:01:59.960">
|
||||||
|
<property name="kdenlive:projectTractor">1</property>
|
||||||
|
<track in="00:00:00.000" out="00:01:59.960" producer="tractor2"/>
|
||||||
|
</tractor>
|
||||||
|
</mlt>
|
BIN
src/blender/wings.blend
Normal file
278
src/camera.rs
|
@ -12,16 +12,16 @@
|
||||||
// movement-related keyboard input, and provides some camera-
|
// movement-related keyboard input, and provides some camera-
|
||||||
// related computation functions.
|
// related computation functions.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::input::mouse::{MouseMotion, MouseWheel};
|
|
||||||
use bevy::window::PrimaryWindow;
|
|
||||||
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
|
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
|
||||||
use bevy::core_pipeline::tonemapping::Tonemapping;
|
use bevy::core_pipeline::tonemapping::Tonemapping;
|
||||||
|
use bevy::input::mouse::{MouseMotion, MouseWheel};
|
||||||
use bevy::pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap};
|
use bevy::pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap};
|
||||||
|
use bevy::prelude::*;
|
||||||
use bevy::transform::TransformSystem;
|
use bevy::transform::TransformSystem;
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy::window::PrimaryWindow;
|
||||||
use bevy_xpbd_3d::plugins::sync;
|
use bevy_xpbd_3d::plugins::sync;
|
||||||
use crate::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub const INITIAL_ZOOM_LEVEL: f64 = 10.0;
|
pub const INITIAL_ZOOM_LEVEL: f64 = 10.0;
|
||||||
|
@ -34,13 +34,19 @@ impl Plugin for CameraPlugin {
|
||||||
app.add_systems(Update, handle_input.run_if(in_control));
|
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, update_map_only_object_visibility.run_if(alive));
|
||||||
app.add_systems(Update, manage_player_actor.after(handle_input));
|
app.add_systems(Update, manage_player_actor.after(handle_input));
|
||||||
app.add_systems(PostUpdate, sync_camera_to_player
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
sync_camera_to_player
|
||||||
.after(PhysicsSet::Sync)
|
.after(PhysicsSet::Sync)
|
||||||
.after(apply_input_to_player)
|
.after(apply_input_to_player)
|
||||||
.before(TransformSystem::TransformPropagate));
|
.before(TransformSystem::TransformPropagate),
|
||||||
app.add_systems(PostUpdate, update_mapcam_center
|
);
|
||||||
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
update_mapcam_center
|
||||||
.before(sync::position_to_transform)
|
.before(sync::position_to_transform)
|
||||||
.in_set(sync::SyncSet::PositionToTransform));
|
.in_set(sync::SyncSet::PositionToTransform),
|
||||||
|
);
|
||||||
app.add_systems(Update, update_map_camera.run_if(in_control));
|
app.add_systems(Update, update_map_camera.run_if(in_control));
|
||||||
app.add_systems(Update, update_fov.run_if(alive));
|
app.add_systems(Update, update_fov.run_if(alive));
|
||||||
app.add_systems(PreUpdate, apply_input_to_player);
|
app.add_systems(PreUpdate, apply_input_to_player);
|
||||||
|
@ -53,9 +59,12 @@ impl Plugin for CameraPlugin {
|
||||||
transform_to_position: false,
|
transform_to_position: false,
|
||||||
});
|
});
|
||||||
// 2. Add own position->transform sync function
|
// 2. Add own position->transform sync function
|
||||||
app.add_systems(PostUpdate, position_to_transform
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
position_to_transform
|
||||||
.after(sync::position_to_transform)
|
.after(sync::position_to_transform)
|
||||||
.in_set(sync::SyncSet::PositionToTransform));
|
.in_set(sync::SyncSet::PositionToTransform),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +102,7 @@ impl Default for MapCam {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_camera(
|
pub fn setup_camera(mut commands: Commands, settings: Res<var::Settings>) {
|
||||||
mut commands: Commands,
|
|
||||||
settings: Res<var::Settings>,
|
|
||||||
) {
|
|
||||||
// Add player
|
// Add player
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Camera3dBundle {
|
Camera3dBundle {
|
||||||
|
@ -122,13 +128,14 @@ pub fn setup_camera(
|
||||||
shadows_enabled: settings.shadows_sun,
|
shadows_enabled: settings.shadows_sun,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
transform: Transform::from_rotation(Quat::from_rotation_y(PI32/2.0)),
|
transform: Transform::from_rotation(Quat::from_rotation_y(PI32 / 2.0)),
|
||||||
cascade_shadow_config: CascadeShadowConfigBuilder {
|
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||||
num_cascades: 4,
|
num_cascades: 4,
|
||||||
minimum_distance: 0.1,
|
minimum_distance: 0.1,
|
||||||
maximum_distance: 5000.0,
|
maximum_distance: 5000.0,
|
||||||
..default()
|
..default()
|
||||||
}.into(),
|
}
|
||||||
|
.into(),
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,10 +160,10 @@ pub fn sync_camera_to_player(
|
||||||
|
|
||||||
// Translation
|
// Translation
|
||||||
if settings.third_person {
|
if settings.third_person {
|
||||||
camera_transform.translation = player_transform.translation + rotation * (actor.camdistance * Vec3::new(0.0, 0.2, 1.0));
|
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.rotation = rotation * Quat::from_euler(EulerRot::XYZ, -0.02, 0.0, 0.0);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
camera_transform.translation = player_transform.translation;
|
camera_transform.translation = player_transform.translation;
|
||||||
camera_transform.rotation = rotation;
|
camera_transform.rotation = rotation;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +174,14 @@ pub fn update_map_camera(
|
||||||
mut mapcam: ResMut<MapCam>,
|
mut mapcam: ResMut<MapCam>,
|
||||||
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
||||||
q_playercam: Query<(Entity, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
|
q_playercam: Query<(Entity, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
|
||||||
q_target: Query<(Entity, &Transform), (With<hud::IsTargeted>, Without<Camera>, Without<actor::PlayerCamera>)>,
|
q_target: Query<
|
||||||
|
(Entity, &Transform),
|
||||||
|
(
|
||||||
|
With<hud::IsTargeted>,
|
||||||
|
Without<Camera>,
|
||||||
|
Without<actor::PlayerCamera>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
q_target_changed: Query<(), Changed<hud::IsTargeted>>,
|
q_target_changed: Query<(), Changed<hud::IsTargeted>>,
|
||||||
mut mouse_events: EventReader<MouseMotion>,
|
mut mouse_events: EventReader<MouseMotion>,
|
||||||
mut er_mousewheel: EventReader<MouseWheel>,
|
mut er_mousewheel: EventReader<MouseWheel>,
|
||||||
|
@ -195,7 +209,9 @@ pub fn update_map_camera(
|
||||||
// at the extreme values and the orientation will flicker back/forth.
|
// at the extreme values and the orientation will flicker back/forth.
|
||||||
let min_zoom: f64 = target_trans.scale.x as f64 * 2.0;
|
let min_zoom: f64 = target_trans.scale.x as f64 * 2.0;
|
||||||
let max_zoom: f64 = 17e18; // at this point, camera starts glitching
|
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(-PI / 2.0 + EPSILON, PI / 2.0 - EPSILON);
|
||||||
mapcam.yaw += mouse_delta.x as f64 / 180.0 * settings.mouse_sensitivity as f64;
|
mapcam.yaw += mouse_delta.x as f64 / 180.0 * settings.mouse_sensitivity as f64;
|
||||||
|
|
||||||
// Reset movement offset if target changes
|
// Reset movement offset if target changes
|
||||||
|
@ -226,7 +242,11 @@ pub fn update_map_camera(
|
||||||
|
|
||||||
// Update zoom level
|
// Update zoom level
|
||||||
if !mapcam.initialized {
|
if !mapcam.initialized {
|
||||||
let factor: f64 = if target_trans == player_trans { 7.0 } else { 1.0 };
|
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.target_zoom_level *= target_trans.scale.x as f64 * factor;
|
||||||
mapcam.zoom_level *= target_trans.scale.x as f64 * factor;
|
mapcam.zoom_level *= target_trans.scale.x as f64 * factor;
|
||||||
mapcam.initialized = true;
|
mapcam.initialized = true;
|
||||||
|
@ -241,12 +261,16 @@ pub fn update_map_camera(
|
||||||
for wheel_event in er_mousewheel.read() {
|
for wheel_event in er_mousewheel.read() {
|
||||||
change_zoom -= wheel_event.y as f64 * 3.0;
|
change_zoom -= wheel_event.y as f64 * 3.0;
|
||||||
}
|
}
|
||||||
mapcam.target_zoom_level = (mapcam.target_zoom_level * 1.1f64.powf(change_zoom)).clamp(min_zoom, max_zoom);
|
mapcam.target_zoom_level =
|
||||||
|
(mapcam.target_zoom_level * 1.1f64.powf(change_zoom)).clamp(min_zoom, max_zoom);
|
||||||
let zoom_speed = 0.05; // should be between 0.0001 (slow) and 1.0 (instant)
|
let zoom_speed = 0.05; // should be between 0.0001 (slow) and 1.0 (instant)
|
||||||
mapcam.zoom_level = (zoom_speed * mapcam.target_zoom_level + (1.0 - zoom_speed) * mapcam.zoom_level).clamp(min_zoom, max_zoom);
|
mapcam.zoom_level = (zoom_speed * mapcam.target_zoom_level
|
||||||
|
+ (1.0 - zoom_speed) * mapcam.zoom_level)
|
||||||
|
.clamp(min_zoom, max_zoom);
|
||||||
|
|
||||||
// Update point of view
|
// Update point of view
|
||||||
let pov_rotation = DQuat::from_euler(EulerRot::XYZ, 0.0, mapcam.yaw as f64, mapcam.pitch as f64);
|
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 point_of_view = pov_rotation * (mapcam.zoom_level as f64 * DVec3::new(1.0, 0.0, 0.0));
|
||||||
|
|
||||||
// Update movement offset
|
// Update movement offset
|
||||||
|
@ -285,21 +309,24 @@ pub fn update_fov(
|
||||||
mut settings: ResMut<var::Settings>,
|
mut settings: ResMut<var::Settings>,
|
||||||
mut q_camera: Query<&mut Projection, With<Camera>>,
|
mut q_camera: Query<&mut Projection, With<Camera>>,
|
||||||
) {
|
) {
|
||||||
if let (Ok(gforce), Ok(mut projection)) = (q_player.get_single(), q_camera.get_single_mut())
|
if let (Ok(gforce), Ok(mut projection)) = (q_player.get_single(), q_camera.get_single_mut()) {
|
||||||
{
|
|
||||||
let fov: f32;
|
let fov: f32;
|
||||||
if settings.hud_active && mouse_input.pressed(settings.key_zoom) {
|
if mouse_input.pressed(settings.key_zoom) {
|
||||||
fov = settings.zoom_fov.to_radians();
|
fov = settings.zoom_fov.to_radians();
|
||||||
if !settings.is_zooming {
|
if !settings.is_zooming {
|
||||||
settings.is_zooming = true;
|
settings.is_zooming = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fov = (gforce.visual_effect.clamp(0.0, 1.0) * settings.fov_highspeed + settings.fov).to_radians();
|
fov = (gforce.visual_effect.clamp(0.0, 1.0) * settings.fov_highspeed + settings.fov)
|
||||||
|
.to_radians();
|
||||||
if settings.is_zooming {
|
if settings.is_zooming {
|
||||||
settings.is_zooming = false;
|
settings.is_zooming = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*projection = Projection::Perspective(PerspectiveProjection { fov: fov, ..default() });
|
*projection = Projection::Perspective(PerspectiveProjection {
|
||||||
|
fov: fov,
|
||||||
|
..default()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,18 +352,40 @@ fn manage_player_actor(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
settings: Res<var::Settings>,
|
settings: Res<var::Settings>,
|
||||||
mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>,
|
mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>,
|
||||||
mut q_hiddenplayer: Query<(Entity, &mut Visibility, &mut Position, &mut Rotation, &mut LinearVelocity, &mut AngularVelocity, Option<&mut actor::ExperiencesGForce>, Option<&actor::JustNowEnteredVehicle>), (With<actor::Player>, Without<actor::PlayerCamera>)>,
|
mut q_hiddenplayer: Query<
|
||||||
q_ride: Query<(&Transform, &Position, &Rotation, &LinearVelocity, &AngularVelocity), (With<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
(
|
||||||
|
Entity,
|
||||||
|
&mut Visibility,
|
||||||
|
&mut Position,
|
||||||
|
&mut Rotation,
|
||||||
|
&mut LinearVelocity,
|
||||||
|
&mut AngularVelocity,
|
||||||
|
Option<&mut actor::ExperiencesGForce>,
|
||||||
|
Option<&actor::JustNowEnteredVehicle>,
|
||||||
|
),
|
||||||
|
(With<actor::Player>, Without<actor::PlayerCamera>),
|
||||||
|
>,
|
||||||
|
q_ride: Query<
|
||||||
|
(
|
||||||
|
&Transform,
|
||||||
|
&Position,
|
||||||
|
&Rotation,
|
||||||
|
&LinearVelocity,
|
||||||
|
&AngularVelocity,
|
||||||
|
),
|
||||||
|
(With<actor::PlayerDrivesThis>, Without<actor::Player>),
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
for mut vis in &mut q_playercam {
|
for mut vis in &mut q_playercam {
|
||||||
if settings.third_person || settings.map_active {
|
if settings.third_person || settings.map_active {
|
||||||
*vis = Visibility::Inherited;
|
*vis = Visibility::Inherited;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (entity, mut vis, mut pos, mut rot, mut v, mut angv, mut gforce, entering) in &mut q_hiddenplayer {
|
for (entity, mut vis, mut pos, mut rot, mut v, mut angv, mut gforce, entering) in
|
||||||
|
&mut q_hiddenplayer
|
||||||
|
{
|
||||||
// If we are riding a vehicle, place the player at the position where
|
// If we are riding a vehicle, place the player at the position where
|
||||||
// it would be after exiting the vehicle.
|
// it would be after exiting the vehicle.
|
||||||
// I would rather place it in the center of the vehicle, but at the time
|
// I would rather place it in the center of the vehicle, but at the time
|
||||||
|
@ -344,7 +393,8 @@ fn manage_player_actor(
|
||||||
// exiting the vehicle, so I'm doing it here instead, as a workaround.
|
// exiting the vehicle, so I'm doing it here instead, as a workaround.
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
if let Ok((ride_trans, ride_pos, ride_rot, ride_v, ride_angv)) = q_ride.get_single() {
|
if let Ok((ride_trans, ride_pos, ride_rot, ride_v, ride_angv)) = q_ride.get_single() {
|
||||||
pos.0 = ride_pos.0 + DVec3::from(ride_trans.rotation * Vec3::new(0.0, 0.0, ride_trans.scale.z * 2.0));
|
pos.0 = ride_pos.0
|
||||||
|
+ DVec3::from(ride_trans.rotation * Vec3::new(0.0, 0.0, ride_trans.scale.z * 2.0));
|
||||||
rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]);
|
rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]);
|
||||||
*v = ride_v.clone();
|
*v = ride_v.clone();
|
||||||
*angv = ride_angv.clone();
|
*angv = ride_angv.clone();
|
||||||
|
@ -353,7 +403,9 @@ fn manage_player_actor(
|
||||||
// vehicles at high relative speed, even though they probably should.
|
// vehicles at high relative speed, even though they probably should.
|
||||||
if let (Some(gforce), Some(_)) = (&mut gforce, entering) {
|
if let (Some(gforce), Some(_)) = (&mut gforce, entering) {
|
||||||
gforce.last_linear_velocity = v.0;
|
gforce.last_linear_velocity = v.0;
|
||||||
commands.entity(entity).remove::<actor::JustNowEnteredVehicle>();
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.remove::<actor::JustNowEnteredVehicle>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,20 +414,24 @@ fn manage_player_actor(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn apply_input_to_player(
|
pub fn apply_input_to_player(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
|
mut commands: Commands,
|
||||||
settings: Res<var::Settings>,
|
settings: Res<var::Settings>,
|
||||||
windows: Query<&Window, With<PrimaryWindow>>,
|
windows: Query<&Window, With<PrimaryWindow>>,
|
||||||
mut mouse_events: EventReader<MouseMotion>,
|
mut mouse_events: EventReader<MouseMotion>,
|
||||||
key_input: Res<ButtonInput<KeyCode>>,
|
key_input: Res<ButtonInput<KeyCode>>,
|
||||||
q_audiosinks: Query<(&audio::Sfx, &AudioSink)>,
|
q_audiosinks: Query<(&audio::Sfx, &AudioSink)>,
|
||||||
q_target: Query<&LinearVelocity, (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
q_target: Query<&LinearVelocity, (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
||||||
mut q_playercam: Query<(
|
mut q_playercam: Query<
|
||||||
|
(
|
||||||
|
Entity,
|
||||||
&Transform,
|
&Transform,
|
||||||
&mut actor::Engine,
|
&mut actor::Engine,
|
||||||
&mut AngularVelocity,
|
|
||||||
&mut LinearVelocity,
|
&mut LinearVelocity,
|
||||||
&mut ExternalTorque,
|
&mut ExternalTorque,
|
||||||
Option<&actor::PlayerDrivesThis>,
|
Option<&actor::PlayerDrivesThis>,
|
||||||
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
),
|
||||||
|
(With<actor::PlayerCamera>, Without<Camera>),
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
if settings.map_active || !settings.in_control() {
|
if settings.map_active || !settings.in_control() {
|
||||||
return;
|
return;
|
||||||
|
@ -390,8 +446,7 @@ pub fn apply_input_to_player(
|
||||||
focused = window.focused;
|
focused = window.focused;
|
||||||
win_res_x = window.resolution.width();
|
win_res_x = window.resolution.width();
|
||||||
win_res_y = window.resolution.height();
|
win_res_y = window.resolution.height();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
win_res_x = 1920.0;
|
win_res_x = 1920.0;
|
||||||
win_res_y = 1050.0;
|
win_res_y = 1050.0;
|
||||||
}
|
}
|
||||||
|
@ -402,7 +457,9 @@ pub fn apply_input_to_player(
|
||||||
DVec3::splat(0.0)
|
DVec3::splat(0.0)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok((player_transform, mut engine, mut angularvelocity, mut v, mut torque, bike)) = q_playercam.get_single_mut() {
|
if let Ok((player_entity, player_transform, mut engine, mut v, mut torque, bike)) =
|
||||||
|
q_playercam.get_single_mut()
|
||||||
|
{
|
||||||
// Handle key input
|
// Handle key input
|
||||||
if focused {
|
if focused {
|
||||||
if key_input.pressed(settings.key_forward) || settings.cruise_control_active {
|
if key_input.pressed(settings.key_forward) || settings.cruise_control_active {
|
||||||
|
@ -426,7 +483,10 @@ pub fn apply_input_to_player(
|
||||||
if key_input.pressed(settings.key_stop) {
|
if key_input.pressed(settings.key_stop) {
|
||||||
let stop_direction = (target_v - v.0).normalize();
|
let stop_direction = (target_v - v.0).normalize();
|
||||||
if stop_direction.length_squared() > 0.3 {
|
if stop_direction.length_squared() > 0.3 {
|
||||||
axis_input += 1.0 * DVec3::from(player_transform.rotation.inverse() * stop_direction.as_vec3());
|
axis_input += 1.0
|
||||||
|
* DVec3::from(
|
||||||
|
player_transform.rotation.inverse() * stop_direction.as_vec3(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -442,7 +502,8 @@ pub fn apply_input_to_player(
|
||||||
axis_input = axis_input.clamp(DVec3::splat(-1.0), DVec3::splat(1.0));
|
axis_input = axis_input.clamp(DVec3::splat(-1.0), DVec3::splat(1.0));
|
||||||
|
|
||||||
// Apply movement update
|
// Apply movement update
|
||||||
let forward_factor = engine.current_warmup * (if axis_input.z > 0.0 {
|
let forward_factor = engine.current_warmup
|
||||||
|
* (if axis_input.z > 0.0 {
|
||||||
engine.thrust_forward
|
engine.thrust_forward
|
||||||
} else {
|
} else {
|
||||||
engine.thrust_back
|
engine.thrust_back
|
||||||
|
@ -452,8 +513,10 @@ pub fn apply_input_to_player(
|
||||||
let factor = DVec3::new(right_factor as f64, up_factor as f64, forward_factor as f64);
|
let factor = DVec3::new(right_factor as f64, up_factor as f64, forward_factor as f64);
|
||||||
|
|
||||||
if axis_input.length_squared() > 0.003 {
|
if axis_input.length_squared() > 0.003 {
|
||||||
let acceleration_global: DVec3 = DVec3::from(player_transform.rotation * (axis_input * factor).as_vec3());
|
let acceleration_global: DVec3 =
|
||||||
let mut acceleration_total: DVec3 = (actor::ENGINE_SPEED_FACTOR * dt) as f64 * acceleration_global;
|
DVec3::from(player_transform.rotation * (axis_input * factor).as_vec3());
|
||||||
|
let mut acceleration_total: DVec3 =
|
||||||
|
(actor::ENGINE_SPEED_FACTOR * dt) as f64 * acceleration_global;
|
||||||
let threshold = 1e-5;
|
let threshold = 1e-5;
|
||||||
if key_input.pressed(settings.key_stop) {
|
if key_input.pressed(settings.key_stop) {
|
||||||
// Decelerate (or match velocity to target_v)
|
// Decelerate (or match velocity to target_v)
|
||||||
|
@ -461,8 +524,7 @@ pub fn apply_input_to_player(
|
||||||
for i in 0..3 {
|
for i in 0..3 {
|
||||||
if dv[i].abs() < threshold {
|
if dv[i].abs() < threshold {
|
||||||
v[i] = target_v[i];
|
v[i] = target_v[i];
|
||||||
}
|
} else if dv[i].signum() != (dv[i] + acceleration_total[i]).signum() {
|
||||||
else if dv[i].signum() != (dv[i] + acceleration_total[i]).signum() {
|
|
||||||
// Almost stopped, but we overshot v=0
|
// Almost stopped, but we overshot v=0
|
||||||
v[i] = target_v[i];
|
v[i] = target_v[i];
|
||||||
acceleration_total[i] = 0.0;
|
acceleration_total[i] = 0.0;
|
||||||
|
@ -471,18 +533,23 @@ pub fn apply_input_to_player(
|
||||||
}
|
}
|
||||||
// TODO: handle mass
|
// TODO: handle mass
|
||||||
v.0 += acceleration_total;
|
v.0 += acceleration_total;
|
||||||
engine.current_warmup = (engine.current_warmup + dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
engine.current_warmup =
|
||||||
|
(engine.current_warmup + dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
||||||
play_thruster_sound = true;
|
play_thruster_sound = true;
|
||||||
}
|
} else {
|
||||||
else {
|
engine.current_warmup =
|
||||||
engine.current_warmup = (engine.current_warmup - dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
(engine.current_warmup - dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle mouse input and mouse-like key bindings
|
// Handle mouse input and mouse-like key bindings
|
||||||
let mut play_reactionwheel_sound = false;
|
let mut play_reactionwheel_sound = false;
|
||||||
let mut mouse_delta = Vec2::ZERO;
|
let mut mouse_delta = Vec2::ZERO;
|
||||||
let mut pitch_yaw_rot = Vec3::ZERO;
|
let mut pitch_yaw_rot = Vec3::ZERO;
|
||||||
let sensitivity_factor = if settings.is_zooming { settings.zoom_sensitivity_factor } else { 1.0 };
|
let sensitivity_factor = if settings.is_zooming {
|
||||||
|
settings.zoom_sensitivity_factor
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
let mouseless_sensitivity = 8.0 * sensitivity_factor;
|
let mouseless_sensitivity = 8.0 * sensitivity_factor;
|
||||||
let mouseless_rotation_sensitivity = 40.0 * sensitivity_factor;
|
let mouseless_rotation_sensitivity = 40.0 * sensitivity_factor;
|
||||||
if key_input.pressed(settings.key_mouseup) {
|
if key_input.pressed(settings.key_mouseup) {
|
||||||
|
@ -492,7 +559,8 @@ pub fn apply_input_to_player(
|
||||||
pitch_yaw_rot[0] += mouseless_sensitivity;
|
pitch_yaw_rot[0] += mouseless_sensitivity;
|
||||||
}
|
}
|
||||||
if key_input.pressed(settings.key_mouseleft) {
|
if key_input.pressed(settings.key_mouseleft) {
|
||||||
pitch_yaw_rot[1] += mouseless_sensitivity; }
|
pitch_yaw_rot[1] += mouseless_sensitivity;
|
||||||
|
}
|
||||||
if key_input.pressed(settings.key_mouseright) {
|
if key_input.pressed(settings.key_mouseright) {
|
||||||
pitch_yaw_rot[1] -= mouseless_sensitivity;
|
pitch_yaw_rot[1] -= mouseless_sensitivity;
|
||||||
}
|
}
|
||||||
|
@ -514,29 +582,27 @@ pub fn apply_input_to_player(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let slowrot = settings.rotation_stabilizer_active || key_input.pressed(settings.key_stop);
|
let mouse_speed = pitch_yaw_rot.length();
|
||||||
let angular_slowdown: f64 = if slowrot {
|
let mouse_moving = mouse_speed > EPSILON32;
|
||||||
(2.0 - engine.reaction_wheels.powf(0.05).clamp(1.001, 1.1)) as f64
|
|
||||||
} else {
|
if mouse_moving {
|
||||||
1.0
|
|
||||||
};
|
|
||||||
if pitch_yaw_rot.length_squared() > 1.0e-18 {
|
|
||||||
play_reactionwheel_sound = true;
|
play_reactionwheel_sound = true;
|
||||||
pitch_yaw_rot *= settings.mouse_sensitivity * sensitivity_factor * engine.reaction_wheels;
|
pitch_yaw_rot *=
|
||||||
|
settings.mouse_sensitivity * sensitivity_factor * engine.reaction_wheels;
|
||||||
torque.apply_torque(DVec3::from(
|
torque.apply_torque(DVec3::from(
|
||||||
player_transform.rotation * Vec3::new(
|
player_transform.rotation
|
||||||
pitch_yaw_rot[0],
|
* Vec3::new(pitch_yaw_rot[0], pitch_yaw_rot[1], pitch_yaw_rot[2]),
|
||||||
pitch_yaw_rot[1],
|
));
|
||||||
pitch_yaw_rot[2])));
|
|
||||||
angularvelocity.0 *= angular_slowdown.clamp(0.97, 1.0) as f64;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if angularvelocity.length_squared() > 1.0e-18 {
|
|
||||||
angularvelocity.0 *= angular_slowdown;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
angularvelocity.0 = DVec3::splat(0.0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if settings.rotation_stabilizer_active || key_input.pressed(settings.key_stop) {
|
||||||
|
commands
|
||||||
|
.entity(player_entity)
|
||||||
|
.try_insert(actor::WantsMaxRotation(mouse_speed as f64 * 0.1));
|
||||||
|
} else {
|
||||||
|
commands
|
||||||
|
.entity(player_entity)
|
||||||
|
.remove::<actor::WantsMaxRotation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut sinks: HashMap<audio::Sfx, &AudioSink> = HashMap::new();
|
let mut sinks: HashMap<audio::Sfx, &AudioSink> = HashMap::new();
|
||||||
|
@ -563,8 +629,16 @@ pub fn apply_input_to_player(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let sinks = vec![
|
let sinks = vec![
|
||||||
(1.2, actor::EngineType::Monopropellant, sinks.get(&audio::Sfx::Thruster)),
|
(
|
||||||
(1.0, actor::EngineType::Rocket, sinks.get(&audio::Sfx::Rocket)),
|
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.4, actor::EngineType::Ion, sinks.get(&audio::Sfx::Ion)),
|
||||||
];
|
];
|
||||||
let seconds_to_max_vol = 0.05;
|
let seconds_to_max_vol = 0.05;
|
||||||
|
@ -573,8 +647,7 @@ pub fn apply_input_to_player(
|
||||||
if let (vol_boost, engine_type, Some(sink)) = sink_data {
|
if let (vol_boost, engine_type, Some(sink)) = sink_data {
|
||||||
if settings.mute_sfx {
|
if settings.mute_sfx {
|
||||||
sink.pause();
|
sink.pause();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let volume = sink.volume();
|
let volume = sink.volume();
|
||||||
if engine.engine_type == engine_type {
|
if engine.engine_type == engine_type {
|
||||||
if play_thruster_sound {
|
if play_thruster_sound {
|
||||||
|
@ -582,13 +655,14 @@ pub fn apply_input_to_player(
|
||||||
if volume < 1.0 {
|
if volume < 1.0 {
|
||||||
let maxvol = vol_boost;
|
let maxvol = vol_boost;
|
||||||
//let maxvol = engine.current_warmup * vol_boost;
|
//let maxvol = engine.current_warmup * vol_boost;
|
||||||
sink.set_volume((volume + dt / seconds_to_max_vol).clamp(0.0, maxvol));
|
sink.set_volume(
|
||||||
|
(volume + dt / seconds_to_max_vol).clamp(0.0, maxvol),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sink.set_volume((volume - dt / seconds_to_min_vol).clamp(0.0, 1.0));
|
sink.set_volume((volume - dt / seconds_to_min_vol).clamp(0.0, 1.0));
|
||||||
}
|
}
|
||||||
}
|
} else if volume > 0.0 {
|
||||||
else if volume > 0.0 {
|
|
||||||
sink.set_volume((volume - dt / seconds_to_min_vol).clamp(0.0, 1.0));
|
sink.set_volume((volume - dt / seconds_to_min_vol).clamp(0.0, 1.0));
|
||||||
}
|
}
|
||||||
if volume < 0.0001 {
|
if volume < 0.0001 {
|
||||||
|
@ -619,12 +693,14 @@ pub fn update_map_only_object_visibility(
|
||||||
let dist = cam_pos.distance(pos.as_vec3());
|
let dist = cam_pos.distance(pos.as_vec3());
|
||||||
if dist >= onlyinmap.min_distance as f32 {
|
if dist >= onlyinmap.min_distance as f32 {
|
||||||
*vis = Visibility::Inherited;
|
*vis = Visibility::Inherited;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Failed get position of actor ID '{}'", &onlyinmap.distance_to_id);
|
error!(
|
||||||
|
"Failed get position of actor ID '{}'",
|
||||||
|
&onlyinmap.distance_to_id
|
||||||
|
);
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -639,17 +715,18 @@ pub fn find_closest_target<TargetSpecifier>(
|
||||||
objects: Vec<(TargetSpecifier, &Transform)>,
|
objects: Vec<(TargetSpecifier, &Transform)>,
|
||||||
camera_transform: &Transform,
|
camera_transform: &Transform,
|
||||||
) -> (Option<TargetSpecifier>, f32)
|
) -> (Option<TargetSpecifier>, f32)
|
||||||
where TargetSpecifier: Clone
|
where
|
||||||
|
TargetSpecifier: Clone,
|
||||||
{
|
{
|
||||||
let mut closest_entity: Option<TargetSpecifier> = None;
|
let mut closest_entity: Option<TargetSpecifier> = None;
|
||||||
let mut closest_distance: f32 = f32::MAX;
|
let mut closest_distance: f32 = f32::MAX;
|
||||||
let target_vector: Vec3 = (camera_transform.rotation * Vec3::new(0.0, 0.0, -1.0))
|
let target_vector: Vec3 =
|
||||||
.normalize_or_zero();
|
(camera_transform.rotation * Vec3::new(0.0, 0.0, -1.0)).normalize_or_zero();
|
||||||
for (entity, trans) in objects {
|
for (entity, trans) in objects {
|
||||||
// Use Transform instead of Position because we're basing this
|
// Use Transform instead of Position because we're basing this
|
||||||
// not on the player mesh but on the camera, which doesn't have a position.
|
// 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(
|
let (angular_diameter, angle, distance) =
|
||||||
trans, camera_transform, &target_vector);
|
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, PI32) {
|
||||||
// It's in the field of view!
|
// It's in the field of view!
|
||||||
//commands.entity(entity).insert(IsTargeted);
|
//commands.entity(entity).insert(IsTargeted);
|
||||||
|
@ -669,8 +746,7 @@ pub fn calc_angular_diameter_known_target_vector(
|
||||||
camera: &Transform,
|
camera: &Transform,
|
||||||
target_vector: &Vec3,
|
target_vector: &Vec3,
|
||||||
) -> (f32, f32, f32) {
|
) -> (f32, f32, f32) {
|
||||||
let pos_vector: Vec3 = (target.translation - camera.translation)
|
let pos_vector: Vec3 = (target.translation - camera.translation).normalize_or_zero();
|
||||||
.normalize_or_zero();
|
|
||||||
let cosine_of_angle: f32 = target_vector.dot(pos_vector);
|
let cosine_of_angle: f32 = target_vector.dot(pos_vector);
|
||||||
let angle: f32 = cosine_of_angle.acos();
|
let angle: f32 = cosine_of_angle.acos();
|
||||||
let distance: f32 = target.translation.distance(camera.translation);
|
let distance: f32 = target.translation.distance(camera.translation);
|
||||||
|
@ -678,20 +754,15 @@ pub fn calc_angular_diameter_known_target_vector(
|
||||||
let angular_diameter: f32 = if distance > 0.0 {
|
let angular_diameter: f32 = if distance > 0.0 {
|
||||||
// Angular Diameter
|
// Angular Diameter
|
||||||
leeway * (target.scale[0] / distance).asin()
|
leeway * (target.scale[0] / distance).asin()
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
return (angular_diameter, angle, distance);
|
return (angular_diameter, angle, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn calc_angular_diameter(
|
pub fn calc_angular_diameter(target: &Transform, camera: &Transform) -> (f32, f32, f32) {
|
||||||
target: &Transform,
|
let target_vector: Vec3 = (camera.rotation * Vec3::new(0.0, 0.0, -1.0)).normalize_or_zero();
|
||||||
camera: &Transform,
|
|
||||||
) -> (f32, f32, f32) {
|
|
||||||
let target_vector: Vec3 = (camera.rotation * Vec3::new(0.0, 0.0, -1.0))
|
|
||||||
.normalize_or_zero();
|
|
||||||
return calc_angular_diameter_known_target_vector(target, camera, &target_vector);
|
return calc_angular_diameter_known_target_vector(target, camera, &target_vector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -702,7 +773,10 @@ pub fn position_to_transform(
|
||||||
mapcam: Res<MapCam>,
|
mapcam: Res<MapCam>,
|
||||||
settings: Res<var::Settings>,
|
settings: Res<var::Settings>,
|
||||||
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
q_player: Query<&Position, With<actor::PlayerCamera>>,
|
||||||
mut q_trans: Query<(&'static mut Transform, &'static Position, &'static Rotation), Without<Parent>>,
|
mut q_trans: Query<
|
||||||
|
(&'static mut Transform, &'static Position, &'static Rotation),
|
||||||
|
Without<Parent>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
let center: DVec3 = if settings.map_active {
|
let center: DVec3 = if settings.map_active {
|
||||||
mapcam.center
|
mapcam.center
|
||||||
|
|
177
src/chat.rs
|
@ -14,8 +14,8 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use serde_yaml::Value;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use serde_yaml::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub const CHATS: &[&str] = &[
|
pub const CHATS: &[&str] = &[
|
||||||
|
@ -65,24 +65,22 @@ pub const NON_CHOICE_TOKENS: &[&str] = &[
|
||||||
TOKEN_SOUND,
|
TOKEN_SOUND,
|
||||||
TOKEN_NOWAIT,
|
TOKEN_NOWAIT,
|
||||||
];
|
];
|
||||||
pub const SKIPPABLE_TOKENS: &[&str] = &[
|
pub const SKIPPABLE_TOKENS: &[&str] = &[TOKEN_CHAT, TOKEN_LABEL, TOKEN_GOTO, TOKEN_NOWAIT];
|
||||||
TOKEN_CHAT,
|
|
||||||
TOKEN_LABEL,
|
|
||||||
TOKEN_GOTO,
|
|
||||||
TOKEN_NOWAIT,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct ChatPlugin;
|
pub struct ChatPlugin;
|
||||||
impl Plugin for ChatPlugin {
|
impl Plugin for ChatPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, load_chats);
|
app.add_systems(Startup, load_chats);
|
||||||
app.add_systems(Update, (
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
handle_reply_keys.before(handle_chat_timer),
|
handle_reply_keys.before(handle_chat_timer),
|
||||||
handle_chat_timer.before(handle_chat_events),
|
handle_chat_timer.before(handle_chat_events),
|
||||||
handle_new_conversations.before(handle_chat_events),
|
handle_new_conversations.before(handle_chat_events),
|
||||||
handle_chat_events.before(handle_chat_scripts),
|
handle_chat_events.before(handle_chat_scripts),
|
||||||
handle_chat_scripts,
|
handle_chat_scripts,
|
||||||
));
|
),
|
||||||
|
);
|
||||||
app.add_event::<StartConversationEvent>();
|
app.add_event::<StartConversationEvent>();
|
||||||
app.add_event::<ChatEvent>();
|
app.add_event::<ChatEvent>();
|
||||||
app.add_event::<ChatScriptEvent>();
|
app.add_event::<ChatScriptEvent>();
|
||||||
|
@ -107,8 +105,7 @@ pub struct Choice {
|
||||||
pub goto: ChatPos,
|
pub goto: ChatPos,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Clone)]
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Talker {
|
pub struct Talker {
|
||||||
pub chat_name: String,
|
pub chat_name: String,
|
||||||
pub actor_id: String,
|
pub actor_id: String,
|
||||||
|
@ -152,7 +149,6 @@ impl Default for Extracted {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is the only place where any YAML interaction should be happening.
|
// This is the only place where any YAML interaction should be happening.
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct ChatDB(Vec<Value>);
|
pub struct ChatDB(Vec<Value>);
|
||||||
|
@ -165,8 +161,7 @@ impl ChatDB {
|
||||||
if let Value::Sequence(yaml_sequence) = yaml_data {
|
if let Value::Sequence(yaml_sequence) = yaml_data {
|
||||||
self.0.push(Value::Sequence(yaml_sequence));
|
self.0.push(Value::Sequence(yaml_sequence));
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Could not load YAML: {:?}", yaml_data);
|
error!("Could not load YAML: {:?}", yaml_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,7 +197,10 @@ impl ChatDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_includes_recursively(sequence: &mut Value, include_db: &HashMap<String, Vec<Value>>) {
|
fn preprocess_includes_recursively(
|
||||||
|
sequence: &mut Value,
|
||||||
|
include_db: &HashMap<String, Vec<Value>>,
|
||||||
|
) {
|
||||||
let mut changes: Vec<(usize, String)> = Vec::new();
|
let mut changes: Vec<(usize, String)> = Vec::new();
|
||||||
if let Some(vector) = sequence.as_sequence_mut() {
|
if let Some(vector) = sequence.as_sequence_mut() {
|
||||||
for (index, item) in vector.iter_mut().enumerate() {
|
for (index, item) in vector.iter_mut().enumerate() {
|
||||||
|
@ -213,8 +211,7 @@ impl ChatDB {
|
||||||
if key == TOKEN_INCLUDE {
|
if key == TOKEN_INCLUDE {
|
||||||
changes.push((index, value.to_string()));
|
changes.push((index, value.to_string()));
|
||||||
}
|
}
|
||||||
}
|
} else if value.is_sequence() {
|
||||||
else if value.is_sequence() {
|
|
||||||
ChatDB::preprocess_includes_recursively(value, include_db);
|
ChatDB::preprocess_includes_recursively(value, include_db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +245,9 @@ impl ChatDB {
|
||||||
if let Some(result) = found {
|
if let Some(result) = found {
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
return Err(format!("No chat with the conversation ID `{id}` was found."));
|
return Err(format!(
|
||||||
|
"No chat with the conversation ID `{id}` was found."
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// For a given Value, check whether it's a Value::Mapping and whether it
|
// For a given Value, check whether it's a Value::Mapping and whether it
|
||||||
|
@ -273,19 +272,15 @@ impl ChatDB {
|
||||||
if let Value::String(key) = key {
|
if let Value::String(key) = key {
|
||||||
if key == TOKEN_NOWAIT && value.as_bool() == Some(true) {
|
if key == TOKEN_NOWAIT && value.as_bool() == Some(true) {
|
||||||
result.nowait = true;
|
result.nowait = true;
|
||||||
}
|
} else if key == TOKEN_IF {
|
||||||
else if key == TOKEN_IF {
|
|
||||||
if let Some(condition) = value.as_str() {
|
if let Some(condition) = value.as_str() {
|
||||||
result.condition = Some(condition.to_string());
|
result.condition = Some(condition.to_string());
|
||||||
}
|
}
|
||||||
}
|
} else if non_choice_tokens.contains(&key.as_str()) {
|
||||||
else if non_choice_tokens.contains(&key.as_str()) {
|
|
||||||
// skip over the other non-choice tokens
|
// skip over the other non-choice tokens
|
||||||
}
|
} else if key.as_str().starts_with(TOKEN_IF_INLINE) {
|
||||||
else if key.as_str().starts_with(TOKEN_IF_INLINE) {
|
|
||||||
// skip over inlined if-statements
|
// skip over inlined if-statements
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
result.choice_text = Some(key.to_string());
|
result.choice_text = Some(key.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,7 +290,12 @@ impl ChatDB {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_label_recursively(&self, sequence: &Value, label: &String, mut pos: ChatPos) -> Option<ChatPos> {
|
fn search_label_recursively(
|
||||||
|
&self,
|
||||||
|
sequence: &Value,
|
||||||
|
label: &String,
|
||||||
|
mut pos: ChatPos,
|
||||||
|
) -> Option<ChatPos> {
|
||||||
if pos.len() > MAX_BRANCH_DEPTH {
|
if pos.len() > MAX_BRANCH_DEPTH {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -315,9 +315,10 @@ impl ChatDB {
|
||||||
}
|
}
|
||||||
if value.is_sequence() {
|
if value.is_sequence() {
|
||||||
pos.push(index);
|
pos.push(index);
|
||||||
if let Some(result) = self.search_label_recursively(
|
if let Some(result) =
|
||||||
value, label, pos.clone()) {
|
self.search_label_recursively(value, label, pos.clone())
|
||||||
return Some(result)
|
{
|
||||||
|
return Some(result);
|
||||||
}
|
}
|
||||||
pos.pop();
|
pos.pop();
|
||||||
}
|
}
|
||||||
|
@ -357,7 +358,7 @@ impl ChatDB {
|
||||||
let index = chat.position.len() - 1;
|
let index = chat.position.len() - 1;
|
||||||
chat.position[index] += 1;
|
chat.position[index] += 1;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Some(Value::Mapping(map)) => {
|
Some(Value::Mapping(map)) => {
|
||||||
if seek_past_dialog_choices && self.is_choice(Some(&Value::Mapping(map))) {
|
if seek_past_dialog_choices && self.is_choice(Some(&Value::Mapping(map))) {
|
||||||
// we just dropped out of a branch and ended up in a dialog
|
// we just dropped out of a branch and ended up in a dialog
|
||||||
|
@ -367,8 +368,7 @@ impl ChatDB {
|
||||||
let index = chat.position.len() - 1;
|
let index = chat.position.len() - 1;
|
||||||
chat.position[index] += 1;
|
chat.position[index] += 1;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,8 +441,7 @@ impl ChatDB {
|
||||||
if !SKIPPABLE_TOKENS.contains(&key) {
|
if !SKIPPABLE_TOKENS.contains(&key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,17 +451,16 @@ impl ChatDB {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_yaml_entry(
|
fn process_yaml_entry(&self, chat: &mut Chat, event: &mut EventWriter<ChatEvent>) -> bool {
|
||||||
&self,
|
|
||||||
chat: &mut Chat,
|
|
||||||
event: &mut EventWriter<ChatEvent>,
|
|
||||||
) -> bool {
|
|
||||||
let current_item = self.at(chat.internal_id, &chat.position);
|
let current_item = self.at(chat.internal_id, &chat.position);
|
||||||
let mut processed_a_choice = false;
|
let mut processed_a_choice = false;
|
||||||
match current_item {
|
match current_item {
|
||||||
Some(Value::String(message)) => {
|
Some(Value::String(message)) => {
|
||||||
event.send(ChatEvent::SpawnMessage(message.to_string(),
|
event.send(ChatEvent::SpawnMessage(
|
||||||
hud::LogLevel::Chat, DEFAULT_SOUND.to_string()));
|
message.to_string(),
|
||||||
|
hud::LogLevel::Chat,
|
||||||
|
DEFAULT_SOUND.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
Some(Value::Mapping(map)) => {
|
Some(Value::Mapping(map)) => {
|
||||||
let mut sound = DEFAULT_SOUND.to_string();
|
let mut sound = DEFAULT_SOUND.to_string();
|
||||||
|
@ -506,15 +504,24 @@ impl ChatDB {
|
||||||
match (key, value) {
|
match (key, value) {
|
||||||
(Some(TOKEN_MSG), Value::String(message)) => {
|
(Some(TOKEN_MSG), Value::String(message)) => {
|
||||||
event.send(ChatEvent::SpawnMessage(
|
event.send(ChatEvent::SpawnMessage(
|
||||||
message.to_string(), hud::LogLevel::Chat, sound.clone()));
|
message.to_string(),
|
||||||
|
hud::LogLevel::Chat,
|
||||||
|
sound.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
(Some(TOKEN_SYSTEM), Value::String(message)) => {
|
(Some(TOKEN_SYSTEM), Value::String(message)) => {
|
||||||
event.send(ChatEvent::SpawnMessage(
|
event.send(ChatEvent::SpawnMessage(
|
||||||
message.to_string(), hud::LogLevel::Info, sound.clone()));
|
message.to_string(),
|
||||||
|
hud::LogLevel::Info,
|
||||||
|
sound.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
(Some(TOKEN_WARN), Value::String(message)) => {
|
(Some(TOKEN_WARN), Value::String(message)) => {
|
||||||
event.send(ChatEvent::SpawnMessage(
|
event.send(ChatEvent::SpawnMessage(
|
||||||
message.to_string(), hud::LogLevel::Warning, sound.clone()));
|
message.to_string(),
|
||||||
|
hud::LogLevel::Warning,
|
||||||
|
sound.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
(Some(TOKEN_SET), Value::String(instructions)) => {
|
(Some(TOKEN_SET), Value::String(instructions)) => {
|
||||||
event.send(ChatEvent::SetVariable(instructions.to_string()));
|
event.send(ChatEvent::SetVariable(instructions.to_string()));
|
||||||
|
@ -551,8 +558,11 @@ impl ChatDB {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if chat.position.len() == 0 {
|
if chat.position.len() == 0 {
|
||||||
event.send(ChatEvent::SpawnMessage("Disconnected.".to_string(),
|
event.send(ChatEvent::SpawnMessage(
|
||||||
hud::LogLevel::Info, DEFAULT_SOUND.to_string()));
|
"Disconnected.".to_string(),
|
||||||
|
hud::LogLevel::Info,
|
||||||
|
DEFAULT_SOUND.to_string(),
|
||||||
|
));
|
||||||
event.send(ChatEvent::DespawnAllChats);
|
event.send(ChatEvent::DespawnAllChats);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -584,19 +594,25 @@ impl ChatDB {
|
||||||
// Spawn choices until we reach a non-choice item or the end of the branch
|
// Spawn choices until we reach a non-choice item or the end of the branch
|
||||||
let mut key: usize = 0;
|
let mut key: usize = 0;
|
||||||
let mut reached_end_of_branch = false;
|
let mut reached_end_of_branch = false;
|
||||||
while let Some(data) = self.extract_choice(self.at(chat.internal_id, &chat.position).as_ref()) {
|
while let Some(data) =
|
||||||
|
self.extract_choice(self.at(chat.internal_id, &chat.position).as_ref())
|
||||||
|
{
|
||||||
if let Some(choice_text) = data.choice_text {
|
if let Some(choice_text) = data.choice_text {
|
||||||
if reached_end_of_branch {
|
if reached_end_of_branch {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let mut goto: Vec<usize> = chat.position.clone();
|
let mut goto: Vec<usize> = chat.position.clone();
|
||||||
goto.push(0);
|
goto.push(0);
|
||||||
event.send(ChatEvent::SpawnChoice(choice_text,
|
event.send(ChatEvent::SpawnChoice(
|
||||||
key, goto, data.nowait, data.condition));
|
choice_text,
|
||||||
|
key,
|
||||||
|
goto,
|
||||||
|
data.nowait,
|
||||||
|
data.condition,
|
||||||
|
));
|
||||||
key += 1;
|
key += 1;
|
||||||
reached_end_of_branch = self.advance_pointer(chat);
|
reached_end_of_branch = self.advance_pointer(chat);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -640,10 +656,7 @@ pub fn handle_new_conversations(
|
||||||
talker: event.talker.clone(),
|
talker: event.talker.clone(),
|
||||||
};
|
};
|
||||||
chatdb.advance_chat(&mut chat, &mut ew_chatevent);
|
chatdb.advance_chat(&mut chat, &mut ew_chatevent);
|
||||||
commands.spawn((
|
commands.spawn((chat, world::DespawnOnPlayerDeath));
|
||||||
chat,
|
|
||||||
world::DespawnOnPlayerDeath,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!("Error while looking for chat ID: {error}");
|
error!("Error while looking for chat ID: {error}");
|
||||||
|
@ -700,7 +713,10 @@ pub fn handle_chat_events(
|
||||||
ChatEvent::SpawnMessage(message, level, sound) => {
|
ChatEvent::SpawnMessage(message, level, sound) => {
|
||||||
match level {
|
match level {
|
||||||
hud::LogLevel::Chat => {
|
hud::LogLevel::Chat => {
|
||||||
log.chat(message.into(), chat.talker.name.clone().unwrap_or("".to_string()));
|
log.chat(
|
||||||
|
message.into(),
|
||||||
|
chat.talker.name.clone().unwrap_or("".to_string()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
hud::LogLevel::Info => {
|
hud::LogLevel::Info => {
|
||||||
log.info(message.into());
|
log.info(message.into());
|
||||||
|
@ -712,18 +728,23 @@ pub fn handle_chat_events(
|
||||||
log.warning(message.into());
|
log.warning(message.into());
|
||||||
}
|
}
|
||||||
hud::LogLevel::Always => {
|
hud::LogLevel::Always => {
|
||||||
log.add(message.into(),
|
log.add(
|
||||||
|
message.into(),
|
||||||
chat.talker.name.clone().unwrap_or("".to_string()),
|
chat.talker.name.clone().unwrap_or("".to_string()),
|
||||||
hud::LogLevel::Always);
|
hud::LogLevel::Always,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chat.timer = now + ((message.len() as f32).max(CHAT_SPEED_MIN_LEN) * TALKER_SPEED_FACTOR * chat.talker.talking_speed / settings.chat_speed) as f64;
|
chat.timer = now
|
||||||
|
+ ((message.len() as f32).max(CHAT_SPEED_MIN_LEN)
|
||||||
|
* TALKER_SPEED_FACTOR
|
||||||
|
* chat.talker.talking_speed
|
||||||
|
/ settings.chat_speed) as f64;
|
||||||
|
|
||||||
let sfx = audio::str2sfx(sound);
|
let sfx = audio::str2sfx(sound);
|
||||||
ew_sfx.send(audio::PlaySfxEvent(sfx));
|
ew_sfx.send(audio::PlaySfxEvent(sfx));
|
||||||
}
|
}
|
||||||
ChatEvent::SpawnChoice(replytext, _key, goto, nowait, condition) => {
|
ChatEvent::SpawnChoice(replytext, _key, goto, nowait, condition) => 'out: {
|
||||||
'out: {
|
|
||||||
if let Some(condition) = condition {
|
if let Some(condition) = condition {
|
||||||
if !vars.evaluate_condition(condition, &chat.talker.actor_id) {
|
if !vars.evaluate_condition(condition, &chat.talker.actor_id) {
|
||||||
break 'out;
|
break 'out;
|
||||||
|
@ -735,14 +756,13 @@ pub fn handle_chat_events(
|
||||||
text: replytext.into(),
|
text: replytext.into(),
|
||||||
key: choice_key,
|
key: choice_key,
|
||||||
goto: goto.clone(),
|
goto: goto.clone(),
|
||||||
}
|
},
|
||||||
));
|
));
|
||||||
choice_key += 1;
|
choice_key += 1;
|
||||||
if !nowait {
|
if !nowait {
|
||||||
chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64;
|
chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ChatEvent::RunScript(script) => {
|
ChatEvent::RunScript(script) => {
|
||||||
ew_chatscript.send(ChatScriptEvent(script.clone()));
|
ew_chatscript.send(ChatScriptEvent(script.clone()));
|
||||||
}
|
}
|
||||||
|
@ -794,7 +814,14 @@ fn handle_reply_keys(
|
||||||
pub fn handle_chat_scripts(
|
pub fn handle_chat_scripts(
|
||||||
mut er_chatscript: EventReader<ChatScriptEvent>,
|
mut er_chatscript: EventReader<ChatScriptEvent>,
|
||||||
mut q_actor: Query<(&mut actor::Actor, &mut actor::Suit), Without<actor::Player>>,
|
mut q_actor: Query<(&mut actor::Actor, &mut actor::Suit), Without<actor::Player>>,
|
||||||
mut q_player: Query<(&mut actor::Actor, &mut actor::Suit, &mut actor::ExperiencesGForce), With<actor::Player>>,
|
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 q_playercam: Query<(&mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
||||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||||
|
@ -815,12 +842,12 @@ pub fn handle_chat_scripts(
|
||||||
|
|
||||||
// Process the script
|
// Process the script
|
||||||
match name {
|
match name {
|
||||||
"refilloxygen" => if let Ok(mut amount) = param1.to_string().parse::<f32>() {
|
"refilloxygen" => {
|
||||||
|
if let Ok(mut amount) = param1.to_string().parse::<f32>() {
|
||||||
for (_, mut suit, _) in q_player.iter_mut() {
|
for (_, mut suit, _) in q_player.iter_mut() {
|
||||||
if param2.is_empty() {
|
if param2.is_empty() {
|
||||||
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
|
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let mut found_other = false;
|
let mut found_other = false;
|
||||||
info!("param2={}", param2);
|
info!("param2={}", param2);
|
||||||
for (other_actor, mut other_suit) in q_actor.iter_mut() {
|
for (other_actor, mut other_suit) in q_actor.iter_mut() {
|
||||||
|
@ -833,7 +860,8 @@ pub fn handle_chat_scripts(
|
||||||
.clamp(0.0, other_suit.oxygen)
|
.clamp(0.0, other_suit.oxygen)
|
||||||
.clamp(0.0, suit.oxygen_max - suit.oxygen);
|
.clamp(0.0, suit.oxygen_max - suit.oxygen);
|
||||||
other_suit.oxygen = other_suit.oxygen - amount;
|
other_suit.oxygen = other_suit.oxygen - amount;
|
||||||
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
|
suit.oxygen =
|
||||||
|
(suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -845,6 +873,7 @@ pub fn handle_chat_scripts(
|
||||||
} else {
|
} else {
|
||||||
error!("Invalid parameter for command `{}`: `{}`", name, param1);
|
error!("Invalid parameter for command `{}`: `{}`", name, param1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
"repairsuit" => {
|
"repairsuit" => {
|
||||||
ew_achievement.send(game::AchievementEvent::RepairSuit);
|
ew_achievement.send(game::AchievementEvent::RepairSuit);
|
||||||
for (_, mut suit, _) in q_player.iter_mut() {
|
for (_, mut suit, _) in q_player.iter_mut() {
|
||||||
|
@ -854,21 +883,23 @@ pub fn handle_chat_scripts(
|
||||||
"cryotrip" => {
|
"cryotrip" => {
|
||||||
if param1.is_empty() {
|
if param1.is_empty() {
|
||||||
error!("Chat script cryotrip needs a parameter");
|
error!("Chat script cryotrip needs a parameter");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() {
|
if let Ok((mut pos, mut v)) = q_playercam.get_single_mut() {
|
||||||
let busstop = match param1 {
|
let busstop = match param1 {
|
||||||
"serenity" => Some("busstopclippy"),
|
"serenity" => Some("busstopclippy"),
|
||||||
"farview" => Some("busstopclippy2"),
|
"farview" => Some("busstopclippy2"),
|
||||||
"metisprime" => Some("busstopclippy3"),
|
"metisprime" => Some("busstopclippy3"),
|
||||||
_ => None
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(station) = busstop {
|
if let Some(station) = busstop {
|
||||||
if let Some(target) = id2pos.0.get(&station.to_string()) {
|
if let Some(target) = id2pos.0.get(&station.to_string()) {
|
||||||
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
pos.0 = *target + DVec3::new(0.0, -1000.0, 0.0);
|
||||||
v.0 = DVec3::ZERO;
|
v.0 = DVec3::ZERO;
|
||||||
} else {
|
} else {
|
||||||
error!("Could not determine position of actor with ID: '{}'", station);
|
error!(
|
||||||
|
"Could not determine position of actor with ID: '{}'",
|
||||||
|
station
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("Invalid destination for cryotrip chat script: '{}'", param1);
|
error!("Invalid destination for cryotrip chat script: '{}'", param1);
|
||||||
|
|
280
src/cmd.rs
|
@ -11,13 +11,14 @@
|
||||||
// This module populates the world with actors as defined in "defs.txt"
|
// This module populates the world with actors as defined in "defs.txt"
|
||||||
|
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
|
||||||
use crate::prelude::*;
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
pub const ID_SPECIAL_PLAYERCAM: &str = "PLAYERCAMERA";
|
||||||
pub const ID_EARTH: &str = "earth";
|
pub const ID_EARTH: &str = "earth";
|
||||||
pub const ID_SOL: &str = "sol";
|
pub const ID_SOL: &str = "sol";
|
||||||
pub const ID_JUPITER: &str = "jupiter";
|
pub const ID_JUPITER: &str = "jupiter";
|
||||||
|
@ -28,14 +29,18 @@ impl Plugin for CmdPlugin {
|
||||||
app.add_systems(Startup, load_defs);
|
app.add_systems(Startup, load_defs);
|
||||||
app.add_systems(Update, spawn_entities);
|
app.add_systems(Update, spawn_entities);
|
||||||
app.add_systems(Update, process_mesh);
|
app.add_systems(Update, process_mesh);
|
||||||
app.add_systems(PreUpdate, hide_colliders
|
app.add_systems(
|
||||||
.run_if(any_with_component::<NeedsSceneColliderRemoved>));
|
PreUpdate,
|
||||||
|
hide_colliders.run_if(any_with_component::<NeedsSceneColliderRemoved>),
|
||||||
|
);
|
||||||
app.add_event::<SpawnEvent>();
|
app.add_event::<SpawnEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)] pub struct NeedsSceneColliderRemoved;
|
#[derive(Component)]
|
||||||
#[derive(Event)] pub struct SpawnEvent(ParserState);
|
pub struct NeedsSceneColliderRemoved;
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct SpawnEvent(ParserState);
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
enum DefClass {
|
enum DefClass {
|
||||||
Actor,
|
Actor,
|
||||||
|
@ -80,6 +85,7 @@ struct ParserState {
|
||||||
has_ring: bool,
|
has_ring: bool,
|
||||||
wants_maxrotation: Option<f64>,
|
wants_maxrotation: Option<f64>,
|
||||||
wants_maxvelocity: Option<f64>,
|
wants_maxvelocity: Option<f64>,
|
||||||
|
wants_tolookat_id: Option<String>,
|
||||||
collider_is_mesh: bool,
|
collider_is_mesh: bool,
|
||||||
collider_is_one_mesh_of_scene: bool,
|
collider_is_one_mesh_of_scene: bool,
|
||||||
thrust_forward: f32,
|
thrust_forward: f32,
|
||||||
|
@ -136,6 +142,7 @@ impl Default for ParserState {
|
||||||
has_ring: false,
|
has_ring: false,
|
||||||
wants_maxrotation: None,
|
wants_maxrotation: None,
|
||||||
wants_maxvelocity: None,
|
wants_maxvelocity: None,
|
||||||
|
wants_tolookat_id: None,
|
||||||
collider_is_mesh: false,
|
collider_is_mesh: false,
|
||||||
collider_is_one_mesh_of_scene: false,
|
collider_is_one_mesh_of_scene: false,
|
||||||
thrust_forward: default_engine.thrust_forward,
|
thrust_forward: default_engine.thrust_forward,
|
||||||
|
@ -157,11 +164,11 @@ impl Default for ParserState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_defs(
|
pub fn load_defs(mut ew_spawn: EventWriter<SpawnEvent>) {
|
||||||
mut ew_spawn: EventWriter<SpawnEvent>,
|
|
||||||
) {
|
|
||||||
let re1 = Regex::new(r"^\s*([a-z_-]+)\s+(.*)$").unwrap();
|
let re1 = Regex::new(r"^\s*([a-z_-]+)\s+(.*)$").unwrap();
|
||||||
let re2 = Regex::new("\"([^\"]*)\"|(-?[0-9]+[0-9e-]*(?:\\.[0-9e-]+)?)|([a-zA-Z_-][a-zA-Z0-9_-]*)").unwrap();
|
let re2 =
|
||||||
|
Regex::new("\"([^\"]*)\"|(-?[0-9]+[0-9e-]*(?:\\.[0-9e-]+)?)|([a-zA-Z_-][a-zA-Z0-9_-]*)")
|
||||||
|
.unwrap();
|
||||||
let defs_string = include_str!("data/defs.txt");
|
let defs_string = include_str!("data/defs.txt");
|
||||||
let mut lines = defs_string.lines();
|
let mut lines = defs_string.lines();
|
||||||
let mut state = ParserState::default();
|
let mut state = ParserState::default();
|
||||||
|
@ -181,9 +188,11 @@ pub fn load_defs(
|
||||||
if let Some(caps) = caps {
|
if let Some(caps) = caps {
|
||||||
command = caps.get(1).unwrap().as_str();
|
command = caps.get(1).unwrap().as_str();
|
||||||
parameters = caps.get(2).unwrap().as_str();
|
parameters = caps.get(2).unwrap().as_str();
|
||||||
}
|
} else {
|
||||||
else {
|
error!(
|
||||||
error!("Failed to read regex captures in line {}: `{}`", line_nr, line);
|
"Failed to read regex captures in line {}: `{}`",
|
||||||
|
line_nr, line
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +215,69 @@ pub fn load_defs(
|
||||||
debug!("Registering name: {}", name);
|
debug!("Registering name: {}", name);
|
||||||
state.name = Some(name.to_string());
|
state.name = Some(name.to_string());
|
||||||
}
|
}
|
||||||
|
["template", "person"] => {
|
||||||
|
// command: collider handcrafted
|
||||||
|
state.collider_is_one_mesh_of_scene = true;
|
||||||
|
|
||||||
|
// command: alive yes
|
||||||
|
state.is_alive = true;
|
||||||
|
state.is_lifeform = true;
|
||||||
|
state.is_suited = true;
|
||||||
|
|
||||||
|
// command: oxygen 0.864
|
||||||
|
state.is_lifeform = true;
|
||||||
|
state.is_suited = true;
|
||||||
|
state.oxygen = nature::OXY_D;
|
||||||
|
|
||||||
|
// command: engine monopropellant
|
||||||
|
state.engine_type = actor::EngineType::Monopropellant;
|
||||||
|
|
||||||
|
// command: thrust 1.2 1 1 14 1.5
|
||||||
|
state.thrust_forward = 1.2;
|
||||||
|
state.thrust_back = 1.0;
|
||||||
|
state.thrust_sideways = 1.0;
|
||||||
|
state.reaction_wheels = 14.0;
|
||||||
|
state.warmup_seconds = 1.5;
|
||||||
|
|
||||||
|
// command: wants maxrotation 0
|
||||||
|
state.wants_maxrotation = Some(0.0);
|
||||||
|
// command: wants maxvelocity 0
|
||||||
|
state.wants_maxvelocity = Some(0.0);
|
||||||
|
|
||||||
|
// command: pointofinterest yes
|
||||||
|
state.is_point_of_interest = true;
|
||||||
|
|
||||||
|
// command: density 200
|
||||||
|
state.density = 200.0;
|
||||||
|
|
||||||
|
// command: angularmomentum 0 0 0
|
||||||
|
state.angular_momentum = DVec3::ZERO;
|
||||||
|
}
|
||||||
|
["template", "clippy"] => {
|
||||||
|
// command: angularmomentum 0 0 0
|
||||||
|
state.angular_momentum = DVec3::ZERO;
|
||||||
|
|
||||||
|
// command: wants maxrotation 0
|
||||||
|
state.wants_maxrotation = Some(0.0);
|
||||||
|
// command: wants maxvelocity 0
|
||||||
|
state.wants_maxvelocity = Some(0.0);
|
||||||
|
|
||||||
|
// command: thrust 15 6 3 400 0.5
|
||||||
|
state.thrust_forward = 15.0;
|
||||||
|
state.thrust_back = 6.0;
|
||||||
|
state.thrust_sideways = 3.0;
|
||||||
|
state.reaction_wheels = 400.0;
|
||||||
|
state.warmup_seconds = 0.5;
|
||||||
|
|
||||||
|
// command: scale 3
|
||||||
|
state.model_scale = 3.0;
|
||||||
|
|
||||||
|
// command: pronoun it
|
||||||
|
state.pronoun = Some("it".to_string());
|
||||||
|
|
||||||
|
// command: pointofinterest yes
|
||||||
|
state.is_point_of_interest = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Parsing actors
|
// Parsing actors
|
||||||
["actor", x, y, z, model] => {
|
["actor", x, y, z, model] => {
|
||||||
|
@ -214,10 +286,10 @@ pub fn load_defs(
|
||||||
state.class = DefClass::Actor;
|
state.class = DefClass::Actor;
|
||||||
state.model = Some(model.to_string());
|
state.model = Some(model.to_string());
|
||||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>())
|
||||||
|
{
|
||||||
state.pos = DVec3::new(x_float, y_float, z_float);
|
state.pos = DVec3::new(x_float, y_float, z_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse coordinates as floats in def: {line}");
|
error!("Can't parse coordinates as floats in def: {line}");
|
||||||
state = ParserState::default();
|
state = ParserState::default();
|
||||||
continue;
|
continue;
|
||||||
|
@ -228,10 +300,10 @@ pub fn load_defs(
|
||||||
state = ParserState::default();
|
state = ParserState::default();
|
||||||
state.class = DefClass::Actor;
|
state.class = DefClass::Actor;
|
||||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>())
|
||||||
|
{
|
||||||
state.pos = DVec3::new(x_float, y_float, z_float);
|
state.pos = DVec3::new(x_float, y_float, z_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse coordinates as floats in def: {line}");
|
error!("Can't parse coordinates as floats in def: {line}");
|
||||||
state = ParserState::default();
|
state = ParserState::default();
|
||||||
continue;
|
continue;
|
||||||
|
@ -244,8 +316,7 @@ pub fn load_defs(
|
||||||
if let Ok(r) = radius_str.parse::<f64>() {
|
if let Ok(r) = radius_str.parse::<f64>() {
|
||||||
state.orbit_distance = Some(r);
|
state.orbit_distance = Some(r);
|
||||||
state.orbit_object_id = Some(object_id.to_string());
|
state.orbit_object_id = Some(object_id.to_string());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -254,8 +325,7 @@ pub fn load_defs(
|
||||||
if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) {
|
if let (Ok(r), Ok(phase)) = (radius_str.parse::<f64>(), phase_str.parse::<f64>()) {
|
||||||
state.orbit_distance = Some(r);
|
state.orbit_distance = Some(r);
|
||||||
state.orbit_phase = Some(phase * PI * 2.0);
|
state.orbit_phase = Some(phase * PI * 2.0);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -265,12 +335,10 @@ pub fn load_defs(
|
||||||
let offset_radians = 2.0 * PI * value_float;
|
let offset_radians = 2.0 * PI * value_float;
|
||||||
if let Some(phase_radians) = state.orbit_phase {
|
if let Some(phase_radians) = state.orbit_phase {
|
||||||
state.orbit_phase = Some(phase_radians + offset_radians);
|
state.orbit_phase = Some(phase_radians + offset_radians);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
state.orbit_phase = Some(offset_radians);
|
state.orbit_phase = Some(offset_radians);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -286,6 +354,9 @@ pub fn load_defs(
|
||||||
state.is_lifeform = true;
|
state.is_lifeform = true;
|
||||||
state.is_suited = true;
|
state.is_suited = true;
|
||||||
}
|
}
|
||||||
|
["alive", "no"] => {
|
||||||
|
state.is_alive = false;
|
||||||
|
}
|
||||||
["vehicle", "yes"] => {
|
["vehicle", "yes"] => {
|
||||||
state.is_vehicle = true;
|
state.is_vehicle = true;
|
||||||
}
|
}
|
||||||
|
@ -312,8 +383,7 @@ pub fn load_defs(
|
||||||
state.is_lifeform = true;
|
state.is_lifeform = true;
|
||||||
state.is_suited = true;
|
state.is_suited = true;
|
||||||
state.oxygen = amount;
|
state.oxygen = amount;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -327,8 +397,7 @@ pub fn load_defs(
|
||||||
["scale", scale] => {
|
["scale", scale] => {
|
||||||
if let Ok(scale_float) = scale.parse::<f32>() {
|
if let Ok(scale_float) = scale.parse::<f32>() {
|
||||||
state.model_scale = scale_float;
|
state.model_scale = scale_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -336,8 +405,7 @@ pub fn load_defs(
|
||||||
["rotationx", rotation_x] => {
|
["rotationx", rotation_x] => {
|
||||||
if let Ok(rotation_x_float) = rotation_x.parse::<f32>() {
|
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(rotation_x_float.to_radians());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -345,8 +413,7 @@ pub fn load_defs(
|
||||||
["rotationy", rotation_y] => {
|
["rotationy", rotation_y] => {
|
||||||
if let Ok(rotation_y_float) = rotation_y.parse::<f32>() {
|
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(rotation_y_float.to_radians());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -354,8 +421,7 @@ pub fn load_defs(
|
||||||
["rotationz", rotation_z] => {
|
["rotationz", rotation_z] => {
|
||||||
if let Ok(rotation_z_float) = rotation_z.parse::<f32>() {
|
if let Ok(rotation_z_float) = rotation_z.parse::<f32>() {
|
||||||
state.rotation *= Quat::from_rotation_z(rotation_z_float.to_radians());
|
state.rotation *= Quat::from_rotation_z(rotation_z_float.to_radians());
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -364,34 +430,45 @@ pub fn load_defs(
|
||||||
if let Ok(rotation_y_float) = rotation_y.parse::<f32>() {
|
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(rotation_y_float.to_radians());
|
||||||
state.axialtilt = rotation_y_float;
|
state.axialtilt = rotation_y_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["velocity", x, y, z] => {
|
["velocity", x, y, z] => {
|
||||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>())
|
||||||
|
{
|
||||||
state.velocity = DVec3::new(x_float, y_float, z_float);
|
state.velocity = DVec3::new(x_float, y_float, z_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["angularmomentum", x, y, z] => {
|
["angularmomentum", x, y, z] => {
|
||||||
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
if let (Ok(x_float), Ok(y_float), Ok(z_float)) =
|
||||||
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>()) {
|
(x.parse::<f64>(), y.parse::<f64>(), z.parse::<f64>())
|
||||||
|
{
|
||||||
state.angular_momentum = DVec3::new(x_float, y_float, z_float);
|
state.angular_momentum = DVec3::new(x_float, y_float, z_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["thrust", forward, back, sideways, reaction_wheels, warmup_time] => {
|
["thrust", forward, back, sideways, reaction_wheels, warmup_time] => {
|
||||||
if let (Ok(forward_float), Ok(back_float), Ok(sideways_float), Ok(reaction_wheels_float), Ok(warmup_time_float)) = (forward.parse::<f32>(), back.parse::<f32>(), sideways.parse::<f32>(), reaction_wheels.parse::<f32>(), warmup_time.parse::<f32>()) {
|
if let (
|
||||||
|
Ok(forward_float),
|
||||||
|
Ok(back_float),
|
||||||
|
Ok(sideways_float),
|
||||||
|
Ok(reaction_wheels_float),
|
||||||
|
Ok(warmup_time_float),
|
||||||
|
) = (
|
||||||
|
forward.parse::<f32>(),
|
||||||
|
back.parse::<f32>(),
|
||||||
|
sideways.parse::<f32>(),
|
||||||
|
reaction_wheels.parse::<f32>(),
|
||||||
|
warmup_time.parse::<f32>(),
|
||||||
|
) {
|
||||||
state.thrust_forward = forward_float;
|
state.thrust_forward = forward_float;
|
||||||
state.thrust_back = back_float;
|
state.thrust_back = back_float;
|
||||||
state.thrust_sideways = sideways_float;
|
state.thrust_sideways = sideways_float;
|
||||||
|
@ -411,8 +488,7 @@ pub fn load_defs(
|
||||||
["health", value] => {
|
["health", value] => {
|
||||||
if let Ok(value_float) = value.parse::<f32>() {
|
if let Ok(value_float) = value.parse::<f32>() {
|
||||||
state.suit_integrity = value_float;
|
state.suit_integrity = value_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -420,8 +496,7 @@ pub fn load_defs(
|
||||||
["density", value] => {
|
["density", value] => {
|
||||||
if let Ok(value_float) = value.parse::<f64>() {
|
if let Ok(value_float) = value.parse::<f64>() {
|
||||||
state.density = value_float;
|
state.density = value_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -432,17 +507,17 @@ pub fn load_defs(
|
||||||
["collider", "sphere", radius] => {
|
["collider", "sphere", radius] => {
|
||||||
if let Ok(radius_float) = radius.parse::<f64>() {
|
if let Ok(radius_float) = radius.parse::<f64>() {
|
||||||
state.collider = Collider::sphere(radius_float);
|
state.collider = Collider::sphere(radius_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
["collider", "capsule", height, radius] => {
|
["collider", "capsule", height, radius] => {
|
||||||
if let (Ok(height_float), Ok(radius_float)) = (height.parse::<f64>(), radius.parse::<f64>()) {
|
if let (Ok(height_float), Ok(radius_float)) =
|
||||||
|
(height.parse::<f64>(), radius.parse::<f64>())
|
||||||
|
{
|
||||||
state.collider = Collider::capsule(height_float, radius_float);
|
state.collider = Collider::capsule(height_float, radius_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -460,8 +535,7 @@ pub fn load_defs(
|
||||||
["camdistance", value] => {
|
["camdistance", value] => {
|
||||||
if let Ok(value_float) = value.parse::<f32>() {
|
if let Ok(value_float) = value.parse::<f32>() {
|
||||||
state.camdistance = value_float;
|
state.camdistance = value_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -471,37 +545,43 @@ pub fn load_defs(
|
||||||
if let Ok(color) = Color::hex(color_hex) {
|
if let Ok(color) = Color::hex(color_hex) {
|
||||||
state.light_color = Some(color);
|
state.light_color = Some(color);
|
||||||
state.light_brightness = brightness_float;
|
state.light_brightness = brightness_float;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse hexadecimal color code: {line}");
|
error!("Can't parse hexadecimal color code: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
["wants", "maxrotation", "none"] => {
|
||||||
|
state.wants_maxrotation = None;
|
||||||
|
}
|
||||||
["wants", "maxrotation", value] => {
|
["wants", "maxrotation", value] => {
|
||||||
// NOTE: requires an engine to slow down velocity
|
// NOTE: requires an engine to slow down velocity
|
||||||
if let Ok(value_float) = value.parse::<f64>() {
|
if let Ok(value_float) = value.parse::<f64>() {
|
||||||
state.wants_maxrotation = Some(value_float);
|
state.wants_maxrotation = Some(value_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
["wants", "maxvelocity", "none"] => {
|
||||||
|
state.wants_maxvelocity = None;
|
||||||
|
}
|
||||||
["wants", "maxvelocity", value] => {
|
["wants", "maxvelocity", value] => {
|
||||||
// NOTE: requires an engine to slow down velocity
|
// NOTE: requires an engine to slow down velocity
|
||||||
if let Ok(value_float) = value.parse::<f64>() {
|
if let Ok(value_float) = value.parse::<f64>() {
|
||||||
state.wants_maxvelocity = Some(value_float);
|
state.wants_maxvelocity = Some(value_float);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
["wants", "lookat", id] => {
|
||||||
|
// NOTE: Will not work if the actor has no engine
|
||||||
|
state.wants_tolookat_id = Some(id.to_string());
|
||||||
|
}
|
||||||
["armodel", asset_name] => {
|
["armodel", asset_name] => {
|
||||||
state.ar_model = Some(asset_name.to_string());
|
state.ar_model = Some(asset_name.to_string());
|
||||||
}
|
}
|
||||||
|
@ -511,8 +591,7 @@ pub fn load_defs(
|
||||||
["only_in_map_at_dist", value, id] => {
|
["only_in_map_at_dist", value, id] => {
|
||||||
if let Ok(value_float) = value.parse::<f64>() {
|
if let Ok(value_float) = value.parse::<f64>() {
|
||||||
state.show_only_in_map_at_distance = Some((value_float, id.to_string()));
|
state.show_only_in_map_at_distance = Some((value_float, id.to_string()));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
error!("Can't parse float: {line}");
|
error!("Can't parse float: {line}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -534,6 +613,7 @@ fn spawn_entities(
|
||||||
mut materials_jupiter: ResMut<Assets<load::JupitersRing>>,
|
mut materials_jupiter: ResMut<Assets<load::JupitersRing>>,
|
||||||
mut id2pos: ResMut<game::Id2Pos>,
|
mut id2pos: ResMut<game::Id2Pos>,
|
||||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||||
|
mut ew_updateavatar: EventWriter<hud::UpdateAvatarEvent>,
|
||||||
settings: Res<var::Settings>,
|
settings: Res<var::Settings>,
|
||||||
) {
|
) {
|
||||||
for state_wrapper in er_spawn.read() {
|
for state_wrapper in er_spawn.read() {
|
||||||
|
@ -543,9 +623,7 @@ fn spawn_entities(
|
||||||
// Preprocessing
|
// Preprocessing
|
||||||
let mut absolute_pos = if let Some(id) = &state.relative_to {
|
let mut absolute_pos = if let Some(id) = &state.relative_to {
|
||||||
match id2pos.0.get(&id.to_string()) {
|
match id2pos.0.get(&id.to_string()) {
|
||||||
Some(pos) => {
|
Some(pos) => state.pos + *pos,
|
||||||
state.pos + *pos
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
error!("Specified `relativeto` command but could not find id `{id}`");
|
error!("Specified `relativeto` command but could not find id `{id}`");
|
||||||
continue;
|
continue;
|
||||||
|
@ -569,7 +647,9 @@ fn spawn_entities(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let orbital_period = nature::simple_orbital_period(mass, r);
|
let orbital_period = nature::simple_orbital_period(mass, r);
|
||||||
phase_radians += if let Ok(epoch) = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
|
phase_radians += if let Ok(epoch) =
|
||||||
|
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
{
|
||||||
let now = epoch.as_secs_f64() + 614533234154.0; // random
|
let now = epoch.as_secs_f64() + 614533234154.0; // random
|
||||||
PI * 2.0 * (now % orbital_period) / orbital_period
|
PI * 2.0 * (now % orbital_period) / orbital_period
|
||||||
} else {
|
} else {
|
||||||
|
@ -579,13 +659,15 @@ fn spawn_entities(
|
||||||
}
|
}
|
||||||
absolute_pos += nature::phase_dist_to_coords(-phase_radians, r);
|
absolute_pos += nature::phase_dist_to_coords(-phase_radians, r);
|
||||||
}
|
}
|
||||||
let scale = Vec3::splat(if state.is_sun {
|
let scale = Vec3::splat(
|
||||||
|
if state.is_sun {
|
||||||
5.0
|
5.0
|
||||||
} else if state.is_moon && settings.large_moons {
|
} else if state.is_moon && settings.large_moons {
|
||||||
3.0
|
3.0
|
||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
} * state.model_scale);
|
} * state.model_scale,
|
||||||
|
);
|
||||||
|
|
||||||
// Spawn the actor
|
// Spawn the actor
|
||||||
let actor_entity;
|
let actor_entity;
|
||||||
|
@ -638,23 +720,24 @@ fn spawn_entities(
|
||||||
actor.insert(ColliderDensity(state.density));
|
actor.insert(ColliderDensity(state.density));
|
||||||
if state.collider_is_mesh {
|
if state.collider_is_mesh {
|
||||||
actor.insert(MassPropertiesBundle::new_computed(
|
actor.insert(MassPropertiesBundle::new_computed(
|
||||||
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
|
&Collider::sphere(0.5 * state.model_scale as f64),
|
||||||
|
state.density,
|
||||||
|
));
|
||||||
actor.insert(AsyncSceneCollider::new(Some(
|
actor.insert(AsyncSceneCollider::new(Some(
|
||||||
ComputedCollider::TriMesh
|
ComputedCollider::TriMesh, //ComputedCollider::ConvexDecomposition(VHACDParameters::default())
|
||||||
//ComputedCollider::ConvexDecomposition(VHACDParameters::default())
|
|
||||||
)));
|
)));
|
||||||
}
|
} else if state.collider_is_one_mesh_of_scene {
|
||||||
else if state.collider_is_one_mesh_of_scene {
|
|
||||||
actor.insert(MassPropertiesBundle::new_computed(
|
actor.insert(MassPropertiesBundle::new_computed(
|
||||||
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
|
&Collider::sphere(0.5 * state.model_scale as f64),
|
||||||
actor.insert(AsyncSceneCollider::new(None)
|
state.density,
|
||||||
|
));
|
||||||
|
actor.insert(
|
||||||
|
AsyncSceneCollider::new(None)
|
||||||
.with_shape_for_name("Collider", ComputedCollider::TriMesh)
|
.with_shape_for_name("Collider", ComputedCollider::TriMesh)
|
||||||
.with_layers_for_name("Collider", CollisionLayers::ALL)
|
.with_layers_for_name("Collider", CollisionLayers::ALL), //.with_density_for_name("Collider", state.density)
|
||||||
//.with_density_for_name("Collider", state.density)
|
|
||||||
);
|
);
|
||||||
actor.insert(NeedsSceneColliderRemoved);
|
actor.insert(NeedsSceneColliderRemoved);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
actor.insert(state.collider.clone());
|
actor.insert(state.collider.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,6 +747,8 @@ fn spawn_entities(
|
||||||
if state.is_player {
|
if state.is_player {
|
||||||
actor.insert(actor::Player);
|
actor.insert(actor::Player);
|
||||||
actor.insert(actor::PlayerCamera);
|
actor.insert(actor::PlayerCamera);
|
||||||
|
actor.insert(hud::AugmentedRealityOverlayBroadcaster);
|
||||||
|
ew_updateavatar.send(hud::UpdateAvatarEvent);
|
||||||
}
|
}
|
||||||
if state.is_sun {
|
if state.is_sun {
|
||||||
let (r, g, b) = nature::star_color_index_to_rgb(0.656);
|
let (r, g, b) = nature::star_color_index_to_rgb(0.656);
|
||||||
|
@ -672,10 +757,7 @@ fn spawn_entities(
|
||||||
unlit: true,
|
unlit: true,
|
||||||
..default()
|
..default()
|
||||||
}));
|
}));
|
||||||
actor.insert((
|
actor.insert((NotShadowCaster, NotShadowReceiver));
|
||||||
NotShadowCaster,
|
|
||||||
NotShadowReceiver,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
if state.is_targeted_on_startup {
|
if state.is_targeted_on_startup {
|
||||||
actor.insert(hud::IsTargeted);
|
actor.insert(hud::IsTargeted);
|
||||||
|
@ -683,7 +765,7 @@ fn spawn_entities(
|
||||||
if let Some((mindist, id)) = &state.show_only_in_map_at_distance {
|
if let Some((mindist, id)) = &state.show_only_in_map_at_distance {
|
||||||
actor.insert(camera::ShowOnlyInMap {
|
actor.insert(camera::ShowOnlyInMap {
|
||||||
min_distance: *mindist,
|
min_distance: *mindist,
|
||||||
distance_to_id: id.clone()
|
distance_to_id: id.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if state.is_player || state.is_vehicle {
|
if state.is_player || state.is_vehicle {
|
||||||
|
@ -714,6 +796,9 @@ fn spawn_entities(
|
||||||
if let Some(value) = state.wants_maxvelocity {
|
if let Some(value) = state.wants_maxvelocity {
|
||||||
actor.insert(actor::WantsMaxVelocity(value));
|
actor.insert(actor::WantsMaxVelocity(value));
|
||||||
}
|
}
|
||||||
|
if let Some(value) = &state.wants_tolookat_id {
|
||||||
|
actor.insert(actor::WantsToLookAt(value.clone()));
|
||||||
|
}
|
||||||
if let Some(color) = state.light_color {
|
if let Some(color) = state.light_color {
|
||||||
actor.insert((
|
actor.insert((
|
||||||
PointLight {
|
PointLight {
|
||||||
|
@ -791,7 +876,7 @@ fn spawn_entities(
|
||||||
},
|
},
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
}
|
},
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -802,6 +887,7 @@ fn spawn_entities(
|
||||||
let mut entitycmd = commands.spawn((
|
let mut entitycmd = commands.spawn((
|
||||||
hud::AugmentedRealityOverlay {
|
hud::AugmentedRealityOverlay {
|
||||||
owner: actor_entity,
|
owner: actor_entity,
|
||||||
|
scale: 1.0,
|
||||||
},
|
},
|
||||||
world::DespawnOnPlayerDeath,
|
world::DespawnOnPlayerDeath,
|
||||||
SpatialBundle {
|
SpatialBundle {
|
||||||
|
@ -837,7 +923,8 @@ fn spawn_entities(
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.has_ring {
|
if state.has_ring {
|
||||||
let ring_radius = state.model_scale * (nature::JUPITER_RING_RADIUS / nature::JUPITER_RADIUS) as f32;
|
let ring_radius = state.model_scale
|
||||||
|
* (nature::JUPITER_RING_RADIUS / nature::JUPITER_RADIUS) as f32;
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
world::DespawnOnPlayerDeath,
|
world::DespawnOnPlayerDeath,
|
||||||
MaterialMeshBundle {
|
MaterialMeshBundle {
|
||||||
|
@ -860,7 +947,9 @@ fn spawn_entities(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_colliders(mut q_mesh: Query<(&mut Visibility, &Name), (Added<Visibility>, With<Handle<Mesh>>)>) {
|
pub fn hide_colliders(
|
||||||
|
mut q_mesh: Query<(&mut Visibility, &Name), (Added<Visibility>, With<Handle<Mesh>>)>,
|
||||||
|
) {
|
||||||
for (mut visibility, name) in &mut q_mesh {
|
for (mut visibility, name) in &mut q_mesh {
|
||||||
if name.as_str() == "Collider" {
|
if name.as_str() == "Collider" {
|
||||||
*visibility = Visibility::Hidden;
|
*visibility = Visibility::Hidden;
|
||||||
|
@ -871,7 +960,12 @@ pub fn hide_colliders(mut q_mesh: Query<(&mut Visibility, &Name), (Added<Visibil
|
||||||
pub fn process_mesh(
|
pub fn process_mesh(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut q_mesh: Query<(Entity, &Name, &Parent), Added<Handle<Mesh>>>,
|
mut q_mesh: Query<(Entity, &Name, &Parent), Added<Handle<Mesh>>>,
|
||||||
q_parents: Query<(Option<&Parent>, Option<&actor::Player>, Option<&NotShadowCaster>, Option<&NotShadowReceiver>)>,
|
q_parents: Query<(
|
||||||
|
Option<&Parent>,
|
||||||
|
Option<&actor::Player>,
|
||||||
|
Option<&NotShadowCaster>,
|
||||||
|
Option<&NotShadowReceiver>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
// Add "PlayerCollider" component to the player's collider mesh entity
|
// Add "PlayerCollider" component to the player's collider mesh entity
|
||||||
for (child_entity, child_name, child_parent) in &mut q_mesh {
|
for (child_entity, child_name, child_parent) in &mut q_mesh {
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
//
|
//
|
||||||
// Various common functions and constants
|
// Various common functions and constants
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub use bevy::math::{DVec3, DQuat};
|
pub use bevy::math::{DQuat, DVec3};
|
||||||
pub use std::f32::consts::PI as PI32;
|
pub use std::f32::consts::PI as PI32;
|
||||||
pub use std::f64::consts::PI;
|
pub use std::f64::consts::PI;
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ pub fn in_shadow(
|
||||||
light_source_r: f64,
|
light_source_r: f64,
|
||||||
shadow_caster_pos: DVec3,
|
shadow_caster_pos: DVec3,
|
||||||
shadow_caster_r: f64,
|
shadow_caster_r: f64,
|
||||||
camera_pos: DVec3
|
camera_pos: DVec3,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if light_source_r < shadow_caster_r {
|
if light_source_r < shadow_caster_r {
|
||||||
error!("common::in_shadow only works if light_source_r > shadow_caster_r");
|
error!("common::in_shadow only works if light_source_r > shadow_caster_r");
|
||||||
|
@ -101,7 +101,19 @@ pub fn in_shadow(
|
||||||
let closest_vector = shadow_caster_to_player - projection;
|
let closest_vector = shadow_caster_to_player - projection;
|
||||||
|
|
||||||
let distance_between_light_and_caster = (shadow_caster_pos - light_source_pos).length();
|
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;
|
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;
|
return closest_vector.length() < max_distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn look_at_quat(from: DVec3, to: DVec3, up: DVec3) -> DQuat {
|
||||||
|
let direction = (to - from).normalize();
|
||||||
|
let right = up.cross(direction).normalize();
|
||||||
|
let corrected_up = direction.cross(right);
|
||||||
|
|
||||||
|
let mat3 = bevy::math::DMat3::from_cols(right, corrected_up, direction);
|
||||||
|
|
||||||
|
DQuat::from_mat3(&mat3)
|
||||||
|
}
|
||||||
|
|
|
@ -257,18 +257,15 @@ actor 0 0 0
|
||||||
|
|
||||||
|
|
||||||
actor 0 593051 0 suitv2
|
actor 0 593051 0 suitv2
|
||||||
|
template person
|
||||||
relativeto jupiter
|
relativeto jupiter
|
||||||
orbit 221900e3 0.338
|
orbit 221900e3 0.338
|
||||||
player yes
|
player yes
|
||||||
id player
|
id player
|
||||||
density 200
|
wants maxvelocity none
|
||||||
collider handcrafted
|
|
||||||
oxygen 0.008
|
oxygen 0.008
|
||||||
health 0.3
|
health 0.3
|
||||||
angularmomentum 0 0 0
|
|
||||||
thrust 1.2 1 1 14 1.5
|
|
||||||
rotationy 135
|
rotationy 135
|
||||||
engine monopropellant
|
|
||||||
|
|
||||||
actor 10 -30 20 cruiser
|
actor 10 -30 20 cruiser
|
||||||
name "Cruiser"
|
name "Cruiser"
|
||||||
|
@ -284,18 +281,12 @@ actor 10 -30 20 cruiser
|
||||||
pointofinterest yes
|
pointofinterest yes
|
||||||
|
|
||||||
actor -55e3 44e3 0 suitv2
|
actor -55e3 44e3 0 suitv2
|
||||||
|
template person
|
||||||
relativeto thebe
|
relativeto thebe
|
||||||
id yuni
|
id yuni
|
||||||
name "Yuni"
|
name "Yuni"
|
||||||
chatid Yuni
|
chatid Yuni
|
||||||
density 200
|
|
||||||
collider handcrafted
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
rotationx 180
|
rotationx 180
|
||||||
engine monopropellant
|
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
pointofinterest yes
|
|
||||||
|
|
||||||
actor 5000 0 -3000 moonlet
|
actor 5000 0 -3000 moonlet
|
||||||
name Moonlet
|
name Moonlet
|
||||||
|
@ -315,26 +306,16 @@ actor 13200 300 -3000 hollow_asteroid
|
||||||
pointofinterest yes
|
pointofinterest yes
|
||||||
angularmomentum 0 0.015 0
|
angularmomentum 0 0.015 0
|
||||||
actor 0 0 0 suitv2
|
actor 0 0 0 suitv2
|
||||||
|
template person
|
||||||
relativeto cultasteroid
|
relativeto cultasteroid
|
||||||
name "Ash"
|
name "Ash"
|
||||||
chatid Ash
|
chatid Ash
|
||||||
alive yes
|
|
||||||
collider handcrafted
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
angularmomentum 0 0 0
|
|
||||||
pronoun they
|
pronoun they
|
||||||
actor -8 8 0 suitv2
|
actor -8 8 0 suitv2
|
||||||
|
template person
|
||||||
relativeto cultasteroid
|
relativeto cultasteroid
|
||||||
name "River"
|
name "River"
|
||||||
chatid River
|
chatid River
|
||||||
alive yes
|
|
||||||
collider handcrafted
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
angularmomentum 0 0 0
|
|
||||||
rotationy 54
|
rotationy 54
|
||||||
rotationx -90
|
rotationx -90
|
||||||
pronoun she
|
pronoun she
|
||||||
|
@ -382,10 +363,11 @@ actor -200 -110 1000 satellite
|
||||||
actor 1000 20 300 monolith
|
actor 1000 20 300 monolith
|
||||||
name "Mysterious Monolith 1"
|
name "Mysterious Monolith 1"
|
||||||
relativeto player
|
relativeto player
|
||||||
scale 2
|
scale 4
|
||||||
density 300
|
density 300
|
||||||
rotationx 90
|
rotationx 90
|
||||||
wants maxrotation 0.01
|
wants maxrotation 0.01
|
||||||
|
wants lookat cultasteroid
|
||||||
angularmomentum 0.0 0.0 0.01
|
angularmomentum 0.0 0.0 0.01
|
||||||
thrust 0 0 0 30 1
|
thrust 0 0 0 30 1
|
||||||
collider mesh
|
collider mesh
|
||||||
|
@ -393,10 +375,11 @@ actor 1000 20 300 monolith
|
||||||
actor 10000 2000 -3500 monolith
|
actor 10000 2000 -3500 monolith
|
||||||
name "Mysterious Monolith 2"
|
name "Mysterious Monolith 2"
|
||||||
relativeto player
|
relativeto player
|
||||||
scale 2
|
scale 4
|
||||||
density 300
|
density 300
|
||||||
rotationx 90
|
rotationx 90
|
||||||
wants maxrotation 0.01
|
wants maxrotation 0.01
|
||||||
|
wants lookat cultasteroid
|
||||||
angularmomentum 0.0 0.0 0.01
|
angularmomentum 0.0 0.0 0.01
|
||||||
thrust 0 0 0 30 1
|
thrust 0 0 0 30 1
|
||||||
collider mesh
|
collider mesh
|
||||||
|
@ -404,10 +387,11 @@ actor 10000 2000 -3500 monolith
|
||||||
actor -8000 -1000 -100 monolith
|
actor -8000 -1000 -100 monolith
|
||||||
name "Mysterious Monolith 3"
|
name "Mysterious Monolith 3"
|
||||||
relativeto player
|
relativeto player
|
||||||
scale 2
|
scale 4
|
||||||
density 300
|
density 300
|
||||||
rotationx 90
|
rotationx 90
|
||||||
wants maxrotation 0.01
|
wants maxrotation 0.01
|
||||||
|
wants lookat cultasteroid
|
||||||
angularmomentum 0.0 0.0 0.01
|
angularmomentum 0.0 0.0 0.01
|
||||||
thrust 0 0 0 30 1
|
thrust 0 0 0 30 1
|
||||||
collider mesh
|
collider mesh
|
||||||
|
@ -453,46 +437,35 @@ actor -3300 10 0 pizzeria
|
||||||
scale 0.25
|
scale 0.25
|
||||||
light FF8F4A 5000000
|
light FF8F4A 5000000
|
||||||
actor -33 0 4 clippy
|
actor -33 0 4 clippy
|
||||||
|
template clippy
|
||||||
name "Clippy™ Convenience Companion"
|
name "Clippy™ Convenience Companion"
|
||||||
relativeto pizzeria
|
relativeto pizzeria
|
||||||
armodel clippy_ar
|
armodel clippy_ar
|
||||||
angularmomentum 0 0 0
|
wants lookat PLAYERCAMERA
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
thrust 15 6 3 400 0.5
|
|
||||||
rotationy -126
|
rotationy -126
|
||||||
scale 3
|
|
||||||
chatid SubduedClippy
|
chatid SubduedClippy
|
||||||
pronoun it
|
|
||||||
|
|
||||||
actor -45 -4 -4 suitv2
|
actor -45 -4 -4 suitv2
|
||||||
|
template person
|
||||||
relativeto pizzeria
|
relativeto pizzeria
|
||||||
name "Nox"
|
name "Nox"
|
||||||
chatid PizzaChef
|
chatid PizzaChef
|
||||||
armodel suit_ar_chefhat
|
armodel suit_ar_chefhat
|
||||||
alive yes
|
wants lookat PLAYERCAMERA
|
||||||
collider handcrafted
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
rotationy -90
|
rotationy -90
|
||||||
angularmomentum 0 0 0
|
|
||||||
pronoun he
|
pronoun he
|
||||||
|
|
||||||
actor 30 -12 -40 suitv2
|
actor 30 -12 -40 suitv2
|
||||||
|
template person
|
||||||
relativeto player
|
relativeto player
|
||||||
name Icarus
|
name Icarus
|
||||||
id Icarus
|
id Icarus
|
||||||
chatid Icarus
|
chatid Icarus
|
||||||
alive yes
|
armodel suit_ar_wings
|
||||||
collider handcrafted
|
|
||||||
angularmomentum 0.4 0.2 0.1
|
angularmomentum 0.4 0.2 0.1
|
||||||
|
wants maxrotation 0.5
|
||||||
rotationy 108
|
rotationy 108
|
||||||
rotationx 180
|
rotationx 180
|
||||||
pointofinterest yes
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
wants maxrotation 0.5
|
|
||||||
wants maxvelocity 0
|
|
||||||
pronoun it
|
pronoun it
|
||||||
actor 12 -35 -27 lightorb
|
actor 12 -35 -27 lightorb
|
||||||
name "Light Orb"
|
name "Light Orb"
|
||||||
|
@ -516,29 +489,24 @@ actor 30 -12 -40 suitv2
|
||||||
light FF8F4A 5000000
|
light FF8F4A 5000000
|
||||||
|
|
||||||
actor -300 0 40 suitv2
|
actor -300 0 40 suitv2
|
||||||
|
template person
|
||||||
relativeto player
|
relativeto player
|
||||||
id Drifter
|
id Drifter
|
||||||
name "梓涵"
|
name "梓涵"
|
||||||
chatid Drifter
|
chatid Drifter
|
||||||
|
alive no
|
||||||
oxygen 0.08
|
oxygen 0.08
|
||||||
pointofinterest yes
|
|
||||||
collider handcrafted
|
|
||||||
pronoun she
|
pronoun she
|
||||||
|
|
||||||
actor 100 -18000 2000 clippy
|
actor 100 -18000 2000 clippy
|
||||||
|
template clippy
|
||||||
relativeto "player"
|
relativeto "player"
|
||||||
id "busstopclippy"
|
id "busstopclippy"
|
||||||
name "StarTrans Clippy™ Serenity Station"
|
name "StarTrans Clippy™ Serenity Station"
|
||||||
armodel clippy_ar
|
armodel clippy_ar
|
||||||
angularmomentum 0 0 0
|
wants lookat PLAYERCAMERA
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
thrust 15 6 3 400 0.5
|
|
||||||
pointofinterest yes
|
|
||||||
rotationy -90
|
rotationy -90
|
||||||
scale 3
|
|
||||||
chatid ClippyTransSerenity
|
chatid ClippyTransSerenity
|
||||||
pronoun it
|
|
||||||
|
|
||||||
actor 60 0 0 "orb_busstop"
|
actor 60 0 0 "orb_busstop"
|
||||||
name "StarTrans Bus Stop: Serenity Station"
|
name "StarTrans Bus Stop: Serenity Station"
|
||||||
|
@ -667,29 +635,21 @@ actor 100 -18000 2000 clippy
|
||||||
scale 5
|
scale 5
|
||||||
|
|
||||||
actor 8 20 0 suitv2
|
actor 8 20 0 suitv2
|
||||||
|
template person
|
||||||
relativeto "busstopclippy"
|
relativeto "busstopclippy"
|
||||||
name "Rudy"
|
name "Rudy"
|
||||||
wants maxrotation 0.2
|
|
||||||
wants maxvelocity 0
|
|
||||||
thrust 1.2 1 1 20 1.5
|
|
||||||
collider capsule 1 0.5
|
|
||||||
chatid NPCinCryoStasis
|
chatid NPCinCryoStasis
|
||||||
pronoun he
|
pronoun he
|
||||||
|
|
||||||
actor -184971e3 149410e3 -134273e3 clippy
|
actor -184971e3 149410e3 -134273e3 clippy
|
||||||
|
template clippy
|
||||||
relativeto jupiter
|
relativeto jupiter
|
||||||
id "busstopclippy2"
|
id "busstopclippy2"
|
||||||
name "StarTrans Clippy™ Farview Station"
|
name "StarTrans Clippy™ Farview Station"
|
||||||
armodel clippy_ar
|
armodel clippy_ar
|
||||||
angularmomentum 0 0 0
|
wants lookat PLAYERCAMERA
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
thrust 15 6 3 400 0.5
|
|
||||||
pointofinterest yes
|
|
||||||
rotationy -90
|
rotationy -90
|
||||||
scale 3
|
|
||||||
chatid ClippyTransFarview
|
chatid ClippyTransFarview
|
||||||
pronoun it
|
|
||||||
|
|
||||||
actor 60 0 0 "orb_busstop"
|
actor 60 0 0 "orb_busstop"
|
||||||
name "StarTrans Bus Stop: Farview Station"
|
name "StarTrans Bus Stop: Farview Station"
|
||||||
|
@ -819,21 +779,16 @@ actor -184971e3 149410e3 -134273e3 clippy
|
||||||
|
|
||||||
|
|
||||||
actor 0 -44e3 0 clippy
|
actor 0 -44e3 0 clippy
|
||||||
|
template clippy
|
||||||
relativeto jupiter
|
relativeto jupiter
|
||||||
id "busstopclippy3"
|
id "busstopclippy3"
|
||||||
name "StarTrans Clippy™ Metis Prime Station"
|
name "StarTrans Clippy™ Metis Prime Station"
|
||||||
armodel clippy_ar
|
armodel clippy_ar
|
||||||
|
wants lookat PLAYERCAMERA
|
||||||
orbitaround jupiter 128000e3
|
orbitaround jupiter 128000e3
|
||||||
orbit_phase_offset -0.002
|
orbit_phase_offset -0.002
|
||||||
angularmomentum 0 0 0
|
|
||||||
wants maxrotation 0
|
|
||||||
wants maxvelocity 0
|
|
||||||
thrust 15 6 3 400 0.5
|
|
||||||
pointofinterest yes
|
|
||||||
rotationy -90
|
rotationy -90
|
||||||
scale 3
|
|
||||||
chatid ClippyTransMetis
|
chatid ClippyTransMetis
|
||||||
pronoun it
|
|
||||||
|
|
||||||
actor 60 0 0 "orb_busstop"
|
actor 60 0 0 "orb_busstop"
|
||||||
name "StarTrans Bus Stop: Metis Prime Station"
|
name "StarTrans Bus Stop: Metis Prime Station"
|
||||||
|
|
|
@ -11,9 +11,7 @@ AWSD/Shift/Ctrl: Move
|
||||||
J/K/U/L/I/O: Rotate
|
J/K/U/L/I/O: Rotate
|
||||||
F11: Fullscreen
|
F11: Fullscreen
|
||||||
Tab: Toggle Augmented Reality
|
Tab: Toggle Augmented Reality
|
||||||
|
Left click: Target objects (in AR only)
|
||||||
Augmented Reality only:
|
|
||||||
Left click: Target objects
|
|
||||||
Right click: Zoom
|
Right click: Zoom
|
||||||
|
|
||||||
Cheats:
|
Cheats:
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
# + ▀████████████████████████████████████████████████████▀
|
# + ▀████████████████████████████████████████████████████▀
|
||||||
#
|
#
|
||||||
# User preferences for the game OutFly.
|
# User preferences for the game OutFly.
|
||||||
# See https://codeberg.org/hut/outfly
|
# See https://codeberg.org/outfly/outfly
|
||||||
|
|
||||||
# fullscreen_mode may be "borderless", "legacy", or "sized"
|
# fullscreen_mode may be "borderless", "legacy", or "sized"
|
||||||
fullscreen_mode = "borderless"
|
fullscreen_mode = "borderless"
|
||||||
|
|
108
src/game.rs
|
@ -11,10 +11,10 @@
|
||||||
// This module handles player input, and coordinates interplay between other modules
|
// This module handles player input, and coordinates interplay between other modules
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::pbr::ExtendedMaterial;
|
use bevy::pbr::ExtendedMaterial;
|
||||||
|
use bevy::prelude::*;
|
||||||
use bevy::scene::SceneInstance;
|
use bevy::scene::SceneInstance;
|
||||||
use bevy::window::{Window, WindowMode, PrimaryWindow};
|
use bevy::window::{PrimaryWindow, Window, WindowMode};
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
@ -26,21 +26,31 @@ impl Plugin for GamePlugin {
|
||||||
app.add_systems(PostUpdate, handle_game_event);
|
app.add_systems(PostUpdate, handle_game_event);
|
||||||
app.add_systems(PreUpdate, handle_player_death);
|
app.add_systems(PreUpdate, handle_player_death);
|
||||||
app.add_systems(PostUpdate, update_id2pos);
|
app.add_systems(PostUpdate, update_id2pos);
|
||||||
app.add_systems(Update, handle_achievement_event.run_if(on_event::<AchievementEvent>()));
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
handle_achievement_event.run_if(on_event::<AchievementEvent>()),
|
||||||
|
);
|
||||||
app.add_systems(Update, check_achievements);
|
app.add_systems(Update, check_achievements);
|
||||||
app.insert_resource(Id2Pos(HashMap::new()));
|
app.insert_resource(Id2Pos(HashMap::new()));
|
||||||
app.insert_resource(var::AchievementTracker::default());
|
app.insert_resource(var::AchievementTracker::default());
|
||||||
app.insert_resource(AchievementCheckTimer(
|
app.insert_resource(var::Settings::default());
|
||||||
Timer::from_seconds(1.0, TimerMode::Repeating)));
|
app.insert_resource(var::GameVars::default());
|
||||||
|
app.insert_resource(AchievementCheckTimer(Timer::from_seconds(
|
||||||
|
1.0,
|
||||||
|
TimerMode::Repeating,
|
||||||
|
)));
|
||||||
app.add_event::<PlayerDiesEvent>();
|
app.add_event::<PlayerDiesEvent>();
|
||||||
app.add_event::<GameEvent>();
|
app.add_event::<GameEvent>();
|
||||||
app.add_event::<AchievementEvent>();
|
app.add_event::<AchievementEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)] pub struct PlayerDiesEvent(pub actor::DamageType);
|
#[derive(Event)]
|
||||||
#[derive(Resource)] pub struct Id2Pos(pub HashMap<String, DVec3>);
|
pub struct PlayerDiesEvent(pub actor::DamageType);
|
||||||
#[derive(Resource)] pub struct AchievementCheckTimer(pub Timer);
|
#[derive(Resource)]
|
||||||
|
pub struct Id2Pos(pub HashMap<String, DVec3>);
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct AchievementCheckTimer(pub Timer);
|
||||||
|
|
||||||
#[derive(Event)]
|
#[derive(Event)]
|
||||||
pub enum AchievementEvent {
|
pub enum AchievementEvent {
|
||||||
|
@ -125,7 +135,7 @@ pub fn handle_game_event(
|
||||||
let current_state = window.mode != WindowMode::Windowed;
|
let current_state = window.mode != WindowMode::Windowed;
|
||||||
window.mode = match turn.to_bool(current_state) {
|
window.mode = match turn.to_bool(current_state) {
|
||||||
true => opt.window_mode_fullscreen,
|
true => opt.window_mode_fullscreen,
|
||||||
false => WindowMode::Windowed
|
false => WindowMode::Windowed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,8 +147,8 @@ pub fn handle_game_event(
|
||||||
settings.third_person = turn.to_bool(settings.third_person);
|
settings.third_person = turn.to_bool(settings.third_person);
|
||||||
}
|
}
|
||||||
GameEvent::SetRotationStabilizer(turn) => {
|
GameEvent::SetRotationStabilizer(turn) => {
|
||||||
settings.rotation_stabilizer_active
|
settings.rotation_stabilizer_active =
|
||||||
= turn.to_bool(settings.rotation_stabilizer_active);
|
turn.to_bool(settings.rotation_stabilizer_active);
|
||||||
}
|
}
|
||||||
GameEvent::SetShadows(turn) => {
|
GameEvent::SetShadows(turn) => {
|
||||||
settings.shadows_sun = turn.to_bool(settings.shadows_sun);
|
settings.shadows_sun = turn.to_bool(settings.shadows_sun);
|
||||||
|
@ -148,8 +158,11 @@ pub fn handle_game_event(
|
||||||
}
|
}
|
||||||
GameEvent::Achievement(name) => {
|
GameEvent::Achievement(name) => {
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Achieve));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Achieve));
|
||||||
log.add(format!("Achievement accomplished: {name}!"),
|
log.add(
|
||||||
"".to_string(), hud::LogLevel::Achievement);
|
format!("Achievement accomplished: {name}!"),
|
||||||
|
"".to_string(),
|
||||||
|
hud::LogLevel::Achievement,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +178,7 @@ fn handle_player_death(
|
||||||
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
|
||||||
mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>,
|
mut ew_deathscreen: EventWriter<menu::DeathScreenEvent>,
|
||||||
mut log: ResMut<hud::Log>,
|
mut log: ResMut<hud::Log>,
|
||||||
|
mut gamevars: ResMut<var::GameVars>,
|
||||||
mut settings: ResMut<Settings>,
|
mut settings: ResMut<Settings>,
|
||||||
) {
|
) {
|
||||||
for death in er_playerdies.read() {
|
for death in er_playerdies.read() {
|
||||||
|
@ -172,6 +186,7 @@ fn handle_player_death(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
settings.reset_player_settings();
|
settings.reset_player_settings();
|
||||||
|
gamevars.reset();
|
||||||
active_asteroids.0.clear();
|
active_asteroids.0.clear();
|
||||||
for entity in &q_noscenes {
|
for entity in &q_noscenes {
|
||||||
cmd.entity(entity).despawn();
|
cmd.entity(entity).despawn();
|
||||||
|
@ -234,9 +249,15 @@ fn handle_player_death(
|
||||||
|
|
||||||
fn handle_cheats(
|
fn handle_cheats(
|
||||||
key_input: Res<ButtonInput<KeyCode>>,
|
key_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut q_player: Query<(&Transform, &mut Position, &mut LinearVelocity), With<actor::PlayerCamera>>,
|
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>>,
|
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>)>,
|
q_target: Query<
|
||||||
|
(&Transform, &Position, Option<&LinearVelocity>),
|
||||||
|
(With<hud::IsTargeted>, Without<actor::PlayerCamera>),
|
||||||
|
>,
|
||||||
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
mut ew_playerdies: EventWriter<PlayerDiesEvent>,
|
||||||
mut settings: ResMut<Settings>,
|
mut settings: ResMut<Settings>,
|
||||||
id2pos: Res<Id2Pos>,
|
id2pos: Res<Id2Pos>,
|
||||||
|
@ -266,9 +287,15 @@ fn handle_cheats(
|
||||||
gforce.ignore_gforce_seconds = 1.0;
|
gforce.ignore_gforce_seconds = 1.0;
|
||||||
v.0 = DVec3::ZERO;
|
v.0 = DVec3::ZERO;
|
||||||
}
|
}
|
||||||
if key_input.pressed(settings.key_cheat_speed) || key_input.pressed(settings.key_cheat_speed_backward) {
|
if key_input.pressed(settings.key_cheat_speed)
|
||||||
|
|| key_input.pressed(settings.key_cheat_speed_backward)
|
||||||
|
{
|
||||||
gforce.ignore_gforce_seconds = 1.0;
|
gforce.ignore_gforce_seconds = 1.0;
|
||||||
let sign = if key_input.pressed(settings.key_cheat_speed) { 1.0 } else { -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 dv = DVec3::from(trans.rotation * Vec3::new(0.0, 0.0, sign * boost));
|
||||||
let current_speed = v.0.length();
|
let current_speed = v.0.length();
|
||||||
let next_speed = (v.0 + dv).length();
|
let next_speed = (v.0 + dv).length();
|
||||||
|
@ -278,7 +305,8 @@ fn handle_cheats(
|
||||||
}
|
}
|
||||||
if key_input.just_pressed(settings.key_cheat_teleport) {
|
if key_input.just_pressed(settings.key_cheat_teleport) {
|
||||||
if let Ok((transform, target_pos, target_v)) = q_target.get_single() {
|
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();
|
let offset: DVec3 =
|
||||||
|
4.0 * (**pos - **target_pos).normalize() * transform.scale.as_dvec3();
|
||||||
pos.0 = **target_pos + offset;
|
pos.0 = **target_pos + offset;
|
||||||
if let Some(target_v) = target_v {
|
if let Some(target_v) = target_v {
|
||||||
*v = target_v.clone();
|
*v = target_v.clone();
|
||||||
|
@ -323,10 +351,7 @@ fn handle_cheats(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_id2pos(
|
fn update_id2pos(mut id2pos: ResMut<Id2Pos>, q_id: Query<(&Position, &actor::Identifier)>) {
|
||||||
mut id2pos: ResMut<Id2Pos>,
|
|
||||||
q_id: Query<(&Position, &actor::Identifier)>,
|
|
||||||
) {
|
|
||||||
id2pos.0.clear();
|
id2pos.0.clear();
|
||||||
for (pos, id) in &q_id {
|
for (pos, id) in &q_id {
|
||||||
id2pos.0.insert(id.0.clone(), pos.0);
|
id2pos.0.insert(id.0.clone(), pos.0);
|
||||||
|
@ -337,7 +362,9 @@ fn debug(
|
||||||
settings: Res<var::Settings>,
|
settings: Res<var::Settings>,
|
||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut extended_materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>>,
|
mut extended_materials: ResMut<
|
||||||
|
Assets<ExtendedMaterial<StandardMaterial, load::AsteroidSurface>>,
|
||||||
|
>,
|
||||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||||
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
materials: Query<(Entity, Option<&Name>, &Handle<Mesh>)>,
|
||||||
) {
|
) {
|
||||||
|
@ -370,7 +397,9 @@ fn handle_achievement_event(
|
||||||
}
|
}
|
||||||
AchievementEvent::InJupitersShadow => {
|
AchievementEvent::InJupitersShadow => {
|
||||||
if !tracker.in_jupiters_shadow {
|
if !tracker.in_jupiters_shadow {
|
||||||
ew_game.send(GameEvent::Achievement("Eclipse the sun with Jupiter".into()));
|
ew_game.send(GameEvent::Achievement(
|
||||||
|
"Eclipse the sun with Jupiter".into(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
tracker.in_jupiters_shadow = true;
|
tracker.in_jupiters_shadow = true;
|
||||||
}
|
}
|
||||||
|
@ -415,13 +444,32 @@ fn check_achievements(
|
||||||
mut ew_achievement: EventWriter<AchievementEvent>,
|
mut ew_achievement: EventWriter<AchievementEvent>,
|
||||||
mut timer: ResMut<AchievementCheckTimer>,
|
mut timer: ResMut<AchievementCheckTimer>,
|
||||||
) {
|
) {
|
||||||
if !timer.0.tick(time.delta()).just_finished() { return; }
|
if !timer.0.tick(time.delta()).just_finished() {
|
||||||
let pos_player = if let Ok(pos) = q_player.get_single() { pos } else { return; };
|
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 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,
|
let shadowed = in_shadow(
|
||||||
*pos_jupiter, nature::JUPITER_RADIUS, **pos_player);
|
*pos_sun,
|
||||||
|
nature::SOL_RADIUS,
|
||||||
|
*pos_jupiter,
|
||||||
|
nature::JUPITER_RADIUS,
|
||||||
|
**pos_player,
|
||||||
|
);
|
||||||
|
|
||||||
if shadowed {
|
if shadowed {
|
||||||
ew_achievement.send(AchievementEvent::InJupitersShadow);
|
ew_achievement.send(AchievementEvent::InJupitersShadow);
|
||||||
|
|
500
src/hud.rs
|
@ -11,9 +11,10 @@
|
||||||
// This module manages the heads-up display and augmented reality overlays.
|
// This module manages the heads-up display and augmented reality overlays.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
|
||||||
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
use bevy::pbr::{NotShadowCaster, NotShadowReceiver};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
|
use bevy::scene::SceneInstance;
|
||||||
use bevy::transform::TransformSystem;
|
use bevy::transform::TransformSystem;
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -41,20 +42,34 @@ pub const DASHBOARD_DEF: &[(Dashboard, &str)] = &[
|
||||||
(Dashboard::Radioactivity, "radioactivity"),
|
(Dashboard::Radioactivity, "radioactivity"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Player avatars: [(Avatar, model name, scale, in-game name)]
|
||||||
|
pub const PLAYER_AR_AVATARS: &[(Avatar, &str, f32, &str)] = &[
|
||||||
|
(Avatar::None, "", 1.0, "No Avatar"),
|
||||||
|
(Avatar::ChefHat, "suit_ar_chefhat", 1.0, "Chef Hat"),
|
||||||
|
(Avatar::Wings, "suit_ar_wings", 1.0, "Wings"),
|
||||||
|
(Avatar::Asteroid, "asteroid2", 1.2, "Asteroid"),
|
||||||
|
];
|
||||||
|
|
||||||
pub struct HudPlugin;
|
pub struct HudPlugin;
|
||||||
impl Plugin for HudPlugin {
|
impl Plugin for HudPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup);
|
app.add_systems(Startup, setup);
|
||||||
app.add_systems(Update, (
|
app.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
update_hud,
|
update_hud,
|
||||||
update_dashboard,
|
update_dashboard,
|
||||||
update_speedometer,
|
update_speedometer,
|
||||||
update_gauges,
|
update_gauges,
|
||||||
handle_input.run_if(in_control),
|
handle_input.run_if(in_control),
|
||||||
handle_target_event,
|
handle_target_event,
|
||||||
));
|
),
|
||||||
app.add_systems(PostUpdate, (
|
);
|
||||||
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
(
|
||||||
update_overlay_visibility,
|
update_overlay_visibility,
|
||||||
|
update_avatar.run_if(on_event::<UpdateAvatarEvent>()),
|
||||||
update_ar_overlays
|
update_ar_overlays
|
||||||
.after(camera::position_to_transform)
|
.after(camera::position_to_transform)
|
||||||
.in_set(sync::SyncSet::PositionToTransform),
|
.in_set(sync::SyncSet::PositionToTransform),
|
||||||
|
@ -65,7 +80,8 @@ impl Plugin for HudPlugin {
|
||||||
.after(PhysicsSet::Sync)
|
.after(PhysicsSet::Sync)
|
||||||
.after(camera::apply_input_to_player)
|
.after(camera::apply_input_to_player)
|
||||||
.before(TransformSystem::TransformPropagate),
|
.before(TransformSystem::TransformPropagate),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
app.insert_resource(AugmentedRealityState {
|
app.insert_resource(AugmentedRealityState {
|
||||||
overlays_visible: false,
|
overlays_visible: false,
|
||||||
});
|
});
|
||||||
|
@ -73,29 +89,52 @@ impl Plugin for HudPlugin {
|
||||||
logs: VecDeque::with_capacity(LOG_MAX),
|
logs: VecDeque::with_capacity(LOG_MAX),
|
||||||
needs_rerendering: true,
|
needs_rerendering: true,
|
||||||
});
|
});
|
||||||
app.insert_resource(FPSUpdateTimer(
|
app.insert_resource(FPSUpdateTimer(Timer::from_seconds(
|
||||||
Timer::from_seconds(HUD_REFRESH_TIME, TimerMode::Repeating)));
|
HUD_REFRESH_TIME,
|
||||||
|
TimerMode::Repeating,
|
||||||
|
)));
|
||||||
app.add_event::<TargetEvent>();
|
app.add_event::<TargetEvent>();
|
||||||
|
app.add_event::<UpdateAvatarEvent>();
|
||||||
app.add_event::<UpdateOverlayVisibility>();
|
app.add_event::<UpdateOverlayVisibility>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)] pub struct TargetEvent(pub Option<Entity>);
|
#[derive(Event)]
|
||||||
#[derive(Event)] pub struct UpdateOverlayVisibility;
|
pub struct TargetEvent(pub Option<Entity>);
|
||||||
#[derive(Component)] struct NodeHud;
|
#[derive(Event)]
|
||||||
#[derive(Component)] struct NodeConsole;
|
pub struct UpdateOverlayVisibility;
|
||||||
#[derive(Component)] struct NodeChoiceText;
|
#[derive(Event)]
|
||||||
#[derive(Component)] struct NodeSpeedometerText;
|
pub struct UpdateAvatarEvent;
|
||||||
#[derive(Component)] struct NodeCurrentChatLine;
|
#[derive(Component)]
|
||||||
#[derive(Component)] struct Reticule;
|
struct NodeHud;
|
||||||
#[derive(Component)] struct Speedometer;
|
#[derive(Component)]
|
||||||
#[derive(Component)] struct Speedometer2;
|
struct NodeConsole;
|
||||||
#[derive(Component)] struct GaugeLength(f32);
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct ToggleableHudElement;
|
struct NodeChoiceText;
|
||||||
#[derive(Component)] pub struct ToggleableHudMapElement;
|
#[derive(Component)]
|
||||||
#[derive(Component)] struct Selectagon;
|
struct NodeSpeedometerText;
|
||||||
#[derive(Component)] pub struct IsTargeted;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct PointOfInterestMarker(pub Entity);
|
struct NodeCurrentChatLine;
|
||||||
|
#[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)]
|
||||||
|
struct PlayerAvatar;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct IsTargeted;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct PointOfInterestMarker(pub Entity);
|
||||||
|
|
||||||
#[derive(Component, Debug, Copy, Clone)]
|
#[derive(Component, Debug, Copy, Clone)]
|
||||||
pub enum Dashboard {
|
pub enum Dashboard {
|
||||||
|
@ -119,15 +158,24 @@ pub struct AugmentedRealityState {
|
||||||
pub overlays_visible: bool,
|
pub overlays_visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)] pub struct AugmentedRealityOverlayBroadcaster;
|
#[derive(Component)]
|
||||||
|
pub struct AugmentedRealityOverlayBroadcaster;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct AugmentedRealityOverlay {
|
pub struct AugmentedRealityOverlay {
|
||||||
pub owner: Entity,
|
pub owner: Entity,
|
||||||
|
pub scale: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct FPSUpdateTimer(Timer);
|
struct FPSUpdateTimer(Timer);
|
||||||
|
|
||||||
|
pub enum Avatar {
|
||||||
|
None,
|
||||||
|
ChefHat,
|
||||||
|
Wings,
|
||||||
|
Asteroid,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum LogLevel {
|
pub enum LogLevel {
|
||||||
Achievement,
|
Achievement,
|
||||||
Always,
|
Always,
|
||||||
|
@ -155,8 +203,7 @@ impl Message {
|
||||||
pub fn format(&self) -> String {
|
pub fn format(&self) -> String {
|
||||||
if self.sender.is_empty() {
|
if self.sender.is_empty() {
|
||||||
return self.text.clone() + "\n";
|
return self.text.clone() + "\n";
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return format!("{}: {}\n", self.sender, self.text);
|
return format!("{}: {}\n", self.sender, self.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,11 +215,15 @@ pub struct IsClickable {
|
||||||
pub pronoun: Option<String>,
|
pub pronoun: Option<String>,
|
||||||
pub distance: Option<f64>,
|
pub distance: Option<f64>,
|
||||||
}
|
}
|
||||||
impl Default for IsClickable { fn default() -> Self { Self {
|
impl Default for IsClickable {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
name: None,
|
name: None,
|
||||||
pronoun: None,
|
pronoun: None,
|
||||||
distance: None,
|
distance: None,
|
||||||
}}}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct Log {
|
pub struct Log {
|
||||||
|
@ -277,33 +328,33 @@ pub fn setup(
|
||||||
let mut bundle_fps = TextBundle::from_sections([
|
let mut bundle_fps = TextBundle::from_sections([
|
||||||
TextSection::new("", style), // Target
|
TextSection::new("", style), // Target
|
||||||
TextSection::new("", style_fps), // Frames per second
|
TextSection::new("", style_fps), // Frames per second
|
||||||
]).with_style(Style {
|
])
|
||||||
|
.with_style(Style {
|
||||||
position_type: PositionType::Absolute,
|
position_type: PositionType::Absolute,
|
||||||
top: Val::VMin(2.0),
|
top: Val::VMin(2.0),
|
||||||
left: Val::VMin(3.0),
|
left: Val::VMin(3.0),
|
||||||
..default()
|
..default()
|
||||||
}).with_text_justify(JustifyText::Left);
|
})
|
||||||
|
.with_text_justify(JustifyText::Left);
|
||||||
bundle_fps.visibility = visibility;
|
bundle_fps.visibility = visibility;
|
||||||
commands.spawn((
|
commands.spawn((NodeHud, ToggleableHudElement, bundle_fps));
|
||||||
NodeHud,
|
|
||||||
ToggleableHudElement,
|
|
||||||
bundle_fps,
|
|
||||||
));
|
|
||||||
|
|
||||||
// Add Console
|
// Add Console
|
||||||
// This one is intentionally NOT a ToggleableHudElement. Instead, console entries
|
// This one is intentionally NOT a ToggleableHudElement. Instead, console entries
|
||||||
// are filtered based on whether the hud is active or not. LogLevel::Always is
|
// are filtered based on whether the hud is active or not. LogLevel::Always is
|
||||||
// even shown when hud is inactive.
|
// even shown when hud is inactive.
|
||||||
let bundle_chatbox = TextBundle::from_sections((0..LOG_MAX_ROWS).map(|_|
|
let bundle_chatbox = TextBundle::from_sections(
|
||||||
TextSection::new("", style_console.clone()))
|
(0..LOG_MAX_ROWS).map(|_| TextSection::new("", style_console.clone())),
|
||||||
).with_style(Style {
|
)
|
||||||
|
.with_style(Style {
|
||||||
position_type: PositionType::Absolute,
|
position_type: PositionType::Absolute,
|
||||||
top: Val::VMin(0.0),
|
top: Val::VMin(0.0),
|
||||||
right: Val::VMin(0.0),
|
right: Val::VMin(0.0),
|
||||||
..default()
|
..default()
|
||||||
}).with_text_justify(JustifyText::Right);
|
})
|
||||||
commands.spawn((
|
.with_text_justify(JustifyText::Right);
|
||||||
NodeBundle {
|
commands
|
||||||
|
.spawn((NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Percent(50.0),
|
width: Val::Percent(50.0),
|
||||||
align_items: AlignItems::Start,
|
align_items: AlignItems::Start,
|
||||||
|
@ -313,24 +364,23 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},))
|
||||||
)).with_children(|parent| {
|
.with_children(|parent| {
|
||||||
parent.spawn((
|
parent.spawn((bundle_chatbox, NodeConsole));
|
||||||
bundle_chatbox,
|
|
||||||
NodeConsole,
|
|
||||||
));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add Reticule
|
// Add Reticule
|
||||||
let reticule_handle: Handle<Image> = asset_server.load("sprites/reticule4.png");
|
let reticule_handle: Handle<Image> = asset_server.load("sprites/reticule4.png");
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: style_centered(),
|
style: style_centered(),
|
||||||
visibility,
|
visibility,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
ImageBundle {
|
ImageBundle {
|
||||||
image: UiImage::new(reticule_handle),
|
image: UiImage::new(reticule_handle),
|
||||||
|
@ -356,10 +406,11 @@ pub fn setup(
|
||||||
let icon_size = 24.0;
|
let icon_size = 24.0;
|
||||||
let gauge_bar_padding_left = 4.0;
|
let gauge_bar_padding_left = 4.0;
|
||||||
for (i, (sprite, gauge)) in gauges.iter().enumerate() {
|
for (i, (sprite, gauge)) in gauges.iter().enumerate() {
|
||||||
let bar_length = if i == 0 { 32.0*8.0 } else { 32.0*5.0 };
|
let bar_length = if i == 0 { 32.0 * 8.0 } else { 32.0 * 5.0 };
|
||||||
|
|
||||||
// The bar with variable width
|
// The bar with variable width
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Percent(30.0),
|
width: Val::Percent(30.0),
|
||||||
|
@ -374,7 +425,8 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
|
@ -394,7 +446,8 @@ pub fn setup(
|
||||||
});
|
});
|
||||||
|
|
||||||
// The decorator sprites surrounding the bar
|
// The decorator sprites surrounding the bar
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Percent(30.0),
|
width: Val::Percent(30.0),
|
||||||
|
@ -409,10 +462,10 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
// The gauge symbol
|
// The gauge symbol
|
||||||
builder.spawn((
|
builder.spawn((ImageBundle {
|
||||||
ImageBundle {
|
|
||||||
image: UiImage::new(asset_server.load(sprite.to_string())),
|
image: UiImage::new(asset_server.load(sprite.to_string())),
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Px(icon_size),
|
width: Val::Px(icon_size),
|
||||||
|
@ -421,11 +474,9 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
visibility,
|
visibility,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
// The gauge bar border
|
// The gauge bar border
|
||||||
builder.spawn((
|
builder.spawn((ImageBundle {
|
||||||
ImageBundle {
|
|
||||||
image: UiImage::new(gauges_handle.clone()),
|
image: UiImage::new(gauges_handle.clone()),
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Px(bar_length),
|
width: Val::Px(bar_length),
|
||||||
|
@ -436,19 +487,18 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
visibility,
|
visibility,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Car-Dashboard-Style icons
|
// Car-Dashboard-Style icons
|
||||||
let style_dashboard = Style {
|
let style_dashboard = Style {
|
||||||
width: Val::Px(DASHBOARD_ICON_SIZE),
|
width: Val::Px(DASHBOARD_ICON_SIZE),
|
||||||
height: Val::Px(DASHBOARD_ICON_SIZE),
|
height: Val::Px(DASHBOARD_ICON_SIZE),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Percent(30.0),
|
width: Val::Percent(30.0),
|
||||||
|
@ -463,13 +513,15 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
for (component, filename) in DASHBOARD_DEF {
|
for (component, filename) in DASHBOARD_DEF {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
*component,
|
*component,
|
||||||
ImageBundle {
|
ImageBundle {
|
||||||
image: UiImage::new(asset_server.load(
|
image: UiImage::new(
|
||||||
format!("sprites/dashboard_{}.png", filename))),
|
asset_server.load(format!("sprites/dashboard_{}.png", filename)),
|
||||||
|
),
|
||||||
style: style_dashboard.clone(),
|
style: style_dashboard.clone(),
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -480,7 +532,8 @@ pub fn setup(
|
||||||
|
|
||||||
// Add Speedometer
|
// Add Speedometer
|
||||||
let speedometer_handle: Handle<Image> = asset_server.load("sprites/speedometer.png");
|
let speedometer_handle: Handle<Image> = asset_server.load("sprites/speedometer.png");
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::VMin(0.0),
|
width: Val::VMin(0.0),
|
||||||
|
@ -495,9 +548,9 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
Speedometer,
|
Speedometer,
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
builder.spawn((
|
.with_children(|builder| {
|
||||||
ImageBundle {
|
builder.spawn((ImageBundle {
|
||||||
image: UiImage::new(speedometer_handle),
|
image: UiImage::new(speedometer_handle),
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Vw(SPEEDOMETER_WIDTH),
|
width: Val::Vw(SPEEDOMETER_WIDTH),
|
||||||
|
@ -505,11 +558,11 @@ pub fn setup(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
let speedometer_handle: Handle<Image> = asset_server.load("sprites/speedometer_white.png");
|
let speedometer_handle: Handle<Image> = asset_server.load("sprites/speedometer_white.png");
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::VMin(0.0),
|
width: Val::VMin(0.0),
|
||||||
|
@ -524,9 +577,9 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
Speedometer2,
|
Speedometer2,
|
||||||
ToggleableHudElement,
|
ToggleableHudElement,
|
||||||
)).with_children(|builder| {
|
))
|
||||||
builder.spawn((
|
.with_children(|builder| {
|
||||||
ImageBundle {
|
builder.spawn((ImageBundle {
|
||||||
image: UiImage::new(speedometer_handle),
|
image: UiImage::new(speedometer_handle),
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Vw(SPEEDOMETER_WIDTH),
|
width: Val::Vw(SPEEDOMETER_WIDTH),
|
||||||
|
@ -534,19 +587,20 @@ pub fn setup(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
let mut bundle_speedometer_text = TextBundle::from_sections([
|
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 target
|
TextSection::new("", style_speedometer.clone()), // speed relative to target
|
||||||
TextSection::new("", style_speedometer.clone()), // speed relative to orbit
|
TextSection::new("", style_speedometer.clone()), // speed relative to orbit
|
||||||
]).with_style(Style {
|
])
|
||||||
|
.with_style(Style {
|
||||||
position_type: PositionType::Absolute,
|
position_type: PositionType::Absolute,
|
||||||
left: Val::Vw(100.0 - SPEEDOMETER_WIDTH + 2.0),
|
left: Val::Vw(100.0 - SPEEDOMETER_WIDTH + 2.0),
|
||||||
bottom: Val::VMin(4.0),
|
bottom: Val::VMin(4.0),
|
||||||
..default()
|
..default()
|
||||||
}).with_text_justify(JustifyText::Left);
|
})
|
||||||
|
.with_text_justify(JustifyText::Left);
|
||||||
bundle_speedometer_text.visibility = visibility;
|
bundle_speedometer_text.visibility = visibility;
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
NodeSpeedometerText,
|
NodeSpeedometerText,
|
||||||
|
@ -555,7 +609,8 @@ pub fn setup(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Chat "subtitles" and choices
|
// Chat "subtitles" and choices
|
||||||
commands.spawn(NodeBundle {
|
commands
|
||||||
|
.spawn(NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
width: Val::Vw(100.0),
|
width: Val::Vw(100.0),
|
||||||
align_items: AlignItems::Center,
|
align_items: AlignItems::Center,
|
||||||
|
@ -566,13 +621,12 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
}).with_children(|builder| {
|
})
|
||||||
|
.with_children(|builder| {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
TextBundle {
|
TextBundle {
|
||||||
text: Text {
|
text: Text {
|
||||||
sections: vec![
|
sections: vec![TextSection::new("", style_conversations)],
|
||||||
TextSection::new("", style_conversations),
|
|
||||||
],
|
|
||||||
justify: JustifyText::Center,
|
justify: JustifyText::Center,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
|
@ -588,8 +642,8 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
NodeCurrentChatLine,
|
NodeCurrentChatLine,
|
||||||
));
|
));
|
||||||
let choice_sections = (0..MAX_CHOICES).map(|_|
|
let choice_sections =
|
||||||
TextSection::new("", style_choices.clone()));
|
(0..MAX_CHOICES).map(|_| TextSection::new("", style_choices.clone()));
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
TextBundle {
|
TextBundle {
|
||||||
text: Text {
|
text: Text {
|
||||||
|
@ -628,23 +682,17 @@ fn update_dashboard(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let player = q_player.get_single();
|
let player = q_player.get_single();
|
||||||
if player.is_err() { return; }
|
if player.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let (suit, pos) = player.unwrap();
|
let (suit, pos) = player.unwrap();
|
||||||
|
|
||||||
for (mut vis, icon) in &mut q_dashboard {
|
for (mut vis, icon) in &mut q_dashboard {
|
||||||
*vis = bool2vis(match icon {
|
*vis = bool2vis(match icon {
|
||||||
Dashboard::Flashlight => {
|
Dashboard::Flashlight => settings.flashlight_active,
|
||||||
settings.flashlight_active
|
Dashboard::Leak => suit.integrity < 0.5,
|
||||||
}
|
Dashboard::RotationStabiliser => !settings.rotation_stabilizer_active,
|
||||||
Dashboard::Leak => {
|
Dashboard::CruiseControl => settings.cruise_control_active,
|
||||||
suit.integrity < 0.5
|
|
||||||
}
|
|
||||||
Dashboard::RotationStabiliser => {
|
|
||||||
!settings.rotation_stabilizer_active
|
|
||||||
}
|
|
||||||
Dashboard::CruiseControl => {
|
|
||||||
settings.cruise_control_active
|
|
||||||
}
|
|
||||||
Dashboard::Radioactivity => {
|
Dashboard::Radioactivity => {
|
||||||
if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
|
if let Some(pos_jupiter) = id2pos.0.get(cmd::ID_JUPITER) {
|
||||||
pos_jupiter.distance(pos.0) < 140_000_000.0
|
pos_jupiter.distance(pos.0) < 140_000_000.0
|
||||||
|
@ -675,13 +723,21 @@ fn update_speedometer(
|
||||||
let speedometer_split = 5_000.0;
|
let speedometer_split = 5_000.0;
|
||||||
if let Ok(mut speedometer) = q_speedometer.get_single_mut() {
|
if let Ok(mut speedometer) = q_speedometer.get_single_mut() {
|
||||||
let custom_c = speedometer_split;
|
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::inverse_lorentz_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);
|
let wid = (fraction * SPEEDOMETER_WIDTH).clamp(0.0, 100.0);
|
||||||
speedometer.width = Val::Vw(wid);
|
speedometer.width = Val::Vw(wid);
|
||||||
}
|
}
|
||||||
if let Ok(mut speedometer2) = q_speedometer2.get_single_mut() {
|
if let Ok(mut speedometer2) = q_speedometer2.get_single_mut() {
|
||||||
let custom_c = nature::C - speedometer_split;
|
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::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 wid = (fraction * SPEEDOMETER_WIDTH).clamp(0.0, 100.0);
|
let wid = (fraction * SPEEDOMETER_WIDTH).clamp(0.0, 100.0);
|
||||||
speedometer2.width = Val::Vw(wid);
|
speedometer2.width = Val::Vw(wid);
|
||||||
}
|
}
|
||||||
|
@ -729,7 +785,9 @@ fn update_gauges(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let player = q_player.get_single();
|
let player = q_player.get_single();
|
||||||
if player.is_err() { return; }
|
if player.is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let (hp, suit, battery) = player.unwrap();
|
let (hp, suit, battery) = player.unwrap();
|
||||||
|
|
||||||
for (mut style, mut bg, gauge, len) in &mut q_gauges {
|
for (mut style, mut bg, gauge, len) in &mut q_gauges {
|
||||||
|
@ -741,8 +799,7 @@ fn update_gauges(
|
||||||
};
|
};
|
||||||
if value < 0.5 {
|
if value < 0.5 {
|
||||||
*bg = settings.hud_color_alert.into();
|
*bg = settings.hud_color_alert.into();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
*bg = settings.hud_color.into();
|
*bg = settings.hud_color.into();
|
||||||
}
|
}
|
||||||
style.width = Val::Px(len.0 * value);
|
style.width = Val::Px(len.0 * value);
|
||||||
|
@ -758,9 +815,23 @@ fn update_hud(
|
||||||
q_choices: Query<&chat::Choice>,
|
q_choices: Query<&chat::Choice>,
|
||||||
q_chat: Query<&chat::Chat>,
|
q_chat: Query<&chat::Chat>,
|
||||||
mut q_node_hud: Query<&mut Text, With<NodeHud>>,
|
mut q_node_hud: Query<&mut Text, With<NodeHud>>,
|
||||||
mut q_node_console: Query<&mut Text, (With<NodeConsole>, Without<NodeHud>, Without<NodeChoiceText>)>,
|
mut q_node_console: Query<
|
||||||
mut q_node_choice: Query<&mut Text, (With<NodeChoiceText>, Without<NodeHud>, Without<NodeConsole>)>,
|
&mut Text,
|
||||||
mut q_node_currentline: Query<&mut Text, (With<NodeCurrentChatLine>, Without<NodeHud>, Without<NodeConsole>, Without<NodeChoiceText>)>,
|
(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>,
|
settings: Res<Settings>,
|
||||||
q_target: Query<(&IsClickable, Option<&Position>, Option<&LinearVelocity>), With<IsTargeted>>,
|
q_target: Query<(&IsClickable, Option<&Position>, Option<&LinearVelocity>), With<IsTargeted>>,
|
||||||
) {
|
) {
|
||||||
|
@ -781,41 +852,42 @@ fn update_hud(
|
||||||
let dist_scalar: f64;
|
let dist_scalar: f64;
|
||||||
let mut target_multiple = false;
|
let mut target_multiple = false;
|
||||||
let mut target_error = false;
|
let mut target_error = false;
|
||||||
if let Ok((IsClickable { distance: Some(dist), .. }, _, _)) = q_target.get_single() {
|
if let Ok((
|
||||||
|
IsClickable {
|
||||||
|
distance: Some(dist),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
)) = q_target.get_single()
|
||||||
|
{
|
||||||
dist_scalar = *dist;
|
dist_scalar = *dist;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let target: Option<DVec3>;
|
let target: Option<DVec3>;
|
||||||
if let Ok((_, Some(targetpos), _)) = q_target.get_single() {
|
if let Ok((_, Some(targetpos), _)) = q_target.get_single() {
|
||||||
target = Some(targetpos.0);
|
target = Some(targetpos.0);
|
||||||
}
|
} else if q_target.is_empty() {
|
||||||
else if q_target.is_empty() {
|
|
||||||
target = Some(DVec3::new(0.0, 0.0, 0.0));
|
target = Some(DVec3::new(0.0, 0.0, 0.0));
|
||||||
}
|
} else if q_target.iter().len() > 1 {
|
||||||
else if q_target.iter().len() > 1 {
|
|
||||||
target_multiple = true;
|
target_multiple = true;
|
||||||
target = None;
|
target = None;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
target_error = true;
|
target_error = true;
|
||||||
target = None;
|
target = None;
|
||||||
}
|
}
|
||||||
if let Some(target_pos) = target {
|
if let Some(target_pos) = target {
|
||||||
let dist = pos.0 - target_pos;
|
let dist = pos.0 - target_pos;
|
||||||
dist_scalar = dist.length();
|
dist_scalar = dist.length();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
dist_scalar = 0.0;
|
dist_scalar = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if target_multiple {
|
if target_multiple {
|
||||||
text.sections[0].value = "ERROR: MULTIPLE TARGETS\n\n".to_string();
|
text.sections[0].value = "ERROR: MULTIPLE TARGETS\n\n".to_string();
|
||||||
}
|
} else if target_error {
|
||||||
else if target_error {
|
|
||||||
text.sections[0].value = "ERROR: FAILED TO AQUIRE TARGET\n\n".to_string();
|
text.sections[0].value = "ERROR: FAILED TO AQUIRE TARGET\n\n".to_string();
|
||||||
}
|
} else if let Ok((clickable, _, _)) = q_target.get_single() {
|
||||||
else if let Ok((clickable, _, _)) = q_target.get_single() {
|
|
||||||
let distance = if dist_scalar.is_nan() {
|
let distance = if dist_scalar.is_nan() {
|
||||||
"UNKNOWN".to_string()
|
"UNKNOWN".to_string()
|
||||||
} else if dist_scalar != 0.0 {
|
} else if dist_scalar != 0.0 {
|
||||||
|
@ -829,9 +901,9 @@ fn update_hud(
|
||||||
} else {
|
} else {
|
||||||
"".to_string()
|
"".to_string()
|
||||||
};
|
};
|
||||||
text.sections[0].value = format!("Target: {target_name}\n{pronoun}Distance: {distance}\n\n");
|
text.sections[0].value =
|
||||||
}
|
format!("Target: {target_name}\n{pronoun}Distance: {distance}\n\n");
|
||||||
else {
|
} else {
|
||||||
text.sections[0].value = "".to_string();
|
text.sections[0].value = "".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,15 +934,17 @@ fn update_hud(
|
||||||
|
|
||||||
// Chat Log and System Log
|
// Chat Log and System Log
|
||||||
let logfilter = if settings.hud_active {
|
let logfilter = if settings.hud_active {
|
||||||
|_msg: &&Message| { true }
|
|_msg: &&Message| true
|
||||||
} else {
|
} else {
|
||||||
|msg: &&Message| { match msg.level {
|
|msg: &&Message| match msg.level {
|
||||||
LogLevel::Always => true,
|
LogLevel::Always => true,
|
||||||
LogLevel::Achievement => true,
|
LogLevel::Achievement => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}}
|
}
|
||||||
};
|
};
|
||||||
let messages: Vec<&Message> = log.logs.iter()
|
let messages: Vec<&Message> = log
|
||||||
|
.logs
|
||||||
|
.iter()
|
||||||
.filter(logfilter)
|
.filter(logfilter)
|
||||||
.rev()
|
.rev()
|
||||||
.take(LOG_MAX_ROWS)
|
.take(LOG_MAX_ROWS)
|
||||||
|
@ -893,11 +967,13 @@ fn update_hud(
|
||||||
|
|
||||||
// Display the last chat line as "subtitles"
|
// Display the last chat line as "subtitles"
|
||||||
if !q_chat.is_empty() {
|
if !q_chat.is_empty() {
|
||||||
let messages: Vec<&Message> = log.logs.iter()
|
let messages: Vec<&Message> = log
|
||||||
.filter(|msg: &&Message| { match msg.level {
|
.logs
|
||||||
|
.iter()
|
||||||
|
.filter(|msg: &&Message| match msg.level {
|
||||||
LogLevel::Chat => true,
|
LogLevel::Chat => true,
|
||||||
_ => false
|
_ => false,
|
||||||
}})
|
})
|
||||||
.rev()
|
.rev()
|
||||||
.take(1)
|
.take(1)
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -963,7 +1039,15 @@ fn handle_input(
|
||||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
mut ew_target: EventWriter<TargetEvent>,
|
mut ew_target: EventWriter<TargetEvent>,
|
||||||
mut ew_game: EventWriter<GameEvent>,
|
mut ew_game: EventWriter<GameEvent>,
|
||||||
q_objects: Query<(Entity, &Transform), (With<IsClickable>, Without<IsTargeted>, Without<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
q_objects: Query<
|
||||||
|
(Entity, &Transform),
|
||||||
|
(
|
||||||
|
With<IsClickable>,
|
||||||
|
Without<IsTargeted>,
|
||||||
|
Without<actor::PlayerDrivesThis>,
|
||||||
|
Without<actor::Player>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
q_camera: Query<&Transform, With<Camera>>,
|
q_camera: Query<&Transform, With<Camera>>,
|
||||||
) {
|
) {
|
||||||
if keyboard_input.just_pressed(settings.key_togglehud) {
|
if keyboard_input.just_pressed(settings.key_togglehud) {
|
||||||
|
@ -973,10 +1057,11 @@ fn handle_input(
|
||||||
if settings.hud_active && mouse_input.just_pressed(settings.key_selectobject) {
|
if settings.hud_active && mouse_input.just_pressed(settings.key_selectobject) {
|
||||||
if let Ok(camtrans) = q_camera.get_single() {
|
if let Ok(camtrans) = q_camera.get_single() {
|
||||||
let objects: Vec<(Entity, &Transform)> = q_objects.iter().collect();
|
let objects: Vec<(Entity, &Transform)> = q_objects.iter().collect();
|
||||||
if let (Some(new_target), _dist) = camera::find_closest_target::<Entity>(objects, camtrans) {
|
if let (Some(new_target), _dist) =
|
||||||
|
camera::find_closest_target::<Entity>(objects, camtrans)
|
||||||
|
{
|
||||||
ew_target.send(TargetEvent(Some(new_target)));
|
ew_target.send(TargetEvent(Some(new_target)));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
ew_target.send(TargetEvent(None));
|
ew_target.send(TargetEvent(None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1103,10 @@ fn handle_target_event(
|
||||||
|
|
||||||
fn update_target_selectagon(
|
fn update_target_selectagon(
|
||||||
settings: Res<Settings>,
|
settings: Res<Settings>,
|
||||||
mut q_selectagon: Query<(&mut Transform, &mut Visibility), (With<Selectagon>, Without<IsTargeted>, Without<Camera>)>,
|
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_target: Query<&Transform, (With<IsTargeted>, Without<Camera>, Without<Selectagon>)>,
|
||||||
q_camera: Query<&Transform, (With<Camera>, Without<IsTargeted>, Without<Selectagon>)>,
|
q_camera: Query<&Transform, (With<Camera>, Without<IsTargeted>, Without<Selectagon>)>,
|
||||||
) {
|
) {
|
||||||
|
@ -1030,7 +1118,9 @@ fn update_target_selectagon(
|
||||||
if let Ok((mut selectagon_trans, mut selectagon_vis)) = q_selectagon.get_single_mut() {
|
if let Ok((mut selectagon_trans, mut selectagon_vis)) = q_selectagon.get_single_mut() {
|
||||||
if let Ok(target_trans) = q_target.get_single() {
|
if let Ok(target_trans) = q_target.get_single() {
|
||||||
match *selectagon_vis {
|
match *selectagon_vis {
|
||||||
Visibility::Hidden => { *selectagon_vis = Visibility::Visible; },
|
Visibility::Hidden => {
|
||||||
|
*selectagon_vis = Visibility::Visible;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
selectagon_trans.translation = target_trans.translation;
|
selectagon_trans.translation = target_trans.translation;
|
||||||
|
@ -1038,25 +1128,36 @@ fn update_target_selectagon(
|
||||||
selectagon_trans.look_at(camera_trans.translation, camera_trans.up().into());
|
selectagon_trans.look_at(camera_trans.translation, camera_trans.up().into());
|
||||||
|
|
||||||
// Enlarge Selectagon to a minimum angular diameter
|
// Enlarge Selectagon to a minimum angular diameter
|
||||||
let (angular_diameter, _, _) = camera::calc_angular_diameter(
|
let (angular_diameter, _, _) =
|
||||||
&selectagon_trans, camera_trans);
|
camera::calc_angular_diameter(&selectagon_trans, camera_trans);
|
||||||
let min_angular_diameter = 2.0f32.to_radians();
|
let min_angular_diameter = 2.0f32.to_radians();
|
||||||
if angular_diameter < min_angular_diameter {
|
if angular_diameter < min_angular_diameter {
|
||||||
selectagon_trans.scale *= min_angular_diameter / angular_diameter;
|
selectagon_trans.scale *= min_angular_diameter / angular_diameter;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
match *selectagon_vis {
|
match *selectagon_vis {
|
||||||
Visibility::Hidden => {},
|
Visibility::Hidden => {}
|
||||||
_ => { *selectagon_vis = Visibility::Hidden; }
|
_ => {
|
||||||
|
*selectagon_vis = Visibility::Hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_ar_overlays (
|
fn update_ar_overlays(
|
||||||
q_owners: Query<(Entity, &Transform, &Visibility), (With<AugmentedRealityOverlayBroadcaster>, Without<AugmentedRealityOverlay>)>,
|
q_owners: Query<
|
||||||
mut q_overlays: Query<(&mut Transform, &mut Visibility, &mut AugmentedRealityOverlay)>,
|
(Entity, &Transform, &Visibility),
|
||||||
|
(
|
||||||
|
With<AugmentedRealityOverlayBroadcaster>,
|
||||||
|
Without<AugmentedRealityOverlay>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
mut q_overlays: Query<(
|
||||||
|
&mut Transform,
|
||||||
|
&mut Visibility,
|
||||||
|
&mut AugmentedRealityOverlay,
|
||||||
|
)>,
|
||||||
settings: ResMut<Settings>,
|
settings: ResMut<Settings>,
|
||||||
mut state: ResMut<AugmentedRealityState>,
|
mut state: ResMut<AugmentedRealityState>,
|
||||||
) {
|
) {
|
||||||
|
@ -1064,8 +1165,7 @@ fn update_ar_overlays (
|
||||||
if settings.hud_active {
|
if settings.hud_active {
|
||||||
need_activate = !state.overlays_visible;
|
need_activate = !state.overlays_visible;
|
||||||
need_clean = false;
|
need_clean = false;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
need_activate = false;
|
need_activate = false;
|
||||||
need_clean = state.overlays_visible;
|
need_clean = state.overlays_visible;
|
||||||
}
|
}
|
||||||
|
@ -1077,10 +1177,12 @@ fn update_ar_overlays (
|
||||||
for (owner_id, owner_trans, owner_vis) in &q_owners {
|
for (owner_id, owner_trans, owner_vis) in &q_owners {
|
||||||
if owner_id == ar.owner {
|
if owner_id == ar.owner {
|
||||||
*trans = *owner_trans;
|
*trans = *owner_trans;
|
||||||
|
if ar.scale != 1.0 {
|
||||||
|
trans.scale *= ar.scale;
|
||||||
|
}
|
||||||
if need_clean {
|
if need_clean {
|
||||||
*vis = Visibility::Hidden;
|
*vis = Visibility::Hidden;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
*vis = *owner_vis;
|
*vis = *owner_vis;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1090,7 +1192,7 @@ fn update_ar_overlays (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_poi_overlays (
|
fn update_poi_overlays(
|
||||||
mut q_marker: Query<(&mut Transform, &PointOfInterestMarker)>,
|
mut q_marker: Query<(&mut Transform, &PointOfInterestMarker)>,
|
||||||
q_parent: Query<&Transform, Without<PointOfInterestMarker>>,
|
q_parent: Query<&Transform, Without<PointOfInterestMarker>>,
|
||||||
q_camera: Query<&Transform, (With<Camera>, Without<PointOfInterestMarker>)>,
|
q_camera: Query<&Transform, (With<Camera>, Without<PointOfInterestMarker>)>,
|
||||||
|
@ -1105,8 +1207,7 @@ fn update_poi_overlays (
|
||||||
// Enlarge POI marker to a minimum angular diameter
|
// Enlarge POI marker to a minimum angular diameter
|
||||||
trans.translation = parent_trans.translation;
|
trans.translation = parent_trans.translation;
|
||||||
trans.scale = Vec3::splat(1.0);
|
trans.scale = Vec3::splat(1.0);
|
||||||
let (angular_diameter, _, _) = camera::calc_angular_diameter(
|
let (angular_diameter, _, _) = camera::calc_angular_diameter(&trans, camera_trans);
|
||||||
&trans, camera_trans);
|
|
||||||
let min_angular_diameter = 3.0f32.to_radians();
|
let min_angular_diameter = 3.0f32.to_radians();
|
||||||
if angular_diameter < min_angular_diameter {
|
if angular_diameter < min_angular_diameter {
|
||||||
trans.scale *= min_angular_diameter / angular_diameter;
|
trans.scale *= min_angular_diameter / angular_diameter;
|
||||||
|
@ -1118,9 +1219,27 @@ fn update_poi_overlays (
|
||||||
|
|
||||||
fn update_overlay_visibility(
|
fn update_overlay_visibility(
|
||||||
mut q_marker: Query<&mut Visibility, With<PointOfInterestMarker>>,
|
mut q_marker: Query<&mut Visibility, With<PointOfInterestMarker>>,
|
||||||
mut q_hudelement: Query<&mut Visibility, (With<ToggleableHudElement>, Without<PointOfInterestMarker>)>,
|
mut q_hudelement: Query<
|
||||||
mut q_selectagon: Query<&mut Visibility, (With<Selectagon>, Without<ToggleableHudElement>, Without<PointOfInterestMarker>)>,
|
&mut Visibility,
|
||||||
q_target: Query<&IsTargeted, (Without<Camera>, Without<Selectagon>, Without<PointOfInterestMarker>, Without<ToggleableHudElement>)>,
|
(With<ToggleableHudElement>, Without<PointOfInterestMarker>),
|
||||||
|
>,
|
||||||
|
mut q_selectagon: Query<
|
||||||
|
&mut Visibility,
|
||||||
|
(
|
||||||
|
With<Selectagon>,
|
||||||
|
Without<ToggleableHudElement>,
|
||||||
|
Without<PointOfInterestMarker>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
|
q_target: Query<
|
||||||
|
&IsTargeted,
|
||||||
|
(
|
||||||
|
Without<Camera>,
|
||||||
|
Without<Selectagon>,
|
||||||
|
Without<PointOfInterestMarker>,
|
||||||
|
Without<ToggleableHudElement>,
|
||||||
|
),
|
||||||
|
>,
|
||||||
mut ambient_light: ResMut<AmbientLight>,
|
mut ambient_light: ResMut<AmbientLight>,
|
||||||
er_target: EventReader<UpdateOverlayVisibility>,
|
er_target: EventReader<UpdateOverlayVisibility>,
|
||||||
settings: Res<Settings>,
|
settings: Res<Settings>,
|
||||||
|
@ -1128,8 +1247,14 @@ fn update_overlay_visibility(
|
||||||
if er_target.is_empty() {
|
if er_target.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let check = {|check: bool|
|
let check = {
|
||||||
if check { Visibility::Inherited } else { Visibility::Hidden }
|
|check: bool| {
|
||||||
|
if check {
|
||||||
|
Visibility::Inherited
|
||||||
|
} else {
|
||||||
|
Visibility::Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let show_poi = check(settings.hud_active && settings.map_active);
|
let show_poi = check(settings.hud_active && settings.map_active);
|
||||||
let show_hud = check(settings.hud_active);
|
let show_hud = check(settings.hud_active);
|
||||||
|
@ -1150,3 +1275,52 @@ fn update_overlay_visibility(
|
||||||
AMBIENT_LIGHT
|
AMBIENT_LIGHT
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_avatar(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut settings: ResMut<Settings>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
q_avatar: Query<(Entity, &SceneInstance), With<PlayerAvatar>>,
|
||||||
|
q_player: Query<Entity, With<actor::Player>>,
|
||||||
|
mut scene_spawner: ResMut<SceneSpawner>,
|
||||||
|
) {
|
||||||
|
if settings.ar_avatar >= PLAYER_AR_AVATARS.len() {
|
||||||
|
settings.ar_avatar = settings.ar_avatar % PLAYER_AR_AVATARS.len();
|
||||||
|
}
|
||||||
|
let ava = if let Some(ava) = PLAYER_AR_AVATARS.get(settings.ar_avatar) {
|
||||||
|
ava
|
||||||
|
} else {
|
||||||
|
error!("Avatar index out of bounds!");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let model_name = ava.1;
|
||||||
|
let model_scale = ava.2;
|
||||||
|
|
||||||
|
for (entity, sceneinstance) in &q_avatar {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
scene_spawner.despawn_instance(**sceneinstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if model_name.is_empty() {
|
||||||
|
// No avatar selected.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(player_entity) = q_player.get_single() {
|
||||||
|
let mut entitycmd = commands.spawn((
|
||||||
|
hud::AugmentedRealityOverlay {
|
||||||
|
owner: player_entity,
|
||||||
|
scale: model_scale,
|
||||||
|
},
|
||||||
|
world::DespawnOnPlayerDeath,
|
||||||
|
PlayerAvatar,
|
||||||
|
SpatialBundle {
|
||||||
|
visibility: bool2vis(settings.hud_active),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
NotShadowCaster,
|
||||||
|
NotShadowReceiver,
|
||||||
|
));
|
||||||
|
load_asset(model_name, &mut entitycmd, &*asset_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
src/load.rs
|
@ -11,16 +11,18 @@
|
||||||
// This module manages asset loading.
|
// This module manages asset loading.
|
||||||
|
|
||||||
use bevy::ecs::system::EntityCommands;
|
use bevy::ecs::system::EntityCommands;
|
||||||
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
|
|
||||||
use bevy::pbr::{ExtendedMaterial, MaterialExtension, OpaqueRendererMethod};
|
use bevy::pbr::{ExtendedMaterial, MaterialExtension, OpaqueRendererMethod};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::render::render_resource::{AsBindGroup, ShaderRef};
|
||||||
|
|
||||||
pub struct LoadPlugin;
|
pub struct LoadPlugin;
|
||||||
impl Plugin for LoadPlugin {
|
impl Plugin for LoadPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins(MaterialPlugin::<JupitersRing>::default());
|
app.add_plugins(MaterialPlugin::<JupitersRing>::default());
|
||||||
app.add_plugins(MaterialPlugin::<SkyBox>::default());
|
app.add_plugins(MaterialPlugin::<SkyBox>::default());
|
||||||
app.add_plugins(MaterialPlugin::<ExtendedMaterial<StandardMaterial, AsteroidSurface, >>::default());
|
app.add_plugins(MaterialPlugin::<
|
||||||
|
ExtendedMaterial<StandardMaterial, AsteroidSurface>,
|
||||||
|
>::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@ pub fn asset_name_to_path(name: &str) -> &'static str {
|
||||||
match name {
|
match name {
|
||||||
"suitv2" => "models/suit_v2/suit_v2.glb#Scene0",
|
"suitv2" => "models/suit_v2/suit_v2.glb#Scene0",
|
||||||
"suit_ar_chefhat" => "models/suit_v2/ar_chefhat.glb#Scene0",
|
"suit_ar_chefhat" => "models/suit_v2/ar_chefhat.glb#Scene0",
|
||||||
|
"suit_ar_wings" => "models/suit_v2/ar_wings.glb#Scene0",
|
||||||
"asteroid1" => "models/asteroid.glb#Scene0",
|
"asteroid1" => "models/asteroid.glb#Scene0",
|
||||||
"asteroid2" => "models/asteroid2.glb#Scene0",
|
"asteroid2" => "models/asteroid2.glb#Scene0",
|
||||||
"asteroid_lum" => "models/asteroid_lum.glb#Scene0",
|
"asteroid_lum" => "models/asteroid_lum.glb#Scene0",
|
||||||
|
@ -55,19 +58,12 @@ pub fn asset_name_to_path(name: &str) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_asset(
|
pub fn load_asset(name: &str, entity_commands: &mut EntityCommands, asset_server: &AssetServer) {
|
||||||
name: &str,
|
|
||||||
entity_commands: &mut EntityCommands,
|
|
||||||
asset_server: &AssetServer,
|
|
||||||
) {
|
|
||||||
entity_commands.insert(load_scene_by_path(asset_name_to_path(name), asset_server));
|
entity_commands.insert(load_scene_by_path(asset_name_to_path(name), asset_server));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn load_scene_by_path(
|
pub fn load_scene_by_path(path: &str, asset_server: &AssetServer) -> Handle<Scene> {
|
||||||
path: &str,
|
|
||||||
asset_server: &AssetServer
|
|
||||||
) -> Handle<Scene> {
|
|
||||||
let path_string = path.to_string();
|
let path_string = path.to_string();
|
||||||
if let Some(handle) = asset_server.get_handle(&path_string) {
|
if let Some(handle) = asset_server.get_handle(&path_string) {
|
||||||
handle
|
handle
|
||||||
|
@ -108,7 +104,7 @@ impl Material for SkyBox {
|
||||||
#[derive(Asset, Reflect, AsBindGroup, Debug, Clone)]
|
#[derive(Asset, Reflect, AsBindGroup, Debug, Clone)]
|
||||||
pub struct AsteroidSurface {
|
pub struct AsteroidSurface {
|
||||||
#[uniform(100)]
|
#[uniform(100)]
|
||||||
quantize_steps: u32
|
quantize_steps: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaterialExtension for AsteroidSurface {
|
impl MaterialExtension for AsteroidSurface {
|
||||||
|
@ -135,8 +131,6 @@ impl AsteroidSurface {
|
||||||
}
|
}
|
||||||
impl Default for AsteroidSurface {
|
impl Default for AsteroidSurface {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { quantize_steps: 3 }
|
||||||
quantize_steps: 3,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
41
src/main.rs
|
@ -28,18 +28,19 @@ pub mod visual;
|
||||||
pub mod world;
|
pub mod world;
|
||||||
|
|
||||||
pub mod prelude {
|
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::common::*;
|
||||||
pub use crate::var::Settings;
|
|
||||||
pub use crate::load::load_asset;
|
pub use crate::load::load_asset;
|
||||||
pub use game::{GameEvent, Turn};
|
pub use crate::var::Settings;
|
||||||
|
pub use crate::{
|
||||||
|
actor, audio, camera, chat, cmd, common, game, hud, load, menu, nature, var, visual, world,
|
||||||
|
};
|
||||||
pub use game::Turn::Toggle;
|
pub use game::Turn::Toggle;
|
||||||
|
pub use game::{GameEvent, Turn};
|
||||||
}
|
}
|
||||||
|
|
||||||
use bevy::window::{Window, WindowMode, PrimaryWindow, CursorGrabMode};
|
|
||||||
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
|
use bevy::diagnostic::FrameTimeDiagnosticsPlugin;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
use bevy::window::{CursorGrabMode, PrimaryWindow, Window, WindowMode};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
const HELP: &str = "./outfly [options]
|
const HELP: &str = "./outfly [options]
|
||||||
|
@ -68,8 +69,7 @@ fn main() {
|
||||||
if arg == "--help" || arg == "-h" {
|
if arg == "--help" || arg == "-h" {
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
return;
|
return;
|
||||||
}
|
} else if arg == "--version" || arg == "-v" {
|
||||||
else if arg == "--version" || arg == "-v" {
|
|
||||||
let version = option_env!("CARGO_PKG_VERSION").unwrap();
|
let version = option_env!("CARGO_PKG_VERSION").unwrap();
|
||||||
let name = option_env!("CARGO_PKG_NAME").unwrap();
|
let name = option_env!("CARGO_PKG_NAME").unwrap();
|
||||||
let homepage = option_env!("CARGO_PKG_HOMEPAGE").unwrap();
|
let homepage = option_env!("CARGO_PKG_HOMEPAGE").unwrap();
|
||||||
|
@ -77,28 +77,23 @@ fn main() {
|
||||||
println!("License: GNU GPL version 3: https://gnu.org/licenses/gpl.html");
|
println!("License: GNU GPL version 3: https://gnu.org/licenses/gpl.html");
|
||||||
println!("{homepage}");
|
println!("{homepage}");
|
||||||
return;
|
return;
|
||||||
}
|
} else if arg == "--gl" {
|
||||||
else if arg == "--gl" {
|
|
||||||
opt.use_gl = true;
|
opt.use_gl = true;
|
||||||
}
|
} else if arg == "--windowed" {
|
||||||
else if arg == "--windowed" {
|
|
||||||
opt.window_mode_initial = WindowMode::Windowed;
|
opt.window_mode_initial = WindowMode::Windowed;
|
||||||
}
|
} else if arg == "--fs-legacy" {
|
||||||
else if arg == "--fs-legacy" {
|
|
||||||
let mode = WindowMode::Fullscreen;
|
let mode = WindowMode::Fullscreen;
|
||||||
if opt.window_mode_initial == opt.window_mode_fullscreen {
|
if opt.window_mode_initial == opt.window_mode_fullscreen {
|
||||||
opt.window_mode_initial = mode;
|
opt.window_mode_initial = mode;
|
||||||
}
|
}
|
||||||
opt.window_mode_fullscreen = mode;
|
opt.window_mode_fullscreen = mode;
|
||||||
}
|
} else if arg == "--fs-sized" {
|
||||||
else if arg == "--fs-sized" {
|
|
||||||
let mode = WindowMode::SizedFullscreen;
|
let mode = WindowMode::SizedFullscreen;
|
||||||
if opt.window_mode_initial == opt.window_mode_fullscreen {
|
if opt.window_mode_initial == opt.window_mode_fullscreen {
|
||||||
opt.window_mode_initial = mode;
|
opt.window_mode_initial = mode;
|
||||||
}
|
}
|
||||||
opt.window_mode_fullscreen = mode;
|
opt.window_mode_fullscreen = mode;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
println!("{}", HELP);
|
println!("{}", HELP);
|
||||||
println!("\nERROR: no such option: `{}`", arg);
|
println!("\nERROR: no such option: `{}`", arg);
|
||||||
return;
|
return;
|
||||||
|
@ -114,7 +109,7 @@ fn main() {
|
||||||
|
|
||||||
#[cfg(feature = "embed_assets")]
|
#[cfg(feature = "embed_assets")]
|
||||||
app.add_plugins(bevy_embedded_assets::EmbeddedAssetPlugin {
|
app.add_plugins(bevy_embedded_assets::EmbeddedAssetPlugin {
|
||||||
mode: bevy_embedded_assets::PluginMode::ReplaceDefault
|
mode: bevy_embedded_assets::PluginMode::ReplaceDefault,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.add_plugins(OutFlyPlugin);
|
app.add_plugins(OutFlyPlugin);
|
||||||
|
@ -127,12 +122,9 @@ impl Plugin for OutFlyPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup);
|
app.add_systems(Startup, setup);
|
||||||
app.add_systems(Update, handle_input);
|
app.add_systems(Update, handle_input);
|
||||||
app.insert_resource(var::Settings::default());
|
|
||||||
app.insert_resource(var::GameVars::default());
|
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
DefaultPlugins,//.set(ImagePlugin::default_nearest()),
|
DefaultPlugins, //.set(ImagePlugin::default_nearest()),
|
||||||
FrameTimeDiagnosticsPlugin,
|
FrameTimeDiagnosticsPlugin,
|
||||||
|
|
||||||
actor::ActorPlugin,
|
actor::ActorPlugin,
|
||||||
audio::AudioPlugin,
|
audio::AudioPlugin,
|
||||||
camera::CameraPlugin,
|
camera::CameraPlugin,
|
||||||
|
@ -148,10 +140,7 @@ impl Plugin for OutFlyPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(
|
fn setup(mut windows: Query<&mut Window, With<PrimaryWindow>>, opt: Res<var::CommandLineOptions>) {
|
||||||
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
|
||||||
opt: Res<var::CommandLineOptions>,
|
|
||||||
) {
|
|
||||||
for mut window in &mut windows {
|
for mut window in &mut windows {
|
||||||
window.cursor.grab_mode = CursorGrabMode::Locked;
|
window.cursor.grab_mode = CursorGrabMode::Locked;
|
||||||
window.cursor.visible = false;
|
window.cursor.visible = false;
|
||||||
|
|
160
src/menu.rs
|
@ -20,32 +20,52 @@ pub struct MenuPlugin;
|
||||||
impl Plugin for MenuPlugin {
|
impl Plugin for MenuPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup.after(hud::setup));
|
app.add_systems(Startup, setup.after(hud::setup));
|
||||||
app.add_systems(PreUpdate, show_deathscreen.run_if(on_event::<DeathScreenEvent>()));
|
app.add_systems(
|
||||||
|
PreUpdate,
|
||||||
|
show_deathscreen.run_if(on_event::<DeathScreenEvent>()),
|
||||||
|
);
|
||||||
app.add_systems(Update, handle_deathscreen_input);
|
app.add_systems(Update, handle_deathscreen_input);
|
||||||
app.add_systems(PostUpdate, update_menu
|
app.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
update_menu
|
||||||
.after(game::handle_game_event)
|
.after(game::handle_game_event)
|
||||||
.run_if(on_event::<UpdateMenuEvent>()));
|
.run_if(on_event::<UpdateMenuEvent>()),
|
||||||
|
);
|
||||||
app.add_systems(Update, handle_input.run_if(alive));
|
app.add_systems(Update, handle_input.run_if(alive));
|
||||||
app.insert_resource(DeathScreenInputDelayTimer(
|
app.insert_resource(DeathScreenInputDelayTimer(Timer::from_seconds(
|
||||||
Timer::from_seconds(1.0, TimerMode::Once)));
|
1.0,
|
||||||
|
TimerMode::Once,
|
||||||
|
)));
|
||||||
app.insert_resource(MenuState::default());
|
app.insert_resource(MenuState::default());
|
||||||
app.add_event::<DeathScreenEvent>();
|
app.add_event::<DeathScreenEvent>();
|
||||||
app.add_event::<UpdateMenuEvent>();
|
app.add_event::<UpdateMenuEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)] pub struct DeathScreenInputDelayTimer(pub Timer);
|
#[derive(Resource)]
|
||||||
#[derive(Component)] pub struct MenuElement;
|
pub struct DeathScreenInputDelayTimer(pub Timer);
|
||||||
#[derive(Component)] pub struct MenuTopLevel;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct MenuAchievements;
|
pub struct MenuElement;
|
||||||
#[derive(Component)] pub struct DeathScreenElement;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct DeathText;
|
pub struct MenuTopLevel;
|
||||||
#[derive(Event)] pub struct UpdateMenuEvent;
|
#[derive(Component)]
|
||||||
#[derive(Event, PartialEq)] pub enum DeathScreenEvent { Show, Hide }
|
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)] = &[
|
pub const MENUDEF: &[(&str, MenuAction)] = &[
|
||||||
("", MenuAction::ToggleMap),
|
("", MenuAction::ToggleMap),
|
||||||
("", MenuAction::ToggleAR),
|
("", MenuAction::ToggleAR),
|
||||||
|
("", MenuAction::ChangeARAvatar),
|
||||||
("", MenuAction::ToggleSound),
|
("", MenuAction::ToggleSound),
|
||||||
("", MenuAction::ToggleMusic),
|
("", MenuAction::ToggleMusic),
|
||||||
("", MenuAction::ToggleCamera),
|
("", MenuAction::ToggleCamera),
|
||||||
|
@ -59,6 +79,7 @@ pub const MENUDEF: &[(&str, MenuAction)] = &[
|
||||||
pub enum MenuAction {
|
pub enum MenuAction {
|
||||||
ToggleMap,
|
ToggleMap,
|
||||||
ToggleAR,
|
ToggleAR,
|
||||||
|
ChangeARAvatar,
|
||||||
ToggleSound,
|
ToggleSound,
|
||||||
ToggleMusic,
|
ToggleMusic,
|
||||||
ToggleCamera,
|
ToggleCamera,
|
||||||
|
@ -115,14 +136,16 @@ pub fn setup(
|
||||||
color: settings.hud_color_death_achievements,
|
color: settings.hud_color_death_achievements,
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
DeathScreenElement,
|
DeathScreenElement,
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: style_centered(),
|
style: style_centered(),
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
DeathText,
|
DeathText,
|
||||||
TextBundle {
|
TextBundle {
|
||||||
|
@ -133,7 +156,10 @@ pub fn setup(
|
||||||
TextSection::new("Cause: ", style_death_subtext.clone()),
|
TextSection::new("Cause: ", style_death_subtext.clone()),
|
||||||
TextSection::new("Unknown", style_death_subtext),
|
TextSection::new("Unknown", style_death_subtext),
|
||||||
TextSection::new("", style_death_achievements),
|
TextSection::new("", style_death_achievements),
|
||||||
TextSection::new("\n\n\n\nPress E to begin anew.", style_death_subsubtext),
|
TextSection::new(
|
||||||
|
"\n\n\n\nPress E to begin anew.",
|
||||||
|
style_death_subsubtext,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
justify: JustifyText::Center,
|
justify: JustifyText::Center,
|
||||||
..default()
|
..default()
|
||||||
|
@ -150,9 +176,11 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let sections: Vec<TextSection> = Vec::from_iter(MENUDEF.iter().map(|(label, _)|
|
let sections: Vec<TextSection> = Vec::from_iter(
|
||||||
TextSection::new(label.to_string() + "\n", style_menu.clone())
|
MENUDEF
|
||||||
));
|
.iter()
|
||||||
|
.map(|(label, _)| TextSection::new(label.to_string() + "\n", style_menu.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
MenuElement,
|
MenuElement,
|
||||||
|
@ -164,7 +192,8 @@ pub fn setup(
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
MenuElement,
|
MenuElement,
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
|
@ -177,7 +206,8 @@ pub fn setup(
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)).with_children(|builder| {
|
))
|
||||||
|
.with_children(|builder| {
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
MenuTopLevel,
|
MenuTopLevel,
|
||||||
TextBundle {
|
TextBundle {
|
||||||
|
@ -205,7 +235,8 @@ pub fn setup(
|
||||||
};
|
};
|
||||||
let achievement_count = achievement_tracker.to_bool_vec().len();
|
let achievement_count = achievement_tracker.to_bool_vec().len();
|
||||||
|
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
MenuElement,
|
MenuElement,
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
|
@ -220,13 +251,15 @@ pub fn setup(
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)).with_children(|builder| {
|
))
|
||||||
let mut sections = vec![
|
.with_children(|builder| {
|
||||||
TextSection::new("Achievements\n", style_achievement_header.clone())
|
let mut sections = vec![TextSection::new(
|
||||||
];
|
"Achievements\n",
|
||||||
sections.extend(Vec::from_iter((0..achievement_count).map(|_|
|
style_achievement_header.clone(),
|
||||||
TextSection::new("", style_achievement.clone())
|
)];
|
||||||
)));
|
sections.extend(Vec::from_iter(
|
||||||
|
(0..achievement_count).map(|_| TextSection::new("", style_achievement.clone())),
|
||||||
|
));
|
||||||
builder.spawn((
|
builder.spawn((
|
||||||
MenuAchievements,
|
MenuAchievements,
|
||||||
TextBundle {
|
TextBundle {
|
||||||
|
@ -248,7 +281,8 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
MenuElement,
|
MenuElement,
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
|
@ -263,20 +297,19 @@ pub fn setup(
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)).with_children(|builder| {
|
))
|
||||||
builder.spawn((
|
.with_children(|builder| {
|
||||||
TextBundle {
|
builder.spawn((TextBundle {
|
||||||
text: Text {
|
text: Text {
|
||||||
sections: vec![
|
sections: vec![
|
||||||
TextSection::new("Controls\n", style_achievement_header),
|
TextSection::new("Controls\n", style_achievement_header),
|
||||||
TextSection::new(keybindings, style_keybindings)
|
TextSection::new(keybindings, style_keybindings),
|
||||||
],
|
],
|
||||||
justify: JustifyText::Right,
|
justify: JustifyText::Right,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let style_version = TextStyle {
|
let style_version = TextStyle {
|
||||||
|
@ -286,7 +319,8 @@ pub fn setup(
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
|
|
||||||
commands.spawn((
|
commands
|
||||||
|
.spawn((
|
||||||
MenuElement,
|
MenuElement,
|
||||||
NodeBundle {
|
NodeBundle {
|
||||||
style: Style {
|
style: Style {
|
||||||
|
@ -301,20 +335,19 @@ pub fn setup(
|
||||||
visibility: Visibility::Hidden,
|
visibility: Visibility::Hidden,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
)).with_children(|builder| {
|
))
|
||||||
builder.spawn((
|
.with_children(|builder| {
|
||||||
TextBundle {
|
builder.spawn((TextBundle {
|
||||||
text: Text {
|
text: Text {
|
||||||
sections: vec![
|
sections: vec![TextSection::new(
|
||||||
TextSection::new(format!("{} {}", GAME_NAME,
|
format!("{} {}", GAME_NAME, settings.version.as_str()),
|
||||||
settings.version.as_str()), style_version),
|
style_version,
|
||||||
],
|
)],
|
||||||
justify: JustifyText::Right,
|
justify: JustifyText::Right,
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
..default()
|
..default()
|
||||||
},
|
},));
|
||||||
));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +392,10 @@ pub fn show_deathscreen(
|
||||||
} else {
|
} else {
|
||||||
ew_respawnaudiosinks.send(audio::RespawnSinksEvent);
|
ew_respawnaudiosinks.send(audio::RespawnSinksEvent);
|
||||||
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
|
||||||
ew_effect.send(visual::SpawnEffectEvent { class: visual::Effects::FadeIn(Color::BLACK), duration: 0.3 });
|
ew_effect.send(visual::SpawnEffectEvent {
|
||||||
|
class: visual::Effects::FadeIn(Color::BLACK),
|
||||||
|
duration: 0.3,
|
||||||
|
});
|
||||||
ew_respawn.send(world::RespawnEvent);
|
ew_respawn.send(world::RespawnEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -433,6 +469,12 @@ pub fn update_menu(
|
||||||
let onoff = bool2string(settings.hud_active);
|
let onoff = bool2string(settings.hud_active);
|
||||||
text.sections[i].value = format!("Augmented Reality: {onoff} [TAB]\n");
|
text.sections[i].value = format!("Augmented Reality: {onoff} [TAB]\n");
|
||||||
}
|
}
|
||||||
|
MenuAction::ChangeARAvatar => {
|
||||||
|
if let Some(ava) = hud::PLAYER_AR_AVATARS.get(settings.ar_avatar) {
|
||||||
|
let avatar_title = ava.3;
|
||||||
|
text.sections[i].value = format!("Avatar: {avatar_title}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
MenuAction::ToggleMap => {
|
MenuAction::ToggleMap => {
|
||||||
let onoff = bool2string(settings.map_active);
|
let onoff = bool2string(settings.map_active);
|
||||||
text.sections[i].value = format!("Map: {onoff} [M]\n");
|
text.sections[i].value = format!("Map: {onoff} [M]\n");
|
||||||
|
@ -468,6 +510,7 @@ pub fn handle_input(
|
||||||
mut ew_playerdies: EventWriter<game::PlayerDiesEvent>,
|
mut ew_playerdies: EventWriter<game::PlayerDiesEvent>,
|
||||||
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
|
||||||
mut ew_updatemenu: EventWriter<UpdateMenuEvent>,
|
mut ew_updatemenu: EventWriter<UpdateMenuEvent>,
|
||||||
|
mut ew_updateavatar: EventWriter<hud::UpdateAvatarEvent>,
|
||||||
) {
|
) {
|
||||||
let last_menu_entry = MENUDEF.len() - 1;
|
let last_menu_entry = MENUDEF.len() - 1;
|
||||||
|
|
||||||
|
@ -512,37 +555,42 @@ pub fn handle_input(
|
||||||
ew_game.send(GameEvent::SetMap(Toggle));
|
ew_game.send(GameEvent::SetMap(Toggle));
|
||||||
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
ew_game.send(GameEvent::SetMenu(Turn::Off));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
MenuAction::ToggleAR => {
|
MenuAction::ToggleAR => {
|
||||||
ew_game.send(GameEvent::SetAR(Toggle));
|
ew_game.send(GameEvent::SetAR(Toggle));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
|
MenuAction::ChangeARAvatar => {
|
||||||
|
settings.ar_avatar += 1;
|
||||||
|
ew_updateavatar.send(hud::UpdateAvatarEvent);
|
||||||
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
|
}
|
||||||
MenuAction::ToggleMusic => {
|
MenuAction::ToggleMusic => {
|
||||||
ew_game.send(GameEvent::SetMusic(Toggle));
|
ew_game.send(GameEvent::SetMusic(Toggle));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
MenuAction::ToggleSound => {
|
MenuAction::ToggleSound => {
|
||||||
ew_game.send(GameEvent::SetSound(Toggle));
|
ew_game.send(GameEvent::SetSound(Toggle));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
MenuAction::ToggleCamera => {
|
MenuAction::ToggleCamera => {
|
||||||
ew_game.send(GameEvent::SetThirdPerson(Toggle));
|
ew_game.send(GameEvent::SetThirdPerson(Toggle));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
MenuAction::ToggleFullscreen => {
|
MenuAction::ToggleFullscreen => {
|
||||||
ew_game.send(GameEvent::SetFullscreen(Toggle));
|
ew_game.send(GameEvent::SetFullscreen(Toggle));
|
||||||
},
|
}
|
||||||
MenuAction::ToggleShadows => {
|
MenuAction::ToggleShadows => {
|
||||||
ew_game.send(GameEvent::SetShadows(Toggle));
|
ew_game.send(GameEvent::SetShadows(Toggle));
|
||||||
ew_updatemenu.send(UpdateMenuEvent);
|
ew_updatemenu.send(UpdateMenuEvent);
|
||||||
},
|
}
|
||||||
MenuAction::Restart => {
|
MenuAction::Restart => {
|
||||||
settings.god_mode = false;
|
settings.god_mode = false;
|
||||||
ew_playerdies.send(game::PlayerDiesEvent(actor::DamageType::Depressurization));
|
ew_playerdies.send(game::PlayerDiesEvent(actor::DamageType::Depressurization));
|
||||||
},
|
}
|
||||||
MenuAction::Quit => {
|
MenuAction::Quit => {
|
||||||
app_exit_events.send(bevy::app::AppExit);
|
app_exit_events.send(bevy::app::AppExit);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ pub const JUPITER_MASS: f64 = 1.8982e27;
|
||||||
pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");
|
pub const STARS: &[(f32, f32, f32, f32, f32, f32, &str)] = &include!("data/stars.in");
|
||||||
|
|
||||||
pub fn star_color_index_to_rgb(color_index: f32) -> (f32, f32, f32) {
|
pub fn star_color_index_to_rgb(color_index: f32) -> (f32, f32, f32) {
|
||||||
let temperature = 4600.0 * ((1.0 / (0.92 * color_index + 1.7)) + (1.0 / (0.92 * color_index + 0.62)));
|
let temperature =
|
||||||
|
4600.0 * ((1.0 / (0.92 * color_index + 1.7)) + (1.0 / (0.92 * color_index + 0.62)));
|
||||||
|
|
||||||
let (red, green, blue) = if temperature <= 6600.0 {
|
let (red, green, blue) = if temperature <= 6600.0 {
|
||||||
let red = 255.0;
|
let red = 255.0;
|
||||||
|
@ -55,7 +56,7 @@ pub fn star_color_index_to_rgb(color_index: f32) -> (f32, f32, f32) {
|
||||||
|
|
||||||
let clamp = |x: f32| -> f32 { (x / 255.0).max(0.0).min(1.0) };
|
let clamp = |x: f32| -> f32 { (x / 255.0).max(0.0).min(1.0) };
|
||||||
|
|
||||||
return (clamp(red), clamp(green), clamp(blue))
|
return (clamp(red), clamp(green), clamp(blue));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn smooth_edge(start: f32, end: f32, value: f32) -> f32 {
|
fn smooth_edge(start: f32, end: f32, value: f32) -> f32 {
|
||||||
|
@ -89,10 +90,19 @@ pub fn ring_density(radius: f32) -> f32 {
|
||||||
density = halo_brightness * smooth_edge(halo_inner, halo_outer, radius);
|
density = halo_brightness * smooth_edge(halo_inner, halo_outer, radius);
|
||||||
} else if radius >= main_inner && radius <= main_outer {
|
} else if radius >= main_inner && radius <= main_outer {
|
||||||
let mut metis_notch_effect: f32 = 1.0;
|
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 {
|
if 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));
|
&& 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,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
density = main_brightness * metis_notch_effect * smooth_edge(main_inner, main_outer, radius);
|
density =
|
||||||
|
main_brightness * metis_notch_effect * smooth_edge(main_inner, main_outer, radius);
|
||||||
} else {
|
} else {
|
||||||
if radius >= amalthea_inner && radius <= amalthea_outer {
|
if radius >= amalthea_inner && radius <= amalthea_outer {
|
||||||
density = almathea_brightness * smooth_edge(amalthea_inner, amalthea_outer, radius);
|
density = almathea_brightness * smooth_edge(amalthea_inner, amalthea_outer, radius);
|
||||||
|
@ -131,8 +141,7 @@ pub fn readable_speed(speed: f64) -> String {
|
||||||
if abs > C * 0.0005 {
|
if abs > C * 0.0005 {
|
||||||
let lightyears = abs / C;
|
let lightyears = abs / C;
|
||||||
return format!("{lightyears:.4} c");
|
return format!("{lightyears:.4} c");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let kmh = abs * 1.0e-3 * 3600.0;
|
let kmh = abs * 1.0e-3 * 3600.0;
|
||||||
return format!("{kmh:.0} km/h");
|
return format!("{kmh:.0} km/h");
|
||||||
}
|
}
|
||||||
|
|
46
src/var.rs
|
@ -12,13 +12,13 @@
|
||||||
// "if"-conditions in chats.
|
// "if"-conditions in chats.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::window::WindowMode;
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use std::collections::{HashMap, HashSet};
|
use bevy::window::WindowMode;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use toml_edit::DocumentMut;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use toml_edit::DocumentMut;
|
||||||
|
|
||||||
pub const SCOPE_SEPARATOR: &str = "$";
|
pub const SCOPE_SEPARATOR: &str = "$";
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ pub struct Settings {
|
||||||
pub hud_color_keybindings: Color,
|
pub hud_color_keybindings: Color,
|
||||||
pub hud_color_version: Color,
|
pub hud_color_version: Color,
|
||||||
pub chat_speed: f32,
|
pub chat_speed: f32,
|
||||||
|
pub ar_avatar: usize,
|
||||||
pub flashlight_active: bool,
|
pub flashlight_active: bool,
|
||||||
pub hud_active: bool,
|
pub hud_active: bool,
|
||||||
pub map_active: bool,
|
pub map_active: bool,
|
||||||
|
@ -208,6 +209,7 @@ impl Default for Settings {
|
||||||
hud_color_keybindings: Color::hex(COLOR_DIM).unwrap(),
|
hud_color_keybindings: Color::hex(COLOR_DIM).unwrap(),
|
||||||
hud_color_version: Color::hex(COLOR_PRIMARY).unwrap(),
|
hud_color_version: Color::hex(COLOR_PRIMARY).unwrap(),
|
||||||
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
|
chat_speed: DEFAULT_CHAT_SPEED * if dev_mode { 2.5 } else { 1.0 },
|
||||||
|
ar_avatar: 0,
|
||||||
flashlight_active: false,
|
flashlight_active: false,
|
||||||
hud_active: true,
|
hud_active: true,
|
||||||
map_active: false,
|
map_active: false,
|
||||||
|
@ -379,28 +381,29 @@ impl AchievementTracker {
|
||||||
(self.ride_every_vehicle, "ride every vehicle".into()),
|
(self.ride_every_vehicle, "ride every vehicle".into()),
|
||||||
(self.talk_to_everyone, "talk to everyone".into()),
|
(self.talk_to_everyone, "talk to everyone".into()),
|
||||||
(self.find_earth, "find Earth".into()),
|
(self.find_earth, "find Earth".into()),
|
||||||
(self.in_jupiters_shadow, "eclipse the Sun with Jupiter".into()),
|
(
|
||||||
|
self.in_jupiters_shadow,
|
||||||
|
"eclipse the Sun with Jupiter".into(),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
pub fn to_summary(&self) -> String {
|
pub fn to_summary(&self) -> String {
|
||||||
let list = self.to_overview();
|
let list = self.to_overview();
|
||||||
let count = list.iter().filter(|(achieved, _)| *achieved).count();
|
let count = list.iter().filter(|(achieved, _)| *achieved).count();
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
return "".to_string()
|
return "".to_string();
|
||||||
}
|
}
|
||||||
let mut summary = "\n\n\nYou managed to ".to_string();
|
let mut summary = "\n\n\nYou managed to ".to_string();
|
||||||
for (i, (_, text)) in list.iter().filter(|(achieved, _)| *achieved).enumerate() {
|
for (i, (_, text)) in list.iter().filter(|(achieved, _)| *achieved).enumerate() {
|
||||||
summary += text.as_str();
|
summary += text.as_str();
|
||||||
if i + 2 == count {
|
if i + 2 == count {
|
||||||
summary += ", and ";
|
summary += ", and ";
|
||||||
}
|
} else if i + 1 == count {
|
||||||
else if i + 1 == count {
|
|
||||||
summary += " before you perished.";
|
summary += " before you perished.";
|
||||||
if count == list.len() {
|
if count == list.len() {
|
||||||
summary += "\nA truly astounding achievement, a glimmer in the void, before it all fades, into nothingness.";
|
summary += "\nA truly astounding achievement, a glimmer in the void, before it all fades, into nothingness.";
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
summary += ", ";
|
summary += ", ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,7 +442,9 @@ impl Preferences {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file_is_readable(file_path: &str) -> bool {
|
fn file_is_readable(file_path: &str) -> bool {
|
||||||
fs::metadata(file_path).map(|metadata| metadata.is_file()).unwrap_or(false)
|
fs::metadata(file_path)
|
||||||
|
.map(|metadata| metadata.is_file())
|
||||||
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prefs_path() -> Option<String> {
|
fn get_prefs_path() -> Option<String> {
|
||||||
|
@ -479,8 +484,7 @@ pub fn load_prefs() -> Preferences {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match toml.parse::<DocumentMut>() {
|
match toml.parse::<DocumentMut>() {
|
||||||
Ok(doc) => {
|
Ok(doc) => match toml_edit::de::from_document::<Preferences>(doc) {
|
||||||
match toml_edit::de::from_document::<Preferences>(doc) {
|
|
||||||
Ok(mut pref) => {
|
Ok(mut pref) => {
|
||||||
if let Some(path) = &path {
|
if let Some(path) = &path {
|
||||||
info!("Loaded preference file from {path}");
|
info!("Loaded preference file from {path}");
|
||||||
|
@ -495,8 +499,7 @@ pub fn load_prefs() -> Preferences {
|
||||||
error!("Failed to read preference line: {error}");
|
error!("Failed to read preference line: {error}");
|
||||||
return Preferences::default();
|
return Preferences::default();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!("Failed to open preferences: {error}");
|
error!("Failed to open preferences: {error}");
|
||||||
return Preferences::default();
|
return Preferences::default();
|
||||||
|
@ -511,13 +514,15 @@ pub struct GameVars {
|
||||||
|
|
||||||
impl Default for GameVars {
|
impl Default for GameVars {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { db: HashMap::new() }
|
||||||
db: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameVars {
|
impl GameVars {
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.db.clear();
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn get(&self, key: &str) -> Option<String> {
|
pub fn get(&self, key: &str) -> Option<String> {
|
||||||
if let Some(value) = self.db.get(key) {
|
if let Some(value) = self.db.get(key) {
|
||||||
|
@ -573,8 +578,7 @@ impl GameVars {
|
||||||
if scope_part.is_empty() {
|
if scope_part.is_empty() {
|
||||||
// we got a key like "$foo", just prefix the fallback scope
|
// we got a key like "$foo", just prefix the fallback scope
|
||||||
fallback_scope.to_string() + key
|
fallback_scope.to_string() + key
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// we got a key like "Ke$ha$foo" or "$$foo" (which is the convention for
|
// we got a key like "Ke$ha$foo" or "$$foo" (which is the convention for
|
||||||
// global variables), leave the scope intact
|
// global variables), leave the scope intact
|
||||||
key.to_string()
|
key.to_string()
|
||||||
|
@ -609,11 +613,9 @@ impl GameVars {
|
||||||
let part = Self::normalize_varname(scope, part);
|
let part = Self::normalize_varname(scope, part);
|
||||||
let value_bool = self.getb(part.as_str());
|
let value_bool = self.getb(part.as_str());
|
||||||
return value_bool ^ negate;
|
return value_bool ^ negate;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return Self::evaluate_str_as_bool(part) ^ negate;
|
return Self::evaluate_str_as_bool(part) ^ negate;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if parts.len() == 2 {
|
} else if parts.len() == 2 {
|
||||||
// Got something like "if $something somethingelse"
|
// Got something like "if $something somethingelse"
|
||||||
// Check whether the two are identical.
|
// Check whether the two are identical.
|
||||||
|
|
|
@ -10,15 +10,18 @@
|
||||||
//
|
//
|
||||||
// This module manages visual effects.
|
// This module manages visual effects.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use bevy::prelude::*;
|
||||||
|
|
||||||
pub struct VisualPlugin;
|
pub struct VisualPlugin;
|
||||||
|
|
||||||
impl Plugin for VisualPlugin {
|
impl Plugin for VisualPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, setup.after(menu::setup).after(hud::setup));
|
app.add_systems(Startup, setup.after(menu::setup).after(hud::setup));
|
||||||
app.add_systems(Startup, spawn_effects.after(setup).after(camera::setup_camera));
|
app.add_systems(
|
||||||
|
Startup,
|
||||||
|
spawn_effects.after(setup).after(camera::setup_camera),
|
||||||
|
);
|
||||||
app.add_systems(Update, spawn_effects);
|
app.add_systems(Update, spawn_effects);
|
||||||
app.add_systems(Update, update_fadein);
|
app.add_systems(Update, update_fadein);
|
||||||
app.add_systems(Update, update_fadeout);
|
app.add_systems(Update, update_fadeout);
|
||||||
|
@ -38,8 +41,10 @@ pub enum Effects {
|
||||||
// Blackout disabled for now
|
// Blackout disabled for now
|
||||||
//#[derive(Component)] pub struct BlackOutOverlay;
|
//#[derive(Component)] pub struct BlackOutOverlay;
|
||||||
|
|
||||||
#[derive(Component)] pub struct FadeIn;
|
#[derive(Component)]
|
||||||
#[derive(Component)] pub struct FadeOut;
|
pub struct FadeIn;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct FadeOut;
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct Effect {
|
pub struct Effect {
|
||||||
pub class: Effects,
|
pub class: Effects,
|
||||||
|
@ -52,29 +57,29 @@ pub struct SpawnEffectEvent {
|
||||||
pub duration: f64,
|
pub duration: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(
|
pub fn setup(settings: Res<var::Settings>, mut ew_effect: EventWriter<SpawnEffectEvent>) {
|
||||||
settings: Res<var::Settings>,
|
|
||||||
mut ew_effect: EventWriter<SpawnEffectEvent>,
|
|
||||||
) {
|
|
||||||
if !settings.dev_mode {
|
if !settings.dev_mode {
|
||||||
ew_effect.send(SpawnEffectEvent { class: Effects::FadeIn(Color::BLACK), duration: 4.0 });
|
ew_effect.send(SpawnEffectEvent {
|
||||||
|
class: Effects::FadeIn(Color::BLACK),
|
||||||
|
duration: 4.0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Blackout disabled for now
|
// Blackout disabled for now
|
||||||
// commands.spawn((
|
// commands.spawn((
|
||||||
// BlackOutOverlay,
|
// BlackOutOverlay,
|
||||||
// NodeBundle {
|
// NodeBundle {
|
||||||
// style: Style {
|
// style: Style {
|
||||||
// width: Val::Vw(100.0),
|
// width: Val::Vw(100.0),
|
||||||
// height: Val::Vh(100.0),
|
// height: Val::Vh(100.0),
|
||||||
// position_type: PositionType::Absolute,
|
// position_type: PositionType::Absolute,
|
||||||
// top: Val::Px(0.0),
|
// top: Val::Px(0.0),
|
||||||
// left: Val::Px(0.0),
|
// left: Val::Px(0.0),
|
||||||
// ..default()
|
// ..default()
|
||||||
// },
|
// },
|
||||||
// background_color: Color::BLACK.into(),
|
// background_color: Color::BLACK.into(),
|
||||||
// ..default()
|
// ..default()
|
||||||
// },
|
// },
|
||||||
// ));
|
// ));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_effects(
|
pub fn spawn_effects(
|
||||||
|
@ -99,7 +104,7 @@ pub fn spawn_effects(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
}
|
||||||
Effects::FadeOut(color) => {
|
Effects::FadeOut(color) => {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Effect {
|
Effect {
|
||||||
|
@ -114,8 +119,7 @@ pub fn spawn_effects(
|
||||||
..default()
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
},
|
}
|
||||||
//_ => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
61
src/world.rs
|
@ -11,13 +11,13 @@
|
||||||
// This module populates the world with stars and asteroids.
|
// This module populates the world with stars and asteroids.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy::prelude::*;
|
|
||||||
use bevy::math::I64Vec3;
|
use bevy::math::I64Vec3;
|
||||||
use bevy::scene::{InstanceId, SceneInstance};
|
use bevy::prelude::*;
|
||||||
use bevy::render::mesh::Indices;
|
use bevy::render::mesh::Indices;
|
||||||
|
use bevy::scene::{InstanceId, SceneInstance};
|
||||||
use bevy_xpbd_3d::prelude::*;
|
use bevy_xpbd_3d::prelude::*;
|
||||||
use std::collections::HashMap;
|
|
||||||
use fastrand;
|
use fastrand;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds
|
const ASTEROID_UPDATE_INTERVAL: f32 = 0.1; // seconds
|
||||||
const ASTEROID_SIZE_FACTOR: f32 = 10.0;
|
const ASTEROID_SIZE_FACTOR: f32 = 10.0;
|
||||||
|
@ -42,20 +42,28 @@ impl Plugin for WorldPlugin {
|
||||||
app.add_plugins(PhysicsPlugins::default());
|
app.add_plugins(PhysicsPlugins::default());
|
||||||
//app.add_plugins(PhysicsDebugPlugin::default());
|
//app.add_plugins(PhysicsDebugPlugin::default());
|
||||||
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
app.insert_resource(Gravity(DVec3::splat(0.0)));
|
||||||
app.insert_resource(AsteroidUpdateTimer(
|
app.insert_resource(AsteroidUpdateTimer(Timer::from_seconds(
|
||||||
Timer::from_seconds(ASTEROID_UPDATE_INTERVAL, TimerMode::Repeating)));
|
ASTEROID_UPDATE_INTERVAL,
|
||||||
|
TimerMode::Repeating,
|
||||||
|
)));
|
||||||
app.insert_resource(ActiveAsteroids(HashMap::new()));
|
app.insert_resource(ActiveAsteroids(HashMap::new()));
|
||||||
app.add_event::<DespawnAsteroidEvent>();
|
app.add_event::<DespawnAsteroidEvent>();
|
||||||
app.add_event::<RespawnEvent>();
|
app.add_event::<RespawnEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource)] struct AsteroidUpdateTimer(Timer);
|
#[derive(Resource)]
|
||||||
#[derive(Resource)] pub struct ActiveAsteroids(pub HashMap<I64Vec3, AsteroidData>);
|
struct AsteroidUpdateTimer(Timer);
|
||||||
#[derive(Component)] struct Asteroid;
|
#[derive(Resource)]
|
||||||
#[derive(Component)] pub struct Star;
|
pub struct ActiveAsteroids(pub HashMap<I64Vec3, AsteroidData>);
|
||||||
#[derive(Component)] pub struct DespawnOnPlayerDeath;
|
#[derive(Component)]
|
||||||
#[derive(Event)] pub struct RespawnEvent;
|
struct Asteroid;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct Star;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct DespawnOnPlayerDeath;
|
||||||
|
#[derive(Event)]
|
||||||
|
pub struct RespawnEvent;
|
||||||
|
|
||||||
pub struct AsteroidData {
|
pub struct AsteroidData {
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
|
@ -93,14 +101,12 @@ pub fn setup(
|
||||||
let scale_factor = 1e-4 * pos_render.length() as f32; // from experimentation
|
let scale_factor = 1e-4 * pos_render.length() as f32; // from experimentation
|
||||||
|
|
||||||
let mag = mag.min(6.0);
|
let mag = mag.min(6.0);
|
||||||
let scale_size = {|mag: f32|
|
let scale_size =
|
||||||
scale_factor * (0.230299 * mag * mag - 3.09013 * mag + 15.1782)
|
{ |mag: f32| scale_factor * (0.230299 * mag * mag - 3.09013 * mag + 15.1782) };
|
||||||
};
|
|
||||||
let scale = scale_size(mag);
|
let scale = scale_size(mag);
|
||||||
|
|
||||||
let scale_color = {|color: f32|
|
let scale_color =
|
||||||
1.2 * color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3)
|
{ |color: f32| 1.2 * color * (0.0659663 * mag * mag - 1.09862 * mag + 4.3) };
|
||||||
};
|
|
||||||
//let scale = translation.length().powf(0.84);
|
//let scale = translation.length().powf(0.84);
|
||||||
//pos_render.length().powf(0.64)
|
//pos_render.length().powf(0.64)
|
||||||
//(radius as f64 * nature::SOL_RADIUS).powf(0.02) as f32 *
|
//(radius as f64 * nature::SOL_RADIUS).powf(0.02) as f32 *
|
||||||
|
@ -211,9 +217,12 @@ fn spawn_despawn_asteroids(
|
||||||
let z_min = player_cell.z - stepmax;
|
let z_min = player_cell.z - stepmax;
|
||||||
let z_max = player_cell.z + stepmax;
|
let z_max = player_cell.z + stepmax;
|
||||||
for (origin, asteroid) in db.0.iter() {
|
for (origin, asteroid) in db.0.iter() {
|
||||||
if origin.x < x_min || origin.x > x_max
|
if origin.x < x_min
|
||||||
|| origin.y < y_min || origin.y > y_max
|
|| origin.x > x_max
|
||||||
|| origin.z < z_min || origin.z > z_max
|
|| origin.y < y_min
|
||||||
|
|| origin.y > y_max
|
||||||
|
|| origin.z < z_min
|
||||||
|
|| origin.z > z_max
|
||||||
{
|
{
|
||||||
if let Ok((pos, sceneinstance)) = q_asteroid.get(asteroid.entity) {
|
if let Ok((pos, sceneinstance)) = q_asteroid.get(asteroid.entity) {
|
||||||
if pos.0.distance(player.0) > 1000.0 {
|
if pos.0.distance(player.0) > 1000.0 {
|
||||||
|
@ -288,7 +297,8 @@ fn spawn_despawn_asteroids(
|
||||||
|
|
||||||
//let max_viewdist = ASTEROID_VIEW_RADIUS / ASTEROID_SPAWN_STEP;
|
//let max_viewdist = ASTEROID_VIEW_RADIUS / ASTEROID_SPAWN_STEP;
|
||||||
let wobble = ASTEROID_SPAWN_STEP * 0.5;
|
let wobble = ASTEROID_SPAWN_STEP * 0.5;
|
||||||
let pos = jupiter_pos + DVec3::new(
|
let pos = jupiter_pos
|
||||||
|
+ DVec3::new(
|
||||||
origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0,
|
origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0,
|
||||||
origin.y as f64 * ASTEROID_SPAWN_STEP + wobble * rand_y * 2.0 - 1.0,
|
origin.y as f64 * ASTEROID_SPAWN_STEP + wobble * rand_y * 2.0 - 1.0,
|
||||||
origin.z as f64 * ASTEROID_SPAWN_STEP + wobble * rand_z * 2.0 - 1.0,
|
origin.z as f64 * ASTEROID_SPAWN_STEP + wobble * rand_z * 2.0 - 1.0,
|
||||||
|
@ -322,10 +332,13 @@ fn spawn_despawn_asteroids(
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
load_asset(model, &mut entity_commands, &*asset_server);
|
load_asset(model, &mut entity_commands, &*asset_server);
|
||||||
db.0.insert(origin, AsteroidData {
|
db.0.insert(
|
||||||
|
origin,
|
||||||
|
AsteroidData {
|
||||||
entity: entity_commands.id(),
|
entity: entity_commands.id(),
|
||||||
//viewdistance: 99999999.0,
|
//viewdistance: 99999999.0,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -344,7 +357,7 @@ fn handle_despawn(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_respawn(
|
pub fn handle_respawn(
|
||||||
ew_spawn: EventWriter<cmd::SpawnEvent>,
|
ew_spawn: EventWriter<cmd::SpawnEvent>,
|
||||||
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
mut achievement_tracker: ResMut<var::AchievementTracker>,
|
||||||
) {
|
) {
|
||||||
|
|