mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Dive melee now scales its attack off of the entity's vertical speed.
Parries now cause the attacker to effectively have a recover that is either twice as long or 0.5s longer, whichever is more. Counters now deal twice as much damage to the target if the target is in the buildup portion of an ability.
This commit is contained in:
parent
356c26cc66
commit
a8212d6f41
@ -6,11 +6,17 @@ DiveMelee(
|
|||||||
recover_duration: 0.3,
|
recover_duration: 0.3,
|
||||||
melee_constructor: (
|
melee_constructor: (
|
||||||
kind: Slash(
|
kind: Slash(
|
||||||
damage: 30,
|
damage: 20,
|
||||||
poise: 0,
|
poise: 0,
|
||||||
knockback: 0,
|
knockback: 0,
|
||||||
energy_regen: 10,
|
energy_regen: 10,
|
||||||
),
|
),
|
||||||
|
scaled: Some(Slash(
|
||||||
|
damage: 10,
|
||||||
|
poise: 0,
|
||||||
|
knockback: 0,
|
||||||
|
energy_regen: 10,
|
||||||
|
)),
|
||||||
range: 6.0,
|
range: 6.0,
|
||||||
angle: 15.0,
|
angle: 15.0,
|
||||||
multi_target: true,
|
multi_target: true,
|
||||||
|
@ -10,6 +10,7 @@ ComboMelee2(
|
|||||||
),
|
),
|
||||||
range: 6.0,
|
range: 6.0,
|
||||||
angle: 5.0,
|
angle: 5.0,
|
||||||
|
damage_effect: Some(BuildupsVulnerable),
|
||||||
),
|
),
|
||||||
buildup_duration: 0.05,
|
buildup_duration: 0.05,
|
||||||
swing_duration: 0.1,
|
swing_duration: 0.1,
|
||||||
|
@ -64,6 +64,9 @@ buff-desc-ensnared = Vines grasp at your legs, impeding your movement.
|
|||||||
## Fortitude
|
## Fortitude
|
||||||
buff-title-fortitude = Fortitude
|
buff-title-fortitude = Fortitude
|
||||||
buff-desc-fortitude = You can withstand staggers.
|
buff-desc-fortitude = You can withstand staggers.
|
||||||
|
## Parried
|
||||||
|
buff-title-parried = Parried
|
||||||
|
buff-desc-parried = You were parried and now are slow to recover.
|
||||||
## Util
|
## Util
|
||||||
buff-text-over_seconds = over { $dur_secs } seconds
|
buff-text-over_seconds = over { $dur_secs } seconds
|
||||||
buff-text-for_seconds = for { $dur_secs } seconds
|
buff-text-for_seconds = for { $dur_secs } seconds
|
||||||
|
@ -154,6 +154,7 @@ lazy_static! {
|
|||||||
BuffKind::Poisoned => "poisoned",
|
BuffKind::Poisoned => "poisoned",
|
||||||
BuffKind::Hastened => "hastened",
|
BuffKind::Hastened => "hastened",
|
||||||
BuffKind::Fortitude => "fortitude",
|
BuffKind::Fortitude => "fortitude",
|
||||||
|
BuffKind::Parried => "parried",
|
||||||
};
|
};
|
||||||
let mut buff_parser = HashMap::new();
|
let mut buff_parser = HashMap::new();
|
||||||
for kind in BuffKind::iter() {
|
for kind in BuffKind::iter() {
|
||||||
|
@ -12,6 +12,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
|
states::utils::StageSection,
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
@ -441,6 +442,16 @@ impl Attack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
CombatEffect::BuildupsVulnerable => {
|
||||||
|
if target.char_state.map_or(false, |cs| {
|
||||||
|
matches!(cs.stage_section(), Some(StageSection::Buildup))
|
||||||
|
}) {
|
||||||
|
emit(ServerEvent::HealthChange {
|
||||||
|
entity: target.entity,
|
||||||
|
change,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -605,6 +616,8 @@ impl Attack {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Only has an effect when attached to a damage
|
||||||
|
CombatEffect::BuildupsVulnerable => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -738,6 +751,11 @@ pub enum CombatEffect {
|
|||||||
Combo(i32),
|
Combo(i32),
|
||||||
// If the attack kills the target, reset the melee attack
|
// If the attack kills the target, reset the melee attack
|
||||||
ResetMelee,
|
ResetMelee,
|
||||||
|
// If the attack hits the target while they are in the buildup portion of a character state,
|
||||||
|
// deal double damage Only has an effect when attached to a damage, otherwise does nothing
|
||||||
|
// if only attached to the attack TODO: Maybe try to make it do something if tied to
|
||||||
|
// attack, not sure if it should double count in that instance?
|
||||||
|
BuildupsVulnerable,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -2507,19 +2507,21 @@ impl From<(&CharacterAbility, AbilityInfo, &JoinData<'_>)> for CharacterState {
|
|||||||
recover_duration,
|
recover_duration,
|
||||||
melee_constructor,
|
melee_constructor,
|
||||||
energy_cost: _,
|
energy_cost: _,
|
||||||
vertical_speed: _,
|
vertical_speed,
|
||||||
meta: _,
|
meta: _,
|
||||||
} => CharacterState::DiveMelee(dive_melee::Data {
|
} => CharacterState::DiveMelee(dive_melee::Data {
|
||||||
static_data: dive_melee::StaticData {
|
static_data: dive_melee::StaticData {
|
||||||
movement_duration: Duration::from_secs_f32(*movement_duration),
|
movement_duration: Duration::from_secs_f32(*movement_duration),
|
||||||
swing_duration: Duration::from_secs_f32(*swing_duration),
|
swing_duration: Duration::from_secs_f32(*swing_duration),
|
||||||
recover_duration: Duration::from_secs_f32(*recover_duration),
|
recover_duration: Duration::from_secs_f32(*recover_duration),
|
||||||
|
vertical_speed: *vertical_speed,
|
||||||
melee_constructor: *melee_constructor,
|
melee_constructor: *melee_constructor,
|
||||||
ability_info,
|
ability_info,
|
||||||
},
|
},
|
||||||
timer: Duration::default(),
|
timer: Duration::default(),
|
||||||
stage_section: StageSection::Movement,
|
stage_section: StageSection::Movement,
|
||||||
exhausted: false,
|
exhausted: false,
|
||||||
|
max_vertical_speed: 0.0,
|
||||||
}),
|
}),
|
||||||
CharacterAbility::RiposteMelee {
|
CharacterAbility::RiposteMelee {
|
||||||
energy_cost: _,
|
energy_cost: _,
|
||||||
@ -2598,9 +2600,13 @@ pub enum SwordStance {
|
|||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
#[derive(Default, Serialize, Deserialize)]
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
pub struct Capability: u8 {
|
pub struct Capability: u8 {
|
||||||
|
// Allows rolls to interrupt the ability at any point, not just during buildup
|
||||||
const ROLL_INTERRUPT = 0b00000001;
|
const ROLL_INTERRUPT = 0b00000001;
|
||||||
|
// Allows blocking to interrupt the ability at any point
|
||||||
const BLOCK_INTERRUPT = 0b00000010;
|
const BLOCK_INTERRUPT = 0b00000010;
|
||||||
|
// When the ability is in the buyildup section, it counts as a parry
|
||||||
const BUILDUP_PARRIES = 0b00000100;
|
const BUILDUP_PARRIES = 0b00000100;
|
||||||
|
// When in the ability, an entity only receives half as much poise damage
|
||||||
const POISE_RESISTANT = 0b00001000;
|
const POISE_RESISTANT = 0b00001000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,10 @@ pub enum BuffKind {
|
|||||||
/// Drain stamina to a creature over time
|
/// Drain stamina to a creature over time
|
||||||
/// Strength should be the energy per second of the debuff
|
/// Strength should be the energy per second of the debuff
|
||||||
Poisoned,
|
Poisoned,
|
||||||
|
/// Results from having an attack parried.
|
||||||
|
/// Causes your attack speed to be slower to emulate the recover duration of
|
||||||
|
/// an ability being lengthened.
|
||||||
|
Parried,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -113,7 +117,8 @@ impl BuffKind {
|
|||||||
| BuffKind::Frozen
|
| BuffKind::Frozen
|
||||||
| BuffKind::Wet
|
| BuffKind::Wet
|
||||||
| BuffKind::Ensnared
|
| BuffKind::Ensnared
|
||||||
| BuffKind::Poisoned => false,
|
| BuffKind::Poisoned
|
||||||
|
| BuffKind::Parried => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,6 +395,7 @@ impl Buff {
|
|||||||
vec![BuffEffect::PoiseReduction(data.strength)],
|
vec![BuffEffect::PoiseReduction(data.strength)],
|
||||||
data.duration,
|
data.duration,
|
||||||
),
|
),
|
||||||
|
BuffKind::Parried => (vec![BuffEffect::AttackSpeed(0.5)], data.duration),
|
||||||
};
|
};
|
||||||
Buff {
|
Buff {
|
||||||
kind,
|
kind,
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage};
|
use specs::{Component, DerefFlaggedStorage};
|
||||||
use std::collections::BTreeMap;
|
use std::{collections::BTreeMap, time::Duration};
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
|
|
||||||
/// Data returned from character behavior fn's to Character Behavior System.
|
/// Data returned from character behavior fn's to Character Behavior System.
|
||||||
@ -571,6 +571,199 @@ impl CharacterState {
|
|||||||
CharacterState::RapidMelee(data) => Some(data.stage_section),
|
CharacterState::RapidMelee(data) => Some(data.stage_section),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn durations(&self) -> Option<DurationsInfo> {
|
||||||
|
match &self {
|
||||||
|
CharacterState::Idle(_) => None,
|
||||||
|
CharacterState::Talk => None,
|
||||||
|
CharacterState::Climb(_) => None,
|
||||||
|
CharacterState::Wallrun(_) => None,
|
||||||
|
CharacterState::Skate(_) => None,
|
||||||
|
CharacterState::Glide(_) => None,
|
||||||
|
CharacterState::GlideWield(_) => None,
|
||||||
|
CharacterState::Stunned(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Sit => None,
|
||||||
|
CharacterState::Dance => None,
|
||||||
|
CharacterState::BasicBlock(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Roll(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
movement: Some(data.static_data.movement_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Wielding(_) => None,
|
||||||
|
CharacterState::Equipping(_) => None,
|
||||||
|
CharacterState::ComboMelee(data) => {
|
||||||
|
let stage_index = data.stage_index();
|
||||||
|
let stage = data.static_data.stage_data[stage_index];
|
||||||
|
Some(DurationsInfo {
|
||||||
|
buildup: Some(stage.base_buildup_duration),
|
||||||
|
action: Some(stage.base_swing_duration),
|
||||||
|
recover: Some(stage.base_recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
CharacterState::ComboMelee2(data) => {
|
||||||
|
let strike = data.strike_data();
|
||||||
|
Some(DurationsInfo {
|
||||||
|
buildup: Some(strike.buildup_duration),
|
||||||
|
action: Some(strike.swing_duration),
|
||||||
|
recover: Some(strike.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
CharacterState::BasicMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::BasicRanged(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Boost(data) => Some(DurationsInfo {
|
||||||
|
movement: Some(data.static_data.movement_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::DashMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
charge: Some(data.static_data.charge_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::LeapMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
movement: Some(data.static_data.movement_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::SpinMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::ChargedMelee(data) => Some(DurationsInfo {
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
charge: Some(data.static_data.charge_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::ChargedRanged(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
charge: Some(data.static_data.charge_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::RepeaterRanged(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.shoot_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Shockwave(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::BasicBeam(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::BasicAura(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.cast_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Blink(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::BasicSummon(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.cast_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::SelfBuff(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.cast_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::SpriteSummon(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.cast_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::UseItem(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.use_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::SpriteInteract(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.use_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::FinisherMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::Music(data) => Some(DurationsInfo {
|
||||||
|
action: Some(data.static_data.play_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::DiveMelee(data) => Some(DurationsInfo {
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
movement: Some(data.static_data.movement_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::RiposteMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
CharacterState::RapidMelee(data) => Some(DurationsInfo {
|
||||||
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
|
action: Some(data.static_data.swing_duration),
|
||||||
|
recover: Some(data.static_data.recover_duration),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct DurationsInfo {
|
||||||
|
pub buildup: Option<Duration>,
|
||||||
|
pub action: Option<Duration>,
|
||||||
|
pub recover: Option<Duration>,
|
||||||
|
pub movement: Option<Duration>,
|
||||||
|
pub charge: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CharacterState {
|
impl Default for CharacterState {
|
||||||
|
@ -162,14 +162,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
handle_move(data, &mut update, 0.4);
|
handle_move(data, &mut update, 0.4);
|
||||||
|
|
||||||
// Index should be `self.stage - 1`, however in cases of client-server desync
|
let stage_index = self.stage_index();
|
||||||
// this can cause panics. This ensures that `self.stage - 1` is valid, and if it
|
|
||||||
// isn't, index of 0 is used, which is always safe.
|
|
||||||
let stage_index = self
|
|
||||||
.static_data
|
|
||||||
.stage_data
|
|
||||||
.get(self.stage as usize - 1)
|
|
||||||
.map_or(0, |_| self.stage as usize - 1);
|
|
||||||
|
|
||||||
let speed_modifier = 1.0
|
let speed_modifier = 1.0
|
||||||
+ self.static_data.max_speed_increase
|
+ self.static_data.max_speed_increase
|
||||||
@ -372,6 +365,19 @@ impl CharacterBehavior for Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
/// Index should be `self.stage - 1`, however in cases of client-server desync
|
||||||
|
/// this can cause panics. This ensures that `self.stage - 1` is valid, and if it
|
||||||
|
/// isn't, index of 0 is used, which is always safe.
|
||||||
|
pub fn stage_index(&self) -> usize {
|
||||||
|
self
|
||||||
|
.static_data
|
||||||
|
.stage_data
|
||||||
|
.get(self.stage as usize - 1)
|
||||||
|
.map_or(0, |_| self.stage as usize - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn reset_state(
|
fn reset_state(
|
||||||
data: &Data,
|
data: &Data,
|
||||||
join: &JoinData,
|
join: &JoinData,
|
||||||
|
@ -122,8 +122,7 @@ impl CharacterBehavior for Data {
|
|||||||
handle_move(data, &mut update, 0.7);
|
handle_move(data, &mut update, 0.7);
|
||||||
handle_interrupts(data, &mut update, Some(ability_input));
|
handle_interrupts(data, &mut update, Some(ability_input));
|
||||||
|
|
||||||
let strike_data =
|
let strike_data = self.strike_data();
|
||||||
self.static_data.strikes[self.completed_strikes % self.static_data.strikes.len()];
|
|
||||||
|
|
||||||
match self.stage_section {
|
match self.stage_section {
|
||||||
Some(StageSection::Buildup) => {
|
Some(StageSection::Buildup) => {
|
||||||
@ -149,10 +148,8 @@ impl CharacterBehavior for Data {
|
|||||||
}
|
}
|
||||||
if input_is_pressed(data, ability_input) {
|
if input_is_pressed(data, ability_input) {
|
||||||
if let CharacterState::ComboMelee2(c) = &mut update.character {
|
if let CharacterState::ComboMelee2(c) = &mut update.character {
|
||||||
// Only allow next strike if attack is a stance or has multiple strikes that
|
// Only have the next strike skip the recover period of this strike if not every strike in the combo is complete yet
|
||||||
// have not finished yet
|
c.start_next_strike = (c.completed_strikes + 1) < c.static_data.strikes.len();
|
||||||
c.start_next_strike = c.static_data.is_stance
|
|
||||||
|| (c.completed_strikes + 1) < c.static_data.strikes.len();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.timer.as_secs_f32()
|
if self.timer.as_secs_f32()
|
||||||
@ -251,6 +248,12 @@ impl CharacterBehavior for Data {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn strike_data(&self) -> &Strike<Duration> {
|
||||||
|
&self.static_data.strikes[self.completed_strikes % self.static_data.strikes.len()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn next_strike(update: &mut StateUpdate) {
|
fn next_strike(update: &mut StateUpdate) {
|
||||||
if let CharacterState::ComboMelee2(c) = &mut update.character {
|
if let CharacterState::ComboMelee2(c) = &mut update.character {
|
||||||
if update
|
if update
|
||||||
|
@ -18,6 +18,8 @@ pub struct StaticData {
|
|||||||
pub swing_duration: Duration,
|
pub swing_duration: Duration,
|
||||||
/// How long the state has until exiting
|
/// How long the state has until exiting
|
||||||
pub recover_duration: Duration,
|
pub recover_duration: Duration,
|
||||||
|
/// The minimum vertical speed the state needed
|
||||||
|
pub vertical_speed: f32,
|
||||||
/// Used to construct the Melee attack
|
/// Used to construct the Melee attack
|
||||||
pub melee_constructor: MeleeConstructor,
|
pub melee_constructor: MeleeConstructor,
|
||||||
/// What key is used to press ability
|
/// What key is used to press ability
|
||||||
@ -35,12 +37,18 @@ pub struct Data {
|
|||||||
pub stage_section: StageSection,
|
pub stage_section: StageSection,
|
||||||
/// Whether the attack can deal more damage
|
/// Whether the attack can deal more damage
|
||||||
pub exhausted: bool,
|
pub exhausted: bool,
|
||||||
|
/// The maximum negative vertical velocity achieved during the state
|
||||||
|
pub max_vertical_speed: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterBehavior for Data {
|
impl CharacterBehavior for Data {
|
||||||
fn behavior(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
fn behavior(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
if let CharacterState::DiveMelee(c) = &mut update.character {
|
||||||
|
c.max_vertical_speed = c.max_vertical_speed.max(-data.vel.0.z);
|
||||||
|
}
|
||||||
|
|
||||||
match self.stage_section {
|
match self.stage_section {
|
||||||
StageSection::Movement => {
|
StageSection::Movement => {
|
||||||
if data.physics.on_ground.is_some() {
|
if data.physics.on_ground.is_some() {
|
||||||
@ -67,11 +75,13 @@ impl CharacterBehavior for Data {
|
|||||||
// Attack
|
// Attack
|
||||||
let crit_data = get_crit_data(data, self.static_data.ability_info);
|
let crit_data = get_crit_data(data, self.static_data.ability_info);
|
||||||
let buff_strength = get_buff_strength(data, self.static_data.ability_info);
|
let buff_strength = get_buff_strength(data, self.static_data.ability_info);
|
||||||
|
let scaling = self.max_vertical_speed / self.static_data.vertical_speed;
|
||||||
|
|
||||||
data.updater.insert(
|
data.updater.insert(
|
||||||
data.entity,
|
data.entity,
|
||||||
self.static_data
|
self.static_data
|
||||||
.melee_constructor
|
.melee_constructor
|
||||||
|
.handle_scaling(scaling)
|
||||||
.create_melee(crit_data, buff_strength),
|
.create_melee(crit_data, buff_strength),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ use rand_distr::Distribution;
|
|||||||
use specs::{
|
use specs::{
|
||||||
join::Join, saveload::MarkerAllocator, Builder, Entity as EcsEntity, Entity, WorldExt,
|
join::Join, saveload::MarkerAllocator, Builder, Entity as EcsEntity, Entity, WorldExt,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, iter, time::Duration};
|
use std::{collections::HashMap, iter, ops::Mul, time::Duration};
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
use vek::{Vec2, Vec3};
|
use vek::{Vec2, Vec3};
|
||||||
|
|
||||||
@ -1224,8 +1224,9 @@ pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_parry_hook(server: &Server, defender: EcsEntity, _attacker: Option<EcsEntity>) {
|
pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<EcsEntity>) {
|
||||||
let ecs = &server.state.ecs();
|
let ecs = &server.state.ecs();
|
||||||
|
let server_eventbus = ecs.read_resource::<EventBus<ServerEvent>>();
|
||||||
// Reset character state of defender
|
// Reset character state of defender
|
||||||
if let Some(mut char_state) = ecs
|
if let Some(mut char_state) = ecs
|
||||||
.write_storage::<comp::CharacterState>()
|
.write_storage::<comp::CharacterState>()
|
||||||
@ -1247,7 +1248,36 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, _attacker: Option
|
|||||||
const PARRY_REWARD: f32 = 5.0;
|
const PARRY_REWARD: f32 = 5.0;
|
||||||
energy.change_by(PARRY_REWARD);
|
energy.change_by(PARRY_REWARD);
|
||||||
}
|
}
|
||||||
// TODO: Some penalties for attacker
|
|
||||||
|
if let Some(attacker) = attacker {
|
||||||
|
if let Some(char_state) = ecs.read_storage::<comp::CharacterState>().get(attacker) {
|
||||||
|
// By having a duration twice as long as either the recovery duration or 0.5 s,
|
||||||
|
// causes recover duration to effectively be either doubled or increased by 0.5
|
||||||
|
// s when a buff is applied that halves their attack speed.
|
||||||
|
let duration = char_state
|
||||||
|
.durations()
|
||||||
|
.and_then(|durs| durs.recover)
|
||||||
|
.map_or(0.5, |dur| dur.as_secs_f32())
|
||||||
|
.max(0.5)
|
||||||
|
.mul(2.0);
|
||||||
|
let data = buff::BuffData::new(1.0, Some(Duration::from_secs_f32(duration)));
|
||||||
|
let source = if let Some(uid) = ecs.read_storage::<Uid>().get(defender) {
|
||||||
|
BuffSource::Character { by: *uid }
|
||||||
|
} else {
|
||||||
|
BuffSource::World
|
||||||
|
};
|
||||||
|
let buff = buff::Buff::new(
|
||||||
|
BuffKind::Parried,
|
||||||
|
data,
|
||||||
|
vec![buff::BuffCategory::Physical],
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
server_eventbus.emit_now(ServerEvent::Buff {
|
||||||
|
entity: attacker,
|
||||||
|
buff_change: buff::BuffChange::Add(buff),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_teleport_to(server: &Server, entity: EcsEntity, target: Uid, max_range: Option<f32>) {
|
pub fn handle_teleport_to(server: &Server, entity: EcsEntity, target: Uid, max_range: Option<f32>) {
|
||||||
|
@ -106,7 +106,7 @@ pub fn localize_chat_message(
|
|||||||
tracing::error!("Player was killed by a positive buff!");
|
tracing::error!("Player was killed by a positive buff!");
|
||||||
"hud-outcome-mysterious"
|
"hud-outcome-mysterious"
|
||||||
},
|
},
|
||||||
BuffKind::Wet | BuffKind::Ensnared | BuffKind::Poisoned => {
|
BuffKind::Wet | BuffKind::Ensnared | BuffKind::Poisoned | BuffKind::Parried => {
|
||||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||||
"hud-outcome-mysterious"
|
"hud-outcome-mysterious"
|
||||||
},
|
},
|
||||||
|
@ -4621,6 +4621,8 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
|||||||
BuffKind::Wet => imgs.debuff_wet_0,
|
BuffKind::Wet => imgs.debuff_wet_0,
|
||||||
BuffKind::Ensnared => imgs.debuff_ensnared_0,
|
BuffKind::Ensnared => imgs.debuff_ensnared_0,
|
||||||
BuffKind::Poisoned => imgs.debuff_poisoned_0,
|
BuffKind::Poisoned => imgs.debuff_poisoned_0,
|
||||||
|
// TODO: Get unique icon
|
||||||
|
BuffKind::Parried => imgs.debuff_crippled_0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4652,6 +4654,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
|
|||||||
BuffKind::Wet { .. } => localized_strings.get_msg("buff-title-wet"),
|
BuffKind::Wet { .. } => localized_strings.get_msg("buff-title-wet"),
|
||||||
BuffKind::Ensnared { .. } => localized_strings.get_msg("buff-title-ensnared"),
|
BuffKind::Ensnared { .. } => localized_strings.get_msg("buff-title-ensnared"),
|
||||||
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-title-poisoned"),
|
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-title-poisoned"),
|
||||||
|
BuffKind::Parried { .. } => localized_strings.get_msg("buff-title-parried"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4687,6 +4690,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
|
|||||||
BuffKind::Wet { .. } => localized_strings.get_msg("buff-desc-wet"),
|
BuffKind::Wet { .. } => localized_strings.get_msg("buff-desc-wet"),
|
||||||
BuffKind::Ensnared { .. } => localized_strings.get_msg("buff-desc-ensnared"),
|
BuffKind::Ensnared { .. } => localized_strings.get_msg("buff-desc-ensnared"),
|
||||||
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-desc-poisoned"),
|
BuffKind::Poisoned { .. } => localized_strings.get_msg("buff-desc-poisoned"),
|
||||||
|
BuffKind::Parried { .. } => localized_strings.get_msg("buff-desc-parried"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Ensnared
|
| BuffKind::Ensnared
|
||||||
| BuffKind::Poisoned
|
| BuffKind::Poisoned
|
||||||
| BuffKind::Hastened
|
| BuffKind::Hastened
|
||||||
| BuffKind::Fortitude => Cow::Borrowed(""),
|
| BuffKind::Fortitude
|
||||||
|
| BuffKind::Parried => Cow::Borrowed(""),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(&mut description, "{}", buff_desc).unwrap();
|
write!(&mut description, "{}", buff_desc).unwrap();
|
||||||
@ -215,7 +216,8 @@ pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Ensnared
|
| BuffKind::Ensnared
|
||||||
| BuffKind::Poisoned
|
| BuffKind::Poisoned
|
||||||
| BuffKind::Hastened
|
| BuffKind::Hastened
|
||||||
| BuffKind::Fortitude => Cow::Borrowed(""),
|
| BuffKind::Fortitude
|
||||||
|
| BuffKind::Parried => Cow::Borrowed(""),
|
||||||
}
|
}
|
||||||
} else if let BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen =
|
} else if let BuffKind::Saturation | BuffKind::Regeneration | BuffKind::EnergyRegen =
|
||||||
buff.kind
|
buff.kind
|
||||||
|
Loading…
Reference in New Issue
Block a user