Make friendly creatures defend each other.

This commit is contained in:
Tormod G. Hellen 2022-01-29 00:07:06 +01:00
parent c946e945cf
commit 82273f0f36
3 changed files with 132 additions and 36 deletions

View File

@ -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 - 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) - Bumped tracing-subscriber to resolve [RUSTSEC-2022-0006](https://rustsec.org/advisories/RUSTSEC-2022-0006)
- Made /home command a mod+ exclusive - Made /home command a mod+ exclusive
- Friendly creatures will now defend each other
### Removed ### Removed

View File

@ -153,6 +153,69 @@ impl<
} }
impl Body { 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_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
pub fn is_campfire(&self) -> bool { matches!(self, Body::Object(object::Body::CampfireLit)) } pub fn is_campfire(&self) -> bool { matches!(self, Body::Object(object::Body::CampfireLit)) }

View File

@ -1452,6 +1452,7 @@ impl<'a> AgentData<'a> {
inventory, inventory,
read_data.alignments.get(entity), read_data.alignments.get(entity),
read_data.char_states.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 let i_am_a_guard = read_data
.stats .stats
.get(*self.entity) .get(*self.entity)
.map_or(false, |stats| stats.name == "Guard"); .map_or(false, |stats| stats.name == "Guard");
let other_is_a_villager = matches!(e_alignment, Some(Alignment::Npc)); 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 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 i_should_defend
.then(|| { .then(|| {
attacker_of(e_health) attacker_of(e_health)
@ -1519,10 +1532,20 @@ impl<'a> AgentData<'a> {
get_entity_by_id(damage_contributor.uid().0, read_data) get_entity_by_id(damage_contributor.uid().0, read_data)
}) })
.and_then(|attacker| { .and_then(|attacker| {
read_data read_data.alignments.get(attacker).and_then(|aa| {
.positions self.alignment.and_then({
.get(attacker) |ma| {
.map(|a_pos| (attacker, *a_pos)) if !ma.passive_towards(*aa) {
read_data
.positions
.get(attacker)
.map(|a_pos| (attacker, *a_pos))
} else {
None
}
}
})
})
}) })
}) })
.flatten() .flatten()
@ -1583,36 +1606,45 @@ impl<'a> AgentData<'a> {
}) })
}; };
let possible_target = let possible_target = |(
|(entity, e_pos, e_health, e_stats, e_inventory, e_alignment, e_char_state): ( entity,
EcsEntity, e_pos,
&Pos, e_health,
&Health, e_stats,
&Stats, e_inventory,
&Inventory, e_alignment,
Option<&Alignment>, e_char_state,
Option<&CharacterState>, e_body,
)| { ): (
let can_target = within_reach(e_pos, e_char_state, e_inventory) EcsEntity,
&& entity != *self.entity &Pos,
&& !e_health.is_dead &Health,
&& !is_invulnerable(entity, read_data); &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 { if !can_target {
None None
} else if is_owner_hostile(e_alignment) { } else if is_owner_hostile(e_alignment) {
Some((entity, *e_pos)) Some((entity, *e_pos))
} else if let Some(villain_info) = guard_defending_villager(e_health, e_alignment) { } else if let Some(villain_info) = guard_other(e_health, e_body, e_alignment) {
aggro_on = true; aggro_on = true;
Some(villain_info) Some(villain_info)
} else if rtsim_remember(e_stats, agent, event_emitter) } else if rtsim_remember(e_stats, agent, event_emitter)
|| npc_sees_cultist(e_stats, e_inventory, agent, event_emitter) || npc_sees_cultist(e_stats, e_inventory, agent, event_emitter)
{ {
Some((entity, *e_pos)) Some((entity, *e_pos))
} else { } else {
None None
} }
}; };
// Search area // Search area
// TODO choose target by more than just distance // TODO choose target by more than just distance