diff --git a/CHANGELOG.md b/CHANGELOG.md index 15938c84dd..1708bad71a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + +- Esperanto translation +- Item quantity sort in player inventory. +- Using Block('Alt' by default) in Defensive Stance now feels stronger - Recipe for twigs from wooden logs - First version of multisalvage that allows to obtain more than one piece of material from salvage - ### Changed + +- Plugins now target wasm32-unknown-wasi and all wasm cfgs are gone +- Slightly reduced quantities of ingredients needed to craft cooked foods +- Improved and cleaned loot tables for T1 and T2 dungeons as well as large cave monsters (Good bye, Bowls and Stones!) +- Defensive Fell Strike's dmg raised +- Defensive Cascade's more effective against parried foes +- Defensive Riposte's buildup duration raised a bit +- Capabilities of strikes to parry & block now more reliable +- Defensive Disengage now more responsive and can block melee +- Deflect no longer parry melee hits + - Made helmets, necklaces, rings, twig armors and some gliders salvageable - Tweaked stats on some foods so they generally increase a tiny bit more HP - Reduced idle time after consumption from 5 to 4 seconds diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 842adf7ad3..5da9ee04bd 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -3,6 +3,13 @@ // A set of abilities is a primary, a secondary, and a vec of all extra abilities ({ Tool(Sword): ( + guard: Some(Contextualized( + pseudo_id: "veloren.core.pseudo_abilities.sword.guard", + abilities: [ + ([Stance(Sword(Defensive))], (None, "common.abilities.sword.defensive_guard")), + ([], (None, "common.abilities.sword.basic_guard")), + ], + )), primary: Contextualized( pseudo_id: "veloren.core.pseudo_abilities.sword.double_slash", abilities: [ @@ -136,6 +143,7 @@ ], ), Tool(Axe): ( + guard: Some(Simple(None, "common.abilities.axe.basic_guard")), primary: Simple(None, "common.abilities.axe.doublestrike"), secondary: Simple(None, "common.abilities.axe.spin"), abilities: [ @@ -143,6 +151,7 @@ ], ), Tool(Hammer): ( + guard: Some(Simple(None, "common.abilities.hammer.basic_guard")), primary: Simple(None, "common.abilities.hammer.singlestrike"), secondary: Simple(None, "common.abilities.hammer.charged"), abilities: [ diff --git a/assets/common/abilities/axe/basic_guard.ron b/assets/common/abilities/axe/basic_guard.ron new file mode 100644 index 0000000000..8d6bf65b4e --- /dev/null +++ b/assets/common/abilities/axe/basic_guard.ron @@ -0,0 +1,21 @@ +BasicBlock( + buildup_duration: 0.25, + recover_duration: 0.2, + max_angle: 60.0, + block_strength: 0.5, + parry_window: ( + buildup: true, + recover: false, + ), + energy_cost: 5, + energy_regen: 2.5, + can_hold: true, + blocked_attacks: ( + melee: true, + projectiles: false, + beams: false, + ground_shockwaves: false, + air_shockwaves: false, + explosions: false, + ), +) \ No newline at end of file diff --git a/assets/common/abilities/hammer/basic_guard.ron b/assets/common/abilities/hammer/basic_guard.ron new file mode 100644 index 0000000000..8d6bf65b4e --- /dev/null +++ b/assets/common/abilities/hammer/basic_guard.ron @@ -0,0 +1,21 @@ +BasicBlock( + buildup_duration: 0.25, + recover_duration: 0.2, + max_angle: 60.0, + block_strength: 0.5, + parry_window: ( + buildup: true, + recover: false, + ), + energy_cost: 5, + energy_regen: 2.5, + can_hold: true, + blocked_attacks: ( + melee: true, + projectiles: false, + beams: false, + ground_shockwaves: false, + air_shockwaves: false, + explosions: false, + ), +) \ No newline at end of file diff --git a/assets/common/abilities/shield/block.ron b/assets/common/abilities/shield/block.ron index 3711e83731..3fb5439393 100644 --- a/assets/common/abilities/shield/block.ron +++ b/assets/common/abilities/shield/block.ron @@ -8,6 +8,7 @@ BasicBlock( recover: false, ), energy_cost: 0.0, + energy_regen: 0.0, can_hold: true, blocked_attacks: ( melee: true, diff --git a/assets/common/abilities/sword/basic_guard.ron b/assets/common/abilities/sword/basic_guard.ron new file mode 100644 index 0000000000..8d6bf65b4e --- /dev/null +++ b/assets/common/abilities/sword/basic_guard.ron @@ -0,0 +1,21 @@ +BasicBlock( + buildup_duration: 0.25, + recover_duration: 0.2, + max_angle: 60.0, + block_strength: 0.5, + parry_window: ( + buildup: true, + recover: false, + ), + energy_cost: 5, + energy_regen: 2.5, + can_hold: true, + blocked_attacks: ( + melee: true, + projectiles: false, + beams: false, + ground_shockwaves: false, + air_shockwaves: false, + explosions: false, + ), +) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_cascade.ron b/assets/common/abilities/sword/defensive_cascade.ron index e130b809aa..56a8065295 100644 --- a/assets/common/abilities/sword/defensive_cascade.ron +++ b/assets/common/abilities/sword/defensive_cascade.ron @@ -10,7 +10,7 @@ ComboMelee2( ), range: 4.0, angle: 15.0, - damage_effect: Some(BuffsVulnerable(0.5, Parried)), + damage_effect: Some(BuffsVulnerable(0.6, Parried)), ), buildup_duration: 0.4, swing_duration: 0.1, @@ -21,7 +21,7 @@ ComboMelee2( ], energy_cost_per_strike: 5, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_crescent_slash.ron b/assets/common/abilities/sword/defensive_crescent_slash.ron index c3dbe28edb..53fb375a65 100644 --- a/assets/common/abilities/sword/defensive_crescent_slash.ron +++ b/assets/common/abilities/sword/defensive_crescent_slash.ron @@ -21,7 +21,7 @@ ComboMelee2( ], energy_cost_per_strike: 5, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_cross_cut.ron b/assets/common/abilities/sword/defensive_cross_cut.ron index 4b28eb0111..e45bb316a1 100644 --- a/assets/common/abilities/sword/defensive_cross_cut.ron +++ b/assets/common/abilities/sword/defensive_cross_cut.ron @@ -40,7 +40,7 @@ ComboMelee2( energy_cost_per_strike: 2.5, auto_progress: true, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_deflect.ron b/assets/common/abilities/sword/defensive_deflect.ron index e122c71c2b..7c0fe8398f 100644 --- a/assets/common/abilities/sword/defensive_deflect.ron +++ b/assets/common/abilities/sword/defensive_deflect.ron @@ -1,18 +1,19 @@ BasicBlock( - buildup_duration: 0.25, - recover_duration: 0.15, + buildup_duration: 0.4, + recover_duration: 0.2, max_angle: 45.0, block_strength: 0.75, parry_window: ( buildup: true, - recover: true, + recover: false, ), - energy_cost: 0, + energy_cost: 2.5, + energy_regen: 17.5, can_hold: false, blocked_attacks: ( - melee: true, + melee: false, projectiles: true, - beams: false, + beams: true, ground_shockwaves: false, air_shockwaves: false, explosions: false, diff --git a/assets/common/abilities/sword/defensive_disengage.ron b/assets/common/abilities/sword/defensive_disengage.ron index a8cdfc733a..f7b6b0679f 100644 --- a/assets/common/abilities/sword/defensive_disengage.ron +++ b/assets/common/abilities/sword/defensive_disengage.ron @@ -11,13 +11,13 @@ ComboMelee2( range: 6.0, angle: 45.0, ), - buildup_duration: 0.1, + buildup_duration: 0.05, swing_duration: 0.1, hit_timing: 0.6, - recover_duration: 0.4, + recover_duration: 0.3, movement: ( buildup: None, - swing: None, + swing: Some(Reverse(2)), recover: Some(Reverse(1.5)), ), ori_modifier: 0.6, @@ -26,5 +26,6 @@ ComboMelee2( energy_cost_per_strike: 0, meta: ( init_event: Some(EnterStance(Sword(Defensive))), + capabilities: ("BLOCKS"), ), ) diff --git a/assets/common/abilities/sword/defensive_double_slash.ron b/assets/common/abilities/sword/defensive_double_slash.ron index 6228a396c7..57e1c89fd1 100644 --- a/assets/common/abilities/sword/defensive_double_slash.ron +++ b/assets/common/abilities/sword/defensive_double_slash.ron @@ -37,7 +37,7 @@ ComboMelee2( ], energy_cost_per_strike: 0, meta: ( - // Blocks melee attacks at 50% strength - capabilities: ("BUILDUP_BLOCKS"), + // Blocks melee attacks at 50% strength during buildup_duration & swing_duration + capabilities: ("BLOCKS"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_dual_cross_cut.ron b/assets/common/abilities/sword/defensive_dual_cross_cut.ron index b026490b71..50803ab50c 100644 --- a/assets/common/abilities/sword/defensive_dual_cross_cut.ron +++ b/assets/common/abilities/sword/defensive_dual_cross_cut.ron @@ -22,7 +22,7 @@ ComboMelee2( ], energy_cost_per_strike: 5, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_fell_strike.ron b/assets/common/abilities/sword/defensive_fell_strike.ron index 7a712668e2..d9857d023a 100644 --- a/assets/common/abilities/sword/defensive_fell_strike.ron +++ b/assets/common/abilities/sword/defensive_fell_strike.ron @@ -3,7 +3,7 @@ ComboMelee2( ( melee_constructor: ( kind: Slash( - damage: 15, + damage: 20, poise: 5, knockback: 0, energy_regen: 0, @@ -21,7 +21,7 @@ ComboMelee2( ], energy_cost_per_strike: 5, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_guard.ron b/assets/common/abilities/sword/defensive_guard.ron new file mode 100644 index 0000000000..d2ff1157cb --- /dev/null +++ b/assets/common/abilities/sword/defensive_guard.ron @@ -0,0 +1,24 @@ +BasicBlock( + buildup_duration: 0.4, + recover_duration: 0.15, + max_angle: 60.0, + block_strength: 0.75, + parry_window: ( + buildup: true, + recover: false, + ), + energy_cost: 2.5, + energy_regen: 17.5, + can_hold: true, + blocked_attacks: ( + melee: true, + projectiles: false, + beams: false, + ground_shockwaves: false, + air_shockwaves: false, + explosions: false, + ), + meta: ( + requirements: (stance: Some(Sword(Defensive))), + ), +) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_riposte.ron b/assets/common/abilities/sword/defensive_riposte.ron index cd96359902..289c276879 100644 --- a/assets/common/abilities/sword/defensive_riposte.ron +++ b/assets/common/abilities/sword/defensive_riposte.ron @@ -1,6 +1,6 @@ RiposteMelee( energy_cost: 5, - buildup_duration: 0.3, + buildup_duration: 0.4, swing_duration: 0.1, recover_duration: 0.2, melee_constructor: ( diff --git a/assets/common/abilities/sword/defensive_skewer.ron b/assets/common/abilities/sword/defensive_skewer.ron index 7c761a81cf..dd8ad2ab29 100644 --- a/assets/common/abilities/sword/defensive_skewer.ron +++ b/assets/common/abilities/sword/defensive_skewer.ron @@ -26,7 +26,7 @@ ComboMelee2( ], energy_cost_per_strike: 5, meta: ( - // The ability will parry melee attacks in the buildup portion - capabilities: ("BUILDUP_PARRIES"), + // The ability will parry melee attacks in the buildup & swing portion + capabilities: ("PARRIES"), ), ) \ No newline at end of file diff --git a/assets/common/abilities/sword/defensive_vital_jab.ron b/assets/common/abilities/sword/defensive_vital_jab.ron index b1bc99e7f8..f970e2096d 100644 --- a/assets/common/abilities/sword/defensive_vital_jab.ron +++ b/assets/common/abilities/sword/defensive_vital_jab.ron @@ -9,7 +9,7 @@ ChargedMelee( energy_regen: 0, ), scaled: Some(Stab( - damage: 10, + damage: 12, poise: 5, knockback: 0, energy_regen: 15, diff --git a/common/src/combat.rs b/common/src/combat.rs index f86d53c693..cee2ca5950 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -148,6 +148,7 @@ impl Attack { emit(ServerEvent::ParryHook { defender: target.entity, attacker: attacker.map(|a| a.entity), + source, }); 1.0 } else if let Some(block_strength) = char_state.block_strength(source) { diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index de5496bf9c..9a31d74f05 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -42,6 +42,7 @@ pub type AuxiliaryKey = (Option, Option); // considerations. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ActiveAbilities { + pub guard: GuardAbility, pub primary: PrimaryAbility, pub secondary: SecondaryAbility, pub movement: MovementAbility, @@ -55,6 +56,7 @@ impl Component for ActiveAbilities { impl Default for ActiveAbilities { fn default() -> Self { Self { + guard: GuardAbility::Tool, primary: PrimaryAbility::Tool, secondary: SecondaryAbility::Tool, movement: MovementAbility::Species, @@ -123,6 +125,7 @@ impl ActiveAbilities { skill_set: Option<&SkillSet>, ) -> Ability { match input { + AbilityInput::Guard => self.guard.into(), AbilityInput::Primary => self.primary.into(), AbilityInput::Secondary => self.secondary.into(), AbilityInput::Movement => self.movement.into(), @@ -165,6 +168,22 @@ impl ActiveAbilities { }; match ability { + Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand) + .and_then(|abilities| { + abilities + .guard(Some(skill_set), contexts) + .map(|a| a.ability.clone()) + }) + .map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), true)) + .or_else(|| { + ability_set(EquipSlot::ActiveOffhand) + .and_then(|abilities| { + abilities + .secondary(Some(skill_set), contexts) + .map(|a| a.ability.clone()) + }) + .map(|ability| (scale_ability(ability, EquipSlot::ActiveOffhand), false)) + }), Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand) .and_then(|abilities| { abilities @@ -250,6 +269,7 @@ impl ActiveAbilities { } pub enum AbilityInput { + Guard, Primary, Secondary, Movement, @@ -258,6 +278,7 @@ pub enum AbilityInput { #[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)] pub enum Ability { + ToolGuard, ToolPrimary, ToolSecondary, SpeciesMovement, @@ -293,6 +314,31 @@ impl Ability { }; match self { + Ability::ToolGuard => ability_set(EquipSlot::ActiveMainhand) + .and_then(|abilities| { + abilities + .guard(skillset, contexts) + .map(|a| a.id.as_str()) + .or_else(|| { + abilities + .guard + .as_ref() + .and_then(|g| contextual_id(Some(g))) + }) + }) + .or_else(|| { + ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| { + abilities + .guard(skillset, contexts) + .map(|a| a.id.as_str()) + .or_else(|| { + abilities + .guard + .as_ref() + .and_then(|g| contextual_id(Some(g))) + }) + }) + }), Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| { abilities .primary(skillset, contexts) @@ -335,6 +381,20 @@ impl Ability { } } } +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] +pub enum GuardAbility { + Tool, + Empty, +} + +impl From for Ability { + fn from(guard: GuardAbility) -> Self { + match guard { + GuardAbility::Tool => Ability::ToolGuard, + GuardAbility::Empty => Ability::Empty, + } + } +} #[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub enum PrimaryAbility { @@ -550,6 +610,7 @@ pub enum CharacterAbility { block_strength: f32, parry_window: basic_block::ParryWindow, energy_cost: f32, + energy_regen: f32, can_hold: bool, blocked_attacks: AttackFilters, #[serde(default)] @@ -967,30 +1028,6 @@ impl CharacterAbility { } } - pub fn default_block() -> CharacterAbility { - CharacterAbility::BasicBlock { - buildup_duration: 0.25, - recover_duration: 0.2, - max_angle: 60.0, - block_strength: 0.5, - parry_window: basic_block::ParryWindow { - buildup: true, - recover: false, - }, - energy_cost: 2.5, - can_hold: true, - blocked_attacks: AttackFilters { - melee: true, - projectiles: false, - ground_shockwaves: false, - air_shockwaves: false, - beams: false, - explosions: false, - }, - meta: Default::default(), - } - } - #[must_use] pub fn adjusted_by_stats(mut self, stats: Stats) -> Self { use CharacterAbility::*; @@ -1090,6 +1127,7 @@ impl CharacterAbility { block_strength: _, parry_window: _, ref mut energy_cost, + energy_regen: _, can_hold: _, blocked_attacks: _, meta: _, @@ -2247,6 +2285,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { block_strength, parry_window, energy_cost, + energy_regen, can_hold, blocked_attacks, meta: _, @@ -2258,6 +2297,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { block_strength: *block_strength, parry_window: *parry_window, energy_cost: *energy_cost, + energy_regen: *energy_regen, can_hold: *can_hold, blocked_attacks: *blocked_attacks, ability_info, @@ -2910,13 +2950,13 @@ bitflags::bitflags! { // Allows blocking to interrupt the ability at any point const BLOCK_INTERRUPT = 0b00000010; // When the ability is in the buildup section, it counts as a block with 50% DR - const BUILDUP_BLOCKS = 0b00000100; + const BLOCKS = 0b00000100; // When in the ability, an entity only receives half as much poise damage const POISE_RESISTANT = 0b00001000; // WHen in the ability, an entity only receives half as much knockback const KNOCKBACK_RESISTANT = 0b00010000; // The ability will parry melee attacks in the buildup portion - const BUILDUP_PARRIES = 0b00100000; + const PARRIES = 0b00100000; } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index e914d4b32b..8cafa1b659 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -289,8 +289,11 @@ impl CharacterState { .map(|a| a.ability_meta) .map(|m| m.capabilities) { - (capabilities.contains(Capability::BUILDUP_BLOCKS) - && matches!(self.stage_section(), Some(StageSection::Buildup))) + (capabilities.contains(Capability::BLOCKS) + && matches!( + self.stage_section(), + Some(StageSection::Buildup | StageSection::Action) + )) .then_some(0.5) } else { None @@ -320,13 +323,20 @@ impl CharacterState { .ability_info() .map(|a| a.ability_meta.capabilities) .map_or(false, |c| { - c.contains(Capability::BUILDUP_PARRIES) - && matches!(self.stage_section(), Some(StageSection::Buildup)) + c.contains(Capability::PARRIES) + && matches!( + self.stage_section(), + Some(StageSection::Buildup | StageSection::Action) + ) }); let from_state = match self { CharacterState::BasicBlock(c) => c.is_parry(attack_source), CharacterState::RiposteMelee(c) => { - melee && matches!(c.stage_section, StageSection::Buildup) + melee + && matches!( + c.stage_section, + StageSection::Buildup | StageSection::Action + ) }, _ => false, }; diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index 112aff9f76..f26396f176 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -215,11 +215,12 @@ impl From for Option { fn from(input: InputKind) -> Option { use ability::AbilityInput; match input { + InputKind::Block => Some(AbilityInput::Guard), InputKind::Primary => Some(AbilityInput::Primary), InputKind::Secondary => Some(AbilityInput::Secondary), InputKind::Roll => Some(AbilityInput::Movement), InputKind::Ability(index) => Some(AbilityInput::Auxiliary(index)), - InputKind::Jump | InputKind::Fly | InputKind::Block => None, + InputKind::Jump | InputKind::Fly => None, } } } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 3127da559e..fa458d4d39 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -294,6 +294,7 @@ impl Tool { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct AbilitySet { + pub guard: Option>, pub primary: AbilityKind, pub secondary: AbilityKind, pub abilities: Vec>, @@ -422,6 +423,7 @@ impl AbilitySet { impl AbilitySet { pub fn map U>(self, mut f: F) -> AbilitySet { AbilitySet { + guard: self.guard.map(|g| g.map(&mut f)), primary: self.primary.map(&mut f), secondary: self.secondary.map(&mut f), abilities: self.abilities.into_iter().map(|x| x.map(&mut f)).collect(), @@ -430,12 +432,19 @@ impl AbilitySet { pub fn map_ref U>(&self, mut f: F) -> AbilitySet { AbilitySet { + guard: self.guard.as_ref().map(|g| g.map_ref(&mut f)), primary: self.primary.map_ref(&mut f), secondary: self.secondary.map_ref(&mut f), abilities: self.abilities.iter().map(|x| x.map_ref(&mut f)).collect(), } } + pub fn guard(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> { + self.guard + .as_ref() + .and_then(|g| g.ability(skillset, contexts)) + } + pub fn primary(&self, skillset: Option<&SkillSet>, contexts: &[AbilityContext]) -> Option<&T> { self.primary.ability(skillset, contexts) } @@ -463,6 +472,7 @@ impl AbilitySet { impl Default for AbilitySet { fn default() -> Self { AbilitySet { + guard: None, primary: AbilityKind::Simple(None, AbilityItem { id: String::new(), ability: CharacterAbility::default(), diff --git a/common/src/event.rs b/common/src/event.rs index 46186047d6..b30b9a048d 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,5 +1,6 @@ use crate::{ character::CharacterId, + combat::AttackSource, comp::{ self, agent::Sound, @@ -261,6 +262,7 @@ pub enum ServerEvent { ParryHook { defender: EcsEntity, attacker: Option, + source: AttackSource, }, RequestSiteInfo { entity: EcsEntity, diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index 386f252941..c29d1b3ee9 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -33,6 +33,8 @@ pub struct StaticData { pub ability_info: AbilityInfo, /// Energy consumed to initiate the block pub energy_cost: f32, + /// Energy recovered upon successful parry + pub energy_regen: f32, /// Whether block can be held pub can_hold: bool, /// What kinds of attacks the block applies to diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 5f29a58837..0f5304fbc6 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -14,8 +14,8 @@ use crate::{ }, quadruped_low, quadruped_medium, quadruped_small, ship, skills::{Skill, SwimSkill, SKILL_MODIFIERS}, - theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind, - InventoryAction, Melee, StateUpdate, + theropod, Body, CharacterState, Density, InputAttr, InputKind, InventoryAction, Melee, + StateUpdate, }, consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE}, event::{LocalEvent, ServerEvent}, @@ -1247,15 +1247,16 @@ pub fn handle_input( input: InputKind, ) { match input { - InputKind::Primary | InputKind::Secondary | InputKind::Ability(_) | InputKind::Roll => { + InputKind::Primary + | InputKind::Secondary + | InputKind::Ability(_) + | InputKind::Block + | InputKind::Roll => { handle_ability(data, update, output_events, input); }, InputKind::Jump => { handle_jump(data, output_events, update, 1.0); }, - InputKind::Block => { - handle_block_input(data, update); - }, InputKind::Fly => {}, } } @@ -1271,30 +1272,6 @@ pub fn attempt_input( } } -/// Checks that player can block, then attempts to block -pub fn handle_block_input(data: &JoinData<'_>, update: &mut StateUpdate) -> bool { - let can_block = |equip_slot| matches!(unwrap_tool_data(data, equip_slot), Some((kind, _)) if kind.can_block()); - let hands = get_hands(data); - if input_is_pressed(data, InputKind::Block) - && (can_block(EquipSlot::ActiveMainhand) - || (hands.0.is_none() && can_block(EquipSlot::ActiveOffhand))) - { - let ability = CharacterAbility::default_block(); - if ability.requirements_paid(data, update) { - update.character = CharacterState::from(( - &ability, - AbilityInfo::from_input(data, false, InputKind::Block, Default::default()), - data, - )); - true - } else { - false - } - } else { - false - } -} - /// Returns whether an interrupt occurred pub fn handle_interrupts( data: &JoinData, @@ -1314,8 +1291,8 @@ pub fn handle_interrupts( }); if can_dodge && input_is_pressed(data, InputKind::Roll) { handle_ability(data, update, output_events, InputKind::Roll) - } else if can_block { - handle_block_input(data, update) + } else if can_block && input_is_pressed(data, InputKind::Block) { + handle_ability(data, update, output_events, InputKind::Block) } else { false } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index e9272365ce..db21c7bc86 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -14,7 +14,7 @@ use crate::{ use authc::Uuid; use common::{ combat, - combat::DamageContributor, + combat::{AttackSource, DamageContributor}, comp::{ self, aura, buff, chat::{KillSource, KillType}, @@ -1361,7 +1361,12 @@ pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) { } } -pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option) { +pub fn handle_parry_hook( + server: &Server, + defender: EcsEntity, + attacker: Option, + source: AttackSource, +) { let ecs = &server.state.ecs(); let server_eventbus = ecs.read_resource::>(); // Reset character state of defender @@ -1379,7 +1384,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option< // Refund half the energy of entering the block for a successful parry server_eventbus.emit_now(ServerEvent::EnergyChange { entity: defender, - change: c.static_data.energy_cost / 2.0, + change: c.static_data.energy_regen, }); true }, @@ -1391,7 +1396,7 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option< } }; - if let Some(attacker) = attacker { + if let Some(attacker) = attacker && matches!(source, AttackSource::Melee){ // When attacker is parried, add the parried debuff for 2 seconds, which slows // them let data = buff::BuffData::new(1.0, Some(Secs(2.0)), None); diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 37669c5581..00cba6b401 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -237,9 +237,11 @@ impl Server { ServerEvent::ComboChange { entity, change } => { handle_combo_change(self, entity, change) }, - ServerEvent::ParryHook { defender, attacker } => { - handle_parry_hook(self, defender, attacker) - }, + ServerEvent::ParryHook { + defender, + attacker, + source, + } => handle_parry_hook(self, defender, attacker, source), ServerEvent::RequestSiteInfo { entity, id } => handle_site_info(self, entity, id), ServerEvent::MineBlock { entity, pos, tool } => { handle_mine_block(self, entity, pos, tool) diff --git a/voxygen/anim/src/character/block.rs b/voxygen/anim/src/character/block.rs index 71967ff244..97d3a65e46 100644 --- a/voxygen/anim/src/character/block.rs +++ b/voxygen/anim/src/character/block.rs @@ -51,7 +51,11 @@ impl Animation for BlockAnimation { next.second.orientation = Quaternion::rotation_z(0.0); match ability_id { - None => { + None + | Some("common.abilities.sword.basic_guard") + | Some("common.abilities.axe.basic_guard") + | Some("common.abilities.hammer.basic_guard") + | Some("common.abilities.sword.defensive_guard") => { let speed = Vec2::::from(velocity).magnitude(); let (movement1base, move2, movement3) = match stage_section {