mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Aura System and Campfire Health Regen
This commit is contained in:
parent
30563f59f3
commit
71303fecfd
@ -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
|
||||
|
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -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",
|
||||
|
BIN
assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/icons/de_buffs/buff_campfire_heal_0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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.",
|
||||
|
@ -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"
|
||||
|
||||
|
83
common/src/comp/aura.rs
Normal file
83
common/src/comp/aura.rs
Normal 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>>;
|
||||
}
|
@ -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<Duration>,
|
||||
}
|
||||
|
||||
impl BuffData {
|
||||
pub fn new(strength: f32, duration: Option<Duration>) -> 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<Item = (BuffId, &Buff)> + '_ {
|
||||
self.kinds
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<comp::CanBuild>),
|
||||
Stats(PhantomData<comp::Stats>),
|
||||
Buffs(PhantomData<comp::Buffs>),
|
||||
Auras(PhantomData<comp::Auras>),
|
||||
Energy(PhantomData<comp::Energy>),
|
||||
Health(PhantomData<comp::Health>),
|
||||
LightEmitter(PhantomData<comp::LightEmitter>),
|
||||
@ -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::<comp::CanBuild>(entity, world),
|
||||
EcsCompPhantom::Stats(_) => sync::handle_remove::<comp::Stats>(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::Health(_) => sync::handle_remove::<comp::Health>(entity, world),
|
||||
EcsCompPhantom::LightEmitter(_) => {
|
||||
|
104
common/sys/src/aura.rs
Normal file
104
common/sys/src/aura.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -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, &[]);
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ impl State {
|
||||
ecs.register::<comp::Player>();
|
||||
ecs.register::<comp::Stats>();
|
||||
ecs.register::<comp::Buffs>();
|
||||
ecs.register::<comp::Auras>();
|
||||
ecs.register::<comp::Energy>();
|
||||
ecs.register::<comp::Health>();
|
||||
ecs.register::<comp::CanBuild>();
|
||||
|
@ -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(
|
||||
|
@ -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<f32>) {
|
||||
})
|
||||
.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();
|
||||
}
|
||||
|
@ -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::<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) {
|
||||
let ecs = &server.state.ecs();
|
||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||
|
@ -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,
|
||||
|
@ -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<Player>>,
|
||||
pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
|
||||
pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>,
|
||||
pub auras: ReadExpect<'a, UpdateTracker<Auras>>,
|
||||
pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
|
||||
pub health: ReadExpect<'a, UpdateTracker<Health>>,
|
||||
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.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<Player>>,
|
||||
stats: WriteExpect<'a, UpdateTracker<Stats>>,
|
||||
buffs: WriteExpect<'a, UpdateTracker<Buffs>>,
|
||||
auras: WriteExpect<'a, UpdateTracker<Auras>>,
|
||||
energy: WriteExpect<'a, UpdateTracker<Energy>>,
|
||||
health: WriteExpect<'a, UpdateTracker<Health>>,
|
||||
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.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::<Player>();
|
||||
world.register_tracker::<Stats>();
|
||||
world.register_tracker::<Buffs>();
|
||||
world.register_tracker::<Auras>();
|
||||
world.register_tracker::<Energy>();
|
||||
world.register_tracker::<Health>();
|
||||
world.register_tracker::<CanBuild>();
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user