From 0095a23dd6ab0bea36eb9efad66ac6a0f1256dfc Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 9 Jan 2024 17:22:52 -0800 Subject: [PATCH] Merged guns and outfits --- content/guns.toml | 37 ------ content/outfits.toml | 39 +++++++ crates/content/src/lib.rs | 8 +- crates/content/src/part/gun.rs | 186 ------------------------------ crates/content/src/part/mod.rs | 4 +- crates/content/src/part/outfit.rs | 173 ++++++++++++++++++++++++++- 6 files changed, 210 insertions(+), 237 deletions(-) delete mode 100644 content/guns.toml delete mode 100644 crates/content/src/part/gun.rs diff --git a/content/guns.toml b/content/guns.toml deleted file mode 100644 index 711fde5..0000000 --- a/content/guns.toml +++ /dev/null @@ -1,37 +0,0 @@ -[gun."blaster"] - -space.weapon = 10 - -# Average delay between shots -rate = 0.2 -# Random rate variation (each cooldown is +- this) -rate_rng = 0.1 - -# TODO: apply force to ship on fire -projectile.sprite = "projectile::blaster" -# Height of projectile in game units -projectile.size = 6 -projectile.size_rng = 0.0 -# Speed of projectile, in game units/second -projectile.speed = 300 -projectile.speed_rng = 10.0 -# Lifetime of projectile, in seconds -projectile.lifetime = 2.0 -projectile.lifetime_rng = 0.2 -projectile.damage = 10.0 -# Random variation of firing angle. -# If this is 0, the projectile always goes straight. -# If this is 10, projectiles will form a cone of 10 degrees -# ( 5 degrees on each side ) -projectile.angle_rng = 2 -# How much force this projectile will apply to an object it hits -projectile.force = 0.0 - -projectile.collider.ball.radius = 2.0 - -projectile.impact_effect = "blaster impact" - -projectile.expire_effect.sprite = "particle::blaster" -projectile.expire_effect.lifetime = "inherit" -projectile.expire_effect.size = 3.0 -projectile.expire_effect.velocity_scale_parent = 1.0 diff --git a/content/outfits.toml b/content/outfits.toml index bcbc42c..d926f5c 100644 --- a/content/outfits.toml +++ b/content/outfits.toml @@ -14,3 +14,42 @@ space.outfit = 5 shield.generation = 10 shield.strength = 500 shield.delay = 2.0 + + +[outfit."blaster"] + +space.weapon = 10 + +# Average delay between shots +gun.rate = 0.2 +# Random rate variation (each cooldown is +- this) +gun.rate_rng = 0.1 + +# TODO: apply force to ship on fire +gun.projectile.sprite = "projectile::blaster" +# Height of projectile in game units +gun.projectile.size = 6 +gun.projectile.size_rng = 0.0 +# Speed of projectile, in game units/second +gun.projectile.speed = 300 +gun.projectile.speed_rng = 10.0 +# Lifetime of projectile, in seconds +gun.projectile.lifetime = 2.0 +gun.projectile.lifetime_rng = 0.2 +gun.projectile.damage = 10.0 +# Random variation of firing angle. +# If this is 0, the projectile always goes straight. +# If this is 10, projectiles will form a cone of 10 degrees +# ( 5 degrees on each side ) +gun.projectile.angle_rng = 2 +# How much force this projectile will apply to an object it hits +gun.projectile.force = 0.0 + +gun.projectile.collider.ball.radius = 2.0 + +gun.projectile.impact_effect = "blaster impact" + +gun.projectile.expire_effect.sprite = "particle::blaster" +gun.projectile.expire_effect.lifetime = "inherit" +gun.projectile.expire_effect.size = 3.0 +gun.projectile.expire_effect.velocity_scale_parent = 1.0 diff --git a/crates/content/src/lib.rs b/crates/content/src/lib.rs index 20f5960..4d63f2b 100644 --- a/crates/content/src/lib.rs +++ b/crates/content/src/lib.rs @@ -28,11 +28,10 @@ mod syntax { use serde::Deserialize; use std::{collections::HashMap, fmt::Display, hash::Hash}; - use crate::part::{effect, faction, gun, outfit, ship, sprite, system}; + use crate::part::{effect, faction, outfit, ship, sprite, system}; #[derive(Debug, Deserialize)] pub struct Root { - pub gun: Option>, pub ship: Option>, pub system: Option>, pub outfit: Option>, @@ -69,7 +68,6 @@ mod syntax { impl Root { pub fn new() -> Self { Self { - gun: None, ship: None, system: None, outfit: None, @@ -80,7 +78,6 @@ mod syntax { } pub fn merge(&mut self, other: Root) -> Result<()> { - merge_hashmap(&mut self.gun, other.gun).with_context(|| "while merging guns")?; merge_hashmap(&mut self.ship, other.ship).with_context(|| "while merging ships")?; merge_hashmap(&mut self.system, other.system) .with_context(|| "while merging systems")?; @@ -241,9 +238,6 @@ impl Content { if root.ship.is_some() { part::ship::Ship::build(root.ship.take().unwrap(), &mut build_context, &mut content)?; } - if root.gun.is_some() { - part::gun::Gun::build(root.gun.take().unwrap(), &mut build_context, &mut content)?; - } if root.outfit.is_some() { part::outfit::Outfit::build( root.outfit.take().unwrap(), diff --git a/crates/content/src/part/gun.rs b/crates/content/src/part/gun.rs deleted file mode 100644 index 3a359c7..0000000 --- a/crates/content/src/part/gun.rs +++ /dev/null @@ -1,186 +0,0 @@ -use anyhow::{bail, Context, Result}; -use cgmath::{Deg, Rad}; -use serde::Deserialize; -use std::collections::HashMap; - -use crate::{handle::SpriteHandle, Content}; - -use crate::{ContentBuildContext, EffectHandle, OutfitSpace}; - -pub(crate) mod syntax { - use crate::part::effect; - use crate::part::outfitspace; - use serde::Deserialize; - // Raw serde syntax structs. - // These are never seen by code outside this crate. - - #[derive(Debug, Deserialize)] - pub struct Gun { - pub projectile: Projectile, - pub rate: f32, - pub rate_rng: f32, - pub space: outfitspace::syntax::OutfitSpace, - } - - #[derive(Debug, Deserialize)] - pub struct Projectile { - pub sprite: String, - pub size: f32, - pub size_rng: f32, - pub speed: f32, - pub speed_rng: f32, - pub lifetime: f32, - pub lifetime_rng: f32, - pub damage: f32, - pub angle_rng: f32, - pub impact_effect: Option, - pub expire_effect: Option, - pub collider: super::ProjectileCollider, - pub force: f32, - } -} - -/// Defines a projectile's collider -#[derive(Debug, Deserialize, Clone)] -pub enum ProjectileCollider { - /// A ball collider - #[serde(rename = "ball")] - Ball(BallCollider), -} - -/// A simple ball-shaped collider, centered at the object's position -#[derive(Debug, Deserialize, Clone)] -pub struct BallCollider { - /// The radius of this ball - pub radius: f32, -} - -/// Represents a gun outfit. -#[derive(Debug, Clone)] -pub struct Gun { - /// The name of this gun - pub name: String, - - /// The projectile this gun produces - pub projectile: Projectile, - - /// Average delay between projectiles, in seconds. - pub rate: f32, - - /// Random variation of projectile delay, in seconds. - /// Each shot waits (rate += rate_rng). - pub rate_rng: f32, - - /// How much space this gun uses - pub space: OutfitSpace, -} - -/// Represents a projectile that a [`Gun`] produces. -#[derive(Debug, Clone)] -pub struct Projectile { - /// The projectile sprite - pub sprite: SpriteHandle, - - /// The average size of this projectile - /// (height in game units) - pub size: f32, - /// Random size variation - pub size_rng: f32, - - /// The speed of this projectile, in game units / second - pub speed: f32, - /// Random speed variation - pub speed_rng: f32, - - /// The lifespan of this projectile. - /// It will vanish if it lives this long without hitting anything. - pub lifetime: f32, - /// Random lifetime variation - pub lifetime_rng: f32, - - /// The damage this projectile does - pub damage: f32, - - /// The force this projectile applies - pub force: f32, - - /// The angle variation of this projectile. - /// Projectiles can be off center up to - /// `spread / 2` degrees in both directions. - /// - /// (Forming a "fire cone" of `spread` degrees) - pub angle_rng: Rad, - - /// The particle this projectile will spawn when it hits something - pub impact_effect: Option, - - /// The particle this projectile will spawn when it expires - pub expire_effect: Option, - - /// Collider parameters for this projectile - pub collider: ProjectileCollider, -} - -impl crate::Build for Gun { - type InputSyntaxType = HashMap; - - fn build( - gun: Self::InputSyntaxType, - build_context: &mut ContentBuildContext, - content: &mut Content, - ) -> Result<()> { - for (gun_name, gun) in gun { - let projectile_sprite_handle = match content.sprite_index.get(&gun.projectile.sprite) { - None => bail!( - "projectile sprite `{}` doesn't exist in gun `{}`", - gun.projectile.sprite, - gun_name, - ), - Some(t) => *t, - }; - - let impact_effect = match gun.projectile.impact_effect { - Some(e) => Some( - e.to_handle(build_context, content) - .with_context(|| format!("while loading gun `{}`", gun_name))?, - ), - None => None, - }; - - let expire_effect = match gun.projectile.expire_effect { - Some(e) => Some( - e.to_handle(build_context, content) - .with_context(|| format!("while loading gun `{}`", gun_name))?, - ), - None => None, - }; - - content.guns.push(Self { - name: gun_name, - space: gun.space.into(), - rate: gun.rate, - rate_rng: gun.rate_rng, - projectile: Projectile { - force: gun.projectile.force, - sprite: projectile_sprite_handle, - size: gun.projectile.size, - size_rng: gun.projectile.size_rng, - speed: gun.projectile.speed, - speed_rng: gun.projectile.speed_rng, - lifetime: gun.projectile.lifetime, - lifetime_rng: gun.projectile.lifetime_rng, - damage: gun.projectile.damage, - - // Divide by 2, so the angle matches the angle of the fire cone. - // This should ALWAYS be done in the content parser. - angle_rng: Deg(gun.projectile.angle_rng / 2.0).into(), - impact_effect, - expire_effect, - collider: gun.projectile.collider, - }, - }); - } - - return Ok(()); - } -} diff --git a/crates/content/src/part/mod.rs b/crates/content/src/part/mod.rs index 4c8a7c7..5d6d21a 100644 --- a/crates/content/src/part/mod.rs +++ b/crates/content/src/part/mod.rs @@ -2,7 +2,6 @@ pub(crate) mod effect; pub(crate) mod faction; -pub(crate) mod gun; pub(crate) mod outfit; pub(crate) mod outfitspace; pub(crate) mod ship; @@ -11,8 +10,7 @@ pub(crate) mod system; pub use effect::Effect; pub use faction::{Faction, Relationship}; -pub use gun::{Gun, Projectile, ProjectileCollider}; -pub use outfit::Outfit; +pub use outfit::{Gun, Outfit, Projectile, ProjectileCollider}; pub use outfitspace::OutfitSpace; pub use ship::{ CollapseEffectSpawner, CollapseEvent, EffectCollapseEvent, EnginePoint, GunPoint, Ship, diff --git a/crates/content/src/part/outfit.rs b/crates/content/src/part/outfit.rs index 2461e3f..acc8806 100644 --- a/crates/content/src/part/outfit.rs +++ b/crates/content/src/part/outfit.rs @@ -1,11 +1,17 @@ use std::collections::HashMap; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; +use cgmath::Rad; +use serde::Deserialize; -use crate::{handle::SpriteHandle, Content, ContentBuildContext, OutfitHandle, OutfitSpace}; +use crate::{ + handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace, +}; pub(crate) mod syntax { - use crate::part::outfitspace; + use crate::{effect, part::outfitspace, ContentBuildContext}; + use anyhow::{bail, Result}; + use cgmath::Deg; use serde::Deserialize; // Raw serde syntax structs. // These are never seen by code outside this crate. @@ -16,6 +22,7 @@ pub(crate) mod syntax { pub steering: Option, pub space: outfitspace::syntax::OutfitSpace, pub shield: Option, + pub gun: Option, } #[derive(Debug, Deserialize)] @@ -36,6 +43,79 @@ pub(crate) mod syntax { pub struct Steering { pub power: f32, } + + #[derive(Debug, Deserialize)] + pub struct Gun { + pub projectile: Projectile, + pub rate: f32, + pub rate_rng: Option, + } + + impl Gun { + pub fn build( + self, + build_context: &mut ContentBuildContext, + content: &mut crate::Content, + ) -> Result { + let projectile_sprite_handle = match content.sprite_index.get(&self.projectile.sprite) { + None => bail!( + "projectile sprite `{}` doesn't exist", + self.projectile.sprite, + ), + Some(t) => *t, + }; + + let impact_effect = match self.projectile.impact_effect { + Some(e) => Some(e.to_handle(build_context, content)?), + None => None, + }; + + let expire_effect = match self.projectile.expire_effect { + Some(e) => Some(e.to_handle(build_context, content)?), + None => None, + }; + + return Ok(super::Gun { + rate: self.rate, + rate_rng: self.rate_rng.unwrap_or(0.0), + projectile: super::Projectile { + force: self.projectile.force, + sprite: projectile_sprite_handle, + size: self.projectile.size, + size_rng: self.projectile.size_rng, + speed: self.projectile.speed, + speed_rng: self.projectile.speed_rng, + lifetime: self.projectile.lifetime, + lifetime_rng: self.projectile.lifetime_rng, + damage: self.projectile.damage, + + // Divide by 2, so the angle matches the angle of the fire cone. + // This should ALWAYS be done in the content parser. + angle_rng: Deg(self.projectile.angle_rng / 2.0).into(), + impact_effect, + expire_effect, + collider: self.projectile.collider, + }, + }); + } + } + + #[derive(Debug, Deserialize)] + pub struct Projectile { + pub sprite: String, + pub size: f32, + pub size_rng: f32, + pub speed: f32, + pub speed_rng: f32, + pub lifetime: f32, + pub lifetime_rng: f32, + pub damage: f32, + pub angle_rng: f32, + pub impact_effect: Option, + pub expire_effect: Option, + pub collider: super::ProjectileCollider, + pub force: f32, + } } /// Represents an outfit that may be attached to a ship. @@ -69,6 +149,82 @@ pub struct Outfit { /// Wait this many seconds after taking damage before regenerating shields pub shield_delay: f32, + + /// This outfit's gun stats. + /// If this is some, this outfit requires a gun point. + pub gun: Option, +} + +/// Defines a projectile's collider +#[derive(Debug, Deserialize, Clone)] +pub enum ProjectileCollider { + /// A ball collider + #[serde(rename = "ball")] + Ball(BallCollider), +} + +/// A simple ball-shaped collider, centered at the object's position +#[derive(Debug, Deserialize, Clone)] +pub struct BallCollider { + /// The radius of this ball + pub radius: f32, +} + +/// Represents gun stats of an outfit. +/// If an outfit has this value, it requires a gun point. +#[derive(Debug, Clone)] +pub struct Gun { + /// The projectile this gun produces + pub projectile: Projectile, + + /// Average delay between projectiles, in seconds. + pub rate: f32, + + /// Random variation of projectile delay, in seconds. + /// Each shot waits (rate += rate_rng). + pub rate_rng: f32, +} + +/// Represents a projectile that a [`Gun`] produces. +#[derive(Debug, Clone)] +pub struct Projectile { + /// The projectile sprite + pub sprite: SpriteHandle, + + /// The average size of this projectile + /// (height in game units) + pub size: f32, + /// Random size variation + pub size_rng: f32, + + /// The speed of this projectile, in game units / second + pub speed: f32, + /// Random speed variation + pub speed_rng: f32, + + /// The lifespan of this projectile. + /// It will vanish if it lives this long without hitting anything. + pub lifetime: f32, + /// Random lifetime variation + pub lifetime_rng: f32, + + /// The damage this projectile does + pub damage: f32, + + /// The force this projectile applies + pub force: f32, + + /// The angle variation of this projectile. + pub angle_rng: Rad, + + /// The particle this projectile will spawn when it hits something + pub impact_effect: Option, + + /// The particle this projectile will spawn when it expires + pub expire_effect: Option, + + /// Collider parameters for this projectile + pub collider: ProjectileCollider, } impl crate::Build for Outfit { @@ -76,7 +232,7 @@ impl crate::Build for Outfit { fn build( outfits: Self::InputSyntaxType, - _build_context: &mut ContentBuildContext, + build_context: &mut ContentBuildContext, content: &mut Content, ) -> Result<()> { for (outfit_name, outfit) in outfits { @@ -84,7 +240,16 @@ impl crate::Build for Outfit { index: content.outfits.len(), }; + let gun = match outfit.gun { + None => None, + Some(g) => Some( + g.build(build_context, content) + .with_context(|| format!("in outfit {}", outfit_name))?, + ), + }; + let mut o = Self { + gun, handle, name: outfit_name.clone(), engine_thrust: 0.0,