From 301205c36995532186b106006c9f6002059ab646 Mon Sep 17 00:00:00 2001
From: Imbris <imbrisf@gmail.com>
Date: Fri, 31 Jul 2020 00:30:17 -0400
Subject: [PATCH] Don't give exp when killing self or group members

---
 server/src/events/entity_manipulation.rs | 110 +++++++++++++----------
 1 file changed, 63 insertions(+), 47 deletions(-)

diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs
index cdb3ee8ba0..b6900b70fd 100644
--- a/server/src/events/entity_manipulation.rs
+++ b/server/src/events/entity_manipulation.rs
@@ -55,56 +55,72 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
         state.notify_registered_clients(comp::ChatType::Kill.server_msg(msg));
     }
 
-    {
-        // Give EXP to the killer if entity had stats
+    // Give EXP to the killer if entity had stats
+    (|| {
         let mut stats = state.ecs().write_storage::<Stats>();
-        if let Some(entity_stats) = stats.get(entity).cloned() {
-            if let HealthSource::Attack { by } | HealthSource::Projectile { owner: Some(by) } =
-                cause
-            {
-                const MAX_EXP_DIST: f32 = 150.0;
-                // Attacker gets same as exp of everyone else
-                const ATTACKER_EXP_WEIGHT: f32 = 1.0;
-                let mut exp_reward = (entity_stats.body_type.base_exp()
-                    + entity_stats.level.level() * entity_stats.body_type.base_exp_increase())
-                    as f32;
-                state.ecs().entity_from_uid(by.into()).map(|attacker| {
-                    // Distribute EXP to group
-                    let groups = state.ecs().read_storage::<Group>();
-                    let positions = state.ecs().read_storage::<Pos>();
-                    // TODO: rework if change to groups makes it easier to iterate entities in a
-                    // group
-                    if let (Some(attacker_group), Some(pos)) =
-                        (groups.get(attacker), positions.get(entity))
-                    {
-                        let members_in_range = (&state.ecs().entities(), &groups, &positions)
-                            .join()
-                            .filter(|(entity, group, member_pos)| {
-                                *group == attacker_group
-                                    && *entity != attacker
-                                    && pos.0.distance_squared(member_pos.0) < MAX_EXP_DIST.powi(2)
-                            })
-                            .map(|(entity, _, _)| entity)
-                            .collect::<Vec<_>>();
-                        let exp =
-                            exp_reward / (members_in_range.len() as f32 + ATTACKER_EXP_WEIGHT);
-                        exp_reward = exp * ATTACKER_EXP_WEIGHT;
-                        members_in_range.into_iter().for_each(|e| {
-                            if let Some(stats) = stats.get_mut(e) {
-                                stats.exp.change_by(exp.ceil() as i64);
-                            }
-                        });
-                    }
+        let by = if let HealthSource::Attack { by } | HealthSource::Projectile { owner: Some(by) } =
+            cause
+        {
+            by
+        } else {
+            return;
+        };
+        let attacker = if let Some(attacker) = state.ecs().entity_from_uid(by.into()) {
+            attacker
+        } else {
+            return;
+        };
+        let entity_stats = if let Some(entity_stats) = stats.get(entity) {
+            entity_stats
+        } else {
+            return;
+        };
 
-                    if let Some(attacker_stats) = stats.get_mut(attacker) {
-                        // TODO: Discuss whether we should give EXP by Player
-                        // Killing or not.
-                        attacker_stats.exp.change_by(exp_reward.ceil() as i64);
-                    }
-                });
-            }
+        let groups = state.ecs().read_storage::<Group>();
+        let attacker_group = groups.get(attacker);
+        let destroyed_group = groups.get(entity);
+        // Don't give exp if attacker destroyed themselves or one of their group members
+        if (attacker_group.is_some() && attacker_group == destroyed_group) || attacker == entity {
+            return;
         }
-    }
+
+        // Maximum distance for other group members to receive exp
+        const MAX_EXP_DIST: f32 = 150.0;
+        // Attacker gets same as exp of everyone else
+        const ATTACKER_EXP_WEIGHT: f32 = 1.0;
+        let mut exp_reward = (entity_stats.body_type.base_exp()
+            + entity_stats.level.level() * entity_stats.body_type.base_exp_increase())
+            as f32;
+
+        // Distribute EXP to group
+        let positions = state.ecs().read_storage::<Pos>();
+        if let (Some(attacker_group), Some(pos)) = (attacker_group, positions.get(entity)) {
+            // TODO: rework if change to groups makes it easier to iterate entities in a
+            // group
+            let members_in_range = (&state.ecs().entities(), &groups, &positions)
+                .join()
+                .filter(|(entity, group, member_pos)| {
+                    *group == attacker_group
+                        && *entity != attacker
+                        && pos.0.distance_squared(member_pos.0) < MAX_EXP_DIST.powi(2)
+                })
+                .map(|(entity, _, _)| entity)
+                .collect::<Vec<_>>();
+            let exp = exp_reward / (members_in_range.len() as f32 + ATTACKER_EXP_WEIGHT);
+            exp_reward = exp * ATTACKER_EXP_WEIGHT;
+            members_in_range.into_iter().for_each(|e| {
+                if let Some(stats) = stats.get_mut(e) {
+                    stats.exp.change_by(exp.ceil() as i64);
+                }
+            });
+        }
+
+        if let Some(attacker_stats) = stats.get_mut(attacker) {
+            // TODO: Discuss whether we should give EXP by Player
+            // Killing or not.
+            attacker_stats.exp.change_by(exp_reward.ceil() as i64);
+        }
+    })();
 
     if state
         .ecs()