diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index a11122f77a..dd56229a4f 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -232,7 +232,7 @@ ((), (Hammer(Intercept), "common.abilities.hammer.intercept")), ], ), - // Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"), + Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"), // Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"), // Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"), // Simple(Hammer(Rampart), "common.abilities.hammer.rampart"), diff --git a/assets/common/abilities/hammer/pile_driver.ron b/assets/common/abilities/hammer/pile_driver.ron new file mode 100644 index 0000000000..8ce019e007 --- /dev/null +++ b/assets/common/abilities/hammer/pile_driver.ron @@ -0,0 +1,24 @@ +BasicMelee( + energy_cost: 20, + buildup_duration: 0.4, + swing_duration: 0.1, + hit_timing: 0.9, + recover_duration: 0.2, + melee_constructor: ( + kind: Bash( + damage: 25, + poise: 30, + knockback: 0, + energy_regen: 0, + ), + range: 3.0, + angle: 15.0, + damage_effect: Some(Buff(( + kind: Rooted, + dur_secs: 4.0, + strength: Value(1.0), + chance: 1.0, + ))), + ), + ori_modifier: 0.6, +) \ No newline at end of file diff --git a/assets/voxygen/element/de_buffs/debuff_rooted.png b/assets/voxygen/element/de_buffs/debuff_rooted.png new file mode 100644 index 0000000000..bcdb4edc24 --- /dev/null +++ b/assets/voxygen/element/de_buffs/debuff_rooted.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6040e18d377f36aeb3343424c674e11e269016960efc3f3ee0c5f82e909b63c4 +size 1017 diff --git a/assets/voxygen/element/skills/hammer/pile_driver.png b/assets/voxygen/element/skills/hammer/pile_driver.png new file mode 100644 index 0000000000..b67f605b13 --- /dev/null +++ b/assets/voxygen/element/skills/hammer/pile_driver.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb6a5109c93d5cf839db709b371fef45fa1f9930cc478fc240aa1a62ffbf45fe +size 1064 diff --git a/assets/voxygen/i18n/en/buff.ftl b/assets/voxygen/i18n/en/buff.ftl index 96c83f48fe..815e05913c 100644 --- a/assets/voxygen/i18n/en/buff.ftl +++ b/assets/voxygen/i18n/en/buff.ftl @@ -124,6 +124,9 @@ buff-heatstroke = Heatstroke ## Scornful Taunt buff-scornfultaunt = Scornful Taunt .desc = You scornfully taunt your enemies, granting you bolstered fortitude and stamina. However, your death will bolster your killer. +## Rooted +buff-rooted = Rooted + .desc = You are stuck in place and cannot move. ## Util buff-text-over_seconds = over { $dur_secs } seconds buff-text-for_seconds = for { $dur_secs } seconds diff --git a/assets/voxygen/i18n/en/hud/ability.ftl b/assets/voxygen/i18n/en/hud/ability.ftl index d46c4d636c..e94a379286 100644 --- a/assets/voxygen/i18n/en/hud/ability.ftl +++ b/assets/voxygen/i18n/en/hud/ability.ftl @@ -413,4 +413,6 @@ common-abilities-hammer-spine_cracker = Spine Cracker common-abilities-hammer-breach = Breach .desc = Breach through your enemy's attempt at defense with a heavy strike from your hammer. - +common-abilities-hammer-pile_driver = Pile Driver + .desc = + Strike your enemy into the ground, stopping their movement until they free their legs. diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 014694f6e9..f3fc520ece 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -190,6 +190,7 @@ lazy_static! { BuffKind::Berserk => "berserk", BuffKind::Heatstroke => "heatstroke", BuffKind::ScornfulTaunt => "scornful_taunt", + BuffKind::Rooted => "rooted", }; let mut buff_parser = HashMap::new(); for kind in BuffKind::iter() { diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 04489a2050..e42fd79a2c 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -177,6 +177,14 @@ pub enum BuffKind { /// 33.3% and energy reward reduced by 200%. Energy reward can't be /// reduced by more than 200%, to a minimum value of -100%. Heatstroke, + /// Reduces movement speed to 0. + /// Strength increases the relative mass of the creature that can be + /// targeted. A strength of 1.0 means that a creature of the same mass gets + /// rooted for the full duration. A strength of 2.0 means a creature of + /// twice the mass gets rooted for the full duration. If the target's mass + /// is higher than the strength allows for, duration gets reduced using a + /// mutiplier from the ratio of masses. + Rooted, // Complex, non-obvious buffs /// Changed into another body. Polymorphed, @@ -239,7 +247,8 @@ impl BuffKind { | BuffKind::Poisoned | BuffKind::Parried | BuffKind::PotionSickness - | BuffKind::Heatstroke => BuffDescriptor::SimpleNegative, + | BuffKind::Heatstroke + | BuffKind::Rooted => BuffDescriptor::SimpleNegative, BuffKind::Polymorphed => BuffDescriptor::Complex, } } @@ -479,6 +488,7 @@ impl BuffKind { duration: data.duration, }), ], + BuffKind::Rooted => vec![BuffEffect::MovementSpeed(0.0)], } } @@ -493,6 +503,21 @@ impl BuffKind { } cat_ids } + + fn modify_data(&self, mut data: BuffData) -> BuffData { + // TODO: Remove clippy allow after another buff needs this + #[allow(clippy::single_match)] + match self { + BuffKind::Rooted => { + let source_mass = 50.0; + let dest_mass = 50.0_f64; + let ratio = (source_mass / dest_mass).min(1.0); + data.duration = data.duration.map(|dur| Secs(dur.0 * ratio)); + }, + _ => {}, + } + data + } } // Struct used to store data relevant to a buff @@ -687,6 +712,7 @@ impl Buff { time: Time, stats: Option<&Stats>, ) -> Self { + let data = kind.modify_data(data); let effects = kind.effects(&data, stats); let cat_ids = kind.extend_cat_ids(cat_ids); let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0)); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 55d03eb3df..c1c6dffa84 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -4551,7 +4551,8 @@ fn build_buff( | BuffKind::Parried | BuffKind::PotionSickness | BuffKind::Heatstroke - | BuffKind::ScornfulTaunt => { + | BuffKind::ScornfulTaunt + | BuffKind::Rooted => { if buff_kind.is_simple() { unreachable!("is_simple() above") } else { diff --git a/voxygen/anim/src/character/alpha.rs b/voxygen/anim/src/character/alpha.rs index 4379bea5ab..f0ec60c302 100644 --- a/voxygen/anim/src/character/alpha.rs +++ b/voxygen/anim/src/character/alpha.rs @@ -154,6 +154,31 @@ impl Animation for AlphaAnimation { next.control.orientation.rotate_x(move2 * -4.5); next.control.position += Vec3::new(0.0, 0.0, -20.0) * move2; }, + Some("common.abilities.hammer.pile_driver") => { + hammer_start(&mut next, s_a); + let (move1, move2, 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 shake = (move1 * 15.0).sin(); + let move1 = (move1 * 2.0).min(1.0) * pullback; + let move2 = move2 * pullback; + + twist_back(&mut next, move1, 0.9, 0.3, 0.1, 0.5); + next.control.orientation.rotate_x(move1 * 2.4); + next.control.position += Vec3::new(-14.0, 0.0, 14.0) * move1; + next.control.orientation.rotate_z(move1 * 1.8); + + next.control.orientation.rotate_x(shake * 0.15); + + twist_forward(&mut next, move2, 1.6, 0.5, 0.2, 0.9); + next.control.orientation.rotate_x(move2 * -4.0); + next.control.orientation.rotate_z(move2 * 0.4); + next.control.position += Vec3::new(0.0, 0.0, -12.0) * move2; + }, _ => { let (move1, move2, _move3, move2h) = match stage_section { Some(StageSection::Buildup) => (anim_time.powf(0.25), 0.0, 0.0, 0.0), diff --git a/voxygen/i18n-helpers/src/lib.rs b/voxygen/i18n-helpers/src/lib.rs index 846af23e9a..e5e6e5bc35 100644 --- a/voxygen/i18n-helpers/src/lib.rs +++ b/voxygen/i18n-helpers/src/lib.rs @@ -406,7 +406,8 @@ fn get_buff_ident(buff: BuffKind) -> &'static str { | BuffKind::Parried | BuffKind::PotionSickness | BuffKind::Polymorphed - | BuffKind::Heatstroke => { + | BuffKind::Heatstroke + | BuffKind::Rooted => { tracing::error!("Player was killed by a debuff that doesn't do damage!"); "mysterious" }, diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 916fd79d1c..5971406f21 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -325,6 +325,7 @@ image_ids! { hammer_retaliate: "voxygen.element.skills.hammer.retaliate", hammer_spine_cracker: "voxygen.element.skills.hammer.spine_cracker", hammer_breach: "voxygen.element.skills.hammer.breach", + hammer_pile_driver: "voxygen.element.skills.hammer.pile_driver", // Skilltree Icons health_plus_skill: "voxygen.element.skills.skilltree.health_plus", energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus", @@ -814,6 +815,7 @@ image_ids! { debuff_potionsickness_0: "voxygen.element.de_buffs.debuff_potionsickness_0", debuff_polymorphed: "voxygen.element.de_buffs.debuff_polymorphed", debuff_heatstroke_0: "voxygen.element.de_buffs.debuff_heatstroke_0", + debuff_rooted: "voxygen.element.de_buffs.debuff_rooted", // Animation Frames // Buff Frame diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c1e6610059..a9b5d16950 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -5275,6 +5275,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::PotionSickness => imgs.debuff_potionsickness_0, BuffKind::Polymorphed => imgs.debuff_polymorphed, BuffKind::Heatstroke => imgs.debuff_heatstroke_0, + BuffKind::Rooted => imgs.debuff_rooted, } } diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 755ef5ba05..12960ffa71 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -210,6 +210,7 @@ fn buff_key(buff: BuffKind) -> &'static str { BuffKind::Parried => "buff-parried", BuffKind::PotionSickness => "buff-potionsickness", BuffKind::Heatstroke => "buff-heatstroke", + BuffKind::Rooted => "buff-rooted", // Neutral BuffKind::Polymorphed => "buff-polymorphed", } @@ -320,7 +321,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec { | BuffKind::Bloodfeast | BuffKind::Berserk | BuffKind::Heatstroke - | BuffKind::ScornfulTaunt => Cow::Borrowed(""), + | BuffKind::ScornfulTaunt + | BuffKind::Rooted => Cow::Borrowed(""), }; write!(&mut description, "{}", buff_desc).unwrap(); @@ -371,7 +373,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec { | BuffKind::Bloodfeast | BuffKind::Berserk | BuffKind::Heatstroke - | BuffKind::ScornfulTaunt => Cow::Borrowed(""), + | BuffKind::ScornfulTaunt + | BuffKind::Rooted => Cow::Borrowed(""), } } else if let BuffKind::Saturation | BuffKind::Regeneration @@ -634,6 +637,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id { "common.abilities.hammer.retaliate" => imgs.hammer_retaliate, "common.abilities.hammer.spine_cracker" => imgs.hammer_spine_cracker, "common.abilities.hammer.breach" => imgs.hammer_breach, + "common.abilities.hammer.pile_driver" => imgs.hammer_pile_driver, // Bow "common.abilities.bow.charged" => imgs.bow_m1, "common.abilities.bow.repeater" => imgs.bow_m2,