mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Finisher melee
This commit is contained in:
parent
057b1a4ad6
commit
38094b6ba2
@ -4,7 +4,9 @@
|
||||
Tool(Sword): (
|
||||
primary: "common.abilities.sword.balancedstance",
|
||||
secondary: "common.abilities.sword.lunge",
|
||||
abilities: [],
|
||||
abilities: [
|
||||
(None, "common.abilities.sword.testfinisher"),
|
||||
],
|
||||
),
|
||||
Tool(Axe): (
|
||||
primary: "common.abilities.axe.doublestrike",
|
||||
|
27
assets/common/abilities/sword/testfinisher.ron
Normal file
27
assets/common/abilities/sword/testfinisher.ron
Normal file
@ -0,0 +1,27 @@
|
||||
FinisherMelee(
|
||||
energy_cost: 40,
|
||||
buildup_duration: 0.2,
|
||||
swing_duration: 0.05,
|
||||
recover_duration: 0.2,
|
||||
melee_constructor: (
|
||||
kind: Slash(
|
||||
damage: 0,
|
||||
poise: 0,
|
||||
knockback: 0,
|
||||
energy_regen: 0,
|
||||
),
|
||||
range: 3.0,
|
||||
angle: 10.0,
|
||||
scaled: Some(Slash(
|
||||
damage: 25,
|
||||
poise: 0,
|
||||
knockback: 0,
|
||||
energy_regen: 0,
|
||||
)),
|
||||
),
|
||||
minimum_combo: 10,
|
||||
scaling: Some((
|
||||
target: Attack,
|
||||
kind: Linear,
|
||||
)),
|
||||
)
|
@ -405,7 +405,8 @@ impl From<&CharacterState> for CharacterAbilityType {
|
||||
| CharacterState::SpriteInteract(_)
|
||||
| CharacterState::Skate(_)
|
||||
| CharacterState::Wallrun(_)
|
||||
| CharacterState::ComboMelee2(_) => Self::Other,
|
||||
| CharacterState::ComboMelee2(_)
|
||||
| CharacterState::FinisherMelee(_) => Self::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,6 +658,19 @@ pub enum CharacterAbility {
|
||||
Music {
|
||||
play_duration: f32,
|
||||
ori_modifier: f32,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
},
|
||||
FinisherMelee {
|
||||
energy_cost: f32,
|
||||
buildup_duration: f32,
|
||||
swing_duration: f32,
|
||||
recover_duration: f32,
|
||||
melee_constructor: MeleeConstructor,
|
||||
minimum_combo: u32,
|
||||
scaling: Option<finisher_melee::Scaling>,
|
||||
#[serde(default)]
|
||||
meta: AbilityMeta,
|
||||
},
|
||||
}
|
||||
|
||||
@ -729,6 +743,14 @@ impl CharacterAbility {
|
||||
| !*scales_with_combo)
|
||||
&& update.energy.try_change_by(-*energy_cost).is_ok()
|
||||
},
|
||||
CharacterAbility::FinisherMelee {
|
||||
energy_cost,
|
||||
minimum_combo,
|
||||
..
|
||||
} => {
|
||||
data.combo.map_or(false, |c| c.counter() >= *minimum_combo)
|
||||
&& update.energy.try_change_by(-*energy_cost).is_ok()
|
||||
},
|
||||
CharacterAbility::ComboMelee { .. }
|
||||
| CharacterAbility::ComboMelee2 { .. }
|
||||
| CharacterAbility::Boost { .. }
|
||||
@ -1144,9 +1166,26 @@ impl CharacterAbility {
|
||||
Music {
|
||||
ref mut play_duration,
|
||||
ori_modifier: _,
|
||||
meta: _,
|
||||
} => {
|
||||
*play_duration /= stats.speed;
|
||||
},
|
||||
FinisherMelee {
|
||||
ref mut energy_cost,
|
||||
ref mut buildup_duration,
|
||||
ref mut swing_duration,
|
||||
ref mut recover_duration,
|
||||
ref mut melee_constructor,
|
||||
minimum_combo: _,
|
||||
scaling: _,
|
||||
meta: _,
|
||||
} => {
|
||||
*buildup_duration /= stats.speed;
|
||||
*swing_duration /= stats.speed;
|
||||
*recover_duration /= stats.speed;
|
||||
*energy_cost /= stats.energy_efficiency;
|
||||
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
|
||||
},
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -1166,7 +1205,8 @@ impl CharacterAbility {
|
||||
| Shockwave { energy_cost, .. }
|
||||
| BasicAura { energy_cost, .. }
|
||||
| BasicBlock { energy_cost, .. }
|
||||
| SelfBuff { energy_cost, .. } => *energy_cost,
|
||||
| SelfBuff { energy_cost, .. }
|
||||
| FinisherMelee { energy_cost, .. } => *energy_cost,
|
||||
BasicBeam { energy_drain, .. } => {
|
||||
if *energy_drain > f32::EPSILON {
|
||||
1.0
|
||||
@ -1207,7 +1247,9 @@ impl CharacterAbility {
|
||||
| ComboMelee2 { meta, .. }
|
||||
| Blink { meta, .. }
|
||||
| BasicSummon { meta, .. }
|
||||
| SpriteSummon { meta, .. } => *meta,
|
||||
| SpriteSummon { meta, .. }
|
||||
| FinisherMelee { meta, .. }
|
||||
| Music { meta, .. } => *meta,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1223,6 +1265,9 @@ impl CharacterAbility {
|
||||
BasicMelee { energy_cost, .. } => {
|
||||
*energy_cost *= ENERGY_REDUCTION;
|
||||
},
|
||||
FinisherMelee { energy_cost, .. } => {
|
||||
*energy_cost *= ENERGY_REDUCTION;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -2271,6 +2316,7 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
CharacterAbility::Music {
|
||||
play_duration,
|
||||
ori_modifier,
|
||||
meta: _,
|
||||
} => CharacterState::Music(music::Data {
|
||||
static_data: music::StaticData {
|
||||
play_duration: Duration::from_secs_f32(*play_duration),
|
||||
@ -2281,6 +2327,30 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
||||
stage_section: StageSection::Action,
|
||||
exhausted: false,
|
||||
}),
|
||||
CharacterAbility::FinisherMelee {
|
||||
energy_cost: _,
|
||||
buildup_duration,
|
||||
swing_duration,
|
||||
recover_duration,
|
||||
melee_constructor,
|
||||
minimum_combo,
|
||||
scaling,
|
||||
meta: _,
|
||||
} => CharacterState::FinisherMelee(finisher_melee::Data {
|
||||
static_data: finisher_melee::StaticData {
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
swing_duration: Duration::from_secs_f32(*swing_duration),
|
||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||
melee_constructor: *melee_constructor,
|
||||
scaling: *scaling,
|
||||
minimum_combo: *minimum_combo,
|
||||
combo_on_use: data.combo.map_or(0, |c| c.counter()),
|
||||
ability_info,
|
||||
},
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Buildup,
|
||||
exhausted: false,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,8 @@ pub enum CharacterState {
|
||||
Skate(skate::Data),
|
||||
/// Play music instrument
|
||||
Music(music::Data),
|
||||
/// Melee attack that scales off and consumes combo
|
||||
FinisherMelee(finisher_melee::Data),
|
||||
}
|
||||
|
||||
impl CharacterState {
|
||||
@ -163,6 +165,7 @@ impl CharacterState {
|
||||
was_wielded: true,
|
||||
..
|
||||
})
|
||||
| CharacterState::FinisherMelee(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -202,6 +205,7 @@ impl CharacterState {
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
| CharacterState::FinisherMelee(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -224,6 +228,7 @@ impl CharacterState {
|
||||
| CharacterState::UseItem(_)
|
||||
| CharacterState::Wielding(_)
|
||||
| CharacterState::Talk
|
||||
| CharacterState::FinisherMelee(_)
|
||||
)
|
||||
}
|
||||
|
||||
@ -354,6 +359,7 @@ impl CharacterState {
|
||||
CharacterState::SpriteInteract(data) => data.behavior(j, output_events),
|
||||
CharacterState::Skate(data) => data.behavior(j, output_events),
|
||||
CharacterState::Music(data) => data.behavior(j, output_events),
|
||||
CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,6 +409,7 @@ impl CharacterState {
|
||||
CharacterState::SpriteInteract(data) => data.handle_event(j, output_events, action),
|
||||
CharacterState::Skate(data) => data.handle_event(j, output_events, action),
|
||||
CharacterState::Music(data) => data.handle_event(j, output_events, action),
|
||||
CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,6 +456,9 @@ impl CharacterState {
|
||||
CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::UseItem(_) => None,
|
||||
CharacterState::SpriteInteract(_) => None,
|
||||
CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::Music(data) => Some(data.static_data.ability_info),
|
||||
CharacterState::Skate(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -487,6 +497,9 @@ impl CharacterState {
|
||||
CharacterState::SpriteSummon(data) => Some(data.stage_section),
|
||||
CharacterState::UseItem(data) => Some(data.stage_section),
|
||||
CharacterState::SpriteInteract(data) => Some(data.stage_section),
|
||||
CharacterState::FinisherMelee(data) => Some(data.stage_section),
|
||||
CharacterState::Music(data) => Some(data.stage_section),
|
||||
CharacterState::Skate(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
171
common/src/states/finisher_melee.rs
Normal file
171
common/src/states/finisher_melee.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use crate::{
|
||||
combat::{CombatBuff, CombatEffect},
|
||||
comp::{character_state::OutputEvents, CharacterState, Melee, MeleeConstructor, StateUpdate},
|
||||
event::ServerEvent,
|
||||
states::{
|
||||
behavior::{CharacterBehavior, JoinData},
|
||||
utils::*,
|
||||
wielding,
|
||||
},
|
||||
};
|
||||
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 deal damage
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state is swinging for
|
||||
pub swing_duration: Duration,
|
||||
/// How long the state has until exiting
|
||||
pub recover_duration: Duration,
|
||||
/// Used to construct the Melee attack
|
||||
pub melee_constructor: MeleeConstructor,
|
||||
/// Used to determine if and how scaling of the melee attack should happen
|
||||
pub scaling: Option<Scaling>,
|
||||
/// Minimum amount of combo needed to activate ability
|
||||
pub minimum_combo: u32,
|
||||
/// Amount of combo when ability was activated
|
||||
pub combo_on_use: u32,
|
||||
/// 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,
|
||||
/// Whether the attack can deal more damage
|
||||
pub exhausted: bool,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||
let mut update = StateUpdate::from(data);
|
||||
|
||||
handle_orientation(data, &mut update, 1.0, None);
|
||||
handle_move(data, &mut update, 0.7);
|
||||
handle_jump(data, output_events, &mut update, 1.0);
|
||||
handle_interrupts(data, &mut update, None);
|
||||
|
||||
match self.stage_section {
|
||||
StageSection::Buildup => {
|
||||
if self.timer < self.static_data.buildup_duration {
|
||||
// Build up
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = tick_attack_or_default(data, self.timer, None);
|
||||
}
|
||||
} else {
|
||||
// Transitions to swing section of stage
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = Duration::default();
|
||||
c.stage_section = StageSection::Action;
|
||||
}
|
||||
}
|
||||
},
|
||||
StageSection::Action => {
|
||||
if !self.exhausted {
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = Duration::default();
|
||||
c.exhausted = true;
|
||||
}
|
||||
|
||||
// Consume combo
|
||||
output_events.emit_server(ServerEvent::ComboChange {
|
||||
entity: data.entity,
|
||||
change: -(self.static_data.combo_on_use as i32),
|
||||
});
|
||||
|
||||
let mut melee_constructor = self.static_data.melee_constructor;
|
||||
|
||||
if let Some(scaling) = self.static_data.scaling {
|
||||
let scaled_by = match scaling.kind {
|
||||
ScalingKind::Linear => {
|
||||
self.static_data.combo_on_use as f32
|
||||
/ self.static_data.minimum_combo as f32
|
||||
},
|
||||
};
|
||||
match scaling.target {
|
||||
ScalingTarget::Attack => {
|
||||
melee_constructor = melee_constructor.handle_scaling(scaled_by);
|
||||
},
|
||||
ScalingTarget::Buff => {
|
||||
if let Some(CombatEffect::Buff(CombatBuff { strength, .. })) =
|
||||
&mut melee_constructor.damage_effect
|
||||
{
|
||||
*strength *= scaled_by;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let crit_data = get_crit_data(data, self.static_data.ability_info);
|
||||
let buff_strength = get_buff_strength(data, self.static_data.ability_info);
|
||||
|
||||
data.updater.insert(
|
||||
data.entity,
|
||||
melee_constructor.create_melee(crit_data, buff_strength),
|
||||
);
|
||||
} else if self.timer < self.static_data.swing_duration {
|
||||
// Swings
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = tick_attack_or_default(data, self.timer, None);
|
||||
}
|
||||
} else {
|
||||
// Transitions to recover section of stage
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = Duration::default();
|
||||
c.stage_section = StageSection::Recover
|
||||
}
|
||||
}
|
||||
},
|
||||
StageSection::Recover => {
|
||||
if self.timer < self.static_data.recover_duration {
|
||||
// Recovery
|
||||
if let CharacterState::FinisherMelee(c) = &mut update.character {
|
||||
c.timer = tick_attack_or_default(data, self.timer, None);
|
||||
}
|
||||
} else {
|
||||
// Done
|
||||
update.character =
|
||||
CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// If it somehow ends up in an incorrect stage section
|
||||
update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
||||
// Make sure attack component is removed
|
||||
data.updater.remove::<Melee>(data.entity);
|
||||
},
|
||||
}
|
||||
|
||||
update
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ScalingTarget {
|
||||
Attack,
|
||||
Buff,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ScalingKind {
|
||||
// Reaches a scaling of 1 when at minimum combo, and a scaling of 2 when at double minimum
|
||||
// combo
|
||||
Linear,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Scaling {
|
||||
pub target: ScalingTarget,
|
||||
pub kind: ScalingKind,
|
||||
}
|
@ -15,6 +15,7 @@ pub mod combo_melee2;
|
||||
pub mod dance;
|
||||
pub mod dash_melee;
|
||||
pub mod equipping;
|
||||
pub mod finisher_melee;
|
||||
pub mod glide;
|
||||
pub mod glide_wield;
|
||||
pub mod idle;
|
||||
|
@ -145,16 +145,16 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
match character_state {
|
||||
// Accelerate recharging energy.
|
||||
CharacterState::Idle { .. }
|
||||
| CharacterState::Talk { .. }
|
||||
| CharacterState::Sit { .. }
|
||||
| CharacterState::Dance { .. }
|
||||
| CharacterState::Glide { .. }
|
||||
| CharacterState::Skate { .. }
|
||||
| CharacterState::GlideWield { .. }
|
||||
| CharacterState::Wielding { .. }
|
||||
| CharacterState::Equipping { .. }
|
||||
| CharacterState::Boost { .. } => {
|
||||
CharacterState::Idle(_)
|
||||
| CharacterState::Talk
|
||||
| CharacterState::Sit
|
||||
| CharacterState::Dance
|
||||
| CharacterState::Glide(_)
|
||||
| CharacterState::Skate(_)
|
||||
| CharacterState::GlideWield(_)
|
||||
| CharacterState::Wielding(_)
|
||||
| CharacterState::Equipping(_)
|
||||
| CharacterState::Boost(_) => {
|
||||
let res = { energy.current() < energy.maximum() };
|
||||
|
||||
if res {
|
||||
@ -179,36 +179,37 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
},
|
||||
// Ability use does not regen and sets the rate back to zero.
|
||||
CharacterState::BasicMelee { .. }
|
||||
| CharacterState::DashMelee { .. }
|
||||
| CharacterState::LeapMelee { .. }
|
||||
| CharacterState::SpinMelee { .. }
|
||||
| CharacterState::ComboMelee { .. }
|
||||
| CharacterState::ComboMelee2 { .. }
|
||||
| CharacterState::BasicRanged { .. }
|
||||
| CharacterState::Music { .. }
|
||||
| CharacterState::ChargedMelee { .. }
|
||||
| CharacterState::ChargedRanged { .. }
|
||||
| CharacterState::RepeaterRanged { .. }
|
||||
| CharacterState::Shockwave { .. }
|
||||
| CharacterState::BasicBeam { .. }
|
||||
| CharacterState::BasicAura { .. }
|
||||
| CharacterState::Blink { .. }
|
||||
| CharacterState::BasicSummon { .. }
|
||||
| CharacterState::SelfBuff { .. }
|
||||
| CharacterState::SpriteSummon { .. } => {
|
||||
CharacterState::BasicMelee(_)
|
||||
| CharacterState::DashMelee(_)
|
||||
| CharacterState::LeapMelee(_)
|
||||
| CharacterState::SpinMelee(_)
|
||||
| CharacterState::ComboMelee(_)
|
||||
| CharacterState::ComboMelee2(_)
|
||||
| CharacterState::BasicRanged(_)
|
||||
| CharacterState::Music(_)
|
||||
| CharacterState::ChargedMelee(_)
|
||||
| CharacterState::ChargedRanged(_)
|
||||
| CharacterState::RepeaterRanged(_)
|
||||
| CharacterState::Shockwave(_)
|
||||
| CharacterState::BasicBeam(_)
|
||||
| CharacterState::BasicAura(_)
|
||||
| CharacterState::Blink(_)
|
||||
| CharacterState::BasicSummon(_)
|
||||
| CharacterState::SelfBuff(_)
|
||||
| CharacterState::SpriteSummon(_)
|
||||
| CharacterState::FinisherMelee(_) => {
|
||||
if energy.regen_rate != 0.0 {
|
||||
energy.regen_rate = 0.0
|
||||
}
|
||||
},
|
||||
// Abilities that temporarily stall energy gain, but preserve regen_rate.
|
||||
CharacterState::Roll { .. }
|
||||
| CharacterState::Wallrun { .. }
|
||||
| CharacterState::Climb { .. }
|
||||
| CharacterState::Stunned { .. }
|
||||
| CharacterState::BasicBlock { .. }
|
||||
| CharacterState::UseItem { .. }
|
||||
| CharacterState::SpriteInteract { .. } => {},
|
||||
CharacterState::Roll(_)
|
||||
| CharacterState::Wallrun(_)
|
||||
| CharacterState::Climb(_)
|
||||
| CharacterState::Stunned(_)
|
||||
| CharacterState::BasicBlock(_)
|
||||
| CharacterState::UseItem(_)
|
||||
| CharacterState::SpriteInteract(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user