mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Blocking now works if no weapon is equipped in main hand.
Added temp sfx for blocking and parrying. Added temp particles for successful parry. Tweaked values of default block ability.
This commit is contained in:
parent
fad31bd7ab
commit
288a6f3a82
@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Missing translations can be displayed in English.
|
||||
- New large birds npcs
|
||||
- Day period dependant wildlife spawns
|
||||
- You can now block and parry with melee weapons
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -17,6 +17,7 @@ use crate::{
|
||||
},
|
||||
event::ServerEvent,
|
||||
outcome::Outcome,
|
||||
states::utils::StageSection,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
@ -116,7 +117,12 @@ impl Attack {
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
|
||||
|
||||
pub fn compute_damage_reduction(target: &TargetInfo, source: AttackSource, dir: Dir) -> f32 {
|
||||
pub fn compute_damage_reduction(
|
||||
target: &TargetInfo,
|
||||
source: AttackSource,
|
||||
dir: Dir,
|
||||
mut emit_outcome: impl FnMut(Outcome),
|
||||
) -> f32 {
|
||||
let damage_reduction = Damage::compute_damage_reduction(target.inventory, target.stats);
|
||||
let block_reduction = match source {
|
||||
AttackSource::Melee => {
|
||||
@ -125,7 +131,11 @@ impl Attack {
|
||||
{
|
||||
if ori.look_vec().angle_between(-*dir) < data.static_data.max_angle.to_radians()
|
||||
{
|
||||
if data.parry {
|
||||
emit_outcome(Outcome::Block {
|
||||
parry: data.parry,
|
||||
pos: target.pos,
|
||||
});
|
||||
if data.parry && matches!(data.stage_section, StageSection::Buildup) {
|
||||
1.0
|
||||
} else {
|
||||
data.static_data.block_strength
|
||||
@ -164,7 +174,8 @@ impl Attack {
|
||||
.filter(|d| d.target.map_or(true, |t| t == target_group))
|
||||
.filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging))
|
||||
{
|
||||
let damage_reduction = Attack::compute_damage_reduction(&target, attack_source, dir);
|
||||
let damage_reduction =
|
||||
Attack::compute_damage_reduction(&target, attack_source, dir, |o| emit_outcome(o));
|
||||
let change = damage.damage.calculate_health_change(
|
||||
damage_reduction,
|
||||
attacker.map(|a| a.uid),
|
||||
|
@ -350,8 +350,8 @@ impl CharacterAbility {
|
||||
|
||||
pub fn default_block() -> CharacterAbility {
|
||||
CharacterAbility::BasicBlock {
|
||||
buildup_duration: 0.1,
|
||||
recover_duration: 0.1,
|
||||
buildup_duration: 0.3,
|
||||
recover_duration: 0.2,
|
||||
max_angle: 60.0,
|
||||
block_strength: 0.5,
|
||||
energy_cost: 50.0,
|
||||
|
@ -158,7 +158,10 @@ pub enum InputKind {
|
||||
|
||||
impl InputKind {
|
||||
pub fn is_ability(self) -> bool {
|
||||
matches!(self, Self::Primary | Self::Secondary | Self::Ability(_))
|
||||
matches!(
|
||||
self,
|
||||
Self::Primary | Self::Secondary | Self::Ability(_) | Self::Block
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,10 @@ pub enum Outcome {
|
||||
Damage {
|
||||
pos: Vec3<f32>,
|
||||
},
|
||||
Block {
|
||||
pos: Vec3<f32>,
|
||||
parry: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Outcome {
|
||||
@ -70,7 +74,8 @@ impl Outcome {
|
||||
| Outcome::Beam { pos, .. }
|
||||
| Outcome::SkillPointGain { pos, .. }
|
||||
| Outcome::SummonedCreature { pos, .. }
|
||||
| Outcome::Damage { pos, .. } => Some(*pos),
|
||||
| Outcome::Damage { pos, .. }
|
||||
| Outcome::Block { pos, .. } => Some(*pos),
|
||||
Outcome::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
||||
}
|
||||
|
@ -538,21 +538,17 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) ->
|
||||
}
|
||||
|
||||
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||
let hands = |equip_slot| match data.inventory.equipped(equip_slot).map(|i| i.kind()) {
|
||||
Some(ItemKind::Tool(tool)) => Some(tool.hands),
|
||||
_ => None,
|
||||
};
|
||||
let hands = get_hands(data);
|
||||
|
||||
// Mouse1 and Skill1 always use the MainHand slot
|
||||
let always_main_hand = matches!(input, InputKind::Primary | InputKind::Ability(0));
|
||||
let no_main_hand = hands(EquipSlot::Mainhand).is_none();
|
||||
let no_main_hand = hands.0.is_none();
|
||||
// skill_index used to select ability for the AbilityKey::Skill2 input
|
||||
let (equip_slot, skill_index) = if no_main_hand {
|
||||
(Some(EquipSlot::Offhand), 1)
|
||||
} else if always_main_hand {
|
||||
(Some(EquipSlot::Mainhand), 0)
|
||||
} else {
|
||||
let hands = (hands(EquipSlot::Mainhand), hands(EquipSlot::Offhand));
|
||||
match hands {
|
||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||
(_, Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
||||
@ -631,7 +627,10 @@ pub fn attempt_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
pub fn handle_block_input(data: &JoinData, update: &mut StateUpdate) {
|
||||
let can_block =
|
||||
|equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some(tool) if tool.can_block());
|
||||
if input_is_pressed(data, InputKind::Block) && can_block(EquipSlot::Mainhand) {
|
||||
let hands = get_hands(data);
|
||||
if input_is_pressed(data, InputKind::Block)
|
||||
&& (can_block(EquipSlot::Mainhand) || (hands.0.is_none() && can_block(EquipSlot::Offhand)))
|
||||
{
|
||||
let ability = CharacterAbility::default_block();
|
||||
if ability.requirements_paid(data, update) {
|
||||
update.character = CharacterState::from((
|
||||
@ -678,6 +677,17 @@ pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_hands(data: &JoinData) -> (Option<Hands>, Option<Hands>) {
|
||||
let hand = |slot| {
|
||||
if let Some(ItemKind::Tool(tool)) = data.inventory.equipped(slot).map(|i| i.kind()) {
|
||||
Some(tool.hands)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
(hand(EquipSlot::Mainhand), hand(EquipSlot::Offhand))
|
||||
}
|
||||
|
||||
pub fn get_crit_data(data: &JoinData, ai: AbilityInfo) -> (f32, f32) {
|
||||
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
||||
use HandInfo::*;
|
||||
|
@ -188,7 +188,7 @@ impl<'a> System<'a> for Sys {
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
@ -146,7 +146,7 @@ impl<'a> System<'a> for Sys {
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.char_states.get(target),
|
||||
};
|
||||
|
@ -111,6 +111,7 @@ impl<'a> System<'a> for Sys {
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(other.into())
|
||||
{
|
||||
if let Some(pos) = read_data.positions.get(target) {
|
||||
let owner_entity = projectile.owner.and_then(|u| {
|
||||
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
||||
});
|
||||
@ -162,6 +163,7 @@ impl<'a> System<'a> for Sys {
|
||||
|o| outcomes.push(o),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
projectile::Effect::Explode(e) => {
|
||||
server_emitter.emit(ServerEvent::Explosion {
|
||||
|
@ -193,7 +193,7 @@ impl<'a> System<'a> for Sys {
|
||||
inventory: read_data.inventories.get(target),
|
||||
stats: read_data.stats.get(target),
|
||||
health: read_data.healths.get(target),
|
||||
pos: pos.0,
|
||||
pos: pos_b.0,
|
||||
ori: read_data.orientations.get(target),
|
||||
char_state: read_data.character_states.get(target),
|
||||
};
|
||||
|
@ -739,7 +739,7 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
|
||||
inventory: inventory_b_maybe,
|
||||
stats: stats_b_maybe,
|
||||
health: Some(health_b),
|
||||
pos,
|
||||
pos: pos_b.0,
|
||||
ori: ori_b_maybe,
|
||||
char_state: char_state_b_maybe,
|
||||
};
|
||||
|
@ -407,9 +407,6 @@ impl SfxMgr {
|
||||
let file_ref = "voxygen.audio.sfx.footsteps.stone_step_1";
|
||||
audio.play_sfx(file_ref, pos.map(|e| e as f32 + 0.5), Some(3.0));
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
Outcome::Damage { pos, .. } => {
|
||||
let file_ref = vec![
|
||||
"voxygen.audio.sfx.character.hit_1",
|
||||
@ -419,6 +416,13 @@ impl SfxMgr {
|
||||
][rand::thread_rng().gen_range(1..4)];
|
||||
audio.play_sfx(file_ref, *pos, None);
|
||||
},
|
||||
Outcome::Block { pos, parry: _parry } => {
|
||||
// TODO: Get audio for blocking and parrying
|
||||
audio.play_sfx("voxygen.audio.sfx.character.arrow_hit", *pos, Some(2.0));
|
||||
},
|
||||
Outcome::ExpChange { .. }
|
||||
| Outcome::ComboChange { .. }
|
||||
| Outcome::SummonedCreature { .. } => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,6 +203,18 @@ impl ParticleMgr {
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::Block { pos, parry } => {
|
||||
if *parry {
|
||||
self.particles.resize_with(self.particles.len() + 20, || {
|
||||
Particle::new(
|
||||
Duration::from_millis(200),
|
||||
time,
|
||||
ParticleMode::GunPowderSpark,
|
||||
*pos + Vec3::unit_z(),
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
Outcome::ProjectileShot { .. }
|
||||
| Outcome::Beam { .. }
|
||||
| Outcome::ExpChange { .. }
|
||||
|
Loading…
Reference in New Issue
Block a user