From c69baa80e911288b5f0b67d0593c1ecf966282b1 Mon Sep 17 00:00:00 2001
From: yuni <hut@hut.pm>
Date: Mon, 2 Dec 2024 00:54:02 +0100
Subject: [PATCH] Icarus can now go sky diving with you \o/

---
 src/actor.rs            |  5 +++--
 src/chat.rs             | 25 ++++++++++++++++++++++++-
 src/chats/serenity.yaml | 20 ++++++++++++++++++++
 src/cmd.rs              |  7 ++++++-
 4 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/src/actor.rs b/src/actor.rs
index a6c1692..4e77c85 100644
--- a/src/actor.rs
+++ b/src/actor.rs
@@ -235,6 +235,8 @@ pub struct HasAtmosphere {
     pub r_inner: f64,
     pub r_outer: f64,
 }
+#[derive(Component)]
+pub struct NPCIcarus;
 
 #[derive(Component)]
 pub struct LifeForm {
@@ -822,8 +824,7 @@ fn handle_wants_acceleration(
             if accel.brake {
                 let stop_direction = (matching.v - v.0).as_vec3();
                 if stop_direction.length_squared() > 0.003 {
-                    delta_v =
-                        (trans.rotation.inverse() * stop_direction.normalize()).as_dvec3();
+                    delta_v = (trans.rotation.inverse() * stop_direction.normalize()).as_dvec3();
                     engine.currently_matching_velocity = true;
                     thruster_on = true; // is this redundant?
                 }
diff --git a/src/chat.rs b/src/chat.rs
index f805776..ef4f41a 100644
--- a/src/chat.rs
+++ b/src/chat.rs
@@ -907,6 +907,10 @@ pub fn handle_chat_scripts(
         (&mut Position, &mut Rotation, &mut LinearVelocity),
         With<actor::PlayerCamera>,
     >,
+    mut q_icarus: Query<
+        (Entity, &mut Position, &mut Rotation, &mut LinearVelocity),
+        (With<actor::NPCIcarus>, Without<actor::PlayerCamera>),
+    >,
     mut q_chats: Query<&mut Chat>,
     mut ew_sfx: EventWriter<audio::PlaySfxEvent>,
     mut ew_effect: EventWriter<visual::SpawnEffectEvent>,
@@ -1030,6 +1034,7 @@ pub fn handle_chat_scripts(
                 let dest = vars.get("$$dive_destination");
                 let speed_kmh = vars.getf("$$dive_speed");
                 let angle = vars.getf("$$dive_angle");
+                let icarus_joins = vars.getb("$$dive_with_icarus");
 
                 if let (Some(dest), Some(speed_kmh), Some(angle)) = (dest, speed_kmh, angle) {
                     let distance = if angle < 10.0 {
@@ -1069,11 +1074,29 @@ pub fn handle_chat_scripts(
                     let towards_jupiter =
                         look_at_quat(target_pos, *jupiter_pos, Dir3::X.as_dvec3());
                     if let Ok((mut pos, mut rot, mut v)) = q_playercam.get_single_mut() {
+                        let offset = (DQuat::from_rotation_x(20.0f64.to_radians())
+                            * towards_jupiter)
+                            * DVec3::new(0.0, 0.0, -15.0);
                         v.0 = v_target;
-                        pos.0 = target_pos;
+                        pos.0 = target_pos + offset;
                         rot.0 = towards_jupiter;
                     }
 
+                    if icarus_joins {
+                        if let Ok((entity, mut pos, mut rot, mut v)) = q_icarus.get_single_mut() {
+                            commands
+                                .entity(entity)
+                                .remove::<actor::WantsMatchVelocityWith>();
+                            commands.entity(entity).remove::<actor::WantsMaxVelocity>();
+                            v.0 = v_target;
+                            pos.0 = target_pos;
+                            rot.0 = towards_jupiter;
+                            vars.set_in_scope("$", "dive_in_progress_icarus", "1".to_string());
+                        } else {
+                            error!("Could not dive with Icarus, failed to find entity with component NPCIcarus");
+                        }
+                    }
+
                     suit.oxygen = suit.oxygen_max;
                     gforce.ignore_gforce_seconds = 1.0;
                     ew_sfx.send(audio::PlaySfxEvent(audio::Sfx::WakeUp));
diff --git a/src/chats/serenity.yaml b/src/chats/serenity.yaml
index 45b62d6..6fa30c5 100644
--- a/src/chats/serenity.yaml
+++ b/src/chats/serenity.yaml
@@ -10,6 +10,8 @@
 
 # Icarus {{{1
 - chat: Icarus
+- if $$dive_in_progress_icarus:
+  - WOOOOOOOOOOOOOHOOOOOOOOOOO!!!!!!
 - if $met:
   - Oh hey, you're back!
   - goto: node_main
@@ -87,6 +89,24 @@
 - I think I need a lowdown on reality...:
   - Sure, what do you wanna know?
   - goto: node_lowdown
+- Let's do something crazy together!:
+  - How about some sky diving into the atmosphere of Jupiter?
+  - I wanted to try it for a while, but it is kind of scary alone.
+  - Awesome idea, let's do it!:
+    - Yes!!
+    - set: $$dive_with_icarus
+    - Let's ask FASTravel to pick us up and launch us into Jupiter. Would take too long to fly ourselves.
+    - label: node_fastravel
+    - What's FASTravel?:
+      - The main cargo corporation around here, surely you've seen them around?
+      - Or just give them a phone call. [press ESC]
+      - goto: node_fastravel
+    - Ok, let's go!:
+      - ALRIGHT, LETS DO SOME SKY DIVING! Call up FASTravel, I will join you.
+      - goto: EXIT
+  - That sounds like a good way to get killed.:
+    - Yeah... it does, doesn't it?
+    - Maybe I'll stick to exploring Jupiter's atmosphere in VR.
 - Can I ask you some general questions?:
   - Yeah, anything.
   - goto: generic_questions
diff --git a/src/cmd.rs b/src/cmd.rs
index ca433fa..a9e5cb7 100644
--- a/src/cmd.rs
+++ b/src/cmd.rs
@@ -19,6 +19,7 @@ use regex::Regex;
 
 pub const ID_SPECIAL_PLAYERCAM: &str = "PLAYERCAMERA";
 pub const ID_PLAYER: &str = "player";
+pub const ID_ICARUS: &str = "Icarus";
 pub const ID_EARTH: &str = "earth";
 pub const ID_SOL: &str = "sol";
 pub const ID_JUPITER: &str = "jupiter";
@@ -1171,7 +1172,7 @@ fn spawn_scenes(
                         state.angular_momentum = DVec3::ZERO;
 
                         if template == "tutorialnpc" {
-                            state.id = "Icarus".to_string();
+                            state.id = ID_ICARUS.to_string();
                             state.name = Some(state.id.clone());
                             state.chat = state.id.clone();
 
@@ -1332,6 +1333,7 @@ fn spawn_entities(
         let mut rotation = state.rotation;
 
         let is_jupiter = state.id == ID_JUPITER;
+        let is_icarus = state.id == ID_ICARUS;
 
         // Preprocessing
         let mut absolute_pos = if let Some(id) = &state.relative_to {
@@ -1408,6 +1410,9 @@ fn spawn_entities(
         actor.insert(world::DespawnOnPlayerDeath);
         actor.insert(actor::HitPoints::default());
         actor.insert(Position::from(absolute_pos));
+        if is_icarus {
+            actor.insert(actor::NPCIcarus);
+        }
         if state.is_sphere {
             let sphere_texture_handle = if let Some(model) = &state.model {
                 Some(asset_server.load(format!("models/textures/{}.jpg", model)))