mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added CharacterActivity, made NPCs look at the player when speaking to them
This commit is contained in:
parent
85c572f6e2
commit
3e0f5295c0
@ -40,6 +40,7 @@ macro_rules! synced_components {
|
|||||||
sticky: Sticky,
|
sticky: Sticky,
|
||||||
immovable: Immovable,
|
immovable: Immovable,
|
||||||
character_state: CharacterState,
|
character_state: CharacterState,
|
||||||
|
character_activity: CharacterActivity,
|
||||||
shockwave: Shockwave,
|
shockwave: Shockwave,
|
||||||
beam_segment: BeamSegment,
|
beam_segment: BeamSegment,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
@ -201,6 +202,10 @@ impl NetSync for CharacterState {
|
|||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NetSync for CharacterActivity {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
impl NetSync for Shockwave {
|
impl NetSync for Shockwave {
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
@ -625,6 +625,11 @@ impl Awareness {
|
|||||||
self.reached = false;
|
self.reached = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_maximally_aware(&mut self) {
|
||||||
|
self.reached = true;
|
||||||
|
self.level = Self::ALERT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialOrd, PartialEq, Eq)]
|
||||||
|
@ -983,7 +983,7 @@ impl Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the eye height for this creature.
|
/// 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<f32> {
|
pub fn default_light_offset(&self) -> Vec3<f32> {
|
||||||
// TODO: Make this a manifest
|
// TODO: Make this a manifest
|
||||||
|
@ -12,6 +12,7 @@ use crate::{
|
|||||||
utils::{AbilityInfo, StageSection},
|
utils::{AbilityInfo, StageSection},
|
||||||
*,
|
*,
|
||||||
},
|
},
|
||||||
|
util::Dir,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
@ -30,6 +31,7 @@ pub struct StateUpdate {
|
|||||||
pub should_strafe: bool,
|
pub should_strafe: bool,
|
||||||
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
|
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
|
||||||
pub removed_inputs: Vec<InputKind>,
|
pub removed_inputs: Vec<InputKind>,
|
||||||
|
pub character_activity: CharacterActivity,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OutputEvents<'a> {
|
pub struct OutputEvents<'a> {
|
||||||
@ -60,6 +62,7 @@ impl From<&JoinData<'_>> for StateUpdate {
|
|||||||
character: data.character.clone(),
|
character: data.character.clone(),
|
||||||
queued_inputs: BTreeMap::new(),
|
queued_inputs: BTreeMap::new(),
|
||||||
removed_inputs: Vec::new(),
|
removed_inputs: Vec::new(),
|
||||||
|
character_activity: *data.character_activity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -979,3 +982,20 @@ impl Default for CharacterState {
|
|||||||
impl Component for CharacterState {
|
impl Component for CharacterState {
|
||||||
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<Dir>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for CharacterActivity {
|
||||||
|
type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use vek::Vec2;
|
use vek::Vec2;
|
||||||
|
// TODO: Move this to common/src/, it's not a component
|
||||||
|
|
||||||
/// Cardinal directions
|
/// Cardinal directions
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
|
@ -73,7 +73,7 @@ pub use self::{
|
|||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||||
ModifierKind,
|
ModifierKind,
|
||||||
},
|
},
|
||||||
character_state::{CharacterState, StateUpdate},
|
character_state::{CharacterActivity, CharacterState, StateUpdate},
|
||||||
chat::{
|
chat::{
|
||||||
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
|
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
|
||||||
},
|
},
|
||||||
|
@ -91,7 +91,10 @@ impl CharacterBehavior for Data {
|
|||||||
// Shoots all projectiles simultaneously
|
// Shoots all projectiles simultaneously
|
||||||
for i in 0..self.static_data.num_projectiles {
|
for i in 0..self.static_data.num_projectiles {
|
||||||
// Gets offsets
|
// 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 pos = Pos(data.pos.0 + body_offsets);
|
||||||
// Adds a slight spread to the projectiles. First projectile has no spread,
|
// Adds a slight spread to the projectiles. First projectile has no spread,
|
||||||
// and spread increases linearly with number of projectiles created.
|
// and spread increases linearly with number of projectiles created.
|
||||||
|
@ -149,7 +149,7 @@ impl CharacterBehavior for Data {
|
|||||||
let collision_vector = Vec3::new(
|
let collision_vector = Vec3::new(
|
||||||
data.pos.0.x + (summon_frac * 2.0 * PI).sin() * obstacle_xy,
|
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.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
|
// Check for collision in z up to 50 blocks
|
||||||
|
@ -3,8 +3,8 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
item::{tool::AbilityMap, MaterialStatManifest},
|
item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Beam, Body, CharacterState, Combo, ControlAction, Controller,
|
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, ControlAction,
|
||||||
ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory,
|
Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory,
|
||||||
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate,
|
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, Scale, SkillSet, Stance, StateUpdate,
|
||||||
Stats, Vel,
|
Stats, Vel,
|
||||||
},
|
},
|
||||||
@ -120,6 +120,7 @@ pub struct JoinData<'a> {
|
|||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub uid: &'a Uid,
|
pub uid: &'a Uid,
|
||||||
pub character: &'a CharacterState,
|
pub character: &'a CharacterState,
|
||||||
|
pub character_activity: &'a CharacterActivity,
|
||||||
pub pos: &'a Pos,
|
pub pos: &'a Pos,
|
||||||
pub vel: &'a Vel,
|
pub vel: &'a Vel,
|
||||||
pub ori: &'a Ori,
|
pub ori: &'a Ori,
|
||||||
@ -153,6 +154,7 @@ pub struct JoinStruct<'a> {
|
|||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub uid: &'a Uid,
|
pub uid: &'a Uid,
|
||||||
pub char_state: FlaggedAccessMut<'a, &'a mut CharacterState, CharacterState>,
|
pub char_state: FlaggedAccessMut<'a, &'a mut CharacterState, CharacterState>,
|
||||||
|
pub character_activity: FlaggedAccessMut<'a, &'a mut CharacterActivity, CharacterActivity>,
|
||||||
pub pos: &'a mut Pos,
|
pub pos: &'a mut Pos,
|
||||||
pub vel: &'a mut Vel,
|
pub vel: &'a mut Vel,
|
||||||
pub ori: &'a mut Ori,
|
pub ori: &'a mut Ori,
|
||||||
@ -190,6 +192,7 @@ impl<'a> JoinData<'a> {
|
|||||||
entity: j.entity,
|
entity: j.entity,
|
||||||
uid: j.uid,
|
uid: j.uid,
|
||||||
character: &j.char_state,
|
character: &j.char_state,
|
||||||
|
character_activity: &j.character_activity,
|
||||||
pos: j.pos,
|
pos: j.pos,
|
||||||
vel: j.vel,
|
vel: j.vel,
|
||||||
ori: j.ori,
|
ori: j.ori,
|
||||||
|
@ -114,7 +114,9 @@ impl CharacterBehavior for Data {
|
|||||||
get_crit_data(data, self.static_data.ability_info);
|
get_crit_data(data, self.static_data.ability_info);
|
||||||
let tool_stats = get_tool_stats(data, self.static_data.ability_info);
|
let tool_stats = get_tool_stats(data, self.static_data.ability_info);
|
||||||
// Gets offsets
|
// 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 pos = Pos(data.pos.0 + body_offsets);
|
||||||
let projectile = arrow.create_projectile(
|
let projectile = arrow.create_projectile(
|
||||||
Some(*data.uid),
|
Some(*data.uid),
|
||||||
|
@ -95,7 +95,9 @@ impl CharacterBehavior for Data {
|
|||||||
get_crit_data(data, self.static_data.ability_info);
|
get_crit_data(data, self.static_data.ability_info);
|
||||||
let tool_stats = get_tool_stats(data, self.static_data.ability_info);
|
let tool_stats = get_tool_stats(data, self.static_data.ability_info);
|
||||||
// Gets offsets
|
// 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 pos = Pos(data.pos.0 + body_offsets);
|
||||||
let projectile = self.static_data.projectile.create_projectile(
|
let projectile = self.static_data.projectile.create_projectile(
|
||||||
Some(*data.uid),
|
Some(*data.uid),
|
||||||
|
@ -298,10 +298,10 @@ impl Body {
|
|||||||
|
|
||||||
/// Returns the position where a projectile should be fired relative to this
|
/// Returns the position where a projectile should be fired relative to this
|
||||||
/// body
|
/// body
|
||||||
pub fn projectile_offsets(&self, ori: Vec3<f32>) -> Vec3<f32> {
|
pub fn projectile_offsets(&self, ori: Vec3<f32>, scale: f32) -> Vec3<f32> {
|
||||||
let body_offsets_z = match self {
|
let body_offsets_z = match self {
|
||||||
Body::Golem(_) => self.height() * 0.4,
|
Body::Golem(_) => self.height() * 0.4,
|
||||||
_ => self.eye_height(),
|
_ => self.eye_height(scale),
|
||||||
};
|
};
|
||||||
|
|
||||||
let dim = self.dimensions();
|
let dim = self.dimensions();
|
||||||
@ -611,6 +611,9 @@ pub fn handle_orientation(
|
|||||||
.ori
|
.ori
|
||||||
.slerped_towards(target_ori, target_fraction.min(1.0))
|
.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
|
/// Updates components to move player as if theyre swimming
|
||||||
|
@ -199,6 +199,7 @@ impl State {
|
|||||||
ecs.register::<comp::Sticky>();
|
ecs.register::<comp::Sticky>();
|
||||||
ecs.register::<comp::Immovable>();
|
ecs.register::<comp::Immovable>();
|
||||||
ecs.register::<comp::CharacterState>();
|
ecs.register::<comp::CharacterState>();
|
||||||
|
ecs.register::<comp::CharacterActivity>();
|
||||||
ecs.register::<comp::Object>();
|
ecs.register::<comp::Object>();
|
||||||
ecs.register::<comp::Group>();
|
ecs.register::<comp::Group>();
|
||||||
ecs.register::<comp::Shockwave>();
|
ecs.register::<comp::Shockwave>();
|
||||||
|
@ -8,9 +8,9 @@ use common::{
|
|||||||
self,
|
self,
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
inventory::item::{tool::AbilityMap, MaterialStatManifest},
|
||||||
ActiveAbilities, Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health,
|
ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller, Density,
|
||||||
Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos, Scale, SkillSet,
|
Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise, Pos,
|
||||||
Stance, StateUpdate, Stats, Vel,
|
Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
@ -65,6 +65,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
type SystemData = (
|
type SystemData = (
|
||||||
ReadData<'a>,
|
ReadData<'a>,
|
||||||
WriteStorage<'a, CharacterState>,
|
WriteStorage<'a, CharacterState>,
|
||||||
|
WriteStorage<'a, CharacterActivity>,
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, Ori>,
|
||||||
@ -84,6 +85,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
(
|
(
|
||||||
read_data,
|
read_data,
|
||||||
mut character_states,
|
mut character_states,
|
||||||
|
mut character_activities,
|
||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
@ -106,6 +108,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
uid,
|
uid,
|
||||||
mut char_state,
|
mut char_state,
|
||||||
|
character_activity,
|
||||||
pos,
|
pos,
|
||||||
vel,
|
vel,
|
||||||
ori,
|
ori,
|
||||||
@ -116,13 +119,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
controller,
|
controller,
|
||||||
health,
|
health,
|
||||||
body,
|
body,
|
||||||
physics,
|
(physics, scale, stat, skill_set, active_abilities, is_rider),
|
||||||
(scale, stat, skill_set, active_abilities, is_rider),
|
|
||||||
combo,
|
combo,
|
||||||
) in (
|
) in (
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
&read_data.uids,
|
&read_data.uids,
|
||||||
&mut character_states,
|
&mut character_states,
|
||||||
|
&mut character_activities,
|
||||||
&mut positions,
|
&mut positions,
|
||||||
&mut velocities,
|
&mut velocities,
|
||||||
&mut orientations,
|
&mut orientations,
|
||||||
@ -133,8 +136,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
&mut controllers,
|
&mut controllers,
|
||||||
read_data.healths.maybe(),
|
read_data.healths.maybe(),
|
||||||
&read_data.bodies,
|
&read_data.bodies,
|
||||||
&read_data.physics_states,
|
|
||||||
(
|
(
|
||||||
|
&read_data.physics_states,
|
||||||
read_data.scales.maybe(),
|
read_data.scales.maybe(),
|
||||||
&read_data.stats,
|
&read_data.stats,
|
||||||
&read_data.skill_sets,
|
&read_data.skill_sets,
|
||||||
@ -182,6 +185,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
entity,
|
entity,
|
||||||
uid,
|
uid,
|
||||||
char_state,
|
char_state,
|
||||||
|
character_activity,
|
||||||
pos,
|
pos,
|
||||||
vel,
|
vel,
|
||||||
ori,
|
ori,
|
||||||
@ -261,6 +265,9 @@ impl Sys {
|
|||||||
if *join.char_state != state_update.character {
|
if *join.char_state != state_update.character {
|
||||||
*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 {
|
if *join.density != state_update.density {
|
||||||
*join.density = state_update.density
|
*join.density = state_update.density
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
ability::Stance,
|
ability::Stance,
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Body, BuffChange, ControlEvent, Controller, Pos,
|
Body, BuffChange, ControlEvent, Controller, Pos, Scale,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
uid::UidAllocator,
|
uid::UidAllocator,
|
||||||
@ -22,6 +22,7 @@ pub struct ReadData<'a> {
|
|||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
|
scales: ReadStorage<'a, Scale>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -91,13 +92,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
},
|
},
|
||||||
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
ControlEvent::Respawn => server_emitter.emit(ServerEvent::Respawn(entity)),
|
||||||
ControlEvent::Utterance(kind) => {
|
ControlEvent::Utterance(kind) => {
|
||||||
if let (Some(pos), Some(body)) = (
|
if let (Some(pos), Some(body), scale) = (
|
||||||
read_data.positions.get(entity),
|
read_data.positions.get(entity),
|
||||||
read_data.bodies.get(entity),
|
read_data.bodies.get(entity),
|
||||||
|
read_data.scales.get(entity),
|
||||||
) {
|
) {
|
||||||
let sound = Sound::new(
|
let sound = Sound::new(
|
||||||
SoundKind::Utterance(kind, *body),
|
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
|
8.0, // TODO: Come up with a better way of determining this
|
||||||
1.0,
|
1.0,
|
||||||
);
|
);
|
||||||
|
@ -68,13 +68,14 @@ impl<'a> System<'a> for Sys {
|
|||||||
let mut outcomes_emitter = outcomes.emitter();
|
let mut outcomes_emitter = outcomes.emitter();
|
||||||
|
|
||||||
// Attacks
|
// 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.entities,
|
||||||
&read_data.uids,
|
&read_data.uids,
|
||||||
&read_data.positions,
|
&read_data.positions,
|
||||||
&read_data.orientations,
|
&read_data.orientations,
|
||||||
&mut melee_attacks,
|
&mut melee_attacks,
|
||||||
&read_data.bodies,
|
&read_data.bodies,
|
||||||
|
read_data.scales.maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
@ -87,7 +88,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
melee_attack.applied = true;
|
melee_attack.applied = true;
|
||||||
|
|
||||||
// Scales
|
// 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 scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0);
|
||||||
let height = body.height() * scale;
|
let height = body.height() * scale;
|
||||||
// TODO: use Capsule Prisms instead of Cylinders
|
// TODO: use Capsule Prisms instead of Cylinders
|
||||||
|
@ -23,7 +23,7 @@ use common::{
|
|||||||
item_drop,
|
item_drop,
|
||||||
projectile::ProjectileConstructor,
|
projectile::ProjectileConstructor,
|
||||||
Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller,
|
Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller,
|
||||||
HealthChange, InputKind, InventoryAction, Pos, UnresolvedChatMsg, UtteranceKind,
|
HealthChange, InputKind, InventoryAction, Pos, Scale, UnresolvedChatMsg, UtteranceKind,
|
||||||
},
|
},
|
||||||
effect::{BuffEffect, Effect},
|
effect::{BuffEffect, Effect},
|
||||||
event::{Emitter, ServerEvent},
|
event::{Emitter, ServerEvent},
|
||||||
@ -514,8 +514,10 @@ impl<'a> AgentData<'a> {
|
|||||||
target: EcsEntity,
|
target: EcsEntity,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some(tgt_pos) = read_data.positions.get(target) {
|
if let Some(tgt_pos) = read_data.positions.get(target) {
|
||||||
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_offset = read_data.bodies.get(target).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(read_data.scales.get(target).map_or(1.0, |s| s.0))
|
||||||
|
});
|
||||||
if let Some(dir) = Dir::from_unnormalized(
|
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(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),
|
- 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| {
|
let is_detected = |entity: &EcsEntity, e_pos: &Pos, e_scale: Option<&Scale>| {
|
||||||
self.detects_other(agent, controller, entity, e_pos, read_data)
|
self.detects_other(agent, controller, entity, e_pos, e_scale, read_data)
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = entities_nearby
|
let target = entities_nearby
|
||||||
@ -805,7 +807,7 @@ impl<'a> AgentData<'a> {
|
|||||||
.filter_map(|(entity, attack_target)| {
|
.filter_map(|(entity, attack_target)| {
|
||||||
get_pos(entity).map(|pos| (entity, pos, 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)| {
|
.min_by_key(|(_, e_pos, attack_target)| {
|
||||||
(
|
(
|
||||||
*attack_target,
|
*attack_target,
|
||||||
@ -997,9 +999,11 @@ impl<'a> AgentData<'a> {
|
|||||||
.angle_between((tgt_data.pos.0 - self.pos.0).xy())
|
.angle_between((tgt_data.pos.0 - self.pos.0).xy())
|
||||||
.to_degrees();
|
.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 +
|
let tgt_eye_offset = tgt_eye_height +
|
||||||
// Special case for jumping attacks to jump at the body
|
// Special case for jumping attacks to jump at the body
|
||||||
// of the target and not the ground around the target
|
// of the target and not the ground around the target
|
||||||
@ -1037,7 +1041,7 @@ impl<'a> AgentData<'a> {
|
|||||||
projectile_speed,
|
projectile_speed,
|
||||||
self.pos.0
|
self.pos.0
|
||||||
+ self.body.map_or(Vec3::zero(), |body| {
|
+ 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(
|
Vec3::new(
|
||||||
tgt_data.pos.0.x,
|
tgt_data.pos.0.x,
|
||||||
@ -1062,7 +1066,7 @@ impl<'a> AgentData<'a> {
|
|||||||
projectile_speed,
|
projectile_speed,
|
||||||
self.pos.0
|
self.pos.0
|
||||||
+ self.body.map_or(Vec3::zero(), |body| {
|
+ 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(
|
Vec3::new(
|
||||||
tgt_data.pos.0.x,
|
tgt_data.pos.0.x,
|
||||||
@ -1077,7 +1081,7 @@ impl<'a> AgentData<'a> {
|
|||||||
projectile_speed,
|
projectile_speed,
|
||||||
self.pos.0
|
self.pos.0
|
||||||
+ self.body.map_or(Vec3::zero(), |body| {
|
+ 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(
|
Vec3::new(
|
||||||
tgt_data.pos.0.x,
|
tgt_data.pos.0.x,
|
||||||
@ -1684,6 +1688,7 @@ impl<'a> AgentData<'a> {
|
|||||||
controller: &Controller,
|
controller: &Controller,
|
||||||
other: EcsEntity,
|
other: EcsEntity,
|
||||||
other_pos: &Pos,
|
other_pos: &Pos,
|
||||||
|
other_scale: Option<&Scale>,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let other_stealth_multiplier = {
|
let other_stealth_multiplier = {
|
||||||
@ -1708,7 +1713,15 @@ impl<'a> AgentData<'a> {
|
|||||||
|
|
||||||
(within_sight_dist)
|
(within_sight_dist)
|
||||||
&& within_fov
|
&& 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(
|
pub fn detects_other(
|
||||||
@ -1717,10 +1730,11 @@ impl<'a> AgentData<'a> {
|
|||||||
controller: &Controller,
|
controller: &Controller,
|
||||||
other: &EcsEntity,
|
other: &EcsEntity,
|
||||||
other_pos: &Pos,
|
other_pos: &Pos,
|
||||||
|
other_scale: Option<&Scale>,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.can_sense_directly_near(other_pos)
|
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 {
|
pub fn can_sense_directly_near(&self, e_pos: &Pos) -> bool {
|
||||||
|
@ -247,7 +247,15 @@ impl<'a> AgentData<'a> {
|
|||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) {
|
) {
|
||||||
let line_of_sight_with_target = || {
|
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;
|
let elevation = self.pos.0.z - tgt_data.pos.0.z;
|
||||||
@ -374,8 +382,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -451,8 +461,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -1269,7 +1281,15 @@ impl<'a> AgentData<'a> {
|
|||||||
const DESIRED_ENERGY_LEVEL: f32 = 50.0;
|
const DESIRED_ENERGY_LEVEL: f32 = 50.0;
|
||||||
|
|
||||||
let line_of_sight_with_target = || {
|
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
|
// Logic to use abilities
|
||||||
@ -1521,8 +1541,10 @@ impl<'a> AgentData<'a> {
|
|||||||
if entities_have_line_of_sight(
|
if entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) && attack_data.angle < 45.0
|
) && attack_data.angle < 45.0
|
||||||
{
|
{
|
||||||
@ -1574,7 +1596,15 @@ impl<'a> AgentData<'a> {
|
|||||||
const DESIRED_COMBO_LEVEL: u32 = 8;
|
const DESIRED_COMBO_LEVEL: u32 = 8;
|
||||||
|
|
||||||
let line_of_sight_with_target = || {
|
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
|
// Logic to use abilities
|
||||||
@ -1724,8 +1754,10 @@ impl<'a> AgentData<'a> {
|
|||||||
) && entities_have_line_of_sight(
|
) && entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) && attack_data.angle < 90.0
|
) && attack_data.angle < 90.0
|
||||||
{
|
{
|
||||||
@ -1907,8 +1939,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -2130,8 +2164,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -2365,8 +2401,15 @@ impl<'a> AgentData<'a> {
|
|||||||
tgt_data: &TargetData,
|
tgt_data: &TargetData,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) {
|
) {
|
||||||
if entities_have_line_of_sight(self.pos, self.body, tgt_data.pos, tgt_data.body, read_data)
|
if entities_have_line_of_sight(
|
||||||
&& attack_data.angle < 15.0
|
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);
|
controller.push_basic_input(InputKind::Primary);
|
||||||
} else {
|
} else {
|
||||||
@ -2383,8 +2426,15 @@ impl<'a> AgentData<'a> {
|
|||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) {
|
) {
|
||||||
controller.inputs.look_dir = self.ori.look_dir();
|
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)
|
if entities_have_line_of_sight(
|
||||||
&& attack_data.angle < 15.0
|
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);
|
controller.push_basic_input(InputKind::Primary);
|
||||||
} else {
|
} else {
|
||||||
@ -2406,8 +2456,15 @@ impl<'a> AgentData<'a> {
|
|||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or_default(),
|
.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);
|
controller.push_basic_input(InputKind::Primary);
|
||||||
} else {
|
} else {
|
||||||
agent.target = None;
|
agent.target = None;
|
||||||
@ -2467,8 +2524,10 @@ impl<'a> AgentData<'a> {
|
|||||||
if entities_have_line_of_sight(
|
if entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) {
|
) {
|
||||||
// If close to target, use either primary or secondary ability
|
// If close to target, use either primary or secondary ability
|
||||||
@ -2564,8 +2623,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
&& attack_data.angle < 15.0
|
&& attack_data.angle < 15.0
|
||||||
@ -2695,8 +2756,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
&& attack_data.angle < 15.0
|
&& attack_data.angle < 15.0
|
||||||
@ -2931,8 +2994,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -3186,7 +3251,15 @@ impl<'a> AgentData<'a> {
|
|||||||
.and_then(|e| read_data.velocities.get(e))
|
.and_then(|e| read_data.velocities.get(e))
|
||||||
.map_or(0.0, |v| v.0.cross(self.ori.look_vec()).magnitude_squared());
|
.map_or(0.0, |v| v.0.cross(self.ori.look_vec()).magnitude_squared());
|
||||||
let line_of_sight_with_target = || {
|
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) {
|
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 health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||||
let line_of_sight_with_target = || {
|
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
|
// 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 health_fraction = self.health.map_or(0.5, |h| h.fraction());
|
||||||
let line_of_sight_with_target = || {
|
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
|
if health_fraction < VINE_CREATION_THRESHOLD
|
||||||
@ -3530,7 +3619,15 @@ impl<'a> AgentData<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let line_of_sight_with_target = || {
|
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());
|
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
|
// 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(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -3738,8 +3837,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -3822,8 +3923,10 @@ impl<'a> AgentData<'a> {
|
|||||||
if entities_have_line_of_sight(
|
if entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) && attack_data.angle < 45.0
|
) && attack_data.angle < 45.0
|
||||||
{
|
{
|
||||||
@ -3901,8 +4004,10 @@ impl<'a> AgentData<'a> {
|
|||||||
} else if entities_have_line_of_sight(
|
} else if entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) {
|
) {
|
||||||
// if enemy in mid range shoot dagon bombs and steamwave
|
// if enemy in mid range shoot dagon bombs and steamwave
|
||||||
@ -4016,8 +4121,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -4177,8 +4284,10 @@ impl<'a> AgentData<'a> {
|
|||||||
} else if entities_have_line_of_sight(
|
} else if entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
) {
|
) {
|
||||||
// Else if in sight, barrage
|
// Else if in sight, barrage
|
||||||
@ -4241,8 +4350,10 @@ impl<'a> AgentData<'a> {
|
|||||||
&& entities_have_line_of_sight(
|
&& entities_have_line_of_sight(
|
||||||
self.pos,
|
self.pos,
|
||||||
self.body,
|
self.body,
|
||||||
|
self.scale,
|
||||||
tgt_data.pos,
|
tgt_data.pos,
|
||||||
tgt_data.body,
|
tgt_data.body,
|
||||||
|
tgt_data.scale,
|
||||||
read_data,
|
read_data,
|
||||||
)
|
)
|
||||||
&& agent.action_state.timers[DASH_TIMER] > 4.0
|
&& agent.action_state.timers[DASH_TIMER] > 4.0
|
||||||
|
@ -2,7 +2,7 @@ use crate::data::{ActionMode, AgentData, AttackData, Path, ReadData, TargetData}
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
agent::Psyche, buff::BuffKind, inventory::item::ItemTag, item::ItemDesc, Agent, Alignment,
|
agent::Psyche, buff::BuffKind, inventory::item::ItemTag, item::ItemDesc, Agent, Alignment,
|
||||||
Body, Controller, InputKind, Pos,
|
Body, Controller, InputKind, Pos, Scale,
|
||||||
},
|
},
|
||||||
consts::GRAVITY,
|
consts::GRAVITY,
|
||||||
terrain::Block,
|
terrain::Block,
|
||||||
@ -146,17 +146,19 @@ pub fn are_our_owners_hostile(
|
|||||||
pub fn entities_have_line_of_sight(
|
pub fn entities_have_line_of_sight(
|
||||||
pos: &Pos,
|
pos: &Pos,
|
||||||
body: Option<&Body>,
|
body: Option<&Body>,
|
||||||
|
scale: f32,
|
||||||
other_pos: &Pos,
|
other_pos: &Pos,
|
||||||
other_body: Option<&Body>,
|
other_body: Option<&Body>,
|
||||||
|
other_scale: Option<&Scale>,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let get_eye_pos = |pos: &Pos, body: Option<&Body>| {
|
let get_eye_pos = |pos: &Pos, body: Option<&Body>, scale: f32| {
|
||||||
let eye_offset = body.map_or(0.0, |b| b.eye_height());
|
let eye_offset = body.map_or(0.0, |b| b.eye_height(scale));
|
||||||
|
|
||||||
Pos(pos.0.with_z(pos.0.z + eye_offset))
|
Pos(pos.0.with_z(pos.0.z + eye_offset))
|
||||||
};
|
};
|
||||||
let eye_pos = get_eye_pos(pos, body);
|
let eye_pos = get_eye_pos(pos, body, scale);
|
||||||
let other_eye_pos = get_eye_pos(other_pos, other_body);
|
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)
|
positions_have_line_of_sight(&eye_pos, &other_eye_pos, read_data)
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,7 @@ impl StateExt for State {
|
|||||||
.with(poise)
|
.with(poise)
|
||||||
.with(comp::Alignment::Npc)
|
.with(comp::Alignment::Npc)
|
||||||
.with(comp::CharacterState::default())
|
.with(comp::CharacterState::default())
|
||||||
|
.with(comp::CharacterActivity::default())
|
||||||
.with(inventory)
|
.with(inventory)
|
||||||
.with(comp::Buffs::default())
|
.with(comp::Buffs::default())
|
||||||
.with(comp::Combo::default())
|
.with(comp::Combo::default())
|
||||||
@ -352,6 +353,7 @@ impl StateExt for State {
|
|||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(Inventory::with_empty())
|
.with(Inventory::with_empty())
|
||||||
.with(comp::CharacterState::default())
|
.with(comp::CharacterState::default())
|
||||||
|
.with(comp::CharacterActivity::default())
|
||||||
// TODO: some of these are required in order for the character_behavior system to
|
// 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()`
|
// recognize a possesed airship; that system should be refactored to use `.maybe()`
|
||||||
.with(comp::Energy::new(ship.into(), 0))
|
.with(comp::Energy::new(ship.into(), 0))
|
||||||
@ -559,6 +561,7 @@ impl StateExt for State {
|
|||||||
z_max: 1.75,
|
z_max: 1.75,
|
||||||
});
|
});
|
||||||
self.write_component_ignore_entity_dead(entity, comp::CharacterState::default());
|
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::Alignment::Owned(player_uid));
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Buffs::default());
|
self.write_component_ignore_entity_dead(entity, comp::Buffs::default());
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Auras::default());
|
self.write_component_ignore_entity_dead(entity, comp::Auras::default());
|
||||||
|
@ -139,11 +139,12 @@ impl BehaviorTree {
|
|||||||
handle_inbox_trade_accepted,
|
handle_inbox_trade_accepted,
|
||||||
handle_inbox_finished_trade,
|
handle_inbox_finished_trade,
|
||||||
handle_inbox_update_pending_trade,
|
handle_inbox_update_pending_trade,
|
||||||
|
handle_timed_events,
|
||||||
]);
|
]);
|
||||||
Self { tree }
|
Self { tree }
|
||||||
} else {
|
} else {
|
||||||
Self {
|
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()
|
if bdata.agent.allowed_to_speak()
|
||||||
&& let Some(target) = bdata.read_data.lookup_actor(actor)
|
&& let Some(target) = bdata.read_data.lookup_actor(actor)
|
||||||
&& let Some(target_pos) = bdata.read_data.positions.get(target)
|
&& 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(
|
bdata.agent.target = Some(Target::new(
|
||||||
target,
|
target,
|
||||||
@ -486,6 +486,8 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
|||||||
false,
|
false,
|
||||||
Some(target_pos.0),
|
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_action(ControlAction::Talk);
|
||||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
||||||
@ -497,16 +499,21 @@ fn handle_rtsim_actions(bdata: &mut BehaviorData) -> bool {
|
|||||||
.agent
|
.agent
|
||||||
.timer
|
.timer
|
||||||
.start(bdata.read_data.time.0, TimerAction::Interact);
|
.start(bdata.read_data.time.0, TimerAction::Interact);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NpcAction::Say(msg) => {
|
NpcAction::Say(msg) => {
|
||||||
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
bdata.controller.push_utterance(UtteranceKind::Greeting);
|
||||||
bdata.agent_data.chat_npc(msg, bdata.event_emitter);
|
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
|
/// Handle timed events, like looking at the player we are talking to
|
||||||
fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
fn handle_timed_events(bdata: &mut BehaviorData) -> bool {
|
||||||
@ -571,7 +578,14 @@ fn update_last_known_pos(bdata: &mut BehaviorData) -> bool {
|
|||||||
let target = target_info.target;
|
let target = target_info.target;
|
||||||
|
|
||||||
if let Some(target_pos) = read_data.positions.get(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 updated_pos = Some(target_pos.0);
|
||||||
|
|
||||||
let Target {
|
let Target {
|
||||||
@ -631,9 +645,10 @@ fn update_target_awareness(bdata: &mut BehaviorData) -> bool {
|
|||||||
|
|
||||||
let target = agent.target.map(|t| t.target);
|
let target = agent.target.map(|t| t.target);
|
||||||
let tgt_pos = target.and_then(|t| read_data.positions.get(t));
|
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 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);
|
agent.awareness.change_by(1.75 * read_data.dt.0);
|
||||||
} else if agent_data.can_sense_directly_near(tgt_pos) {
|
} else if agent_data.can_sense_directly_near(tgt_pos) {
|
||||||
agent.awareness.change_by(0.25);
|
agent.awareness.change_by(0.25);
|
||||||
|
@ -97,8 +97,9 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
false,
|
false,
|
||||||
target_pos,
|
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::Stand);
|
||||||
controller.push_action(ControlAction::Talk);
|
controller.push_action(ControlAction::Talk);
|
||||||
controller.push_utterance(UtteranceKind::Greeting);
|
controller.push_utterance(UtteranceKind::Greeting);
|
||||||
@ -111,8 +112,8 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
let standard_response_msg = || -> String {
|
let standard_response_msg = || -> String {
|
||||||
if personality.will_ambush() {
|
if personality.will_ambush() {
|
||||||
format!(
|
format!(
|
||||||
"I'm heading to {}! Want to come along? We'll \
|
"I'm heading to {}! Want to come along? We'll make \
|
||||||
make great travel buddies, hehe.",
|
great travel buddies, hehe.",
|
||||||
destination_name
|
destination_name
|
||||||
)
|
)
|
||||||
} else if personality.is(PersonalityTrait::Extroverted) {
|
} else if personality.is(PersonalityTrait::Extroverted) {
|
||||||
@ -135,17 +136,15 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
if personality.is(PersonalityTrait::Extroverted) {
|
if personality.is(PersonalityTrait::Extroverted) {
|
||||||
format!(
|
format!(
|
||||||
"Greetings fair {}! It has been far too long \
|
"Greetings fair {}! It has been far too long \
|
||||||
since last I saw you. I'm going to {} right \
|
since last I saw you. I'm going to {} right now.",
|
||||||
now.",
|
|
||||||
&tgt_stats.name, destination_name
|
&tgt_stats.name, destination_name
|
||||||
)
|
)
|
||||||
} else if personality.is(PersonalityTrait::Disagreeable)
|
} else if personality.is(PersonalityTrait::Disagreeable) {
|
||||||
{
|
|
||||||
"Oh. It's you again.".to_string()
|
"Oh. It's you again.".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"Hi again {}! Unfortunately I'm in a hurry \
|
"Hi again {}! Unfortunately I'm in a hurry right \
|
||||||
right now. See you!",
|
now. See you!",
|
||||||
&tgt_stats.name
|
&tgt_stats.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -166,9 +165,7 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
PersonalityTrait::Adventurous => {
|
PersonalityTrait::Adventurous => {
|
||||||
"npc-speech-villager_adventurous"
|
"npc-speech-villager_adventurous"
|
||||||
},
|
},
|
||||||
PersonalityTrait::Closed => {
|
PersonalityTrait::Closed => "npc-speech-villager_closed",
|
||||||
"npc-speech-villager_closed"
|
|
||||||
},
|
|
||||||
PersonalityTrait::Conscientious => {
|
PersonalityTrait::Conscientious => {
|
||||||
"npc-speech-villager_conscientious"
|
"npc-speech-villager_conscientious"
|
||||||
},
|
},
|
||||||
@ -196,18 +193,12 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
PersonalityTrait::Neurotic => {
|
PersonalityTrait::Neurotic => {
|
||||||
"npc-speech-villager_neurotic"
|
"npc-speech-villager_neurotic"
|
||||||
},
|
},
|
||||||
PersonalityTrait::Seeker => {
|
PersonalityTrait::Seeker => "npc-speech-villager_seeker",
|
||||||
"npc-speech-villager_seeker"
|
|
||||||
},
|
|
||||||
PersonalityTrait::SadLoner => {
|
PersonalityTrait::SadLoner => {
|
||||||
"npc-speech-villager_sad_loner"
|
"npc-speech-villager_sad_loner"
|
||||||
},
|
},
|
||||||
PersonalityTrait::Worried => {
|
PersonalityTrait::Worried => "npc-speech-villager_worried",
|
||||||
"npc-speech-villager_worried"
|
PersonalityTrait::Stable => "npc-speech-villager_stable",
|
||||||
},
|
|
||||||
PersonalityTrait::Stable => {
|
|
||||||
"npc-speech-villager_stable"
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
agent_data.chat_npc(msg, event_emitter);
|
agent_data.chat_npc(msg, event_emitter);
|
||||||
} else {
|
} else {
|
||||||
@ -261,24 +252,21 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
Subject::Person(person) => {
|
Subject::Person(person) => {
|
||||||
if let Some(src_pos) = read_data.positions.get(target) {
|
if let Some(src_pos) = read_data.positions.get(target) {
|
||||||
let msg = if let Some(person_pos) = person.origin {
|
let msg = if let Some(person_pos) = person.origin {
|
||||||
let distance =
|
let distance = Distance::from_dir(person_pos.xy() - src_pos.0.xy());
|
||||||
Distance::from_dir(person_pos.xy() - src_pos.0.xy());
|
|
||||||
match distance {
|
match distance {
|
||||||
Distance::NextTo | Distance::Near => {
|
Distance::NextTo | Distance::Near => {
|
||||||
format!(
|
format!(
|
||||||
"{} ? I think he's {} {} from here!",
|
"{} ? I think he's {} {} from here!",
|
||||||
person.name(),
|
person.name(),
|
||||||
distance.name(),
|
distance.name(),
|
||||||
Direction::from_dir(
|
Direction::from_dir(person_pos.xy() - src_pos.0.xy(),)
|
||||||
person_pos.xy() - src_pos.0.xy(),
|
|
||||||
)
|
|
||||||
.name()
|
.name()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
format!(
|
format!(
|
||||||
"{} ? I think he's gone visiting another town. \
|
"{} ? I think he's gone visiting another town. Come \
|
||||||
Come back later!",
|
back later!",
|
||||||
person.name()
|
person.name()
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -297,7 +285,6 @@ pub fn handle_inbox_talk(bdata: &mut BehaviorData) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,8 @@ impl Animation for TalkAnimation {
|
|||||||
let slowb = (anim_time * 4.0 + PI / 2.0).sin();
|
let slowb = (anim_time * 4.0 + PI / 2.0).sin();
|
||||||
let slowc = (anim_time * 12.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(
|
next.hand_l.position = Vec3::new(
|
||||||
-s_a.hand.0 + 0.5 + slowb * 0.5,
|
-s_a.hand.0 + 0.5 + slowb * 0.5,
|
||||||
s_a.hand.1 + 5.0 + slowc * 1.0,
|
s_a.hand.1 + 5.0 + slowc * 1.0,
|
||||||
|
@ -346,8 +346,19 @@ pub fn maintain_egui_inner(
|
|||||||
ui.label("Body");
|
ui.label("Body");
|
||||||
ui.label("Poise");
|
ui.label("Poise");
|
||||||
ui.label("Character State");
|
ui.label("Character State");
|
||||||
|
ui.label("Character Activity");
|
||||||
ui.end_row();
|
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.entities(),
|
||||||
ecs.read_storage::<Body>().maybe(),
|
ecs.read_storage::<Body>().maybe(),
|
||||||
ecs.read_storage::<comp::Stats>().maybe(),
|
ecs.read_storage::<comp::Stats>().maybe(),
|
||||||
@ -356,14 +367,18 @@ pub fn maintain_egui_inner(
|
|||||||
ecs.read_storage::<comp::Vel>().maybe(),
|
ecs.read_storage::<comp::Vel>().maybe(),
|
||||||
ecs.read_storage::<Poise>().maybe(),
|
ecs.read_storage::<Poise>().maybe(),
|
||||||
ecs.read_storage::<comp::CharacterState>().maybe(),
|
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||||
|
ecs.read_storage::<comp::CharacterActivity>().maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(_, _, _, pos, _, _, _, _)| {
|
.filter(
|
||||||
|
|(_, _, _, pos, _, _, _, _, _)| {
|
||||||
client_pos.map_or(true, |client_pos| {
|
client_pos.map_or(true, |client_pos| {
|
||||||
pos.map_or(0.0, |pos| pos.0.distance_squared(client_pos.0))
|
pos.map_or(0.0, |pos| {
|
||||||
< max_entity_distance
|
pos.0.distance_squared(client_pos.0)
|
||||||
})
|
}) < max_entity_distance
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if ui.button("View").clicked() {
|
if ui.button("View").clicked() {
|
||||||
previous_selected_entity =
|
previous_selected_entity =
|
||||||
@ -420,6 +435,12 @@ pub fn maintain_egui_inner(
|
|||||||
ui.label("-");
|
ui.label("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(character_activity) = character_activity {
|
||||||
|
ui.label(format!("{:?}", character_activity));
|
||||||
|
} else {
|
||||||
|
ui.label("-");
|
||||||
|
}
|
||||||
|
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -507,11 +528,11 @@ fn selected_entity_window(
|
|||||||
buffs,
|
buffs,
|
||||||
auras,
|
auras,
|
||||||
character_state,
|
character_state,
|
||||||
|
character_activity,
|
||||||
physics_state,
|
physics_state,
|
||||||
alignment,
|
alignment,
|
||||||
scale,
|
scale,
|
||||||
mass,
|
(mass, density, health, energy),
|
||||||
(density, health, energy),
|
|
||||||
) in (
|
) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
ecs.read_storage::<Body>().maybe(),
|
ecs.read_storage::<Body>().maybe(),
|
||||||
@ -523,18 +544,19 @@ fn selected_entity_window(
|
|||||||
ecs.read_storage::<comp::Buffs>().maybe(),
|
ecs.read_storage::<comp::Buffs>().maybe(),
|
||||||
ecs.read_storage::<comp::Auras>().maybe(),
|
ecs.read_storage::<comp::Auras>().maybe(),
|
||||||
ecs.read_storage::<comp::CharacterState>().maybe(),
|
ecs.read_storage::<comp::CharacterState>().maybe(),
|
||||||
|
ecs.read_storage::<comp::CharacterActivity>().maybe(),
|
||||||
ecs.read_storage::<comp::PhysicsState>().maybe(),
|
ecs.read_storage::<comp::PhysicsState>().maybe(),
|
||||||
ecs.read_storage::<comp::Alignment>().maybe(),
|
ecs.read_storage::<comp::Alignment>().maybe(),
|
||||||
ecs.read_storage::<comp::Scale>().maybe(),
|
ecs.read_storage::<comp::Scale>().maybe(),
|
||||||
ecs.read_storage::<comp::Mass>().maybe(),
|
|
||||||
(
|
(
|
||||||
|
ecs.read_storage::<comp::Mass>().maybe(),
|
||||||
ecs.read_storage::<comp::Density>().maybe(),
|
ecs.read_storage::<comp::Density>().maybe(),
|
||||||
ecs.read_storage::<comp::Health>().maybe(),
|
ecs.read_storage::<comp::Health>().maybe(),
|
||||||
ecs.read_storage::<comp::Energy>().maybe(),
|
ecs.read_storage::<comp::Energy>().maybe(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _))| e.id() == entity_id)
|
.filter(|(e, _, _, _, _, _, _, _, _, _, _, _, _, _, (_, _, _, _))| e.id() == entity_id)
|
||||||
{
|
{
|
||||||
let time = ecs.read_resource::<Time>();
|
let time = ecs.read_resource::<Time>();
|
||||||
if let Some(pos) = pos {
|
if let Some(pos) = pos {
|
||||||
@ -702,6 +724,18 @@ fn selected_entity_window(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(character_activity) = character_activity {
|
||||||
|
CollapsingHeader::new("CharacterActivity").default_open(false).show(ui, |ui| {
|
||||||
|
Grid::new("selected_entity_character_activity_grid")
|
||||||
|
.spacing([40.0, 4.0])
|
||||||
|
.max_col_width(100.0)
|
||||||
|
.striped(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
two_col_row(ui, "look_dir", format!("{:.3?}", character_activity.look_dir));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(physics_state) = physics_state {
|
if let Some(physics_state) = physics_state {
|
||||||
CollapsingHeader::new("Physics State").default_open(false).show(ui, |ui| {
|
CollapsingHeader::new("Physics State").default_open(false).show(ui, |ui| {
|
||||||
Grid::new("selected_entity_physics_state_grid")
|
Grid::new("selected_entity_physics_state_grid")
|
||||||
|
@ -33,9 +33,9 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
item::{tool::AbilityContext, Hands, ItemKind, ToolKind},
|
item::{tool::AbilityContext, Hands, ItemKind, ToolKind},
|
||||||
ship, Body, CharacterState, Collider, Controller, Health, Inventory, Item, ItemKey, Last,
|
ship, Body, CharacterActivity, CharacterState, Collider, Controller, Health, Inventory,
|
||||||
LightAnimation, LightEmitter, Ori, PhysicsState, PoiseState, Pos, Scale, SkillSet, Stance,
|
Item, ItemKey, Last, LightAnimation, LightEmitter, Ori, PhysicsState, PoiseState, Pos,
|
||||||
Vel,
|
Scale, SkillSet, Stance, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::Rider,
|
||||||
@ -741,14 +741,14 @@ impl FigureMgr {
|
|||||||
scale,
|
scale,
|
||||||
body,
|
body,
|
||||||
character,
|
character,
|
||||||
|
character_activity,
|
||||||
last_character,
|
last_character,
|
||||||
physics,
|
physics,
|
||||||
health,
|
health,
|
||||||
inventory,
|
inventory,
|
||||||
item,
|
item,
|
||||||
light_emitter,
|
light_emitter,
|
||||||
is_rider,
|
(is_rider, collider, stance, skillset),
|
||||||
(collider, stance, skillset),
|
|
||||||
),
|
),
|
||||||
) in (
|
) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
@ -759,14 +759,15 @@ impl FigureMgr {
|
|||||||
ecs.read_storage::<Scale>().maybe(),
|
ecs.read_storage::<Scale>().maybe(),
|
||||||
&ecs.read_storage::<Body>(),
|
&ecs.read_storage::<Body>(),
|
||||||
ecs.read_storage::<CharacterState>().maybe(),
|
ecs.read_storage::<CharacterState>().maybe(),
|
||||||
|
ecs.read_storage::<CharacterActivity>().maybe(),
|
||||||
ecs.read_storage::<Last<CharacterState>>().maybe(),
|
ecs.read_storage::<Last<CharacterState>>().maybe(),
|
||||||
&ecs.read_storage::<PhysicsState>(),
|
&ecs.read_storage::<PhysicsState>(),
|
||||||
ecs.read_storage::<Health>().maybe(),
|
ecs.read_storage::<Health>().maybe(),
|
||||||
ecs.read_storage::<Inventory>().maybe(),
|
ecs.read_storage::<Inventory>().maybe(),
|
||||||
ecs.read_storage::<Item>().maybe(),
|
ecs.read_storage::<Item>().maybe(),
|
||||||
ecs.read_storage::<LightEmitter>().maybe(),
|
ecs.read_storage::<LightEmitter>().maybe(),
|
||||||
ecs.read_storage::<Is<Rider>>().maybe(),
|
|
||||||
(
|
(
|
||||||
|
ecs.read_storage::<Is<Rider>>().maybe(),
|
||||||
ecs.read_storage::<Collider>().maybe(),
|
ecs.read_storage::<Collider>().maybe(),
|
||||||
ecs.read_storage::<Stance>().maybe(),
|
ecs.read_storage::<Stance>().maybe(),
|
||||||
ecs.read_storage::<SkillSet>().maybe(),
|
ecs.read_storage::<SkillSet>().maybe(),
|
||||||
@ -779,7 +780,13 @@ impl FigureMgr {
|
|||||||
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel)
|
let rel_vel = anim::vek::Vec3::<f32>::from(vel.0 - physics.ground_vel)
|
||||||
/ scale.map_or(1.0, |s| s.0);
|
/ scale.map_or(1.0, |s| s.0);
|
||||||
|
|
||||||
let look_dir = controller.map(|c| c.inputs.look_dir).unwrap_or_default();
|
// Priortise CharacterActivity as the source of the look direction
|
||||||
|
let look_dir = character_activity.and_then(|ca| ca.look_dir)
|
||||||
|
// Failing that, take the controller as the source of truth
|
||||||
|
.or_else(|| controller.map(|c| c.inputs.look_dir))
|
||||||
|
// If that still didn't work, fall back to the interpolation orientation
|
||||||
|
.or_else(|| interpolated.map(|i| i.ori.look_dir()))
|
||||||
|
.unwrap_or_default();
|
||||||
let is_viewpoint = scene_data.viewpoint_entity == entity;
|
let is_viewpoint = scene_data.viewpoint_entity == entity;
|
||||||
let viewpoint_camera_mode = if is_viewpoint {
|
let viewpoint_camera_mode = if is_viewpoint {
|
||||||
camera_mode
|
camera_mode
|
||||||
|
@ -547,7 +547,7 @@ impl Scene {
|
|||||||
(
|
(
|
||||||
matches!(b, comp::Body::Humanoid(_)),
|
matches!(b, comp::Body::Humanoid(_)),
|
||||||
b.height(),
|
b.height(),
|
||||||
b.eye_height(),
|
b.eye_height(1.0), // Scale is applied later
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use common::{
|
|||||||
assets::{AssetExt, DotVoxAsset},
|
assets::{AssetExt, DotVoxAsset},
|
||||||
comp::{
|
comp::{
|
||||||
self, aura, beam, body, buff, item::Reagent, object, shockwave, BeamSegment, Body,
|
self, aura, beam, body, buff, item::Reagent, object, shockwave, BeamSegment, Body,
|
||||||
CharacterState, Ori, Pos, Shockwave, Vel,
|
CharacterState, Ori, Pos, Scale, Shockwave, Vel,
|
||||||
},
|
},
|
||||||
figure::Segment,
|
figure::Segment,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
@ -1264,12 +1264,13 @@ impl ParticleMgr {
|
|||||||
let time = state.get_time();
|
let time = state.get_time();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
for (interp, pos, buffs, body, ori) in (
|
for (interp, pos, buffs, body, ori, scale) in (
|
||||||
ecs.read_storage::<Interpolated>().maybe(),
|
ecs.read_storage::<Interpolated>().maybe(),
|
||||||
&ecs.read_storage::<Pos>(),
|
&ecs.read_storage::<Pos>(),
|
||||||
&ecs.read_storage::<comp::Buffs>(),
|
&ecs.read_storage::<comp::Buffs>(),
|
||||||
&ecs.read_storage::<Body>(),
|
&ecs.read_storage::<Body>(),
|
||||||
&ecs.read_storage::<Ori>(),
|
&ecs.read_storage::<Ori>(),
|
||||||
|
ecs.read_storage::<Scale>().maybe(),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
@ -1328,7 +1329,8 @@ impl ParticleMgr {
|
|||||||
self.scheduler.heartbeats(Duration::from_millis(25)),
|
self.scheduler.heartbeats(Duration::from_millis(25)),
|
||||||
),
|
),
|
||||||
|| {
|
|| {
|
||||||
let start_pos = pos + Vec3::unit_z() * body.eye_height();
|
let start_pos = pos
|
||||||
|
+ Vec3::unit_z() * body.eye_height(scale.map_or(1.0, |s| s.0));
|
||||||
let (radius, theta) =
|
let (radius, theta) =
|
||||||
(rng.gen_range(0.0f32..1.0).sqrt(), rng.gen_range(0.0..TAU));
|
(rng.gen_range(0.0f32..1.0).sqrt(), rng.gen_range(0.0..TAU));
|
||||||
let end_pos = pos
|
let end_pos = pos
|
||||||
@ -1393,7 +1395,9 @@ impl ParticleMgr {
|
|||||||
+ multiplicity
|
+ multiplicity
|
||||||
* self.scheduler.heartbeats(Duration::from_millis(3)) as usize,
|
* self.scheduler.heartbeats(Duration::from_millis(3)) as usize,
|
||||||
|| {
|
|| {
|
||||||
let start_pos = pos + Vec3::unit_z() * body.eye_height() / 2.0;
|
let start_pos = pos
|
||||||
|
+ Vec3::unit_z() * body.eye_height(scale.map_or(1.0, |s| s.0))
|
||||||
|
/ 2.0;
|
||||||
let end_pos = start_pos
|
let end_pos = start_pos
|
||||||
+ Vec3::<f32>::zero()
|
+ Vec3::<f32>::zero()
|
||||||
.map(|_| rng.gen_range(-1.0..1.0))
|
.map(|_| rng.gen_range(-1.0..1.0))
|
||||||
|
Loading…
Reference in New Issue
Block a user