Fixed sneak toggle, sneaking no longer has sound, rolling can return to sneaking state, sneaking reduces aggro distance

This commit is contained in:
ubruntu 2020-11-03 04:09:38 +00:00 committed by Samuel Keiffer
parent e5cda89192
commit 8c1e1fdc5c
10 changed files with 46 additions and 11 deletions

View File

@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- A new secondary charged melee attack for the hammer - A new secondary charged melee attack for the hammer
- Added Dutch translations - Added Dutch translations
- Buff system - Buff system
- Sneaking lets you be closer to enemies without being detected
### Changed ### Changed

View File

@ -469,6 +469,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Buildup, stage_section: StageSection::Buildup,
was_wielded: false, // false by default. utils might set it to true was_wielded: false, // false by default. utils might set it to true
was_sneak: false,
}), }),
CharacterAbility::ComboMelee { CharacterAbility::ComboMelee {
stage_data, stage_data,

View File

@ -100,6 +100,10 @@ impl CharacterState {
) )
} }
pub fn is_stealthy(&self) -> bool {
matches!(self, CharacterState::Sneak | CharacterState::Roll(_))
}
pub fn is_attack(&self) -> bool { pub fn is_attack(&self) -> bool {
matches!(self, matches!(self,
CharacterState::BasicMelee(_) CharacterState::BasicMelee(_)

View File

@ -32,6 +32,8 @@ pub struct Data {
pub stage_section: StageSection, pub stage_section: StageSection,
/// Had weapon /// Had weapon
pub was_wielded: bool, pub was_wielded: bool,
/// Was sneaking
pub was_sneak: bool,
} }
impl CharacterBehavior for Data { impl CharacterBehavior for Data {
@ -102,6 +104,8 @@ impl CharacterBehavior for Data {
// Done // Done
if self.was_wielded { if self.was_wielded {
update.character = CharacterState::Wielding; update.character = CharacterState::Wielding;
} else if self.was_sneak {
update.character = CharacterState::Sneak;
} else { } else {
update.character = CharacterState::Idle; update.character = CharacterState::Idle;
} }

View File

@ -53,4 +53,10 @@ impl CharacterBehavior for Data {
attempt_swap_loadout(data, &mut update); attempt_swap_loadout(data, &mut update);
update update
} }
fn stand(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
update.character = CharacterState::Idle;
update
}
} }

View File

@ -373,6 +373,11 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) {
if let CharacterState::Roll(roll) = &mut update.character { if let CharacterState::Roll(roll) = &mut update.character {
roll.was_wielded = true; roll.was_wielded = true;
} }
} else if data.character.is_stealthy() {
update.character = (ability, AbilityKey::Dodge).into();
if let CharacterState::Roll(roll) = &mut update.character {
roll.was_sneak = true;
}
} else { } else {
update.character = (ability, AbilityKey::Dodge).into(); update.character = (ability, AbilityKey::Dodge).into();
} }

View File

@ -5,9 +5,9 @@ use crate::{
group, group,
group::Invite, group::Invite,
item::{tool::ToolKind, ItemKind}, item::{tool::ToolKind, ItemKind},
Agent, Alignment, Body, ControlAction, ControlEvent, Controller, Energy, GroupManip, Agent, Alignment, Body, CharacterState, ControlAction, ControlEvent, Controller, Energy,
LightEmitter, Loadout, MountState, Ori, PhysicsState, Pos, Scale, Stats, UnresolvedChatMsg, GroupManip, LightEmitter, Loadout, MountState, Ori, PhysicsState, Pos, Scale, Stats,
Vel, UnresolvedChatMsg, Vel,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
metrics::SysMetrics, metrics::SysMetrics,
@ -60,6 +60,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Invite>, ReadStorage<'a, Invite>,
Read<'a, TimeOfDay>, Read<'a, TimeOfDay>,
ReadStorage<'a, LightEmitter>, ReadStorage<'a, LightEmitter>,
ReadStorage<'a, CharacterState>,
); );
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
@ -89,6 +90,7 @@ impl<'a> System<'a> for Sys {
invites, invites,
time_of_day, time_of_day,
light_emitter, light_emitter,
char_states,
): Self::SystemData, ): Self::SystemData,
) { ) {
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
@ -191,6 +193,7 @@ impl<'a> System<'a> for Sys {
const SIGHT_DIST: f32 = 80.0; const SIGHT_DIST: f32 = 80.0;
const MIN_ATTACK_DIST: f32 = 2.0; const MIN_ATTACK_DIST: f32 = 2.0;
const MAX_FLEE_DIST: f32 = 20.0; const MAX_FLEE_DIST: f32 = 20.0;
const SNEAK_COEFFICIENT: f32 = 0.25;
let scale = scales.get(entity).map(|s| s.0).unwrap_or(1.0); let scale = scales.get(entity).map(|s| s.0).unwrap_or(1.0);
@ -557,14 +560,21 @@ impl<'a> System<'a> for Sys {
if choose_target { if choose_target {
// Search for new targets (this looks expensive, but it's only run occasionally) // Search for new targets (this looks expensive, but it's only run occasionally)
// TODO: Replace this with a better system that doesn't consider *all* entities // TODO: Replace this with a better system that doesn't consider *all* entities
let closest_entity = (&entities, &positions, &stats, alignments.maybe()) let closest_entity = (&entities, &positions, &stats, alignments.maybe(), char_states.maybe())
.join() .join()
.filter(|(e, e_pos, e_stats, e_alignment)| { .filter(|(e, e_pos, e_stats, e_alignment, char_state)| {
((e_pos.0.distance_squared(pos.0) < SEARCH_DIST.powf(2.0) && let mut search_dist = SEARCH_DIST;
let mut listen_dist = LISTEN_DIST;
if char_state.map_or(false, |c_s| c_s.is_stealthy()) {
// TODO: make sneak more effective based on a stat like e_stats.fitness
search_dist *= SNEAK_COEFFICIENT;
listen_dist *= SNEAK_COEFFICIENT;
}
((e_pos.0.distance_squared(pos.0) < search_dist.powf(2.0) &&
// Within our view // Within our view
(e_pos.0 - pos.0).try_normalized().map(|v| v.dot(*inputs.look_dir) > 0.15).unwrap_or(true)) (e_pos.0 - pos.0).try_normalized().map(|v| v.dot(*inputs.look_dir) > 0.15).unwrap_or(true))
// Within listen distance // Within listen distance
|| e_pos.0.distance_squared(pos.0) < LISTEN_DIST.powf(2.0)) || e_pos.0.distance_squared(pos.0) < listen_dist.powf(2.0))
&& *e != entity && *e != entity
&& !e_stats.is_dead && !e_stats.is_dead
&& alignment && alignment
@ -572,13 +582,13 @@ impl<'a> System<'a> for Sys {
.unwrap_or(false) .unwrap_or(false)
}) })
// Can we even see them? // Can we even see them?
.filter(|(_, e_pos, _, _)| terrain .filter(|(_, e_pos, _, _, _)| terrain
.ray(pos.0 + Vec3::unit_z(), e_pos.0 + Vec3::unit_z()) .ray(pos.0 + Vec3::unit_z(), e_pos.0 + Vec3::unit_z())
.until(Block::is_opaque) .until(Block::is_opaque)
.cast() .cast()
.0 >= e_pos.0.distance(pos.0)) .0 >= e_pos.0.distance(pos.0))
.min_by_key(|(_, e_pos, _, _)| (e_pos.0.distance_squared(pos.0) * 100.0) as i32) .min_by_key(|(_, e_pos, _, _, _)| (e_pos.0.distance_squared(pos.0) * 100.0) as i32)
.map(|(e, _, _, _)| e); .map(|(e, _, _, _, _)| e);
if let Some(target) = closest_entity { if let Some(target) = closest_entity {
agent.activity = Activity::Attack { agent.activity = Activity::Attack {

View File

@ -158,8 +158,10 @@ impl MovementEventMapper {
if physics_state.on_ground && vel.magnitude() > 0.1 if physics_state.on_ground && vel.magnitude() > 0.1
|| !previous_state.on_ground && physics_state.on_ground || !previous_state.on_ground && physics_state.on_ground
{ {
return if character_state.is_dodge() { return if matches!(character_state, CharacterState::Roll(_)) {
SfxEvent::Roll SfxEvent::Roll
} else if matches!(character_state, CharacterState::Sneak) {
SfxEvent::Sneak
} else { } else {
SfxEvent::Run SfxEvent::Run
}; };

View File

@ -161,6 +161,7 @@ fn maps_roll() {
timer: Duration::default(), timer: Duration::default(),
stage_section: states::utils::StageSection::Buildup, stage_section: states::utils::StageSection::Buildup,
was_wielded: true, was_wielded: true,
was_sneak: false,
}), }),
&PhysicsState { &PhysicsState {
on_ground: true, on_ground: true,

View File

@ -135,6 +135,7 @@ pub enum SfxEvent {
Idle, Idle,
Run, Run,
Roll, Roll,
Sneak,
Climb, Climb,
GliderOpen, GliderOpen,
Glide, Glide,