Aura System and Campfire Health Regen

This commit is contained in:
James Melkonian 2020-12-04 22:24:56 +00:00 committed by Samuel Keiffer
parent 30563f59f3
commit 71303fecfd
23 changed files with 351 additions and 20 deletions

View File

@ -10,11 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Added chat commands for inviting, kicking, leaving, and promoting in groups - Added chat commands for inviting, kicking, leaving, and promoting in groups
- Aura system
- Campfire resting heal
### Changed ### Changed
- Doubled range of ScaleMode slider when set to Custom - Doubled range of ScaleMode slider when set to Custom
### Removed ### Removed
- SSAAx4 option - SSAAx4 option
### Fixed ### Fixed

10
Cargo.lock generated
View File

@ -4393,6 +4393,15 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "slotmap"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "0.6.13" version = "0.6.13"
@ -5373,6 +5382,7 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"slab", "slab",
"slotmap",
"specs", "specs",
"specs-idvs", "specs-idvs",
"spin_sleep", "spin_sleep",

Binary file not shown.

View File

@ -550,6 +550,8 @@ Protection
"buff.desc.potion": "Drinking...", "buff.desc.potion": "Drinking...",
"buff.title.saturation": "Saturation", "buff.title.saturation": "Saturation",
"buff.desc.saturation": "Gain health over time from consumables.", "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 // Debuffs
"debuff.title.bleed": "Bleeding", "debuff.title.bleed": "Bleeding",
"debuff.desc.bleed": "Inflicts regular damage.", "debuff.desc.bleed": "Inflicts regular damage.",

View File

@ -39,6 +39,7 @@ authc = { git = "https://gitlab.com/veloren/auth.git", rev = "b943c85e4a38f5ec60
# Data structures # Data structures
hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] } hashbrown = { version = "0.7.2", features = ["rayon", "serde", "nightly"] }
slotmap = { version = "0.4.0", features = ["serde", "unstable"] }
indexmap = "1.3.0" indexmap = "1.3.0"
slab = "0.4.2" slab = "0.4.2"

83
common/src/comp/aura.rs Normal file
View File

@ -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<Duration>,
/* 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<AuraKey>),
}
impl Aura {
/// Creates a new Aura to be assigned to an entity
pub fn new(aura_kind: AuraKind, radius: f32, duration: Option<Duration>) -> 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<AuraKey, Aura>,
}
impl Auras {
pub fn new(aura: Aura) -> Self {
let mut auras: SlotMap<AuraKey, Aura> = 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<Self, IdvStorage<Self>>;
}

View File

@ -17,8 +17,10 @@ pub enum BuffKind {
/// Lower a creature's max health /// Lower a creature's max health
/// Currently placeholder buff to show other stuff is possible /// Currently placeholder buff to show other stuff is possible
Cursed, Cursed,
// Applied when drinking a potion /// Applied when drinking a potion
Potion, Potion,
/// Applied when sitting at a campfire
CampfireHeal,
} }
impl BuffKind { impl BuffKind {
@ -30,6 +32,7 @@ impl BuffKind {
BuffKind::Bleeding { .. } => false, BuffKind::Bleeding { .. } => false,
BuffKind::Cursed { .. } => false, BuffKind::Cursed { .. } => false,
BuffKind::Potion { .. } => true, BuffKind::Potion { .. } => true,
BuffKind::CampfireHeal { .. } => true,
} }
} }
} }
@ -41,6 +44,10 @@ pub struct BuffData {
pub duration: Option<Duration>, pub duration: Option<Duration>,
} }
impl BuffData {
pub fn new(strength: f32, duration: Option<Duration>) -> Self { Self { strength, duration } }
}
/// De/buff category ID. /// De/buff category ID.
/// Similar to `BuffKind`, but to mark a category (for more generic usage, like /// Similar to `BuffKind`, but to mark a category (for more generic usage, like
/// positive/negative buffs). /// positive/negative buffs).
@ -56,14 +63,18 @@ pub enum BuffCategory {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ModifierKind { pub enum ModifierKind {
Additive, Additive,
Multiplicative, Fractional,
} }
/// Data indicating and configuring behaviour of a de/buff. /// Data indicating and configuring behaviour of a de/buff.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BuffEffect { pub enum BuffEffect {
/// Periodically damages or heals entity /// Periodically damages or heals entity
HealthChangeOverTime { rate: f32, accumulated: f32 }, HealthChangeOverTime {
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 },
} }
@ -124,6 +135,7 @@ impl Buff {
vec![BuffEffect::HealthChangeOverTime { vec![BuffEffect::HealthChangeOverTime {
rate: -data.strength, rate: -data.strength,
accumulated: 0.0, accumulated: 0.0,
kind: ModifierKind::Additive,
}], }],
data.duration, data.duration,
), ),
@ -131,6 +143,15 @@ impl Buff {
vec![BuffEffect::HealthChangeOverTime { vec![BuffEffect::HealthChangeOverTime {
rate: data.strength, rate: data.strength,
accumulated: 0.0, accumulated: 0.0,
kind: ModifierKind::Additive,
}],
data.duration,
),
BuffKind::CampfireHeal => (
vec![BuffEffect::HealthChangeOverTime {
rate: data.strength,
accumulated: 0.0,
kind: ModifierKind::Fractional,
}], }],
data.duration, data.duration,
), ),
@ -255,6 +276,8 @@ impl Buffs {
self.force_insert(self.id_counter, buff) 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) // Iterate through buffs of a given kind in effect order (most powerful first)
pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffId, &Buff)> + '_ { pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffId, &Buff)> + '_ {
self.kinds self.kinds

View File

@ -1,6 +1,7 @@
mod ability; mod ability;
mod admin; mod admin;
pub mod agent; pub mod agent;
pub mod aura;
pub mod beam; pub mod beam;
pub mod body; pub mod body;
pub mod buff; pub mod buff;
@ -28,6 +29,7 @@ pub mod visual;
pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout}; pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout};
pub use admin::Admin; pub use admin::Admin;
pub use agent::{Agent, Alignment}; pub use agent::{Agent, Alignment};
pub use aura::{Aura, AuraChange, AuraKind, Auras};
pub use beam::{Beam, BeamSegment}; pub use beam::{Beam, BeamSegment};
pub use body::{ pub use body::{
biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object, biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object,

View File

@ -116,6 +116,10 @@ pub enum ServerEvent {
ChatCmd(EcsEntity, String), ChatCmd(EcsEntity, String),
/// 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),
Aura {
entity: EcsEntity,
aura_change: comp::AuraChange,
},
Buff { Buff {
entity: EcsEntity, entity: EcsEntity,
buff_change: comp::BuffChange, buff_change: comp::BuffChange,

View File

@ -14,6 +14,7 @@ sum_type! {
CanBuild(comp::CanBuild), CanBuild(comp::CanBuild),
Stats(comp::Stats), Stats(comp::Stats),
Buffs(comp::Buffs), Buffs(comp::Buffs),
Auras(comp::Auras),
Energy(comp::Energy), Energy(comp::Energy),
Health(comp::Health), Health(comp::Health),
LightEmitter(comp::LightEmitter), LightEmitter(comp::LightEmitter),
@ -45,6 +46,7 @@ sum_type! {
CanBuild(PhantomData<comp::CanBuild>), CanBuild(PhantomData<comp::CanBuild>),
Stats(PhantomData<comp::Stats>), Stats(PhantomData<comp::Stats>),
Buffs(PhantomData<comp::Buffs>), Buffs(PhantomData<comp::Buffs>),
Auras(PhantomData<comp::Auras>),
Energy(PhantomData<comp::Energy>), Energy(PhantomData<comp::Energy>),
Health(PhantomData<comp::Health>), Health(PhantomData<comp::Health>),
LightEmitter(PhantomData<comp::LightEmitter>), LightEmitter(PhantomData<comp::LightEmitter>),
@ -76,6 +78,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::CanBuild(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Stats(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::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::Energy(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Health(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), 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::CanBuild(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Stats(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::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::Energy(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Health(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), EcsCompPacket::LightEmitter(comp) => sync::handle_modify(comp, entity, world),
@ -134,6 +138,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world), EcsCompPhantom::CanBuild(_) => sync::handle_remove::<comp::CanBuild>(entity, world),
EcsCompPhantom::Stats(_) => sync::handle_remove::<comp::Stats>(entity, world), EcsCompPhantom::Stats(_) => sync::handle_remove::<comp::Stats>(entity, world),
EcsCompPhantom::Buffs(_) => sync::handle_remove::<comp::Buffs>(entity, world), EcsCompPhantom::Buffs(_) => sync::handle_remove::<comp::Buffs>(entity, world),
EcsCompPhantom::Auras(_) => sync::handle_remove::<comp::Auras>(entity, world),
EcsCompPhantom::Energy(_) => sync::handle_remove::<comp::Energy>(entity, world), EcsCompPhantom::Energy(_) => sync::handle_remove::<comp::Energy>(entity, world),
EcsCompPhantom::Health(_) => sync::handle_remove::<comp::Health>(entity, world), EcsCompPhantom::Health(_) => sync::handle_remove::<comp::Health>(entity, world),
EcsCompPhantom::LightEmitter(_) => { EcsCompPhantom::LightEmitter(_) => {

104
common/sys/src/aura.rs Normal file
View File

@ -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<ServerEvent>>,
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::<AuraKey>::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);
}
}

View File

@ -79,7 +79,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 &mut buff.effects {
match effect { match effect {
BuffEffect::HealthChangeOverTime { rate, accumulated } => { BuffEffect::HealthChangeOverTime {
rate,
accumulated,
kind,
} => {
*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
// when a buff is removed // when a buff is removed
@ -94,23 +98,26 @@ impl<'a> System<'a> for Sys {
by: buff_owner, 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 { server_emitter.emit(ServerEvent::Damage {
entity, entity,
change: HealthChange { change: HealthChange { amount, cause },
amount: *accumulated as i32,
cause,
},
}); });
*accumulated = 0.0; *accumulated = 0.0;
}; };
}, },
BuffEffect::MaxHealthModifier { value, kind } => match kind { BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Multiplicative => {
health.set_maximum((health.maximum() as f32 * *value) as u32);
},
ModifierKind::Additive => { ModifierKind::Additive => {
health.set_maximum((health.maximum() as f32 + *value) as u32); health.set_maximum((health.maximum() as f32 + *value) as u32);
}, },
ModifierKind::Fractional => {
health.set_maximum((health.maximum() as f32 * *value) as u32);
},
}, },
}; };
} }

View File

@ -1,6 +1,7 @@
#![feature(label_break_value, bool_to_option)] #![feature(label_break_value, bool_to_option)]
pub mod agent; pub mod agent;
mod aura;
mod beam; mod beam;
mod buff; mod buff;
pub mod character_behavior; pub mod character_behavior;
@ -28,6 +29,7 @@ pub const PROJECTILE_SYS: &str = "projectile_sys";
pub const SHOCKWAVE_SYS: &str = "shockwave_sys"; pub const SHOCKWAVE_SYS: &str = "shockwave_sys";
pub const STATS_SYS: &str = "stats_sys"; pub const STATS_SYS: &str = "stats_sys";
pub const BUFFS_SYS: &str = "buffs_sys"; pub const BUFFS_SYS: &str = "buffs_sys";
pub const AURAS_SYS: &str = "auras_sys";
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); 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(shockwave::Sys, SHOCKWAVE_SYS, &[PHYS_SYS]);
dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]); dispatch_builder.add(beam::Sys, BEAM_SYS, &[PHYS_SYS]);
dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]); dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]);
dispatch_builder.add(aura::Sys, AURAS_SYS, &[]);
} }

View File

@ -101,6 +101,7 @@ impl State {
ecs.register::<comp::Player>(); ecs.register::<comp::Player>();
ecs.register::<comp::Stats>(); ecs.register::<comp::Stats>();
ecs.register::<comp::Buffs>(); ecs.register::<comp::Buffs>();
ecs.register::<comp::Auras>();
ecs.register::<comp::Energy>(); ecs.register::<comp::Energy>();
ecs.register::<comp::Health>(); ecs.register::<comp::Health>();
ecs.register::<comp::CanBuild>(); ecs.register::<comp::CanBuild>();

View File

@ -9,7 +9,12 @@ use crate::{
use chrono::{NaiveTime, Timelike}; use chrono::{NaiveTime, Timelike};
use common::{ use common::{
cmd::{ChatCommand, CHAT_COMMANDS, CHAT_SHORTCUTS}, 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, effect::Effect,
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral}, msg::{DisconnectReason, Notification, PlayerListUpdate, ServerGeneral},
@ -23,7 +28,7 @@ use common::{
}; };
use rand::Rng; use rand::Rng;
use specs::{Builder, Entity as EcsEntity, Join, WorldExt}; use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
use std::convert::TryFrom; use std::{convert::TryFrom, time::Duration};
use vek::*; use vek::*;
use world::util::Sampler; use world::util::Sampler;
@ -875,6 +880,16 @@ fn handle_spawn_campfire(
animated: true, animated: true,
}) })
.with(WaypointArea::default()) .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(); .build();
server.notify_client( server.notify_client(

View File

@ -2,15 +2,19 @@ use crate::{sys, Server, StateExt};
use common::{ use common::{
character::CharacterId, character::CharacterId,
comp::{ 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, LightEmitter, Loadout, Ori, Pos, Projectile, Scale, Stats, Vel, WaypointArea,
}, },
outcome::Outcome, outcome::Outcome,
rtsim::RtSimEntity, rtsim::RtSimEntity,
util::Dir, util::Dir,
}; };
use comp::group;
use specs::{Builder, Entity as EcsEntity, WorldExt}; use specs::{Builder, Entity as EcsEntity, WorldExt};
use std::time::Duration;
use vek::{Rgb, Vec3}; use vek::{Rgb, Vec3};
pub fn handle_initialize_character( pub fn handle_initialize_character(
@ -180,5 +184,15 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
}) })
.with(WaypointArea::default()) .with(WaypointArea::default())
.with(comp::Mass(10_f32.powi(10))) .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(); .build();
} }

View File

@ -7,7 +7,7 @@ use crate::{
use common::{ use common::{
assets::Asset, assets::Asset,
comp::{ comp::{
self, buff, self, aura, buff,
chat::{KillSource, KillType}, chat::{KillSource, KillType},
object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource, object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource,
Item, Player, Pos, Stats, 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 }); .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::<comp::Auras>();
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) { 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>();

View File

@ -8,7 +8,7 @@ use entity_creation::{
handle_loaded_character_data, handle_shockwave, handle_shoot, handle_loaded_character_data, handle_shockwave, handle_shoot,
}; };
use entity_manipulation::{ 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, handle_explosion, handle_knockback, handle_land_on_ground, handle_level_up, handle_respawn,
}; };
use group_manip::handle_group; use group_manip::handle_group;
@ -147,6 +147,10 @@ impl Server {
ServerEvent::Chat(msg) => { ServerEvent::Chat(msg) => {
chat_messages.push(msg); chat_messages.push(msg);
}, },
ServerEvent::Aura {
entity,
aura_change,
} => handle_aura(self, entity, aura_change),
ServerEvent::Buff { ServerEvent::Buff {
entity, entity,
buff_change, buff_change,

View File

@ -1,9 +1,9 @@
use super::SysTimer; use super::SysTimer;
use common::{ use common::{
comp::{ comp::{
BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity, Group, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Energy, Gravity,
Health, Item, LightEmitter, Loadout, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Group, Health, Item, LightEmitter, Loadout, Mass, MountState, Mounting, Ori, Player, Pos,
Shockwave, Stats, Sticky, Vel, Scale, Shockwave, Stats, Sticky, Vel,
}, },
msg::EcsCompPacket, msg::EcsCompPacket,
span, span,
@ -45,6 +45,7 @@ pub struct TrackedComps<'a> {
pub player: ReadStorage<'a, Player>, pub player: ReadStorage<'a, Player>,
pub stats: ReadStorage<'a, Stats>, pub stats: ReadStorage<'a, Stats>,
pub buffs: ReadStorage<'a, Buffs>, pub buffs: ReadStorage<'a, Buffs>,
pub auras: ReadStorage<'a, Auras>,
pub energy: ReadStorage<'a, Energy>, pub energy: ReadStorage<'a, Energy>,
pub health: ReadStorage<'a, Health>, pub health: ReadStorage<'a, Health>,
pub can_build: ReadStorage<'a, CanBuild>, pub can_build: ReadStorage<'a, CanBuild>,
@ -91,6 +92,10 @@ impl<'a> TrackedComps<'a> {
.get(entity) .get(entity)
.cloned() .cloned()
.map(|c| comps.push(c.into())); .map(|c| comps.push(c.into()));
self.auras
.get(entity)
.cloned()
.map(|c| comps.push(c.into()));
self.energy self.energy
.get(entity) .get(entity)
.cloned() .cloned()
@ -168,6 +173,7 @@ pub struct ReadTrackers<'a> {
pub player: ReadExpect<'a, UpdateTracker<Player>>, pub player: ReadExpect<'a, UpdateTracker<Player>>,
pub stats: ReadExpect<'a, UpdateTracker<Stats>>, pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>, pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>,
pub auras: ReadExpect<'a, UpdateTracker<Auras>>,
pub energy: ReadExpect<'a, UpdateTracker<Energy>>, pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
pub health: ReadExpect<'a, UpdateTracker<Health>>, pub health: ReadExpect<'a, UpdateTracker<Health>>,
pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>, pub can_build: ReadExpect<'a, UpdateTracker<CanBuild>>,
@ -200,6 +206,7 @@ impl<'a> ReadTrackers<'a> {
.with_component(&comps.uid, &*self.player, &comps.player, filter) .with_component(&comps.uid, &*self.player, &comps.player, filter)
.with_component(&comps.uid, &*self.stats, &comps.stats, filter) .with_component(&comps.uid, &*self.stats, &comps.stats, filter)
.with_component(&comps.uid, &*self.buffs, &comps.buffs, 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.energy, &comps.energy, filter)
.with_component(&comps.uid, &*self.health, &comps.health, filter) .with_component(&comps.uid, &*self.health, &comps.health, filter)
.with_component(&comps.uid, &*self.can_build, &comps.can_build, filter) .with_component(&comps.uid, &*self.can_build, &comps.can_build, filter)
@ -239,6 +246,7 @@ pub struct WriteTrackers<'a> {
player: WriteExpect<'a, UpdateTracker<Player>>, player: WriteExpect<'a, UpdateTracker<Player>>,
stats: WriteExpect<'a, UpdateTracker<Stats>>, stats: WriteExpect<'a, UpdateTracker<Stats>>,
buffs: WriteExpect<'a, UpdateTracker<Buffs>>, buffs: WriteExpect<'a, UpdateTracker<Buffs>>,
auras: WriteExpect<'a, UpdateTracker<Auras>>,
energy: WriteExpect<'a, UpdateTracker<Energy>>, energy: WriteExpect<'a, UpdateTracker<Energy>>,
health: WriteExpect<'a, UpdateTracker<Health>>, health: WriteExpect<'a, UpdateTracker<Health>>,
can_build: WriteExpect<'a, UpdateTracker<CanBuild>>, can_build: WriteExpect<'a, UpdateTracker<CanBuild>>,
@ -265,6 +273,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
trackers.player.record_changes(&comps.player); trackers.player.record_changes(&comps.player);
trackers.stats.record_changes(&comps.stats); trackers.stats.record_changes(&comps.stats);
trackers.buffs.record_changes(&comps.buffs); trackers.buffs.record_changes(&comps.buffs);
trackers.auras.record_changes(&comps.auras);
trackers.energy.record_changes(&comps.energy); trackers.energy.record_changes(&comps.energy);
trackers.health.record_changes(&comps.health); trackers.health.record_changes(&comps.health);
trackers.can_build.record_changes(&comps.can_build); 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!(uid, "Uids");
log_counts!(body, "Bodies"); log_counts!(body, "Bodies");
log_counts!(buffs, "Buffs"); log_counts!(buffs, "Buffs");
log_counts!(auras, "Auras");
log_counts!(player, "Players"); log_counts!(player, "Players");
log_counts!(stats, "Stats"); log_counts!(stats, "Stats");
log_counts!(energy, "Energies"); log_counts!(energy, "Energies");
@ -328,6 +338,7 @@ pub fn register_trackers(world: &mut World) {
world.register_tracker::<Player>(); world.register_tracker::<Player>();
world.register_tracker::<Stats>(); world.register_tracker::<Stats>();
world.register_tracker::<Buffs>(); world.register_tracker::<Buffs>();
world.register_tracker::<Auras>();
world.register_tracker::<Energy>(); world.register_tracker::<Energy>();
world.register_tracker::<Health>(); world.register_tracker::<Health>();
world.register_tracker::<CanBuild>(); world.register_tracker::<CanBuild>();

View File

@ -184,6 +184,7 @@ impl<'a> Widget for BuffsBar<'a> {
BuffKind::Regeneration { .. } => self.imgs.buff_plus_0, BuffKind::Regeneration { .. } => self.imgs.buff_plus_0,
BuffKind::Saturation { .. } => self.imgs.buff_saturation_0, BuffKind::Saturation { .. } => self.imgs.buff_saturation_0,
BuffKind::Potion { .. } => self.imgs.buff_potion_0, BuffKind::Potion { .. } => self.imgs.buff_potion_0,
BuffKind::CampfireHeal { .. } => self.imgs.buff_campfire_heal_0,
_ => self.imgs.missing_icon, _ => self.imgs.missing_icon,
}; };
let buff_widget = Image::new(buff_img).w_h(40.0, 40.0); 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") localized_strings.get("buff.title.saturation")
}, },
BuffKind::Potion { .. } => localized_strings.get("buff.title.potion"), BuffKind::Potion { .. } => localized_strings.get("buff.title.potion"),
BuffKind::CampfireHeal { .. } => {
localized_strings.get("buff.title.campfire_heal")
},
_ => localized_strings.get("buff.title.missing"), _ => localized_strings.get("buff.title.missing"),
}; };
let remaining_time = if current_duration.is_none() { let remaining_time = if current_duration.is_none() {
@ -225,6 +229,9 @@ impl<'a> Widget for BuffsBar<'a> {
localized_strings.get("buff.desc.saturation") localized_strings.get("buff.desc.saturation")
}, },
BuffKind::Potion { .. } => localized_strings.get("buff.desc.potion"), BuffKind::Potion { .. } => localized_strings.get("buff.desc.potion"),
BuffKind::CampfireHeal { .. } => {
localized_strings.get("buff.desc.campfire_heal")
},
_ => localized_strings.get("buff.desc.missing"), _ => localized_strings.get("buff.desc.missing"),
}; };
let desc = format!("{}\n\n{}\n\n{}", desc_txt, remaining_time, click_to_remove); 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::Bleeding { .. } => self.imgs.debuff_bleed_0,
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
BuffKind::Potion { .. } => self.imgs.buff_potion_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); let buff_widget = Image::new(buff_img).w_h(40.0, 40.0);
// Sort buffs into rows of 6 slots // 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::Potion { .. } => localized_strings.get("buff.title.potion"),
BuffKind::Bleeding { .. } => localized_strings.get("debuff.title.bleed"), BuffKind::Bleeding { .. } => localized_strings.get("debuff.title.bleed"),
BuffKind::CampfireHeal { .. } => {
localized_strings.get("buff.title.campfire_heal")
},
_ => localized_strings.get("buff.title.missing"), _ => localized_strings.get("buff.title.missing"),
}; };
let remaining_time = if current_duration.is_none() { 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::Potion { .. } => localized_strings.get("buff.desc.potion"),
BuffKind::Bleeding { .. } => localized_strings.get("debuff.desc.bleed"), BuffKind::Bleeding { .. } => localized_strings.get("debuff.desc.bleed"),
BuffKind::CampfireHeal { .. } => {
localized_strings.get("buff.desc.campfire_heal")
},
_ => localized_strings.get("buff.desc.missing"), _ => localized_strings.get("buff.desc.missing"),
}; };
let desc = if buff.is_buff { let desc = if buff.is_buff {

View File

@ -486,6 +486,7 @@ impl<'a> Widget for Group<'a> {
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0, BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
BuffKind::Potion { .. } => self.imgs.buff_potion_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 = Image::new(buff_img).w_h(15.0, 15.0);
let buff_widget = if let Some(id) = prev_id { let buff_widget = if let Some(id) = prev_id {

View File

@ -375,6 +375,7 @@ image_ids! {
buff_plus_0: "voxygen.element.icons.de_buffs.buff_plus_0", buff_plus_0: "voxygen.element.icons.de_buffs.buff_plus_0",
buff_saturation_0: "voxygen.element.icons.de_buffs.buff_saturation_0", buff_saturation_0: "voxygen.element.icons.de_buffs.buff_saturation_0",
buff_potion_0: "voxygen.element.icons.de_buffs.buff_potion_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 // Debuffs
debuff_skull_0: "voxygen.element.icons.de_buffs.debuff_skull_0", debuff_skull_0: "voxygen.element.icons.de_buffs.debuff_skull_0",

View File

@ -247,6 +247,7 @@ impl<'a> Widget for Overhead<'a> {
BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0, BuffKind::Bleeding { .. } => self.imgs.debuff_bleed_0,
BuffKind::Cursed { .. } => self.imgs.debuff_skull_0, BuffKind::Cursed { .. } => self.imgs.debuff_skull_0,
BuffKind::Potion { .. } => self.imgs.buff_potion_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); let buff_widget = Image::new(buff_img).w_h(20.0, 20.0);
// Sort buffs into rows of 5 slots // Sort buffs into rows of 5 slots