diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index 3450737137..d829c25d3f 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -393,6 +393,7 @@ impl Tool { max_heal: (140.0 * self.base_power()) as u32, min_heal: (50.0 * self.base_power()) as u32, terrain_destruction_power: 0.0, + energy_regen: 0, }), projectile::Effect::Vanish, ], @@ -404,6 +405,7 @@ impl Tool { max_heal: (140.0 * self.base_power()) as u32, min_heal: (50.0 * self.base_power()) as u32, terrain_destruction_power: 0.0, + energy_regen: 0, }), projectile::Effect::Vanish, ], @@ -431,10 +433,11 @@ impl Tool { projectile::Effect::Explode(Explosion { radius: 4.0, max_damage: (100.0 * self.base_power()) as u32, - min_damage: (40.0 * self.base_power()) as u32, + min_damage: (10.0 * self.base_power()) as u32, max_heal: 0, min_heal: 0, terrain_destruction_power: 0.0, + energy_regen: 50, }), projectile::Effect::Vanish, ], @@ -442,10 +445,11 @@ impl Tool { projectile::Effect::Explode(Explosion { radius: 4.0, max_damage: (100.0 * self.base_power()) as u32, - min_damage: (40.0 * self.base_power()) as u32, + min_damage: (10.0 * self.base_power()) as u32, max_heal: 0, min_heal: 0, terrain_destruction_power: 0.0, + energy_regen: 50, }), projectile::Effect::Vanish, ], @@ -466,7 +470,7 @@ impl Tool { recover_duration: Duration::from_millis(250), beam_duration: Duration::from_millis(500), base_hps: 0, - base_dps: (100.0 * self.base_power()) as u32, + base_dps: (200.0 * self.base_power()) as u32, tick_rate: 2.0, range: 15.0, max_angle: 22.5, @@ -480,8 +484,8 @@ impl Tool { energy_cost: 0, buildup_duration: Duration::from_millis(500), recover_duration: Duration::from_millis(300), - damage: 200, - knockback: 12.0, + damage: (200.0 * self.base_power()) as u32, + knockback: 20.0, shockwave_angle: 360.0, shockwave_speed: 20.0, shockwave_duration: Duration::from_millis(500), diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 18b86ff78d..9df2d33004 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -54,7 +54,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::{Explosion, Projectile}; -pub use shockwave::Shockwave; +pub use shockwave::{Shockwave, ShockwaveHitEntities}; 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/projectile.rs b/common/src/comp/projectile.rs index 34b85b1ad5..44140f82d2 100644 --- a/common/src/comp/projectile.rs +++ b/common/src/comp/projectile.rs @@ -23,6 +23,7 @@ pub struct Explosion { pub max_heal: u32, pub min_heal: u32, pub terrain_destruction_power: f32, + pub energy_regen: u32, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/shockwave.rs b/common/src/comp/shockwave.rs index e53d611716..26ff9e8fa8 100644 --- a/common/src/comp/shockwave.rs +++ b/common/src/comp/shockwave.rs @@ -34,3 +34,12 @@ impl std::ops::Deref for Shockwave { fn deref(&self) -> &Properties { &self.properties } } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct ShockwaveHitEntities { + pub hit_entities: Vec, +} + +impl Component for ShockwaveHitEntities { + type Storage = IdvStorage; +} diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 644eece5b6..f680c321ca 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -14,6 +14,7 @@ pub enum Outcome { pos: Vec3, power: f32, radius: f32, + is_attack: bool, reagent: Option, // How can we better define this? }, ProjectileShot { diff --git a/common/src/state.rs b/common/src/state.rs index aa39b19c23..d54a5d9433 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -127,6 +127,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); // Register components send from clients -> server diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index b06f329b9e..ea56b4e0e9 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ BeamSegment, Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Projectile, Scale, - Sticky, Vel, + Shockwave, Sticky, Vel, }, event::{EventBus, ServerEvent}, metrics::SysMetrics, @@ -66,6 +66,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Mounting>, ReadStorage<'a, Projectile>, ReadStorage<'a, BeamSegment>, + ReadStorage<'a, Shockwave>, ); #[allow(clippy::or_fun_call)] // TODO: Pending review in #587 @@ -91,6 +92,7 @@ impl<'a> System<'a> for Sys { mountings, projectiles, beams, + shockwaves, ): Self::SystemData, ) { let start_time = std::time::Instant::now(); @@ -168,6 +170,7 @@ impl<'a> System<'a> for Sys { _, _, _, + _, ) in ( &entities, &uids, @@ -178,6 +181,7 @@ impl<'a> System<'a> for Sys { !&projectiles, !&mountings, !&beams, + !&shockwaves, ) .join() { diff --git a/common/src/sys/shockwave.rs b/common/src/sys/shockwave.rs index ced2d336e1..4326c5de6f 100644 --- a/common/src/sys/shockwave.rs +++ b/common/src/sys/shockwave.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ group, Body, CharacterState, Damage, DamageSource, HealthChange, HealthSource, Last, - Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, Stats, + Loadout, Ori, PhysicsState, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, }, event::{EventBus, LocalEvent, ServerEvent}, state::{DeltaTime, Time}, @@ -37,6 +37,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, CharacterState>, ReadStorage<'a, PhysicsState>, WriteStorage<'a, Shockwave>, + WriteStorage<'a, ShockwaveHitEntities>, ); fn run( @@ -60,6 +61,7 @@ impl<'a> System<'a> for Sys { character_states, physics_states, mut shockwaves, + mut shockwave_hit_lists, ): Self::SystemData, ) { let mut server_emitter = server_bus.emitter(); @@ -69,8 +71,15 @@ impl<'a> System<'a> for Sys { let dt = dt.0; // Shockwaves - for (entity, uid, pos, ori, shockwave) in - (&entities, &uids, &positions, &orientations, &shockwaves).join() + for (entity, uid, pos, ori, shockwave, shockwave_hit_list) in ( + &entities, + &uids, + &positions, + &orientations, + &shockwaves, + &mut shockwave_hit_lists, + ) + .join() { let creation_time = match shockwave.creation { Some(time) => time, @@ -142,6 +151,15 @@ impl<'a> System<'a> for Sys { ) .join() { + // Check to see if entity has already been hit + if shockwave_hit_list + .hit_entities + .iter() + .any(|&uid| uid == *uid_b) + { + continue; + } + // 2D versions let pos_b2 = pos_b.0.xy(); let last_pos_b2_maybe = last_pos_b_maybe.map(|p| (p.0).0.xy()); @@ -204,6 +222,7 @@ impl<'a> System<'a> for Sys { cause, }, }); + shockwave_hit_list.hit_entities.push(*uid_b); } if shockwave.knockback != 0.0 && damage.healthchange != 0.0 { let impulse = if shockwave.knockback < 0.0 { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 1d940cbc57..fb01571199 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1136,6 +1136,7 @@ fn handle_explosion( max_heal: 0, min_heal: 0, terrain_destruction_power: power, + energy_regen: 0, }, owner: ecs.read_storage::().get(target).copied(), friendly_damage: true, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 5f48498221..9868b37027 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -510,6 +510,7 @@ pub fn handle_explosion( pos, power: outcome_power, radius: explosion.radius, + is_attack: explosion.max_heal > 0 || explosion.max_damage > 0, reagent, }); let owner_entity = owner.and_then(|uid| { @@ -588,6 +589,12 @@ pub fn handle_explosion( amount: damage.healthchange as i32, cause, }); + if let Some(owner) = owner_entity { + if let Some(energy) = ecs.write_storage::().get_mut(owner) { + energy + .change_by(explosion.energy_regen as i32, comp::EnergySource::HitEnemy); + } + } } } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 2b2c173a38..aa4486f9e7 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -162,6 +162,9 @@ impl StateExt for State { properties, creation: None, }) + .with(comp::ShockwaveHitEntities { + hit_entities: Vec::::new(), + }) } fn create_beam( diff --git a/server/src/sys/object.rs b/server/src/sys/object.rs index 5c04f9f25a..d37827fbc9 100644 --- a/server/src/sys/object.rs +++ b/server/src/sys/object.rs @@ -53,6 +53,7 @@ impl<'a> System<'a> for Sys { max_heal: 0, min_heal: 0, terrain_destruction_power: 4.0, + energy_regen: 0, }, owner: *owner, friendly_damage: true, @@ -75,6 +76,7 @@ impl<'a> System<'a> for Sys { max_heal: 0, min_heal: 0, terrain_destruction_power: 4.0, + energy_regen: 0, }, owner: *owner, friendly_damage: true, diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 02e8253bf9..74affc6ab2 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -390,6 +390,7 @@ impl Scene { pos, power, radius: _, + is_attack: _, reagent, } => self.event_lights.push(EventLight { light: Light::new( diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index 1489d59302..267dcf006b 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -59,20 +59,37 @@ impl ParticleMgr { pos, power, radius, + is_attack, reagent, } => { - if *power < 0.0 { - self.particles.resize_with( - self.particles.len() + (200.0 * power.abs()) as usize, - || { - Particle::new( - Duration::from_secs(1), - time, - ParticleMode::EnergyNature, - *pos + Vec3::::zero().map(|_| rng.gen_range(-radius, radius)), - ) - }, - ); + if *is_attack { + if *power < 0.0 { + self.particles.resize_with( + self.particles.len() + (200.0 * power.abs()) as usize, + || { + Particle::new( + Duration::from_secs(1), + time, + ParticleMode::EnergyNature, + *pos + Vec3::::zero() + .map(|_| rng.gen_range(-radius, radius)), + ) + }, + ); + } else { + self.particles.resize_with( + self.particles.len() + (50.0 * power.abs()) as usize, + || { + Particle::new( + Duration::from_secs(1), + time, + ParticleMode::CampfireFire, + *pos + Vec3::::zero() + .map(|_| rng.gen_range(-radius, radius)), + ) + }, + ); + } } else { self.particles.resize_with( self.particles.len() + if reagent.is_some() { 300 } else { 150 },