mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fixed sneak toggle, sneaking no longer has sound, rolling can return to sneaking state, sneaking reduces aggro distance
This commit is contained in:
parent
e5cda89192
commit
8c1e1fdc5c
@ -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
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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(_)
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -135,6 +135,7 @@ pub enum SfxEvent {
|
|||||||
Idle,
|
Idle,
|
||||||
Run,
|
Run,
|
||||||
Roll,
|
Roll,
|
||||||
|
Sneak,
|
||||||
Climb,
|
Climb,
|
||||||
GliderOpen,
|
GliderOpen,
|
||||||
Glide,
|
Glide,
|
||||||
|
Loading…
Reference in New Issue
Block a user