Addressed more comments. Changed how buffs were sorted so that duration was also taken into account.

This commit is contained in:
Sam 2020-10-24 20:20:03 -05:00
parent f759895d63
commit 5d0fd3d9bc
7 changed files with 104 additions and 92 deletions

View File

@ -2,7 +2,7 @@ use crate::sync::Uid;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage}; use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
use std::{collections::HashMap, time::Duration}; use std::{cmp::Ordering, collections::HashMap, time::Duration};
/// De/buff Kind. /// De/buff Kind.
/// This is used to determine what effects a buff will have, as well as /// This is used to determine what effects a buff will have, as well as
@ -132,7 +132,7 @@ impl Buff {
), ),
BuffKind::Cursed => ( BuffKind::Cursed => (
vec![BuffEffect::MaxHealthModifier { vec![BuffEffect::MaxHealthModifier {
value: -100., value: -100. * data.strength,
kind: ModifierKind::Additive, kind: ModifierKind::Additive,
}], }],
data.duration, data.duration,
@ -149,6 +149,34 @@ impl Buff {
} }
} }
impl PartialOrd for Buff {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.data.strength > other.data.strength {
Some(Ordering::Greater)
} else if self.data.strength < other.data.strength {
Some(Ordering::Less)
} else if compare_duration(self.time, other.time) {
Some(Ordering::Greater)
} else if compare_duration(other.time, self.time) {
Some(Ordering::Less)
} else if self == other {
Some(Ordering::Equal)
} else {
None
}
}
}
fn compare_duration(a: Option<Duration>, b: Option<Duration>) -> bool {
a.map_or(true, |dur_a| b.map_or(false, |dur_b| dur_a > dur_b))
}
impl PartialEq for Buff {
fn eq(&self, other: &Self) -> bool {
self.data.strength == other.data.strength || self.time == other.time
}
}
/// Source of the de/buff /// Source of the de/buff
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum BuffSource { pub enum BuffSource {
@ -190,17 +218,13 @@ pub struct Buffs {
impl Buffs { impl Buffs {
fn sort_kind(&mut self, kind: BuffKind) { fn sort_kind(&mut self, kind: BuffKind) {
if let Some(buff_order) = self.kinds.get_mut(&kind) { if let Some(buff_order) = self.kinds.get_mut(&kind) {
if buff_order.len() == 0 { if buff_order.is_empty() {
self.kinds.remove(&kind); self.kinds.remove(&kind);
} else { } else {
let buffs = &self.buffs; let buffs = &self.buffs;
buff_order.sort_by(|a, b| { // Intentionally sorted in reverse so that the strongest buffs are earlier in
buffs[&b] // the vector
.data buff_order.sort_by(|a, b| buffs[&b].partial_cmp(&buffs[&a]).unwrap());
.strength
.partial_cmp(&buffs[&a].data.strength)
.unwrap()
});
} }
} }
} }
@ -233,7 +257,7 @@ impl Buffs {
self.kinds self.kinds
.get(&kind) .get(&kind)
.map(|ids| ids.iter()) .map(|ids| ids.iter())
.unwrap_or((&[]).iter()) .unwrap_or_else(|| (&[]).iter())
.map(move |id| (*id, &self.buffs[id])) .map(move |id| (*id, &self.buffs[id]))
} }

View File

@ -107,7 +107,7 @@ pub enum ServerEvent {
/// Send a chat message to the player from an npc or other player /// Send a chat message to the player from an npc or other player
Chat(comp::UnresolvedChatMsg), Chat(comp::UnresolvedChatMsg),
Buff { Buff {
uid: Uid, entity: EcsEntity,
buff_change: comp::BuffChange, buff_change: comp::BuffChange,
}, },
} }

View File

@ -7,13 +7,14 @@ use crate::{
state::DeltaTime, state::DeltaTime,
sync::Uid, sync::Uid,
}; };
use specs::{Join, Read, ReadStorage, System, WriteStorage}; use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
use std::time::Duration; use std::time::Duration;
pub struct Sys; pub struct Sys;
impl<'a> System<'a> for Sys { impl<'a> System<'a> for Sys {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type SystemData = ( type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>, Read<'a, DeltaTime>,
Read<'a, EventBus<ServerEvent>>, Read<'a, EventBus<ServerEvent>>,
ReadStorage<'a, Uid>, ReadStorage<'a, Uid>,
@ -21,11 +22,11 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Buffs>, WriteStorage<'a, Buffs>,
); );
fn run(&mut self, (dt, server_bus, uids, mut stats, mut buffs): Self::SystemData) { fn run(&mut self, (entities, dt, server_bus, uids, mut stats, mut buffs): Self::SystemData) {
let mut server_emitter = server_bus.emitter(); let mut server_emitter = server_bus.emitter();
// Set to false to avoid spamming server // Set to false to avoid spamming server
// buffs.set_event_emission(false); buffs.set_event_emission(false);
for (buff_comp, uid, stat) in (&mut buffs, &uids, &mut stats).join() { for (entity, buff_comp, uid, stat) in (&entities, &mut buffs, &uids, &mut stats).join() {
let mut expired_buffs = Vec::<BuffId>::new(); let mut expired_buffs = Vec::<BuffId>::new();
for (id, buff) in buff_comp.buffs.iter_mut() { for (id, buff) in buff_comp.buffs.iter_mut() {
// Tick the buff and subtract delta from it // Tick the buff and subtract delta from it
@ -63,7 +64,6 @@ 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 &mut buff.effects {
match effect { match effect {
// Only add an effect here if it is continuous or it is not immediate
BuffEffect::HealthChangeOverTime { rate, accumulated } => { BuffEffect::HealthChangeOverTime { rate, accumulated } => {
*accumulated += *rate * dt.0; *accumulated += *rate * dt.0;
// Apply damage only once a second (with a minimum of 1 damage), or // Apply damage only once a second (with a minimum of 1 damage), or
@ -106,7 +106,7 @@ impl<'a> System<'a> for Sys {
// Remove buffs that expire // Remove buffs that expire
if !expired_buffs.is_empty() { if !expired_buffs.is_empty() {
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
uid: *uid, entity,
buff_change: BuffChange::RemoveById(expired_buffs), buff_change: BuffChange::RemoveById(expired_buffs),
}); });
} }
@ -114,7 +114,7 @@ impl<'a> System<'a> for Sys {
// Remove stats that don't persist on death // Remove stats that don't persist on death
if stat.is_dead { if stat.is_dead {
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
uid: *uid, entity,
buff_change: BuffChange::RemoveByCategory { buff_change: BuffChange::RemoveByCategory {
all_required: vec![], all_required: vec![],
any_required: vec![], any_required: vec![],
@ -123,6 +123,6 @@ impl<'a> System<'a> for Sys {
}); });
} }
} }
// buffs.set_event_emission(true); buffs.set_event_emission(true);
} }
} }

View File

@ -153,25 +153,12 @@ impl<'a> System<'a> for Sys {
}, },
}); });
use buff::*;
server_emitter.emit(ServerEvent::Buff {
uid: *uid_b,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Cursed,
BuffData {
strength: attack.base_damage as f32 / 10.0,
duration: Some(Duration::from_secs(10)),
},
vec![BuffCategory::Physical],
BuffSource::Character { by: *uid },
)),
});
// Apply bleeding buff on melee hits with 10% chance // Apply bleeding buff on melee hits with 10% chance
// TODO: Don't have buff uniformly applied on all melee attacks // TODO: Don't have buff uniformly applied on all melee attacks
if thread_rng().gen::<f32>() < 0.1 { if thread_rng().gen::<f32>() < 0.1 {
use buff::*;
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
uid: *uid_b, entity: b,
buff_change: BuffChange::Add(Buff::new( buff_change: BuffChange::Add(Buff::new(
BuffKind::Bleeding, BuffKind::Bleeding,
BuffData { BuffData {

View File

@ -51,7 +51,7 @@ impl<'a> System<'a> for Sys {
span!(_guard, "run", "controller::Sys::run"); span!(_guard, "run", "controller::Sys::run");
let mut server_emitter = server_bus.emitter(); let mut server_emitter = server_bus.emitter();
for (entity, uid, controller, character_state) in for (entity, _uid, controller, character_state) in
(&entities, &uids, &mut controllers, &mut character_states).join() (&entities, &uids, &mut controllers, &mut character_states).join()
{ {
let mut inputs = &mut controller.inputs; let mut inputs = &mut controller.inputs;
@ -85,7 +85,7 @@ impl<'a> System<'a> for Sys {
}, },
ControlEvent::RemoveBuff(buff_id) => { ControlEvent::RemoveBuff(buff_id) => {
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
uid: *uid, entity,
buff_change: BuffChange::RemoveFromController(buff_id), buff_change: BuffChange::RemoveFromController(buff_id),
}); });
}, },

View File

@ -699,66 +699,64 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
)); ));
} }
pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange) { pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::BuffChange) {
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>();
if let Some(entity) = ecs.entity_from_uid(uid.into()) { if let Some(buffs) = buffs_all.get_mut(entity) {
if let Some(buffs) = buffs_all.get_mut(entity) { use buff::BuffChange;
use buff::BuffChange; match buff_change {
match buff_change { BuffChange::Add(new_buff) => {
BuffChange::Add(new_buff) => { buffs.insert(new_buff);
buffs.insert(new_buff); },
}, BuffChange::RemoveById(ids) => {
BuffChange::RemoveById(ids) => { for id in ids {
for id in ids { buffs.remove(id);
buffs.remove(id); }
} },
}, BuffChange::RemoveByKind(kind) => {
BuffChange::RemoveByKind(kind) => { buffs.remove_kind(kind);
},
BuffChange::RemoveFromController(kind) => {
if kind.is_buff() {
buffs.remove_kind(kind); buffs.remove_kind(kind);
}, }
BuffChange::RemoveFromController(kind) => { },
if kind.is_buff() { BuffChange::RemoveByCategory {
buffs.remove_kind(kind); all_required,
} any_required,
}, none_required,
BuffChange::RemoveByCategory { } => {
all_required, let mut ids_to_remove = Vec::new();
any_required, for (id, buff) in buffs.buffs.iter() {
none_required, let mut required_met = true;
} => { for required in &all_required {
let mut ids_to_remove = Vec::new(); if !buff.cat_ids.iter().any(|cat| cat == required) {
for (id, buff) in buffs.buffs.iter() { required_met = false;
let mut required_met = true; break;
for required in &all_required {
if !buff.cat_ids.iter().any(|cat| cat == required) {
required_met = false;
break;
}
}
let mut any_met = any_required.is_empty();
for any in &any_required {
if buff.cat_ids.iter().any(|cat| cat == any) {
any_met = true;
break;
}
}
let mut none_met = true;
for none in &none_required {
if buff.cat_ids.iter().any(|cat| cat == none) {
none_met = false;
break;
}
}
if required_met && any_met && none_met {
ids_to_remove.push(*id);
} }
} }
for id in ids_to_remove { let mut any_met = any_required.is_empty();
buffs.remove(id); for any in &any_required {
if buff.cat_ids.iter().any(|cat| cat == any) {
any_met = true;
break;
}
} }
}, let mut none_met = true;
} for none in &none_required {
if buff.cat_ids.iter().any(|cat| cat == none) {
none_met = false;
break;
}
}
if required_met && any_met && none_met {
ids_to_remove.push(*id);
}
}
for id in ids_to_remove {
buffs.remove(id);
}
},
} }
} }
} }

View File

@ -133,7 +133,10 @@ impl Server {
ServerEvent::Chat(msg) => { ServerEvent::Chat(msg) => {
chat_messages.push(msg); chat_messages.push(msg);
}, },
ServerEvent::Buff { uid, buff_change } => handle_buff(self, uid, buff_change), ServerEvent::Buff {
entity,
buff_change,
} => handle_buff(self, entity, buff_change),
} }
} }