mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Changed buff effects so they did not need to mutably change buffs every tick. Buff system now no longer mutably accesses buffs component.
This commit is contained in:
parent
b1b41e95f6
commit
9efac9957d
@ -9,7 +9,7 @@ ItemDef(
|
||||
data: (
|
||||
strength: 100.0,
|
||||
duration: Some(1),
|
||||
),
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
Buff((
|
||||
@ -17,7 +17,6 @@ ItemDef(
|
||||
data: (
|
||||
strength: 0.33,
|
||||
duration: Some(45),
|
||||
delay: Some(1)
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
|
@ -9,7 +9,7 @@ ItemDef(
|
||||
data: (
|
||||
strength: 75.0,
|
||||
duration: Some(1),
|
||||
),
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
Buff((
|
||||
@ -17,7 +17,6 @@ ItemDef(
|
||||
data: (
|
||||
strength: 0.33,
|
||||
duration: Some(45),
|
||||
delay: Some(1)
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
|
@ -9,7 +9,7 @@ ItemDef(
|
||||
data: (
|
||||
strength: 50.0,
|
||||
duration: Some(1),
|
||||
),
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
Buff((
|
||||
@ -17,7 +17,6 @@ ItemDef(
|
||||
data: (
|
||||
strength: 0.33,
|
||||
duration: Some(45),
|
||||
delay: Some(1)
|
||||
),
|
||||
cat_ids: [Natural],
|
||||
)),
|
||||
|
@ -365,6 +365,7 @@ impl Attack {
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
attacker.map(|a| a.uid),
|
||||
target.stats,
|
||||
applied_damage,
|
||||
strength_modifier,
|
||||
)),
|
||||
@ -532,6 +533,7 @@ impl Attack {
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
attacker.map(|a| a.uid),
|
||||
target.stats,
|
||||
accumulated_damage,
|
||||
strength_modifier,
|
||||
)),
|
||||
@ -1029,7 +1031,14 @@ impl MulAssign<f32> for CombatBuffStrength {
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl CombatBuff {
|
||||
fn to_buff(self, time: Time, uid: Option<Uid>, damage: f32, strength_modifier: f32) -> Buff {
|
||||
fn to_buff(
|
||||
self,
|
||||
time: Time,
|
||||
uid: Option<Uid>,
|
||||
stats: Option<&Stats>,
|
||||
damage: f32,
|
||||
strength_modifier: f32,
|
||||
) -> Buff {
|
||||
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
|
||||
let source = if let Some(uid) = uid {
|
||||
BuffSource::Character { by: uid }
|
||||
@ -1046,6 +1055,7 @@ impl CombatBuff {
|
||||
Vec::new(),
|
||||
source,
|
||||
time,
|
||||
stats,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
|
||||
use crate::{resources::Time, uid::Uid};
|
||||
use crate::{comp::Stats, resources::Time, uid::Uid};
|
||||
use core::cmp::Ordering;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use hashbrown::HashMap;
|
||||
@ -183,16 +183,11 @@ pub enum BuffEffect {
|
||||
/// Periodically damages or heals entity
|
||||
HealthChangeOverTime {
|
||||
rate: f32,
|
||||
accumulated: f32,
|
||||
kind: ModifierKind,
|
||||
instance: u64,
|
||||
},
|
||||
/// Periodically consume entity energy
|
||||
EnergyChangeOverTime {
|
||||
rate: f32,
|
||||
accumulated: f32,
|
||||
kind: ModifierKind,
|
||||
},
|
||||
EnergyChangeOverTime { rate: f32, kind: ModifierKind },
|
||||
/// Changes maximum health by a certain amount
|
||||
MaxHealthModifier { value: f32, kind: ModifierKind },
|
||||
/// Changes maximum energy by a certain amount
|
||||
@ -204,7 +199,6 @@ pub enum BuffEffect {
|
||||
rate: f32,
|
||||
kind: ModifierKind,
|
||||
target_fraction: f32,
|
||||
achieved_fraction: Option<f32>,
|
||||
},
|
||||
/// Modifies move speed of target
|
||||
MovementSpeed(f32),
|
||||
@ -215,7 +209,7 @@ pub enum BuffEffect {
|
||||
/// Reduces poise damage taken after armor is accounted for by this fraction
|
||||
PoiseReduction(f32),
|
||||
/// Reduces amount healed by consumables
|
||||
HealReduction { rate: f32 },
|
||||
HealReduction(f32),
|
||||
}
|
||||
|
||||
/// Actual de/buff.
|
||||
@ -271,6 +265,7 @@ impl Buff {
|
||||
cat_ids: Vec<BuffCategory>,
|
||||
source: BuffSource,
|
||||
time: Time,
|
||||
stats: Option<&Stats>,
|
||||
) -> Self {
|
||||
// Normalized nonlinear scaling
|
||||
let nn_scaling = |a| a / (a + 0.5);
|
||||
@ -278,21 +273,25 @@ impl Buff {
|
||||
let effects = match kind {
|
||||
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: -data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
}],
|
||||
BuffKind::Regeneration | BuffKind::Saturation | BuffKind::Potion => {
|
||||
BuffKind::Regeneration | BuffKind::Saturation => {
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
}]
|
||||
},
|
||||
BuffKind::Potion => {
|
||||
vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength * dbg!(stats.map_or(1.0, |s| s.heal_multiplier)),
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
}]
|
||||
},
|
||||
BuffKind::CampfireHeal => vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Fractional,
|
||||
instance,
|
||||
}],
|
||||
@ -301,18 +300,15 @@ impl Buff {
|
||||
rate: -1.0,
|
||||
kind: ModifierKind::Additive,
|
||||
target_fraction: 1.0 - data.strength,
|
||||
achieved_fraction: None,
|
||||
},
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: -1.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
},
|
||||
],
|
||||
BuffKind::EnergyRegen => vec![BuffEffect::EnergyChangeOverTime {
|
||||
rate: data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
}],
|
||||
BuffKind::IncreaseMaxEnergy => vec![BuffEffect::MaxEnergyModifier {
|
||||
@ -332,20 +328,17 @@ impl Buff {
|
||||
)],
|
||||
BuffKind::Burning => vec![BuffEffect::HealthChangeOverTime {
|
||||
rate: -data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
}],
|
||||
BuffKind::Poisoned => vec![BuffEffect::EnergyChangeOverTime {
|
||||
rate: -data.strength,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
}],
|
||||
BuffKind::Crippled => vec![
|
||||
BuffEffect::MovementSpeed(1.0 - nn_scaling(data.strength)),
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: -data.strength * 4.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
},
|
||||
@ -354,7 +347,6 @@ impl Buff {
|
||||
BuffEffect::MovementSpeed(1.0 + data.strength),
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate: data.strength * 10.0,
|
||||
accumulated: 0.0,
|
||||
kind: ModifierKind::Additive,
|
||||
instance,
|
||||
},
|
||||
@ -371,9 +363,7 @@ impl Buff {
|
||||
],
|
||||
BuffKind::Fortitude => vec![BuffEffect::PoiseReduction(data.strength)],
|
||||
BuffKind::Parried => vec![BuffEffect::AttackSpeed(0.5)],
|
||||
BuffKind::PotionSickness => vec![BuffEffect::HealReduction {
|
||||
rate: data.strength,
|
||||
}],
|
||||
BuffKind::PotionSickness => vec![BuffEffect::HealReduction(data.strength)],
|
||||
};
|
||||
let start_time = Time(time.0 + data.delay.unwrap_or(0.0));
|
||||
Buff {
|
||||
@ -495,7 +485,9 @@ impl Buffs {
|
||||
self.kinds.entry(kind).or_default().push(id);
|
||||
self.buffs.insert(id, buff);
|
||||
self.sort_kind(kind);
|
||||
self.delay_queueable_buffs(kind, current_time);
|
||||
if kind.queues() {
|
||||
self.delay_queueable_buffs(kind, current_time);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
@ -548,6 +540,7 @@ impl Buffs {
|
||||
|
||||
fn delay_queueable_buffs(&mut self, kind: BuffKind, current_time: Time) {
|
||||
let mut next_start_time: Option<Time> = None;
|
||||
debug_assert!(kind.queues());
|
||||
if let Some(buffs) = self.kinds.get(&kind) {
|
||||
buffs.iter().for_each(|id| {
|
||||
if let Some(buff) = self.buffs.get_mut(id) {
|
||||
|
@ -70,6 +70,7 @@ impl CharacterBehavior for Data {
|
||||
Vec::new(),
|
||||
BuffSource::Character { by: *data.uid },
|
||||
*data.time,
|
||||
Some(data.stats),
|
||||
);
|
||||
output_events.emit_server(ServerEvent::Buff {
|
||||
entity: data.entity,
|
||||
|
@ -4,7 +4,7 @@ use common::{
|
||||
aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
||||
group::Group,
|
||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos,
|
||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
||||
},
|
||||
event::{Emitter, EventBus, ServerEvent},
|
||||
resources::{DeltaTime, Time},
|
||||
@ -32,6 +32,7 @@ pub struct ReadData<'a> {
|
||||
healths: ReadStorage<'a, Health>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -97,10 +98,16 @@ impl<'a> System<'a> for Sys {
|
||||
.and_then(|l| read_data.healths.get(target).map(|r| (l, r)))
|
||||
.and_then(|l| read_data.uids.get(target).map(|r| (l, r)))
|
||||
.map(|((target_pos, health), target_uid)| {
|
||||
(target, target_pos, health, target_uid)
|
||||
(
|
||||
target,
|
||||
target_pos,
|
||||
health,
|
||||
target_uid,
|
||||
read_data.stats.get(target),
|
||||
)
|
||||
})
|
||||
});
|
||||
target_iter.for_each(|(target, target_pos, health, target_uid)| {
|
||||
target_iter.for_each(|(target, target_pos, health, target_uid, stats)| {
|
||||
let mut target_buffs = match buffs.get_mut(target) {
|
||||
Some(buff) => buff,
|
||||
None => return,
|
||||
@ -132,6 +139,7 @@ impl<'a> System<'a> for Sys {
|
||||
target,
|
||||
health,
|
||||
&mut target_buffs,
|
||||
stats,
|
||||
&read_data,
|
||||
&mut server_emitter,
|
||||
);
|
||||
@ -158,6 +166,7 @@ fn activate_aura(
|
||||
target: EcsEntity,
|
||||
health: &Health,
|
||||
target_buffs: &mut Buffs,
|
||||
stats: Option<&Stats>,
|
||||
read_data: &ReadData,
|
||||
server_emitter: &mut Emitter<ServerEvent>,
|
||||
) {
|
||||
@ -255,6 +264,7 @@ fn activate_aura(
|
||||
vec![category, BuffCategory::FromAura(true)],
|
||||
source,
|
||||
*read_data.time,
|
||||
stats,
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ pub struct ReadData<'a> {
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
time: Read<'a, Time>,
|
||||
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -45,7 +46,6 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Buffs>,
|
||||
WriteStorage<'a, Stats>,
|
||||
WriteStorage<'a, Body>,
|
||||
WriteStorage<'a, LightEmitter>,
|
||||
@ -57,12 +57,11 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
job: &mut Job<Self>,
|
||||
(read_data, mut buffs, mut stats, mut bodies, mut light_emitters): Self::SystemData,
|
||||
(read_data, mut stats, mut bodies, mut light_emitters): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
stats.set_event_emission(false);
|
||||
|
||||
// Put out underwater campfires. Logically belongs here since this system also
|
||||
@ -123,9 +122,9 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, mut buff_comp, mut stat, health, energy, physics_state) in (
|
||||
for (entity, buff_comp, mut stat, health, energy, physics_state) in (
|
||||
&read_data.entities,
|
||||
&mut buffs,
|
||||
&read_data.buffs,
|
||||
&mut stats,
|
||||
&read_data.healths,
|
||||
&read_data.energies,
|
||||
@ -148,6 +147,7 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -164,6 +164,7 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -180,6 +181,7 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
)),
|
||||
});
|
||||
// When standing on IceSpike also apply Frozen
|
||||
@ -191,6 +193,7 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -210,6 +213,7 @@ impl<'a> System<'a> for Sys {
|
||||
vec![BuffCategory::Natural],
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
)),
|
||||
});
|
||||
} else if matches!(
|
||||
@ -253,7 +257,6 @@ impl<'a> System<'a> for Sys {
|
||||
stat.reset_temp_modifiers();
|
||||
|
||||
// Iterator over the lists of buffs by kind
|
||||
let buff_comp = &mut *buff_comp;
|
||||
let mut buff_kinds = buff_comp
|
||||
.kinds
|
||||
.iter()
|
||||
@ -270,7 +273,7 @@ impl<'a> System<'a> for Sys {
|
||||
active_buff_ids.push(buff_ids[0]);
|
||||
}
|
||||
for buff_id in active_buff_ids.into_iter() {
|
||||
if let Some(buff) = buff_comp.buffs.get_mut(&buff_id) {
|
||||
if let Some(buff) = buff_comp.buffs.get(&buff_id) {
|
||||
// Skip the effect of buffs whose start delay hasn't expired.
|
||||
if buff.start_time.0 > read_data.time.0 {
|
||||
continue;
|
||||
@ -283,11 +286,11 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
|
||||
// Now, execute the buff, based on it's delta
|
||||
for effect in &mut buff.effects {
|
||||
for effect in &buff.effects {
|
||||
execute_effect(
|
||||
effect,
|
||||
buff.kind,
|
||||
buff.end_time,
|
||||
buff.start_time,
|
||||
&read_data,
|
||||
&mut stat,
|
||||
health,
|
||||
@ -297,6 +300,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut server_emitter,
|
||||
dt,
|
||||
*read_data.time,
|
||||
expired_buffs.contains(&buff_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -324,15 +328,14 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
// Turned back to true
|
||||
buffs.set_event_emission(true);
|
||||
stats.set_event_emission(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_effect(
|
||||
effect: &mut BuffEffect,
|
||||
effect: &BuffEffect,
|
||||
buff_kind: BuffKind,
|
||||
buff_end_time: Option<Time>,
|
||||
buff_start_time: Time,
|
||||
read_data: &ReadData,
|
||||
stat: &mut Stats,
|
||||
health: &Health,
|
||||
@ -342,28 +345,36 @@ fn execute_effect(
|
||||
server_emitter: &mut Emitter<ServerEvent>,
|
||||
dt: f32,
|
||||
time: Time,
|
||||
buff_will_expire: bool,
|
||||
) {
|
||||
match effect {
|
||||
BuffEffect::HealthChangeOverTime {
|
||||
rate,
|
||||
accumulated,
|
||||
kind,
|
||||
instance,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
// Apply health change only once per second, per health, or
|
||||
// when a buff is removed
|
||||
if accumulated.abs() > rate.abs().min(1.0)
|
||||
|| buff_end_time.map_or(false, |end| end.0 < time.0)
|
||||
{
|
||||
let (cause, by) = if *accumulated != 0.0 {
|
||||
// Apply health change if buff wasn't just added, only once per second or when
|
||||
// buff is about to be removed
|
||||
let time_passed = (time.0 - buff_start_time.0) as f32;
|
||||
let one_second = (time_passed % 1.0) < dt;
|
||||
if time_passed > dt && (one_second || buff_will_expire) {
|
||||
let amount = if one_second {
|
||||
// If buff not expiring this tick, then 1 second has passed
|
||||
*rate
|
||||
} else {
|
||||
// If buff expiring this tick, only a fraction of the second has passed since
|
||||
// last tick
|
||||
((time.0 - buff_start_time.0) % 1.0) as f32 * rate
|
||||
};
|
||||
|
||||
let (cause, by) = if amount != 0.0 {
|
||||
(Some(DamageSource::Buff(buff_kind)), buff_owner)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let mut amount = match *kind {
|
||||
ModifierKind::Additive => *accumulated,
|
||||
ModifierKind::Fractional => health.maximum() * *accumulated,
|
||||
let amount = match *kind {
|
||||
ModifierKind::Additive => amount,
|
||||
ModifierKind::Fractional => health.maximum() * amount,
|
||||
};
|
||||
let damage_contributor = by.and_then(|uid| {
|
||||
read_data
|
||||
@ -373,9 +384,6 @@ fn execute_effect(
|
||||
DamageContributor::new(uid, read_data.groups.get(entity).cloned())
|
||||
})
|
||||
});
|
||||
if amount > 0.0 && matches!(buff_kind, BuffKind::Potion) {
|
||||
amount *= stat.heal_multiplier;
|
||||
}
|
||||
server_emitter.emit(ServerEvent::HealthChange {
|
||||
entity,
|
||||
change: HealthChange {
|
||||
@ -387,29 +395,31 @@ fn execute_effect(
|
||||
instance: *instance,
|
||||
},
|
||||
});
|
||||
*accumulated = 0.0;
|
||||
};
|
||||
},
|
||||
BuffEffect::EnergyChangeOverTime {
|
||||
rate,
|
||||
accumulated,
|
||||
kind,
|
||||
} => {
|
||||
*accumulated += *rate * dt;
|
||||
// Apply energy change only once per second, per energy, or
|
||||
// when a buff is removed
|
||||
if accumulated.abs() > rate.abs().min(10.0)
|
||||
|| buff_end_time.map_or(false, |end| end.0 < time.0)
|
||||
{
|
||||
BuffEffect::EnergyChangeOverTime { rate, kind } => {
|
||||
// Apply energy change if buff wasn't just added, only once per second or when
|
||||
// buff is about to be removed
|
||||
let time_passed = (time.0 - buff_start_time.0) as f32;
|
||||
let one_second = (time_passed % 1.0) < dt;
|
||||
if time_passed > dt && (one_second || buff_will_expire) {
|
||||
let amount = if one_second {
|
||||
// If buff not expiring this tick, then 1 second has passed
|
||||
*rate
|
||||
} else {
|
||||
// If buff expiring this tick, only a fraction of the second has passed since
|
||||
// last tick
|
||||
((time.0 - buff_start_time.0) % 1.0) as f32 * rate
|
||||
};
|
||||
|
||||
let amount = match *kind {
|
||||
ModifierKind::Additive => *accumulated,
|
||||
ModifierKind::Fractional => energy.maximum() * *accumulated,
|
||||
ModifierKind::Additive => amount,
|
||||
ModifierKind::Fractional => energy.maximum() * amount,
|
||||
};
|
||||
server_emitter.emit(ServerEvent::EnergyChange {
|
||||
entity,
|
||||
change: amount,
|
||||
});
|
||||
*accumulated = 0.0;
|
||||
};
|
||||
},
|
||||
BuffEffect::MaxHealthModifier { value, kind } => match kind {
|
||||
@ -436,60 +446,46 @@ fn execute_effect(
|
||||
rate,
|
||||
kind,
|
||||
target_fraction,
|
||||
achieved_fraction,
|
||||
} => {
|
||||
// Current fraction uses information from last tick, which is
|
||||
// necessary as buffs from this tick are not guaranteed to have
|
||||
// finished applying
|
||||
let current_fraction = health.maximum() / health.base_max();
|
||||
let potential_amount = (time.0 - buff_start_time.0) as f32 * rate;
|
||||
|
||||
// If achieved_fraction not initialized, initialize it to health
|
||||
// fraction
|
||||
if achieved_fraction.is_none() {
|
||||
*achieved_fraction = Some(current_fraction)
|
||||
}
|
||||
|
||||
if let Some(achieved_fraction) = achieved_fraction {
|
||||
// Percentage change that should be applied to max_health
|
||||
let health_tick = match kind {
|
||||
// Percentage change that should be applied to max_health
|
||||
let potential_fraction = 1.0
|
||||
+ match kind {
|
||||
ModifierKind::Additive => {
|
||||
// `rate * dt` is amount of health, dividing by base max
|
||||
// creates fraction
|
||||
*rate * dt / health.base_max()
|
||||
potential_amount / health.base_max()
|
||||
},
|
||||
ModifierKind::Fractional => {
|
||||
// `rate * dt` is the fraction
|
||||
*rate * dt
|
||||
potential_amount
|
||||
},
|
||||
};
|
||||
|
||||
let potential_fraction = *achieved_fraction + health_tick;
|
||||
// Potential progress towards target fraction, if
|
||||
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
|
||||
// divide by zero
|
||||
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON {
|
||||
(1.0 - potential_fraction) / (1.0 - *target_fraction)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
// Potential progress towards target fraction, if
|
||||
// target_fraction ~ 1.0 then set progress to 1.0 to avoid
|
||||
// divide by zero
|
||||
let progress = if (1.0 - *target_fraction).abs() > f32::EPSILON {
|
||||
(1.0 - potential_fraction) / (1.0 - *target_fraction)
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
// Change achieved_fraction depending on what other buffs have
|
||||
// occurred
|
||||
let achieved_fraction = if progress > 1.0 {
|
||||
// If potential fraction already beyond target fraction,
|
||||
// simply multiply max_health_modifier by the target
|
||||
// fraction, and set achieved fraction to target_fraction
|
||||
*target_fraction
|
||||
} else {
|
||||
// Else have not achieved target yet, use potential_fraction
|
||||
potential_fraction
|
||||
};
|
||||
|
||||
// Change achieved_fraction depending on what other buffs have
|
||||
// occurred
|
||||
if progress > 1.0 {
|
||||
// If potential fraction already beyond target fraction,
|
||||
// simply multiply max_health_modifier by the target
|
||||
// fraction, and set achieved fraction to target_fraction
|
||||
*achieved_fraction = *target_fraction;
|
||||
} else {
|
||||
// Else have not achieved target yet, update
|
||||
// achieved_fraction
|
||||
*achieved_fraction = potential_fraction;
|
||||
}
|
||||
|
||||
// Apply achieved_fraction to max_health_modifier
|
||||
stat.max_health_modifiers.mult_mod *= *achieved_fraction;
|
||||
}
|
||||
// Apply achieved_fraction to max_health_modifier
|
||||
stat.max_health_modifiers.mult_mod *= achieved_fraction;
|
||||
},
|
||||
BuffEffect::MovementSpeed(speed) => {
|
||||
stat.move_speed_modifier *= *speed;
|
||||
@ -504,8 +500,8 @@ fn execute_effect(
|
||||
BuffEffect::PoiseReduction(pr) => {
|
||||
stat.poise_reduction = stat.poise_reduction.max(*pr).min(1.0);
|
||||
},
|
||||
BuffEffect::HealReduction { rate } => {
|
||||
stat.heal_multiplier *= 1.0 - *rate;
|
||||
BuffEffect::HealReduction(red) => {
|
||||
stat.heal_multiplier *= 1.0 - *red;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -3537,10 +3537,18 @@ fn cast_buff(kind: &str, data: BuffData, server: &mut Server, target: EcsEntity)
|
||||
if let Some(buffkind) = parse_buffkind(kind) {
|
||||
let ecs = &server.state.ecs();
|
||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||
let stats = ecs.read_storage::<comp::Stats>();
|
||||
let time = ecs.read_resource::<Time>();
|
||||
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
||||
buffs.insert(
|
||||
Buff::new(buffkind, data, vec![], BuffSource::Command, *time),
|
||||
Buff::new(
|
||||
buffkind,
|
||||
data,
|
||||
vec![],
|
||||
BuffSource::Command,
|
||||
*time,
|
||||
stats.get(target),
|
||||
),
|
||||
*time,
|
||||
);
|
||||
}
|
||||
|
@ -1318,12 +1318,14 @@ pub fn handle_parry_hook(server: &Server, defender: EcsEntity, attacker: Option<
|
||||
BuffSource::World
|
||||
};
|
||||
let time = ecs.read_resource::<Time>();
|
||||
let stats = ecs.read_storage::<comp::Stats>();
|
||||
let buff = buff::Buff::new(
|
||||
BuffKind::Parried,
|
||||
data,
|
||||
vec![buff::BuffCategory::Physical],
|
||||
source,
|
||||
*time,
|
||||
stats.get(attacker),
|
||||
);
|
||||
server_eventbus.emit_now(ServerEvent::Buff {
|
||||
entity: attacker,
|
||||
|
@ -223,6 +223,7 @@ impl StateExt for State {
|
||||
},
|
||||
Effect::Buff(buff) => {
|
||||
let time = self.ecs().read_resource::<Time>();
|
||||
let stats = self.ecs().read_storage::<comp::Stats>();
|
||||
self.ecs()
|
||||
.write_storage::<comp::Buffs>()
|
||||
.get_mut(entity)
|
||||
@ -234,6 +235,7 @@ impl StateExt for State {
|
||||
buff.cat_ids,
|
||||
comp::BuffSource::Item,
|
||||
*time,
|
||||
stats.get(entity),
|
||||
),
|
||||
*time,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user