mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Move addition/removal of buffs to server event.
This commit is contained in:
parent
7ab99a3bbf
commit
c50063ad0c
@ -5,7 +5,7 @@ use specs_idvs::IdvStorage;
|
||||
use std::time::Duration;
|
||||
|
||||
/// De/buff ID.
|
||||
/// ID can be independant of an actual type/config of a `BuffData`.
|
||||
/// ID can be independant of an actual type/config of a `BuffEffect`.
|
||||
/// Therefore, information provided by `BuffId` can be incomplete/incorrect.
|
||||
///
|
||||
/// For example, there could be two regeneration buffs, each with
|
||||
@ -19,6 +19,8 @@ pub enum BuffId {
|
||||
Regeneration,
|
||||
/// Lowers health/time for some period, but faster
|
||||
Poison,
|
||||
/// Lowers health over time for some duration
|
||||
Bleeding,
|
||||
/// Changes entity name as to "Cursed {}"
|
||||
Cursed,
|
||||
}
|
||||
@ -29,6 +31,7 @@ pub enum BuffId {
|
||||
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum BuffCategoryId {
|
||||
Natural,
|
||||
Physical,
|
||||
Magical,
|
||||
Divine,
|
||||
Negative,
|
||||
@ -39,9 +42,9 @@ pub enum BuffCategoryId {
|
||||
///
|
||||
/// NOTE: Contents of this enum are WIP/Placeholder
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum BuffData {
|
||||
/// Periodically damages health
|
||||
RepeatedHealthChange { speed: f32, accumulated: f32 },
|
||||
pub enum BuffEffect {
|
||||
/// Periodically damages or heals entity
|
||||
RepeatedHealthChange { rate: f32, accumulated: f32 },
|
||||
/// Changes name on_add/on_remove
|
||||
NameChange { prefix: String },
|
||||
}
|
||||
@ -68,7 +71,7 @@ pub struct Buff {
|
||||
pub id: BuffId,
|
||||
pub cat_ids: Vec<BuffCategoryId>,
|
||||
pub time: Option<Duration>,
|
||||
pub data: BuffData,
|
||||
pub effects: Vec<BuffEffect>,
|
||||
}
|
||||
|
||||
/// Information about whether buff addition or removal was requested.
|
||||
@ -78,8 +81,6 @@ pub enum BuffChange {
|
||||
/// Adds this buff.
|
||||
Add(Buff),
|
||||
/// Removes all buffs with this ID.
|
||||
/// TODO: Better removal, allowing to specify which ability to remove
|
||||
/// directly.
|
||||
Remove(BuffId),
|
||||
}
|
||||
|
||||
@ -102,10 +103,10 @@ 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 `BuffData`
|
||||
/// (specs system will decide based on `BuffData`, to simplify implementation).
|
||||
/// 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
|
||||
/// `BuffData` enum?
|
||||
/// `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).
|
||||
@ -120,17 +121,11 @@ pub enum BuffSource {
|
||||
pub struct Buffs {
|
||||
/// Active de/buffs.
|
||||
pub buffs: Vec<Buff>,
|
||||
/// Request to add/remove a buff.
|
||||
/// Used for reacting on buff changes by the ECS system.
|
||||
/// TODO: Can be `EventBus<T>` used instead of this?
|
||||
pub changes: Vec<BuffChange>,
|
||||
/// Last time since any buff change to limit syncing.
|
||||
pub last_change: f64,
|
||||
}
|
||||
|
||||
impl Buffs {
|
||||
/// Adds a request for adding given `buff`.
|
||||
pub fn add_buff(&mut self, buff: Buff) {
|
||||
/*pub fn add_buff(&mut self, buff: Buff) {
|
||||
let change = BuffChange::Add(buff);
|
||||
self.changes.push(change);
|
||||
self.last_change = 0.0;
|
||||
@ -143,7 +138,7 @@ impl Buffs {
|
||||
let change = BuffChange::Remove(id);
|
||||
self.changes.push(change);
|
||||
self.last_change = 0.0;
|
||||
}
|
||||
}*/
|
||||
|
||||
/// This is a primitive check if a specific buff is present.
|
||||
/// (for purposes like blocking usage of abilities or something like this).
|
||||
|
@ -3,7 +3,7 @@ mod admin;
|
||||
pub mod agent;
|
||||
pub mod beam;
|
||||
pub mod body;
|
||||
mod buff;
|
||||
pub mod buff;
|
||||
mod character_state;
|
||||
pub mod chat;
|
||||
mod controller;
|
||||
@ -32,7 +32,7 @@ pub use body::{
|
||||
biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object,
|
||||
quadruped_low, quadruped_medium, quadruped_small, theropod, AllBodies, Body, BodyData,
|
||||
};
|
||||
pub use buff::{Buff, BuffCategoryId, BuffChange, BuffData, BuffId, Buffs};
|
||||
pub use buff::{Buff, BuffCategoryId, BuffChange, BuffEffect, BuffId, Buffs};
|
||||
pub use character_state::{Attacking, CharacterState, StateUpdate};
|
||||
pub use chat::{
|
||||
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
|
||||
|
@ -106,6 +106,10 @@ pub enum ServerEvent {
|
||||
ChatCmd(EcsEntity, String),
|
||||
/// Send a chat message to the player from an npc or other player
|
||||
Chat(comp::UnresolvedChatMsg),
|
||||
Buff {
|
||||
uid: Uid,
|
||||
buff: comp::BuffChange,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::{BuffChange, BuffData, BuffId, Buffs, HealthChange, HealthSource, Stats},
|
||||
comp::{BuffChange, BuffEffect, BuffId, Buffs, HealthChange, HealthSource, Stats},
|
||||
state::DeltaTime,
|
||||
};
|
||||
use specs::{Entities, Join, Read, System, WriteStorage};
|
||||
@ -21,58 +21,8 @@ impl<'a> System<'a> for Sys {
|
||||
);
|
||||
|
||||
fn run(&mut self, (entities, dt, mut stats, mut buffs): Self::SystemData) {
|
||||
// Increment last change timer
|
||||
buffs.set_event_emission(false);
|
||||
for buff in (&mut buffs).join() {
|
||||
buff.last_change += f64::from(dt.0);
|
||||
}
|
||||
buffs.set_event_emission(true);
|
||||
|
||||
for (entity, mut buffs) in (&entities, &mut buffs.restrict_mut()).join() {
|
||||
let buff_comp = buffs.get_mut_unchecked();
|
||||
// Add/Remove de/buffs
|
||||
// While adding/removing buffs, it could call respective hooks
|
||||
// Currently, it is done based on enum variant
|
||||
let changes = buff_comp.changes.drain(0..buff_comp.changes.len());
|
||||
for change in changes {
|
||||
match change {
|
||||
// Hooks for on_add could be here
|
||||
BuffChange::Add(new_buff) => {
|
||||
match &new_buff.data {
|
||||
BuffData::NameChange { prefix } => {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
let mut pref = String::from(prefix);
|
||||
pref.push_str(&stats.name);
|
||||
stats.name = pref;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
buff_comp.buffs.push(new_buff.clone());
|
||||
},
|
||||
// Hooks for on_remove could be here
|
||||
BuffChange::Remove(id) => {
|
||||
let some_predicate = |current_id: &BuffId| *current_id == id;
|
||||
let mut i = 0;
|
||||
while i != buff_comp.buffs.len() {
|
||||
if some_predicate(&mut buff_comp.buffs[i].id) {
|
||||
let buff = buff_comp.buffs.remove(i);
|
||||
match &buff.data {
|
||||
BuffData::NameChange { prefix } => {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
stats.name = stats.name.replace(prefix, "");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let mut buffs_for_removal = Vec::new();
|
||||
// Tick all de/buffs on a Buffs component.
|
||||
for active_buff in &mut buff_comp.buffs {
|
||||
@ -102,29 +52,32 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
|
||||
// Now, execute the buff, based on it's delta
|
||||
match &mut active_buff.data {
|
||||
BuffData::RepeatedHealthChange { speed, accumulated } => {
|
||||
*accumulated += *speed * buff_delta;
|
||||
// Apply only 0.5 or higher damage
|
||||
if accumulated.abs() > 5.0 {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
let change = HealthChange {
|
||||
amount: *accumulated as i32,
|
||||
cause: HealthSource::Unknown,
|
||||
};
|
||||
stats.health.change_by(change);
|
||||
}
|
||||
*accumulated = 0.0;
|
||||
};
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
for effect in &mut active_buff.effects {
|
||||
match effect {
|
||||
// Only add an effect here if it is continuous or it is not immediate
|
||||
BuffEffect::RepeatedHealthChange { rate, accumulated } => {
|
||||
*accumulated += *rate * buff_delta;
|
||||
// Apply only 0.5 or higher damage
|
||||
if accumulated.abs() > 5.0 {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
let change = HealthChange {
|
||||
amount: *accumulated as i32,
|
||||
cause: HealthSource::Unknown,
|
||||
};
|
||||
stats.health.change_by(change);
|
||||
}
|
||||
*accumulated = 0.0;
|
||||
};
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
// Truly mark expired buffs for removal.
|
||||
// TODO: Review this, as it is ugly.
|
||||
for to_remove in buffs_for_removal {
|
||||
/*for to_remove in buffs_for_removal {
|
||||
buff_comp.remove_buff_by_id(to_remove);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource,
|
||||
buff, group, Attacking, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource,
|
||||
Loadout, Ori, Pos, Scale, Stats,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
@ -10,6 +10,7 @@ use crate::{
|
||||
util::Dir,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
pub const BLOCK_EFFICIENCY: f32 = 0.9;
|
||||
@ -150,6 +151,19 @@ impl<'a> System<'a> for Sys {
|
||||
cause,
|
||||
},
|
||||
});
|
||||
// Test for server event of buff, remove before merging
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
uid: *uid_b,
|
||||
buff: buff::BuffChange::Add(buff::Buff {
|
||||
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
|
||||
}],
|
||||
}),
|
||||
});
|
||||
attack.hit_count += 1;
|
||||
}
|
||||
if attack.knockback != 0.0 && damage.healthchange != 0.0 {
|
||||
|
@ -7,7 +7,7 @@ use common::{
|
||||
assets::Asset,
|
||||
comp::{
|
||||
self,
|
||||
chat::{KillSource, KillType},
|
||||
buff, chat::{KillSource, KillType},
|
||||
object, Alignment, Body, Damage, DamageSource, Group, HealthChange, HealthSource, Item,
|
||||
Player, Pos, Stats,
|
||||
},
|
||||
@ -674,3 +674,53 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) {
|
||||
PlayerListUpdate::LevelChange(*uid, new_level),
|
||||
));
|
||||
}
|
||||
|
||||
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()) {
|
||||
if let Some(buffs) = buffs_all.get_mut(entity) {
|
||||
let mut stats = ecs.write_storage::<comp::Stats>();
|
||||
match buff {
|
||||
buff::BuffChange::Add(new_buff) => {
|
||||
for effect in &new_buff.effects {
|
||||
match effect {
|
||||
// Only add an effect here if it is immediate and is not continuous
|
||||
buff::BuffEffect::NameChange { prefix } => {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
let mut pref = String::from(prefix);
|
||||
pref.push_str(&stats.name);
|
||||
stats.name = pref;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
buffs.buffs.push(new_buff.clone());
|
||||
},
|
||||
buff::BuffChange::Remove(id) => {
|
||||
let some_predicate = |current_id: &buff::BuffId| *current_id == id;
|
||||
let mut i = 0;
|
||||
while i != buffs.buffs.len() {
|
||||
if some_predicate(&mut buffs.buffs[i].id) {
|
||||
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
|
||||
buff::BuffEffect::NameChange { prefix } => {
|
||||
if let Some(stats) = stats.get_mut(entity) {
|
||||
stats.name = stats.name.replace(prefix, "");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use entity_creation::{
|
||||
handle_loaded_character_data, handle_shockwave, handle_shoot,
|
||||
};
|
||||
use entity_manipulation::{
|
||||
handle_damage, handle_destroy, handle_explosion, handle_knockback, handle_land_on_ground,
|
||||
handle_buff, handle_damage, handle_destroy, handle_explosion, handle_knockback, handle_land_on_ground,
|
||||
handle_level_up, handle_respawn,
|
||||
};
|
||||
use group_manip::handle_group;
|
||||
@ -133,6 +133,10 @@ impl Server {
|
||||
ServerEvent::Chat(msg) => {
|
||||
chat_messages.push(msg);
|
||||
},
|
||||
ServerEvent::Buff {
|
||||
uid,
|
||||
buff,
|
||||
} => handle_buff(self, uid, buff),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,39 +93,6 @@ impl StateExt for State {
|
||||
loadout: comp::Loadout,
|
||||
body: comp::Body,
|
||||
) -> EcsEntityBuilder {
|
||||
// NOTE: This is placeholder code for testing and does not
|
||||
// belongs here really
|
||||
let mut buffs = comp::Buffs::default();
|
||||
// Damages slightly each NPC spawned for 20 seconds
|
||||
buffs.add_buff(comp::Buff {
|
||||
id: comp::BuffId::Poison,
|
||||
cat_ids: vec![],
|
||||
time: Some(std::time::Duration::from_secs(20)),
|
||||
data: comp::BuffData::RepeatedHealthChange {
|
||||
speed: -10.0,
|
||||
accumulated: 0.0,
|
||||
},
|
||||
});
|
||||
// Prepends "Cursed " to each NPC's name for 35 secs
|
||||
buffs.add_buff(comp::Buff {
|
||||
id: comp::BuffId::Cursed,
|
||||
cat_ids: vec![],
|
||||
time: Some(std::time::Duration::from_secs(35)),
|
||||
data: comp::BuffData::NameChange {
|
||||
prefix: String::from("Cursed "),
|
||||
},
|
||||
});
|
||||
// Adds super-slow regen to each NPC spawned, indefinitely
|
||||
buffs.add_buff(comp::Buff {
|
||||
id: comp::BuffId::Regeneration,
|
||||
cat_ids: vec![],
|
||||
time: None,
|
||||
data: comp::BuffData::RepeatedHealthChange {
|
||||
speed: 1.0,
|
||||
accumulated: 0.0,
|
||||
},
|
||||
});
|
||||
|
||||
self.ecs_mut()
|
||||
.create_entity_synced()
|
||||
.with(pos)
|
||||
@ -144,7 +111,6 @@ impl StateExt for State {
|
||||
.with(comp::Gravity(1.0))
|
||||
.with(comp::CharacterState::default())
|
||||
.with(loadout)
|
||||
.with(buffs)
|
||||
}
|
||||
|
||||
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder {
|
||||
|
Loading…
Reference in New Issue
Block a user