Compare commits

..

43 commits

Author SHA1 Message Date
yuni 4fecab5428 bump version to v0.9.2 2024-06-07 00:48:02 +02:00
yuni 3429ca5ab7 update changelog 2024-06-07 00:47:03 +02:00
yuni 9f936989f3 fix player erroneously slowing down automatically 2024-06-07 00:43:27 +02:00
yuni 1ce864c746 tweak suit_v2 curves 2024-06-07 00:33:21 +02:00
yuni 63beec86a2 fix icarus' rotation 2024-06-02 21:37:29 +02:00
yuni 3f1dc27684 add command "template clippy" 2024-06-02 21:35:40 +02:00
yuni 0bbca303cb add "template person" command 2024-06-02 21:29:08 +02:00
yuni f7002fd064 reset chat variables (GameVars) on death 2024-06-02 21:04:03 +02:00
yuni 76bfdf0bfb (fix in bevy repo) release keys when moving window to other workspace with hotkey 2024-06-02 20:58:58 +02:00
yuni 23c86e9c2f update changelog 2024-05-23 06:11:25 +02:00
yuni 28cf269907 keep avatar on death/respawn 2024-05-23 05:42:25 +02:00
yuni cc3213788e implement wing avatar, give it to icarus (and player, optionally) 2024-05-23 05:42:18 +02:00
yuni 192d2e0fcb update changelog 2024-05-23 05:04:17 +02:00
yuni efbb44a9fc implement player avatars 2024-05-23 05:02:59 +02:00
yuni 099e935e3e avoid errors during player death 2024-05-23 05:01:53 +02:00
yuni 2a6e14aa90 add JupiterRecording.ogg, toggle BGM with TAB 2024-05-23 03:56:13 +02:00
yuni 8d4ad64330 cut out a different part from the jupiter recording 2024-05-23 03:53:08 +02:00
yuni e2046380ea WIP jupiter recording 2024-05-23 03:50:09 +02:00
yuni 60be58b0fa delete unused monolith.glb 2024-05-23 02:07:37 +02:00
yuni 224e0ce2c9 cleanup, add TODO 2024-05-23 01:26:24 +02:00
yuni bfad39613e better value for "up" vector 2024-05-23 00:59:33 +02:00
yuni e9afeefb7d fix "want to look at player" for when player rides a vehicle 2024-05-23 00:56:12 +02:00
yuni c9e38c7b29 avoid potential panic 2024-05-23 00:40:47 +02:00
yuni 62a0387867 fix monolith model, make it look at cult asteroid 2024-05-23 00:40:14 +02:00
yuni 91d19e94a0 implement preferred looking direction for NPCs 2024-05-23 00:31:31 +02:00
yuni 8a07e9cfb7 cleanup 2024-05-22 23:54:11 +02:00
yuni c56b5d6d74 dynamic camera movement speed limit based on mouse speed 2024-05-22 23:50:28 +02:00
yuni bcba3d0945 cleanup 2024-05-22 23:33:37 +02:00
yuni 9b48112ee6 remove unnecessary check 2024-05-22 23:31:57 +02:00
yuni 8acbd4f33b unify rotation stabilizer of players and npcs 2024-05-22 23:29:54 +02:00
yuni 93e5ee26e4 bump version to v0.9.1 2024-05-22 22:33:54 +02:00
yuni 8621002931 fix build instruction link 2024-05-22 22:33:48 +02:00
yuni 220ab340fb fix banner in README 2024-05-22 05:01:49 +02:00
yuni 87199f41db apply cargo fmt 2024-05-22 05:00:45 +02:00
yuni b0ac508d91 enable zoom outside of augmented reality (why confuse the player?) 2024-05-22 04:11:48 +02:00
yuni 14a22699bc brevity 2024-05-21 23:32:23 +02:00
yuni 2bc86227a9 highlight keys 2024-05-21 23:29:21 +02:00
yuni 61b63aefbe wording 2024-05-21 23:28:17 +02:00
yuni c7e3a9396e add sticker parameters 2024-05-21 19:19:42 +02:00
yuni b62c5a6287 preview of branding colors 2024-05-21 19:07:40 +02:00
yuni 42c1d3e191 add branding directory 2024-05-21 19:05:49 +02:00
yuni 77b682a7c1 update repository URL 2024-05-20 21:56:25 +02:00
yuni 6043d4a1b0 Revert "brighter logo"
This reverts commit 83313c1a5f.
2024-05-17 17:17:18 +02:00
41 changed files with 2534 additions and 1491 deletions

View file

@ -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
View file

@ -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",

View file

@ -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"]

View file

@ -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)

View file

@ -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"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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]

View file

@ -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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

26
doc/branding/README.md Normal file
View 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

View file

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
doc/branding/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

View file

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View file

@ -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(
update_physics_lifeforms, FixedUpdate,
update_power, (
handle_wants_maxrotation, update_physics_lifeforms,
handle_wants_maxvelocity, update_power,
)); handle_wants_maxrotation,
app.add_systems(PostUpdate, handle_gforce handle_wants_maxvelocity,
.after(PhysicsSet::Sync) handle_wants_lookat.run_if(alive),
.after(sync::position_to_transform)); ),
app.add_systems(Update, ( );
handle_input.run_if(in_control), app.add_systems(
handle_collisions, PostUpdate,
handle_damage, handle_gforce
)); .after(PhysicsSet::Sync)
app.add_systems(PostUpdate, ( .after(sync::position_to_transform),
handle_vehicle_enter_exit, );
)); app.add_systems(
Update,
(
handle_input.run_if(in_control),
handle_collisions,
handle_damage,
),
);
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 {
gforce: 0.0, fn default() -> Self {
damage_threshold: 100.0, Self {
visual_effect_threshold: 20.0, gforce: 0.0,
visual_effect: 0.0, damage_threshold: 100.0,
last_linear_velocity: DVec3::splat(0.0), visual_effect_threshold: 20.0,
ignore_gforce_seconds: 0.0, visual_effect: 0.0,
}}} last_linear_velocity: DVec3::splat(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 {
is_alive: true, fn default() -> Self {
adrenaline: 0.3, Self {
adrenaline_baseline: 0.3, is_alive: true,
adrenaline_jolt: 0.0, adrenaline: 0.3,
}}} adrenaline_baseline: 0.3,
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 {
stored_drivers_collider: None, fn default() -> Self {
}}} Self {
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,
@ -202,9 +239,9 @@ const SUIT_SIMPLE: Suit = Suit {
#[derive(Component)] #[derive(Component)]
pub struct Battery { pub struct Battery {
pub power: f32, // Watt-seconds pub power: f32, // Watt-seconds
pub capacity: f32, // Watt-seconds pub capacity: f32, // Watt-seconds
pub reactor: f32, // Watt (production) pub reactor: f32, // Watt (production)
} }
impl Default for Battery { impl Default for Battery {
@ -212,7 +249,7 @@ impl Default for Battery {
Self { Self {
power: 10e3 * 3600.0, power: 10e3 * 3600.0,
capacity: 10e3 * 3600.0, // 10kWh capacity: 10e3 * 3600.0, // 10kWh
reactor: 2000e3, // 2MW reactor: 2000e3, // 2MW
} }
} }
} }
@ -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
@ -325,12 +371,12 @@ pub fn handle_input(
.map(|(entity, actor, transform)| ((entity, actor), transform)) .map(|(entity, actor, transform)| ((entity, actor), transform))
.collect(); .collect();
if let (Some((entity, actor)), dist) = if let (Some((entity, actor)), dist) =
camera::find_closest_target::<(Entity, &Actor)>(objects, camtrans) camera::find_closest_target::<(Entity, &Actor)>(objects, camtrans)
{ {
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,17 +546,40 @@ 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
let avg_thrust =
(engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0;
let acceleration = (avg_thrust * dt) as f64 * -v.0;
v.0 += acceleration;
if v.0.length() + EPSILON < acceleration.length() {
v.0 = DVec3::splat(0.0);
}
} }
}
}
// TODO: respect engine parameters for different thrusts for different directions fn handle_wants_lookat(
let avg_thrust = (engine.thrust_forward + engine.thrust_back + engine.thrust_sideways) / 3.0; mut query: Query<(&Position, &mut Rotation, &Transform, &WantsToLookAt)>,
let acceleration = (avg_thrust * dt) as f64 * -v.0; q_playercam: Query<&Position, With<PlayerCamera>>,
v.0 += acceleration; id2pos: Res<game::Id2Pos>,
if v.0.length() + EPSILON < acceleration.length() { ) {
v.0 = DVec3::splat(0.0); 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());
} }
} }
@ -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
} }
} }
} }

View file

@ -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,23 +234,19 @@ 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>, for (bgm_sink, sfx) in &q_audiosinks {
q_audiosinks: Query<(&AudioSink, &Sfx)>, let play = match *sfx {
settings: Res<var::Settings>, Sfx::BGM => settings.hud_active,
) { Sfx::BGMActualJupiterRecording => !settings.hud_active,
if !events.is_empty() { _ => {
events.clear();
for (bgm_sink, sfx) in &q_audiosinks {
if *sfx != Sfx::BGM {
continue; continue;
} }
if settings.mute_music { };
bgm_sink.pause(); if settings.mute_music || !play {
} bgm_sink.pause();
else { } else {
bgm_sink.play(); bgm_sink.play();
}
} }
} }
} }
@ -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();
} }

View 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 &amp; 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 &amp; 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 &amp; 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>

Binary file not shown.

BIN
src/blender/wings.blend Normal file

Binary file not shown.

View file

@ -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(
.after(PhysicsSet::Sync) PostUpdate,
.after(apply_input_to_player) sync_camera_to_player
.before(TransformSystem::TransformPropagate)); .after(PhysicsSet::Sync)
app.add_systems(PostUpdate, update_mapcam_center .after(apply_input_to_player)
.before(sync::position_to_transform) .before(TransformSystem::TransformPropagate),
.in_set(sync::SyncSet::PositionToTransform)); );
app.add_systems(
PostUpdate,
update_mapcam_center
.before(sync::position_to_transform)
.in_set(sync::SyncSet::PositionToTransform),
);
app.add_systems(Update, update_map_camera.run_if(in_control)); app.add_systems(Update, update_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(
.after(sync::position_to_transform) PostUpdate,
.in_set(sync::SyncSet::PositionToTransform)); position_to_transform
.after(sync::position_to_transform)
.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,18 +502,21 @@ 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
engine.thrust_forward * (if axis_input.z > 0.0 {
} else { engine.thrust_forward
engine.thrust_back } else {
}); engine.thrust_back
});
let right_factor = engine.thrust_sideways * engine.current_warmup; let right_factor = engine.thrust_sideways * engine.current_warmup;
let up_factor = engine.thrust_sideways * engine.current_warmup; let up_factor = engine.thrust_sideways * engine.current_warmup;
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 { if settings.rotation_stabilizer_active || key_input.pressed(settings.key_stop) {
angularvelocity.0 *= angular_slowdown; commands
} .entity(player_entity)
else { .try_insert(actor::WantsMaxRotation(mouse_speed as f64 * 0.1));
angularvelocity.0 = DVec3::splat(0.0); } 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,21 +715,22 @@ 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);
let distance_to_surface = distance - trans.scale.x; let distance_to_surface = distance - trans.scale.x;
if distance_to_surface < closest_distance { if distance_to_surface < closest_distance {
closest_distance = distance_to_surface; closest_distance = distance_to_surface;
closest_entity = Some(entity); closest_entity = Some(entity);
@ -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

View file

@ -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] = &[
@ -40,7 +40,7 @@ pub const TOKEN_NOWAIT: &str = "nowait";
pub const TOKEN_INCLUDE: &str = "include"; pub const TOKEN_INCLUDE: &str = "include";
pub const TOKEN_GOTO_EXIT: &str = "EXIT"; pub const TOKEN_GOTO_EXIT: &str = "EXIT";
pub const TOKEN_IF_INLINE: &str = "if "; // for lines like `- if foo:` pub const TOKEN_IF_INLINE: &str = "if "; // for lines like `- if foo:`
pub const DEFAULT_SOUND: &str = "chat"; pub const DEFAULT_SOUND: &str = "chat";
pub const MAX_BRANCH_DEPTH: usize = 64; pub const MAX_BRANCH_DEPTH: usize = 64;
@ -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(
handle_reply_keys.before(handle_chat_timer), Update,
handle_chat_timer.before(handle_chat_events), (
handle_new_conversations.before(handle_chat_events), handle_reply_keys.before(handle_chat_timer),
handle_chat_events.before(handle_chat_scripts), handle_chat_timer.before(handle_chat_events),
handle_chat_scripts, handle_new_conversations.before(handle_chat_events),
)); handle_chat_events.before(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,36 +728,40 @@ 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;
}
}
commands.spawn((
world::DespawnOnPlayerDeath,
Choice {
text: replytext.into(),
key: choice_key,
goto: goto.clone(),
}
));
choice_key += 1;
if !nowait {
chat.timer = now + CHOICE_TIMER / settings.chat_speed as f64;
} }
} }
commands.spawn((
world::DespawnOnPlayerDeath,
Choice {
text: replytext.into(),
key: choice_key,
goto: goto.clone(),
},
));
choice_key += 1;
if !nowait {
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,35 +842,37 @@ 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" => {
for (_, mut suit, _) in q_player.iter_mut() { if let Ok(mut amount) = param1.to_string().parse::<f32>() {
if param2.is_empty() { for (_, mut suit, _) in q_player.iter_mut() {
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max); if param2.is_empty() {
} 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() {
if !other_actor.id.is_empty() { if !other_actor.id.is_empty() {
info!("ID={}", other_actor.id); info!("ID={}", other_actor.id);
}
if other_actor.id == param2 {
found_other = true;
amount = amount
.clamp(0.0, other_suit.oxygen)
.clamp(0.0, suit.oxygen_max - suit.oxygen);
other_suit.oxygen = other_suit.oxygen - amount;
suit.oxygen =
(suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
break;
}
} }
if other_actor.id == param2 { if !found_other {
found_other = true; error!("Script error: could not find actor with ID `{}`", param2);
amount = amount
.clamp(0.0, other_suit.oxygen)
.clamp(0.0, suit.oxygen_max - suit.oxygen);
other_suit.oxygen = other_suit.oxygen - amount;
suit.oxygen = (suit.oxygen + amount).clamp(0.0, suit.oxygen_max);
break;
} }
} }
if !found_other {
error!("Script error: could not find actor with ID `{}`", param2);
}
} }
} else {
error!("Invalid parameter for command `{}`: `{}`", name, param1);
} }
} else {
error!("Invalid parameter for command `{}`: `{}`", name, param1);
} }
"repairsuit" => { "repairsuit" => {
ew_achievement.send(game::AchievementEvent::RepairSuit); ew_achievement.send(game::AchievementEvent::RepairSuit);
@ -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);

View file

@ -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,229 +659,235 @@ 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(
5.0 if state.is_sun {
} else if state.is_moon && settings.large_moons { 5.0
3.0 } else if state.is_moon && settings.large_moons {
} else { 3.0
1.0 } else {
} * state.model_scale); 1.0
} * state.model_scale,
);
// Spawn the actor // Spawn the actor
let actor_entity; let actor_entity;
{ {
let mut actor = commands.spawn_empty(); let mut actor = commands.spawn_empty();
actor.insert(actor::Actor { actor.insert(actor::Actor {
id: state.id.clone(), id: state.id.clone(),
name: state.name.clone(),
camdistance: state.camdistance,
..default()
});
actor.insert(SleepingDisabled);
actor.insert(world::DespawnOnPlayerDeath);
actor.insert(actor::HitPoints::default());
actor.insert(Position::from(absolute_pos));
if state.is_sphere {
let sphere_texture_handle = if let Some(model) = &state.model {
Some(asset_server.load(format!("textures/{}.jpg", model)))
} else {
None
};
rotation = Quat::from_rotation_x(-90f32.to_radians()) * rotation;
let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128));
let sphere_material_handle = materials.add(StandardMaterial {
base_color_texture: sphere_texture_handle,
perceptual_roughness: 1.0,
metallic: 0.0,
..default()
});
actor.insert(PbrBundle {
mesh: sphere_handle,
material: sphere_material_handle,
transform: Transform::from_scale(scale),
..default()
});
} else if let Some(model) = &state.model {
actor.insert(SpatialBundle {
transform: Transform::from_scale(scale),
..default()
});
load_asset(model.as_str(), &mut actor, &*asset_server);
}
actor.insert(Rotation::from(rotation));
// Physics Parameters
if state.has_physics {
actor.insert(RigidBody::Dynamic);
actor.insert(LinearVelocity(state.velocity));
actor.insert(AngularVelocity(state.angular_momentum));
actor.insert(ColliderDensity(state.density));
if state.collider_is_mesh {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
actor.insert(AsyncSceneCollider::new(Some(
ComputedCollider::TriMesh
//ComputedCollider::ConvexDecomposition(VHACDParameters::default())
)));
}
else if state.collider_is_one_mesh_of_scene {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64), state.density));
actor.insert(AsyncSceneCollider::new(None)
.with_shape_for_name("Collider", ComputedCollider::TriMesh)
.with_layers_for_name("Collider", CollisionLayers::ALL)
//.with_density_for_name("Collider", state.density)
);
actor.insert(NeedsSceneColliderRemoved);
}
else {
actor.insert(state.collider.clone());
}
}
// TODO: angular velocity for objects without collisions, static objects
// Optional Components
if state.is_player {
actor.insert(actor::Player);
actor.insert(actor::PlayerCamera);
}
if state.is_sun {
let (r, g, b) = nature::star_color_index_to_rgb(0.656);
actor.insert(materials.add(StandardMaterial {
base_color: Color::rgb(r, g, b) * 13.0,
unlit: true,
..default()
}));
actor.insert((
NotShadowCaster,
NotShadowReceiver,
));
}
if state.is_targeted_on_startup {
actor.insert(hud::IsTargeted);
}
if let Some((mindist, id)) = &state.show_only_in_map_at_distance {
actor.insert(camera::ShowOnlyInMap {
min_distance: *mindist,
distance_to_id: id.clone()
});
}
if state.is_player || state.is_vehicle {
// used to apply mouse movement to actor rotation
actor.insert(ExternalTorque::ZERO.with_persistence(false));
}
if state.is_lifeform {
actor.insert(actor::LifeForm::default());
actor.insert(actor::ExperiencesGForce::default());
actor.insert(actor::Suit {
oxygen: state.oxygen,
oxygen_max: nature::OXY_D,
integrity: state.suit_integrity,
..default()
});
actor.insert(actor::Battery::default());
}
if state.is_clickable {
actor.insert(hud::IsClickable {
name: state.name.clone(), name: state.name.clone(),
pronoun: state.pronoun.clone(), camdistance: state.camdistance,
..default() ..default()
}); });
} actor.insert(SleepingDisabled);
if let Some(value) = state.wants_maxrotation { actor.insert(world::DespawnOnPlayerDeath);
actor.insert(actor::WantsMaxRotation(value)); actor.insert(actor::HitPoints::default());
} actor.insert(Position::from(absolute_pos));
if let Some(value) = state.wants_maxvelocity { if state.is_sphere {
actor.insert(actor::WantsMaxVelocity(value)); let sphere_texture_handle = if let Some(model) = &state.model {
} Some(asset_server.load(format!("textures/{}.jpg", model)))
if let Some(color) = state.light_color { } else {
actor.insert(( None
PointLight { };
intensity: state.light_brightness, rotation = Quat::from_rotation_x(-90f32.to_radians()) * rotation;
color, let sphere_handle = meshes.add(Sphere::new(1.0).mesh().uv(128, 128));
range: 2000.0, let sphere_material_handle = materials.add(StandardMaterial {
shadows_enabled: settings.shadows_pointlights, base_color_texture: sphere_texture_handle,
perceptual_roughness: 1.0,
metallic: 0.0,
..default() ..default()
}, });
bevy::pbr::CubemapVisibleEntities::default(), actor.insert(PbrBundle {
bevy::render::primitives::CubemapFrusta::default(), mesh: sphere_handle,
)); material: sphere_material_handle,
} transform: Transform::from_scale(scale),
if !state.id.is_empty() { ..default()
actor.insert(actor::Identifier(state.id.clone())); });
id2pos.0.insert(state.id.clone(), absolute_pos); } else if let Some(model) = &state.model {
} actor.insert(SpatialBundle {
if !state.chat.is_empty() { transform: Transform::from_scale(scale),
actor.insert(chat::Talker { ..default()
actor_id: state.id.clone(), });
chat_name: state.chat.clone(), load_asset(model.as_str(), &mut actor, &*asset_server);
name: state.name.clone(),
pronoun: state.pronoun.clone(),
talking_speed: 1.0,
});
if let Some(name) = &state.name {
achievement_tracker.all_people.insert(name.clone());
} }
} actor.insert(Rotation::from(rotation));
if state.is_vehicle {
actor.insert(actor::Vehicle::default()); // Physics Parameters
if let Some(name) = &state.name { if state.has_physics {
achievement_tracker.all_vehicles.insert(name.clone()); actor.insert(RigidBody::Dynamic);
actor.insert(LinearVelocity(state.velocity));
actor.insert(AngularVelocity(state.angular_momentum));
actor.insert(ColliderDensity(state.density));
if state.collider_is_mesh {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64),
state.density,
));
actor.insert(AsyncSceneCollider::new(Some(
ComputedCollider::TriMesh, //ComputedCollider::ConvexDecomposition(VHACDParameters::default())
)));
} else if state.collider_is_one_mesh_of_scene {
actor.insert(MassPropertiesBundle::new_computed(
&Collider::sphere(0.5 * state.model_scale as f64),
state.density,
));
actor.insert(
AsyncSceneCollider::new(None)
.with_shape_for_name("Collider", ComputedCollider::TriMesh)
.with_layers_for_name("Collider", CollisionLayers::ALL), //.with_density_for_name("Collider", state.density)
);
actor.insert(NeedsSceneColliderRemoved);
} else {
actor.insert(state.collider.clone());
}
} }
} // TODO: angular velocity for objects without collisions, static objects
if state.is_vehicle
|| state.is_suited // Optional Components
|| state.thrust_forward > 0.0 if state.is_player {
|| state.thrust_sideways > 0.0 actor.insert(actor::Player);
|| state.thrust_back > 0.0 actor.insert(actor::PlayerCamera);
|| state.reaction_wheels > 0.0 actor.insert(hud::AugmentedRealityOverlayBroadcaster);
{ ew_updateavatar.send(hud::UpdateAvatarEvent);
actor.insert(actor::Engine { }
thrust_forward: state.thrust_forward, if state.is_sun {
thrust_back: state.thrust_back, let (r, g, b) = nature::star_color_index_to_rgb(0.656);
thrust_sideways: state.thrust_sideways, actor.insert(materials.add(StandardMaterial {
reaction_wheels: state.reaction_wheels, base_color: Color::rgb(r, g, b) * 13.0,
warmup_seconds: state.warmup_seconds, unlit: true,
engine_type: state.engine_type, ..default()
..default() }));
}); actor.insert((NotShadowCaster, NotShadowReceiver));
} }
if let Some(_) = state.ar_model { if state.is_targeted_on_startup {
actor.insert(hud::AugmentedRealityOverlayBroadcaster); actor.insert(hud::IsTargeted);
} }
if state.is_player { if let Some((mindist, id)) = &state.show_only_in_map_at_distance {
actor.with_children(|builder| { actor.insert(camera::ShowOnlyInMap {
builder.spawn(( min_distance: *mindist,
world::DespawnOnPlayerDeath, distance_to_id: id.clone(),
actor::PlayersFlashLight, });
SpotLightBundle { }
transform: Transform { if state.is_player || state.is_vehicle {
translation: Vec3::new(0.0, 0.0, 1.0), // used to apply mouse movement to actor rotation
rotation: Quat::from_rotation_y(180f32.to_radians()), actor.insert(ExternalTorque::ZERO.with_persistence(false));
..default() }
}, if state.is_lifeform {
spot_light: SpotLight { actor.insert(actor::LifeForm::default());
intensity: 40_000_000.0, // lumens actor.insert(actor::ExperiencesGForce::default());
color: Color::WHITE, actor.insert(actor::Suit {
shadows_enabled: true, oxygen: state.oxygen,
inner_angle: PI32 / 8.0 * 0.85, oxygen_max: nature::OXY_D,
outer_angle: PI32 / 4.0, integrity: state.suit_integrity,
range: 2000.0, ..default()
..default() });
}, actor.insert(actor::Battery::default());
visibility: Visibility::Hidden, }
if state.is_clickable {
actor.insert(hud::IsClickable {
name: state.name.clone(),
pronoun: state.pronoun.clone(),
..default()
});
}
if let Some(value) = state.wants_maxrotation {
actor.insert(actor::WantsMaxRotation(value));
}
if let Some(value) = state.wants_maxvelocity {
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 {
actor.insert((
PointLight {
intensity: state.light_brightness,
color,
range: 2000.0,
shadows_enabled: settings.shadows_pointlights,
..default() ..default()
} },
bevy::pbr::CubemapVisibleEntities::default(),
bevy::render::primitives::CubemapFrusta::default(),
)); ));
}); }
} if !state.id.is_empty() {
actor_entity = actor.id(); actor.insert(actor::Identifier(state.id.clone()));
id2pos.0.insert(state.id.clone(), absolute_pos);
}
if !state.chat.is_empty() {
actor.insert(chat::Talker {
actor_id: state.id.clone(),
chat_name: state.chat.clone(),
name: state.name.clone(),
pronoun: state.pronoun.clone(),
talking_speed: 1.0,
});
if let Some(name) = &state.name {
achievement_tracker.all_people.insert(name.clone());
}
}
if state.is_vehicle {
actor.insert(actor::Vehicle::default());
if let Some(name) = &state.name {
achievement_tracker.all_vehicles.insert(name.clone());
}
}
if state.is_vehicle
|| state.is_suited
|| state.thrust_forward > 0.0
|| state.thrust_sideways > 0.0
|| state.thrust_back > 0.0
|| state.reaction_wheels > 0.0
{
actor.insert(actor::Engine {
thrust_forward: state.thrust_forward,
thrust_back: state.thrust_back,
thrust_sideways: state.thrust_sideways,
reaction_wheels: state.reaction_wheels,
warmup_seconds: state.warmup_seconds,
engine_type: state.engine_type,
..default()
});
}
if let Some(_) = state.ar_model {
actor.insert(hud::AugmentedRealityOverlayBroadcaster);
}
if state.is_player {
actor.with_children(|builder| {
builder.spawn((
world::DespawnOnPlayerDeath,
actor::PlayersFlashLight,
SpotLightBundle {
transform: Transform {
translation: Vec3::new(0.0, 0.0, 1.0),
rotation: Quat::from_rotation_y(180f32.to_radians()),
..default()
},
spot_light: SpotLight {
intensity: 40_000_000.0, // lumens
color: Color::WHITE,
shadows_enabled: true,
inner_angle: PI32 / 8.0 * 0.85,
outer_angle: PI32 / 4.0,
range: 2000.0,
..default()
},
visibility: Visibility::Hidden,
..default()
},
));
});
}
actor_entity = actor.id();
} }
if let Some(ar_asset_name) = &state.ar_model { if let Some(ar_asset_name) = &state.ar_model {
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 {

View file

@ -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)
}

View file

@ -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"

View file

@ -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:

View file

@ -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"

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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,
}
} }
} }

View file

@ -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;

View file

@ -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(
.after(game::handle_game_event) PostUpdate,
.run_if(on_event::<UpdateMenuEvent>())); update_menu
.after(game::handle_game_event)
.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,33 +136,38 @@ pub fn setup(
color: settings.hud_color_death_achievements, color: settings.hud_color_death_achievements,
..default() ..default()
}; };
commands.spawn(( commands
DeathScreenElement, .spawn((
NodeBundle { DeathScreenElement,
style: style_centered(), NodeBundle {
visibility: Visibility::Hidden, style: style_centered(),
..default() visibility: Visibility::Hidden,
},
)).with_children(|builder| {
builder.spawn((
DeathText,
TextBundle {
text: Text {
sections: vec![
TextSection::new("", style_death_poem),
TextSection::new("You are dead.\n", style_death),
TextSection::new("Cause: ", style_death_subtext.clone()),
TextSection::new("Unknown", style_death_subtext),
TextSection::new("", style_death_achievements),
TextSection::new("\n\n\n\nPress E to begin anew.", style_death_subsubtext),
],
justify: JustifyText::Center,
..default()
},
..default() ..default()
}, },
)); ))
}); .with_children(|builder| {
builder.spawn((
DeathText,
TextBundle {
text: Text {
sections: vec![
TextSection::new("", style_death_poem),
TextSection::new("You are dead.\n", style_death),
TextSection::new("Cause: ", style_death_subtext.clone()),
TextSection::new("Unknown", style_death_subtext),
TextSection::new("", style_death_achievements),
TextSection::new(
"\n\n\n\nPress E to begin anew.",
style_death_subsubtext,
),
],
justify: JustifyText::Center,
..default()
},
..default()
},
));
});
let style_menu = TextStyle { let style_menu = TextStyle {
font: font_handle.clone(), font: font_handle.clone(),
@ -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,32 +192,34 @@ pub fn setup(
}, },
)); ));
commands.spawn(( commands
MenuElement, .spawn((
NodeBundle { MenuElement,
style: Style { NodeBundle {
width: Val::Percent(100.0), style: Style {
height: Val::Percent(100.0), width: Val::Percent(100.0),
align_items: AlignItems::Center, height: Val::Percent(100.0),
justify_content: JustifyContent::SpaceAround, align_items: AlignItems::Center,
..default() justify_content: JustifyContent::SpaceAround,
},
visibility: Visibility::Hidden,
..default()
},
)).with_children(|builder| {
builder.spawn((
MenuTopLevel,
TextBundle {
text: Text {
sections,
justify: JustifyText::Center,
..default() ..default()
}, },
visibility: Visibility::Hidden,
..default() ..default()
}, },
)); ))
}); .with_children(|builder| {
builder.spawn((
MenuTopLevel,
TextBundle {
text: Text {
sections,
justify: JustifyText::Center,
..default()
},
..default()
},
));
});
let style_achievement_header = TextStyle { let style_achievement_header = TextStyle {
font: font_handle.clone(), font: font_handle.clone(),
@ -205,40 +235,43 @@ pub fn setup(
}; };
let achievement_count = achievement_tracker.to_bool_vec().len(); let achievement_count = achievement_tracker.to_bool_vec().len();
commands.spawn(( commands
MenuElement, .spawn((
NodeBundle { MenuElement,
style: Style { NodeBundle {
width: Val::Percent(100.0), style: Style {
height: Val::Percent(100.0), width: Val::Percent(100.0),
left: Val::Percent(2.0), height: Val::Percent(100.0),
top: Val::Percent(2.0), left: Val::Percent(2.0),
align_items: AlignItems::Start, top: Val::Percent(2.0),
justify_content: JustifyContent::Start, align_items: AlignItems::Start,
..default() justify_content: JustifyContent::Start,
},
visibility: Visibility::Hidden,
..default()
},
)).with_children(|builder| {
let mut sections = vec![
TextSection::new("Achievements\n", style_achievement_header.clone())
];
sections.extend(Vec::from_iter((0..achievement_count).map(|_|
TextSection::new("", style_achievement.clone())
)));
builder.spawn((
MenuAchievements,
TextBundle {
text: Text {
sections,
justify: JustifyText::Left,
..default() ..default()
}, },
visibility: Visibility::Hidden,
..default() ..default()
}, },
)); ))
}); .with_children(|builder| {
let mut sections = vec![TextSection::new(
"Achievements\n",
style_achievement_header.clone(),
)];
sections.extend(Vec::from_iter(
(0..achievement_count).map(|_| TextSection::new("", style_achievement.clone())),
));
builder.spawn((
MenuAchievements,
TextBundle {
text: Text {
sections,
justify: JustifyText::Left,
..default()
},
..default()
},
));
});
let keybindings = include_str!("data/keybindings.in"); let keybindings = include_str!("data/keybindings.in");
let style_keybindings = TextStyle { let style_keybindings = TextStyle {
@ -248,36 +281,36 @@ pub fn setup(
..default() ..default()
}; };
commands.spawn(( commands
MenuElement, .spawn((
NodeBundle { MenuElement,
style: Style { NodeBundle {
width: Val::Percent(96.0), style: Style {
height: Val::Percent(96.0), width: Val::Percent(96.0),
left: Val::Percent(2.0), height: Val::Percent(96.0),
top: Val::Percent(2.0), left: Val::Percent(2.0),
align_items: AlignItems::Start, top: Val::Percent(2.0),
justify_content: JustifyContent::End, align_items: AlignItems::Start,
justify_content: JustifyContent::End,
..default()
},
visibility: Visibility::Hidden,
..default() ..default()
}, },
visibility: Visibility::Hidden, ))
..default() .with_children(|builder| {
}, builder.spawn((TextBundle {
)).with_children(|builder| {
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 {
font: font_handle.clone(), font: font_handle.clone(),
@ -286,36 +319,36 @@ pub fn setup(
..default() ..default()
}; };
commands.spawn(( commands
MenuElement, .spawn((
NodeBundle { MenuElement,
style: Style { NodeBundle {
width: Val::Percent(96.0), style: Style {
height: Val::Percent(96.0), width: Val::Percent(96.0),
left: Val::Percent(2.0), height: Val::Percent(96.0),
top: Val::Percent(2.0), left: Val::Percent(2.0),
align_items: AlignItems::End, top: Val::Percent(2.0),
justify_content: JustifyContent::End, align_items: AlignItems::End,
justify_content: JustifyContent::End,
..default()
},
visibility: Visibility::Hidden,
..default() ..default()
}, },
visibility: Visibility::Hidden, ))
..default() .with_children(|builder| {
}, builder.spawn((TextBundle {
)).with_children(|builder| {
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()
}, },));
)); });
});
} }
pub fn show_deathscreen( pub fn show_deathscreen(
@ -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,11 +510,12 @@ 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;
if keyboard_input.just_pressed(settings.key_menu) if keyboard_input.just_pressed(settings.key_menu)
|| keyboard_input.just_pressed(settings.key_vehicle) && settings.menu_active || keyboard_input.just_pressed(settings.key_vehicle) && settings.menu_active
{ {
ew_game.send(GameEvent::SetMenu(Toggle)); ew_game.send(GameEvent::SetMenu(Toggle));
} }
@ -480,8 +523,8 @@ pub fn handle_input(
return; return;
} }
if keyboard_input.just_pressed(settings.key_forward) if keyboard_input.just_pressed(settings.key_forward)
|| keyboard_input.just_pressed(settings.key_mouseup) || keyboard_input.just_pressed(settings.key_mouseup)
|| keyboard_input.just_pressed(KeyCode::ArrowUp) || keyboard_input.just_pressed(KeyCode::ArrowUp)
{ {
menustate.cursor = if menustate.cursor == 0 { menustate.cursor = if menustate.cursor == 0 {
last_menu_entry last_menu_entry
@ -492,8 +535,8 @@ pub fn handle_input(
ew_updatemenu.send(UpdateMenuEvent); ew_updatemenu.send(UpdateMenuEvent);
} }
if keyboard_input.just_pressed(settings.key_back) if keyboard_input.just_pressed(settings.key_back)
|| keyboard_input.just_pressed(settings.key_mousedown) || keyboard_input.just_pressed(settings.key_mousedown)
|| keyboard_input.just_pressed(KeyCode::ArrowDown) || keyboard_input.just_pressed(KeyCode::ArrowDown)
{ {
menustate.cursor = if menustate.cursor == last_menu_entry { menustate.cursor = if menustate.cursor == last_menu_entry {
0 0
@ -504,7 +547,7 @@ pub fn handle_input(
ew_updatemenu.send(UpdateMenuEvent); ew_updatemenu.send(UpdateMenuEvent);
} }
if keyboard_input.just_pressed(settings.key_interact) if keyboard_input.just_pressed(settings.key_interact)
|| keyboard_input.just_pressed(KeyCode::Enter) || keyboard_input.just_pressed(KeyCode::Enter)
{ {
ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click)); ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::Click));
match MENUDEF[menustate.cursor].1 { match MENUDEF[menustate.cursor].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);
}, }
}; };
} }
} }

View file

@ -24,8 +24,8 @@ pub const EARTH_GRAVITY: f32 = 9.81;
pub const C: f64 = 299792458.0; // m/s pub const C: f64 = 299792458.0; // m/s
pub const G: f64 = 6.6743015e-11; // Gravitational constant in Nm²/kg² pub const G: f64 = 6.6743015e-11; // Gravitational constant in Nm²/kg²
pub const SOL_RADIUS: f64 = 696_300_000.0; pub const SOL_RADIUS: f64 = 696_300_000.0;
pub const JUPITER_RADIUS: f64 = 71_492_000.0; pub const JUPITER_RADIUS: f64 = 71_492_000.0;
pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0; pub const JUPITER_RING_RADIUS: f64 = 229_000_000.0;
pub const SOL_MASS: f64 = 1.9885e30; pub const SOL_MASS: f64 = 1.9885e30;
@ -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");
} }

View file

@ -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,24 +484,22 @@ 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}"); } else {
} else { info!("Loaded preferences from internal defaults");
info!("Loaded preferences from internal defaults");
}
pref.source_file = path;
dbg!(&pref);
return pref;
}
Err(error) => {
error!("Failed to read preference line: {error}");
return Preferences::default();
} }
pref.source_file = path;
dbg!(&pref);
return pref;
} }
} Err(error) => {
error!("Failed to read preference line: {error}");
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.

View file

@ -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()
}, },
)); ));
}, }
//_ => {},
} }
} }
} }

View file

@ -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,17 +101,15 @@ 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 *
let star_color_handle = materials.add(StandardMaterial { let star_color_handle = materials.add(StandardMaterial {
base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)), base_color: Color::rgb(scale_color(r), scale_color(g), scale_color(b)),
@ -176,8 +182,8 @@ fn spawn_despawn_asteroids(
id2pos: Res<game::Id2Pos>, id2pos: Res<game::Id2Pos>,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
) { ) {
if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() { if !timer.0.tick(time.delta()).just_finished() || q_player.is_empty() {
//if q_player.is_empty() { //if q_player.is_empty() {
return; return;
} }
let jupiter_pos = if let Some(jupiter_pos) = id2pos.0.get(&"jupiter".to_string()) { let jupiter_pos = if let Some(jupiter_pos) = id2pos.0.get(&"jupiter".to_string()) {
@ -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,11 +297,12 @@ 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
origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0, + DVec3::new(
origin.y as f64 * ASTEROID_SPAWN_STEP + wobble * rand_y * 2.0 - 1.0, origin.x as f64 * ASTEROID_SPAWN_STEP + wobble * rand_x * 2.0 - 1.0,
origin.z as f64 * ASTEROID_SPAWN_STEP + wobble * rand_z * 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,
);
// Spawn // Spawn
let mut entity_commands = commands.spawn(( let mut entity_commands = commands.spawn((
@ -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(
entity: entity_commands.id(), origin,
//viewdistance: 99999999.0, AsteroidData {
}); entity: entity_commands.id(),
//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>,
) { ) {