From 71303fecfd353a7c4a9fd23f166cbc6c9ecc4d52 Mon Sep 17 00:00:00 2001 From: James Melkonian Date: Fri, 4 Dec 2020 22:24:56 +0000 Subject: [PATCH] Aura System and Campfire Health Regen --- CHANGELOG.md | 4 + Cargo.lock | 10 ++ .../icons/de_buffs/buff_campfire_heal_0.png | 3 + assets/voxygen/i18n/en.ron | 2 + common/Cargo.toml | 1 + common/src/comp/aura.rs | 83 ++++++++++++++ common/src/comp/buff.rs | 29 ++++- common/src/comp/mod.rs | 2 + common/src/event.rs | 4 + common/src/msg/ecs_packet.rs | 5 + common/sys/src/aura.rs | 104 ++++++++++++++++++ common/sys/src/buff.rs | 23 ++-- common/sys/src/lib.rs | 3 + common/sys/src/state.rs | 1 + server/src/cmd.rs | 19 +++- server/src/events/entity_creation.rs | 18 ++- server/src/events/entity_manipulation.rs | 20 +++- server/src/events/mod.rs | 6 +- server/src/sys/sentinel.rs | 17 ++- voxygen/src/hud/buffs.rs | 14 +++ voxygen/src/hud/group.rs | 1 + voxygen/src/hud/img_ids.rs | 1 + voxygen/src/hud/overhead.rs | 1 + 23 files changed, 351 insertions(+), 20 deletions(-) create mode 100644 assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png create mode 100644 common/src/comp/aura.rs create mode 100644 common/sys/src/aura.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c3145669..b7d0b9bb4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added chat commands for inviting, kicking, leaving, and promoting in groups +- Aura system +- Campfire resting heal ### Changed + - Doubled range of ScaleMode slider when set to Custom ### Removed + - SSAAx4 option ### Fixed diff --git a/Cargo.lock b/Cargo.lock index a81f5726e7..ea3c4133e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4393,6 +4393,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "slotmap" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" +dependencies = [ + "serde", +] + [[package]] name = "smallvec" version = "0.6.13" @@ -5373,6 +5382,7 @@ dependencies = [ "serde_json", "serde_repr", "slab", + "slotmap", "specs", "specs-idvs", "spin_sleep", diff --git a/assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png b/assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png new file mode 100644 index 0000000000..b0bb533e77 --- /dev/null +++ b/assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06ddc169c14d6677bc9b31235802273477266c6d56c564201bea29219eace2b2 +size 303 diff --git a/assets/voxygen/i18n/en.ron b/assets/voxygen/i18n/en.ron index 2c57b849ef..96b1c510b6 100644 --- a/assets/voxygen/i18n/en.ron +++ b/assets/voxygen/i18n/en.ron @@ -550,6 +550,8 @@ Protection "buff.desc.potion": "Drinking...", "buff.title.saturation": "Saturation", "buff.desc.saturation": "Gain health over time from consumables.", + "buff.title.campfire_heal": "Campfire Heal", + "buff.desc.campfire_heal": "Resting at a campfire heals 1% per second.", // Debuffs "debuff.title.bleed": "Bleeding", "debuff.desc.bleed": "Inflicts regular damage.", diff --git a/common/Cargo.toml b/common/Cargo.toml index e522c3c0aa..a57ac12db3 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -39,6 +39,7 @@ authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60 # Data structures hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] } +slotmap = { version = "0.4.0", features = ["serde", "unstable"] } indexmap = "1.3.0" slab = "0.4.2" diff --git a/common/src/comp/aura.rs b/common/src/comp/aura.rs new file mode 100644 index 0000000000..a3ed6753dd --- /dev/null +++ b/common/src/comp/aura.rs @@ -0,0 +1,83 @@ +use crate::comp::buff::{BuffCategory, BuffData, BuffKind, BuffSource}; +use serde::{Deserialize, Serialize}; +use slotmap::{new_key_type, SlotMap}; +use specs::{Component, FlaggedStorage}; +use specs_idvs::IdvStorage; +use std::time::Duration; + +new_key_type! { pub struct AuraKey; } + +/// AuraKind is what kind of effect an aura applies +/// Currently only buffs are implemented +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum AuraKind { + /// The Buff kind is (surprise!) a buff :D + Buff { + kind: BuffKind, + data: BuffData, + category: BuffCategory, + source: BuffSource, + }, + /* TODO: Implement other effects here. Things to think about + * are terrain/sprite effects, collision and physics, and + * environmental conditions like temperature and humidity + * Multiple auras can be given to an entity. */ +} + +/// Aura +/// Applies a buff to entities in the radius if meeting +/// conditions set forth in the aura system. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Aura { + /// The kind of aura applied + pub aura_kind: AuraKind, + /// The radius of the aura + pub radius: f32, + /// How long the aura lasts. None corresponds to an indefinite length + pub duration: Option, + /* TODO: Add functionality for fading or a gradient + * TODO: Make alignment specific auras work */ +} + +/// Information about whether aura addition or removal was requested. +/// This to implement "on_add" and "on_remove" hooks for auras +#[derive(Clone, Debug)] +pub enum AuraChange { + /// Adds this aura + Add(Aura), + /// Removes auras of these indices + RemoveByKey(Vec), +} + +impl Aura { + /// Creates a new Aura to be assigned to an entity + pub fn new(aura_kind: AuraKind, radius: f32, duration: Option) -> Self { + Self { + aura_kind, + radius, + duration, + } + } +} + +/// Component holding all auras emitted by an entity. +#[derive(Clone, Debug, Serialize, Deserialize, Default)] +pub struct Auras { + pub auras: SlotMap, +} + +impl Auras { + pub fn new(aura: Aura) -> Self { + let mut auras: SlotMap = SlotMap::with_key(); + auras.insert(aura); + Self { auras } + } + + pub fn insert(&mut self, aura: Aura) { self.auras.insert(aura); } + + pub fn remove(&mut self, key: AuraKey) { self.auras.remove(key); } +} + +impl Component for Auras { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index d54da80d44..c5de02505e 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -17,8 +17,10 @@ pub enum BuffKind { /// Lower a creature's max health /// Currently placeholder buff to show other stuff is possible Cursed, - // Applied when drinking a potion + /// Applied when drinking a potion Potion, + /// Applied when sitting at a campfire + CampfireHeal, } impl BuffKind { @@ -30,6 +32,7 @@ impl BuffKind { BuffKind::Bleeding { .. } => false, BuffKind::Cursed { .. } => false, BuffKind::Potion { .. } => true, + BuffKind::CampfireHeal { .. } => true, } } } @@ -41,6 +44,10 @@ pub struct BuffData { pub duration: Option, } +impl BuffData { + pub fn new(strength: f32, duration: Option) -> Self { Self { strength, duration } } +} + /// De/buff category ID. /// Similar to `BuffKind`, but to mark a category (for more generic usage, like /// positive/negative buffs). @@ -56,14 +63,18 @@ pub enum BuffCategory { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum ModifierKind { Additive, - Multiplicative, + Fractional, } /// Data indicating and configuring behaviour of a de/buff. #[derive(Clone, Debug, Serialize, Deserialize)] pub enum BuffEffect { /// Periodically damages or heals entity - HealthChangeOverTime { rate: f32, accumulated: f32 }, + HealthChangeOverTime { + rate: f32, + accumulated: f32, + kind: ModifierKind, + }, /// Changes maximum health by a certain amount MaxHealthModifier { value: f32, kind: ModifierKind }, } @@ -124,6 +135,7 @@ impl Buff { vec![BuffEffect::HealthChangeOverTime { rate: -data.strength, accumulated: 0.0, + kind: ModifierKind::Additive, }], data.duration, ), @@ -131,6 +143,15 @@ impl Buff { vec![BuffEffect::HealthChangeOverTime { rate: data.strength, accumulated: 0.0, + kind: ModifierKind::Additive, + }], + data.duration, + ), + BuffKind::CampfireHeal => ( + vec![BuffEffect::HealthChangeOverTime { + rate: data.strength, + accumulated: 0.0, + kind: ModifierKind::Fractional, }], data.duration, ), @@ -255,6 +276,8 @@ impl Buffs { self.force_insert(self.id_counter, buff) } + pub fn contains(&self, kind: BuffKind) -> bool { self.kinds.contains_key(&kind) } + // Iterate through buffs of a given kind in effect order (most powerful first) pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator + '_ { self.kinds diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 774f4fcb2a..22ee92c660 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,6 +1,7 @@ mod ability; mod admin; pub mod agent; +pub mod aura; pub mod beam; pub mod body; pub mod buff; @@ -28,6 +29,7 @@ pub mod visual; pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout}; pub use admin::Admin; pub use agent::{Agent, Alignment}; +pub use aura::{Aura, AuraChange, AuraKind, Auras}; pub use beam::{Beam, BeamSegment}; pub use body::{ biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object, diff --git a/common/src/event.rs b/common/src/event.rs index d53df9d410..e31bd4ef3b 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -116,6 +116,10 @@ pub enum ServerEvent { ChatCmd(EcsEntity, String), /// Send a chat message to the player from an npc or other player Chat(comp::UnresolvedChatMsg), + Aura { + entity: EcsEntity, + aura_change: comp::AuraChange, + }, Buff { entity: EcsEntity, buff_change: comp::BuffChange, diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 4aff0fbdd5..3cacc2dab1 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -14,6 +14,7 @@ sum_type! { CanBuild(comp::CanBuild), Stats(comp::Stats), Buffs(comp::Buffs), + Auras(comp::Auras), Energy(comp::Energy), Health(comp::Health), LightEmitter(comp::LightEmitter), @@ -45,6 +46,7 @@ sum_type! { CanBuild(PhantomData), Stats(PhantomData), Buffs(PhantomData), + Auras(PhantomData), Energy(PhantomData), Health(PhantomData), LightEmitter(PhantomData), @@ -76,6 +78,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Stats(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Buffs(comp) => sync::handle_insert(comp, entity, world), + EcsCompPacket::Auras(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_insert(comp, entity, world), @@ -105,6 +108,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPacket::CanBuild(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Stats(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Buffs(comp) => sync::handle_modify(comp, entity, world), + EcsCompPacket::Auras(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Energy(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Health(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world), @@ -134,6 +138,7 @@ impl sync::CompPacket for EcsCompPacket { EcsCompPhantom::CanBuild(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Stats(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Buffs(_) => sync::handle_remove::(entity, world), + EcsCompPhantom::Auras(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Energy(_) => sync::handle_remove::(entity, world), EcsCompPhantom::Health(_) => sync::handle_remove::(entity, world), EcsCompPhantom::LightEmitter(_) => { diff --git a/common/sys/src/aura.rs b/common/sys/src/aura.rs new file mode 100644 index 0000000000..f77c1aa062 --- /dev/null +++ b/common/sys/src/aura.rs @@ -0,0 +1,104 @@ +use common::{ + comp::{ + aura::AuraKey, buff, AuraChange, AuraKind, Auras, BuffKind, Buffs, CharacterState, Pos, + }, + event::{EventBus, ServerEvent}, + resources::DeltaTime, +}; +use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use std::time::Duration; + +pub struct Sys; +impl<'a> System<'a> for Sys { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + Read<'a, DeltaTime>, + ReadStorage<'a, Pos>, + Read<'a, EventBus>, + ReadStorage<'a, CharacterState>, + WriteStorage<'a, Auras>, + WriteStorage<'a, Buffs>, + ); + + fn run( + &mut self, + (entities, dt, positions, server_bus, character_states, mut auras, mut buffs): Self::SystemData, + ) { + let mut server_emitter = server_bus.emitter(); + + auras.set_event_emission(false); + + // Iterate through all entities with an aura + for (entity, pos, auras_comp) in (&entities, &positions, &mut auras).join() { + let mut expired_auras = Vec::::new(); + // Iterate through the auras attached to this entity + for (key, aura) in auras_comp.auras.iter_mut() { + // Tick the aura and subtract dt from it + if let Some(remaining_time) = &mut aura.duration { + if let Some(new_duration) = + remaining_time.checked_sub(Duration::from_secs_f32(dt.0)) + { + *remaining_time = new_duration; + } else { + *remaining_time = Duration::default(); + expired_auras.push(key); + } + } + for (target_entity, target_pos, target_character_state_maybe, target_buffs) in + (&entities, &positions, character_states.maybe(), &mut buffs).join() + { + // Ensure entity is within the aura radius + if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) { + // TODO: When more aura kinds (besides Buff) are + // implemented, match on them here + match aura.aura_kind { + AuraKind::Buff { + kind, + data, + category, + source, + } => { + // Checks if the buff is not active so it isn't applied + // every tick, but rather only once it runs out + // TODO: Check for stronger buff of same kind so it can replace + // active buff. + if !target_buffs.contains(kind) { + // Conditions for different buffs are in this match + // statement + let apply_buff = match kind { + BuffKind::CampfireHeal => matches!( + target_character_state_maybe, + Some(CharacterState::Sit) + ), + // Add other specific buff conditions here + _ => true, + }; + if apply_buff { + use buff::*; + server_emitter.emit(ServerEvent::Buff { + entity: target_entity, + buff_change: BuffChange::Add(Buff::new( + kind, + data, + vec![category], + source, + )), + }); + } + } + }, + } + } + } + } + if !expired_auras.is_empty() { + server_emitter.emit(ServerEvent::Aura { + entity, + aura_change: AuraChange::RemoveByKey(expired_auras), + }); + } + } + auras.set_event_emission(true); + } +} diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index ff4c5c55af..871d6687b9 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -79,7 +79,11 @@ impl<'a> System<'a> for Sys { // Now, execute the buff, based on it's delta for effect in &mut buff.effects { match effect { - BuffEffect::HealthChangeOverTime { rate, accumulated } => { + BuffEffect::HealthChangeOverTime { + rate, + accumulated, + kind, + } => { *accumulated += *rate * dt.0; // Apply damage only once a second (with a minimum of 1 damage), or // when a buff is removed @@ -94,23 +98,26 @@ impl<'a> System<'a> for Sys { by: buff_owner, } }; + let amount = match *kind { + ModifierKind::Additive => *accumulated as i32, + ModifierKind::Fractional => { + (health.maximum() as f32 * *accumulated) as i32 + }, + }; server_emitter.emit(ServerEvent::Damage { entity, - change: HealthChange { - amount: *accumulated as i32, - cause, - }, + change: HealthChange { amount, cause }, }); *accumulated = 0.0; }; }, BuffEffect::MaxHealthModifier { value, kind } => match kind { - ModifierKind::Multiplicative => { - health.set_maximum((health.maximum() as f32 * *value) as u32); - }, ModifierKind::Additive => { health.set_maximum((health.maximum() as f32 + *value) as u32); }, + ModifierKind::Fractional => { + health.set_maximum((health.maximum() as f32 * *value) as u32); + }, }, }; } diff --git a/common/sys/src/lib.rs b/common/sys/src/lib.rs index 1fc2614c64..ff140d5d11 100644 --- a/common/sys/src/lib.rs +++ b/common/sys/src/lib.rs @@ -1,6 +1,7 @@ #![feature(label_break_value, bool_to_option)] pub mod agent; +mod aura; mod beam; mod buff; pub mod character_behavior; @@ -28,6 +29,7 @@ pub const PROJECTILE_SYS: &str = "projectile_sys"; pub const SHOCKWAVE_SYS: &str = "shockwave_sys"; pub const STATS_SYS: &str = "stats_sys"; pub const BUFFS_SYS: &str = "buffs_sys"; +pub const AURAS_SYS: &str = "auras_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); @@ -43,4 +45,5 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]); dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]); dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); + dispatch_builder.add(aura::Sys, AURAS_SYS, &[]); } diff --git a/common/sys/src/state.rs b/common/sys/src/state.rs index e890853a02..62191885d2 100644 --- a/common/sys/src/state.rs +++ b/common/sys/src/state.rs @@ -101,6 +101,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index cb93fc4b95..a4f8924530 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -9,7 +9,12 @@ use crate::{ use chrono::{NaiveTime, Timelike}; use common::{ cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS}, - comp::{self, ChatType, Item, LightEmitter, WaypointArea}, + comp::{ + self, + aura::{Aura, AuraKind}, + buff::{BuffCategory, BuffData, BuffKind, BuffSource}, + ChatType, Item, LightEmitter, WaypointArea, + }, effect::Effect, event::{EventBus, ServerEvent}, msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral}, @@ -23,7 +28,7 @@ use common::{ }; use rand::Rng; use specs::{Builder, Entity as EcsEntity, Join, WorldExt}; -use std::convert::TryFrom; +use std::{convert::TryFrom, time::Duration}; use vek::*; use world::util::Sampler; @@ -875,6 +880,16 @@ fn handle_spawn_campfire( animated: true, }) .with(WaypointArea::default()) + .with(comp::Auras::new(Aura::new( + AuraKind::Buff { + kind: BuffKind::CampfireHeal, + data: BuffData::new(0.01, Some(Duration::from_secs(1))), + category: BuffCategory::Natural, + source: BuffSource::World, + }, + 5.0, + None, + ))) .build(); server.notify_client( diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 5f3a3161a4..4c08fa1aa8 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -2,15 +2,19 @@ use crate::{sys, Server, StateExt}; use common::{ character::CharacterId, comp::{ - self, beam, shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Item, ItemDrop, + self, + aura::{Aura, AuraKind}, + beam, + buff::{BuffCategory, BuffData, BuffKind, BuffSource}, + group, shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Item, ItemDrop, LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea, }, outcome::Outcome, rtsim::RtSimEntity, util::Dir, }; -use comp::group; use specs::{Builder, Entity as EcsEntity, WorldExt}; +use std::time::Duration; use vek::{Rgb, Vec3}; pub fn handle_initialize_character( @@ -180,5 +184,15 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { }) .with(WaypointArea::default()) .with(comp::Mass(10_f32.powi(10))) + .with(comp::Auras::new(Aura::new( + AuraKind::Buff { + kind: BuffKind::CampfireHeal, + data: BuffData::new(0.01, Some(Duration::from_secs(1))), + category: BuffCategory::Natural, + source: BuffSource::World, + }, + 5.0, + None, + ))) .build(); } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index c7bd045315..1eef4279cf 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -7,7 +7,7 @@ use crate::{ use common::{ assets::Asset, comp::{ - self, buff, + self, aura, buff, chat::{KillSource, KillType}, object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource, Item, Player, Pos, Stats, @@ -740,6 +740,24 @@ pub fn handle_level_up(server: &mut Server, entity: EcsEntity, new_level: u32) { .push(Outcome::LevelUp { pos }); } +pub fn handle_aura(server: &mut Server, entity: EcsEntity, aura_change: aura::AuraChange) { + let ecs = &server.state.ecs(); + let mut auras_all = ecs.write_storage::(); + if let Some(auras) = auras_all.get_mut(entity) { + use aura::AuraChange; + match aura_change { + AuraChange::Add(new_aura) => { + auras.insert(new_aura); + }, + AuraChange::RemoveByKey(keys) => { + for key in keys { + auras.remove(key); + } + }, + } + } +} + pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::BuffChange) { let ecs = &server.state.ecs(); let mut buffs_all = ecs.write_storage::(); diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 0b12d9560a..99c0700e82 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -8,7 +8,7 @@ use entity_creation::{ handle_loaded_character_data, handle_shockwave, handle_shoot, }; use entity_manipulation::{ - handle_buff, handle_damage, handle_delete, handle_destroy, handle_energy_change, + handle_aura, handle_buff, handle_damage, handle_delete, handle_destroy, handle_energy_change, handle_explosion, handle_knockback, handle_land_on_ground, handle_level_up, handle_respawn, }; use group_manip::handle_group; @@ -147,6 +147,10 @@ impl Server { ServerEvent::Chat(msg) => { chat_messages.push(msg); }, + ServerEvent::Aura { + entity, + aura_change, + } => handle_aura(self, entity, aura_change), ServerEvent::Buff { entity, buff_change, diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs index a61dc65b6f..283b0b8898 100644 --- a/server/src/sys/sentinel.rs +++ b/server/src/sys/sentinel.rs @@ -1,9 +1,9 @@ use super::SysTimer; use common::{ comp::{ - BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity, Group, - Health, Item, LightEmitter, Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, - Shockwave, Stats, Sticky, Vel, + Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity, + Group, Health, Item, LightEmitter, Loadout, Mass, MountState, Mounting, Ori, Player, Pos, + Scale, Shockwave, Stats, Sticky, Vel, }, msg::EcsCompPacket, span, @@ -45,6 +45,7 @@ pub struct TrackedComps<'a> { pub player: ReadStorage<'a, Player>, pub stats: ReadStorage<'a, Stats>, pub buffs: ReadStorage<'a, Buffs>, + pub auras: ReadStorage<'a, Auras>, pub energy: ReadStorage<'a, Energy>, pub health: ReadStorage<'a, Health>, pub can_build: ReadStorage<'a, CanBuild>, @@ -91,6 +92,10 @@ impl<'a> TrackedComps<'a> { .get(entity) .cloned() .map(|c| comps.push(c.into())); + self.auras + .get(entity) + .cloned() + .map(|c| comps.push(c.into())); self.energy .get(entity) .cloned() @@ -168,6 +173,7 @@ pub struct ReadTrackers<'a> { pub player: ReadExpect<'a, UpdateTracker>, pub stats: ReadExpect<'a, UpdateTracker>, pub buffs: ReadExpect<'a, UpdateTracker>, + pub auras: ReadExpect<'a, UpdateTracker>, pub energy: ReadExpect<'a, UpdateTracker>, pub health: ReadExpect<'a, UpdateTracker>, pub can_build: ReadExpect<'a, UpdateTracker>, @@ -200,6 +206,7 @@ impl<'a> ReadTrackers<'a> { .with_component(&comps.uid, &*self.player, &comps.player, filter) .with_component(&comps.uid, &*self.stats, &comps.stats, filter) .with_component(&comps.uid, &*self.buffs, &comps.buffs, filter) + .with_component(&comps.uid, &*self.auras, &comps.auras, filter) .with_component(&comps.uid, &*self.energy, &comps.energy, filter) .with_component(&comps.uid, &*self.health, &comps.health, filter) .with_component(&comps.uid, &*self.can_build, &comps.can_build, filter) @@ -239,6 +246,7 @@ pub struct WriteTrackers<'a> { player: WriteExpect<'a, UpdateTracker>, stats: WriteExpect<'a, UpdateTracker>, buffs: WriteExpect<'a, UpdateTracker>, + auras: WriteExpect<'a, UpdateTracker>, energy: WriteExpect<'a, UpdateTracker>, health: WriteExpect<'a, UpdateTracker>, can_build: WriteExpect<'a, UpdateTracker>, @@ -265,6 +273,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { trackers.player.record_changes(&comps.player); trackers.stats.record_changes(&comps.stats); trackers.buffs.record_changes(&comps.buffs); + trackers.auras.record_changes(&comps.auras); trackers.energy.record_changes(&comps.energy); trackers.health.record_changes(&comps.health); trackers.can_build.record_changes(&comps.can_build); @@ -302,6 +311,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) { log_counts!(uid, "Uids"); log_counts!(body, "Bodies"); log_counts!(buffs, "Buffs"); + log_counts!(auras, "Auras"); log_counts!(player, "Players"); log_counts!(stats, "Stats"); log_counts!(energy, "Energies"); @@ -328,6 +338,7 @@ pub fn register_trackers(world: &mut World) { world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); + world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); world.register_tracker::(); diff --git a/voxygen/src/hud/buffs.rs b/voxygen/src/hud/buffs.rs index 7035cb474b..b299b07d47 100644 --- a/voxygen/src/hud/buffs.rs +++ b/voxygen/src/hud/buffs.rs @@ -184,6 +184,7 @@ impl<'a> Widget for BuffsBar<'a> { BuffKind::Regeneration { .. } => self.imgs.buff_plus_0, BuffKind::Saturation { .. } => self.imgs.buff_saturation_0, BuffKind::Potion { .. } => self.imgs.buff_potion_0, + BuffKind::CampfireHeal { .. } => self.imgs.buff_campfire_heal_0, _ => self.imgs.missing_icon, }; let buff_widget = Image::new(buff_img).w_h(40.0, 40.0); @@ -211,6 +212,9 @@ impl<'a> Widget for BuffsBar<'a> { localized_strings.get("buff.title.saturation") }, BuffKind::Potion { .. } => localized_strings.get("buff.title.potion"), + BuffKind::CampfireHeal { .. } => { + localized_strings.get("buff.title.campfire_heal") + }, _ => localized_strings.get("buff.title.missing"), }; let remaining_time = if current_duration.is_none() { @@ -225,6 +229,9 @@ impl<'a> Widget for BuffsBar<'a> { localized_strings.get("buff.desc.saturation") }, BuffKind::Potion { .. } => localized_strings.get("buff.desc.potion"), + BuffKind::CampfireHeal { .. } => { + localized_strings.get("buff.desc.campfire_heal") + }, _ => localized_strings.get("buff.desc.missing"), }; let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove); @@ -385,6 +392,7 @@ impl<'a> Widget for BuffsBar<'a> { BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Potion { .. } => self.imgs.buff_potion_0, + BuffKind::CampfireHeal { .. } => self.imgs.buff_campfire_heal_0, }; let buff_widget = Image::new(buff_img).w_h(40.0, 40.0); // Sort buffs into rows of 6 slots @@ -412,6 +420,9 @@ impl<'a> Widget for BuffsBar<'a> { }, BuffKind::Potion { .. } => localized_strings.get("buff.title.potion"), BuffKind::Bleeding { .. } => localized_strings.get("debuff.title.bleed"), + BuffKind::CampfireHeal { .. } => { + localized_strings.get("buff.title.campfire_heal") + }, _ => localized_strings.get("buff.title.missing"), }; let remaining_time = if current_duration.is_none() { @@ -427,6 +438,9 @@ impl<'a> Widget for BuffsBar<'a> { }, BuffKind::Potion { .. } => localized_strings.get("buff.desc.potion"), BuffKind::Bleeding { .. } => localized_strings.get("debuff.desc.bleed"), + BuffKind::CampfireHeal { .. } => { + localized_strings.get("buff.desc.campfire_heal") + }, _ => localized_strings.get("buff.desc.missing"), }; let desc = if buff.is_buff { diff --git a/voxygen/src/hud/group.rs b/voxygen/src/hud/group.rs index 595a44ab99..7cc7b279ca 100644 --- a/voxygen/src/hud/group.rs +++ b/voxygen/src/hud/group.rs @@ -486,6 +486,7 @@ impl<'a> Widget for Group<'a> { BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Potion { .. } => self.imgs.buff_potion_0, + BuffKind::CampfireHeal { .. } => self.imgs.buff_campfire_heal_0, }; let buff_widget = Image::new(buff_img).w_h(15.0, 15.0); let buff_widget = if let Some(id) = prev_id { diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 2c93a68ff2..1648664d4b 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -375,6 +375,7 @@ image_ids! { buff_plus_0: "voxygen.element.icons.de_buffs.buff_plus_0", buff_saturation_0: "voxygen.element.icons.de_buffs.buff_saturation_0", buff_potion_0: "voxygen.element.icons.de_buffs.buff_potion_0", + buff_campfire_heal_0: "voxygen.element.icons.de_buffs.buff_campfire_heal_0", // Debuffs debuff_skull_0: "voxygen.element.icons.de_buffs.debuff_skull_0", diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 170c6a8d29..e4204a2893 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -247,6 +247,7 @@ impl<'a> Widget for Overhead<'a> { BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Potion { .. } => self.imgs.buff_potion_0, + BuffKind::CampfireHeal { .. } => self.imgs.buff_campfire_heal_0, }; let buff_widget = Image::new(buff_img).w_h(20.0, 20.0); // Sort buffs into rows of 5 slots