mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/diminishing-cc' into 'master'
Diminishing returns for certain crowd control debuffs See merge request veloren/veloren!4482
This commit is contained in:
commit
79d97cdc22
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
|
||||
buff-tenacity = Tenacity
|
||||
.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
|
||||
buff-text-over_seconds = over { $dur_secs } seconds
|
||||
buff-text-for_seconds = for { $dur_secs } seconds
|
||||
|
@ -195,6 +195,7 @@ lazy_static! {
|
||||
BuffKind::Concussion => "concussion",
|
||||
BuffKind::Staggered => "staggered",
|
||||
BuffKind::Tenacity => "tenacity",
|
||||
BuffKind::Resilience => "resilience",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
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
|
||||
/// 70%.
|
||||
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
|
||||
// =================
|
||||
@ -261,7 +267,8 @@ impl BuffKind {
|
||||
| BuffKind::Bloodfeast
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::ScornfulTaunt
|
||||
| BuffKind::Tenacity => BuffDescriptor::SimplePositive,
|
||||
| BuffKind::Tenacity
|
||||
| BuffKind::Resilience => BuffDescriptor::SimplePositive,
|
||||
BuffKind::Bleeding
|
||||
| BuffKind::Cursed
|
||||
| BuffKind::Burning
|
||||
@ -310,7 +317,7 @@ impl BuffKind {
|
||||
|
||||
/// Checks if multiple instances of the buff should be processed, instead of
|
||||
/// 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> {
|
||||
// Normalized nonlinear scaling
|
||||
@ -534,6 +541,7 @@ impl BuffKind {
|
||||
BuffEffect::MovementSpeed(0.7),
|
||||
BuffEffect::DamagedEffect(DamagedEffect::Energy(data.strength * 10.0)),
|
||||
],
|
||||
BuffKind::Resilience => vec![BuffEffect::CrowdControlResistance(data.strength)],
|
||||
}
|
||||
}
|
||||
|
||||
@ -553,21 +561,37 @@ impl BuffKind {
|
||||
&self,
|
||||
mut data: BuffData,
|
||||
source_mass: Option<&Mass>,
|
||||
dest_mass: Option<&Mass>,
|
||||
dest_info: DestInfo,
|
||||
) -> BuffData {
|
||||
// TODO: Remove clippy allow after another buff needs this
|
||||
#[allow(clippy::single_match)]
|
||||
match self {
|
||||
BuffKind::Rooted => {
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -711,6 +735,8 @@ pub enum BuffEffect {
|
||||
DeathEffect(DeathEffect),
|
||||
/// Prevents use of auxiliary abilities
|
||||
DisableAuxiliaryAbilities,
|
||||
/// Reduces duration of crowd control debuffs
|
||||
CrowdControlResistance(f32),
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -770,7 +796,7 @@ impl Buff {
|
||||
// Create source_info if we need more parameters from source
|
||||
source_mass: Option<&Mass>,
|
||||
) -> 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 cat_ids = kind.extend_cat_ids(cat_ids);
|
||||
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
||||
|
@ -80,6 +80,7 @@ pub struct Stats {
|
||||
/// This creates effects when the entity is killed
|
||||
pub effects_on_death: Vec<DeathEffect>,
|
||||
pub disable_auxiliary_abilities: bool,
|
||||
pub crowd_control_resistance: f32,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
@ -109,6 +110,7 @@ impl Stats {
|
||||
effects_on_damaged: Vec::new(),
|
||||
effects_on_death: Vec::new(),
|
||||
disable_auxiliary_abilities: false,
|
||||
crowd_control_resistance: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -827,5 +827,8 @@ fn execute_effect(
|
||||
BuffEffect::DamagedEffect(effect) => stat.effects_on_damaged.push(effect.clone()),
|
||||
BuffEffect::DeathEffect(effect) => stat.effects_on_death.push(effect.clone()),
|
||||
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::Concussion
|
||||
| BuffKind::Staggered
|
||||
| BuffKind::Tenacity => {
|
||||
| BuffKind::Tenacity
|
||||
| BuffKind::Resilience => {
|
||||
if buff_kind.is_simple() {
|
||||
unreachable!("is_simple() above")
|
||||
} else {
|
||||
|
@ -1617,11 +1617,13 @@ impl ServerEvent for BuffEvent {
|
||||
WriteStorage<'a, comp::Buffs>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, comp::Mass>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
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 {
|
||||
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))
|
||||
&& 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);
|
||||
}
|
||||
},
|
||||
|
@ -397,7 +397,8 @@ fn get_buff_ident(buff: BuffKind) -> &'static str {
|
||||
| BuffKind::Bloodfeast
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::ScornfulTaunt
|
||||
| BuffKind::Tenacity => {
|
||||
| BuffKind::Tenacity
|
||||
| BuffKind::Resilience => {
|
||||
tracing::error!("Player was killed by a positive buff!");
|
||||
"mysterious"
|
||||
},
|
||||
|
@ -818,6 +818,7 @@ image_ids! {
|
||||
buff_frigid: "voxygen.element.de_buffs.buff_frigid",
|
||||
buff_scornfultaunt: "voxygen.element.de_buffs.buff_scornfultaunt",
|
||||
buff_tenacity: "voxygen.element.de_buffs.buff_tenacity",
|
||||
buff_resilience: "voxygen.element.de_buffs.buff_resilience",
|
||||
|
||||
// Debuffs
|
||||
debuff_skull_0: "voxygen.element.de_buffs.debuff_skull_0",
|
||||
|
@ -5260,6 +5260,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
||||
BuffKind::Flame => imgs.buff_flame,
|
||||
BuffKind::Frigid => imgs.buff_frigid,
|
||||
BuffKind::Lifesteal => imgs.buff_lifesteal,
|
||||
BuffKind::Resilience => imgs.buff_resilience,
|
||||
// TODO: Get image
|
||||
// BuffKind::SalamanderAspect => imgs.debuff_burning_0,
|
||||
BuffKind::ImminentCritical => imgs.buff_imminentcritical,
|
||||
|
@ -199,6 +199,7 @@ fn buff_key(buff: BuffKind) -> &'static str {
|
||||
BuffKind::Berserk => "buff-berserk",
|
||||
BuffKind::ScornfulTaunt => "buff-scornfultaunt",
|
||||
BuffKind::Tenacity => "buff-tenacity",
|
||||
BuffKind::Resilience => "buff-resilience",
|
||||
// Debuffs
|
||||
BuffKind::Bleeding => "buff-bleed",
|
||||
BuffKind::Cursed => "buff-cursed",
|
||||
@ -330,7 +331,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Winded
|
||||
| BuffKind::Concussion
|
||||
| BuffKind::Staggered
|
||||
| BuffKind::Tenacity => Cow::Borrowed(""),
|
||||
| BuffKind::Tenacity
|
||||
| BuffKind::Resilience => Cow::Borrowed(""),
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -386,7 +388,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Winded
|
||||
| BuffKind::Concussion
|
||||
| BuffKind::Staggered
|
||||
| BuffKind::Tenacity => Cow::Borrowed(""),
|
||||
| BuffKind::Tenacity
|
||||
| BuffKind::Resilience => Cow::Borrowed(""),
|
||||
}
|
||||
} else if let BuffKind::Saturation
|
||||
| BuffKind::Regeneration
|
||||
|
Loading…
Reference in New Issue
Block a user