diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index eddc857526..5a36727eb9 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -147,24 +147,30 @@ primary: Simple(None, "common.abilities.axe.triple_chop"), secondary: Simple(None, "common.abilities.axe.cleave"), abilities: [ - Simple(Some(Axe(BrutalSwing)), "common.abilities.axe.brutal_swing"), - Simple(Some(Axe(Berserk)), "common.abilities.axe.berserk"), - Simple(Some(Axe(RisingTide)), "common.abilities.axe.rising_tide"), - Simple(Some(Axe(SavageSense)), "common.abilities.axe.savage_sense"), - Simple(Some(Axe(AdrenalineRush)), "common.abilities.axe.adrenaline_rush"), - Simple(Some(Axe(Execute)), "common.abilities.axe.execute"), - Simple(Some(Axe(Rake)), "common.abilities.axe.rake"), - Simple(Some(Axe(Bloodfeast)), "common.abilities.axe.bloodfeast"), - Simple(Some(Axe(FierceRaze)), "common.abilities.axe.fierce_raze"), - Simple(Some(Axe(Furor)), "common.abilities.axe.furor"), - Simple(Some(Axe(Fracture)), "common.abilities.axe.fracture"), - Simple(Some(Axe(Lacerate)), "common.abilities.axe.lacerate"), - Simple(Some(Axe(SkullBash)), "common.abilities.axe.skull_bash"), - Simple(Some(Axe(Sunder)), "common.abilities.axe.sunder"), - Simple(Some(Axe(Plunder)), "common.abilities.axe.plunder"), - Simple(Some(Axe(Defiance)), "common.abilities.axe.defiance"), - Simple(Some(Axe(Keelhaul)), "common.abilities.axe.keelhaul"), - Simple(Some(Axe(Bulkhead)), "common.abilities.axe.bulkhead"), + Simple(Axe(BrutalSwing), "common.abilities.axe.brutal_swing"), + Simple(Axe(Berserk), "common.abilities.axe.berserk"), + Simple(Axe(RisingTide), "common.abilities.axe.rising_tide"), + Simple(Axe(SavageSense), "common.abilities.axe.savage_sense"), + Simple(Axe(AdrenalineRush), "common.abilities.axe.adrenaline_rush"), + Contextualized( + pseudo_id: "common.abilities.axe.execute", + abilities: [ + ([Combo(50)], (Axe(Maelstrom), "common.abilities.axe.maelstrom")), + ([], (Axe(Execute), "common.abilities.axe.execute")), + ], + ), + Simple(Axe(Rake), "common.abilities.axe.rake"), + Simple(Axe(Bloodfeast), "common.abilities.axe.bloodfeast"), + Simple(Axe(FierceRaze), "common.abilities.axe.fierce_raze"), + Simple(Axe(Furor), "common.abilities.axe.furor"), + Simple(Axe(Fracture), "common.abilities.axe.fracture"), + Simple(Axe(Lacerate), "common.abilities.axe.lacerate"), + Simple(Axe(SkullBash), "common.abilities.axe.skull_bash"), + Simple(Axe(Sunder), "common.abilities.axe.sunder"), + Simple(Axe(Plunder), "common.abilities.axe.plunder"), + Simple(Axe(Defiance), "common.abilities.axe.defiance"), + Simple(Axe(Keelhaul), "common.abilities.axe.keelhaul"), + Simple(Axe(Bulkhead), "common.abilities.axe.bulkhead"), ], ), Tool(Hammer): ( diff --git a/assets/common/abilities/axe/execute.ron b/assets/common/abilities/axe/execute.ron index de19c7f486..1857c8c998 100644 --- a/assets/common/abilities/axe/execute.ron +++ b/assets/common/abilities/axe/execute.ron @@ -1,6 +1,6 @@ FinisherMelee( energy_cost: 0, - buildup_duration: 0.6, + buildup_duration: 0.5, swing_duration: 0.2, recover_duration: 0.4, melee_constructor: ( diff --git a/assets/common/abilities/axe/maelstrom.ron b/assets/common/abilities/axe/maelstrom.ron index b4732b4a2f..5416516fe3 100644 --- a/assets/common/abilities/axe/maelstrom.ron +++ b/assets/common/abilities/axe/maelstrom.ron @@ -1,22 +1,18 @@ -ComboMelee2( - strikes: [ - ( - melee_constructor: ( - kind: Slash( - damage: 4, - poise: 5, - knockback: 0, - energy_regen: 5, - ), - range: 3.0, - angle: 45.0, - ), - buildup_duration: 0.15, - swing_duration: 0.05, - hit_timing: 0.5, - recover_duration: 0.1, - ori_modifier: 0.6, +FinisherMelee( + energy_cost: 0, + buildup_duration: 0.5, + swing_duration: 0.4, + recover_duration: 0.2, + melee_constructor: ( + kind: Slash( + damage: 75, + poise: 100, + knockback: 0, + energy_regen: 0, ), - ], - energy_cost_per_strike: 0, + range: 3.0, + angle: 360.0, + multi_target: Some(Normal), + ), + minimum_combo: 50, ) \ No newline at end of file diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index fa458d4d39..b78505615f 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -11,7 +11,7 @@ use crate::{ Inventory, }, skills::Skill, - CharacterAbility, SkillSet, + CharacterAbility, Combo, SkillSet, }, }; use hashbrown::HashMap; @@ -361,7 +361,7 @@ impl AbilityKind { .find_map(|(req_contexts, a)| { req_contexts .iter() - .all(|req| contexts.contains(req)) + .all(|req| req.fulfilled_by(contexts)) .then_some(a) }), } @@ -376,10 +376,15 @@ pub enum AbilityContext { /// files(s). Stance(Stance), DualWieldingSameKind, + Combo(u32), } impl AbilityContext { - pub fn from(stance: Option<&Stance>, inv: Option<&Inventory>) -> Vec { + pub fn from( + stance: Option<&Stance>, + inv: Option<&Inventory>, + combo: Option<&Combo>, + ) -> Vec { let mut contexts = Vec::new(); match stance { Some(Stance::None) => {}, @@ -400,8 +405,29 @@ impl AbilityContext { contexts.push(AbilityContext::DualWieldingSameKind) } } + if let Some(combo) = combo { + contexts.push(AbilityContext::Combo(combo.counter())); + } contexts } + + fn fulfilled_by(&self, contexts: &[AbilityContext]) -> bool { + match self { + basic_context @ Self::Stance(_) | basic_context @ Self::DualWieldingSameKind => { + contexts.contains(basic_context) + }, + Self::Combo(required) => contexts + .iter() + .filter_map(|context| { + if let Self::Combo(combo) = context { + Some(combo) + } else { + None + } + }) + .any(|combo| combo >= required), + } + } } impl AbilitySet { diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 47876f5cc6..4dd1b5b14b 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1204,7 +1204,7 @@ fn handle_ability( output_events: &mut OutputEvents, input: InputKind, ) -> bool { - let contexts = AbilityContext::from(data.stance, data.inventory); + let contexts = AbilityContext::from(data.stance, data.inventory, data.combo); if let Some(ability_input) = input.into() { if let Some((ability, from_offhand)) = data .active_abilities diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 784861ce6b..b44899a9e1 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -1359,7 +1359,7 @@ impl<'a> AgentData<'a> { enum ActionStateConditions { ConditionStaffCanShockwave = 0, } - let contexts = AbilityContext::from(self.stance, Some(self.inventory)); + let contexts = AbilityContext::from(self.stance, Some(self.inventory), self.combo); let extract_ability = |input: AbilityInput| { self.active_abilities .activate_ability( diff --git a/server/agent/src/util.rs b/server/agent/src/util.rs index 57a635381d..0703b9d991 100644 --- a/server/agent/src/util.rs +++ b/server/agent/src/util.rs @@ -208,7 +208,7 @@ impl<'a> AgentData<'a> { } pub fn extract_ability(&self, input: AbilityInput) -> Option { - let context = AbilityContext::from(self.stance, Some(self.inventory)); + let context = AbilityContext::from(self.stance, Some(self.inventory), self.combo); AbilityData::from_ability( &self .active_abilities diff --git a/voxygen/anim/src/character/chargeswing.rs b/voxygen/anim/src/character/chargeswing.rs index 88ce552c10..c8e91d0c40 100644 --- a/voxygen/anim/src/character/chargeswing.rs +++ b/voxygen/anim/src/character/chargeswing.rs @@ -37,10 +37,12 @@ impl Animation for ChargeswingAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_z(0.0); - next.main_weapon_trail = true; next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.orientation = Quaternion::rotation_z(0.0); - next.off_weapon_trail = true; + if matches!(stage_section, Some(StageSection::Action)) { + next.main_weapon_trail = true; + next.off_weapon_trail = true; + } match ability_id { Some( @@ -237,16 +239,10 @@ impl Animation for ChargeswingAnimation { Some("common.abilities.axe.cleave") => { let (move1, move2, move3, tension) = match stage_section { Some(StageSection::Charge) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; (anim_time.min(1.0), 0.0, 0.0, (anim_time * 20.0).sin()) }, Some(StageSection::Action) => (1.0, anim_time.powi(2), 0.0, 0.0), - Some(StageSection::Recover) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (1.0, 1.0, anim_time, 0.0) - }, + Some(StageSection::Recover) => (1.0, 1.0, anim_time, 0.0), _ => (0.0, 0.0, 0.0, 0.0), }; let pullback = 1.0 - move3; diff --git a/voxygen/anim/src/character/combomelee.rs b/voxygen/anim/src/character/combomelee.rs index d72659302b..e75fd42e19 100644 --- a/voxygen/anim/src/character/combomelee.rs +++ b/voxygen/anim/src/character/combomelee.rs @@ -32,10 +32,12 @@ impl Animation for ComboAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_z(0.0); - next.main_weapon_trail = true; next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.orientation = Quaternion::rotation_z(0.0); - next.off_weapon_trail = true; + if matches!(stage_section, Some(StageSection::Action)) { + next.main_weapon_trail = true; + next.off_weapon_trail = true; + } let multi_strike_pullback = 1.0 - if matches!(stage_section, Some(StageSection::Recover)) { anim_time.powi(4) @@ -882,17 +884,9 @@ impl Animation for ComboAnimation { Some("common.abilities.axe.triple_chop") => { let (move1, move2) = if strike == current_strike { match stage_section { - Some(StageSection::Buildup) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (anim_time, 0.0) - }, + Some(StageSection::Buildup) => (anim_time, 0.0), Some(StageSection::Action) => (1.0, anim_time), - Some(StageSection::Recover) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (1.0, 1.0) - }, + Some(StageSection::Recover) => (1.0, 1.0), _ => (0.0, 0.0), } } else { @@ -972,17 +966,9 @@ impl Animation for ComboAnimation { }, Some("common.abilities.axe.brutal_swing") => { let (move1, move2_raw) = match stage_section { - Some(StageSection::Buildup) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (anim_time, 0.0) - }, + Some(StageSection::Buildup) => (anim_time, 0.0), Some(StageSection::Action) => (1.0, anim_time), - Some(StageSection::Recover) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (1.0, 1.0) - }, + Some(StageSection::Recover) => (1.0, 1.0), _ => (0.0, 0.0), }; let move1 = move1 * multi_strike_pullback; @@ -1007,17 +993,9 @@ impl Animation for ComboAnimation { }, Some("common.abilities.axe.rising_tide") => { let (move1, move2_raw) = match stage_section { - Some(StageSection::Buildup) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (anim_time, 0.0) - }, + Some(StageSection::Buildup) => (anim_time, 0.0), Some(StageSection::Action) => (1.0, anim_time), - Some(StageSection::Recover) => { - next.main_weapon_trail = false; - next.off_weapon_trail = false; - (1.0, 1.0) - }, + Some(StageSection::Recover) => (1.0, 1.0), _ => (0.0, 0.0), }; let move1 = move1 * multi_strike_pullback; diff --git a/voxygen/anim/src/character/finishermelee.rs b/voxygen/anim/src/character/finishermelee.rs index 4b41b529e7..0b0a1cab35 100644 --- a/voxygen/anim/src/character/finishermelee.rs +++ b/voxygen/anim/src/character/finishermelee.rs @@ -26,10 +26,12 @@ impl Animation for FinisherMeleeAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_z(0.0); - next.main_weapon_trail = true; next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.orientation = Quaternion::rotation_z(0.0); - next.off_weapon_trail = true; + if matches!(stage_section, Some(StageSection::Action)) { + next.main_weapon_trail = true; + next.off_weapon_trail = true; + } match ability_id { Some("common.abilities.sword.basic_mighty_strike") => { @@ -176,6 +178,46 @@ impl Animation for FinisherMeleeAnimation { next.control.position += Vec3::new(move2 * -3.0, move2 * 12.0, move2 * -17.0); next.control.orientation.rotate_z(move2 * 0.7); }, + Some("common.abilities.axe.maelstrom") => { + let (move1, move2_raw, move3) = match stage_section { + Some(StageSection::Buildup) => (anim_time, 0.0, 0.0), + Some(StageSection::Action) => (1.0, anim_time, 0.0), + Some(StageSection::Recover) => (1.0, 1.0, anim_time), + _ => (0.0, 0.0, 0.0), + }; + let pullback = 1.0 - move3; + let move1 = move1 * pullback; + let move2 = move2_raw * pullback; + + next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2); + next.hand_l.orientation = + Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4); + next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2); + next.hand_r.orientation = + Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5); + + next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2); + next.control.orientation = Quaternion::rotation_x(s_a.ac.3) + * Quaternion::rotation_y(s_a.ac.4) + * Quaternion::rotation_z(s_a.ac.5); + + next.control.orientation.rotate_x(move1 * 0.9); + next.chest.orientation.rotate_z(move1 * 1.2); + next.head.orientation.rotate_z(move1 * -0.5); + next.belt.orientation.rotate_z(move1 * -0.3); + next.shorts.orientation.rotate_z(move1 * -0.7); + next.control.position += Vec3::new(move1 * 4.0, move1 * -12.0, move1 * 11.0); + + next.chest.orientation.rotate_z(move2 * -2.0); + next.head.orientation.rotate_z(move2 * 0.9); + next.belt.orientation.rotate_z(move2 * 0.4); + next.shorts.orientation.rotate_z(move2 * 1.1); + next.control.orientation.rotate_x(move2 * -5.0); + next.control.position += Vec3::new(move2 * 5.0, move2 * 12.0, move2 * -17.0); + next.control.orientation.rotate_y(move2 * -2.0); + next.control.orientation.rotate_z(move2 * -1.0); + next.torso.orientation.rotate_z(move2_raw * -4.0 * PI); + }, _ => {}, } diff --git a/voxygen/anim/src/character/rapidmelee.rs b/voxygen/anim/src/character/rapidmelee.rs index f88e241d54..c1ca9e7dd4 100644 --- a/voxygen/anim/src/character/rapidmelee.rs +++ b/voxygen/anim/src/character/rapidmelee.rs @@ -34,10 +34,12 @@ impl Animation for RapidMeleeAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_z(0.0); - next.main_weapon_trail = true; next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.orientation = Quaternion::rotation_z(0.0); - next.off_weapon_trail = true; + if matches!(stage_section, Some(StageSection::Action)) { + next.main_weapon_trail = true; + next.off_weapon_trail = true; + } match ability_id { Some( diff --git a/voxygen/anim/src/character/ripostemelee.rs b/voxygen/anim/src/character/ripostemelee.rs index 327aba27ae..cb9d96dc97 100644 --- a/voxygen/anim/src/character/ripostemelee.rs +++ b/voxygen/anim/src/character/ripostemelee.rs @@ -25,10 +25,12 @@ impl Animation for RiposteMeleeAnimation { next.main.position = Vec3::new(0.0, 0.0, 0.0); next.main.orientation = Quaternion::rotation_z(0.0); - next.main_weapon_trail = true; next.second.position = Vec3::new(0.0, 0.0, 0.0); next.second.orientation = Quaternion::rotation_z(0.0); - next.off_weapon_trail = true; + if matches!(stage_section, Some(StageSection::Action)) { + next.main_weapon_trail = true; + next.off_weapon_trail = true; + } match ability_id { Some("common.abilities.sword.defensive_riposte") => { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c2156b3072..6831d99058 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3047,6 +3047,7 @@ impl Hud { let bodies = ecs.read_storage::(); let poises = ecs.read_storage::(); let combos = ecs.read_storage::(); + let combo = combos.get(entity); let time = ecs.read_resource::