mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Have agents update awareness of targets.
This is preliminary and has no visible effect.
This commit is contained in:
parent
f45c465c75
commit
7502cd664a
@ -514,6 +514,7 @@ pub struct Agent {
|
||||
/// Always clamped between `0.0` and `1.0`.
|
||||
pub struct Awareness {
|
||||
level: f32,
|
||||
reached: bool,
|
||||
}
|
||||
impl fmt::Display for Awareness {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:.2}", self.level) }
|
||||
@ -528,11 +529,15 @@ impl Awareness {
|
||||
pub fn new(level: f32) -> Self {
|
||||
Self {
|
||||
level: level.clamp(Self::UNAWARE, Self::AWARE),
|
||||
reached: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The level of awareness as a decimal.
|
||||
pub fn level(&self) -> f32 { self.level }
|
||||
|
||||
/// The level of awareness in English. To see if awareness has been fully
|
||||
/// reached, use `self.reached()`.
|
||||
pub fn state(&self) -> AwarenessState {
|
||||
if self.level == Self::AWARE {
|
||||
AwarenessState::Aware
|
||||
@ -547,13 +552,22 @@ impl Awareness {
|
||||
}
|
||||
}
|
||||
|
||||
/// Awareness was reached at some point and has not been reset.
|
||||
pub fn reached(&self) -> bool { self.reached }
|
||||
|
||||
pub fn change_by(&mut self, amount: f32, dt: f32) {
|
||||
let change = amount * dt * 30.0;
|
||||
self.level = (self.level + change).clamp(Self::UNAWARE, Self::AWARE);
|
||||
|
||||
if self.state() == AwarenessState::Aware {
|
||||
self.reached = true;
|
||||
} else if self.state() == AwarenessState::Unaware {
|
||||
self.reached = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||
pub enum AwarenessState {
|
||||
Unaware = 0,
|
||||
Low = 1,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
consts::{
|
||||
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, PARTIAL_PATH_DIST,
|
||||
SEPARATION_BIAS, SEPARATION_DIST,
|
||||
SEPARATION_BIAS, SEPARATION_DIST, STD_AWARENESS_DECREMENT,
|
||||
},
|
||||
data::{AgentData, AttackData, Path, ReadData, Tactic, TargetData},
|
||||
util::{
|
||||
@ -163,6 +163,10 @@ impl<'a> AgentData<'a> {
|
||||
read_data: &ReadData,
|
||||
rng: &mut impl Rng,
|
||||
) {
|
||||
agent
|
||||
.awareness
|
||||
.change_by(STD_AWARENESS_DECREMENT, read_data.dt.0);
|
||||
|
||||
// Light lanterns at night
|
||||
// TODO Add a method to turn on NPC lanterns underground
|
||||
let lantern_equipped = self
|
||||
@ -653,13 +657,10 @@ impl<'a> AgentData<'a> {
|
||||
},
|
||||
};
|
||||
|
||||
let can_sense_directly_near =
|
||||
{ |e_pos: &Pos| e_pos.0.distance_squared(self.pos.0) < 5_f32.powi(2) };
|
||||
|
||||
let is_detected = |entity: EcsEntity, e_pos: &Pos| {
|
||||
let chance = thread_rng().gen_bool(0.3);
|
||||
|
||||
(can_sense_directly_near(e_pos) && chance)
|
||||
(self.can_sense_directly_near(e_pos) && chance)
|
||||
|| self.can_see_entity(agent, controller, entity, e_pos, read_data)
|
||||
};
|
||||
|
||||
@ -1466,7 +1467,7 @@ impl<'a> AgentData<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn can_see_entity(
|
||||
pub fn can_see_entity(
|
||||
&self,
|
||||
agent: &Agent,
|
||||
controller: &Controller,
|
||||
@ -1499,6 +1500,10 @@ impl<'a> AgentData<'a> {
|
||||
&& entities_have_line_of_sight(self.pos, self.body, other_pos, other_body, read_data)
|
||||
}
|
||||
|
||||
pub fn can_sense_directly_near(&self, e_pos: &Pos) -> bool {
|
||||
e_pos.0.distance_squared(self.pos.0) < 5_f32.powi(2)
|
||||
}
|
||||
|
||||
pub fn menacing(
|
||||
&self,
|
||||
agent: &mut Agent,
|
||||
|
@ -12,3 +12,4 @@ pub const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0;
|
||||
pub const HEALING_ITEM_THRESHOLD: f32 = 0.5;
|
||||
pub const IDLE_HEALING_ITEM_THRESHOLD: f32 = 0.999;
|
||||
pub const DEFAULT_ATTACK_RANGE: f32 = 2.0;
|
||||
pub const STD_AWARENESS_DECREMENT: f32 = -0.001;
|
||||
|
@ -196,6 +196,7 @@ impl<'a> System<'a> for Sys {
|
||||
cached_spatial_grid: &read_data.cached_spatial_grid,
|
||||
msm: &read_data.msm,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Behavior tree
|
||||
///////////////////////////////////////////////////////////
|
||||
|
@ -2,7 +2,8 @@ use crate::rtsim::Entity as RtSimEntity;
|
||||
use common::{
|
||||
comp::{
|
||||
agent::{
|
||||
AgentEvent, Target, TimerAction, DEFAULT_INTERACTION_TIME, TRADE_INTERACTION_TIME,
|
||||
AgentEvent, AwarenessState, Target, TimerAction, DEFAULT_INTERACTION_TIME,
|
||||
TRADE_INTERACTION_TIME,
|
||||
},
|
||||
Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction,
|
||||
ControlEvent, Controller, InputKind, InventoryEvent, UtteranceKind,
|
||||
@ -26,7 +27,7 @@ use self::interaction::{
|
||||
use super::{
|
||||
consts::{
|
||||
DAMAGE_MEMORY_DURATION, FLEE_DURATION, HEALING_ITEM_THRESHOLD, MAX_FLEE_DIST,
|
||||
MAX_FOLLOW_DIST, NPC_PICKUP_RANGE, RETARGETING_THRESHOLD_SECONDS,
|
||||
MAX_FOLLOW_DIST, NPC_PICKUP_RANGE, RETARGETING_THRESHOLD_SECONDS, STD_AWARENESS_DECREMENT,
|
||||
},
|
||||
data::{AgentData, ReadData, TargetData},
|
||||
util::{get_entity_by_id, is_dead, is_dead_or_invulnerable, is_invulnerable, stop_pursuing},
|
||||
@ -83,7 +84,8 @@ impl BehaviorTree {
|
||||
Self {
|
||||
tree: vec![
|
||||
untarget_if_dead,
|
||||
do_hostile_tree_if_hostile,
|
||||
update_target_awareness,
|
||||
do_hostile_tree_if_hostile_and_aware,
|
||||
do_pet_tree_if_owned,
|
||||
do_pickup_loot,
|
||||
do_idle_tree,
|
||||
@ -230,6 +232,8 @@ fn target_if_attacked(bdata: &mut BehaviorData) -> bool {
|
||||
.push_event(ControlEvent::Utterance(UtteranceKind::Angry));
|
||||
}
|
||||
|
||||
bdata.agent.awareness.change_by(1.0, bdata.read_data.dt.0);
|
||||
|
||||
// Determine whether the new target should be a priority
|
||||
// over the old one (i.e: because it's either close or
|
||||
// because they attacked us).
|
||||
@ -299,10 +303,13 @@ fn untarget_if_dead(bdata: &mut BehaviorData) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// If target is hostile, do the hostile tree and stop the current BehaviorTree
|
||||
fn do_hostile_tree_if_hostile(bdata: &mut BehaviorData) -> bool {
|
||||
/// If target is hostile and agent is aware of target, do the hostile tree and
|
||||
/// stop the current BehaviorTree
|
||||
fn do_hostile_tree_if_hostile_and_aware(bdata: &mut BehaviorData) -> bool {
|
||||
let aware = bdata.agent.awareness.reached();
|
||||
|
||||
if let Some(Target { hostile, .. }) = bdata.agent.target {
|
||||
if hostile {
|
||||
if aware && hostile {
|
||||
BehaviorTree::hostile().run(bdata);
|
||||
return true;
|
||||
}
|
||||
@ -496,6 +503,43 @@ fn hurt_utterance(bdata: &mut BehaviorData) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn update_target_awareness(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
agent_data,
|
||||
read_data,
|
||||
controller,
|
||||
..
|
||||
} = bdata;
|
||||
|
||||
let target = agent.target.map(|t| t.target);
|
||||
let tgt_pos = target.and_then(|t| read_data.positions.get(t));
|
||||
|
||||
if let (Some(target), Some(tgt_pos)) = (target, tgt_pos) {
|
||||
let perceives_target = agent_data
|
||||
.can_see_entity(agent, controller, target, tgt_pos, read_data)
|
||||
|| agent_data.can_sense_directly_near(tgt_pos);
|
||||
|
||||
if perceives_target {
|
||||
agent.awareness.change_by(0.04, read_data.dt.0);
|
||||
} else {
|
||||
agent
|
||||
.awareness
|
||||
.change_by(STD_AWARENESS_DECREMENT, read_data.dt.0);
|
||||
}
|
||||
} else {
|
||||
agent
|
||||
.awareness
|
||||
.change_by(STD_AWARENESS_DECREMENT, read_data.dt.0);
|
||||
}
|
||||
|
||||
if bdata.agent.awareness.state() == AwarenessState::Unaware {
|
||||
bdata.agent.target = None;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn do_combat(bdata: &mut BehaviorData) -> bool {
|
||||
let BehaviorData {
|
||||
agent,
|
||||
|
Loading…
Reference in New Issue
Block a user