Scornful swipe

This commit is contained in:
Sam 2024-02-16 23:40:42 -05:00
parent af755197bb
commit cc70685f7a
22 changed files with 176 additions and 19 deletions

View File

@ -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]

View File

@ -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"),

View File

@ -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))),
),
)

BIN
assets/voxygen/element/de_buffs/buff_scornfultaunt.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/skills/hammer/scornful_swipe.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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.

View File

@ -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() {

View File

@ -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<Secs>,
},
}
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub enum DamageContributor {
Solo(Uid),

View File

@ -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<Secs>,
},
}
impl Default for Stance {

View File

@ -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.

View File

@ -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<DamagedEffect>,
/// This creates effects when the entity is killed
pub effects_on_death: Vec<DeathEffect>,
}
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(),
}
}

View File

@ -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 {

View File

@ -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()),
};
}

View File

@ -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 {

View File

@ -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<BuffEvent>>,
}
/// 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)

View File

@ -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),

View File

@ -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"
},

View File

@ -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",

View File

@ -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,

View File

@ -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<String> {
| 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<String> {
| 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,