Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap all existing powerup code over to using the effects system #216

Merged
merged 20 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d2156b7
Add effects to MobPrototype
steamroller-airmash Jun 20, 2022
e6b6f26
Add a new effect manager component
steamroller-airmash Jun 20, 2022
a70cc06
Add the new effect manager to the default component set
steamroller-airmash Jun 20, 2022
e177d3f
Refactor EffectPrototype in favour of PowerupPrototype
steamroller-airmash Jun 20, 2022
db21805
Rewrite effects component to use PowerupPrototype
steamroller-airmash Jun 20, 2022
2ec7245
Add a powerup prototype to the PlayerPowerup event
steamroller-airmash Jun 21, 2022
e0bb336
Add a speed modification effect
steamroller-airmash Jun 21, 2022
17dbe58
Add player powerup handler to add effects component
steamroller-airmash Jun 21, 2022
59671b9
Add test for inferno behaviour
steamroller-airmash Jun 21, 2022
acf7b2a
Remove speed effect
steamroller-airmash Jun 21, 2022
3747eb9
Add some helpers to effect component
steamroller-airmash Jun 21, 2022
1e49b24
Convert physics update to use effects component
steamroller-airmash Jun 21, 2022
4806eb5
Use effects within special
steamroller-airmash Jun 21, 2022
a457014
Use effect component as the source of truth for expiring powerups
steamroller-airmash Jun 21, 2022
4f43f2d
Use effects within player missile collision handler
steamroller-airmash Jun 21, 2022
dbb7cba
Convert main physics loop to use effects
steamroller-airmash Jun 21, 2022
8246a19
Convert key handler to use effects
steamroller-airmash Jun 21, 2022
3c15de8
Convert get_server_upgrades to use effects
steamroller-airmash Jun 21, 2022
f20fbd7
Remove logging within powerup expiry
steamroller-airmash Jun 21, 2022
743aaa7
Remove Powerups component and use effects everywhere
steamroller-airmash Jun 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>;
}