Creatures and playrs now have buffs component, buffs that expire now only end their particular buff instead of every buff with the same type.

This commit is contained in:
Sam 2020-09-30 20:35:57 -05:00
parent c50063ad0c
commit 125de0b46e
6 changed files with 36 additions and 32 deletions

View File

@ -44,7 +44,7 @@ pub enum BuffCategoryId {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BuffEffect {
/// Periodically damages or heals entity
RepeatedHealthChange { rate: f32, accumulated: f32 },
HealthChangeOverTime { rate: f32, accumulated: f32 },
/// Changes name on_add/on_remove
NameChange { prefix: String },
}
@ -104,9 +104,9 @@ pub enum BuffSource {
/// Component holding all de/buffs that gets resolved each tick.
/// On each tick, remaining time of buffs get lowered and
/// buff effect of each buff is applied or not, depending on the `BuffEffect`
/// (specs system will decide based on `BuffEffect`, to simplify implementation).
/// TODO: Something like `once` flag for `Buff` to remove the dependence on
/// `BuffEffect` enum?
/// (specs system will decide based on `BuffEffect`, to simplify
/// implementation). TODO: Something like `once` flag for `Buff` to remove the
/// dependence on `BuffEffect` enum?
///
/// In case of one-time buffs, buff effects will be applied on addition
/// and undone on removal of the buff (by the specs system).

View File

@ -23,13 +23,13 @@ impl<'a> System<'a> for Sys {
fn run(&mut self, (entities, dt, mut stats, mut buffs): Self::SystemData) {
for (entity, mut buffs) in (&entities, &mut buffs.restrict_mut()).join() {
let buff_comp = buffs.get_mut_unchecked();
let mut buffs_for_removal = Vec::new();
let mut buff_indices_for_removal = Vec::new();
// Tick all de/buffs on a Buffs component.
for active_buff in &mut buff_comp.buffs {
for i in 0..buff_comp.buffs.len() {
// First, tick the buff and subtract delta from it
// and return how much "real" time the buff took (for tick independence).
// TODO: handle delta for "indefinite" buffs, i.e. time since they got removed.
let buff_delta = if let Some(remaining_time) = &mut active_buff.time {
let buff_delta = if let Some(remaining_time) = &mut buff_comp.buffs[i].time {
let pre_tick = remaining_time.as_secs_f32();
let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0));
let post_tick = if let Some(dur) = new_duration {
@ -38,9 +38,8 @@ impl<'a> System<'a> for Sys {
dur.as_secs_f32()
} else {
// The buff has expired.
// Mark it for removal.
// TODO: This removes by ID! better method required
buffs_for_removal.push(active_buff.id.clone());
// Remove it.
buff_indices_for_removal.push(i);
0.0
};
pre_tick - post_tick
@ -52,10 +51,10 @@ impl<'a> System<'a> for Sys {
};
// Now, execute the buff, based on it's delta
for effect in &mut active_buff.effects {
for effect in &mut buff_comp.buffs[i].effects {
match effect {
// Only add an effect here if it is continuous or it is not immediate
BuffEffect::RepeatedHealthChange { rate, accumulated } => {
BuffEffect::HealthChangeOverTime { rate, accumulated } => {
*accumulated += *rate * buff_delta;
// Apply only 0.5 or higher damage
if accumulated.abs() > 5.0 {
@ -73,11 +72,16 @@ impl<'a> System<'a> for Sys {
};
}
}
// Truly mark expired buffs for removal.
// TODO: Review this, as it is ugly.
/*for to_remove in buffs_for_removal {
buff_comp.remove_buff_by_id(to_remove);
}*/
// Remove buffs that have expired.
// Since buffs are added into this vec as it iterates up through the list, it
// will be in order of increasing values. Therefore to avoid
// removing the incorrect buff, removal will start from the greatest index
// value, which is the last in this vec.
while !buff_indices_for_removal.is_empty() {
if let Some(i) = buff_indices_for_removal.pop() {
buff_comp.buffs.remove(i);
}
}
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
buff, group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource,
Loadout, Ori, Pos, Scale, Stats,
buff, group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange,
HealthSource, Loadout, Ori, Pos, Scale, Stats,
},
event::{EventBus, LocalEvent, ServerEvent},
metrics::SysMetrics,
@ -158,9 +158,9 @@ impl<'a> System<'a> for Sys {
id: buff::BuffId::Bleeding,
cat_ids: vec![buff::BuffCategoryId::Physical],
time: Some(Duration::from_millis(2000)),
effects: vec![buff::BuffEffect::RepeatedHealthChange {
rate: 50.0,
accumulated: 0.0
effects: vec![buff::BuffEffect::HealthChangeOverTime {
rate: 10.0,
accumulated: 0.0,
}],
}),
});

View File

@ -6,8 +6,8 @@ use crate::{
use common::{
assets::Asset,
comp::{
self,
buff, chat::{KillSource, KillType},
self, buff,
chat::{KillSource, KillType},
object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Item,
Player, Pos, Stats,
},
@ -675,7 +675,7 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
));
}
pub fn handle_buff(server: &mut Server, uid: Uid, buff: buff::BuffChange ) {
pub fn handle_buff(server: &mut Server, uid: Uid, buff: buff::BuffChange) {
let ecs = &server.state.ecs();
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
if let Some(entity) = ecs.entity_from_uid(uid.into()) {
@ -706,7 +706,8 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff: buff::BuffChange ) {
let buff = buffs.buffs.remove(i);
for effect in &buff.effects {
match effect {
// Only remove an effect here if its effect was not continuously applied
// Only remove an effect here if its effect was not continuously
// applied
buff::BuffEffect::NameChange { prefix } => {
if let Some(stats) = stats.get_mut(entity) {
stats.name = stats.name.replace(prefix, "");

View File

@ -8,8 +8,8 @@ use entity_creation::{
handle_loaded_character_data, handle_shockwave, handle_shoot,
};
use entity_manipulation::{
handle_buff, handle_damage, handle_destroy, handle_explosion, handle_knockback, handle_land_on_ground,
handle_level_up, handle_respawn,
handle_buff, handle_damage, handle_destroy, handle_explosion, handle_knockback,
handle_land_on_ground, handle_level_up, handle_respawn,
};
use group_manip::handle_group;
use interaction::{handle_lantern, handle_mount, handle_possess, handle_unmount};
@ -133,10 +133,7 @@ impl Server {
ServerEvent::Chat(msg) => {
chat_messages.push(msg);
},
ServerEvent::Buff {
uid,
buff,
} => handle_buff(self, uid, buff),
ServerEvent::Buff { uid, buff } => handle_buff(self, uid, buff),
}
}

View File

@ -111,6 +111,7 @@ impl StateExt for State {
.with(comp::Gravity(1.0))
.with(comp::CharacterState::default())
.with(loadout)
.with(comp::Buffs::default())
}
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder {
@ -202,6 +203,7 @@ impl StateExt for State {
entity,
comp::Alignment::Owned(self.read_component_copied(entity).unwrap()),
);
self.write_component(entity, comp::Buffs::default());
// Make sure physics components are updated
self.write_component(entity, comp::ForceUpdate);