From 3d4ad29d1d76ff899039c9bf1ddb601860adbc30 Mon Sep 17 00:00:00 2001 From: holychowders Date: Fri, 28 Oct 2022 21:31:49 +0000 Subject: [PATCH] Flee if attacked even if attacker is not close. --- CHANGELOG.md | 1 + common/src/comp/agent.rs | 7 +++++- server/agent/src/consts.rs | 4 ++- server/src/sys/agent/behavior_tree.rs | 35 +++++++++++++++++++-------- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd6cb667b..9c90aa4256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed npcs using `/say` instead of `/tell` - Camera jittering in third person has been significantly reduced - Many water shader issues have been fixed +- Flee if attacked even if attacker is not close. ## [0.13.0] - 2022-07-23 diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 69844f78bd..a874aa02e2 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -14,7 +14,7 @@ use std::{collections::VecDeque, fmt}; use strum::{EnumIter, IntoEnumIterator}; use vek::*; -use super::dialogue::Subject; +use super::{dialogue::Subject, Pos}; pub const DEFAULT_INTERACTION_TIME: f32 = 3.0; pub const TRADE_INTERACTION_TIME: f32 = 300.0; @@ -531,6 +531,10 @@ pub struct Agent { pub sounds_heard: Vec, pub awareness: f32, pub position_pid_controller: Option, Vec3) -> f32, 16>>, + /// Position from which to flee. Intended to be the agent's position plus a + /// random position offset, to be used when a random flee direction is + /// required and reset each time the flee timer is reset. + pub flee_from_pos: Option, } /// State persistence object for the behavior tree @@ -563,6 +567,7 @@ impl Agent { sounds_heard: Vec::new(), awareness: 0.0, position_pid_controller: None, + flee_from_pos: None, } } diff --git a/server/agent/src/consts.rs b/server/agent/src/consts.rs index 24d2f34008..ac5722121f 100644 --- a/server/agent/src/consts.rs +++ b/server/agent/src/consts.rs @@ -6,7 +6,9 @@ pub const MAX_PATH_DIST: f32 = 170.0; pub const PARTIAL_PATH_DIST: f32 = 50.0; pub const SEPARATION_DIST: f32 = 10.0; pub const SEPARATION_BIAS: f32 = 0.8; -pub const MAX_FLEE_DIST: f32 = 20.0; +/// The distance within which agents will flee directly away if attacked from +/// distance or if attacker detected. +pub const NORMAL_FLEE_DIR_DIST: f32 = 20.0; pub const AVG_FOLLOW_DIST: f32 = 6.0; pub const RETARGETING_THRESHOLD_SECONDS: f64 = 10.0; pub const HEALING_ITEM_THRESHOLD: f32 = 0.5; diff --git a/server/src/sys/agent/behavior_tree.rs b/server/src/sys/agent/behavior_tree.rs index b13eab80d0..1a507230e5 100644 --- a/server/src/sys/agent/behavior_tree.rs +++ b/server/src/sys/agent/behavior_tree.rs @@ -5,17 +5,18 @@ use common::{ AgentEvent, Target, TimerAction, DEFAULT_INTERACTION_TIME, TRADE_INTERACTION_TIME, }, Agent, Alignment, BehaviorCapability, BehaviorState, Body, BuffKind, ControlAction, - ControlEvent, Controller, InputKind, InventoryEvent, UtteranceKind, + ControlEvent, Controller, InputKind, InventoryEvent, Pos, UtteranceKind, }, event::{Emitter, ServerEvent}, path::TraversalConfig, }; -use rand::{prelude::ThreadRng, Rng}; +use rand::{prelude::ThreadRng, thread_rng, Rng}; +use server_agent::consts::NORMAL_FLEE_DIR_DIST; use specs::{ saveload::{Marker, MarkerAllocator}, Entity as EcsEntity, }; -use vek::Vec2; +use vek::{Vec2, Vec3}; use self::interaction::{ handle_inbox_cancel_interactions, handle_inbox_finished_trade, handle_inbox_talk, @@ -25,8 +26,8 @@ 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, + DAMAGE_MEMORY_DURATION, FLEE_DURATION, HEALING_ITEM_THRESHOLD, MAX_FOLLOW_DIST, + NPC_PICKUP_RANGE, RETARGETING_THRESHOLD_SECONDS, }, data::{AgentData, ReadData, TargetData}, util::{get_entity_by_id, is_dead, is_dead_or_invulnerable, is_invulnerable, stop_pursuing}, @@ -560,10 +561,10 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { let aggro_on = *aggro_on; if agent_data.below_flee_health(agent) { - let has_opportunity_to_flee = agent.action_state.timers + let flee_timer_done = agent.action_state.timers [ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] - < FLEE_DURATION; - let within_flee_distance = dist_sqrd < MAX_FLEE_DIST.powi(2); + > FLEE_DURATION; + let within_normal_flee_dir_dist = dist_sqrd < NORMAL_FLEE_DIR_DIST.powi(2); // FIXME: Using action state timer to see if allowed to speak is a hack. if agent.action_state.timers @@ -573,8 +574,21 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { agent_data.cry_out(agent, event_emitter, read_data); agent.action_state.timers [ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01; - } else if within_flee_distance && has_opportunity_to_flee { - agent_data.flee(agent, controller, tgt_pos, &read_data.terrain); + agent.flee_from_pos = { + let random = || thread_rng().gen_range(-1.0..1.0); + Some(Pos( + agent_data.pos.0 + Vec3::new(random(), random(), random()) + )) + }; + } else if !flee_timer_done { + if within_normal_flee_dir_dist { + agent_data.flee(agent, controller, tgt_pos, &read_data.terrain); + } else if let Some(random_pos) = agent.flee_from_pos { + agent_data.flee(agent, controller, &random_pos, &read_data.terrain); + } else { + agent_data.flee(agent, controller, tgt_pos, &read_data.terrain); + } + agent.action_state.timers [ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] += read_data.dt.0; @@ -582,6 +596,7 @@ fn do_combat(bdata: &mut BehaviorData) -> bool { agent.action_state.timers [ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.0; agent.target = None; + agent.flee_from_pos = None; agent_data.idle(agent, controller, read_data, rng); } } else if is_dead(target, read_data) {