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.
|
- Missing translations can be displayed in English.
|
||||||
- New large birds npcs
|
- New large birds npcs
|
||||||
- Day period dependant wildlife spawns
|
- Day period dependant wildlife spawns
|
||||||
|
- You can now block and parry with melee weapons
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
|
states::utils::StageSection,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
@ -116,7 +117,12 @@ impl Attack {
|
|||||||
|
|
||||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
|
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 damage_reduction = Damage::compute_damage_reduction(target.inventory, target.stats);
|
||||||
let block_reduction = match source {
|
let block_reduction = match source {
|
||||||
AttackSource::Melee => {
|
AttackSource::Melee => {
|
||||||
@ -125,7 +131,11 @@ impl Attack {
|
|||||||
{
|
{
|
||||||
if ori.look_vec().angle_between(-*dir) < data.static_data.max_angle.to_radians()
|
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
|
1.0
|
||||||
} else {
|
} else {
|
||||||
data.static_data.block_strength
|
data.static_data.block_strength
|
||||||
@ -164,7 +174,8 @@ impl Attack {
|
|||||||
.filter(|d| d.target.map_or(true, |t| t == target_group))
|
.filter(|d| d.target.map_or(true, |t| t == target_group))
|
||||||
.filter(|d| !(matches!(d.target, Some(GroupTarget::OutOfGroup)) && target_dodging))
|
.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(
|
let change = damage.damage.calculate_health_change(
|
||||||
damage_reduction,
|
damage_reduction,
|
||||||
attacker.map(|a| a.uid),
|
attacker.map(|a| a.uid),
|
||||||
|
@ -350,8 +350,8 @@ impl CharacterAbility {
|
|||||||
|
|
||||||
pub fn default_block() -> CharacterAbility {
|
pub fn default_block() -> CharacterAbility {
|
||||||
CharacterAbility::BasicBlock {
|
CharacterAbility::BasicBlock {
|
||||||
buildup_duration: 0.1,
|
buildup_duration: 0.3,
|
||||||
recover_duration: 0.1,
|
recover_duration: 0.2,
|
||||||
max_angle: 60.0,
|
max_angle: 60.0,
|
||||||
block_strength: 0.5,
|
block_strength: 0.5,
|
||||||
energy_cost: 50.0,
|
energy_cost: 50.0,
|
||||||
|
@ -158,7 +158,10 @@ pub enum InputKind {
|
|||||||
|
|
||||||
impl InputKind {
|
impl InputKind {
|
||||||
pub fn is_ability(self) -> bool {
|
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 {
|
Damage {
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
},
|
},
|
||||||
|
Block {
|
||||||
|
pos: Vec3<f32>,
|
||||||
|
parry: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Outcome {
|
impl Outcome {
|
||||||
@ -70,7 +74,8 @@ impl Outcome {
|
|||||||
| Outcome::Beam { pos, .. }
|
| Outcome::Beam { pos, .. }
|
||||||
| Outcome::SkillPointGain { pos, .. }
|
| Outcome::SkillPointGain { pos, .. }
|
||||||
| Outcome::SummonedCreature { 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::BreakBlock { pos, .. } => Some(pos.map(|e| e as f32 + 0.5)),
|
||||||
Outcome::ExpChange { .. } | Outcome::ComboChange { .. } => None,
|
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) {
|
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||||
let hands = |equip_slot| match data.inventory.equipped(equip_slot).map(|i| i.kind()) {
|
let hands = get_hands(data);
|
||||||
Some(ItemKind::Tool(tool)) => Some(tool.hands),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mouse1 and Skill1 always use the MainHand slot
|
// Mouse1 and Skill1 always use the MainHand slot
|
||||||
let always_main_hand = matches!(input, InputKind::Primary | InputKind::Ability(0));
|
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
|
// skill_index used to select ability for the AbilityKey::Skill2 input
|
||||||
let (equip_slot, skill_index) = if no_main_hand {
|
let (equip_slot, skill_index) = if no_main_hand {
|
||||||
(Some(EquipSlot::Offhand), 1)
|
(Some(EquipSlot::Offhand), 1)
|
||||||
} else if always_main_hand {
|
} else if always_main_hand {
|
||||||
(Some(EquipSlot::Mainhand), 0)
|
(Some(EquipSlot::Mainhand), 0)
|
||||||
} else {
|
} else {
|
||||||
let hands = (hands(EquipSlot::Mainhand), hands(EquipSlot::Offhand));
|
|
||||||
match hands {
|
match hands {
|
||||||
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
(Some(Hands::Two), _) => (Some(EquipSlot::Mainhand), 1),
|
||||||
(_, Some(Hands::One)) => (Some(EquipSlot::Offhand), 0),
|
(_, 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) {
|
pub fn handle_block_input(data: &JoinData, update: &mut StateUpdate) {
|
||||||
let can_block =
|
let can_block =
|
||||||
|equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some(tool) if tool.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();
|
let ability = CharacterAbility::default_block();
|
||||||
if ability.requirements_paid(data, update) {
|
if ability.requirements_paid(data, update) {
|
||||||
update.character = CharacterState::from((
|
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) {
|
pub fn get_crit_data(data: &JoinData, ai: AbilityInfo) -> (f32, f32) {
|
||||||
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
const DEFAULT_CRIT_DATA: (f32, f32) = (0.5, 1.3);
|
||||||
use HandInfo::*;
|
use HandInfo::*;
|
||||||
|
@ -188,7 +188,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
inventory: read_data.inventories.get(target),
|
inventory: read_data.inventories.get(target),
|
||||||
stats: read_data.stats.get(target),
|
stats: read_data.stats.get(target),
|
||||||
health: read_data.healths.get(target),
|
health: read_data.healths.get(target),
|
||||||
pos: pos.0,
|
pos: pos_b.0,
|
||||||
ori: read_data.orientations.get(target),
|
ori: read_data.orientations.get(target),
|
||||||
char_state: read_data.character_states.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),
|
inventory: read_data.inventories.get(target),
|
||||||
stats: read_data.stats.get(target),
|
stats: read_data.stats.get(target),
|
||||||
health: read_data.healths.get(target),
|
health: read_data.healths.get(target),
|
||||||
pos: pos.0,
|
pos: pos_b.0,
|
||||||
ori: read_data.orientations.get(target),
|
ori: read_data.orientations.get(target),
|
||||||
char_state: read_data.char_states.get(target),
|
char_state: read_data.char_states.get(target),
|
||||||
};
|
};
|
||||||
|
@ -111,56 +111,58 @@ impl<'a> System<'a> for Sys {
|
|||||||
.uid_allocator
|
.uid_allocator
|
||||||
.retrieve_entity_internal(other.into())
|
.retrieve_entity_internal(other.into())
|
||||||
{
|
{
|
||||||
let owner_entity = projectile.owner.and_then(|u| {
|
if let Some(pos) = read_data.positions.get(target) {
|
||||||
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
let owner_entity = projectile.owner.and_then(|u| {
|
||||||
});
|
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
||||||
|
|
||||||
let attacker_info =
|
|
||||||
owner_entity.zip(projectile.owner).map(|(entity, uid)| {
|
|
||||||
AttackerInfo {
|
|
||||||
entity,
|
|
||||||
uid,
|
|
||||||
energy: read_data.energies.get(entity),
|
|
||||||
combo: read_data.combos.get(entity),
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_info = TargetInfo {
|
let attacker_info =
|
||||||
entity: target,
|
owner_entity.zip(projectile.owner).map(|(entity, uid)| {
|
||||||
inventory: read_data.inventories.get(target),
|
AttackerInfo {
|
||||||
stats: read_data.stats.get(target),
|
entity,
|
||||||
health: read_data.healths.get(target),
|
uid,
|
||||||
pos: pos.0,
|
energy: read_data.energies.get(entity),
|
||||||
// TODO: Let someone smarter figure this out
|
combo: read_data.combos.get(entity),
|
||||||
// ori: orientations.get(target),
|
}
|
||||||
ori: None,
|
});
|
||||||
char_state: read_data.character_states.get(target),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(&body) = read_data.bodies.get(entity) {
|
let target_info = TargetInfo {
|
||||||
outcomes.push(Outcome::ProjectileHit {
|
entity: target,
|
||||||
|
inventory: read_data.inventories.get(target),
|
||||||
|
stats: read_data.stats.get(target),
|
||||||
|
health: read_data.healths.get(target),
|
||||||
pos: pos.0,
|
pos: pos.0,
|
||||||
body,
|
// TODO: Let someone smarter figure this out
|
||||||
vel: read_data
|
// ori: orientations.get(target),
|
||||||
.velocities
|
ori: None,
|
||||||
.get(entity)
|
char_state: read_data.character_states.get(target),
|
||||||
.map_or(Vec3::zero(), |v| v.0),
|
};
|
||||||
source: projectile.owner,
|
|
||||||
target: read_data.uids.get(target).copied(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
attack.apply_attack(
|
if let Some(&body) = read_data.bodies.get(entity) {
|
||||||
target_group,
|
outcomes.push(Outcome::ProjectileHit {
|
||||||
attacker_info,
|
pos: pos.0,
|
||||||
target_info,
|
body,
|
||||||
ori.look_dir(),
|
vel: read_data
|
||||||
false,
|
.velocities
|
||||||
1.0,
|
.get(entity)
|
||||||
AttackSource::Projectile,
|
.map_or(Vec3::zero(), |v| v.0),
|
||||||
|e| server_emitter.emit(e),
|
source: projectile.owner,
|
||||||
|o| outcomes.push(o),
|
target: read_data.uids.get(target).copied(),
|
||||||
);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
attack.apply_attack(
|
||||||
|
target_group,
|
||||||
|
attacker_info,
|
||||||
|
target_info,
|
||||||
|
ori.look_dir(),
|
||||||
|
false,
|
||||||
|
1.0,
|
||||||
|
AttackSource::Projectile,
|
||||||
|
|e| server_emitter.emit(e),
|
||||||
|
|o| outcomes.push(o),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
projectile::Effect::Explode(e) => {
|
projectile::Effect::Explode(e) => {
|
||||||
|
@ -193,7 +193,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
inventory: read_data.inventories.get(target),
|
inventory: read_data.inventories.get(target),
|
||||||
stats: read_data.stats.get(target),
|
stats: read_data.stats.get(target),
|
||||||
health: read_data.healths.get(target),
|
health: read_data.healths.get(target),
|
||||||
pos: pos.0,
|
pos: pos_b.0,
|
||||||
ori: read_data.orientations.get(target),
|
ori: read_data.orientations.get(target),
|
||||||
char_state: read_data.character_states.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,
|
inventory: inventory_b_maybe,
|
||||||
stats: stats_b_maybe,
|
stats: stats_b_maybe,
|
||||||
health: Some(health_b),
|
health: Some(health_b),
|
||||||
pos,
|
pos: pos_b.0,
|
||||||
ori: ori_b_maybe,
|
ori: ori_b_maybe,
|
||||||
char_state: char_state_b_maybe,
|
char_state: char_state_b_maybe,
|
||||||
};
|
};
|
||||||
|
@ -407,9 +407,6 @@ impl SfxMgr {
|
|||||||
let file_ref = "voxygen.audio.sfx.footsteps.stone_step_1";
|
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));
|
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, .. } => {
|
Outcome::Damage { pos, .. } => {
|
||||||
let file_ref = vec![
|
let file_ref = vec![
|
||||||
"voxygen.audio.sfx.character.hit_1",
|
"voxygen.audio.sfx.character.hit_1",
|
||||||
@ -419,6 +416,13 @@ impl SfxMgr {
|
|||||||
][rand::thread_rng().gen_range(1..4)];
|
][rand::thread_rng().gen_range(1..4)];
|
||||||
audio.play_sfx(file_ref, *pos, None);
|
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::ProjectileShot { .. }
|
||||||
| Outcome::Beam { .. }
|
| Outcome::Beam { .. }
|
||||||
| Outcome::ExpChange { .. }
|
| Outcome::ExpChange { .. }
|
||||||
|
Loading…
Reference in New Issue
Block a user