Skip to content

Commit

Permalink
Swap all existing powerup code over to using the effects system (#216)
Browse files Browse the repository at this point in the history
* Add effects to MobPrototype
* Add a new effect manager component
* Add the new effect manager to the default component set
* Refactor EffectPrototype in favour of PowerupPrototype
* Rewrite effects component to use PowerupPrototype
* Add a powerup prototype to the PlayerPowerup event
* Add a speed modification effect
* Add player powerup handler to add effects component
* Add test for inferno behaviour
* Remove speed effect
* Add some helpers to effect component
* Convert physics update to use effects component
* Use effects within special
* Use effect component as the source of truth for expiring powerups
* Use effects within player missile collision handler
* Convert main physics loop to use effects
* Convert key handler to use effects
* Convert get_server_upgrades to use effects
* Remove logging within powerup expiry
* Remove Powerups component and use effects everywhere
  • Loading branch information
steamroller-airmash committed Jun 21, 2022
1 parent 2a27bc5 commit 691ff5a
Show file tree
Hide file tree
Showing 24 changed files with 420 additions and 296 deletions.
34 changes: 17 additions & 17 deletions server-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::ptr::NonNull;

use crate::util::{DropPtr, MaybeDrop};
use crate::{
EffectPrototype, GameConfigCommon, GamePrototype, MissilePrototype, MobPrototype, PlanePrototype,
PtrRef, SpecialPrototype, StringRef, ValidationError,
GameConfigCommon, GamePrototype, MissilePrototype, MobPrototype, PlanePrototype,
PowerupPrototype, PtrRef, SpecialPrototype, StringRef, ValidationError,
};

macro_rules! transform_protos {
Expand Down Expand Up @@ -40,8 +40,8 @@ pub struct GameConfig {
pub planes: HashMap<&'static str, &'static PlanePrototype<'static, PtrRef>>,
pub missiles: HashMap<&'static str, &'static MissilePrototype>,
pub specials: HashMap<&'static str, &'static SpecialPrototype<'static, PtrRef>>,
pub effects: HashMap<&'static str, &'static EffectPrototype>,
pub mobs: HashMap<&'static str, &'static MobPrototype>,
pub powerups: HashMap<&'static str, &'static PowerupPrototype>,
pub mobs: HashMap<&'static str, &'static MobPrototype<'static, PtrRef>>,

pub common: GameConfigCommon<'static, PtrRef>,

Expand All @@ -53,20 +53,20 @@ impl GameConfig {
planes: &[PlanePrototype<PtrRef>],
missiles: &[MissilePrototype],
specials: &[SpecialPrototype<PtrRef>],
mobs: &[MobPrototype],
effects: &[EffectPrototype],
mobs: &[MobPrototype<PtrRef>],
powerups: &[PowerupPrototype],

common: GameConfigCommon<StringRef>,
) -> Result<Self, ValidationError> {
let data = unsafe { GameConfigData::new(&planes, &missiles, &specials, &mobs, &effects) };
let data = unsafe { GameConfigData::new(&planes, &missiles, &specials, &mobs, &powerups) };

let mut missiles = HashMap::new();
let mut planes = HashMap::new();
let mut specials = HashMap::new();
let mut effects = HashMap::new();
let mut mobs = HashMap::new();

for effect in data.effects() {
for effect in data.powerups() {
if effects.insert(&*effect.name, effect).is_some() {
return Err(
ValidationError::custom("name", "multiple effect prototypes had the same name")
Expand Down Expand Up @@ -120,7 +120,7 @@ impl GameConfig {
missiles,
planes,
specials,
effects,
powerups: effects,
mobs,

common: common.resolve(data.planes())?,
Expand All @@ -133,9 +133,9 @@ impl GameConfig {
// These ones will be automatically dropped if something goes wrong. In order to
// prevent UB we just need to call MaybeDrop::cancel_drop if everything works
// out at the end.
let mobs = MaybeDrop::from(transform_protos!(proto.mobs => |m| m.resolve())?);
let missiles = MaybeDrop::from(transform_protos!(proto.missiles => |m| m.resolve())?);
let effects = MaybeDrop::from(transform_protos!(proto.effects => |m| m.resolve())?);
let effects = MaybeDrop::from(transform_protos!(proto.powerups => |m| m.resolve())?);
let mobs = MaybeDrop::from(transform_protos!(proto.mobs => |m| m.resolve(&effects))?);

let mut specials = transform_protos!(proto.specials => |s| s.resolve(&missiles))?;
// Due to some lifetime issues it's not actually possible to store specials in
Expand Down Expand Up @@ -213,8 +213,8 @@ struct GameConfigData {
planes: NonNull<[PlanePrototype<'static, PtrRef>]>,
missiles: NonNull<[MissilePrototype]>,
specials: NonNull<[SpecialPrototype<'static, PtrRef>]>,
effects: NonNull<[EffectPrototype]>,
mobs: NonNull<[MobPrototype]>,
effects: NonNull<[PowerupPrototype]>,
mobs: NonNull<[MobPrototype<'static, PtrRef>]>,
}

impl GameConfigData {
Expand All @@ -228,8 +228,8 @@ impl GameConfigData {
planes: &[PlanePrototype<PtrRef>],
missiles: &[MissilePrototype],
specials: &[SpecialPrototype<PtrRef>],
mobs: &[MobPrototype],
effects: &[EffectPrototype],
mobs: &[MobPrototype<PtrRef>],
effects: &[PowerupPrototype],
) -> Self {
Self {
planes: NonNull::new(planes as *const _ as *mut _).unwrap(),
Expand Down Expand Up @@ -266,11 +266,11 @@ impl GameConfigData {
unsafe { self.specials.as_ref() }
}

fn mobs(&self) -> &'static [MobPrototype] {
fn mobs(&self) -> &'static [MobPrototype<'static, PtrRef>] {
unsafe { self.mobs.as_ref() }
}

fn effects(&self) -> &'static [EffectPrototype] {
fn powerups(&self) -> &'static [PowerupPrototype] {
unsafe { self.effects.as_ref() }
}
}
Expand Down
138 changes: 29 additions & 109 deletions server-config/src/effect.rs
Original file line number Diff line number Diff line change
@@ -1,145 +1,65 @@
use std::borrow::Cow;
use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate::util::option_duration;
use crate::ValidationError;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct EffectPrototype {
pub name: Cow<'static, str>,

#[serde(with = "option_duration")]
pub duration: Option<Duration>,

#[serde(flatten)]
pub data: EffectPrototypeData,
#[serde(tag = "type", rename = "kebab-case")]
pub enum EffectPrototype {
Shield {
damage_mult: f32,
},
Inferno,
FixedSpeed {
speed: f32,
},
Upgrade,
/// Despawn the mob that just collided.
Despawn,
}

impl EffectPrototype {
pub const fn shield() -> Self {
Self {
name: Cow::Borrowed("shield"),
duration: Some(Duration::from_secs(10)),
data: EffectPrototypeData::Shield(ShieldEffectPrototype { damage_mult: 0.0 }),
}
}

pub const fn spawn_shield() -> Self {
Self {
name: Cow::Borrowed("spawn-shield"),
duration: Some(Duration::from_secs(2)),
data: EffectPrototypeData::Shield(ShieldEffectPrototype { damage_mult: 0.0 }),
}
}

pub const fn invulnerable() -> Self {
Self {
name: Cow::Borrowed("invulnerable"),
duration: None,
data: EffectPrototypeData::Shield(ShieldEffectPrototype { damage_mult: 0.0 }),
}
Self::Shield { damage_mult: 0.0 }
}

pub const fn inferno() -> Self {
Self {
name: Cow::Borrowed("inferno"),
duration: Some(Duration::from_secs(10)),
data: EffectPrototypeData::Inferno,
}
Self::Inferno
}

pub const fn flag_speed() -> Self {
Self {
name: Cow::Borrowed("flag-speed"),
duration: None,
data: EffectPrototypeData::FixedSpeed(FixedSpeedEffectPrototype { speed: 5.0 }),
}
Self::FixedSpeed { speed: 5.0 }
}

pub const fn upgrade() -> Self {
Self {
name: Cow::Borrowed("upgrade"),
duration: None,
data: EffectPrototypeData::Upgrade,
}
Self::Upgrade
}

pub const fn despawn() -> Self {
Self::Despawn
}
}

impl EffectPrototype {
pub const fn is_shield(&self) -> bool {
matches!(self.data, EffectPrototypeData::Shield(_))
}

pub const fn as_shield(&self) -> Option<&ShieldEffectPrototype> {
match &self.data {
EffectPrototypeData::Shield(shield) => Some(shield),
_ => None,
}
matches!(self, Self::Shield { .. })
}

pub const fn is_inferno(&self) -> bool {
matches!(self.data, EffectPrototypeData::Inferno)
matches!(self, Self::Inferno)
}

pub const fn is_fixed_speed(&self) -> bool {
matches!(self.data, EffectPrototypeData::FixedSpeed(_))
}

pub const fn as_fixed_speed(&self) -> Option<&FixedSpeedEffectPrototype> {
match &self.data {
EffectPrototypeData::FixedSpeed(speed) => Some(speed),
_ => None,
}
matches!(self, Self::FixedSpeed { .. })
}

pub const fn is_upgrade(&self) -> bool {
matches!(self.data, EffectPrototypeData::Upgrade)
matches!(self, Self::Upgrade)
}
}

impl EffectPrototype {
pub(crate) fn resolve(self) -> Result<Self, ValidationError> {
if self.name.is_empty() {
return Err(ValidationError::custom("name", "prototype had empty name"));
}

Ok(self)
pub const fn is_despawn(&self) -> bool {
matches!(self, Self::Despawn)
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
#[serde(tag = "type", rename = "kebab-case")]
pub enum EffectPrototypeData {
Shield(ShieldEffectPrototype),
Inferno,
FixedSpeed(FixedSpeedEffectPrototype),
Upgrade,
}

/// Effect that modifies the amount of damage done to a plane when it is hit by
/// a missile.
///
/// This is named after the shield (which sets all damage to 0) but can be used
/// for any effect that needs to modify the damage taken by a plane. If multiple
/// instances of this effect are present the all the damage multipliers will be
/// applied.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ShieldEffectPrototype {
pub damage_mult: f32,
}

/// Effect that sets a plane's speed to a fixed value.
///
/// This is used for the flag speed effect in CTF and will set the corresponding
/// bit in the keystate. Note that setting the speed to anything other than 5
/// will cause client desyncs unless the client has been modified as well.
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub struct FixedSpeedEffectPrototype {
pub speed: f32,
pub const fn is_instant(&self) -> bool {
matches!(self, Self::Upgrade | Self::Despawn)
}
}
22 changes: 10 additions & 12 deletions server-config/src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::ops::{Deref, DerefMut};
use serde::{Deserialize, Serialize};

use crate::{
EffectPrototype, GameConfigCommon, MissilePrototype, MobPrototype, PlanePrototype, PrototypeRef,
GameConfigCommon, MissilePrototype, MobPrototype, PlanePrototype, PowerupPrototype, PrototypeRef,
SpecialPrototype, StringRef,
};

Expand All @@ -14,14 +14,14 @@ use crate::{
serialize = "
Ref::MissileRef: Serialize,
Ref::SpecialRef: Serialize,
Ref::EffectRef: Serialize,
Ref::PowerupRef: Serialize,
Ref::PlaneRef: Serialize,
Ref::MobRef: Serialize,
",
deserialize = "
Ref::MissileRef: Deserialize<'de>,
Ref::SpecialRef: Deserialize<'de>,
Ref::EffectRef: Deserialize<'de>,
Ref::PowerupRef: Deserialize<'de>,
Ref::PlaneRef: Deserialize<'de>,
Ref::MobRef: Deserialize<'de>,
"
Expand All @@ -30,8 +30,8 @@ pub struct GamePrototype<'a, Ref: PrototypeRef<'a>> {
pub planes: Vec<PlanePrototype<'a, Ref>>,
pub missiles: Vec<MissilePrototype>,
pub specials: Vec<SpecialPrototype<'a, Ref>>,
pub mobs: Vec<MobPrototype>,
pub effects: Vec<EffectPrototype>,
pub mobs: Vec<MobPrototype<'a, Ref>>,
pub powerups: Vec<PowerupPrototype>,

#[serde(flatten)]
pub common: GameConfigCommon<'a, Ref>,
Expand Down Expand Up @@ -67,13 +67,11 @@ impl Default for GamePrototype<'_, StringRef> {
MobPrototype::shield(),
MobPrototype::upgrade(),
],
effects: vec![
EffectPrototype::shield(),
EffectPrototype::spawn_shield(),
EffectPrototype::invulnerable(),
EffectPrototype::inferno(),
EffectPrototype::flag_speed(),
EffectPrototype::upgrade(),
powerups: vec![
PowerupPrototype::shield(),
PowerupPrototype::spawn_shield(),
PowerupPrototype::inferno(),
PowerupPrototype::upgrade(),
],
common: GameConfigCommon::default(),
}
Expand Down
12 changes: 7 additions & 5 deletions server-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod game;
mod missile;
mod mob;
mod plane;
mod powerup;
mod special;
mod util;

Expand All @@ -19,12 +20,13 @@ use std::fmt::Debug;

pub use self::common::GameConfigCommon;
pub use self::config::GameConfig;
pub use self::effect::*;
pub use self::effect::EffectPrototype;
pub use self::error::{Path, Segment, ValidationError};
pub use self::game::GamePrototype;
pub use self::missile::MissilePrototype;
pub use self::mob::MobPrototype;
pub use self::plane::PlanePrototype;
pub use self::powerup::PowerupPrototype;
pub use self::special::*;

mod sealed {
Expand All @@ -40,7 +42,7 @@ pub trait PrototypeRef<'a>: Sealed {
type SpecialRef: Clone + Debug + 'a;
type PlaneRef: Clone + Debug + 'a;
type MobRef: Clone + Debug + 'a;
type EffectRef: Clone + Debug + 'a;
type PowerupRef: Clone + Debug + 'a;
}

#[derive(Copy, Clone, Debug)]
Expand All @@ -55,15 +57,15 @@ impl Sealed for PtrRef {}
impl<'a> PrototypeRef<'a> for StringRef {
type MissileRef = Cow<'a, str>;
type SpecialRef = Cow<'a, str>;
type EffectRef = Cow<'a, str>;
type PowerupRef = Cow<'a, str>;
type PlaneRef = Cow<'a, str>;
type MobRef = Cow<'a, str>;
}

impl<'a> PrototypeRef<'a> for PtrRef {
type MissileRef = &'a MissilePrototype;
type SpecialRef = &'a SpecialPrototype<'a, Self>;
type EffectRef = &'a EffectPrototype;
type PowerupRef = &'a PowerupPrototype;
type PlaneRef = &'a PlanePrototype<'a, Self>;
type MobRef = &'a MobPrototype;
type MobRef = &'a MobPrototype<'a, Self>;
}

0 comments on commit 691ff5a

Please sign in to comment.