From 82273f0f368bdf1aa284fbd8113a5e545bc3d781 Mon Sep 17 00:00:00 2001 From: "Tormod G. Hellen" Date: Sat, 29 Jan 2022 00:07:06 +0100 Subject: [PATCH] Make friendly creatures defend each other. --- CHANGELOG.md | 1 + common/src/comp/body.rs | 63 ++++++++++++++++++++++++ server/src/sys/agent.rs | 104 ++++++++++++++++++++++++++-------------- 3 files changed, 132 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acafc0a25f..1eaef1588e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed the way light strength is rendered by moving processing from shader code (GPU) to CPU code - Bumped tracing-subscriber to resolve [RUSTSEC-2022-0006](https://rustsec.org/advisories/RUSTSEC-2022-0006) - Made /home command a mod+ exclusive +- Friendly creatures will now defend each other ### Removed diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index f64052232f..4795cd6ca6 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -153,6 +153,69 @@ impl< } impl Body { + pub fn is_same_species_as(&self, other: &Body) -> bool { + match self { + Body::Humanoid(b1) => match other { + Body::Humanoid(b2) => b1.species == b2.species, + _ => false, + }, + Body::QuadrupedSmall(b1) => match other { + Body::QuadrupedSmall(b2) => b1.species == b2.species, + _ => false, + }, + Body::QuadrupedMedium(b1) => match other { + Body::QuadrupedMedium(b2) => b1.species == b2.species, + _ => false, + }, + Body::BirdMedium(b1) => match other { + Body::BirdMedium(b2) => b1.species == b2.species, + _ => false, + }, + Body::BirdLarge(b1) => match other { + Body::BirdLarge(b2) => b1.species == b2.species, + _ => false, + }, + Body::FishMedium(b1) => match other { + Body::FishMedium(b2) => b1.species == b2.species, + _ => false, + }, + Body::Dragon(b1) => match other { + Body::Dragon(b2) => b1.species == b2.species, + _ => false, + }, + Body::FishSmall(b1) => match other { + Body::FishSmall(b2) => b1.species == b2.species, + _ => false, + }, + Body::BipedLarge(b1) => match other { + Body::BipedLarge(b2) => b1.species == b2.species, + _ => false, + }, + Body::BipedSmall(b1) => match other { + Body::BipedSmall(b2) => b1.species == b2.species, + _ => false, + }, + Body::Object(_) => false, + Body::Golem(b1) => match other { + Body::Golem(b2) => b1.species == b2.species, + _ => false, + }, + Body::Theropod(b1) => match other { + Body::Theropod(b2) => b1.species == b2.species, + _ => false, + }, + Body::QuadrupedLow(b1) => match other { + Body::QuadrupedLow(b2) => b1.species == b2.species, + _ => false, + }, + Body::Arthropod(b1) => match other { + Body::Arthropod(b2) => b1.species == b2.species, + _ => false, + }, + Body::Ship(_) => false, + } + } + pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) } pub fn is_campfire(&self) -> bool { matches!(self, Body::Object(object::Body::CampfireLit)) } diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 4fc8ae11e3..b22e5b83f4 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -1452,6 +1452,7 @@ impl<'a> AgentData<'a> { inventory, read_data.alignments.get(entity), read_data.char_states.get(entity), + read_data.bodies.get(entity), ) }) }; @@ -1502,16 +1503,28 @@ impl<'a> AgentData<'a> { }) }; - let guard_defending_villager = |e_health: &Health, e_alignment: Option<&Alignment>| { + let guard_other = |e_health: &Health, + e_body: Option<&Body>, + e_alignment: Option<&Alignment>| { let i_am_a_guard = read_data .stats .get(*self.entity) .map_or(false, |stats| stats.name == "Guard"); let other_is_a_villager = matches!(e_alignment, Some(Alignment::Npc)); - let villager_has_taken_damage = read_data.time.0 - e_health.last_change.time.0 < 5.0; + let we_are_friendly: bool = self.alignment.map_or(false, |ma| { + e_alignment.map_or(false, |ea| !ea.hostile_towards(*ma)) + }); + let we_share_species: bool = self.body.map_or(false, |mb| { + e_body.map_or(false, |eb| { + eb.is_same_species_as(mb) || (eb.is_humanoid() && mb.is_humanoid()) + }) + }); + let other_has_taken_damage = read_data.time.0 - e_health.last_change.time.0 < 5.0; let attacker_of = |health: &Health| health.last_change.damage_by(); - let i_should_defend = i_am_a_guard && other_is_a_villager && villager_has_taken_damage; + let i_should_defend = other_has_taken_damage + && ((we_are_friendly && we_share_species) || (i_am_a_guard && other_is_a_villager)); + i_should_defend .then(|| { attacker_of(e_health) @@ -1519,10 +1532,20 @@ impl<'a> AgentData<'a> { get_entity_by_id(damage_contributor.uid().0, read_data) }) .and_then(|attacker| { - read_data - .positions - .get(attacker) - .map(|a_pos| (attacker, *a_pos)) + read_data.alignments.get(attacker).and_then(|aa| { + self.alignment.and_then({ + |ma| { + if !ma.passive_towards(*aa) { + read_data + .positions + .get(attacker) + .map(|a_pos| (attacker, *a_pos)) + } else { + None + } + } + }) + }) }) }) .flatten() @@ -1583,36 +1606,45 @@ impl<'a> AgentData<'a> { }) }; - let possible_target = - |(entity, e_pos, e_health, e_stats, e_inventory, e_alignment, e_char_state): ( - EcsEntity, - &Pos, - &Health, - &Stats, - &Inventory, - Option<&Alignment>, - Option<&CharacterState>, - )| { - let can_target = within_reach(e_pos, e_char_state, e_inventory) - && entity != *self.entity - && !e_health.is_dead - && !is_invulnerable(entity, read_data); + let possible_target = |( + entity, + e_pos, + e_health, + e_stats, + e_inventory, + e_alignment, + e_char_state, + e_body, + ): ( + EcsEntity, + &Pos, + &Health, + &Stats, + &Inventory, + Option<&Alignment>, + Option<&CharacterState>, + Option<&Body>, + )| { + let can_target = within_reach(e_pos, e_char_state, e_inventory) + && entity != *self.entity + && !e_health.is_dead + && !is_invulnerable(entity, read_data); - if !can_target { - None - } else if is_owner_hostile(e_alignment) { - Some((entity, *e_pos)) - } else if let Some(villain_info) = guard_defending_villager(e_health, e_alignment) { - aggro_on = true; - Some(villain_info) - } else if rtsim_remember(e_stats, agent, event_emitter) - || npc_sees_cultist(e_stats, e_inventory, agent, event_emitter) - { - Some((entity, *e_pos)) - } else { - None - } - }; + if !can_target { + None + } else if is_owner_hostile(e_alignment) { + Some((entity, *e_pos)) + } else if let Some(villain_info) = guard_other(e_health, e_body, e_alignment) { + aggro_on = true; + Some(villain_info) + } else if rtsim_remember(e_stats, agent, event_emitter) + || npc_sees_cultist(e_stats, e_inventory, agent, event_emitter) + { + Some((entity, *e_pos)) + } else { + None + } + }; // Search area // TODO choose target by more than just distance