Combo melee 2 state

This commit is contained in:
Sam 2022-01-23 01:39:59 -05:00
parent 38c9d87624
commit 17258975fc
42 changed files with 462 additions and 772 deletions

View File

@ -2,11 +2,9 @@
// A set of abilities is a primary, a secondary, and a vec of all extra abilities
({
Tool(Sword): (
primary: "common.abilities.sword.triplestrike",
secondary: "common.abilities.sword.dash",
abilities: [
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
],
primary: "common.abilities.sword.balancedstance",
secondary: "common.abilities.sword.balancedstance",
abilities: [],
),
Tool(Axe): (
primary: "common.abilities.axe.doublestrike",

View File

@ -0,0 +1,58 @@
ComboMelee2(
strikes: [
(
melee_constructor: (
kind: Slash(
damage: 5.0,
poise: 0.0,
knockback: 0.0,
energy_regen: 5.0,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.5,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.3,
forward_movement: 0.0,
ori_modifier: 0.6,
),
(
melee_constructor: (
kind: Slash(
damage: 10.0,
poise: 0.0,
knockback: 0.0,
energy_regen: 5.0,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.5,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.3,
forward_movement: 0.0,
ori_modifier: 0.6,
),
(
melee_constructor: (
kind: Slash(
damage: 15.0,
poise: 0.0,
knockback: 0.0,
energy_regen: 5.0,
),
range: 3.0,
angle: 45.0,
),
buildup_duration: 0.5,
swing_duration: 0.1,
hit_timing: 0.5,
recover_duration: 0.3,
forward_movement: 0.0,
ori_modifier: 0.6,
),
]
)

View File

@ -1,28 +0,0 @@
DashMelee(
energy_cost: 10.0,
melee_constructor: (
kind: Stab(
damage: 4.0,
poise: 0.0,
knockback: 8.0,
energy_regen: 0.0,
),
scaled: Some(Stab(
damage: 16.0,
poise: 0.0,
knockback: 7.0,
energy_regen: 0.0,
)),
range: 4.0,
angle: 60.0,
),
energy_drain: 30.0,
forward_speed: 2.5,
buildup_duration: 0.2,
charge_duration: 1.2,
swing_duration: 0.1,
recover_duration: 0.5,
ori_modifier: 0.3,
charge_through: true,
is_interruptible: true,
)

View File

@ -1,22 +0,0 @@
SpinMelee(
buildup_duration: 0.35,
swing_duration: 0.4,
recover_duration: 0.5,
melee_constructor: (
kind: Slash(
damage: 12.0,
poise: 10.0,
knockback: 10.0,
energy_regen: 0.0,
),
range: 3.5,
angle: 360.0,
),
energy_cost: 20.0,
is_infinite: false,
movement_behavior: ForwardGround,
is_interruptible: true,
forward_speed: 1.0,
num_spins: 3,
specifier: None,
)

View File

@ -1,78 +0,0 @@
ComboMelee(
stage_data: [
(
stage: 1,
base_damage: 8.0,
damage_increase: 0.5,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 0.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.25,
forward_movement: 0.5,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 2,
base_damage: 9.0,
damage_increase: 0.5,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 2.0,
range: 3.5,
angle: 40.0,
base_buildup_duration: 0.1,
base_swing_duration: 0.1,
hit_timing: 0.5,
base_recover_duration: 0.3,
forward_movement: 0.0,
damage_kind: Slashing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
(
stage: 3,
base_damage: 10.0,
damage_increase: 0.5,
base_poise_damage: 0,
poise_damage_increase: 0,
knockback: 2.0,
range: 6.0,
angle: 10.0,
base_buildup_duration: 0.15,
base_swing_duration: 0.1,
hit_timing: 0.2,
base_recover_duration: 0.35,
forward_movement: 1.2,
damage_kind: Piercing,
damage_effect: Some(Buff((
kind: Bleeding,
dur_secs: 10.0,
strength: DamageFraction(0.1),
chance: 0.1,
))),
),
],
initial_energy_gain: 0,
max_energy_gain: 12.5,
energy_increase: 2.5,
speed_increase: 0.1,
max_speed_increase: 0.8,
scales_from_combo: 2,
is_interruptible: true,
ori_modifier: 1.0,
)

View File

@ -1,17 +1,6 @@
({
General(HealthIncrease): 10,
General(EnergyIncrease): 5,
Sword(TsDamage): 3,
Sword(TsRegen): 2,
Sword(TsSpeed): 3,
Sword(DCost): 2,
Sword(DDrain): 2,
Sword(DDamage): 2,
Sword(DScaling): 3,
Sword(SDamage): 2,
Sword(SSpeed): 2,
Sword(SCost): 2,
Sword(SSpins): 2,
Axe(DsDamage): 3,
Axe(DsRegen): 2,
Axe(DsSpeed): 3,

View File

@ -1,8 +1,4 @@
({
Sword(SDamage): {Sword(UnlockSpin): 1},
Sword(SSpeed): {Sword(UnlockSpin): 1},
Sword(SCost): {Sword(UnlockSpin): 1},
Sword(SSpins): {Sword(UnlockSpin): 1},
Axe(LDamage): {Axe(UnlockLeap): 1},
Axe(LKnockback): {Axe(UnlockLeap): 1},
Axe(LCost): {Axe(UnlockLeap): 1},

View File

@ -15,24 +15,7 @@
Climb(Speed),
Swim(Speed),
],
Weapon(Sword): [
Sword(InterruptingAttacks),
Sword(TsCombo),
Sword(TsDamage),
Sword(TsRegen),
Sword(TsSpeed),
Sword(DCost),
Sword(DDrain),
Sword(DDamage),
Sword(DScaling),
Sword(DSpeed),
Sword(DChargeThrough),
Sword(UnlockSpin),
Sword(SDamage),
Sword(SSpeed),
Sword(SCost),
Sword(SSpins),
],
Weapon(Sword): [],
Weapon(Axe): [
Axe(DsCombo),
Axe(DsDamage),

View File

@ -1,14 +1,3 @@
([
Group(Weapon(Sword)),
Skill((Sword(InterruptingAttacks), 1)),
// TripleStrike
Skill((Sword(TsCombo), 1)),
Skill((Sword(TsDamage), 1)),
Skill((Sword(TsRegen), 1)),
// Dash
Skill((Sword(DCost), 1)),
])

View File

@ -1,17 +1,3 @@
([
Group(Weapon(Sword)),
Skill((Sword(InterruptingAttacks), 1)),
// TripleStrike
Skill((Sword(TsCombo), 1)),
Skill((Sword(TsDamage), 1)),
Skill((Sword(TsRegen), 1)),
Skill((Sword(TsSpeed), 1)),
// Dash
Skill((Sword(DCost), 1)),
Skill((Sword(DDrain), 1)),
Skill((Sword(DDamage), 1)),
])

View File

@ -1,23 +1,3 @@
([
Group(Weapon(Sword)),
Skill((Sword(InterruptingAttacks), 1)),
// TripleStrike
Skill((Sword(TsCombo), 1)),
Skill((Sword(TsDamage), 2)),
Skill((Sword(TsRegen), 2)),
Skill((Sword(TsSpeed), 1)),
// Dash
Skill((Sword(DCost), 2)),
Skill((Sword(DDrain), 2)),
Skill((Sword(DDamage), 1)),
Skill((Sword(DScaling), 1)),
Skill((Sword(DSpeed), 1)),
// Spin of death
Skill((Sword(UnlockSpin), 1)),
Skill((Sword(SDamage), 1)),
Skill((Sword(SSpeed), 1)),
])

View File

@ -1,26 +1,3 @@
([
Group(Weapon(Sword)),
Skill((Sword(InterruptingAttacks), 1)),
// TripleStrike
Skill((Sword(TsCombo), 1)),
Skill((Sword(TsDamage), 2)),
Skill((Sword(TsRegen), 2)),
Skill((Sword(TsSpeed), 3)),
// Dash
Skill((Sword(DCost), 2)),
Skill((Sword(DDrain), 2)),
Skill((Sword(DDamage), 2)),
Skill((Sword(DScaling), 2)),
Skill((Sword(DSpeed), 1)),
Skill((Sword(DChargeThrough), 1)),
// Spin of death
Skill((Sword(UnlockSpin), 1)),
Skill((Sword(SDamage), 1)),
Skill((Sword(SSpeed), 1)),
Skill((Sword(SSpins), 1)),
Skill((Sword(SCost), 1)),
])

View File

@ -1,26 +1,3 @@
([
Group(Weapon(Sword)),
Skill((Sword(InterruptingAttacks), 1)),
// TripleStrike
Skill((Sword(TsCombo), 1)),
Skill((Sword(TsDamage), 3)),
Skill((Sword(TsRegen), 2)),
Skill((Sword(TsSpeed), 3)),
// Dash
Skill((Sword(DCost), 2)),
Skill((Sword(DDrain), 2)),
Skill((Sword(DDamage), 2)),
Skill((Sword(DScaling), 3)),
Skill((Sword(DSpeed), 1)),
Skill((Sword(DChargeThrough), 1)),
// Spin of death
Skill((Sword(UnlockSpin), 1)),
Skill((Sword(SDamage), 2)),
Skill((Sword(SSpeed), 2)),
Skill((Sword(SSpins), 2)),
Skill((Sword(SCost), 2)),
])

View File

@ -15,25 +15,6 @@
// Sword
(UnlockGroup(Weapon(Sword)), 1),
(Sword(InterruptingAttacks), 1),
(Sword(TsCombo), 1),
(Sword(TsDamage), 3),
(Sword(TsRegen), 2),
(Sword(TsSpeed), 3),
(Sword(DCost), 2),
(Sword(DDrain), 2),
(Sword(DDamage), 2),
(Sword(DScaling), 3),
(Sword(DSpeed), 1),
(Sword(DChargeThrough), 1),
(Sword(UnlockSpin), 1),
(Sword(SDamage), 2),
(Sword(SSpeed), 2),
(Sword(SCost), 2),
(Sword(SSpins), 2),
// Axe
(UnlockGroup(Weapon(Axe)), 1),
@ -145,25 +126,6 @@
// Sword
(UnlockGroup(Weapon(Sword)), 1),
(Sword(InterruptingAttacks), 1),
(Sword(TsCombo), 1),
(Sword(TsDamage), 2),
(Sword(TsRegen), 1),
(Sword(TsSpeed), 2),
(Sword(DCost), 1),
(Sword(DDrain), 2),
(Sword(DDamage), 2),
(Sword(DScaling), 2),
(Sword(DSpeed), 1),
(Sword(DChargeThrough), 1),
(Sword(UnlockSpin), 1),
(Sword(SDamage), 1),
(Sword(SSpeed), 2),
(Sword(SCost), 2),
(Sword(SSpins), 2),
// Axe
(UnlockGroup(Weapon(Axe)), 1),
@ -265,8 +227,6 @@
// Sword
(UnlockGroup(Weapon(Sword)), 1),
(Sword(UnlockSpin), 1),
// Axe
(UnlockGroup(Weapon(Axe)), 1),

View File

@ -404,7 +404,8 @@ impl From<&CharacterState> for CharacterAbilityType {
| CharacterState::UseItem(_)
| CharacterState::SpriteInteract(_)
| CharacterState::Skate(_)
| CharacterState::Wallrun(_) => Self::Other,
| CharacterState::Wallrun(_)
| CharacterState::ComboMelee2(_) => Self::Other,
}
}
}
@ -487,6 +488,9 @@ pub enum CharacterAbility {
is_interruptible: bool,
ori_modifier: f32,
},
ComboMelee2 {
strikes: Vec<combo_melee2::Strike<f32>>,
},
LeapMelee {
energy_cost: f32,
buildup_duration: f32,
@ -687,6 +691,7 @@ impl CharacterAbility {
&& update.energy.try_change_by(-*energy_cost).is_ok()
},
CharacterAbility::ComboMelee { .. }
| CharacterAbility::ComboMelee2 { .. }
| CharacterAbility::Boost { .. }
| CharacterAbility::BasicBeam { .. }
| CharacterAbility::Blink { .. }
@ -733,7 +738,7 @@ impl CharacterAbility {
*swing_duration /= stats.speed;
*recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency;
*melee_constructor = melee_constructor.adjusted_by_stats(stats, 1.0);
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
},
BasicRanged {
ref mut energy_cost,
@ -798,7 +803,7 @@ impl CharacterAbility {
*recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency;
*energy_drain /= stats.energy_efficiency;
*melee_constructor = melee_constructor.adjusted_by_stats(stats, 1.0);
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
},
BasicBlock {
ref mut buildup_duration,
@ -842,6 +847,12 @@ impl CharacterAbility {
.map(|s| s.adjusted_by_stats(stats))
.collect();
},
ComboMelee2 { ref mut strikes } => {
*strikes = strikes
.iter_mut()
.map(|s| s.adjusted_by_stats(stats))
.collect();
},
LeapMelee {
ref mut energy_cost,
ref mut buildup_duration,
@ -856,7 +867,7 @@ impl CharacterAbility {
*swing_duration /= stats.speed;
*recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency;
*melee_constructor = melee_constructor.adjusted_by_stats(stats, 1.0);
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
},
SpinMelee {
ref mut buildup_duration,
@ -875,7 +886,7 @@ impl CharacterAbility {
*swing_duration /= stats.speed;
*recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency;
*melee_constructor = melee_constructor.adjusted_by_stats(stats, 1.0);
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
},
ChargedMelee {
ref mut energy_cost,
@ -891,7 +902,7 @@ impl CharacterAbility {
*recover_duration /= stats.speed;
*energy_cost /= stats.energy_efficiency;
*energy_drain /= stats.energy_efficiency;
*melee_constructor = melee_constructor.adjusted_by_stats(stats, 1.0);
*melee_constructor = melee_constructor.adjusted_by_stats(stats);
},
ChargedRanged {
ref mut energy_cost,
@ -1105,6 +1116,7 @@ impl CharacterAbility {
},
Boost { .. }
| ComboMelee { .. }
| ComboMelee2 { .. }
| Blink { .. }
| Music { .. }
| BasicSummon { .. }
@ -1115,7 +1127,6 @@ impl CharacterAbility {
#[must_use = "method returns new ability and doesn't mutate the original value"]
pub fn adjusted_by_skills(mut self, skillset: &SkillSet, tool: Option<ToolKind>) -> Self {
match tool {
Some(ToolKind::Sword) => self.adjusted_by_sword_skills(skillset),
Some(ToolKind::Axe) => self.adjusted_by_axe_skills(skillset),
Some(ToolKind::Hammer) => self.adjusted_by_hammer_skills(skillset),
Some(ToolKind::Bow) => self.adjusted_by_bow_skills(skillset),
@ -1173,103 +1184,6 @@ impl CharacterAbility {
}
}
fn adjusted_by_sword_skills(&mut self, skillset: &SkillSet) {
use skills::{Skill::Sword, SwordSkill::*};
match self {
CharacterAbility::ComboMelee {
ref mut is_interruptible,
ref mut speed_increase,
ref mut max_speed_increase,
ref stage_data,
ref mut max_energy_gain,
ref mut scales_from_combo,
..
} => {
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
if skillset.has_skill(Sword(TsCombo)) {
let speed_segments = f32::from(Sword(TsSpeed).max_level()) + 1.0;
let speed_level = f32::from(skillset.skill_level(Sword(TsSpeed)).unwrap_or(0));
*speed_increase = (speed_level + 1.0) / speed_segments;
*max_speed_increase = (speed_level + 1.0) / speed_segments;
} else {
*speed_increase = 0.0;
*max_speed_increase = 0.0;
}
let energy_level = skillset.skill_level(Sword(TsRegen)).unwrap_or(0);
let stages = u16::try_from(stage_data.len())
.expect("number of stages can't be more than u16");
*max_energy_gain *= f32::from((energy_level + 1) * stages - 1)
* f32::from(stages - 1)
/ f32::from(Sword(TsRegen).max_level() + 1);
*scales_from_combo = skillset.skill_level(Sword(TsDamage)).unwrap_or(0).into();
},
CharacterAbility::DashMelee {
ref mut is_interruptible,
ref mut energy_cost,
ref mut energy_drain,
ref mut forward_speed,
ref mut charge_through,
ref mut melee_constructor,
..
} => {
let modifiers = SKILL_MODIFIERS.sword_tree.dash;
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
if let Ok(level) = skillset.skill_level(Sword(DCost)) {
*energy_cost *= modifiers.energy_cost.powi(level.into());
}
if let Ok(level) = skillset.skill_level(Sword(DDrain)) {
*energy_drain *= modifiers.energy_drain.powi(level.into());
}
if let MeleeConstructorKind::Slash { ref mut damage, .. } = melee_constructor.kind {
if let Ok(level) = skillset.skill_level(Sword(DDamage)) {
*damage *= modifiers.base_damage.powi(level.into());
}
}
if let Some(MeleeConstructorKind::Slash { ref mut damage, .. }) =
melee_constructor.scaled
{
if let Ok(level) = skillset.skill_level(Sword(DScaling)) {
*damage *= modifiers.scaled_damage.powi(level.into());
}
}
if skillset.has_skill(Sword(DSpeed)) {
*forward_speed *= modifiers.forward_speed;
}
*charge_through = skillset.has_skill(Sword(DChargeThrough));
},
CharacterAbility::SpinMelee {
ref mut is_interruptible,
ref mut swing_duration,
ref mut energy_cost,
ref mut num_spins,
ref mut melee_constructor,
..
} => {
let modifiers = SKILL_MODIFIERS.sword_tree.spin;
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
if let MeleeConstructorKind::Slash { ref mut damage, .. } = melee_constructor.kind {
if let Ok(level) = skillset.skill_level(Sword(SDamage)) {
*damage *= modifiers.base_damage.powi(level.into());
}
}
if let Ok(level) = skillset.skill_level(Sword(SSpeed)) {
*swing_duration *= modifiers.swing_duration.powi(level.into());
}
if let Ok(level) = skillset.skill_level(Sword(SCost)) {
*energy_cost *= modifiers.energy_cost.powi(level.into());
}
let spin_level = skillset.skill_level(Sword(SSpins)).unwrap_or(0);
*num_spins = u32::from(spin_level) * modifiers.num + 1;
},
_ => {},
}
}
fn adjusted_by_axe_skills(&mut self, skillset: &SkillSet) {
#![allow(clippy::enum_glob_use)]
use skills::{AxeSkill::*, Skill::Axe};
@ -1890,6 +1804,18 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
timer: Duration::default(),
stage_section: StageSection::Buildup,
}),
CharacterAbility::ComboMelee2 { strikes } => {
CharacterState::ComboMelee2(combo_melee2::Data {
static_data: combo_melee2::StaticData {
strikes: strikes.iter().map(|s| s.to_duration()).collect(),
ability_info,
},
exhausted: false,
timer: Duration::default(),
stage_section: None,
completed_strikes: 0,
})
},
CharacterAbility::LeapMelee {
energy_cost: _,
buildup_duration,

View File

@ -91,6 +91,8 @@ pub enum CharacterState {
/// A three-stage attack where each attack pushes player forward
/// and successive attacks increase in damage, while player holds button.
ComboMelee(combo_melee::Data),
/// A state where you progress through multiple melee attacks
ComboMelee2(combo_melee2::Data),
/// A leap followed by a small aoe ground attack
LeapMelee(leap_melee::Data),
/// Spin around, dealing damage to enemies surrounding you
@ -138,6 +140,7 @@ impl CharacterState {
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::ComboMelee(_)
| CharacterState::ComboMelee2(_)
| CharacterState::BasicBlock(_)
| CharacterState::LeapMelee(_)
| CharacterState::SpinMelee(_)
@ -186,6 +189,7 @@ impl CharacterState {
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::ComboMelee(_)
| CharacterState::ComboMelee2(_)
| CharacterState::LeapMelee(_)
| CharacterState::SpinMelee(_)
| CharacterState::ChargedMelee(_)
@ -208,6 +212,7 @@ impl CharacterState {
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::ComboMelee(_)
| CharacterState::ComboMelee2(_)
| CharacterState::BasicBlock(_)
| CharacterState::LeapMelee(_)
| CharacterState::ChargedMelee(_)
@ -254,6 +259,7 @@ impl CharacterState {
pub fn is_forced_movement(&self) -> bool {
matches!(self,
CharacterState::ComboMelee(s) if s.stage_section == StageSection::Action)
|| matches!(self, CharacterState::ComboMelee2(s) if s.stage_section == Some(StageSection::Action))
|| matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge)
|| matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement)
|| matches!(self, CharacterState::SpinMelee(s) if s.stage_section == StageSection::Action)
@ -274,6 +280,7 @@ impl CharacterState {
| CharacterState::BasicMelee(_)
| CharacterState::BasicRanged(_)
| CharacterState::ComboMelee(_)
| CharacterState::ComboMelee2(_)
| CharacterState::ChargedRanged(_)
| CharacterState::RepeaterRanged(_)
| CharacterState::BasicBeam(_)
@ -326,6 +333,7 @@ impl CharacterState {
CharacterState::Wielding(data) => data.behavior(j, output_events),
CharacterState::Equipping(data) => data.behavior(j, output_events),
CharacterState::ComboMelee(data) => data.behavior(j, output_events),
CharacterState::ComboMelee2(data) => data.behavior(j, output_events),
CharacterState::BasicMelee(data) => data.behavior(j, output_events),
CharacterState::BasicRanged(data) => data.behavior(j, output_events),
CharacterState::Boost(data) => data.behavior(j, output_events),
@ -374,6 +382,7 @@ impl CharacterState {
CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
CharacterState::Equipping(data) => data.handle_event(j, output_events, action),
CharacterState::ComboMelee(data) => data.handle_event(j, output_events, action),
CharacterState::ComboMelee2(data) => data.handle_event(j, output_events, action),
CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action),
CharacterState::BasicRanged(data) => data.handle_event(j, output_events, action),
CharacterState::Boost(data) => data.handle_event(j, output_events, action),

View File

@ -399,11 +399,11 @@ impl MeleeConstructor {
}
#[must_use]
pub fn adjusted_by_stats(mut self, stats: Stats, regen: f32) -> Self {
pub fn adjusted_by_stats(mut self, stats: Stats) -> Self {
self.range *= stats.range;
self.kind = self.kind.adjusted_by_stats(stats, regen);
self.kind = self.kind.adjusted_by_stats(stats);
if let Some(ref mut scaled) = &mut self.scaled {
*scaled = scaled.adjusted_by_stats(stats, regen);
*scaled = scaled.adjusted_by_stats(stats);
}
if let Some(CombatEffect::Buff(CombatBuff { strength, .. })) = &mut self.damage_effect {
*strength *= stats.buff_strength;
@ -447,38 +447,35 @@ pub enum MeleeConstructorKind {
impl MeleeConstructorKind {
#[must_use]
pub fn adjusted_by_stats(mut self, stats: Stats, regen: f32) -> Self {
pub fn adjusted_by_stats(mut self, stats: Stats) -> Self {
use MeleeConstructorKind::*;
match self {
Slash {
ref mut damage,
ref mut poise,
knockback: _,
ref mut energy_regen,
energy_regen: _,
} => {
*damage *= stats.power;
*poise *= stats.effect_power;
*energy_regen *= regen;
},
Stab {
ref mut damage,
ref mut poise,
knockback: _,
ref mut energy_regen,
energy_regen: _,
} => {
*damage *= stats.power;
*poise *= stats.effect_power;
*energy_regen *= regen;
},
Bash {
ref mut damage,
ref mut poise,
knockback: _,
ref mut energy_regen,
energy_regen: _,
} => {
*damage *= stats.power;
*poise *= stats.effect_power;
*energy_regen *= regen;
},
NecroticVortex {
ref mut damage,

View File

@ -26,28 +26,7 @@ pub enum Skill {
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Ord, PartialOrd)]
pub enum SwordSkill {
// Sword passives
InterruptingAttacks,
// Triple strike upgrades
TsCombo,
TsDamage,
TsRegen,
TsSpeed,
// Dash upgrades
DCost,
DDrain,
DDamage,
DScaling,
DSpeed,
DChargeThrough,
// Spin upgrades
UnlockSpin,
SDamage,
SSpeed,
SCost,
SSpins,
}
pub enum SwordSkill {}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize, Ord, PartialOrd)]
pub enum AxeSkill {
@ -227,7 +206,6 @@ impl Skill {
pub const SKILL_MODIFIERS: SkillTreeModifiers = SkillTreeModifiers::get();
pub struct SkillTreeModifiers {
pub sword_tree: SwordTreeModifiers,
pub axe_tree: AxeTreeModifiers,
pub hammer_tree: HammerTreeModifiers,
pub bow_tree: BowTreeModifiers,
@ -240,7 +218,6 @@ pub struct SkillTreeModifiers {
impl SkillTreeModifiers {
const fn get() -> Self {
Self {
sword_tree: SwordTreeModifiers::get(),
axe_tree: AxeTreeModifiers::get(),
hammer_tree: HammerTreeModifiers::get(),
bow_tree: BowTreeModifiers::get(),
@ -252,46 +229,6 @@ impl SkillTreeModifiers {
}
}
pub struct SwordTreeModifiers {
pub dash: SwordDashModifiers,
pub spin: SwordSpinModifiers,
}
pub struct SwordDashModifiers {
pub energy_cost: f32,
pub energy_drain: f32,
pub base_damage: f32,
pub scaled_damage: f32,
pub forward_speed: f32,
}
pub struct SwordSpinModifiers {
pub base_damage: f32,
pub swing_duration: f32,
pub energy_cost: f32,
pub num: u32,
}
impl SwordTreeModifiers {
const fn get() -> Self {
Self {
dash: SwordDashModifiers {
energy_cost: 0.95,
energy_drain: 0.95,
base_damage: 1.05,
scaled_damage: 1.05,
forward_speed: 1.025,
},
spin: SwordSpinModifiers {
base_damage: 1.1,
swing_duration: 0.95,
energy_cost: 0.95,
num: 1,
},
}
}
}
pub struct AxeTreeModifiers {
pub spin: AxeSpinModifiers,
pub leap: AxeLeapModifiers,

View File

@ -148,7 +148,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -222,7 +222,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -98,7 +98,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -137,7 +137,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -140,7 +140,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -173,7 +173,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -185,7 +185,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -362,7 +362,7 @@ impl CharacterBehavior for Data {
// 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, self.static_data.is_interruptible);
handle_dodge_input(data, &mut update);
}
update

View File

@ -0,0 +1,220 @@
use crate::{
comp::{
character_state::OutputEvents, tool::Stats, CharacterState, InputKind, Melee,
MeleeConstructor, StateUpdate,
},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
wielding,
},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Strike<T> {
/// Used to construct the Melee attack
pub melee_constructor: MeleeConstructor,
/// Initial buildup duration of stage (how long until state can deal damage)
pub buildup_duration: T,
/// Duration of stage spent in swing (controls animation stuff, and can also
/// be used to handle movement separately to buildup)
pub swing_duration: T,
/// At what fraction of the swing duration to apply the melee "hit"
pub hit_timing: f32,
/// Initial recover duration of stage (how long until character exits state)
pub recover_duration: T,
/// How much forward movement there is in the swing portion of the stage
pub forward_movement: f32,
/// Adjusts turning rate during the attack
pub ori_modifier: f32,
}
impl Strike<f32> {
pub fn to_duration(self) -> Strike<Duration> {
Strike::<Duration> {
melee_constructor: self.melee_constructor,
buildup_duration: Duration::from_secs_f32(self.buildup_duration),
swing_duration: Duration::from_secs_f32(self.swing_duration),
hit_timing: self.hit_timing,
recover_duration: Duration::from_secs_f32(self.recover_duration),
forward_movement: self.forward_movement,
ori_modifier: self.ori_modifier,
}
}
#[must_use]
pub fn adjusted_by_stats(self, stats: Stats) -> Self {
Self {
melee_constructor: self.melee_constructor.adjusted_by_stats(stats),
buildup_duration: self.buildup_duration / stats.speed,
swing_duration: self.swing_duration / stats.speed,
hit_timing: self.hit_timing,
recover_duration: self.recover_duration / stats.speed,
forward_movement: self.forward_movement,
ori_modifier: self.ori_modifier,
}
}
}
// TODO: Completely rewrite this with skill tree rework. Don't bother converting
// to melee constructor.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// Separated out to condense update portions of character state
pub struct StaticData {
/// Data for each stage
pub strikes: Vec<Strike<Duration>>,
/// What key is used to press ability
pub ability_info: AbilityInfo,
}
/// A sequence of attacks that can incrementally become faster and more
/// damaging.
#[derive(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,
/// Whether the attack was executed already
pub exhausted: bool,
/// Timer for each stage
pub timer: Duration,
/// Checks what section a strike is in, if a strike is currently being
/// performed
pub stage_section: Option<StageSection>,
/// Index of the strike that is currently in progress, or if not in a strike
/// currently the next strike that will occur
pub completed_strikes: usize,
}
pub const STANCE_TIME: Duration = Duration::from_secs(5);
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);
let strike_data =
self.static_data.strikes[self.completed_strikes % self.static_data.strikes.len()];
match self.stage_section {
Some(StageSection::Buildup) => {
if self.timer < strike_data.buildup_duration {
// Build up
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to swing section of stage
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: Some(StageSection::Action),
..*self
});
}
},
Some(StageSection::Action) => {
if self.timer.as_secs_f32()
> strike_data.hit_timing * strike_data.swing_duration.as_secs_f32()
&& !self.exhausted
{
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
exhausted: true,
..*self
});
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,
strike_data
.melee_constructor
.create_melee(crit_data, buff_strength),
);
} else if self.timer < strike_data.swing_duration {
// Swings
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: Some(StageSection::Recover),
..*self
});
}
},
Some(StageSection::Recover) => {
if self.timer < strike_data.recover_duration {
// Recovery
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Done
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
completed_strikes: self.completed_strikes + 1,
stage_section: None,
..*self
});
}
},
Some(_) => {
// 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);
},
None => {
if self.timer < STANCE_TIME {
update.character = CharacterState::ComboMelee2(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Done
update.character =
CharacterState::Wielding(wielding::Data { is_sneaking: false });
// Make sure melee component is removed
data.updater.remove::<Melee>(data.entity);
}
if input_is_pressed(data, InputKind::Primary) {
next_strike(&mut update)
}
},
}
// At end of state logic so an interrupt isn't overwritten
if !input_is_pressed(data, InputKind::Primary) {
handle_dodge_input(data, &mut update);
}
update
}
}
fn next_strike(update: &mut StateUpdate) {
if let CharacterState::ComboMelee2(c) = &mut update.character {
c.exhausted = false;
c.timer = Duration::default();
c.stage_section = Some(StageSection::Buildup);
}
}

View File

@ -248,7 +248,7 @@ impl CharacterBehavior for Data {
// 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, self.static_data.is_interruptible);
handle_dodge_input(data, &mut update);
}
update

View File

@ -156,7 +156,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -11,6 +11,7 @@ pub mod charged_melee;
pub mod charged_ranged;
pub mod climb;
pub mod combo_melee;
pub mod combo_melee2;
pub mod dance;
pub mod dash_melee;
pub mod equipping;

View File

@ -75,7 +75,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -161,7 +161,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -117,7 +117,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -170,7 +170,7 @@ impl CharacterBehavior for Data {
// 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);
handle_dodge_input(data, &mut update);
}
update

View File

@ -168,7 +168,7 @@ impl CharacterBehavior for Data {
// 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, self.static_data.is_interruptible);
handle_dodge_input(data, &mut update);
}
update

View File

@ -121,7 +121,7 @@ impl CharacterBehavior for Data {
handle_wield(data, &mut update);
// At end of state logic so an interrupt isn't overwritten
handle_state_interrupt(data, &mut update, false);
handle_dodge_input(data, &mut update);
update
}

View File

@ -144,7 +144,7 @@ impl CharacterBehavior for Data {
}
// At end of state logic so an interrupt isn't overwritten
handle_state_interrupt(data, &mut update, false);
handle_dodge_input(data, &mut update);
if matches!(update.character, CharacterState::Roll(_)) {
// Remove potion/saturation effect if left the use item state early by rolling

View File

@ -1137,17 +1137,6 @@ pub fn get_buff_strength(data: &JoinData<'_>, ai: AbilityInfo) -> f32 {
.unwrap_or(1.0)
}
pub fn handle_state_interrupt(
data: &JoinData<'_>,
update: &mut StateUpdate,
attacks_interrupt: bool,
) {
if attacks_interrupt {
handle_ability_input(data, update);
}
handle_dodge_input(data, update);
}
pub fn input_is_pressed(data: &JoinData<'_>, input: InputKind) -> bool {
data.controller.queued_inputs.contains_key(&input)
}

View File

@ -184,6 +184,7 @@ impl<'a> System<'a> for Sys {
| CharacterState::LeapMelee { .. }
| CharacterState::SpinMelee { .. }
| CharacterState::ComboMelee { .. }
| CharacterState::ComboMelee2 { .. }
| CharacterState::BasicRanged { .. }
| CharacterState::Music { .. }
| CharacterState::ChargedMelee { .. }

View File

@ -6,7 +6,7 @@ use crate::{
use common::{
comp::{
buff::BuffKind,
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill},
AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction, Controller,
InputKind,
},
@ -410,6 +410,7 @@ impl<'a> AgentData<'a> {
}
}
#[allow(unused_variables)]
pub fn handle_sword_attack(
&self,
agent: &mut Agent,
@ -419,73 +420,7 @@ impl<'a> AgentData<'a> {
read_data: &ReadData,
rng: &mut impl Rng,
) {
enum ActionStateTimers {
TimerHandleSwordAttack = 0,
}
if attack_data.in_min_range() && attack_data.angle < 45.0 {
controller.inputs.move_dir = Vec2::zero();
if self
.skill_set
.has_skill(Skill::Sword(SwordSkill::UnlockSpin))
&& agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize]
< 2.0
&& self.energy.current() > 60.0
{
controller.push_basic_input(InputKind::Ability(0));
agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize] +=
read_data.dt.0;
} else if agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize]
> 2.0
{
agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize] = 0.0;
} else {
controller.push_basic_input(InputKind::Primary);
agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize] +=
read_data.dt.0;
}
} else if attack_data.dist_sqrd < MAX_PATH_DIST.powi(2) {
if self.path_toward_target(
agent,
controller,
tgt_data.pos.0,
read_data,
Path::Separate,
None,
) && entities_have_line_of_sight(
self.pos,
self.body,
tgt_data.pos,
tgt_data.body,
read_data,
) {
if agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize]
> 4.0
&& attack_data.angle < 45.0
{
controller.push_basic_input(InputKind::Secondary);
agent.action_state.timers[ActionStateTimers::TimerHandleSwordAttack as usize] =
0.0;
} else {
agent.action_state.timers
[ActionStateTimers::TimerHandleSwordAttack as usize] += read_data.dt.0;
}
}
if self.body.map(|b| b.is_humanoid()).unwrap_or(false)
&& attack_data.dist_sqrd < 16.0f32.powi(2)
&& rng.gen::<f32>() < 0.02
{
controller.push_basic_input(InputKind::Roll);
}
} else {
self.path_toward_target(
agent,
controller,
tgt_data.pos.0,
read_data,
Path::Partial,
None,
);
}
// Rewrite before merging
}
pub fn handle_bow_attack(

View File

@ -1536,8 +1536,6 @@ impl<'a> Diary<'a> {
skills_bot_r,
);
// Skill icons and buttons
use skills::SwordSkill::*;
// Sword
Image::new(animate_by_pulse(
&self
@ -1549,126 +1547,7 @@ impl<'a> Diary<'a> {
.middle_of(state.ids.content_align)
.color(Some(Color::Rgba(1.0, 1.0, 1.0, 1.0)))
.set(state.ids.sword_render, ui);
use PositionSpecifier::MidTopWithMarginOn;
let skill_buttons = &[
// Top Left skills
// 5 1 6
// 3 0 4
// 8 2 7
SkillIcon::Descriptive {
title: "hud-skill-sw_trip_str_title",
desc: "hud-skill-sw_trip_str",
image: self.imgs.twohsword_m1,
position: MidTopWithMarginOn(state.ids.skills_top_l[0], 3.0),
id: state.ids.skill_sword_combo_0,
},
SkillIcon::Unlockable {
skill: Skill::Sword(TsCombo),
image: self.imgs.physical_combo_skill,
position: MidTopWithMarginOn(state.ids.skills_top_l[1], 3.0),
id: state.ids.skill_sword_combo_1,
},
SkillIcon::Unlockable {
skill: Skill::Sword(TsDamage),
image: self.imgs.physical_damage_skill,
position: MidTopWithMarginOn(state.ids.skills_top_l[2], 3.0),
id: state.ids.skill_sword_combo_2,
},
SkillIcon::Unlockable {
skill: Skill::Sword(TsSpeed),
image: self.imgs.physical_speed_skill,
position: MidTopWithMarginOn(state.ids.skills_top_l[3], 3.0),
id: state.ids.skill_sword_combo_3,
},
SkillIcon::Unlockable {
skill: Skill::Sword(TsRegen),
image: self.imgs.physical_energy_regen_skill,
position: MidTopWithMarginOn(state.ids.skills_top_l[4], 3.0),
id: state.ids.skill_sword_combo_4,
},
// Top right skills
SkillIcon::Descriptive {
title: "hud-skill-sw_dash_title",
desc: "hud-skill-sw_dash",
image: self.imgs.twohsword_m2,
position: MidTopWithMarginOn(state.ids.skills_top_r[0], 3.0),
id: state.ids.skill_sword_dash_0,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DDamage),
image: self.imgs.physical_damage_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[1], 3.0),
id: state.ids.skill_sword_dash_1,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DDrain),
image: self.imgs.physical_energy_drain_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[2], 3.0),
id: state.ids.skill_sword_dash_2,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DCost),
image: self.imgs.physical_cost_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[3], 3.0),
id: state.ids.skill_sword_dash_3,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DSpeed),
image: self.imgs.physical_speed_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[4], 3.0),
id: state.ids.skill_sword_dash_4,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DChargeThrough),
image: self.imgs.physical_distance_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[5], 3.0),
id: state.ids.skill_sword_dash_5,
},
SkillIcon::Unlockable {
skill: Skill::Sword(DScaling),
image: self.imgs.physical_amount_skill,
position: MidTopWithMarginOn(state.ids.skills_top_r[6], 3.0),
id: state.ids.skill_sword_dash_6,
},
// Bottom left skills
SkillIcon::Unlockable {
skill: Skill::Sword(UnlockSpin),
image: self.imgs.sword_whirlwind,
position: MidTopWithMarginOn(state.ids.skills_bot_l[0], 3.0),
id: state.ids.skill_sword_spin_0,
},
SkillIcon::Unlockable {
skill: Skill::Sword(SDamage),
image: self.imgs.physical_damage_skill,
position: MidTopWithMarginOn(state.ids.skills_bot_l[1], 3.0),
id: state.ids.skill_sword_spin_1,
},
SkillIcon::Unlockable {
skill: Skill::Sword(SSpeed),
image: self.imgs.physical_damage_skill,
position: MidTopWithMarginOn(state.ids.skills_bot_l[2], 3.0),
id: state.ids.skill_sword_spin_2,
},
SkillIcon::Unlockable {
skill: Skill::Sword(SCost),
image: self.imgs.physical_cost_skill,
position: MidTopWithMarginOn(state.ids.skills_bot_l[3], 3.0),
id: state.ids.skill_sword_spin_3,
},
SkillIcon::Unlockable {
skill: Skill::Sword(SSpins),
image: self.imgs.physical_amount_skill,
position: MidTopWithMarginOn(state.ids.skills_bot_l[4], 3.0),
id: state.ids.skill_sword_spin_4,
},
// Bottom right skills
SkillIcon::Unlockable {
skill: Skill::Sword(InterruptingAttacks),
image: self.imgs.physical_damage_skill,
position: MidTopWithMarginOn(state.ids.skills_bot_r[0], 3.0),
id: state.ids.skill_sword_passive_0,
},
];
let skill_buttons = &[];
self.handle_skill_buttons(skill_buttons, ui, &mut events, diary_tooltip);
events
@ -2844,85 +2723,7 @@ fn unlock_skill_strings(group: SkillGroupKind) -> SkillStrings<'static> {
}
}
fn sword_skill_strings(skill: SwordSkill) -> SkillStrings<'static> {
let modifiers = SKILL_MODIFIERS.sword_tree;
match skill {
// triple strike
SwordSkill::TsCombo => SkillStrings::plain(
"hud-skill-sw_trip_str_combo_title",
"hud-skill-sw_trip_str_combo",
),
SwordSkill::TsDamage => SkillStrings::plain(
"hud-skill-sw_trip_str_dmg_title",
"hud-skill-sw_trip_str_dmg",
),
SwordSkill::TsSpeed => {
SkillStrings::plain("hud-skill-sw_trip_str_sp_title", "hud-skill-sw_trip_str_sp")
},
SwordSkill::TsRegen => SkillStrings::plain(
"hud-skill-sw_trip_str_reg_title",
"hud-skill-sw_trip_str_reg",
),
// dash
SwordSkill::DDamage => SkillStrings::with_mult(
"hud-skill-sw_dash_dmg_title",
"hud-skill-sw_dash_dmg",
modifiers.dash.base_damage,
),
SwordSkill::DDrain => SkillStrings::with_mult(
"hud-skill-sw_dash_drain_title",
"hud-skill-sw_dash_drain",
modifiers.dash.energy_drain,
),
SwordSkill::DCost => SkillStrings::with_mult(
"hud-skill-sw_dash_cost_title",
"hud-skill-sw_dash_cost",
modifiers.dash.energy_cost,
),
SwordSkill::DSpeed => SkillStrings::with_mult(
"hud-skill-sw_dash_speed_title",
"hud-skill-sw_dash_speed",
modifiers.dash.forward_speed,
),
SwordSkill::DChargeThrough => SkillStrings::plain(
"hud-skill-sw_dash_charge_through_title",
"hud-skill-sw_dash_charge_through",
),
SwordSkill::DScaling => SkillStrings::with_mult(
"hud-skill-sw_dash_scale_title",
"hud-skill-sw_dash_scale",
modifiers.dash.scaled_damage,
),
// spin
SwordSkill::UnlockSpin => {
SkillStrings::plain("hud-skill-sw_spin_title", "hud-skill-sw_spin")
},
SwordSkill::SDamage => SkillStrings::with_mult(
"hud-skill-sw_spin_dmg_title",
"hud-skill-sw_spin_dmg",
modifiers.spin.base_damage,
),
SwordSkill::SSpeed => SkillStrings::with_mult(
"hud-skill-sw_spin_spd_title",
"hud-skill-sw_spin_spd",
modifiers.spin.swing_duration,
),
SwordSkill::SCost => SkillStrings::with_mult(
"hud-skill-sw_spin_cost_title",
"hud-skill-sw_spin_cost",
modifiers.spin.energy_cost,
),
SwordSkill::SSpins => SkillStrings::with_const(
"hud-skill-sw_spin_spins_title",
"hud-skill-sw_spin_spins",
modifiers.spin.num,
),
// independent skills
SwordSkill::InterruptingAttacks => {
SkillStrings::plain("hud-skill-sw_interrupt_title", "hud-skill-sw_interrupt")
},
}
}
fn sword_skill_strings(_skill: SwordSkill) -> SkillStrings<'static> { SkillStrings::plain("", "") }
fn axe_skill_strings(skill: AxeSkill) -> SkillStrings<'static> {
let modifiers = SKILL_MODIFIERS.axe_tree;

View File

@ -1623,6 +1623,115 @@ impl FigureMgr {
),
}
},
CharacterState::ComboMelee2(s) => {
if let Some(stage_section) = s.stage_section {
let timer = s.timer.as_secs_f32();
let strike_data = s.static_data.strikes
[s.completed_strikes % s.static_data.strikes.len()];
let progress = match stage_section {
StageSection::Buildup => {
timer / strike_data.buildup_duration.as_secs_f32()
},
StageSection::Action => {
timer / strike_data.swing_duration.as_secs_f32()
},
StageSection::Recover => {
timer / strike_data.recover_duration.as_secs_f32()
},
_ => 0.0,
};
match s.completed_strikes % s.static_data.strikes.len() {
0 => anim::character::AlphaAnimation::update_skeleton(
&target_base,
(
hands,
Some(stage_section),
(Some(s.static_data.ability_info), active_tool_spec),
),
progress,
&mut state_animation_rate,
skeleton_attr,
),
1 => anim::character::SpinAnimation::update_skeleton(
&target_base,
(
hands,
rel_vel,
time,
Some(stage_section),
Some(s.static_data.ability_info),
),
progress,
&mut state_animation_rate,
skeleton_attr,
),
_ => anim::character::BetaAnimation::update_skeleton(
&target_base,
(
hands,
rel_vel.magnitude(),
time,
Some(stage_section),
Some(s.static_data.ability_info),
),
progress,
&mut state_animation_rate,
skeleton_attr,
),
}
} else if physics.in_liquid().is_some() {
anim::character::SwimWieldAnimation::update_skeleton(
&target_base,
(
active_tool_kind,
second_tool_kind,
hands,
rel_vel.magnitude(),
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
} else if false {
// Check for sneaking here if we want combo melee 2 to be able to
// sneak when not actively swinging
anim::character::SneakWieldAnimation::update_skeleton(
&target_base,
(
(active_tool_kind, active_tool_spec),
second_tool_kind,
hands,
rel_vel,
// TODO: Update to use the quaternion.
ori * anim::vek::Vec3::<f32>::unit_y(),
state.last_ori * anim::vek::Vec3::<f32>::unit_y(),
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
} else {
anim::character::WieldAnimation::update_skeleton(
&target_base,
(
(active_tool_kind, active_tool_spec),
second_tool_kind,
hands,
// TODO: Update to use the quaternion.
ori * anim::vek::Vec3::<f32>::unit_y(),
state.last_ori * anim::vek::Vec3::<f32>::unit_y(),
look_dir,
rel_vel,
time,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
}
},
CharacterState::BasicBlock(s) => {
let stage_time = s.timer.as_secs_f32();
let stage_progress = match s.stage_section {