2022-07-29 10:13:00 +00:00
|
|
|
pub mod behavior_tree;
|
2022-09-16 00:29:12 +00:00
|
|
|
pub use server_agent::{action_nodes, attack, consts, data, util};
|
2022-01-18 03:02:43 +00:00
|
|
|
|
|
|
|
use crate::{
|
2022-07-29 16:33:34 +00:00
|
|
|
rtsim::RtSim,
|
2022-01-18 03:02:43 +00:00
|
|
|
sys::agent::{
|
2022-08-11 19:15:46 +00:00
|
|
|
behavior_tree::{BehaviorData, BehaviorTree},
|
2022-09-16 00:29:12 +00:00
|
|
|
data::{AgentData, ReadData},
|
2022-01-18 03:02:43 +00:00
|
|
|
},
|
|
|
|
};
|
2020-12-01 00:28:00 +00:00
|
|
|
use common::{
|
2020-04-18 18:28:19 +00:00
|
|
|
comp::{
|
2022-09-16 00:29:12 +00:00
|
|
|
self, inventory::slot::EquipSlot, item::ItemDesc, Agent, Alignment, Body, CharacterState,
|
|
|
|
Controller, Health, InputKind, Scale,
|
2020-04-18 18:28:19 +00:00
|
|
|
},
|
2022-09-16 00:29:12 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2021-02-07 07:22:06 +00:00
|
|
|
path::TraversalConfig,
|
2022-07-29 16:33:34 +00:00
|
|
|
rtsim::RtSimEvent,
|
2020-01-24 21:24:57 +00:00
|
|
|
};
|
2021-03-13 06:48:30 +00:00
|
|
|
use common_base::prof_span;
|
2021-03-09 10:52:57 +00:00
|
|
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
2022-09-16 00:29:12 +00:00
|
|
|
use rand::thread_rng;
|
2020-11-25 22:47:16 +00:00
|
|
|
use rayon::iter::ParallelIterator;
|
2022-09-16 00:29:12 +00:00
|
|
|
use specs::{Join, ParJoin, Read, WriteExpect, WriteStorage};
|
2019-04-16 21:06:33 +00:00
|
|
|
|
2019-06-09 19:33:20 +00:00
|
|
|
/// This system will allow NPCs to modify their controller
|
2021-03-04 14:00:16 +00:00
|
|
|
#[derive(Default)]
|
2019-04-16 21:06:33 +00:00
|
|
|
pub struct Sys;
|
2021-03-08 11:13:59 +00:00
|
|
|
impl<'a> System<'a> for Sys {
|
2019-04-16 21:06:33 +00:00
|
|
|
type SystemData = (
|
2021-02-07 07:22:06 +00:00
|
|
|
ReadData<'a>,
|
2022-05-09 19:58:13 +00:00
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
2019-08-02 20:25:33 +00:00
|
|
|
WriteStorage<'a, Agent>,
|
2019-06-09 14:20:20 +00:00
|
|
|
WriteStorage<'a, Controller>,
|
2021-03-16 01:30:35 +00:00
|
|
|
WriteExpect<'a, RtSim>,
|
2019-04-16 21:06:33 +00:00
|
|
|
);
|
|
|
|
|
2021-03-04 14:00:16 +00:00
|
|
|
const NAME: &'static str = "agent";
|
|
|
|
const ORIGIN: Origin = Origin::Server;
|
|
|
|
const PHASE: Phase = Phase::Create;
|
|
|
|
|
2019-08-04 08:21:29 +00:00
|
|
|
fn run(
|
2021-03-09 10:52:57 +00:00
|
|
|
job: &mut Job<Self>,
|
2021-03-16 01:30:35 +00:00
|
|
|
(read_data, event_bus, mut agents, mut controllers, mut rtsim): Self::SystemData,
|
2019-08-04 08:21:29 +00:00
|
|
|
) {
|
2021-03-16 01:30:35 +00:00
|
|
|
let rtsim = &mut *rtsim;
|
2021-03-09 10:52:57 +00:00
|
|
|
job.cpu_stats.measure(ParMode::Rayon);
|
2021-07-12 15:40:29 +00:00
|
|
|
|
2020-11-25 22:47:16 +00:00
|
|
|
(
|
2021-02-07 07:22:06 +00:00
|
|
|
&read_data.entities,
|
2022-09-18 02:11:41 +00:00
|
|
|
(
|
|
|
|
&read_data.energies,
|
|
|
|
read_data.healths.maybe(),
|
|
|
|
read_data.combos.maybe(),
|
|
|
|
),
|
2021-03-29 20:30:09 +00:00
|
|
|
(
|
|
|
|
&read_data.positions,
|
|
|
|
&read_data.velocities,
|
|
|
|
&read_data.orientations,
|
|
|
|
),
|
2021-02-07 07:22:06 +00:00
|
|
|
read_data.bodies.maybe(),
|
|
|
|
&read_data.inventories,
|
2021-11-12 03:37:37 +00:00
|
|
|
(
|
|
|
|
&read_data.char_states,
|
|
|
|
&read_data.skill_set,
|
|
|
|
&read_data.active_abilities,
|
|
|
|
),
|
2021-02-07 07:22:06 +00:00
|
|
|
&read_data.physics_states,
|
|
|
|
&read_data.uids,
|
2019-09-09 19:11:40 +00:00
|
|
|
&mut agents,
|
|
|
|
&mut controllers,
|
2021-02-07 07:22:06 +00:00
|
|
|
read_data.light_emitter.maybe(),
|
|
|
|
read_data.groups.maybe(),
|
2022-01-15 18:22:28 +00:00
|
|
|
!&read_data.is_mounts,
|
2019-09-09 19:11:40 +00:00
|
|
|
)
|
2020-11-25 22:47:16 +00:00
|
|
|
.par_join()
|
2021-03-13 06:48:30 +00:00
|
|
|
.for_each_init(
|
|
|
|
|| {
|
|
|
|
prof_span!(guard, "agent rayon job");
|
|
|
|
guard
|
|
|
|
},
|
|
|
|
|_guard,
|
|
|
|
(
|
2021-02-07 07:22:06 +00:00
|
|
|
entity,
|
2022-09-18 02:11:41 +00:00
|
|
|
(energy, health, combo),
|
2021-03-29 20:30:09 +00:00
|
|
|
(pos, vel, ori),
|
2021-02-07 07:22:06 +00:00
|
|
|
body,
|
|
|
|
inventory,
|
2021-11-12 03:37:37 +00:00
|
|
|
(char_state, skill_set, active_abilities),
|
2021-02-07 07:22:06 +00:00
|
|
|
physics_state,
|
|
|
|
uid,
|
|
|
|
agent,
|
|
|
|
controller,
|
|
|
|
light_emitter,
|
2021-07-12 15:40:29 +00:00
|
|
|
group,
|
2021-02-07 07:22:06 +00:00
|
|
|
_,
|
|
|
|
)| {
|
2022-01-18 03:02:43 +00:00
|
|
|
let mut event_emitter = event_bus.emitter();
|
2022-08-11 19:15:46 +00:00
|
|
|
let mut rng = thread_rng();
|
2022-01-18 03:02:43 +00:00
|
|
|
|
2021-07-12 15:40:29 +00:00
|
|
|
// Hack, replace with better system when groups are more sophisticated
|
|
|
|
// Override alignment if in a group unless entity is owned already
|
|
|
|
let alignment = if matches!(
|
2021-02-07 07:22:06 +00:00
|
|
|
&read_data.alignments.get(entity),
|
|
|
|
&Some(Alignment::Owned(_))
|
|
|
|
) {
|
2021-07-12 15:40:29 +00:00
|
|
|
read_data.alignments.get(entity).copied()
|
|
|
|
} else {
|
|
|
|
group
|
2021-02-07 07:22:06 +00:00
|
|
|
.and_then(|g| read_data.group_manager.group_info(*g))
|
|
|
|
.and_then(|info| read_data.uids.get(info.leader))
|
|
|
|
.copied()
|
2021-07-12 15:40:29 +00:00
|
|
|
.map_or_else(
|
|
|
|
|| read_data.alignments.get(entity).copied(),
|
|
|
|
|uid| Some(Alignment::Owned(uid)),
|
|
|
|
)
|
2021-02-07 07:22:06 +00:00
|
|
|
};
|
2019-09-09 19:11:40 +00:00
|
|
|
|
2021-07-14 01:40:56 +00:00
|
|
|
if !matches!(char_state, CharacterState::LeapMelee(_)) {
|
2021-07-14 10:22:47 +00:00
|
|
|
// Default to looking in orientation direction
|
|
|
|
// (can be overridden below)
|
|
|
|
//
|
2022-07-15 16:59:37 +00:00
|
|
|
// This definitely breaks LeapMelee and
|
2021-07-14 19:04:42 +00:00
|
|
|
// probably not only that, do we really need this at all?
|
2021-07-14 01:40:56 +00:00
|
|
|
controller.reset();
|
|
|
|
controller.inputs.look_dir = ori.look_dir();
|
|
|
|
}
|
2021-02-22 00:57:25 +00:00
|
|
|
|
2021-07-12 15:40:29 +00:00
|
|
|
let scale = read_data.scales.get(entity).map_or(1.0, |Scale(s)| *s);
|
2020-01-25 18:49:47 +00:00
|
|
|
|
2021-02-07 07:22:06 +00:00
|
|
|
let glider_equipped = inventory
|
|
|
|
.equipped(EquipSlot::Glider)
|
|
|
|
.as_ref()
|
|
|
|
.map_or(false, |item| {
|
2022-05-18 20:28:06 +00:00
|
|
|
matches!(&*item.kind(), comp::item::ItemKind::Glider)
|
2021-02-07 07:22:06 +00:00
|
|
|
});
|
2021-07-12 15:40:29 +00:00
|
|
|
|
2021-02-07 07:22:06 +00:00
|
|
|
let is_gliding = matches!(
|
|
|
|
read_data.char_states.get(entity),
|
2021-08-01 11:20:46 +00:00
|
|
|
Some(CharacterState::GlideWield(_) | CharacterState::Glide(_))
|
2021-06-20 03:51:04 +00:00
|
|
|
) && physics_state.on_ground.is_none();
|
2020-11-12 21:31:28 +00:00
|
|
|
|
2021-05-30 15:38:47 +00:00
|
|
|
if let Some(pid) = agent.position_pid_controller.as_mut() {
|
2021-05-29 18:45:46 +00:00
|
|
|
pid.add_measurement(read_data.time.0, pos.0);
|
|
|
|
}
|
|
|
|
|
2021-07-12 15:40:29 +00:00
|
|
|
// This controls how picky NPCs are about their pathfinding.
|
|
|
|
// Giants are larger and so can afford to be less precise
|
|
|
|
// when trying to move around the world
|
|
|
|
// (especially since they would otherwise get stuck on
|
2021-02-07 07:22:06 +00:00
|
|
|
// obstacles that smaller entities would not).
|
|
|
|
let node_tolerance = scale * 1.5;
|
2021-07-12 15:40:29 +00:00
|
|
|
let slow_factor = body.map_or(0.0, |b| b.base_accel() / 250.0).min(1.0);
|
2021-02-07 07:22:06 +00:00
|
|
|
let traversal_config = TraversalConfig {
|
|
|
|
node_tolerance,
|
|
|
|
slow_factor,
|
2021-06-20 03:51:04 +00:00
|
|
|
on_ground: physics_state.on_ground.is_some(),
|
2021-03-23 09:51:53 +00:00
|
|
|
in_liquid: physics_state.in_liquid().is_some(),
|
2021-02-07 07:22:06 +00:00
|
|
|
min_tgt_dist: 1.0,
|
2021-07-12 15:40:29 +00:00
|
|
|
can_climb: body.map_or(false, Body::can_climb),
|
|
|
|
can_fly: body.map_or(false, |b| b.fly_thrust().is_some()),
|
2021-02-07 07:22:06 +00:00
|
|
|
};
|
2021-07-12 15:40:29 +00:00
|
|
|
let health_fraction = health.map_or(1.0, Health::fraction);
|
2021-03-16 01:30:35 +00:00
|
|
|
let rtsim_entity = read_data
|
|
|
|
.rtsim_entities
|
|
|
|
.get(entity)
|
|
|
|
.and_then(|rtsim_ent| rtsim.get_entity(rtsim_ent.0));
|
2020-11-23 19:27:18 +00:00
|
|
|
|
2021-07-14 19:04:42 +00:00
|
|
|
if traversal_config.can_fly && matches!(body, Some(Body::Ship(_))) {
|
|
|
|
// hack (kinda): Never turn off flight airships
|
2021-07-12 15:40:29 +00:00
|
|
|
// since it results in stuttering and falling back to the ground.
|
2021-07-14 19:04:42 +00:00
|
|
|
//
|
|
|
|
// TODO: look into `controller.reset()` line above
|
|
|
|
// and see if it fixes it
|
2022-01-26 19:09:59 +00:00
|
|
|
controller.push_basic_input(InputKind::Fly);
|
2021-04-28 02:31:51 +00:00
|
|
|
}
|
|
|
|
|
2021-02-07 07:22:06 +00:00
|
|
|
// Package all this agent's data into a convenient struct
|
|
|
|
let data = AgentData {
|
|
|
|
entity: &entity,
|
|
|
|
uid,
|
|
|
|
pos,
|
|
|
|
vel,
|
|
|
|
ori,
|
|
|
|
energy,
|
|
|
|
body,
|
|
|
|
inventory,
|
2021-04-14 15:35:34 +00:00
|
|
|
skill_set,
|
2021-02-07 07:22:06 +00:00
|
|
|
physics_state,
|
|
|
|
alignment: alignment.as_ref(),
|
|
|
|
traversal_config,
|
|
|
|
scale,
|
2021-07-12 15:40:29 +00:00
|
|
|
damage: health_fraction,
|
2021-02-07 07:22:06 +00:00
|
|
|
light_emitter,
|
|
|
|
glider_equipped,
|
|
|
|
is_gliding,
|
2021-03-23 15:02:15 +00:00
|
|
|
health: read_data.healths.get(entity),
|
2021-03-21 18:22:14 +00:00
|
|
|
char_state,
|
2021-11-12 03:37:37 +00:00
|
|
|
active_abilities,
|
2022-09-18 02:11:41 +00:00
|
|
|
combo,
|
2021-04-21 17:10:13 +00:00
|
|
|
cached_spatial_grid: &read_data.cached_spatial_grid,
|
2022-05-28 23:41:31 +00:00
|
|
|
msm: &read_data.msm,
|
2021-02-07 07:22:06 +00:00
|
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
// Behavior tree
|
|
|
|
///////////////////////////////////////////////////////////
|
2021-03-30 00:25:59 +00:00
|
|
|
// The behavior tree is meant to make decisions for agents
|
|
|
|
// *but should not* mutate any data (only action nodes
|
|
|
|
// should do that). Each path should lead to one (and only
|
|
|
|
// one) action node. This makes bugfinding much easier and
|
|
|
|
// debugging way easier. If you don't think so, try
|
|
|
|
// debugging the agent code before this MR
|
|
|
|
// (https://gitlab.com/veloren/veloren/-/merge_requests/1801).
|
|
|
|
// Each tick should arrive at one (1) action node which
|
|
|
|
// then determines what the agent does. If this makes you
|
|
|
|
// uncomfortable, consider dt the response time of the
|
|
|
|
// NPC. To make the tree easier to read, subtrees can be
|
|
|
|
// created as methods on `AgentData`. Action nodes are
|
|
|
|
// also methods on the `AgentData` struct. Action nodes
|
|
|
|
// are the only parts of this tree that should provide
|
|
|
|
// inputs.
|
2022-08-11 19:15:46 +00:00
|
|
|
let mut behavior_data = BehaviorData {
|
2022-07-28 21:31:44 +00:00
|
|
|
agent,
|
2022-09-16 00:29:12 +00:00
|
|
|
rtsim_entity,
|
2022-08-11 19:15:46 +00:00
|
|
|
agent_data: data,
|
|
|
|
read_data: &read_data,
|
|
|
|
event_emitter: &mut event_emitter,
|
2022-07-28 21:31:44 +00:00
|
|
|
controller,
|
2022-08-11 19:15:46 +00:00
|
|
|
rng: &mut rng,
|
|
|
|
};
|
|
|
|
|
|
|
|
BehaviorTree::root().run(&mut behavior_data);
|
2022-07-28 21:31:44 +00:00
|
|
|
|
2021-02-07 07:22:06 +00:00
|
|
|
debug_assert!(controller.inputs.move_dir.map(|e| !e.is_nan()).reduce_and());
|
|
|
|
debug_assert!(controller.inputs.look_dir.map(|e| !e.is_nan()).reduce_and());
|
|
|
|
},
|
|
|
|
);
|
2021-03-16 01:30:35 +00:00
|
|
|
for (agent, rtsim_entity) in (&mut agents, &read_data.rtsim_entities).join() {
|
|
|
|
// Entity must be loaded in as it has an agent component :)
|
|
|
|
// React to all events in the controller
|
|
|
|
for event in core::mem::take(&mut agent.rtsim_controller.events) {
|
2021-03-29 14:47:42 +00:00
|
|
|
match event {
|
|
|
|
RtSimEvent::AddMemory(memory) => {
|
2021-07-12 15:40:29 +00:00
|
|
|
rtsim.insert_entity_memory(rtsim_entity.0, memory.clone());
|
2021-03-29 14:47:42 +00:00
|
|
|
},
|
2021-07-14 15:26:29 +00:00
|
|
|
RtSimEvent::ForgetEnemy(name) => {
|
|
|
|
rtsim.forget_entity_enemy(rtsim_entity.0, &name);
|
|
|
|
},
|
2021-03-29 14:47:42 +00:00
|
|
|
RtSimEvent::SetMood(memory) => {
|
2021-07-12 15:40:29 +00:00
|
|
|
rtsim.set_entity_mood(rtsim_entity.0, memory.clone());
|
2021-03-29 14:47:42 +00:00
|
|
|
},
|
2021-07-12 15:40:29 +00:00
|
|
|
RtSimEvent::PrintMemories => {},
|
2021-03-16 01:30:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-07 07:22:06 +00:00
|
|
|
}
|
|
|
|
}
|