From 98a913195b5e22681bba0ad79c4e343a51e65755 Mon Sep 17 00:00:00 2001
From: Joshua Barretto <joshua.s.barretto@gmail.com>
Date: Wed, 7 Aug 2019 10:50:49 +0100
Subject: [PATCH] Added entity event system, fixed fall damage

---
 common/src/comp/event.rs        | 30 ++++++++++++++++++++++++++++++
 common/src/comp/mod.rs          |  2 ++
 common/src/state.rs             |  1 +
 common/src/sys/event_handler.rs | 33 +++++++++++++++++++++++++++++++++
 common/src/sys/mod.rs           |  7 +++++++
 common/src/sys/phys.rs          | 27 ++++++++++++++++-----------
 6 files changed, 89 insertions(+), 11 deletions(-)
 create mode 100644 common/src/comp/event.rs
 create mode 100644 common/src/sys/event_handler.rs

diff --git a/common/src/comp/event.rs b/common/src/comp/event.rs
new file mode 100644
index 0000000000..bed74d4669
--- /dev/null
+++ b/common/src/comp/event.rs
@@ -0,0 +1,30 @@
+use specs::Component;
+use specs_idvs::IDVStorage;
+use std::ops::{Deref, DerefMut};
+use vek::*;
+
+#[derive(Clone, Debug, Default)]
+pub struct Events(pub Vec<EntityEvent>);
+
+impl Deref for Events {
+    type Target = Vec<EntityEvent>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl DerefMut for Events {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl Component for Events {
+    type Storage = IDVStorage<Self>;
+}
+
+#[derive(Clone, Debug)]
+pub enum EntityEvent {
+    HitGround { vel: Vec3<f32> },
+}
diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs
index bee5458a2c..54ac7dd856 100644
--- a/common/src/comp/mod.rs
+++ b/common/src/comp/mod.rs
@@ -3,6 +3,7 @@ mod agent;
 mod animation;
 mod body;
 mod controller;
+mod event;
 mod inputs;
 mod inventory;
 mod last;
@@ -17,6 +18,7 @@ pub use agent::Agent;
 pub use animation::{Animation, AnimationInfo};
 pub use body::{humanoid, object, quadruped, quadruped_medium, Body};
 pub use controller::Controller;
+pub use event::{EntityEvent, Events};
 pub use inputs::{
     Attacking, CanBuild, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling, Wielding,
 };
diff --git a/common/src/state.rs b/common/src/state.rs
index 0a5a4ef7af..81cfc2d7bb 100644
--- a/common/src/state.rs
+++ b/common/src/state.rs
@@ -161,6 +161,7 @@ impl State {
         ecs.register::<comp::ForceUpdate>();
         ecs.register::<comp::InventoryUpdate>();
         ecs.register::<comp::Inventory>();
+        ecs.register::<comp::Events>();
         // Controller effects
         ecs.register::<comp::MoveDir>();
         ecs.register::<comp::OnGround>();
diff --git a/common/src/sys/event_handler.rs b/common/src/sys/event_handler.rs
new file mode 100644
index 0000000000..c7f6e72198
--- /dev/null
+++ b/common/src/sys/event_handler.rs
@@ -0,0 +1,33 @@
+use crate::{
+    comp::{EntityEvent, Events, HealthSource, Stats},
+    state::DeltaTime,
+};
+use log::warn;
+use specs::{Entities, Join, Read, System, WriteStorage};
+
+/// This system kills players
+pub struct Sys;
+impl<'a> System<'a> for Sys {
+    type SystemData = (
+        Entities<'a>,
+        WriteStorage<'a, Events>,
+        WriteStorage<'a, Stats>,
+    );
+
+    fn run(&mut self, (entities, mut events, mut stats): Self::SystemData) {
+        for (entity, mut events) in (&entities, &mut events).join() {
+            for event in events.drain(..) {
+                match event {
+                    EntityEvent::HitGround { vel } => {
+                        if let Some(stat) = stats.get_mut(entity) {
+                            let falldmg = (vel.z / 1.5 + 6.0) as i32;
+                            if falldmg < 0 {
+                                stat.health.change_by(falldmg, HealthSource::World);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs
index 7664b4af8a..a71a03bdcb 100644
--- a/common/src/sys/mod.rs
+++ b/common/src/sys/mod.rs
@@ -3,6 +3,7 @@ pub mod agent;
 pub mod animation;
 pub mod combat;
 pub mod controller;
+mod event_handler;
 pub mod movement;
 pub mod phys;
 mod stats;
@@ -19,6 +20,7 @@ const MOVEMENT_SYS: &str = "movement_sys";
 const COMBAT_SYS: &str = "combat_sys";
 const ANIMATION_SYS: &str = "animation_sys";
 const STATS_SYS: &str = "stats_sys";
+const EVENT_HANDLER_SYS: &str = "event_handler_sys";
 
 pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
     dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
@@ -33,4 +35,9 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
     dispatch_builder.add(combat::Sys, COMBAT_SYS, &[ACTION_STATE_SYS]);
     dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[ACTION_STATE_SYS]);
     dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
+    dispatch_builder.add(
+        event_handler::Sys,
+        EVENT_HANDLER_SYS,
+        &[AGENT_SYS, PHYS_SYS, ACTION_STATE_SYS, COMBAT_SYS],
+    );
 }
diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs
index de3045b0a6..674e7cd5fb 100644
--- a/common/src/sys/phys.rs
+++ b/common/src/sys/phys.rs
@@ -1,8 +1,8 @@
 use crate::{
     comp::HealthSource,
     comp::{
-        ActionState, Body, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Scale, Stats, Vel,
-        Wielding,
+        ActionState, Body, EntityEvent, Events, Jumping, MoveDir, OnGround, Ori, Pos, Rolling,
+        Scale, Stats, Vel, Wielding,
     },
     state::DeltaTime,
     terrain::TerrainMap,
@@ -42,7 +42,7 @@ impl<'a> System<'a> for Sys {
         WriteStorage<'a, Pos>,
         WriteStorage<'a, Vel>,
         WriteStorage<'a, Ori>,
-        WriteStorage<'a, Stats>,
+        WriteStorage<'a, Events>,
     );
 
     fn run(
@@ -58,11 +58,11 @@ impl<'a> System<'a> for Sys {
             mut positions,
             mut velocities,
             mut orientations,
-            mut stats,
+            mut events,
         ): Self::SystemData,
     ) {
         // Apply movement inputs
-        for (entity, a, scale, b, mut pos, mut vel, mut ori, mut stat) in (
+        for (entity, a, scale, b, mut pos, mut vel, mut ori) in (
             &entities,
             &action_states,
             scales.maybe(),
@@ -70,7 +70,6 @@ impl<'a> System<'a> for Sys {
             &mut positions,
             &mut velocities,
             &mut orientations,
-            &mut stats,
         )
             .join()
         {
@@ -210,12 +209,18 @@ impl<'a> System<'a> for Sys {
 
                     // When the resolution direction is pointing upwards, we must be on the ground
                     if resolve_dir.z > 0.0 && vel.0.z <= 0.0 {
-                        // Check for fall damage
-                        let falldmg = (vel.0.z / 1.5 + 6.0) as i32;
-                        if falldmg < 0 {
-                            stat.health.change_by(falldmg, HealthSource::World);
-                        }
                         on_ground = true;
+
+                        // Hitting the ground
+                        const COLLISION_VEL: f32 = GRAVITY * 0.75; // Falling for 0.75 seconds
+                        if vel.0.z < -COLLISION_VEL {
+                            if events.get(entity).is_none() {
+                                events.insert(entity, Events::default());
+                            }
+                            events
+                                .get_mut(entity) // TODO: Use get_mut_or_default when updating to SPECS 15
+                                .map(|e| e.push(EntityEvent::HitGround { vel: vel.0 }));
+                        }
                     }
 
                     // When the resolution direction is non-vertical, we must be colliding with a wall