From 2e97fad3d811063b35a52fb005f1055ab4e5b097 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 24 Apr 2021 15:01:36 -0400 Subject: [PATCH] Added frenzy ability to minotaur. Added self-buff character state. Added frenzied buff kind. Added better comments on each buff kind. --- .../abilities/custom/minotaur/frenzy.ron | 24 +--- assets/voxygen/i18n/en/buff.ron | 2 + common/src/comp/ability.rs | 65 ++++++++- common/src/comp/buff.rs | 31 +++++ common/src/comp/character_state.rs | 4 + common/src/states/mod.rs | 1 + common/src/states/self_buff.rs | 131 ++++++++++++++++++ common/systems/src/character_behavior.rs | 2 + common/systems/src/stats.rs | 3 +- voxygen/src/hud/mod.rs | 4 + voxygen/src/hud/util.rs | 6 +- 11 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 common/src/states/self_buff.rs diff --git a/assets/common/abilities/custom/minotaur/frenzy.ron b/assets/common/abilities/custom/minotaur/frenzy.ron index 6563de650a..4a014d42bb 100644 --- a/assets/common/abilities/custom/minotaur/frenzy.ron +++ b/assets/common/abilities/custom/minotaur/frenzy.ron @@ -1,19 +1,9 @@ -DashMelee( +SelfBuff( + buildup_duration: 0.5, + cast_duration: 0.25, + recover_duration: 0.25, + buff_kind: Frenzied, + buff_strength: 0.5, + buff_duration: None, energy_cost: 0, - base_damage: 500, - scaled_damage: 2000, - base_poise_damage: 25, - scaled_poise_damage: 100, - base_knockback: 10.0, - scaled_knockback: 30.0, - range: 7.5, - angle: 90.0, - energy_drain: 0, - forward_speed: 5.0, - buildup_duration: 0.4, - charge_duration: 4.0, - swing_duration: 0.1, - recover_duration: 0.5, - infinite_charge: false, - is_interruptible: false, ) \ No newline at end of file diff --git a/assets/voxygen/i18n/en/buff.ron b/assets/voxygen/i18n/en/buff.ron index cbd6d30f19..6f6047386e 100644 --- a/assets/voxygen/i18n/en/buff.ron +++ b/assets/voxygen/i18n/en/buff.ron @@ -19,6 +19,8 @@ "buff.desc.invulnerability": "You cannot be damaged by any attack.", "buff.title.protectingward": "Protecting Ward", "buff.desc.protectingward": "You are protected, somewhat, from attacks.", + "buff.title.frenzied": "Frenzied", + "buff.desc.frenzied": "You are imbued with nunnatural speed and can ignore minor injuries." // Debuffs "buff.title.bleed": "Bleeding", "buff.desc.bleed": "Inflicts regular damage.", diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 5ec986c363..96b2f9d3fa 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -2,8 +2,8 @@ use crate::{ assets::{self, Asset}, combat::{self, CombatEffect, Knockback}, comp::{ - aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills, - Body, CharacterState, EnergySource, LightEmitter, StateUpdate, + aura, beam, buff, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, + skills, Body, CharacterState, EnergySource, LightEmitter, StateUpdate, }, states::{ behavior::JoinData, @@ -30,6 +30,7 @@ pub enum CharacterAbilityType { BasicBeam, RepeaterRanged, BasicAura, + SelfBuff, } impl From<&CharacterState> for CharacterAbilityType { @@ -49,6 +50,7 @@ impl From<&CharacterState> for CharacterAbilityType { CharacterState::BasicBeam(_) => Self::BasicBeam, CharacterState::RepeaterRanged(_) => Self::RepeaterRanged, CharacterState::BasicAura(_) => Self::BasicAura, + CharacterState::SelfBuff(_) => Self::SelfBuff, _ => Self::BasicMelee, } } @@ -269,6 +271,15 @@ pub enum CharacterAbility { summon_amount: u32, summon_info: basic_summon::SummonInfo, }, + SelfBuff { + buildup_duration: f32, + cast_duration: f32, + recover_duration: f32, + buff_kind: buff::BuffKind, + buff_strength: f32, + buff_duration: Option, + energy_cost: f32, + }, } impl Default for CharacterAbility { @@ -315,7 +326,8 @@ impl CharacterAbility { | CharacterAbility::ChargedMelee { energy_cost, .. } | CharacterAbility::Shockwave { energy_cost, .. } | CharacterAbility::BasicAura { energy_cost, .. } - | CharacterAbility::BasicBlock { energy_cost, .. } => update + | CharacterAbility::BasicBlock { energy_cost, .. } + | CharacterAbility::SelfBuff { energy_cost, .. } => update .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), @@ -336,7 +348,11 @@ impl CharacterAbility { .is_ok() }, CharacterAbility::HealingBeam { .. } => data.combo.counter() > 0, - _ => true, + CharacterAbility::ComboMelee { .. } + | CharacterAbility::Boost { .. } + | CharacterAbility::BasicBeam { .. } + | CharacterAbility::Blink { .. } + | CharacterAbility::BasicSummon { .. } => true, } } @@ -588,6 +604,18 @@ impl CharacterAbility { *cast_duration /= speed; *recover_duration /= speed; }, + SelfBuff { + ref mut buff_strength, + ref mut buildup_duration, + ref mut cast_duration, + ref mut recover_duration, + .. + } => { + *buff_strength *= power; + *buildup_duration /= speed; + *cast_duration /= speed; + *recover_duration /= speed; + }, } self } @@ -607,7 +635,8 @@ impl CharacterAbility { | Shockwave { energy_cost, .. } | HealingBeam { energy_cost, .. } | BasicAura { energy_cost, .. } - | BasicBlock { energy_cost, .. } => *energy_cost as u32, + | BasicBlock { energy_cost, .. } + | SelfBuff { energy_cost, .. } => *energy_cost as u32, BasicBeam { energy_drain, .. } => { if *energy_drain > f32::EPSILON { 1 @@ -615,7 +644,10 @@ impl CharacterAbility { 0 } }, - Boost { .. } | ComboMelee { .. } | Blink { .. } | BasicSummon { .. } => 0, + Boost { .. } + | ComboMelee { .. } + | Blink { .. } + | BasicSummon { .. } => 0, } } @@ -1661,6 +1693,27 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { timer: Duration::default(), stage_section: StageSection::Buildup, }), + CharacterAbility::SelfBuff { + buildup_duration, + cast_duration, + recover_duration, + buff_kind, + buff_strength, + buff_duration, + energy_cost: _, + } => CharacterState::SelfBuff(self_buff::Data { + static_data: self_buff::StaticData { + buildup_duration: Duration::from_secs_f32(*buildup_duration), + cast_duration: Duration::from_secs_f32(*cast_duration), + recover_duration: Duration::from_secs_f32(*recover_duration), + buff_kind: *buff_kind, + buff_strength: *buff_strength, + buff_duration: buff_duration.map(Duration::from_secs_f32), + ability_info, + }, + timer: Duration::default(), + stage_section: StageSection::Buildup, + }), } } } diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index f93c419855..131a6af390 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -15,29 +15,48 @@ use std::{cmp::Ordering, time::Duration}; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)] pub enum BuffKind { /// Does damage to a creature over time + /// Strength should be 10x the DPS of the debuff Burning, /// Restores health/time for some period + /// Strength should be 10x the healing per second Regeneration, /// Restores health/time for some period for consumables + /// Strength should be 10x the healing per second Saturation, /// Lowers health over time for some duration + /// Strength should be 10x the DPS of the debuff Bleeding, /// Lower a creature's max health over time + /// Strength only affects the target max health, 0.5 targets 50% of base + /// max, 1.0 targets 100% of base max Cursed, /// Applied when drinking a potion + /// Strength should be 10x the healing per second Potion, /// Applied when sitting at a campfire + /// Strength is fraction of health resotred per second CampfireHeal, /// Raises maximum stamina + /// Strength should be 10x the effect to max energy IncreaseMaxEnergy, /// Raises maximum health + /// Strength should be 10x the effect to max health IncreaseMaxHealth, /// Makes you immune to attacks + /// Strength does not affect this buff Invulnerability, /// Reduces incoming damage + /// Strength scales the damage reduction non-linearly. 0.5 provides 50% DR, + /// 1.0 provides 67% DR ProtectingWard, /// Reduces movement speed and causes bleeding damage + /// Strength scales the movement speed debuff non-linearly. 0.5 is 50% + /// speed, 1.0 is 33% speed. Bleeding is at 10x the value of the strength. Crippled, + /// Increases movement speed and gives health regeneration + /// Strength scales the movement speed linearly. 0.5 is 150% speed, 1.0 is + /// 200% speed. Provides regeneration at 10x the value of the strength + Frenzied, } #[cfg(not(target_arch = "wasm32"))] @@ -57,6 +76,7 @@ impl BuffKind { BuffKind::ProtectingWard => true, BuffKind::Burning => false, BuffKind::Crippled => false, + BuffKind::Frenzied => true, } } @@ -265,6 +285,17 @@ impl Buff { ], data.duration, ), + BuffKind::Frenzied => ( + vec![ + BuffEffect::MovementSpeed(1.0 + data.strength), + BuffEffect::HealthChangeOverTime { + rate: data.strength * 100.0, + accumulated: 0.0, + kind: ModifierKind::Additive, + }, + ], + data.duration, + ), }; Buff { kind, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 4b9abe1cb2..d73be47340 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -99,6 +99,8 @@ pub enum CharacterState { Blink(blink::Data), /// Summons creatures that fight for the caster BasicSummon(basic_summon::Data), + /// Inserts a buff on the caster + SelfBuff(self_buff::Data), } impl CharacterState { @@ -120,6 +122,7 @@ impl CharacterState { | CharacterState::BasicBeam(_) | CharacterState::BasicAura(_) | CharacterState::HealingBeam(_) + | CharacterState::SelfBuff(_) ) } @@ -143,6 +146,7 @@ impl CharacterState { | CharacterState::BasicBeam(_) | CharacterState::BasicAura(_) | CharacterState::HealingBeam(_) + | CharacterState::SelfBuff(_) ) } diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 15cb6c72d6..30b86e3766 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -21,6 +21,7 @@ pub mod idle; pub mod leap_melee; pub mod repeater_ranged; pub mod roll; +pub mod self_buff; pub mod shockwave; pub mod sit; pub mod sneak; diff --git a/common/src/states/self_buff.rs b/common/src/states/self_buff.rs new file mode 100644 index 0000000000..85a99e46ab --- /dev/null +++ b/common/src/states/self_buff.rs @@ -0,0 +1,131 @@ +use crate::{ + comp::{ + buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource}, + CharacterState, StateUpdate, + }, + event::ServerEvent, + states::{ + behavior::{CharacterBehavior, JoinData}, + utils::*, + }, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +/// Separated out to condense update portions of character state +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct StaticData { + /// How long until state should create the aura + pub buildup_duration: Duration, + /// How long the state is creating an aura + pub cast_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// What kind of buff is created + pub buff_kind: BuffKind, + /// Strength of the created buff + pub buff_strength: f32, + /// How long buff lasts + pub buff_duration: Option, + /// What key is used to press ability + pub ability_info: AbilityInfo, +} + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Struct containing data that does not change over the course of the + /// character state + pub static_data: StaticData, + /// Timer for each stage + pub timer: Duration, + /// What section the character stage is in + pub stage_section: StageSection, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 0.8); + handle_jump(data, &mut update, 1.0); + + match self.stage_section { + StageSection::Buildup => { + if self.timer < self.static_data.buildup_duration { + // Build up + update.character = CharacterState::SelfBuff(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Creates buff + let buff = Buff::new( + self.static_data.buff_kind, + BuffData { + strength: self.static_data.buff_strength, + duration: self.static_data.buff_duration, + }, + Vec::new(), + BuffSource::Character { by: *data.uid }, + ); + update.server_events.push_front(ServerEvent::Buff { + entity: data.entity, + buff_change: BuffChange::Add(buff), + }); + // Build up + update.character = CharacterState::SelfBuff(Data { + timer: Duration::default(), + stage_section: StageSection::Cast, + ..*self + }); + } + }, + StageSection::Cast => { + if self.timer < self.static_data.cast_duration { + // Cast + update.character = CharacterState::SelfBuff(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + update.character = CharacterState::SelfBuff(Data { + timer: Duration::default(), + stage_section: StageSection::Recover, + ..*self + }); + } + }, + StageSection::Recover => { + if self.timer < self.static_data.recover_duration { + update.character = CharacterState::SelfBuff(Data { + timer: self + .timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + ..*self + }); + } else { + // Done + update.character = CharacterState::Wielding; + } + }, + _ => { + // If it somehow ends up in an incorrect stage section + update.character = CharacterState::Wielding; + }, + } + + // At end of state logic so an interrupt isn't overwritten + if !input_is_pressed(data, self.static_data.ability_info.input) { + handle_state_interrupt(data, &mut update, false); + } + + update + } +} diff --git a/common/systems/src/character_behavior.rs b/common/systems/src/character_behavior.rs index d93791c78e..a97350888b 100644 --- a/common/systems/src/character_behavior.rs +++ b/common/systems/src/character_behavior.rs @@ -341,6 +341,7 @@ impl<'a> System<'a> for Sys { CharacterState::HealingBeam(data) => data.handle_event(&j, action), CharacterState::Blink(data) => data.handle_event(&j, action), CharacterState::BasicSummon(data) => data.handle_event(&j, action), + CharacterState::SelfBuff(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -395,6 +396,7 @@ impl<'a> System<'a> for Sys { CharacterState::HealingBeam(data) => data.behavior(&j), CharacterState::Blink(data) => data.behavior(&j), CharacterState::BasicSummon(data) => data.behavior(&j), + CharacterState::SelfBuff(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index 4d999387db..18ef179826 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -248,7 +248,8 @@ impl<'a> System<'a> for Sys { | CharacterState::BasicAura { .. } | CharacterState::HealingBeam { .. } | CharacterState::Blink { .. } - | CharacterState::BasicSummon { .. } => { + | CharacterState::BasicSummon { .. } + | CharacterState::SelfBuff { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 3fe30786da..83ad74a6d5 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3622,6 +3622,8 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id { BuffKind::IncreaseMaxHealth { .. } => imgs.buff_healthplus_0, BuffKind::Invulnerability => imgs.buff_invincibility_0, BuffKind::ProtectingWard => imgs.buff_dmg_red_0, + // TODO: Get buff icon + BuffKind::Frenzied { .. } => imgs.buff_invincibility_0, // Debuffs BuffKind::Bleeding { .. } => imgs.debuff_bleed_0, BuffKind::Cursed { .. } => imgs.debuff_skull_0, @@ -3642,6 +3644,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> &str BuffKind::IncreaseMaxEnergy { .. } => localized_strings.get("buff.title.staminaup"), BuffKind::Invulnerability => localized_strings.get("buff.title.invulnerability"), BuffKind::ProtectingWard => localized_strings.get("buff.title.protectingward"), + BuffKind::Frenzied => localized_strings.get("buff.title.frenzied"), // Debuffs BuffKind::Bleeding { .. } => localized_strings.get("buff.title.bleed"), BuffKind::Cursed { .. } => localized_strings.get("buff.title.cursed"), @@ -3673,6 +3676,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz BuffKind::ProtectingWard => { Cow::Borrowed(localized_strings.get("buff.desc.protectingward")) }, + BuffKind::Frenzied => Cow::Borrowed(localized_strings.get("buff.desc.frenzied")), // Debuffs BuffKind::Bleeding { .. } => Cow::Borrowed(localized_strings.get("buff.desc.bleed")), BuffKind::Cursed { .. } => Cow::Borrowed(localized_strings.get("buff.desc.cursed")), diff --git a/voxygen/src/hud/util.rs b/voxygen/src/hud/util.rs index e9cdc8d0cf..68271e7ebb 100644 --- a/voxygen/src/hud/util.rs +++ b/voxygen/src/hud/util.rs @@ -119,7 +119,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { | BuffKind::CampfireHeal | BuffKind::Cursed | BuffKind::ProtectingWard - | BuffKind::Crippled => continue, + | BuffKind::Crippled + | BuffKind::Frenzied => continue, }; write!(&mut description, "{}", buff_desc).unwrap(); @@ -140,7 +141,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> String { | BuffKind::CampfireHeal | BuffKind::Cursed | BuffKind::ProtectingWard - | BuffKind::Crippled => continue, + | BuffKind::Crippled + | BuffKind::Frenzied => continue, } } else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind { i18n.get("buff.text.every_second").to_string()