diff --git a/common/net/src/synced_components.rs b/common/net/src/synced_components.rs index 299a496ee6..71bcf20610 100644 --- a/common/net/src/synced_components.rs +++ b/common/net/src/synced_components.rs @@ -40,6 +40,7 @@ macro_rules! synced_components { sticky: Sticky, immovable: Immovable, character_state: CharacterState, + character_activity: CharacterActivity, shockwave: Shockwave, beam_segment: BeamSegment, alignment: Alignment, @@ -201,6 +202,10 @@ impl NetSync for CharacterState { const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; } +impl NetSync for CharacterActivity { + const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; +} + impl NetSync for Shockwave { const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; } diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 6d9785f924..24a01fc26c 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -625,6 +625,11 @@ impl Awareness { self.reached = false; } } + + pub fn set_maximally_aware(&mut self) { + self.reached = true; + self.level = Self::ALERT; + } } #[derive(Clone, Debug, PartialOrd, PartialEq, Eq)] diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index b259123d0d..de07e6e590 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -983,7 +983,7 @@ impl Body { } /// Returns the eye height for this creature. - pub fn eye_height(&self) -> f32 { self.height() * 0.9 } + pub fn eye_height(&self, scale: f32) -> f32 { self.height() * 0.9 * scale } pub fn default_light_offset(&self) -> Vec3 { // TODO: Make this a manifest diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 1c3bf62e55..e914d4b32b 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -12,6 +12,7 @@ use crate::{ utils::{AbilityInfo, StageSection}, *, }, + util::Dir, }; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; @@ -30,6 +31,7 @@ pub struct StateUpdate { pub should_strafe: bool, pub queued_inputs: BTreeMap, pub removed_inputs: Vec, + pub character_activity: CharacterActivity, } pub struct OutputEvents<'a> { @@ -60,6 +62,7 @@ impl From<&JoinData<'_>> for StateUpdate { character: data.character.clone(), queued_inputs: BTreeMap::new(), removed_inputs: Vec::new(), + character_activity: *data.character_activity, } } } @@ -979,3 +982,20 @@ impl Default for CharacterState { impl Component for CharacterState { type Storage = DerefFlaggedStorage>; } + +/// Contains information about the visual activity of a character. +/// +/// For now this only includes the direction they're looking in, but later it +/// might include markers indicating that they're available for +/// trade/interaction, more details about their stance or appearance, facial +/// expression, etc. +#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] +pub struct CharacterActivity { + /// `None` means that the look direction should be derived from the + /// orientation + pub look_dir: Option, +} + +impl Component for CharacterActivity { + type Storage = DerefFlaggedStorage>; +} diff --git a/common/src/comp/compass.rs b/common/src/comp/compass.rs index 389c4fb67f..4937e87d1c 100644 --- a/common/src/comp/compass.rs +++ b/common/src/comp/compass.rs @@ -1,4 +1,5 @@ use vek::Vec2; +// TODO: Move this to common/src/, it's not a component /// Cardinal directions pub enum Direction { diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index a66a22f130..9b47025300 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -73,7 +73,7 @@ pub use self::{ Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, ModifierKind, }, - character_state::{CharacterState, StateUpdate}, + character_state::{CharacterActivity, CharacterState, StateUpdate}, chat::{ ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg, }, diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 84fbd82165..f03e7810f2 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -91,7 +91,10 @@ impl CharacterBehavior for Data { // Shoots all projectiles simultaneously for i in 0..self.static_data.num_projectiles { // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data.body.projectile_offsets( + update.ori.look_vec(), + data.scale.map_or(1.0, |s| s.0), + ); let pos = Pos(data.pos.0 + body_offsets); // Adds a slight spread to the projectiles. First projectile has no spread, // and spread increases linearly with number of projectiles created. diff --git a/common/src/states/basic_summon.rs b/common/src/states/basic_summon.rs index 316985b89f..fdda7ed54d 100644 --- a/common/src/states/basic_summon.rs +++ b/common/src/states/basic_summon.rs @@ -149,7 +149,7 @@ impl CharacterBehavior for Data { let collision_vector = Vec3::new( data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy, data.pos.0.y + (summon_frac * 2.0 * PI).cos() * obstacle_xy, - data.pos.0.z + data.body.eye_height(), + data.pos.0.z + data.body.eye_height(data.scale.map_or(1.0, |s| s.0)), ); // Check for collision in z up to 50 blocks diff --git a/common/src/states/behavior.rs b/common/src/states/behavior.rs index dd52911ca8..0746a94b84 100644 --- a/common/src/states/behavior.rs +++ b/common/src/states/behavior.rs @@ -3,8 +3,8 @@ use crate::{ self, character_state::OutputEvents, item::{tool::AbilityMap, MaterialStatManifest}, - ActiveAbilities, Beam, Body, CharacterState, Combo, ControlAction, Controller, - ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory, + ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, ControlAction, + Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory, InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate, Stats, Vel, }, @@ -120,6 +120,7 @@ pub struct JoinData<'a> { pub entity: Entity, pub uid: &'a Uid, pub character: &'a CharacterState, + pub character_activity: &'a CharacterActivity, pub pos: &'a Pos, pub vel: &'a Vel, pub ori: &'a Ori, @@ -153,6 +154,7 @@ pub struct JoinStruct<'a> { pub entity: Entity, pub uid: &'a Uid, pub char_state: FlaggedAccessMut<'a, &'a mut CharacterState, CharacterState>, + pub character_activity: FlaggedAccessMut<'a, &'a mut CharacterActivity, CharacterActivity>, pub pos: &'a mut Pos, pub vel: &'a mut Vel, pub ori: &'a mut Ori, @@ -190,6 +192,7 @@ impl<'a> JoinData<'a> { entity: j.entity, uid: j.uid, character: &j.char_state, + character_activity: &j.character_activity, pos: j.pos, vel: j.vel, ori: j.ori, diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index 0277c42312..fcc03f9b2b 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -114,7 +114,9 @@ impl CharacterBehavior for Data { get_crit_data(data, self.static_data.ability_info); let tool_stats = get_tool_stats(data, self.static_data.ability_info); // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data + .body + .projectile_offsets(update.ori.look_vec(), data.scale.map_or(1.0, |s| s.0)); let pos = Pos(data.pos.0 + body_offsets); let projectile = arrow.create_projectile( Some(*data.uid), diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index 639c47f57b..f1bcce0972 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -95,7 +95,9 @@ impl CharacterBehavior for Data { get_crit_data(data, self.static_data.ability_info); let tool_stats = get_tool_stats(data, self.static_data.ability_info); // Gets offsets - let body_offsets = data.body.projectile_offsets(update.ori.look_vec()); + let body_offsets = data + .body + .projectile_offsets(update.ori.look_vec(), data.scale.map_or(1.0, |s| s.0)); let pos = Pos(data.pos.0 + body_offsets); let projectile = self.static_data.projectile.create_projectile( Some(*data.uid), diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3aaf1177b8..ade2c10ed0 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -298,10 +298,10 @@ impl Body { /// Returns the position where a projectile should be fired relative to this /// body - pub fn projectile_offsets(&self, ori: Vec3) -> Vec3 { + pub fn projectile_offsets(&self, ori: Vec3, scale: f32) -> Vec3 { let body_offsets_z = match self { Body::Golem(_) => self.height() * 0.4, - _ => self.eye_height(), + _ => self.eye_height(scale), }; let dim = self.dimensions(); @@ -611,6 +611,9 @@ pub fn handle_orientation( .ori .slerped_towards(target_ori, target_fraction.min(1.0)) }; + + // Look at things + update.character_activity.look_dir = Some(data.controller.inputs.look_dir); } /// Updates components to move player as if theyre swimming diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 20ac10d6d4..db0d1f834c 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -199,6 +199,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index aaad927eaa..39ffd3e13a 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -8,9 +8,9 @@ use common::{ self, character_state::OutputEvents, inventory::item::{tool::AbilityMap, MaterialStatManifest}, - ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health, - Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, Scale, SkillSet, - Stance, StateUpdate, Stats, Vel, + ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density, + Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, + Scale, SkillSet, Stance, StateUpdate, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, link::Is, @@ -65,6 +65,7 @@ impl<'a> System<'a> for Sys { type SystemData = ( ReadData<'a>, WriteStorage<'a, CharacterState>, + WriteStorage<'a, CharacterActivity>, WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, @@ -84,6 +85,7 @@ impl<'a> System<'a> for Sys { ( read_data, mut character_states, + mut character_activities, mut positions, mut velocities, mut orientations, @@ -106,6 +108,7 @@ impl<'a> System<'a> for Sys { entity, uid, mut char_state, + character_activity, pos, vel, ori, @@ -116,13 +119,13 @@ impl<'a> System<'a> for Sys { controller, health, body, - physics, - (scale, stat, skill_set, active_abilities, is_rider), + (physics, scale, stat, skill_set, active_abilities, is_rider), combo, ) in ( &read_data.entities, &read_data.uids, &mut character_states, + &mut character_activities, &mut positions, &mut velocities, &mut orientations, @@ -133,8 +136,8 @@ impl<'a> System<'a> for Sys { &mut controllers, read_data.healths.maybe(), &read_data.bodies, - &read_data.physics_states, ( + &read_data.physics_states, read_data.scales.maybe(), &read_data.stats, &read_data.skill_sets, @@ -182,6 +185,7 @@ impl<'a> System<'a> for Sys { entity, uid, char_state, + character_activity, pos, vel, ori, @@ -261,6 +265,9 @@ impl Sys { if *join.char_state != state_update.character { *join.char_state = state_update.character } + if *join.character_activity != state_update.character_activity { + *join.character_activity = state_update.character_activity + } if *join.density != state_update.density { *join.density = state_update.density } diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 2386709f06..0102b5ee11 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -2,7 +2,7 @@ use common::{ comp::{ ability::Stance, agent::{Sound, SoundKind}, - Body, BuffChange, ControlEvent, Controller, Pos, + Body, BuffChange, ControlEvent, Controller, Pos, Scale, }, event::{EventBus, ServerEvent}, uid::UidAllocator, @@ -22,6 +22,7 @@ pub struct ReadData<'a> { server_bus: Read<'a, EventBus>, positions: ReadStorage<'a, Pos>, bodies: ReadStorage<'a, Body>, + scales: ReadStorage<'a, Scale>, } #[derive(Default)] @@ -91,13 +92,15 @@ impl<'a> System<'a> for Sys { }, ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)), ControlEvent::Utterance(kind) => { - if let (Some(pos), Some(body)) = ( + if let (Some(pos), Some(body), scale) = ( read_data.positions.get(entity), read_data.bodies.get(entity), + read_data.scales.get(entity), ) { let sound = Sound::new( SoundKind::Utterance(kind, *body), - pos.0 + Vec3::unit_z() * body.eye_height(), + pos.0 + + Vec3::unit_z() * body.eye_height(scale.map_or(1.0, |s| s.0)), 8.0, // TODO: Come up with a better way of determining this 1.0, ); diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index b08576e145..f344db95c7 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -68,13 +68,14 @@ impl<'a> System<'a> for Sys { let mut outcomes_emitter = outcomes.emitter(); // Attacks - for (attacker, uid, pos, ori, melee_attack, body) in ( + for (attacker, uid, pos, ori, melee_attack, body, scale) in ( &read_data.entities, &read_data.uids, &read_data.positions, &read_data.orientations, &mut melee_attacks, &read_data.bodies, + read_data.scales.maybe(), ) .join() { @@ -87,7 +88,7 @@ impl<'a> System<'a> for Sys { melee_attack.applied = true; // Scales - let eye_pos = pos.0 + Vec3::unit_z() * body.eye_height(); + let eye_pos = pos.0 + Vec3::unit_z() * body.eye_height(scale.map_or(1.0, |s| s.0)); let scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0); let height = body.height() * scale; // TODO: use Capsule Prisms instead of Cylinders diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index 031d6bb369..a05bdb3711 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -23,7 +23,7 @@ use common::{ item_drop, projectile::ProjectileConstructor, Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller, - HealthChange, InputKind, InventoryAction, Pos, UnresolvedChatMsg, UtteranceKind, + HealthChange, InputKind, InventoryAction, Pos, Scale, UnresolvedChatMsg, UtteranceKind, }, effect::{BuffEffect, Effect}, event::{Emitter, ServerEvent}, @@ -514,8 +514,10 @@ impl<'a> AgentData<'a> { target: EcsEntity, ) -> bool { if let Some(tgt_pos) = read_data.positions.get(target) { - let eye_offset = self.body.map_or(0.0, |b| b.eye_height()); - let tgt_eye_offset = read_data.bodies.get(target).map_or(0.0, |b| b.eye_height()); + let eye_offset = self.body.map_or(0.0, |b| b.eye_height(self.scale)); + let tgt_eye_offset = read_data.bodies.get(target).map_or(0.0, |b| { + b.eye_height(read_data.scales.get(target).map_or(1.0, |s| s.0)) + }); if let Some(dir) = Dir::from_unnormalized( Vec3::new(tgt_pos.0.x, tgt_pos.0.y, tgt_pos.0.z + tgt_eye_offset) - Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset), @@ -794,8 +796,8 @@ impl<'a> AgentData<'a> { }, }; - let is_detected = |entity: &EcsEntity, e_pos: &Pos| { - self.detects_other(agent, controller, entity, e_pos, read_data) + let is_detected = |entity: &EcsEntity, e_pos: &Pos, e_scale: Option<&Scale>| { + self.detects_other(agent, controller, entity, e_pos, e_scale, read_data) }; let target = entities_nearby @@ -805,7 +807,7 @@ impl<'a> AgentData<'a> { .filter_map(|(entity, attack_target)| { get_pos(entity).map(|pos| (entity, pos, attack_target)) }) - .filter(|(entity, e_pos, _)| is_detected(entity, e_pos)) + .filter(|(entity, e_pos, _)| is_detected(entity, e_pos, read_data.scales.get(*entity))) .min_by_key(|(_, e_pos, attack_target)| { ( *attack_target, @@ -997,9 +999,11 @@ impl<'a> AgentData<'a> { .angle_between((tgt_data.pos.0 - self.pos.0).xy()) .to_degrees(); - let eye_offset = self.body.map_or(0.0, |b| b.eye_height()); + let eye_offset = self.body.map_or(0.0, |b| b.eye_height(self.scale)); - let tgt_eye_height = tgt_data.body.map_or(0.0, |b| b.eye_height()); + let tgt_eye_height = tgt_data + .body + .map_or(0.0, |b| b.eye_height(tgt_data.scale.map_or(1.0, |s| s.0))); let tgt_eye_offset = tgt_eye_height + // Special case for jumping attacks to jump at the body // of the target and not the ground around the target @@ -1037,7 +1041,7 @@ impl<'a> AgentData<'a> { projectile_speed, self.pos.0 + self.body.map_or(Vec3::zero(), |body| { - body.projectile_offsets(self.ori.look_vec()) + body.projectile_offsets(self.ori.look_vec(), self.scale) }), Vec3::new( tgt_data.pos.0.x, @@ -1062,7 +1066,7 @@ impl<'a> AgentData<'a> { projectile_speed, self.pos.0 + self.body.map_or(Vec3::zero(), |body| { - body.projectile_offsets(self.ori.look_vec()) + body.projectile_offsets(self.ori.look_vec(), self.scale) }), Vec3::new( tgt_data.pos.0.x, @@ -1077,7 +1081,7 @@ impl<'a> AgentData<'a> { projectile_speed, self.pos.0 + self.body.map_or(Vec3::zero(), |body| { - body.projectile_offsets(self.ori.look_vec()) + body.projectile_offsets(self.ori.look_vec(), self.scale) }), Vec3::new( tgt_data.pos.0.x, @@ -1684,6 +1688,7 @@ impl<'a> AgentData<'a> { controller: &Controller, other: EcsEntity, other_pos: &Pos, + other_scale: Option<&Scale>, read_data: &ReadData, ) -> bool { let other_stealth_multiplier = { @@ -1708,7 +1713,15 @@ impl<'a> AgentData<'a> { (within_sight_dist) && within_fov - && entities_have_line_of_sight(self.pos, self.body, other_pos, other_body, read_data) + && entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + other_pos, + other_body, + other_scale, + read_data, + ) } pub fn detects_other( @@ -1717,10 +1730,11 @@ impl<'a> AgentData<'a> { controller: &Controller, other: &EcsEntity, other_pos: &Pos, + other_scale: Option<&Scale>, read_data: &ReadData, ) -> bool { self.can_sense_directly_near(other_pos) - || self.can_see_entity(agent, controller, *other, other_pos, read_data) + || self.can_see_entity(agent, controller, *other, other_pos, other_scale, read_data) } pub fn can_sense_directly_near(&self, e_pos: &Pos) -> bool { diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 031e134609..85e4a16010 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -247,7 +247,15 @@ impl<'a> AgentData<'a> { read_data: &ReadData, ) { let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; let elevation = self.pos.0.z - tgt_data.pos.0.z; @@ -374,8 +382,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -451,8 +461,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -1269,7 +1281,15 @@ impl<'a> AgentData<'a> { const DESIRED_ENERGY_LEVEL: f32 = 50.0; let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; // Logic to use abilities @@ -1521,8 +1541,10 @@ impl<'a> AgentData<'a> { if entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && attack_data.angle < 45.0 { @@ -1574,7 +1596,15 @@ impl<'a> AgentData<'a> { const DESIRED_COMBO_LEVEL: u32 = 8; let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; // Logic to use abilities @@ -1724,8 +1754,10 @@ impl<'a> AgentData<'a> { ) && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && attack_data.angle < 90.0 { @@ -1907,8 +1939,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -2130,8 +2164,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -2365,8 +2401,15 @@ impl<'a> AgentData<'a> { tgt_data: &TargetData, read_data: &ReadData, ) { - if entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) - && attack_data.angle < 15.0 + if entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) && attack_data.angle < 15.0 { controller.push_basic_input(InputKind::Primary); } else { @@ -2383,8 +2426,15 @@ impl<'a> AgentData<'a> { read_data: &ReadData, ) { controller.inputs.look_dir = self.ori.look_dir(); - if entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) - && attack_data.angle < 15.0 + if entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) && attack_data.angle < 15.0 { controller.push_basic_input(InputKind::Primary); } else { @@ -2406,8 +2456,15 @@ impl<'a> AgentData<'a> { .try_normalized() .unwrap_or_default(), ); - if entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) - { + if entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) { controller.push_basic_input(InputKind::Primary); } else { agent.target = None; @@ -2467,8 +2524,10 @@ impl<'a> AgentData<'a> { if entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { // If close to target, use either primary or secondary ability @@ -2564,8 +2623,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && attack_data.angle < 15.0 @@ -2695,8 +2756,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && attack_data.angle < 15.0 @@ -2931,8 +2994,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -3186,7 +3251,15 @@ impl<'a> AgentData<'a> { .and_then(|e| read_data.velocities.get(e)) .map_or(0.0, |v| v.0.cross(self.ori.look_vec()).magnitude_squared()); let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; if attack_data.dist_sqrd < golem_melee_range.powi(2) { @@ -3263,7 +3336,15 @@ impl<'a> AgentData<'a> { let health_fraction = self.health.map_or(0.5, |h| h.fraction()); let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; // Sets counter at start of combat, using `condition` to keep track of whether @@ -3461,7 +3542,15 @@ impl<'a> AgentData<'a> { let health_fraction = self.health.map_or(0.5, |h| h.fraction()); let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; if health_fraction < VINE_CREATION_THRESHOLD @@ -3530,7 +3619,15 @@ impl<'a> AgentData<'a> { } let line_of_sight_with_target = || { - entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data) + entities_have_line_of_sight( + self.pos, + self.body, + self.scale, + tgt_data.pos, + tgt_data.body, + tgt_data.scale, + read_data, + ) }; let health_fraction = self.health.map_or(0.5, |h| h.fraction()); // Sets counter at start of combat, using `condition` to keep track of whether @@ -3671,8 +3768,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -3738,8 +3837,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -3822,8 +3923,10 @@ impl<'a> AgentData<'a> { if entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && attack_data.angle < 45.0 { @@ -3901,8 +4004,10 @@ impl<'a> AgentData<'a> { } else if entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { // if enemy in mid range shoot dagon bombs and steamwave @@ -4016,8 +4121,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { @@ -4177,8 +4284,10 @@ impl<'a> AgentData<'a> { } else if entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) { // Else if in sight, barrage @@ -4241,8 +4350,10 @@ impl<'a> AgentData<'a> { && entities_have_line_of_sight( self.pos, self.body, + self.scale, tgt_data.pos, tgt_data.body, + tgt_data.scale, read_data, ) && agent.action_state.timers[DASH_TIMER] > 4.0 diff --git a/server/agent/src/util.rs b/server/agent/src/util.rs index 22ba3b8ba4..7dfe62b4e2 100644 --- a/server/agent/src/util.rs +++ b/server/agent/src/util.rs @@ -2,7 +2,7 @@ use crate::data::{ActionMode, AgentData, AttackData, Path, ReadData, TargetData} use common::{ comp::{ agent::Psyche, buff::BuffKind, inventory::item::ItemTag, item::ItemDesc, Agent, Alignment, - Body, Controller, InputKind, Pos, + Body, Controller, InputKind, Pos, Scale, }, consts::GRAVITY, terrain::Block, @@ -146,17 +146,19 @@ pub fn are_our_owners_hostile( pub fn entities_have_line_of_sight( pos: &Pos, body: Option<&Body>, + scale: f32, other_pos: &Pos, other_body: Option<&Body>, + other_scale: Option<&Scale>, read_data: &ReadData, ) -> bool { - let get_eye_pos = |pos: &Pos, body: Option<&Body>| { - let eye_offset = body.map_or(0.0, |b| b.eye_height()); + let get_eye_pos = |pos: &Pos, body: Option<&Body>, scale: f32| { + let eye_offset = body.map_or(0.0, |b| b.eye_height(scale)); Pos(pos.0.with_z(pos.0.z + eye_offset)) }; - let eye_pos = get_eye_pos(pos, body); - let other_eye_pos = get_eye_pos(other_pos, other_body); + let eye_pos = get_eye_pos(pos, body, scale); + let other_eye_pos = get_eye_pos(other_pos, other_body, other_scale.map_or(1.0, |s| s.0)); positions_have_line_of_sight(&eye_pos, &other_eye_pos, read_data) } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 4cc1f23cc4..872dd1a56c 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -286,6 +286,7 @@ impl StateExt for State { .with(poise) .with(comp::Alignment::Npc) .with(comp::CharacterState::default()) + .with(comp::CharacterActivity::default()) .with(inventory) .with(comp::Buffs::default()) .with(comp::Combo::default()) @@ -352,6 +353,7 @@ impl StateExt for State { .with(comp::Controller::default()) .with(Inventory::with_empty()) .with(comp::CharacterState::default()) + .with(comp::CharacterActivity::default()) // TODO: some of these are required in order for the character_behavior system to // recognize a possesed airship; that system should be refactored to use `.maybe()` .with(comp::Energy::new(ship.into(), 0)) @@ -559,6 +561,7 @@ impl StateExt for State { z_max: 1.75, }); self.write_component_ignore_entity_dead(entity, comp::CharacterState::default()); + self.write_component_ignore_entity_dead(entity, comp::CharacterActivity::default()); self.write_component_ignore_entity_dead(entity, comp::Alignment::Owned(player_uid)); self.write_component_ignore_entity_dead(entity, comp::Buffs::default()); self.write_component_ignore_entity_dead(entity, comp::Auras::default()); diff --git a/server/src/sys/agent/behavior_tree.rs b/server/src/sys/agent/behavior_tree.rs index 6f9a914fd5..96b2faaa19 100644 --- a/server/src/sys/agent/behavior_tree.rs +++ b/server/src/sys/agent/behavior_tree.rs @@ -139,11 +139,12 @@ impl BehaviorTree { handle_inbox_trade_accepted, handle_inbox_finished_trade, handle_inbox_update_pending_trade, + handle_timed_events, ]); Self { tree } } else { Self { - tree: vec![handle_inbox_cancel_interactions], + tree: vec![handle_inbox_cancel_interactions, handle_timed_events], } } } @@ -477,7 +478,6 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool { if bdata.agent.allowed_to_speak() && let Some(target) = bdata.read_data.lookup_actor(actor) && let Some(target_pos) = bdata.read_data.positions.get(target) - && bdata.agent_data.look_toward(bdata.controller, bdata.read_data, target) { bdata.agent.target = Some(Target::new( target, @@ -486,6 +486,8 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool { false, Some(target_pos.0), )); + // We're always aware of someone we're talking to + bdata.agent.awareness.set_maximally_aware(); bdata.controller.push_action(ControlAction::Talk); bdata.controller.push_utterance(UtteranceKind::Greeting); @@ -497,15 +499,20 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool { .agent .timer .start(bdata.read_data.time.0, TimerAction::Interact); + true + } else { + false } }, NpcAction::Say(msg) => { bdata.controller.push_utterance(UtteranceKind::Greeting); bdata.agent_data.chat_npc(msg, bdata.event_emitter); + false }, } + } else { + false } - false } /// Handle timed events, like looking at the player we are talking to @@ -571,7 +578,14 @@ fn update_last_known_pos(bdata: &mut BehaviorData) -> bool { let target = target_info.target; if let Some(target_pos) = read_data.positions.get(target) { - if agent_data.detects_other(agent, controller, &target, target_pos, read_data) { + if agent_data.detects_other( + agent, + controller, + &target, + target_pos, + read_data.scales.get(target), + read_data, + ) { let updated_pos = Some(target_pos.0); let Target { @@ -631,9 +645,10 @@ fn update_target_awareness(bdata: &mut BehaviorData) -> bool { let target = agent.target.map(|t| t.target); let tgt_pos = target.and_then(|t| read_data.positions.get(t)); + let tgt_scale = target.and_then(|t| read_data.scales.get(t)); if let (Some(target), Some(tgt_pos)) = (target, tgt_pos) { - if agent_data.can_see_entity(agent, controller, target, tgt_pos, read_data) { + if agent_data.can_see_entity(agent, controller, target, tgt_pos, tgt_scale, read_data) { agent.awareness.change_by(1.75 * read_data.dt.0); } else if agent_data.can_sense_directly_near(tgt_pos) { agent.awareness.change_by(0.25); diff --git a/server/src/sys/agent/behavior_tree/interaction.rs b/server/src/sys/agent/behavior_tree/interaction.rs index 0aaf20d73f..92e0c15deb 100644 --- a/server/src/sys/agent/behavior_tree/interaction.rs +++ b/server/src/sys/agent/behavior_tree/interaction.rs @@ -97,203 +97,190 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool { false, target_pos, )); + // We're always aware of someone we're talking to + agent.awareness.set_maximally_aware(); - if agent_data.look_toward(controller, read_data, target) { - controller.push_action(ControlAction::Stand); - controller.push_action(ControlAction::Talk); - controller.push_utterance(UtteranceKind::Greeting); + controller.push_action(ControlAction::Stand); + controller.push_action(ControlAction::Talk); + controller.push_utterance(UtteranceKind::Greeting); - match subject { - Subject::Regular => { - if let Some(tgt_stats) = read_data.stats.get(target) { - if let Some(destination_name) = &agent.rtsim_controller.heading_to { - let personality = &agent.rtsim_controller.personality; - let standard_response_msg = || -> String { - if personality.will_ambush() { + match subject { + Subject::Regular => { + if let Some(tgt_stats) = read_data.stats.get(target) { + if let Some(destination_name) = &agent.rtsim_controller.heading_to { + let personality = &agent.rtsim_controller.personality; + let standard_response_msg = || -> String { + if personality.will_ambush() { + format!( + "I'm heading to {}! Want to come along? We'll make \ + great travel buddies, hehe.", + destination_name + ) + } else if personality.is(PersonalityTrait::Extroverted) { + format!( + "I'm heading to {}! Want to come along?", + destination_name + ) + } else if personality.is(PersonalityTrait::Disagreeable) { + "Hrm.".to_string() + } else { + "Hello!".to_string() + } + }; + let msg = if false + /* TODO: Remembers character */ + { + if personality.will_ambush() { + "Just follow me a bit more, hehe.".to_string() + } else if personality.is(PersonalityTrait::Extroverted) { + if personality.is(PersonalityTrait::Extroverted) { format!( - "I'm heading to {}! Want to come along? We'll \ - make great travel buddies, hehe.", - destination_name - ) - } else if personality.is(PersonalityTrait::Extroverted) { - format!( - "I'm heading to {}! Want to come along?", - destination_name + "Greetings fair {}! It has been far too long \ + since last I saw you. I'm going to {} right now.", + &tgt_stats.name, destination_name ) } else if personality.is(PersonalityTrait::Disagreeable) { - "Hrm.".to_string() + "Oh. It's you again.".to_string() } else { - "Hello!".to_string() - } - }; - let msg = if false - /* TODO: Remembers character */ - { - if personality.will_ambush() { - "Just follow me a bit more, hehe.".to_string() - } else if personality.is(PersonalityTrait::Extroverted) { - if personality.is(PersonalityTrait::Extroverted) { - format!( - "Greetings fair {}! It has been far too long \ - since last I saw you. I'm going to {} right \ - now.", - &tgt_stats.name, destination_name - ) - } else if personality.is(PersonalityTrait::Disagreeable) - { - "Oh. It's you again.".to_string() - } else { - format!( - "Hi again {}! Unfortunately I'm in a hurry \ - right now. See you!", - &tgt_stats.name - ) - } - } else { - standard_response_msg() + format!( + "Hi again {}! Unfortunately I'm in a hurry right \ + now. See you!", + &tgt_stats.name + ) } } else { standard_response_msg() + } + } else { + standard_response_msg() + }; + agent_data.chat_npc(msg, event_emitter); + } else { + let mut rng = thread_rng(); + if let Some(extreme_trait) = + agent.rtsim_controller.personality.chat_trait(&mut rng) + { + let msg = match extreme_trait { + PersonalityTrait::Open => "npc-speech-villager_open", + PersonalityTrait::Adventurous => { + "npc-speech-villager_adventurous" + }, + PersonalityTrait::Closed => "npc-speech-villager_closed", + PersonalityTrait::Conscientious => { + "npc-speech-villager_conscientious" + }, + PersonalityTrait::Busybody => { + "npc-speech-villager_busybody" + }, + PersonalityTrait::Unconscientious => { + "npc-speech-villager_unconscientious" + }, + PersonalityTrait::Extroverted => { + "npc-speech-villager_extroverted" + }, + PersonalityTrait::Introverted => { + "npc-speech-villager_introverted" + }, + PersonalityTrait::Agreeable => { + "npc-speech-villager_agreeable" + }, + PersonalityTrait::Sociable => { + "npc-speech-villager_sociable" + }, + PersonalityTrait::Disagreeable => { + "npc-speech-villager_disagreeable" + }, + PersonalityTrait::Neurotic => { + "npc-speech-villager_neurotic" + }, + PersonalityTrait::Seeker => "npc-speech-villager_seeker", + PersonalityTrait::SadLoner => { + "npc-speech-villager_sad_loner" + }, + PersonalityTrait::Worried => "npc-speech-villager_worried", + PersonalityTrait::Stable => "npc-speech-villager_stable", }; agent_data.chat_npc(msg, event_emitter); } else { - let mut rng = thread_rng(); - if let Some(extreme_trait) = - agent.rtsim_controller.personality.chat_trait(&mut rng) - { - let msg = match extreme_trait { - PersonalityTrait::Open => "npc-speech-villager_open", - PersonalityTrait::Adventurous => { - "npc-speech-villager_adventurous" - }, - PersonalityTrait::Closed => { - "npc-speech-villager_closed" - }, - PersonalityTrait::Conscientious => { - "npc-speech-villager_conscientious" - }, - PersonalityTrait::Busybody => { - "npc-speech-villager_busybody" - }, - PersonalityTrait::Unconscientious => { - "npc-speech-villager_unconscientious" - }, - PersonalityTrait::Extroverted => { - "npc-speech-villager_extroverted" - }, - PersonalityTrait::Introverted => { - "npc-speech-villager_introverted" - }, - PersonalityTrait::Agreeable => { - "npc-speech-villager_agreeable" - }, - PersonalityTrait::Sociable => { - "npc-speech-villager_sociable" - }, - PersonalityTrait::Disagreeable => { - "npc-speech-villager_disagreeable" - }, - PersonalityTrait::Neurotic => { - "npc-speech-villager_neurotic" - }, - PersonalityTrait::Seeker => { - "npc-speech-villager_seeker" - }, - PersonalityTrait::SadLoner => { - "npc-speech-villager_sad_loner" - }, - PersonalityTrait::Worried => { - "npc-speech-villager_worried" - }, - PersonalityTrait::Stable => { - "npc-speech-villager_stable" - }, - }; - agent_data.chat_npc(msg, event_emitter); - } else { - agent_data.chat_npc("npc-speech-villager", event_emitter); - } + agent_data.chat_npc("npc-speech-villager", event_emitter); } } - }, - Subject::Trade => { - if agent.behavior.can_trade(agent_data.alignment.copied(), by) { - if !agent.behavior.is(BehaviorState::TRADING) { - controller.push_initiate_invite(by, InviteKind::Trade); - agent_data.chat_npc_if_allowed_to_speak( - "npc-speech-merchant_advertisement", - agent, - event_emitter, - ); - } else { - agent_data.chat_npc_if_allowed_to_speak( - "npc-speech-merchant_busy", - agent, - event_emitter, - ); - } - } else { - // TODO: maybe make some travellers willing to trade with - // simpler goods like potions + } + }, + Subject::Trade => { + if agent.behavior.can_trade(agent_data.alignment.copied(), by) { + if !agent.behavior.is(BehaviorState::TRADING) { + controller.push_initiate_invite(by, InviteKind::Trade); agent_data.chat_npc_if_allowed_to_speak( - "npc-speech-villager_decline_trade", + "npc-speech-merchant_advertisement", + agent, + event_emitter, + ); + } else { + agent_data.chat_npc_if_allowed_to_speak( + "npc-speech-merchant_busy", agent, event_emitter, ); } - }, - Subject::Mood => { - // TODO: Reimplement in rtsim2 - }, - Subject::Location(location) => { - if let Some(tgt_pos) = read_data.positions.get(target) { - let raw_dir = location.origin.as_::() - tgt_pos.0.xy(); - let dist = Distance::from_dir(raw_dir).name(); - let dir = Direction::from_dir(raw_dir).name(); + } else { + // TODO: maybe make some travellers willing to trade with + // simpler goods like potions + agent_data.chat_npc_if_allowed_to_speak( + "npc-speech-villager_decline_trade", + agent, + event_emitter, + ); + } + }, + Subject::Mood => { + // TODO: Reimplement in rtsim2 + }, + Subject::Location(location) => { + if let Some(tgt_pos) = read_data.positions.get(target) { + let raw_dir = location.origin.as_::() - tgt_pos.0.xy(); + let dist = Distance::from_dir(raw_dir).name(); + let dir = Direction::from_dir(raw_dir).name(); - let msg = format!( - "{} ? I think it's {} {} from here!", - location.name, dist, dir - ); - agent_data.chat_npc(msg, event_emitter); - } - }, - Subject::Person(person) => { - if let Some(src_pos) = read_data.positions.get(target) { - let msg = if let Some(person_pos) = person.origin { - let distance = - Distance::from_dir(person_pos.xy() - src_pos.0.xy()); - match distance { - Distance::NextTo | Distance::Near => { - format!( - "{} ? I think he's {} {} from here!", - person.name(), - distance.name(), - Direction::from_dir( - person_pos.xy() - src_pos.0.xy(), - ) + let msg = format!( + "{} ? I think it's {} {} from here!", + location.name, dist, dir + ); + agent_data.chat_npc(msg, event_emitter); + } + }, + Subject::Person(person) => { + if let Some(src_pos) = read_data.positions.get(target) { + let msg = if let Some(person_pos) = person.origin { + let distance = Distance::from_dir(person_pos.xy() - src_pos.0.xy()); + match distance { + Distance::NextTo | Distance::Near => { + format!( + "{} ? I think he's {} {} from here!", + person.name(), + distance.name(), + Direction::from_dir(person_pos.xy() - src_pos.0.xy(),) .name() - ) - }, - _ => { - format!( - "{} ? I think he's gone visiting another town. \ - Come back later!", - person.name() - ) - }, - } - } else { - format!( - "{} ? Sorry, I don't know where you can find him.", - person.name() - ) - }; - agent_data.chat_npc(msg, event_emitter); - } - }, - Subject::Work => {}, - } + ) + }, + _ => { + format!( + "{} ? I think he's gone visiting another town. Come \ + back later!", + person.name() + ) + }, + } + } else { + format!( + "{} ? Sorry, I don't know where you can find him.", + person.name() + ) + }; + agent_data.chat_npc(msg, event_emitter); + } + }, + Subject::Work => {}, } } } diff --git a/voxygen/anim/src/character/talk.rs b/voxygen/anim/src/character/talk.rs index f37de18162..74027cc41c 100644 --- a/voxygen/anim/src/character/talk.rs +++ b/voxygen/anim/src/character/talk.rs @@ -31,7 +31,8 @@ impl Animation for TalkAnimation { let slowb = (anim_time * 4.0 + PI / 2.0).sin(); let slowc = (anim_time * 12.0 + PI / 2.0).sin(); - next.head.orientation = Quaternion::rotation_x(slowc * 0.035 + look_dir.z * 0.7); + next.head.orientation = + Quaternion::rotation_x(slowc * 0.035 + look_dir.z.atan2(look_dir.xy().magnitude())); next.hand_l.position = Vec3::new( -s_a.hand.0 + 0.5 + slowb * 0.5, s_a.hand.1 + 5.0 + slowc * 1.0, diff --git a/voxygen/egui/src/lib.rs b/voxygen/egui/src/lib.rs index f83b0f59dd..25c2c699bf 100644 --- a/voxygen/egui/src/lib.rs +++ b/voxygen/egui/src/lib.rs @@ -346,8 +346,19 @@ pub fn maintain_egui_inner( ui.label("Body"); ui.label("Poise"); ui.label("Character State"); + ui.label("Character Activity"); ui.end_row(); - for (entity, body, stats, pos, _ori, vel, poise, character_state) in ( + for ( + entity, + body, + stats, + pos, + _ori, + vel, + poise, + character_state, + character_activity, + ) in ( &ecs.entities(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), @@ -356,14 +367,18 @@ pub fn maintain_egui_inner( ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ) .join() - .filter(|(_, _, _, pos, _, _, _, _)| { - client_pos.map_or(true, |client_pos| { - pos.map_or(0.0, |pos| pos.0.distance_squared(client_pos.0)) - < max_entity_distance - }) - }) + .filter( + |(_, _, _, pos, _, _, _, _, _)| { + client_pos.map_or(true, |client_pos| { + pos.map_or(0.0, |pos| { + pos.0.distance_squared(client_pos.0) + }) < max_entity_distance + }) + }, + ) { if ui.button("View").clicked() { previous_selected_entity = @@ -420,6 +435,12 @@ pub fn maintain_egui_inner( ui.label("-"); } + if let Some(character_activity) = character_activity { + ui.label(format!("{:?}", character_activity)); + } else { + ui.label("-"); + } + ui.end_row(); } }); @@ -507,11 +528,11 @@ fn selected_entity_window( buffs, auras, character_state, + character_activity, physics_state, alignment, scale, - mass, - (density, health, energy), + (mass, density, health, energy), ) in ( &ecs.entities(), ecs.read_storage::().maybe(), @@ -523,18 +544,19 @@ fn selected_entity_window( ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), - ecs.read_storage::().maybe(), ( + ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ecs.read_storage::().maybe(), ), ) .join() - .filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _))| e.id() == entity_id) + .filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _, _))| e.id() == entity_id) { let time = ecs.read_resource::