Added combo component

This commit is contained in:
Sam 2021-02-27 14:55:06 -05:00
parent d2fffa1a92
commit f24433c8cf
11 changed files with 112 additions and 6 deletions

View File

@ -17,6 +17,7 @@ sum_type! {
Buffs(comp::Buffs), Buffs(comp::Buffs),
Auras(comp::Auras), Auras(comp::Auras),
Energy(comp::Energy), Energy(comp::Energy),
Combo(comp::Combo),
Health(comp::Health), Health(comp::Health),
Poise(comp::Poise), Poise(comp::Poise),
LightEmitter(comp::LightEmitter), LightEmitter(comp::LightEmitter),
@ -50,6 +51,7 @@ sum_type! {
Buffs(PhantomData<comp::Buffs>), Buffs(PhantomData<comp::Buffs>),
Auras(PhantomData<comp::Auras>), Auras(PhantomData<comp::Auras>),
Energy(PhantomData<comp::Energy>), Energy(PhantomData<comp::Energy>),
Combo(PhantomData<comp::Combo>),
Health(PhantomData<comp::Health>), Health(PhantomData<comp::Health>),
Poise(PhantomData<comp::Poise>), Poise(PhantomData<comp::Poise>),
LightEmitter(PhantomData<comp::LightEmitter>), LightEmitter(PhantomData<comp::LightEmitter>),
@ -83,6 +85,7 @@ impl sync::CompPacket for EcsCompPacket {
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::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::Combo(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::Poise(comp) => sync::handle_insert(comp, entity, world), EcsCompPacket::Poise(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),
@ -114,6 +117,7 @@ impl sync::CompPacket for EcsCompPacket {
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::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::Combo(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::Poise(comp) => sync::handle_modify(comp, entity, world), EcsCompPacket::Poise(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),
@ -145,6 +149,7 @@ impl sync::CompPacket for EcsCompPacket {
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::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::Combo(_) => sync::handle_remove::<comp::Combo>(entity, world),
EcsCompPhantom::Health(_) => sync::handle_remove::<comp::Health>(entity, world), EcsCompPhantom::Health(_) => sync::handle_remove::<comp::Health>(entity, world),
EcsCompPhantom::Poise(_) => sync::handle_remove::<comp::Poise>(entity, world), EcsCompPhantom::Poise(_) => sync::handle_remove::<comp::Poise>(entity, world),
EcsCompPhantom::LightEmitter(_) => { EcsCompPhantom::LightEmitter(_) => {

View File

@ -86,6 +86,13 @@ impl Attack {
self self
} }
pub fn with_combo_increment(self) -> Self {
self.with_effect(
AttackEffect::new(None, CombatEffect::Combo(1))
.with_requirement(CombatRequirement::AnyDamage),
)
}
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() } pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> { self.effects.iter() }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -195,6 +202,14 @@ impl Attack {
}); });
} }
}, },
CombatEffect::Combo(c) => {
if let Some(attacker_entity) = attacker_info.map(|a| a.entity) {
emit(ServerEvent::ComboChange {
entity: attacker_entity,
change: *c,
});
}
},
} }
} }
} }
@ -303,6 +318,14 @@ impl Attack {
}); });
} }
}, },
CombatEffect::Combo(c) => {
if let Some(attacker_entity) = attacker_info.map(|a| a.entity) {
emit(ServerEvent::ComboChange {
entity: attacker_entity,
change: c,
});
}
},
} }
} }
} }
@ -368,6 +391,7 @@ pub enum CombatEffect {
EnergyReward(f32), EnergyReward(f32),
Lifesteal(f32), Lifesteal(f32),
Poise(f32), Poise(f32),
Combo(i32),
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]

39
common/src/comp/combo.rs Normal file
View File

@ -0,0 +1,39 @@
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Combo {
counter: u32,
last_increase: f64,
}
impl Default for Combo {
fn default() -> Self {
Self {
counter: 0,
last_increase: 0.0,
}
}
}
impl Combo {
pub fn counter(&self) -> u32 { self.counter }
pub fn last_increase(&self) -> f64 { self.last_increase }
pub fn reset(&mut self) { self.counter = 0; }
pub fn increase_by(&mut self, amount: u32, time: f64) {
self.counter = self.counter.saturating_add(amount);
self.last_increase = time;
}
pub fn decrease_by(&mut self, amount: u32) {
self.counter = self.counter.saturating_sub(amount);
}
}
impl Component for Combo {
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
}

View File

@ -8,6 +8,7 @@ pub mod buff;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
mod character_state; mod character_state;
#[cfg(not(target_arch = "wasm32"))] pub mod chat; #[cfg(not(target_arch = "wasm32"))] pub mod chat;
pub mod combo;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
mod controller; mod controller;
#[cfg(not(target_arch = "wasm32"))] mod energy; #[cfg(not(target_arch = "wasm32"))] mod energy;
@ -58,6 +59,7 @@ pub use self::{
chat::{ chat::{
ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg, ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg,
}, },
combo::Combo,
controller::{ controller::{
Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input, Climb, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, Input,
InventoryManip, LoadoutManip, MountState, Mounting, SlotManip, InventoryManip, LoadoutManip, MountState, Mounting, SlotManip,

View File

@ -143,6 +143,10 @@ pub enum ServerEvent {
entity: EcsEntity, entity: EcsEntity,
change: comp::EnergyChange, change: comp::EnergyChange,
}, },
ComboChange {
entity: EcsEntity,
change: i32,
},
} }
pub struct EventBus<E> { pub struct EventBus<E> {

View File

@ -214,7 +214,8 @@ impl CharacterBehavior for Data {
.with_crit(0.5, 1.3) .with_crit(0.5, 1.3)
.with_effect(energy) .with_effect(energy)
.with_effect(poise) .with_effect(poise)
.with_effect(knockback); .with_effect(knockback)
.with_combo_increment();
data.updater.insert(data.entity, Melee { data.updater.insert(data.entity, Melee {
attack, attack,

View File

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

View File

@ -1,13 +1,13 @@
use common::{ use common::{
comp::{ comp::{
skills::{GeneralSkill, Skill}, skills::{GeneralSkill, Skill},
Body, CharacterState, Energy, EnergyChange, EnergySource, Health, Poise, PoiseChange, Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health, Poise,
PoiseSource, Pos, Stats, PoiseChange, PoiseSource, Pos, Stats,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
metrics::SysMetrics, metrics::SysMetrics,
outcome::Outcome, outcome::Outcome,
resources::DeltaTime, resources::{DeltaTime, Time},
span, span,
uid::Uid, uid::Uid,
}; };
@ -20,11 +20,13 @@ use vek::Vec3;
const ENERGY_REGEN_ACCEL: f32 = 10.0; const ENERGY_REGEN_ACCEL: f32 = 10.0;
const POISE_REGEN_ACCEL: f32 = 2.0; const POISE_REGEN_ACCEL: f32 = 2.0;
const COMBO_DECAY_START: f64 = 10.0; // seconds
#[derive(SystemData)] #[derive(SystemData)]
pub struct ReadData<'a> { pub struct ReadData<'a> {
entities: Entities<'a>, entities: Entities<'a>,
dt: Read<'a, DeltaTime>, dt: Read<'a, DeltaTime>,
time: Read<'a, Time>,
server_bus: Read<'a, EventBus<ServerEvent>>, server_bus: Read<'a, EventBus<ServerEvent>>,
metrics: ReadExpect<'a, SysMetrics>, metrics: ReadExpect<'a, SysMetrics>,
positions: ReadStorage<'a, Pos>, positions: ReadStorage<'a, Pos>,
@ -43,6 +45,7 @@ impl<'a> System<'a> for Sys {
WriteStorage<'a, Health>, WriteStorage<'a, Health>,
WriteStorage<'a, Poise>, WriteStorage<'a, Poise>,
WriteStorage<'a, Energy>, WriteStorage<'a, Energy>,
WriteStorage<'a, Combo>,
Write<'a, Vec<Outcome>>, Write<'a, Vec<Outcome>>,
); );
@ -54,6 +57,7 @@ impl<'a> System<'a> for Sys {
mut healths, mut healths,
mut poises, mut poises,
mut energies, mut energies,
mut combos,
mut outcomes, mut outcomes,
): Self::SystemData, ): Self::SystemData,
) { ) {
@ -256,6 +260,13 @@ impl<'a> System<'a> for Sys {
} }
} }
// Decay combo
for (_, mut combo) in (&read_data.entities, &mut combos).join() {
if combo.counter() > 0 && read_data.time.0 - combo.last_increase() > COMBO_DECAY_START {
combo.reset();
}
}
read_data.metrics.stats_ns.store( read_data.metrics.stats_ns.store(
start_time.elapsed().as_nanos() as u64, start_time.elapsed().as_nanos() as u64,
std::sync::atomic::Ordering::Relaxed, std::sync::atomic::Ordering::Relaxed,

View File

@ -20,6 +20,7 @@ use common::{
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
lottery::Lottery, lottery::Lottery,
outcome::Outcome, outcome::Outcome,
resources::Time,
rtsim::RtSimEntity, rtsim::RtSimEntity,
terrain::{Block, TerrainGrid}, terrain::{Block, TerrainGrid},
uid::{Uid, UidAllocator}, uid::{Uid, UidAllocator},
@ -880,3 +881,15 @@ fn handle_exp_gain(
exp: exp_reward as i32, exp: exp_reward as i32,
}); });
} }
pub fn handle_combo_change(server: &Server, entity: EcsEntity, change: i32) {
let ecs = &server.state.ecs();
if let Some(mut combo) = ecs.write_storage::<comp::Combo>().get_mut(entity) {
if change > 0 {
let time = ecs.read_resource::<Time>();
combo.increase_by(change as u32, time.0);
} else {
combo.decrease_by(change.abs() as u32);
}
}
}

View File

@ -8,8 +8,9 @@ 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_aura, handle_buff, handle_damage, handle_delete, handle_destroy, handle_energy_change, handle_aura, handle_buff, handle_combo_change, handle_damage, handle_delete, handle_destroy,
handle_explosion, handle_knockback, handle_land_on_ground, handle_poise, handle_respawn, handle_energy_change, handle_explosion, handle_knockback, handle_land_on_ground, handle_poise,
handle_respawn,
}; };
use group_manip::handle_group; use group_manip::handle_group;
use interaction::{ use interaction::{
@ -182,6 +183,9 @@ impl Server {
ServerEvent::EnergyChange { entity, change } => { ServerEvent::EnergyChange { entity, change } => {
handle_energy_change(&self, entity, change) handle_energy_change(&self, entity, change)
}, },
ServerEvent::ComboChange { entity, change } => {
handle_combo_change(&self, entity, change)
},
} }
} }

View File

@ -178,6 +178,7 @@ impl StateExt for State {
.with(comp::CharacterState::default()) .with(comp::CharacterState::default())
.with(inventory) .with(inventory)
.with(comp::Buffs::default()) .with(comp::Buffs::default())
.with(comp::Combo::default())
} }
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder { fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder {
@ -269,6 +270,7 @@ impl StateExt for State {
comp::Alignment::Owned(self.read_component_copied(entity).unwrap()), comp::Alignment::Owned(self.read_component_copied(entity).unwrap()),
); );
self.write_component(entity, comp::Buffs::default()); self.write_component(entity, comp::Buffs::default());
self.write_component(entity, comp::Combo::default());
// Make sure physics components are updated // Make sure physics components are updated
self.write_component(entity, comp::ForceUpdate); self.write_component(entity, comp::ForceUpdate);