mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Move agent code into separate files
This commit is contained in:
parent
bb21dc2708
commit
ac6f53922f
@ -67,6 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Removed or reduced poise damage from most abilities
|
- Removed or reduced poise damage from most abilities
|
||||||
- Made the hotbar link to items by item definition id and component composition instead of specific inventory slots.
|
- Made the hotbar link to items by item definition id and component composition instead of specific inventory slots.
|
||||||
- Made loot boxes drop items instead of doing nothing in order to loot forcing
|
- Made loot boxes drop items instead of doing nothing in order to loot forcing
|
||||||
|
- Refactored agent code file structure
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
Body, UtteranceKind,
|
Body, UtteranceKind,
|
||||||
},
|
},
|
||||||
path::Chaser,
|
path::Chaser,
|
||||||
rtsim::RtSimController,
|
rtsim::{Memory, MemoryItem, RtSimController, RtSimEvent},
|
||||||
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -20,6 +20,8 @@ use super::dialogue::Subject;
|
|||||||
|
|
||||||
pub const DEFAULT_INTERACTION_TIME: f32 = 3.0;
|
pub const DEFAULT_INTERACTION_TIME: f32 = 3.0;
|
||||||
pub const TRADE_INTERACTION_TIME: f32 = 300.0;
|
pub const TRADE_INTERACTION_TIME: f32 = 300.0;
|
||||||
|
const AWARENESS_DECREMENT_CONSTANT: f32 = 2.1;
|
||||||
|
const SECONDS_BEFORE_FORGET_SOUNDS: f64 = 180.0;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
@ -351,6 +353,17 @@ pub struct Target {
|
|||||||
pub aggro_on: bool,
|
pub aggro_on: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Target {
|
||||||
|
pub fn new(target: EcsEntity, hostile: bool, selected_at: f64, aggro_on: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
target,
|
||||||
|
hostile,
|
||||||
|
selected_at,
|
||||||
|
aggro_on,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, EnumIter)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, EnumIter)]
|
||||||
pub enum TimerAction {
|
pub enum TimerAction {
|
||||||
Interact,
|
Interact,
|
||||||
@ -522,6 +535,61 @@ impl Agent {
|
|||||||
self.psyche.aggro_dist = None;
|
self.psyche.aggro_dist = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decrement_awareness(&mut self, dt: f32) {
|
||||||
|
let mut decrement = dt * AWARENESS_DECREMENT_CONSTANT;
|
||||||
|
let awareness = self.awareness;
|
||||||
|
|
||||||
|
let too_high = awareness >= 100.0;
|
||||||
|
let high = awareness >= 50.0;
|
||||||
|
let medium = awareness >= 30.0;
|
||||||
|
let low = awareness > 15.0;
|
||||||
|
let positive = awareness >= 0.0;
|
||||||
|
let negative = awareness < 0.0;
|
||||||
|
|
||||||
|
if too_high {
|
||||||
|
decrement *= 3.0;
|
||||||
|
} else if high {
|
||||||
|
decrement *= 1.0;
|
||||||
|
} else if medium {
|
||||||
|
decrement *= 2.5;
|
||||||
|
} else if low {
|
||||||
|
decrement *= 0.70;
|
||||||
|
} else if positive {
|
||||||
|
decrement *= 0.5;
|
||||||
|
} else if negative {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.awareness -= decrement;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forget_old_sounds(&mut self, time: f64) {
|
||||||
|
if !self.sounds_heard.is_empty() {
|
||||||
|
// Keep (retain) only newer sounds
|
||||||
|
self.sounds_heard
|
||||||
|
.retain(|&sound| time - sound.time <= SECONDS_BEFORE_FORGET_SOUNDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allowed_to_speak(&self) -> bool { self.behavior.can(BehaviorCapability::SPEAK) }
|
||||||
|
|
||||||
|
pub fn forget_enemy(&mut self, target_name: &str) {
|
||||||
|
self.rtsim_controller
|
||||||
|
.events
|
||||||
|
.push(RtSimEvent::ForgetEnemy(target_name.to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_enemy(&mut self, target_name: &str, time: f64) {
|
||||||
|
self.rtsim_controller
|
||||||
|
.events
|
||||||
|
.push(RtSimEvent::AddMemory(Memory {
|
||||||
|
item: MemoryItem::CharacterFight {
|
||||||
|
name: target_name.to_owned(),
|
||||||
|
},
|
||||||
|
time_to_forget: time + 300.0,
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Agent {
|
impl Component for Agent {
|
||||||
|
File diff suppressed because it is too large
Load Diff
2051
server/src/sys/agent/attack.rs
Normal file
2051
server/src/sys/agent/attack.rs
Normal file
File diff suppressed because it is too large
Load Diff
14
server/src/sys/agent/consts.rs
Normal file
14
server/src/sys/agent/consts.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
pub const DAMAGE_MEMORY_DURATION: f64 = 0.25;
|
||||||
|
pub const FLEE_DURATION: f32 = 3.0;
|
||||||
|
pub const MAX_FOLLOW_DIST: f32 = 12.0;
|
||||||
|
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;
|
||||||
|
pub const AVG_FOLLOW_DIST: f32 = 6.0;
|
||||||
|
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 AWARENESS_INVESTIGATE_THRESHOLD: f32 = 1.0;
|
137
server/src/sys/agent/data.rs
Normal file
137
server/src/sys/agent/data.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use crate::rtsim::Entity as RtSimData;
|
||||||
|
use common::{
|
||||||
|
comp::{
|
||||||
|
buff::Buffs, group, ActiveAbilities, Alignment, Body, CharacterState, Combo, Energy,
|
||||||
|
Health, Inventory, LightEmitter, Ori, PhysicsState, Pos, Scale, SkillSet, Stats, Vel,
|
||||||
|
},
|
||||||
|
link::Is,
|
||||||
|
mounting::Mount,
|
||||||
|
path::TraversalConfig,
|
||||||
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
|
rtsim::RtSimEntity,
|
||||||
|
terrain::TerrainGrid,
|
||||||
|
uid::{Uid, UidAllocator},
|
||||||
|
};
|
||||||
|
use specs::{
|
||||||
|
shred::ResourceId, Entities, Entity as EcsEntity, Read, ReadExpect, ReadStorage, SystemData,
|
||||||
|
World,
|
||||||
|
};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct AgentData<'a> {
|
||||||
|
pub entity: &'a EcsEntity,
|
||||||
|
pub rtsim_entity: Option<&'a RtSimData>,
|
||||||
|
pub uid: &'a Uid,
|
||||||
|
pub pos: &'a Pos,
|
||||||
|
pub vel: &'a Vel,
|
||||||
|
pub ori: &'a Ori,
|
||||||
|
pub energy: &'a Energy,
|
||||||
|
pub body: Option<&'a Body>,
|
||||||
|
pub inventory: &'a Inventory,
|
||||||
|
pub skill_set: &'a SkillSet,
|
||||||
|
#[allow(dead_code)] // may be useful for pathing
|
||||||
|
pub physics_state: &'a PhysicsState,
|
||||||
|
pub alignment: Option<&'a Alignment>,
|
||||||
|
pub traversal_config: TraversalConfig,
|
||||||
|
pub scale: f32,
|
||||||
|
pub damage: f32,
|
||||||
|
pub light_emitter: Option<&'a LightEmitter>,
|
||||||
|
pub glider_equipped: bool,
|
||||||
|
pub is_gliding: bool,
|
||||||
|
pub health: Option<&'a Health>,
|
||||||
|
pub char_state: &'a CharacterState,
|
||||||
|
pub active_abilities: &'a ActiveAbilities,
|
||||||
|
pub cached_spatial_grid: &'a common::CachedSpatialGrid,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TargetData<'a> {
|
||||||
|
pub pos: &'a Pos,
|
||||||
|
pub body: Option<&'a Body>,
|
||||||
|
pub scale: Option<&'a Scale>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TargetData<'a> {
|
||||||
|
pub fn new(pos: &'a Pos, body: Option<&'a Body>, scale: Option<&'a Scale>) -> Self {
|
||||||
|
Self { pos, body, scale }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AttackData {
|
||||||
|
pub min_attack_dist: f32,
|
||||||
|
pub dist_sqrd: f32,
|
||||||
|
pub angle: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttackData {
|
||||||
|
pub fn in_min_range(&self) -> bool { self.dist_sqrd < self.min_attack_dist.powi(2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
pub enum Tactic {
|
||||||
|
Melee,
|
||||||
|
Axe,
|
||||||
|
Hammer,
|
||||||
|
Sword,
|
||||||
|
Bow,
|
||||||
|
Staff,
|
||||||
|
Sceptre,
|
||||||
|
StoneGolem,
|
||||||
|
CircleCharge { radius: u32, circle_time: u32 },
|
||||||
|
QuadLowRanged,
|
||||||
|
TailSlap,
|
||||||
|
QuadLowQuick,
|
||||||
|
QuadLowBasic,
|
||||||
|
QuadLowBeam,
|
||||||
|
QuadMedJump,
|
||||||
|
QuadMedBasic,
|
||||||
|
Theropod,
|
||||||
|
Turret,
|
||||||
|
FixedTurret,
|
||||||
|
RotatingTurret,
|
||||||
|
RadialTurret,
|
||||||
|
Mindflayer,
|
||||||
|
BirdLargeBreathe,
|
||||||
|
BirdLargeFire,
|
||||||
|
BirdLargeBasic,
|
||||||
|
Minotaur,
|
||||||
|
ClayGolem,
|
||||||
|
TidalWarrior,
|
||||||
|
Yeti,
|
||||||
|
Tornado,
|
||||||
|
Harvester,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SystemData)]
|
||||||
|
pub struct ReadData<'a> {
|
||||||
|
pub entities: Entities<'a>,
|
||||||
|
pub uid_allocator: Read<'a, UidAllocator>,
|
||||||
|
pub dt: Read<'a, DeltaTime>,
|
||||||
|
pub time: Read<'a, Time>,
|
||||||
|
pub cached_spatial_grid: Read<'a, common::CachedSpatialGrid>,
|
||||||
|
pub group_manager: Read<'a, group::GroupManager>,
|
||||||
|
pub energies: ReadStorage<'a, Energy>,
|
||||||
|
pub positions: ReadStorage<'a, Pos>,
|
||||||
|
pub velocities: ReadStorage<'a, Vel>,
|
||||||
|
pub orientations: ReadStorage<'a, Ori>,
|
||||||
|
pub scales: ReadStorage<'a, Scale>,
|
||||||
|
pub healths: ReadStorage<'a, Health>,
|
||||||
|
pub inventories: ReadStorage<'a, Inventory>,
|
||||||
|
pub stats: ReadStorage<'a, Stats>,
|
||||||
|
pub skill_set: ReadStorage<'a, SkillSet>,
|
||||||
|
pub physics_states: ReadStorage<'a, PhysicsState>,
|
||||||
|
pub char_states: ReadStorage<'a, CharacterState>,
|
||||||
|
pub uids: ReadStorage<'a, Uid>,
|
||||||
|
pub groups: ReadStorage<'a, group::Group>,
|
||||||
|
pub terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
|
pub alignments: ReadStorage<'a, Alignment>,
|
||||||
|
pub bodies: ReadStorage<'a, Body>,
|
||||||
|
pub is_mounts: ReadStorage<'a, Is<Mount>>,
|
||||||
|
pub time_of_day: Read<'a, TimeOfDay>,
|
||||||
|
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||||
|
#[cfg(feature = "worldgen")]
|
||||||
|
pub world: ReadExpect<'a, Arc<world::World>>,
|
||||||
|
pub rtsim_entities: ReadStorage<'a, RtSimEntity>,
|
||||||
|
pub buffs: ReadStorage<'a, Buffs>,
|
||||||
|
pub combos: ReadStorage<'a, Combo>,
|
||||||
|
pub active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||||
|
}
|
72
server/src/sys/agent/util.rs
Normal file
72
server/src/sys/agent/util.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use crate::sys::agent::ReadData;
|
||||||
|
use common::{
|
||||||
|
comp::{buff::BuffKind, Alignment, Pos},
|
||||||
|
consts::GRAVITY,
|
||||||
|
terrain::{Block, TerrainGrid},
|
||||||
|
util::Dir,
|
||||||
|
vol::ReadVol,
|
||||||
|
};
|
||||||
|
use specs::{
|
||||||
|
saveload::{Marker, MarkerAllocator},
|
||||||
|
Entity as EcsEntity,
|
||||||
|
};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
pub fn can_see_tgt(terrain: &TerrainGrid, pos: &Pos, tgt_pos: &Pos, dist_sqrd: f32) -> bool {
|
||||||
|
terrain
|
||||||
|
.ray(pos.0 + Vec3::unit_z(), tgt_pos.0 + Vec3::unit_z())
|
||||||
|
.until(Block::is_opaque)
|
||||||
|
.cast()
|
||||||
|
.0
|
||||||
|
.powi(2)
|
||||||
|
>= dist_sqrd
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dead_or_invulnerable(entity: EcsEntity, read_data: &ReadData) -> bool {
|
||||||
|
is_dead(entity, read_data) || is_invulnerable(entity, read_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dead(entity: EcsEntity, read_data: &ReadData) -> bool {
|
||||||
|
let health = read_data.healths.get(entity);
|
||||||
|
health.map_or(false, |a| a.is_dead)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: The logic that is used in this function and throughout the code
|
||||||
|
// shouldn't be used to mean that a character is in a safezone.
|
||||||
|
pub fn is_invulnerable(entity: EcsEntity, read_data: &ReadData) -> bool {
|
||||||
|
let buffs = read_data.buffs.get(entity);
|
||||||
|
|
||||||
|
buffs.map_or(false, |b| b.kinds.contains_key(&BuffKind::Invulnerability))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to get alignment of owner if entity has Owned alignment
|
||||||
|
pub fn try_owner_alignment<'a>(
|
||||||
|
alignment: Option<&'a Alignment>,
|
||||||
|
read_data: &'a ReadData,
|
||||||
|
) -> Option<&'a Alignment> {
|
||||||
|
if let Some(Alignment::Owned(owner_uid)) = alignment {
|
||||||
|
if let Some(owner) = get_entity_by_id(owner_uid.id(), read_data) {
|
||||||
|
return read_data.alignments.get(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alignment
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Projectile motion: Returns the direction to aim for the projectile to reach
|
||||||
|
/// target position. Does not take any forces but gravity into account.
|
||||||
|
pub fn aim_projectile(speed: f32, pos: Vec3<f32>, tgt: Vec3<f32>) -> Option<Dir> {
|
||||||
|
let mut to_tgt = tgt - pos;
|
||||||
|
let dist_sqrd = to_tgt.xy().magnitude_squared();
|
||||||
|
let u_sqrd = speed.powi(2);
|
||||||
|
to_tgt.z = (u_sqrd
|
||||||
|
- (u_sqrd.powi(2) - GRAVITY * (GRAVITY * dist_sqrd + 2.0 * to_tgt.z * u_sqrd))
|
||||||
|
.sqrt()
|
||||||
|
.max(0.0))
|
||||||
|
/ GRAVITY;
|
||||||
|
|
||||||
|
Dir::from_unnormalized(to_tgt)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_entity_by_id(id: u64, read_data: &ReadData) -> Option<EcsEntity> {
|
||||||
|
read_data.uid_allocator.retrieve_entity_internal(id)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user