mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Diminishing returns for certain crowd control debuffs
This commit is contained in:
parent
f5fc2294a6
commit
07220dfee8
BIN
assets/voxygen/element/de_buffs/buff_resilience.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/buff_resilience.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -139,6 +139,9 @@ buff-staggered = Staggered
|
|||||||
## Tenacity
|
## Tenacity
|
||||||
buff-tenacity = Tenacity
|
buff-tenacity = Tenacity
|
||||||
.desc = You are not only able to shrug off heavier attacks, they energize you as well. However you are also slower.
|
.desc = You are not only able to shrug off heavier attacks, they energize you as well. However you are also slower.
|
||||||
|
## Resilience
|
||||||
|
buff-resilience = Resilience
|
||||||
|
.desc = After having just taken a debilitating attack, you become more resilient to future incapaciting effects.
|
||||||
## 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
|
||||||
|
@ -195,6 +195,7 @@ lazy_static! {
|
|||||||
BuffKind::Concussion => "concussion",
|
BuffKind::Concussion => "concussion",
|
||||||
BuffKind::Staggered => "staggered",
|
BuffKind::Staggered => "staggered",
|
||||||
BuffKind::Tenacity => "tenacity",
|
BuffKind::Tenacity => "tenacity",
|
||||||
|
BuffKind::Resilience => "resilience",
|
||||||
};
|
};
|
||||||
let mut buff_parser = HashMap::new();
|
let mut buff_parser = HashMap::new();
|
||||||
for kind in BuffKind::iter() {
|
for kind in BuffKind::iter() {
|
||||||
|
@ -141,6 +141,12 @@ pub enum BuffKind {
|
|||||||
/// with strength, 1.0 is 10 energy per hit. Movement speed is decreased to
|
/// with strength, 1.0 is 10 energy per hit. Movement speed is decreased to
|
||||||
/// 70%.
|
/// 70%.
|
||||||
Tenacity,
|
Tenacity,
|
||||||
|
/// Applies to some debuffs that have strong CC effects. Automatically
|
||||||
|
/// gained upon receiving those debuffs, and causes future instances of
|
||||||
|
/// those debuffs to be applied with reduced duration.
|
||||||
|
/// Strength linearly decreases the duration of newly applied, affected
|
||||||
|
/// debuffs, 0.5 is a 50% reduction.
|
||||||
|
Resilience,
|
||||||
// =================
|
// =================
|
||||||
// DEBUFFS
|
// DEBUFFS
|
||||||
// =================
|
// =================
|
||||||
@ -261,7 +267,8 @@ impl BuffKind {
|
|||||||
| BuffKind::Bloodfeast
|
| BuffKind::Bloodfeast
|
||||||
| BuffKind::Berserk
|
| BuffKind::Berserk
|
||||||
| BuffKind::ScornfulTaunt
|
| BuffKind::ScornfulTaunt
|
||||||
| BuffKind::Tenacity => BuffDescriptor::SimplePositive,
|
| BuffKind::Tenacity
|
||||||
|
| BuffKind::Resilience => BuffDescriptor::SimplePositive,
|
||||||
BuffKind::Bleeding
|
BuffKind::Bleeding
|
||||||
| BuffKind::Cursed
|
| BuffKind::Cursed
|
||||||
| BuffKind::Burning
|
| BuffKind::Burning
|
||||||
@ -310,7 +317,7 @@ impl BuffKind {
|
|||||||
|
|
||||||
/// Checks if multiple instances of the buff should be processed, instead of
|
/// Checks if multiple instances of the buff should be processed, instead of
|
||||||
/// only the strongest.
|
/// only the strongest.
|
||||||
pub fn stacks(self) -> bool { matches!(self, BuffKind::PotionSickness) }
|
pub fn stacks(self) -> bool { matches!(self, BuffKind::PotionSickness | BuffKind::Resilience) }
|
||||||
|
|
||||||
pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec<BuffEffect> {
|
pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec<BuffEffect> {
|
||||||
// Normalized nonlinear scaling
|
// Normalized nonlinear scaling
|
||||||
@ -531,6 +538,7 @@ impl BuffKind {
|
|||||||
BuffEffect::MovementSpeed(0.7),
|
BuffEffect::MovementSpeed(0.7),
|
||||||
BuffEffect::DamagedEffect(DamagedEffect::Energy(data.strength * 10.0)),
|
BuffEffect::DamagedEffect(DamagedEffect::Energy(data.strength * 10.0)),
|
||||||
],
|
],
|
||||||
|
BuffKind::Resilience => vec![BuffEffect::CrowdControlResistance(data.strength)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,21 +558,37 @@ impl BuffKind {
|
|||||||
&self,
|
&self,
|
||||||
mut data: BuffData,
|
mut data: BuffData,
|
||||||
source_mass: Option<&Mass>,
|
source_mass: Option<&Mass>,
|
||||||
dest_mass: Option<&Mass>,
|
dest_info: DestInfo,
|
||||||
) -> BuffData {
|
) -> BuffData {
|
||||||
// TODO: Remove clippy allow after another buff needs this
|
// TODO: Remove clippy allow after another buff needs this
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match self {
|
match self {
|
||||||
BuffKind::Rooted => {
|
BuffKind::Rooted => {
|
||||||
let source_mass = source_mass.map_or(50.0, |m| m.0 as f64);
|
let source_mass = source_mass.map_or(50.0, |m| m.0 as f64);
|
||||||
let dest_mass = dest_mass.map_or(50.0, |m| m.0 as f64);
|
let dest_mass = dest_info.mass.map_or(50.0, |m| m.0 as f64);
|
||||||
let ratio = (source_mass / dest_mass).min(1.0);
|
let ratio = (source_mass / dest_mass).min(1.0);
|
||||||
data.duration = data.duration.map(|dur| Secs(dur.0 * ratio));
|
data.duration = data.duration.map(|dur| Secs(dur.0 * ratio));
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
if self.resilience_ccr_strength(data).is_some() {
|
||||||
|
let dur_mult = dest_info
|
||||||
|
.stats
|
||||||
|
.map_or(1.0, |s| (1.0 - s.crowd_control_resistance).max(0.0));
|
||||||
|
data.duration = data.duration.map(|dur| dur * dur_mult as f64);
|
||||||
|
}
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If a buff kind should also give resilience when applied, return the
|
||||||
|
/// strength that resilience should have, otherwise return None
|
||||||
|
pub fn resilience_ccr_strength(&self, data: BuffData) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
BuffKind::Concussion => Some(0.3),
|
||||||
|
BuffKind::Frozen => Some(data.strength),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Struct used to store data relevant to a buff
|
// Struct used to store data relevant to a buff
|
||||||
@ -704,6 +728,8 @@ pub enum BuffEffect {
|
|||||||
DeathEffect(DeathEffect),
|
DeathEffect(DeathEffect),
|
||||||
/// Prevents use of auxiliary abilities
|
/// Prevents use of auxiliary abilities
|
||||||
DisableAuxiliaryAbilities,
|
DisableAuxiliaryAbilities,
|
||||||
|
/// Reduces duration of crowd control debuffs
|
||||||
|
CrowdControlResistance(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actual de/buff.
|
/// Actual de/buff.
|
||||||
@ -763,7 +789,7 @@ impl Buff {
|
|||||||
// Create source_info if we need more parameters from source
|
// Create source_info if we need more parameters from source
|
||||||
source_mass: Option<&Mass>,
|
source_mass: Option<&Mass>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let data = kind.modify_data(data, source_mass, dest_info.mass);
|
let data = kind.modify_data(data, source_mass, dest_info);
|
||||||
let effects = kind.effects(&data, dest_info.stats);
|
let effects = kind.effects(&data, dest_info.stats);
|
||||||
let cat_ids = kind.extend_cat_ids(cat_ids);
|
let cat_ids = kind.extend_cat_ids(cat_ids);
|
||||||
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
||||||
|
@ -78,6 +78,7 @@ pub struct Stats {
|
|||||||
/// This creates effects when the entity is killed
|
/// This creates effects when the entity is killed
|
||||||
pub effects_on_death: Vec<DeathEffect>,
|
pub effects_on_death: Vec<DeathEffect>,
|
||||||
pub disable_auxiliary_abilities: bool,
|
pub disable_auxiliary_abilities: bool,
|
||||||
|
pub crowd_control_resistance: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
@ -105,6 +106,7 @@ impl Stats {
|
|||||||
effects_on_damaged: Vec::new(),
|
effects_on_damaged: Vec::new(),
|
||||||
effects_on_death: Vec::new(),
|
effects_on_death: Vec::new(),
|
||||||
disable_auxiliary_abilities: false,
|
disable_auxiliary_abilities: false,
|
||||||
|
crowd_control_resistance: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -817,5 +817,8 @@ fn execute_effect(
|
|||||||
BuffEffect::DamagedEffect(effect) => stat.effects_on_damaged.push(effect.clone()),
|
BuffEffect::DamagedEffect(effect) => stat.effects_on_damaged.push(effect.clone()),
|
||||||
BuffEffect::DeathEffect(effect) => stat.effects_on_death.push(effect.clone()),
|
BuffEffect::DeathEffect(effect) => stat.effects_on_death.push(effect.clone()),
|
||||||
BuffEffect::DisableAuxiliaryAbilities => stat.disable_auxiliary_abilities = true,
|
BuffEffect::DisableAuxiliaryAbilities => stat.disable_auxiliary_abilities = true,
|
||||||
|
BuffEffect::CrowdControlResistance(ccr) => {
|
||||||
|
stat.crowd_control_resistance += ccr;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4556,7 +4556,8 @@ fn build_buff(
|
|||||||
| BuffKind::Winded
|
| BuffKind::Winded
|
||||||
| BuffKind::Concussion
|
| BuffKind::Concussion
|
||||||
| BuffKind::Staggered
|
| BuffKind::Staggered
|
||||||
| BuffKind::Tenacity => {
|
| BuffKind::Tenacity
|
||||||
|
| BuffKind::Resilience => {
|
||||||
if buff_kind.is_simple() {
|
if buff_kind.is_simple() {
|
||||||
unreachable!("is_simple() above")
|
unreachable!("is_simple() above")
|
||||||
} else {
|
} else {
|
||||||
|
@ -1617,11 +1617,13 @@ impl ServerEvent for BuffEvent {
|
|||||||
WriteStorage<'a, comp::Buffs>,
|
WriteStorage<'a, comp::Buffs>,
|
||||||
ReadStorage<'a, Body>,
|
ReadStorage<'a, Body>,
|
||||||
ReadStorage<'a, Health>,
|
ReadStorage<'a, Health>,
|
||||||
|
ReadStorage<'a, Stats>,
|
||||||
|
ReadStorage<'a, comp::Mass>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
events: impl ExactSizeIterator<Item = Self>,
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
(time, mut buffs, bodies, healths): Self::SystemData<'_>,
|
(time, mut buffs, bodies, healths, stats, masses): Self::SystemData<'_>,
|
||||||
) {
|
) {
|
||||||
for ev in events {
|
for ev in events {
|
||||||
if let Some(mut buffs) = buffs.get_mut(ev.entity) {
|
if let Some(mut buffs) = buffs.get_mut(ev.entity) {
|
||||||
@ -1633,6 +1635,32 @@ impl ServerEvent for BuffEvent {
|
|||||||
.map_or(false, |body| body.immune_to(new_buff.kind))
|
.map_or(false, |body| body.immune_to(new_buff.kind))
|
||||||
&& healths.get(ev.entity).map_or(true, |h| !h.is_dead)
|
&& healths.get(ev.entity).map_or(true, |h| !h.is_dead)
|
||||||
{
|
{
|
||||||
|
if let Some(strength) =
|
||||||
|
new_buff.kind.resilience_ccr_strength(new_buff.data)
|
||||||
|
{
|
||||||
|
let resilience_buff = buff::Buff::new(
|
||||||
|
BuffKind::Resilience,
|
||||||
|
buff::BuffData::new(
|
||||||
|
strength,
|
||||||
|
Some(
|
||||||
|
new_buff
|
||||||
|
.data
|
||||||
|
.duration
|
||||||
|
.map_or(Secs(30.0), |dur| dur * 5.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Vec::new(),
|
||||||
|
BuffSource::Buff,
|
||||||
|
*time,
|
||||||
|
buff::DestInfo {
|
||||||
|
stats: stats.get(ev.entity),
|
||||||
|
mass: masses.get(ev.entity),
|
||||||
|
},
|
||||||
|
// There is no source entity
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
buffs.insert(resilience_buff, *time);
|
||||||
|
}
|
||||||
buffs.insert(new_buff, *time);
|
buffs.insert(new_buff, *time);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -397,7 +397,8 @@ fn get_buff_ident(buff: BuffKind) -> &'static str {
|
|||||||
| BuffKind::Bloodfeast
|
| BuffKind::Bloodfeast
|
||||||
| BuffKind::Berserk
|
| BuffKind::Berserk
|
||||||
| BuffKind::ScornfulTaunt
|
| BuffKind::ScornfulTaunt
|
||||||
| BuffKind::Tenacity => {
|
| BuffKind::Tenacity
|
||||||
|
| BuffKind::Resilience => {
|
||||||
tracing::error!("Player was killed by a positive buff!");
|
tracing::error!("Player was killed by a positive buff!");
|
||||||
"mysterious"
|
"mysterious"
|
||||||
},
|
},
|
||||||
|
@ -815,6 +815,7 @@ image_ids! {
|
|||||||
buff_frigid: "voxygen.element.de_buffs.buff_frigid",
|
buff_frigid: "voxygen.element.de_buffs.buff_frigid",
|
||||||
buff_scornfultaunt: "voxygen.element.de_buffs.buff_scornfultaunt",
|
buff_scornfultaunt: "voxygen.element.de_buffs.buff_scornfultaunt",
|
||||||
buff_tenacity: "voxygen.element.de_buffs.buff_tenacity",
|
buff_tenacity: "voxygen.element.de_buffs.buff_tenacity",
|
||||||
|
buff_resilience: "voxygen.element.de_buffs.buff_resilience",
|
||||||
|
|
||||||
// Debuffs
|
// Debuffs
|
||||||
debuff_skull_0: "voxygen.element.de_buffs.debuff_skull_0",
|
debuff_skull_0: "voxygen.element.de_buffs.debuff_skull_0",
|
||||||
|
@ -5256,6 +5256,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
|||||||
BuffKind::Flame => imgs.buff_flame,
|
BuffKind::Flame => imgs.buff_flame,
|
||||||
BuffKind::Frigid => imgs.buff_frigid,
|
BuffKind::Frigid => imgs.buff_frigid,
|
||||||
BuffKind::Lifesteal => imgs.buff_lifesteal,
|
BuffKind::Lifesteal => imgs.buff_lifesteal,
|
||||||
|
BuffKind::Resilience => imgs.buff_resilience,
|
||||||
// TODO: Get image
|
// TODO: Get image
|
||||||
// BuffKind::SalamanderAspect => imgs.debuff_burning_0,
|
// BuffKind::SalamanderAspect => imgs.debuff_burning_0,
|
||||||
BuffKind::ImminentCritical => imgs.buff_imminentcritical,
|
BuffKind::ImminentCritical => imgs.buff_imminentcritical,
|
||||||
|
@ -199,6 +199,7 @@ fn buff_key(buff: BuffKind) -> &'static str {
|
|||||||
BuffKind::Berserk => "buff-berserk",
|
BuffKind::Berserk => "buff-berserk",
|
||||||
BuffKind::ScornfulTaunt => "buff-scornfultaunt",
|
BuffKind::ScornfulTaunt => "buff-scornfultaunt",
|
||||||
BuffKind::Tenacity => "buff-tenacity",
|
BuffKind::Tenacity => "buff-tenacity",
|
||||||
|
BuffKind::Resilience => "buff-resilience",
|
||||||
// Debuffs
|
// Debuffs
|
||||||
BuffKind::Bleeding => "buff-bleed",
|
BuffKind::Bleeding => "buff-bleed",
|
||||||
BuffKind::Cursed => "buff-cursed",
|
BuffKind::Cursed => "buff-cursed",
|
||||||
@ -330,7 +331,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Winded
|
| BuffKind::Winded
|
||||||
| BuffKind::Concussion
|
| BuffKind::Concussion
|
||||||
| BuffKind::Staggered
|
| BuffKind::Staggered
|
||||||
| BuffKind::Tenacity => Cow::Borrowed(""),
|
| BuffKind::Tenacity
|
||||||
|
| BuffKind::Resilience => Cow::Borrowed(""),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(&mut description, "{}", buff_desc).unwrap();
|
write!(&mut description, "{}", buff_desc).unwrap();
|
||||||
@ -386,7 +388,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Winded
|
| BuffKind::Winded
|
||||||
| BuffKind::Concussion
|
| BuffKind::Concussion
|
||||||
| BuffKind::Staggered
|
| BuffKind::Staggered
|
||||||
| BuffKind::Tenacity => Cow::Borrowed(""),
|
| BuffKind::Tenacity
|
||||||
|
| BuffKind::Resilience => Cow::Borrowed(""),
|
||||||
}
|
}
|
||||||
} else if let BuffKind::Saturation
|
} else if let BuffKind::Saturation
|
||||||
| BuffKind::Regeneration
|
| BuffKind::Regeneration
|
||||||
|
Loading…
Reference in New Issue
Block a user