From 717142d5ea616845be43b5bbcf27644155d183ea Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 8 Aug 2020 15:53:55 -0500 Subject: [PATCH] Started to implement shockwave system. --- common/src/comp/ability.rs | 35 ++++++ common/src/comp/character_state.rs | 5 + common/src/comp/inventory/item/tool.rs | 30 +++-- common/src/comp/mod.rs | 2 + common/src/comp/shockwave.rs | 25 +++++ common/src/event.rs | 1 + common/src/states/ground_shockwave.rs | 103 ++++++++++++++++++ common/src/states/mod.rs | 1 + common/src/sys/character_behavior.rs | 2 + common/src/sys/stats.rs | 3 +- server/src/events/entity_creation.rs | 8 +- server/src/events/mod.rs | 1 + .../audio/sfx/event_mapper/combat/tests.rs | 1 + 13 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 common/src/comp/shockwave.rs create mode 100644 common/src/states/ground_shockwave.rs diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 2477760ea7..843bea5933 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -24,6 +24,7 @@ pub enum CharacterAbilityType { TripleStrike(Stage), LeapMelee, SpinMelee, + GroundShockwave, } impl From<&CharacterState> for CharacterAbilityType { @@ -38,6 +39,7 @@ impl From<&CharacterState> for CharacterAbilityType { CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage), CharacterState::SpinMelee(_) => Self::SpinMelee, CharacterState::ChargedRanged(_) => Self::ChargedRanged, + CharacterState::GroundShockwave(_) => Self::ChargedRanged, _ => Self::BasicMelee, } } @@ -110,6 +112,16 @@ pub enum CharacterAbility { initial_projectile_speed: f32, max_projectile_speed: f32, }, + GroundShockwave { + energy_cost: u32, + buildup_duration: Duration, + recover_duration: Duration, + damage: u32, + knockback: f32, + shockwave_angle: f32, + shockwave_speed: f32, + shockwave_duration: Duration, + }, } impl CharacterAbility { @@ -155,6 +167,10 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), + CharacterAbility::GroundShockwave { energy_cost, .. } => update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok(), _ => true, } } @@ -392,6 +408,25 @@ impl From<&CharacterAbility> for CharacterState { initial_projectile_speed: *initial_projectile_speed, max_projectile_speed: *max_projectile_speed, }), + CharacterAbility::GroundShockwave { + energy_cost: _, + buildup_duration, + recover_duration, + damage, + knockback, + shockwave_angle, + shockwave_speed, + shockwave_duration, + } => CharacterState::GroundShockwave(ground_shockwave::Data { + exhausted: false, + buildup_duration: *buildup_duration, + recover_duration: *recover_duration, + damage: *damage, + knockback: *knockback, + shockwave_angle: *shockwave_angle, + shockwave_speed: *shockwave_speed, + shockwave_duration: *shockwave_duration, + }), } } } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 26b490a7f4..e262c86905 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -69,6 +69,8 @@ pub enum CharacterState { SpinMelee(spin_melee::Data), /// A charged ranged attack (e.g. bow) ChargedRanged(charged_ranged::Data), + /// A ground shockwave attack + GroundShockwave(ground_shockwave::Data), } impl CharacterState { @@ -83,6 +85,7 @@ impl CharacterState { | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } @@ -95,6 +98,7 @@ impl CharacterState { | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } @@ -107,6 +111,7 @@ impl CharacterState { | CharacterState::BasicBlock | CharacterState::LeapMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::GroundShockwave(_) ) } diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 667571bda2..0d2530b7ea 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -350,15 +350,27 @@ impl Tool { ], NpcWeapon(kind) => { if kind == "CyclopsHammer" { - vec![BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(0), - recover_duration: Duration::from_millis(300), - knockback: 20.0, - base_healthchange: -200, - range: 10.0, - max_angle: 120.0, - }] + vec![ + BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(0), + recover_duration: Duration::from_millis(300), + knockback: 20.0, + base_healthchange: -200, + range: 10.0, + max_angle: 120.0, + }, + GroundShockwave { + energy_cost: 0, + buildup_duration: Duration::from_millis(1000), + recover_duration: Duration::from_millis(2000), + damage: 300, + knockback: -30.0, + shockwave_angle: 15.0, + shockwave_speed: 10.0, + shockwave_duration: Duration::from_millis(3000), + }, + ] } else { vec![BasicMelee { energy_cost: 0, diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 322d5025e7..bf73199a18 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -16,6 +16,7 @@ mod misc; mod phys; mod player; pub mod projectile; +pub mod shockwave; pub mod skills; mod stats; pub mod visual; @@ -51,6 +52,7 @@ pub use misc::Object; pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel}; pub use player::{Player, MAX_MOUNT_RANGE_SQR}; pub use projectile::Projectile; +pub use shockwave::Shockwave; pub use skills::{Skill, SkillGroup, SkillGroupType, SkillSet}; pub use stats::{Exp, HealthChange, HealthSource, Level, Stats}; pub use visual::{LightAnimation, LightEmitter}; diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs new file mode 100644 index 0000000000..49d5a28251 --- /dev/null +++ b/common/src/comp/shockwave.rs @@ -0,0 +1,25 @@ +use crate::{ + comp::phys::{Ori, Pos}, + sync::Uid, +}; +use serde::{Deserialize, Serialize}; +use specs::{Component, FlaggedStorage}; +use specs_idvs::IdvStorage; +use std::time::Duration; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Shockwave { + pub shockwave_origin: Pos, + pub shockwave_direction: Ori, + pub shockwave_angle: f32, + pub shockwave_speed: f32, + pub shockwave_duration: Duration, + pub damage: u32, + pub knockback: f32, + pub requires_ground: bool, + pub owner: Option, +} + +impl Component for Shockwave { + type Storage = FlaggedStorage>; +} diff --git a/common/src/event.rs b/common/src/event.rs index bea3abf131..e2bd4d1ea4 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -48,6 +48,7 @@ pub enum ServerEvent { gravity: Option, speed: f32, }, + Shockwave {shockwave: comp::Shockwave}, LandOnGround { entity: EcsEntity, vel: Vec3, diff --git a/common/src/states/ground_shockwave.rs b/common/src/states/ground_shockwave.rs new file mode 100644 index 0000000000..c9fc147dce --- /dev/null +++ b/common/src/states/ground_shockwave.rs @@ -0,0 +1,103 @@ +use crate::{ + comp::{Attacking, CharacterState, Shockwave, StateUpdate}, + event::ServerEvent, + states::utils::*, + sys::character_behavior::*, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Whether the attack can deal more damage + pub exhausted: bool, + /// How long until state should deal damage + pub buildup_duration: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Base damage + pub damage: u32, + /// Knockback + pub knockback: f32, + /// Angle of the shockwave + pub shockwave_angle: f32, + /// Speed of the shockwave + pub shockwave_speed: f32, + /// How long the shockwave travels for + pub shockwave_duration: Duration, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 0.7); + handle_jump(data, &mut update); + + if self.buildup_duration != Duration::default() { + // Build up + update.character = CharacterState::GroundShockwave(Data { + exhausted: false, + buildup_duration: self + .buildup_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + recover_duration: self.recover_duration, + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else if !self.exhausted { + // Attack + let shockwave = Shockwave { + shockwave_origin: *data.pos, + shockwave_direction: *data.ori, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + damage: self.damage, + knockback: self.knockback, + requires_ground: true, + owner: Some(*data.uid), + }; + update.server_events.push_front(ServerEvent::Shockwave { + shockwave, + }); + + update.character = CharacterState::GroundShockwave(Data { + exhausted: true, + buildup_duration: self.buildup_duration, + recover_duration: self.recover_duration, + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::GroundShockwave(Data { + exhausted: false, + buildup_duration: self.buildup_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + damage: self.damage, + knockback: self.knockback, + shockwave_angle: self.shockwave_angle, + shockwave_speed: self.shockwave_speed, + shockwave_duration: self.shockwave_duration, + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + } + + update + } +} diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 5c620b8c6b..435d35683c 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -9,6 +9,7 @@ pub mod dash_melee; pub mod equipping; pub mod glide; pub mod glide_wield; +pub mod ground_shockwave; pub mod idle; pub mod leap_melee; pub mod roll; diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 95feb503be..2c4b38186d 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -258,6 +258,7 @@ impl<'a> System<'a> for Sys { CharacterState::LeapMelee(data) => data.handle_event(&j, action), CharacterState::SpinMelee(data) => data.handle_event(&j, action), CharacterState::ChargedRanged(data) => data.handle_event(&j, action), + CharacterState::GroundShockwave(data) => data.handle_event(&j, action), }; local_emitter.append(&mut state_update.local_events); server_emitter.append(&mut state_update.server_events); @@ -286,6 +287,7 @@ impl<'a> System<'a> for Sys { CharacterState::LeapMelee(data) => data.behavior(&j), CharacterState::SpinMelee(data) => data.behavior(&j), CharacterState::ChargedRanged(data) => data.behavior(&j), + CharacterState::GroundShockwave(data) => data.behavior(&j), }; local_emitter.append(&mut state_update.local_events); diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index eafa9c8e3d..e7b26d9436 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -112,7 +112,8 @@ impl<'a> System<'a> for Sys { | CharacterState::SpinMelee { .. } | CharacterState::TripleStrike { .. } | CharacterState::BasicRanged { .. } - | CharacterState::ChargedRanged { .. } => { + | CharacterState::ChargedRanged { .. } + | CharacterState::GroundShockwave { .. } => { if energy.get_unchecked().regen_rate != 0.0 { energy.get_mut_unchecked().regen_rate = 0.0 } diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index d9a2263557..f120bd7e34 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -3,7 +3,7 @@ use common::{ character::CharacterId, comp::{ self, humanoid::DEFAULT_HUMANOID_EYE_HEIGHT, Agent, Alignment, Body, Gravity, Item, - ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Stats, Vel, WaypointArea, + ItemDrop, LightEmitter, Loadout, Pos, Projectile, Scale, Shockwave, Stats, Vel, WaypointArea, }, outcome::Outcome, util::Dir, @@ -124,6 +124,12 @@ pub fn handle_shoot( builder.build(); } +pub fn handle_shockwave(server: &mut Server, shockwave: Shockwave) { + let state = server.state_mut(); + let builder = state.create_shockwave(shockwave); + builder.build(); +} + pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { server .state diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 31b3dc631a..6cdd018c0b 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -70,6 +70,7 @@ impl Server { gravity, speed, } => handle_shoot(self, entity, dir, body, light, projectile, gravity, speed), + ServerEvent::Shockwave {shockwave} => handle_shockwave(self, shockwave), ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change), ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause), ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip), diff --git a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs index 8dbb73ce5c..457908ef38 100644 --- a/voxygen/src/audio/sfx/event_mapper/combat/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/combat/tests.rs @@ -79,6 +79,7 @@ fn maps_basic_melee() { &CharacterState::BasicMelee(states::basic_melee::Data { buildup_duration: Duration::default(), recover_duration: Duration::default(), + knockback: 0.0, base_healthchange: 10, range: 1.0, max_angle: 1.0,