use common::{ comp::{HealthSource, Object, PhysicsState, PoiseChange, PoiseSource, Pos, Vel}, effect::Effect, event::{EventBus, ServerEvent}, resources::DeltaTime, Damage, DamageSource, Explosion, RadiusEffect, }; use common_ecs::{Job, Origin, Phase, System}; use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; /// This system is responsible for handling misc object behaviours #[derive(Default)] pub struct Sys; impl<'a> System<'a> for Sys { #[allow(clippy::type_complexity)] type SystemData = ( Entities<'a>, Read<'a, DeltaTime>, Read<'a, EventBus>, ReadStorage<'a, Pos>, ReadStorage<'a, Vel>, ReadStorage<'a, PhysicsState>, WriteStorage<'a, Object>, ); const NAME: &'static str = "object"; const ORIGIN: Origin = Origin::Server; const PHASE: Phase = Phase::Create; fn run( _job: &mut Job, (entities, _dt, server_bus, positions, velocities, physics_states, mut objects): Self::SystemData, ) { let mut server_emitter = server_bus.emitter(); // Objects for (entity, pos, vel, physics, object) in ( &entities, &positions, &velocities, &physics_states, &mut objects, ) .join() { match object { Object::Bomb { owner } => { if physics.on_surface().is_some() { server_emitter.emit(ServerEvent::Destroy { entity, cause: HealthSource::Suicide, }); server_emitter.emit(ServerEvent::Explosion { pos: pos.0, explosion: Explosion { effects: vec![ RadiusEffect::Entity(Effect::Damage(Damage { source: DamageSource::Explosion, value: 400.0, })), RadiusEffect::Entity(Effect::PoiseChange(PoiseChange { source: PoiseSource::Explosion, amount: -100, })), RadiusEffect::TerrainDestruction(4.0), ], radius: 12.0, reagent: None, }, owner: *owner, }); } }, Object::Firework { owner, reagent } => { if vel.0.z < 0.0 { const ENABLE_RECURSIVE_FIREWORKS: bool = true; if ENABLE_RECURSIVE_FIREWORKS { use common::{ comp::{object, Body, Gravity, LightEmitter, Projectile}, util::Dir, }; use rand::Rng; use std::{f32::consts::PI, time::Duration}; use vek::{Rgb, Vec3}; let mut rng = rand::thread_rng(); // Note that if the expected fireworks per firework is > 1, this will // eventually cause enough server lag that more players can't log in. let thresholds: &[(f32, usize)] = &[(0.25, 2), (0.7, 1)]; let expected = { let mut total = 0.0; let mut cumulative_probability = 0.0; for (p, n) in thresholds { total += (p - cumulative_probability) * *n as f32; cumulative_probability += p; } total }; assert!(expected < 1.0); let num_fireworks = (|| { let x = rng.gen_range(0.0..1.0); for (p, n) in thresholds { if x < *p { return *n; } } 0 })(); for _ in 0..num_fireworks { let speed: f32 = rng.gen_range(40.0..80.0); let theta: f32 = rng.gen_range(0.0..2.0 * PI); let phi: f32 = rng.gen_range(0.25 * PI..0.5 * PI); let dir = Dir::from_unnormalized(Vec3::new( theta.cos(), theta.sin(), phi.sin(), )) .expect("nonzero vector should normalize"); server_emitter.emit(ServerEvent::Shoot { entity, dir, body: Body::Object(object::Body::for_firework(*reagent)), light: Some(LightEmitter { animated: true, flicker: 2.0, strength: 2.0, col: Rgb::new(1.0, 1.0, 0.0), }), projectile: Projectile { hit_solid: Vec::new(), hit_entity: Vec::new(), time_left: Duration::from_secs(60), owner: *owner, ignore_group: true, }, gravity: Some(Gravity(1.0)), speed, object: Some(Object::Firework { owner: *owner, reagent: *reagent, }), }); } } server_emitter.emit(ServerEvent::Destroy { entity, cause: HealthSource::Suicide, }); server_emitter.emit(ServerEvent::Explosion { pos: pos.0, explosion: Explosion { effects: vec![ RadiusEffect::Entity(Effect::Damage(Damage { source: DamageSource::Explosion, value: 50.0, })), RadiusEffect::Entity(Effect::PoiseChange(PoiseChange { source: PoiseSource::Explosion, amount: -40, })), RadiusEffect::TerrainDestruction(4.0), ], radius: 12.0, reagent: Some(*reagent), }, owner: *owner, }); } }, } } } }