diff --git a/ctf/src/main.rs b/ctf/src/main.rs index 5c62d7c3..02713c16 100644 --- a/ctf/src/main.rs +++ b/ctf/src/main.rs @@ -1,4 +1,6 @@ -use airmash::AirmashGame; +use std::time::Duration; + +use airmash::{util::PeriodicPowerupSpawner, AirmashGame, Vector2}; use serde_deserialize_over::DeserializeOver; fn set_default_var(name: &str, value: &str) { @@ -61,5 +63,30 @@ fn main() { airmash_server_ctf::setup_ctf_server(&mut game); + // Inferno in Europe + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(920.0, -2800.0), + Duration::from_secs(105), + )); + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(-7440.0, -1360.0), + Duration::from_secs(105), + )); + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(6565.0, -935.0), + Duration::from_secs(105), + )); + + // Blue base shield + game.register(PeriodicPowerupSpawner::shield( + Vector2::new(-9300.0, -1480.0), + Duration::from_secs(90), + )); + // Red base shield + game.register(PeriodicPowerupSpawner::shield( + Vector2::new(8350.0, -935.0), + Duration::from_secs(90), + )); + game.run_until_shutdown(); } diff --git a/ffa/src/main.rs b/ffa/src/main.rs index 8cb1b33d..eae85126 100644 --- a/ffa/src/main.rs +++ b/ffa/src/main.rs @@ -1,5 +1,6 @@ use std::env; use std::fs::File; +use std::time::Duration; use clap::arg; use serde_deserialize_over::DeserializeOver; @@ -8,6 +9,7 @@ use server::*; use server::{ protocol::GameType, resource::{Config, RegionName}, + util::PeriodicPowerupSpawner, }; mod systems; @@ -48,6 +50,20 @@ fn main() { // Use the provided FFA scoreboard systems. server::system::ffa::register_all(&mut game); + // Inferno in Europe + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(920.0, -2800.0), + Duration::from_secs(105), + )); + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(-7440.0, -1360.0), + Duration::from_secs(105), + )); + game.register(PeriodicPowerupSpawner::inferno( + Vector2::new(6565.0, -935.0), + Duration::from_secs(105), + )); + if let Some(path) = matches.value_of("config") { let file = match File::open(path) { Ok(x) => x, diff --git a/server-next/src/util/mod.rs b/server-next/src/util/mod.rs index 5596aeba..dd58d872 100644 --- a/server-next/src/util/mod.rs +++ b/server-next/src/util/mod.rs @@ -9,8 +9,11 @@ use nalgebra::vector; use std::time::{Duration, Instant}; pub(crate) mod escapes; +mod powerup_spawner; pub mod spectate; +pub use self::powerup_spawner::PeriodicPowerupSpawner; + pub fn convert_time(dur: Duration) -> Time { dur.as_secs_f32() * 60.0 } diff --git a/server-next/src/util/powerup_spawner.rs b/server-next/src/util/powerup_spawner.rs new file mode 100644 index 00000000..2725b1c9 --- /dev/null +++ b/server-next/src/util/powerup_spawner.rs @@ -0,0 +1,56 @@ +use std::time::{Duration, Instant}; + +use crate::protocol::{MobType, Position}; +use crate::{event::Frame, Entity, EventHandler}; + +#[derive(Copy, Clone)] +enum SpawnerState { + Spawned(Entity), + Unspawned(Instant), +} + +pub struct PeriodicPowerupSpawner { + state: SpawnerState, + interval: Duration, + mob: MobType, + pos: Position, +} + +impl PeriodicPowerupSpawner { + pub fn new(mob: MobType, pos: Position, interval: Duration) -> Self { + Self { + mob, + pos, + interval, + state: SpawnerState::Unspawned(Instant::now()), + } + } + + pub fn inferno(pos: Position, interval: Duration) -> Self { + Self::new(MobType::Inferno, pos, interval) + } + + pub fn shield(pos: Position, interval: Duration) -> Self { + Self::new(MobType::Shield, pos, interval) + } +} + +impl EventHandler for PeriodicPowerupSpawner { + fn on_event(&mut self, _: &Frame, game: &mut crate::AirmashGame) { + let frame = game.this_frame(); + + match self.state { + SpawnerState::Spawned(entity) => { + if !game.world.contains(entity) { + self.state = SpawnerState::Unspawned(frame + self.interval); + } + } + SpawnerState::Unspawned(next) => { + if frame > next { + let entity = game.spawn_mob(self.mob, self.pos, Duration::from_secs(60)); + self.state = SpawnerState::Spawned(entity); + } + } + } + } +}