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