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
|
||||
- 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
|
||||
- Refactored agent code file structure
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
Body, UtteranceKind,
|
||||
},
|
||||
path::Chaser,
|
||||
rtsim::RtSimController,
|
||||
rtsim::{Memory, MemoryItem, RtSimController, RtSimEvent},
|
||||
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
|
||||
uid::Uid,
|
||||
};
|
||||
@ -20,6 +20,8 @@ use super::dialogue::Subject;
|
||||
|
||||
pub const DEFAULT_INTERACTION_TIME: f32 = 3.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)]
|
||||
pub enum Alignment {
|
||||
@ -351,6 +353,17 @@ pub struct Target {
|
||||
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)]
|
||||
pub enum TimerAction {
|
||||
Interact,
|
||||
@ -522,6 +535,61 @@ impl Agent {
|
||||
self.psyche.aggro_dist = None;
|
||||
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 {
|
||||
|
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