Make selfish bastards ambush people when they're alone.

This commit is contained in:
Tormod G. Hellen 2022-06-14 23:14:42 +02:00 committed by Tormod G. Hellen
parent c6bcdd7a2c
commit 753a51e683
No known key found for this signature in database
GPG Key ID: 48FAFB3169034023
3 changed files with 50 additions and 7 deletions

View File

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed ### Changed
- Use fluent for translations - Use fluent for translations
- First tab on Login screen triggers username focus - First tab on Login screen triggers username focus
- Certain NPCs will now attack when alone with victim
### Removed ### Removed

View File

@ -706,6 +706,8 @@ impl PersonalityBase {
* don't want everyone to be completely weird. * don't want everyone to be completely weird.
*/ */
pub fn to_personality(&self) -> Personality { pub fn to_personality(&self) -> Personality {
let will_ambush = self.agreeableness < Personality::LOW_THRESHOLD
&& self.conscientiousness < Personality::LOW_THRESHOLD;
let mut chat_traits: EnumSet<PersonalityTrait> = EnumSet::new(); let mut chat_traits: EnumSet<PersonalityTrait> = EnumSet::new();
if self.openness > Personality::HIGH_THRESHOLD { if self.openness > Personality::HIGH_THRESHOLD {
chat_traits.insert(PersonalityTrait::Open); chat_traits.insert(PersonalityTrait::Open);
@ -752,12 +754,14 @@ impl PersonalityBase {
} }
Personality { Personality {
personality_traits: chat_traits, personality_traits: chat_traits,
will_ambush,
} }
} }
} }
pub struct Personality { pub struct Personality {
pub personality_traits: EnumSet<PersonalityTrait>, pub personality_traits: EnumSet<PersonalityTrait>,
pub will_ambush: bool,
} }
#[derive(EnumSetType)] #[derive(EnumSetType)]

View File

@ -48,6 +48,7 @@ use common::{
}; };
use common_base::prof_span; use common_base::prof_span;
use common_ecs::{Job, Origin, ParMode, Phase, System}; use common_ecs::{Job, Origin, ParMode, Phase, System};
use itertools::Itertools;
use rand::{thread_rng, Rng}; use rand::{thread_rng, Rng};
use rayon::iter::ParallelIterator; use rayon::iter::ParallelIterator;
use specs::{Entity as EcsEntity, Join, ParJoin, Read, WriteExpect, WriteStorage}; use specs::{Entity as EcsEntity, Join, ParJoin, Read, WriteExpect, WriteStorage};
@ -748,11 +749,44 @@ impl<'a> AgentData<'a> {
agent.action_state.timer = 0.0; agent.action_state.timer = 0.0;
let mut aggro_on = false; let mut aggro_on = false;
// Search the area.
// TODO: choose target by more than just distance
let common::CachedSpatialGrid(grid) = self.cached_spatial_grid;
let entities_nearby = grid
.in_circle_aabr(self.pos.0.xy(), agent.psyche.search_dist())
.collect_vec();
let can_ambush = |entity: EcsEntity, read_data: &ReadData| {
let self_different_from_entity = || {
read_data
.uids
.get(entity)
.map_or(false, |eu| eu != self.uid)
};
if self.will_ambush()
&& self_different_from_entity()
&& !self.passive_towards(entity, read_data)
{
let surrounding_humanoids = entities_nearby
.iter()
.filter(|e| read_data.bodies.get(**e).map_or(false, |b| b.is_humanoid()))
.collect_vec();
surrounding_humanoids.len() == 2
&& surrounding_humanoids.iter().any(|e| **e == entity)
} else {
false
}
};
let get_pos = |entity| read_data.positions.get(entity); let get_pos = |entity| read_data.positions.get(entity);
let get_enemy = |(entity, attack_target): (EcsEntity, bool)| { let get_enemy = |(entity, attack_target): (EcsEntity, bool)| {
if attack_target { if attack_target {
if self.is_enemy(entity, read_data) { if self.is_enemy(entity, read_data) {
Some((entity, true)) Some((entity, true))
} else if can_ambush(entity, read_data) {
aggro_on = true;
Some((entity, true))
} else if self.should_defend(entity, read_data) { } else if self.should_defend(entity, read_data) {
if let Some(attacker) = get_attacker(entity, read_data) { if let Some(attacker) = get_attacker(entity, read_data) {
if !self.passive_towards(attacker, read_data) { if !self.passive_towards(attacker, read_data) {
@ -822,13 +856,9 @@ impl<'a> AgentData<'a> {
|| self.can_see_entity(agent, controller, entity, e_pos, read_data) || self.can_see_entity(agent, controller, entity, e_pos, read_data)
}; };
// Search the area. let target = entities_nearby
// TODO: choose target by more than just distance .iter()
let common::CachedSpatialGrid(grid) = self.cached_spatial_grid; .filter_map(|e| is_valid_target(*e))
let target = grid
.in_circle_aabr(self.pos.0.xy(), agent.psyche.search_dist())
.filter_map(is_valid_target)
.filter_map(get_enemy) .filter_map(get_enemy)
.filter_map(|(entity, attack_target)| { .filter_map(|(entity, attack_target)| {
get_pos(entity).map(|pos| (entity, pos, attack_target)) get_pos(entity).map(|pos| (entity, pos, attack_target))
@ -1632,6 +1662,14 @@ impl<'a> AgentData<'a> {
|| (is_villager(self.alignment) && is_dressed_as_cultist(entity, read_data))) || (is_villager(self.alignment) && is_dressed_as_cultist(entity, read_data)))
} }
fn will_ambush(&self) -> bool {
self.health
.map_or(false, |h| h.current() / h.maximum() > 0.7)
&& self
.rtsim_entity
.map_or(false, |re| re.brain.personality.will_ambush)
}
fn should_defend(&self, entity: EcsEntity, read_data: &ReadData) -> bool { fn should_defend(&self, entity: EcsEntity, read_data: &ReadData) -> bool {
let entity_alignment = read_data.alignments.get(entity); let entity_alignment = read_data.alignments.get(entity);