diff --git a/.cargo/config b/.cargo/config index e78c87c91f..89c3d55c3a 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,6 +1,6 @@ [target.x86_64-unknown-linux-gnu] rustflags = [ - "-C", "link-arg=-fuse-ld=gold", + "-C", "link-arg=-fuse-ld=mold", ] [target.x86_64-pc-windows-gnu] diff --git a/assets/common/abilities/ability_set_manifest.ron b/assets/common/abilities/ability_set_manifest.ron index 772b9826be..5f42e92da1 100644 --- a/assets/common/abilities/ability_set_manifest.ron +++ b/assets/common/abilities/ability_set_manifest.ron @@ -196,7 +196,7 @@ primary: Simple(None, "common.abilities.hammer.solid_smash"), secondary: Simple(None, "common.abilities.hammer.wide_wallop"), abilities: [ - // Simple(Hammer(ScornfulSwipe), "common.abilities.hammer.scornful_swipe"), + Simple(Hammer(ScornfulSwipe), "common.abilities.hammer.scornful_swipe"), // Simple(Hammer(Tremor), "common.abilities.hammer.tremor"), // Simple(Hammer(VigorousBash), "common.abilities.hammer.vigorous_bash"), // Simple(Hammer(Retaliate), "common.abilities.hammer.retaliate"), diff --git a/assets/common/abilities/hammer/scornful_swipe.ron b/assets/common/abilities/hammer/scornful_swipe.ron new file mode 100644 index 0000000000..4a2cdbf19f --- /dev/null +++ b/assets/common/abilities/hammer/scornful_swipe.ron @@ -0,0 +1,21 @@ +BasicMelee( + energy_cost: 0, + buildup_duration: 0.7, + swing_duration: 0.1, + hit_timing: 0.5, + recover_duration: 0.2, + melee_constructor: ( + kind: Bash( + damage: 20, + poise: 10, + knockback: 5, + energy_regen: 0, + ), + range: 3.0, + angle: 15.0, + ), + ori_modifier: 0.6, + meta: ( + init_event: Some(GainBuff(kind: ScornfulTaunt, strength: 0.5, duration: Some(20.0))), + ), +) diff --git a/assets/voxygen/element/de_buffs/buff_scornfultaunt.png b/assets/voxygen/element/de_buffs/buff_scornfultaunt.png new file mode 100644 index 0000000000..e8c0683684 --- /dev/null +++ b/assets/voxygen/element/de_buffs/buff_scornfultaunt.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5ab398c9aefaf8cae9041648a72ddb368a18ebc725566a9df5c68c6f6ecf4c9 +size 997 diff --git a/assets/voxygen/element/skills/hammer/scornful_swipe.png b/assets/voxygen/element/skills/hammer/scornful_swipe.png new file mode 100644 index 0000000000..3ba7add30c --- /dev/null +++ b/assets/voxygen/element/skills/hammer/scornful_swipe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:000cd05af65d01e4e866cb1a28cc3736248cb9aac977a5b139abb76589daff63 +size 1007 diff --git a/assets/voxygen/element/skills/hammer/wide_wallop.png b/assets/voxygen/element/skills/hammer/wide_wallop.png index d28435adba..5a0d9ec4fd 100644 --- a/assets/voxygen/element/skills/hammer/wide_wallop.png +++ b/assets/voxygen/element/skills/hammer/wide_wallop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:155b65dc04dd9c0803b9e3a5512cadde3cd94ed2355cf78839b82de723b823cd -size 576 +oid sha256:8419a506f439909517ec1ac8f99fc5d4ba62f6c8fdbcfde382af2b6afb3f8042 +size 1265 diff --git a/assets/voxygen/i18n/en/buff.ftl b/assets/voxygen/i18n/en/buff.ftl index 9198f41b1c..96c83f48fe 100644 --- a/assets/voxygen/i18n/en/buff.ftl +++ b/assets/voxygen/i18n/en/buff.ftl @@ -121,8 +121,9 @@ buff-berserk = Berserk ## Heatstroke buff-heatstroke = Heatstroke .desc = You were exposed to heat and now suffer from heatstroke. Your energy reward and movement speed are cut down. Chill. - - +## 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. ## 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 d2733c60f6..25e44c77ee 100644 --- a/assets/voxygen/i18n/en/hud/ability.ftl +++ b/assets/voxygen/i18n/en/hud/ability.ftl @@ -383,3 +383,6 @@ common-abilities-hammer-solid_smash = Solid Smash common-abilities-hammer-wide_wallop = Wide Wallop .desc = Pull back and send them flying +common-abilities-hammer-scornful_swipe = Scornful Swipe + .desc = + Bolster your fortitude and stamina by taunting your enemies before striking at them. If you fall to an enemy they become empowered. diff --git a/common/src/cmd.rs b/common/src/cmd.rs index ea56970e7b..014694f6e9 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -188,7 +188,8 @@ lazy_static! { BuffKind::Defiance => "defiance", BuffKind::Bloodfeast => "bloodfeast", BuffKind::Berserk => "berserk", - BuffKind::Heatstroke => "heatstroke" + BuffKind::Heatstroke => "heatstroke", + BuffKind::ScornfulTaunt => "scornful_taunt", }; let mut buff_parser = HashMap::new(); for kind in BuffKind::iter() { diff --git a/common/src/combat.rs b/common/src/combat.rs index 6a1f28e638..c98743e296 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -1013,6 +1013,16 @@ pub enum DamagedEffect { Combo(i32), } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum DeathEffect { + /// Adds buff to the attacker that killed this entity + AttackerBuff { + kind: BuffKind, + strength: f32, + duration: Option, + }, +} + #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] pub enum DamageContributor { Solo(Uid), diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index d6d2d336c3..92ec1b96b3 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2907,7 +2907,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct AbilityMeta { #[serde(default)] @@ -2987,9 +2987,14 @@ impl Stance { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum AbilityInitEvent { EnterStance(Stance), + GainBuff { + kind: buff::BuffKind, + strength: f32, + duration: Option, + }, } impl Default for Stance { diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 876ac01cb4..04489a2050 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -2,7 +2,7 @@ use crate::{ combat::{ AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, CombatRequirement, - DamagedEffect, + DamagedEffect, DeathEffect, }, comp::{aura::AuraKey, Stats}, resources::{Secs, Time}, @@ -127,6 +127,12 @@ pub enum BuffKind { /// non-linearly with strength, 0.5 is a 12.5% increase, 1.0 is a 16.7% /// increase decrease. Berserk, + /// Increases poise resistance and energy reward. However if killed, buffs + /// killer with Reckless buff. Poise resistance scales non-linearly with + /// strength, 0.5 is 50% and 1.0 is 67%. Energy reward scales linearly with + /// strength, 0.5 is +50% and 1.0 is +100% strength. Reckless buff reward + /// strength is equal to scornful taunt buff strength. + ScornfulTaunt, // Debuffs /// Does damage to a creature over time. /// Strength should be the DPS of the debuff. @@ -221,7 +227,8 @@ impl BuffKind { | BuffKind::Sunderer | BuffKind::Defiance | BuffKind::Bloodfeast - | BuffKind::Berserk => BuffDescriptor::SimplePositive, + | BuffKind::Berserk + | BuffKind::ScornfulTaunt => BuffDescriptor::SimplePositive, BuffKind::Bleeding | BuffKind::Cursed | BuffKind::Burning @@ -463,6 +470,15 @@ impl BuffKind { BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength) * 0.5), BuffEffect::EnergyReward((1.0 - nn_scaling(data.strength) * 3.0).max(-1.0)), ], + BuffKind::ScornfulTaunt => vec![ + BuffEffect::PoiseReduction(nn_scaling(data.strength)), + BuffEffect::EnergyReward(1.0 + data.strength), + BuffEffect::DeathEffect(DeathEffect::AttackerBuff { + kind: BuffKind::Reckless, + strength: data.strength, + duration: data.duration, + }), + ], } } @@ -612,6 +628,8 @@ pub enum BuffEffect { EnergyReward(f32), /// Add an effect to the entity when damaged by an attack DamagedEffect(DamagedEffect), + /// Add an effect to the entity when killed + DeathEffect(DeathEffect), } /// Actual de/buff. diff --git a/common/src/comp/stats.rs b/common/src/comp/stats.rs index 7a61319d32..da9c105b7c 100644 --- a/common/src/comp/stats.rs +++ b/common/src/comp/stats.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use std::{error::Error, fmt}; -use crate::combat::{AttackEffect, DamagedEffect}; +use crate::combat::{AttackEffect, DamagedEffect, DeathEffect}; use super::Body; @@ -75,6 +75,8 @@ pub struct Stats { pub energy_reward_modifier: f32, /// This creates effects when the entity is damaged pub effects_on_damaged: Vec, + /// This creates effects when the entity is killed + pub effects_on_death: Vec, } impl Stats { @@ -100,6 +102,7 @@ impl Stats { mitigations_penetration: 0.0, energy_reward_modifier: 1.0, effects_on_damaged: Vec::new(), + effects_on_death: Vec::new(), } } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index dfa96f0b41..5287bce7d8 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -3,7 +3,7 @@ use crate::{ comp::{ ability::{AbilityInitEvent, AbilityMeta, Capability, SpecifiedAbility, Stance}, arthropod, biped_large, biped_small, bird_medium, - buff::{BuffCategory, BuffChange}, + buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource}, character_state::OutputEvents, controller::InventoryManip, golem, @@ -1321,6 +1321,23 @@ fn handle_ability( stance, }); }, + AbilityInitEvent::GainBuff { + kind, + strength, + duration, + } => { + output_events.emit_server(BuffEvent { + entity: data.entity, + buff_change: BuffChange::Add(Buff::new( + kind, + BuffData::new(strength, duration), + vec![BuffCategory::SelfBuff], + BuffSource::Character { by: *data.uid }, + *data.time, + Some(data.stats), + )), + }); + }, } } if let CharacterState::Roll(roll) = &mut update.character { diff --git a/common/systems/src/buff.rs b/common/systems/src/buff.rs index 5f4dd50035..2a0ef88ca5 100644 --- a/common/systems/src/buff.rs +++ b/common/systems/src/buff.rs @@ -792,5 +792,6 @@ fn execute_effect( stat.energy_reward_modifier *= er; }, BuffEffect::DamagedEffect(effect) => stat.effects_on_damaged.push(effect.clone()), + BuffEffect::DeathEffect(effect) => stat.effects_on_death.push(effect.clone()), }; } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index ac8a887813..55d03eb3df 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -4550,7 +4550,8 @@ fn build_buff( | BuffKind::Poisoned | BuffKind::Parried | BuffKind::PotionSickness - | BuffKind::Heatstroke => { + | BuffKind::Heatstroke + | BuffKind::ScornfulTaunt => { if buff_kind.is_simple() { unreachable!("is_simple() above") } else { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 3e39ca39e7..ccdf386025 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -18,8 +18,7 @@ use crate::{ #[cfg(feature = "worldgen")] use common::rtsim::{Actor, RtSimEntity}; use common::{ - combat, - combat::{AttackSource, DamageContributor}, + combat::{self, AttackSource, DamageContributor, DeathEffect}, comp::{ self, aura::{self, EnteredAuras}, @@ -332,6 +331,7 @@ pub struct DestroyEventData<'a> { rtsim_entities: ReadStorage<'a, RtSimEntity>, #[cfg(feature = "worldgen")] presences: ReadStorage<'a, Presence>, + buff_events: Read<'a, EventBus>, } /// Handle an entity dying. If it is a player, it will send a message to all @@ -346,6 +346,7 @@ impl ServerEvent for DestroyEvent { let mut create_item_drop = data.create_item_drop.emitter(); let mut delete_emitter = data.delete_event.emitter(); let mut outcomes_emitter = data.outcomes.emitter(); + let mut buff_emitter = data.buff_events.emitter(); for ev in events { // TODO: Investigate duplicate `Destroy` events (but don't remove this). // If the entity was already deleted, it can't be destroyed again. @@ -383,6 +384,40 @@ impl ServerEvent for DestroyEvent { outcomes_emitter.emit(Outcome::Death { pos: pos.0 }); } + // Handle any effects on death + if let Some(killed_stats) = data.stats.get(ev.entity) { + let attacker_entity = ev.cause.by.and_then(|x| data.id_maps.uid_entity(x.uid())); + let killed_uid = data.uids.get(ev.entity); + let attacker_stats = attacker_entity.and_then(|e| data.stats.get(e)); + for effect in killed_stats.effects_on_death.iter() { + match effect { + DeathEffect::AttackerBuff { + kind, + strength, + duration, + } => { + if let Some(attacker) = attacker_entity { + buff_emitter.emit(BuffEvent { + entity: attacker, + buff_change: buff::BuffChange::Add(buff::Buff::new( + *kind, + buff::BuffData::new(*strength, *duration), + vec![], + if let Some(uid) = killed_uid { + BuffSource::Character { by: *uid } + } else { + BuffSource::World + }, + *data.time, + attacker_stats, + )), + }); + } + }, + } + } + } + // Chat message // If it was a player that died if let Some((uid, _player)) = (&data.uids, &data.players) diff --git a/voxygen/anim/src/character/alpha.rs b/voxygen/anim/src/character/alpha.rs index b28d7d6ee1..ddc50830c5 100644 --- a/voxygen/anim/src/character/alpha.rs +++ b/voxygen/anim/src/character/alpha.rs @@ -62,6 +62,33 @@ impl Animation for AlphaAnimation { next.control.orientation.rotate_x(move2 * -1.9); next.control.orientation.rotate_z(move2 * 0.6); }, + Some("common.abilities.hammer.scornful_swipe") => { + 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 move1 = move1 * pullback; + let move2 = move2 * pullback; + let move1_pre = move1.min(0.5) * 2.0; + let move1_shake = ((move1.max(0.3) - 0.3) * 15.0).sin(); + let move1_late = move1.powi(4); + + next.control.orientation.rotate_x(move1_pre * 2.3); + next.control.position += Vec3::new(0.0, 2.0, 16.0) * move1_pre; + next.control.position += Vec3::new(0.0, 0.0, 4.0) * move1_shake; + next.control.orientation.rotate_y(move1_late * 1.6); + next.control.position += Vec3::new(-8.0, 0.0, -8.0) * move1_late; + twist_back(&mut next, move1_late, 1.0, 0.4, 0.2, 0.7); + next.control.orientation.rotate_z(move1_late * 1.2); + + twist_forward(&mut next, move2, 1.9, 0.9, 0.6, 1.1); + next.control.orientation.rotate_y(move2 * -1.7); + next.control.orientation.rotate_z(move2 * -2.7); + }, _ => { 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 1f60cc7094..846af23e9a 100644 --- a/voxygen/i18n-helpers/src/lib.rs +++ b/voxygen/i18n-helpers/src/lib.rs @@ -395,7 +395,8 @@ fn get_buff_ident(buff: BuffKind) -> &'static str { | BuffKind::Sunderer | BuffKind::Defiance | BuffKind::Bloodfeast - | BuffKind::Berserk => { + | BuffKind::Berserk + | BuffKind::ScornfulTaunt => { tracing::error!("Player was killed by a positive buff!"); "mysterious" }, diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index ef2ed4ac51..04c572512f 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -317,6 +317,7 @@ image_ids! { // Hammer hammer_solid_smash: "voxygen.element.skills.hammer.solid_smash", hammer_wide_wallop: "voxygen.element.skills.hammer.wide_wallop", + hammer_scornful_swipe: "voxygen.element.skills.hammer.scornful_swipe", // Skilltree Icons health_plus_skill: "voxygen.element.skills.skilltree.health_plus", energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus", @@ -791,6 +792,7 @@ image_ids! { buff_lifesteal: "voxygen.element.de_buffs.buff_lifesteal", buff_flame: "voxygen.element.de_buffs.buff_flame", buff_frigid: "voxygen.element.de_buffs.buff_frigid", + buff_scornfultaunt: "voxygen.element.de_buffs.buff_scornfultaunt", // Debuffs debuff_skull_0: "voxygen.element.de_buffs.debuff_skull_0", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 0fe59514b1..c1e6610059 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -5261,6 +5261,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::Defiance => imgs.buff_defiance, BuffKind::Bloodfeast => imgs.buff_plus_0, BuffKind::Berserk => imgs.buff_reckless, + BuffKind::ScornfulTaunt => imgs.buff_scornfultaunt, // Debuffs BuffKind::Bleeding => imgs.debuff_bleed_0, BuffKind::Cursed => imgs.debuff_skull_0, diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index 322ba4b253..548aa6a197 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -197,6 +197,7 @@ fn buff_key(buff: BuffKind) -> &'static str { BuffKind::Defiance => "buff-defiance", BuffKind::Bloodfeast => "buff-bloodfeast", BuffKind::Berserk => "buff-berserk", + BuffKind::ScornfulTaunt => "buff-scornfultaunt", // Debuffs BuffKind::Bleeding => "buff-bleed", BuffKind::Cursed => "buff-cursed", @@ -318,7 +319,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec { | BuffKind::Defiance | BuffKind::Bloodfeast | BuffKind::Berserk - | BuffKind::Heatstroke => Cow::Borrowed(""), + | BuffKind::Heatstroke + | BuffKind::ScornfulTaunt => Cow::Borrowed(""), }; write!(&mut description, "{}", buff_desc).unwrap(); @@ -368,7 +370,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec { | BuffKind::Defiance | BuffKind::Bloodfeast | BuffKind::Berserk - | BuffKind::Heatstroke => Cow::Borrowed(""), + | BuffKind::Heatstroke + | BuffKind::ScornfulTaunt => Cow::Borrowed(""), } } else if let BuffKind::Saturation | BuffKind::Regeneration @@ -621,6 +624,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id { // Hammer "common.abilities.hammer.solid_smash" => imgs.hammer_solid_smash, "common.abilities.hammer.wide_wallop" => imgs.hammer_wide_wallop, + "common.abilities.hammer.scornful_swipe" => imgs.hammer_scornful_swipe, // Bow "common.abilities.bow.charged" => imgs.bow_m1, "common.abilities.bow.repeater" => imgs.bow_m2,