mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added support for multiple timers, conditions, and counters in a single action node.
This commit is contained in:
parent
471b5996c3
commit
a9add7012e
@ -21,6 +21,25 @@ pub const TRADE_INTERACTION_TIME: f32 = 300.0;
|
|||||||
const AWARENESS_DECREMENT_CONSTANT: f32 = 2.1;
|
const AWARENESS_DECREMENT_CONSTANT: f32 = 2.1;
|
||||||
const SECONDS_BEFORE_FORGET_SOUNDS: f64 = 180.0;
|
const SECONDS_BEFORE_FORGET_SOUNDS: f64 = 180.0;
|
||||||
|
|
||||||
|
//intentionally very few concurrent action state variables are allowed. This is to keep the
|
||||||
|
//complexity of our AI from getting too large, too quickly. Originally I was
|
||||||
|
//going to provide 30 of these, but if we decide later that this is too many
|
||||||
|
//and somebody is already using 30 in one of their AI, it will be difficult
|
||||||
|
//to go back.
|
||||||
|
|
||||||
|
/// The number of timers that a single Action node can track concurrently
|
||||||
|
/// Define constants within a given action node to index between them.
|
||||||
|
const ACTIONSTATE_NUMBER_OF_CONCURRENT_TIMERS: usize = 5;
|
||||||
|
/// The number of float counters that a single Action node can track concurrently
|
||||||
|
/// Define constants within a given action node to index between them.
|
||||||
|
const ACTIONSTATE_NUMBER_OF_CONCURRENT_COUNTERS: usize = 5;
|
||||||
|
/// The number of integer counters that a single Action node can track concurrently
|
||||||
|
/// Define constants within a given action node to index between them.
|
||||||
|
const ACTIONSTATE_NUMBER_OF_CONCURRENT_INT_COUNTERS: usize = 5;
|
||||||
|
/// The number of booleans that a single Action node can track concurrently
|
||||||
|
/// Define constants within a given action node to index between them.
|
||||||
|
const ACTIONSTATE_NUMBER_OF_CONCURRENT_CONDITIONS: usize = 5;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
/// Wild animals and gentle giants
|
/// Wild animals and gentle giants
|
||||||
@ -511,12 +530,15 @@ pub struct Agent {
|
|||||||
pub position_pid_controller: Option<PidController<fn(Vec3<f32>, Vec3<f32>) -> f32, 16>>,
|
pub position_pid_controller: Option<PidController<fn(Vec3<f32>, Vec3<f32>) -> f32, 16>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// State persistence object for the behavior tree
|
||||||
|
/// Allows for state to be stored between subsequent, sequential calls of a single action node.
|
||||||
|
/// If the executed action node changes between ticks, then the state should be considered lost.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ActionState {
|
pub struct ActionState {
|
||||||
pub timer: f32,
|
pub timers: [f32; ACTIONSTATE_NUMBER_OF_CONCURRENT_TIMERS],
|
||||||
pub counter: f32,
|
pub counters: [f32; ACTIONSTATE_NUMBER_OF_CONCURRENT_COUNTERS],
|
||||||
pub condition: bool,
|
pub conditions: [bool; ACTIONSTATE_NUMBER_OF_CONCURRENT_CONDITIONS],
|
||||||
pub int_counter: u8,
|
pub int_counters: [u8; ACTIONSTATE_NUMBER_OF_CONCURRENT_INT_COUNTERS],
|
||||||
pub initialized: bool,
|
pub initialized: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consts::{
|
consts::{
|
||||||
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, PARTIAL_PATH_DIST,
|
AVG_FOLLOW_DIST, DEFAULT_ATTACK_RANGE, IDLE_HEALING_ITEM_THRESHOLD, PARTIAL_PATH_DIST,
|
||||||
@ -43,6 +44,9 @@ use vek::*;
|
|||||||
#[cfg(feature = "use-dyn-lib")]
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
use {crate::LIB, std::ffi::CStr};
|
use {crate::LIB, std::ffi::CStr};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl<'a> AgentData<'a> {
|
impl<'a> AgentData<'a> {
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// Action Nodes
|
// Action Nodes
|
||||||
@ -163,6 +167,9 @@ impl<'a> AgentData<'a> {
|
|||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
rng: &mut impl Rng,
|
rng: &mut impl Rng,
|
||||||
) {
|
) {
|
||||||
|
enum ActionTimers {
|
||||||
|
TimerIdle = 0,
|
||||||
|
}
|
||||||
// Light lanterns at night
|
// Light lanterns at night
|
||||||
// TODO Add a method to turn on NPC lanterns underground
|
// TODO Add a method to turn on NPC lanterns underground
|
||||||
let lantern_equipped = self
|
let lantern_equipped = self
|
||||||
@ -196,15 +203,15 @@ impl<'a> AgentData<'a> {
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if attempt_heal && self.heal_self(agent, controller, true) {
|
if attempt_heal && self.heal_self(agent, controller, true) {
|
||||||
agent.action_state.timer = 0.01;
|
agent.action_state.timers[ActionTimers::TimerIdle as usize] = 0.01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
agent.action_state.timer = 0.01;
|
agent.action_state.timers[ActionTimers::TimerIdle as usize] = 0.01;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
agent.action_state.timer = 0.0;
|
agent.action_state.timers[ActionTimers::TimerIdle as usize] = 0.0;
|
||||||
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
if let Some((travel_to, _destination)) = &agent.rtsim_controller.travel_to {
|
||||||
// If it has an rtsim destination and can fly, then it should.
|
// If it has an rtsim destination and can fly, then it should.
|
||||||
// If it is flying and bumps something above it, then it should move down.
|
// If it is flying and bumps something above it, then it should move down.
|
||||||
@ -547,7 +554,10 @@ impl<'a> AgentData<'a> {
|
|||||||
event_emitter: &mut Emitter<ServerEvent>,
|
event_emitter: &mut Emitter<ServerEvent>,
|
||||||
will_ambush: bool,
|
will_ambush: bool,
|
||||||
) {
|
) {
|
||||||
agent.action_state.timer = 0.0;
|
enum ActionStateTimers {
|
||||||
|
TimerChooseTarget = 0,
|
||||||
|
}
|
||||||
|
agent.action_state.timers[ActionStateTimers::TimerChooseTarget as usize] = 0.0;
|
||||||
let mut aggro_on = false;
|
let mut aggro_on = false;
|
||||||
|
|
||||||
// Search the area.
|
// Search the area.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -58,6 +58,20 @@ pub struct BehaviorTree {
|
|||||||
tree: Vec<BehaviorFn>,
|
tree: Vec<BehaviorFn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enumeration of the timers used by the behavior tree.
|
||||||
|
// FIXME: We shouldnt have a global timer enumeration for the whole behavior tree.
|
||||||
|
// It isnt entirely clear where a lot of the agents in some of the bdata objects in
|
||||||
|
// behavior tree functions come from, so it's hard to granularly define these timers per action
|
||||||
|
// node. As such, the behavior tree currently has one global enumeration for mapping timers
|
||||||
|
// in all functions, regardless as to use case or action node currently executed -- even if the
|
||||||
|
// agent might be different between calls. This doesn't break anything as each agent has its own
|
||||||
|
// instance of timers, but it is much less clear than I would like.
|
||||||
|
//
|
||||||
|
// This may require some refactoring to fix, and I don't feel confident doing so.
|
||||||
|
enum ActionStateBehaviorTreeTimers {
|
||||||
|
TimerBehaviorTree = 0,
|
||||||
|
}
|
||||||
|
|
||||||
impl BehaviorTree {
|
impl BehaviorTree {
|
||||||
/// Base BehaviorTree
|
/// Base BehaviorTree
|
||||||
///
|
///
|
||||||
@ -474,12 +488,13 @@ fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
|||||||
|
|
||||||
/// Try to heal self if our damage went below a certain threshold
|
/// Try to heal self if our damage went below a certain threshold
|
||||||
fn heal_self_if_hurt(bdata: &mut BehaviorData) -> bool {
|
fn heal_self_if_hurt(bdata: &mut BehaviorData) -> bool {
|
||||||
|
|
||||||
if bdata.agent_data.damage < HEALING_ITEM_THRESHOLD
|
if bdata.agent_data.damage < HEALING_ITEM_THRESHOLD
|
||||||
&& bdata
|
&& bdata
|
||||||
.agent_data
|
.agent_data
|
||||||
.heal_self(bdata.agent, bdata.controller, false)
|
.heal_self(bdata.agent, bdata.controller, false)
|
||||||
{
|
{
|
||||||
bdata.agent.action_state.timer = 0.01;
|
bdata.agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -543,18 +558,18 @@ fn do_combat(bdata: &mut BehaviorData) -> bool {
|
|||||||
let aggro_on = *aggro_on;
|
let aggro_on = *aggro_on;
|
||||||
|
|
||||||
if agent_data.below_flee_health(agent) {
|
if agent_data.below_flee_health(agent) {
|
||||||
let has_opportunity_to_flee = agent.action_state.timer < FLEE_DURATION;
|
let has_opportunity_to_flee = agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] < FLEE_DURATION;
|
||||||
let within_flee_distance = dist_sqrd < MAX_FLEE_DIST.powi(2);
|
let within_flee_distance = dist_sqrd < MAX_FLEE_DIST.powi(2);
|
||||||
|
|
||||||
// FIXME: Using action state timer to see if allowed to speak is a hack.
|
// FIXME: Using action state timer to see if allowed to speak is a hack.
|
||||||
if agent.action_state.timer == 0.0 {
|
if agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] == 0.0 {
|
||||||
agent_data.cry_out(agent, event_emitter, read_data);
|
agent_data.cry_out(agent, event_emitter, read_data);
|
||||||
agent.action_state.timer = 0.01;
|
agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.01;
|
||||||
} else if within_flee_distance && has_opportunity_to_flee {
|
} else if within_flee_distance && has_opportunity_to_flee {
|
||||||
agent_data.flee(agent, controller, tgt_pos, &read_data.terrain);
|
agent_data.flee(agent, controller, tgt_pos, &read_data.terrain);
|
||||||
agent.action_state.timer += read_data.dt.0;
|
agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] += read_data.dt.0;
|
||||||
} else {
|
} else {
|
||||||
agent.action_state.timer = 0.0;
|
agent.action_state.timers[ActionStateBehaviorTreeTimers::TimerBehaviorTree as usize] = 0.0;
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
agent_data.idle(agent, controller, read_data, rng);
|
agent_data.idle(agent, controller, read_data, rng);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,11 @@ use crate::{
|
|||||||
|
|
||||||
use super::{BehaviorData, BehaviorTree};
|
use super::{BehaviorData, BehaviorTree};
|
||||||
|
|
||||||
|
enum ActionStateInteractionTimers {
|
||||||
|
TimerInteraction = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Interact if incoming messages
|
/// Interact if incoming messages
|
||||||
pub fn process_inbox_sound_and_hurt(bdata: &mut BehaviorData) -> bool {
|
pub fn process_inbox_sound_and_hurt(bdata: &mut BehaviorData) -> bool {
|
||||||
if !bdata.agent.inbox.is_empty() {
|
if !bdata.agent.inbox.is_empty() {
|
||||||
@ -44,7 +49,7 @@ pub fn process_inbox_sound_and_hurt(bdata: &mut BehaviorData) -> bool {
|
|||||||
Some(_) | None => {},
|
Some(_) | None => {},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bdata.agent.action_state.timer = 0.1;
|
bdata.agent.action_state.timers[ActionStateInteractionTimers::TimerInteraction as usize] = 0.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -63,7 +68,7 @@ pub fn process_inbox_interaction(bdata: &mut BehaviorData) -> bool {
|
|||||||
|
|
||||||
/// Increment agent's action_state timer
|
/// Increment agent's action_state timer
|
||||||
pub fn increment_timer_deltatime(bdata: &mut BehaviorData) -> bool {
|
pub fn increment_timer_deltatime(bdata: &mut BehaviorData) -> bool {
|
||||||
bdata.agent.action_state.timer += bdata.read_data.dt.0;
|
bdata.agent.action_state.timers[ActionStateInteractionTimers::TimerInteraction as usize] += bdata.read_data.dt.0;
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user