mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Overhauled dash melee.
This commit is contained in:
parent
cb817c0313
commit
ce7581037c
@ -61,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- There is now a brief period after a character leaves the world where they cannot rejoin until their data is saved
|
||||
- Certain uses of client-authoritative physics now subject the player to server-authoritative physics.
|
||||
- Dodge roll iframes and staff explosion are now unlocked by default, with points refunded for existing characters.
|
||||
- Dash melee now stops after hitting something. Infinite dash also now replaced with dash through.
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -14,6 +14,6 @@ DashMelee(
|
||||
charge_duration: 3.0,
|
||||
swing_duration: 0.35,
|
||||
recover_duration: 1.2,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: true,
|
||||
)
|
||||
|
@ -14,6 +14,6 @@ DashMelee(
|
||||
charge_duration: 1.0,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: true,
|
||||
)
|
||||
|
@ -6,14 +6,14 @@ DashMelee(
|
||||
scaled_poise_damage: 0,
|
||||
base_knockback: 8.0,
|
||||
scaled_knockback: 7.0,
|
||||
range: 5.0,
|
||||
angle: 45.0,
|
||||
energy_drain: 600,
|
||||
range: 4.0,
|
||||
angle: 60.0,
|
||||
energy_drain: 300,
|
||||
forward_speed: 3.0,
|
||||
buildup_duration: 0.25,
|
||||
charge_duration: 0.6,
|
||||
charge_duration: 1.2,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: true,
|
||||
)
|
||||
|
@ -11,9 +11,9 @@ DashMelee(
|
||||
energy_drain: 0,
|
||||
forward_speed: 4.0,
|
||||
buildup_duration: 0.25,
|
||||
charge_duration: 0.6,
|
||||
charge_duration: 1.2,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: true,
|
||||
)
|
||||
|
@ -14,6 +14,6 @@ DashMelee(
|
||||
charge_duration: 1.0,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.8,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
)
|
||||
|
@ -11,9 +11,9 @@ DashMelee(
|
||||
energy_drain: 0,
|
||||
forward_speed: 2.0,
|
||||
buildup_duration: 0.5,
|
||||
charge_duration: 0.4,
|
||||
charge_duration: 0.8,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
)
|
||||
|
@ -14,6 +14,6 @@ DashMelee(
|
||||
charge_duration: 1.2,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 1.1,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
)
|
||||
|
@ -11,9 +11,9 @@ DashMelee(
|
||||
energy_drain: 0,
|
||||
forward_speed: 2.5,
|
||||
buildup_duration: 1.2,
|
||||
charge_duration: 0.5,
|
||||
charge_duration: 1.0,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 0.5,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
)
|
||||
|
@ -14,6 +14,6 @@ DashMelee(
|
||||
charge_duration: 1.2,
|
||||
swing_duration: 0.1,
|
||||
recover_duration: 1.1,
|
||||
infinite_charge: true,
|
||||
charge_through: true,
|
||||
is_interruptible: false,
|
||||
)
|
@ -189,8 +189,8 @@
|
||||
"hud.skill.sw_dash_cost": "Decreases the initial cost of the dash by 25%{SP}",
|
||||
"hud.skill.sw_dash_speed_title": "Dash Speed",
|
||||
"hud.skill.sw_dash_speed": "Increases how fast you go while dashing by 30%{SP}",
|
||||
"hud.skill.sw_dash_inf_title": "Dash Infinite",
|
||||
"hud.skill.sw_dash_inf": "Allows you to dash for as long as you have energy{SP}",
|
||||
"hud.skill.sw_dash_charge_through_title": "Charge Through",
|
||||
"hud.skill.sw_dash_charge_through": "Allows you to charge through the first enemies you hit{SP}",
|
||||
"hud.skill.sw_dash_scale_title": "Dash Scaling Damage",
|
||||
"hud.skill.sw_dash_scale": "Increases the damage scaling from the dash by 20%{SP}",
|
||||
"hud.skill.sw_spin_title": "Spin Unlock",
|
||||
|
@ -113,7 +113,7 @@ pub enum CharacterAbility {
|
||||
charge_duration: f32,
|
||||
swing_duration: f32,
|
||||
recover_duration: f32,
|
||||
infinite_charge: bool,
|
||||
charge_through: bool,
|
||||
is_interruptible: bool,
|
||||
},
|
||||
BasicBlock,
|
||||
@ -650,7 +650,7 @@ impl CharacterAbility {
|
||||
ref mut base_damage,
|
||||
ref mut scaled_damage,
|
||||
ref mut forward_speed,
|
||||
ref mut infinite_charge,
|
||||
ref mut charge_through,
|
||||
..
|
||||
} => {
|
||||
*is_interruptible = skillset.has_skill(Sword(InterruptingAttacks));
|
||||
@ -669,7 +669,7 @@ impl CharacterAbility {
|
||||
if skillset.has_skill(Sword(DSpeed)) {
|
||||
*forward_speed *= 1.15;
|
||||
}
|
||||
*infinite_charge = skillset.has_skill(Sword(DInfinite));
|
||||
*charge_through = skillset.has_skill(Sword(DInfinite));
|
||||
},
|
||||
SpinMelee {
|
||||
ref mut is_interruptible,
|
||||
@ -1212,7 +1212,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
charge_duration,
|
||||
swing_duration,
|
||||
recover_duration,
|
||||
infinite_charge,
|
||||
charge_through,
|
||||
is_interruptible,
|
||||
} => CharacterState::DashMelee(dash_melee::Data {
|
||||
static_data: dash_melee::StaticData {
|
||||
@ -1226,7 +1226,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
angle: *angle,
|
||||
energy_drain: *energy_drain,
|
||||
forward_speed: *forward_speed,
|
||||
infinite_charge: *infinite_charge,
|
||||
charge_through: *charge_through,
|
||||
buildup_duration: Duration::from_secs_f32(*buildup_duration),
|
||||
charge_duration: Duration::from_secs_f32(*charge_duration),
|
||||
swing_duration: Duration::from_secs_f32(*swing_duration),
|
||||
@ -1236,7 +1236,7 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
||||
},
|
||||
auto_charge: false,
|
||||
timer: Duration::default(),
|
||||
refresh_distance: 0.0,
|
||||
charge_end_timer: Duration::from_secs_f32(*charge_duration),
|
||||
stage_section: StageSection::Buildup,
|
||||
exhausted: false,
|
||||
}),
|
||||
|
@ -128,7 +128,7 @@ pub enum SwordSkill {
|
||||
DDamage,
|
||||
DScaling,
|
||||
DSpeed,
|
||||
DInfinite,
|
||||
DInfinite, // Represents charge through, not migrated because laziness
|
||||
// Spin upgrades
|
||||
UnlockSpin,
|
||||
SDamage,
|
||||
|
@ -9,7 +9,6 @@ use crate::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use vek::Vec3;
|
||||
|
||||
/// Separated out to condense update portions of character state
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
@ -34,8 +33,8 @@ pub struct StaticData {
|
||||
pub energy_drain: f32,
|
||||
/// How quickly dasher moves forward
|
||||
pub forward_speed: f32,
|
||||
/// Whether state keeps charging after reaching max charge duration
|
||||
pub infinite_charge: bool,
|
||||
/// Whether the state can charge through enemies and do a second hit
|
||||
pub charge_through: bool,
|
||||
/// How long until state should deal damage
|
||||
pub buildup_duration: Duration,
|
||||
/// How long the state charges for until it reaches max damage
|
||||
@ -60,12 +59,12 @@ pub struct Data {
|
||||
pub auto_charge: bool,
|
||||
/// Timer for each stage
|
||||
pub timer: Duration,
|
||||
/// Distance used to limit how often another attack will be applied
|
||||
pub refresh_distance: f32,
|
||||
/// What section the character stage is in
|
||||
pub stage_section: StageSection,
|
||||
/// Whether the state should attempt attacking again
|
||||
pub exhausted: bool,
|
||||
/// Time that charge should end (used for charge through)
|
||||
pub charge_end_timer: Duration,
|
||||
}
|
||||
|
||||
impl CharacterBehavior for Data {
|
||||
@ -97,8 +96,7 @@ impl CharacterBehavior for Data {
|
||||
}
|
||||
},
|
||||
StageSection::Charge => {
|
||||
if (self.static_data.infinite_charge
|
||||
|| self.timer < self.static_data.charge_duration)
|
||||
if self.timer < self.charge_end_timer
|
||||
&& (input_is_pressed(data, self.static_data.ability_info.input)
|
||||
|| (self.auto_charge && self.timer < self.static_data.charge_duration))
|
||||
&& update.energy.current() > 0
|
||||
@ -120,8 +118,7 @@ impl CharacterBehavior for Data {
|
||||
// This logic basically just decides if a charge should end, and prevents the
|
||||
// character state spamming attacks while checking if it has hit something
|
||||
if !self.exhausted {
|
||||
// Hit attempt (also checks if player is moving)
|
||||
if update.vel.0.distance_squared(Vec3::zero()) > 1.0 {
|
||||
// Hit attempt
|
||||
let poise = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Poise(
|
||||
@ -175,7 +172,6 @@ impl CharacterBehavior for Data {
|
||||
})
|
||||
.filter(|(_, tool)| tool == &Some(ToolKind::Pick)),
|
||||
});
|
||||
}
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
@ -185,56 +181,68 @@ impl CharacterBehavior for Data {
|
||||
..*self
|
||||
})
|
||||
} else if let Some(melee) = data.melee_attack {
|
||||
// Creates timer ahead of time so xmac can look at line count
|
||||
let timer = self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default();
|
||||
// If melee attack has not applied yet, tick both duration and dsitance
|
||||
if !melee.applied {
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer,
|
||||
refresh_distance: self.refresh_distance
|
||||
+ data.dt.0 * data.vel.0.magnitude(),
|
||||
..*self
|
||||
})
|
||||
// If melee attack has applied, but hit nothing, remove
|
||||
// exhausted so it can attack again
|
||||
} else if melee.hit_count == 0 {
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer,
|
||||
refresh_distance: 0.0,
|
||||
exhausted: false,
|
||||
..*self
|
||||
})
|
||||
// Else, melee attack applied and hit something, enter
|
||||
// cooldown
|
||||
} else if self.refresh_distance < self.static_data.range {
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer,
|
||||
refresh_distance: self.refresh_distance
|
||||
+ data.dt.0 * data.vel.0.magnitude(),
|
||||
..*self
|
||||
})
|
||||
// Else cooldown has finished, remove exhausted
|
||||
} else {
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer,
|
||||
refresh_distance: 0.0,
|
||||
exhausted: false,
|
||||
..*self
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// If melee attack has not applied, just tick duration
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
..*self
|
||||
});
|
||||
} else if melee.hit_count == 0 {
|
||||
// If melee attack has applied, but not hit anything, remove exhausted
|
||||
// so it can attack again
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
refresh_distance: 0.0,
|
||||
exhausted: false,
|
||||
..*self
|
||||
})
|
||||
});
|
||||
} else if self.static_data.charge_through {
|
||||
// If can charge through, set charge_end_timer to stop after a little
|
||||
// more time
|
||||
let charge_end_timer =
|
||||
if self.charge_end_timer != self.static_data.charge_duration {
|
||||
self.charge_end_timer
|
||||
} else {
|
||||
self.timer
|
||||
.checked_add(Duration::from_secs_f32(
|
||||
0.2 * self.static_data.range
|
||||
/ self.static_data.forward_speed,
|
||||
))
|
||||
.unwrap_or(self.static_data.charge_duration)
|
||||
.min(self.static_data.charge_duration)
|
||||
};
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
charge_end_timer,
|
||||
..*self
|
||||
});
|
||||
} else {
|
||||
// Stop charging now and go to swing stage section
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Swing,
|
||||
exhausted: false,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// If melee attack has not applied, just tick duration
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
exhausted: false,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
|
||||
// Consumes energy if there's enough left and charge has not stopped
|
||||
@ -247,12 +255,82 @@ impl CharacterBehavior for Data {
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: Duration::default(),
|
||||
stage_section: StageSection::Swing,
|
||||
exhausted: false,
|
||||
..*self
|
||||
});
|
||||
}
|
||||
},
|
||||
StageSection::Swing => {
|
||||
if self.timer < self.static_data.swing_duration {
|
||||
if self.static_data.charge_through && !self.exhausted {
|
||||
// If can charge through and not exhausted, do one more melee attack
|
||||
|
||||
// Assumes charge got to charge_end_timer for damage calculations
|
||||
let charge_frac = (self.charge_end_timer.as_secs_f32()
|
||||
/ self.static_data.charge_duration.as_secs_f32())
|
||||
.min(1.0);
|
||||
|
||||
let poise = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Poise(
|
||||
self.static_data.base_poise_damage as f32
|
||||
+ charge_frac * self.static_data.scaled_poise_damage as f32,
|
||||
),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let knockback = AttackEffect::new(
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
CombatEffect::Knockback(Knockback {
|
||||
strength: self.static_data.base_knockback
|
||||
+ charge_frac * self.static_data.scaled_knockback,
|
||||
direction: KnockbackDir::Away,
|
||||
}),
|
||||
)
|
||||
.with_requirement(CombatRequirement::AnyDamage);
|
||||
let buff = CombatEffect::Buff(CombatBuff::default_physical());
|
||||
let damage = AttackDamage::new(
|
||||
Damage {
|
||||
source: DamageSource::Melee,
|
||||
value: self.static_data.base_damage as f32
|
||||
+ charge_frac * self.static_data.scaled_damage as f32,
|
||||
},
|
||||
Some(GroupTarget::OutOfGroup),
|
||||
)
|
||||
.with_effect(buff);
|
||||
let (crit_chance, crit_mult) =
|
||||
get_crit_data(data, self.static_data.ability_info);
|
||||
let attack = Attack::default()
|
||||
.with_damage(damage)
|
||||
.with_crit(crit_chance, crit_mult)
|
||||
.with_effect(poise)
|
||||
.with_effect(knockback)
|
||||
.with_combo_increment();
|
||||
|
||||
data.updater.insert(data.entity, Melee {
|
||||
attack,
|
||||
range: self.static_data.range,
|
||||
max_angle: self.static_data.angle.to_radians(),
|
||||
applied: false,
|
||||
hit_count: 0,
|
||||
break_block: data
|
||||
.inputs
|
||||
.select_pos
|
||||
.map(|p| {
|
||||
(
|
||||
p.map(|e| e.floor() as i32),
|
||||
self.static_data.ability_info.tool,
|
||||
)
|
||||
})
|
||||
.filter(|(_, tool)| tool == &Some(ToolKind::Pick)),
|
||||
});
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
.timer
|
||||
.checked_add(Duration::from_secs_f32(data.dt.0))
|
||||
.unwrap_or_default(),
|
||||
exhausted: true,
|
||||
..*self
|
||||
})
|
||||
} else if self.timer < self.static_data.swing_duration {
|
||||
// Swings
|
||||
update.character = CharacterState::DashMelee(Data {
|
||||
timer: self
|
||||
|
@ -1387,7 +1387,7 @@ impl<'a> Widget for Diary<'a> {
|
||||
};
|
||||
let skill = Skill::Sword(DInfinite);
|
||||
if create_skill_button(
|
||||
self.imgs.physical_infinite_skill,
|
||||
self.imgs.physical_distance_skill,
|
||||
state.skills_top_r[5],
|
||||
&self.skill_set,
|
||||
skill,
|
||||
@ -1396,9 +1396,13 @@ impl<'a> Widget for Diary<'a> {
|
||||
)
|
||||
.with_tooltip(
|
||||
self.tooltip_manager,
|
||||
&self.localized_strings.get("hud.skill.sw_dash_inf_title"),
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.skill.sw_dash_charge_through_title"),
|
||||
&add_sp_cost_tooltip(
|
||||
&self.localized_strings.get("hud.skill.sw_dash_inf"),
|
||||
&self
|
||||
.localized_strings
|
||||
.get("hud.skill.sw_dash_charge_through"),
|
||||
skill,
|
||||
&self.skill_set,
|
||||
&self.localized_strings,
|
||||
|
Loading…
Reference in New Issue
Block a user