2024-01-03 13:19:10 -08:00
|
|
|
use anyhow::{bail, Context, Result};
|
2023-12-28 17:04:41 -08:00
|
|
|
use cgmath::Deg;
|
2024-01-03 13:19:10 -08:00
|
|
|
use serde::Deserialize;
|
|
|
|
use std::collections::HashMap;
|
2023-12-27 20:13:39 -08:00
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
use crate::{handle::TextureHandle, Content};
|
2023-12-30 10:58:17 -08:00
|
|
|
|
2023-12-31 18:48:35 -08:00
|
|
|
use crate::OutfitSpace;
|
2023-12-30 21:05:06 -08:00
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
pub(crate) mod syntax {
|
2023-12-31 18:48:35 -08:00
|
|
|
use crate::part::shared;
|
2023-12-27 20:13:39 -08:00
|
|
|
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,
|
2023-12-28 17:04:41 -08:00
|
|
|
pub rate: f32,
|
|
|
|
pub rate_rng: f32,
|
2023-12-30 21:05:06 -08:00
|
|
|
pub space: shared::syntax::OutfitSpace,
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Projectile {
|
2023-12-30 11:25:51 -08:00
|
|
|
pub sprite_texture: String,
|
2023-12-27 20:13:39 -08:00
|
|
|
pub size: f32,
|
2023-12-28 17:04:41 -08:00
|
|
|
pub size_rng: f32,
|
2023-12-27 20:13:39 -08:00
|
|
|
pub speed: f32,
|
2023-12-28 17:04:41 -08:00
|
|
|
pub speed_rng: f32,
|
2023-12-27 20:13:39 -08:00
|
|
|
pub lifetime: f32,
|
2023-12-28 17:04:41 -08:00
|
|
|
pub lifetime_rng: f32,
|
2023-12-29 12:21:56 -08:00
|
|
|
pub damage: f32,
|
2024-01-01 15:41:47 -08:00
|
|
|
pub angle_rng: f32,
|
2024-01-03 13:19:10 -08:00
|
|
|
pub impact: Option<ProjectileParticle>,
|
|
|
|
pub expire: Option<ProjectileParticle>,
|
|
|
|
pub collider: super::ProjectileCollider,
|
|
|
|
pub force: f32,
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
2024-01-03 13:19:10 -08:00
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct ProjectileParticle {
|
|
|
|
pub texture: String,
|
|
|
|
pub lifetime: ParticleLifetime,
|
|
|
|
pub inherit_velocity: super::ImpactInheritVelocity,
|
|
|
|
pub size: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum ParticleLifetime {
|
|
|
|
Inherit(String),
|
|
|
|
Seconds(f32),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Defines a projectile's collider
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
pub enum ProjectileCollider {
|
|
|
|
/// A ball collider
|
|
|
|
#[serde(rename = "ball")]
|
|
|
|
Ball(BallCollider),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
pub struct BallCollider {
|
|
|
|
pub radius: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// How we should set an impact particle's velocity
|
|
|
|
#[derive(Debug, Deserialize, Clone)]
|
|
|
|
pub enum ImpactInheritVelocity {
|
|
|
|
/// Don't inherit any velocity.
|
|
|
|
/// This impact particle will be still.
|
|
|
|
#[serde(rename = "don't")]
|
|
|
|
Dont,
|
|
|
|
|
|
|
|
/// Inherit target velocity.
|
|
|
|
/// This impact particle will stick to the object it hits.
|
|
|
|
#[serde(rename = "target")]
|
|
|
|
Target,
|
|
|
|
|
|
|
|
/// Inherit projectile velocity.
|
|
|
|
/// This impact particle will continue on its projectile's path.
|
|
|
|
#[serde(rename = "projectile")]
|
|
|
|
Projectile,
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Represents a gun outfit.
|
2023-12-27 20:13:39 -08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Gun {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// The name of this gun
|
2023-12-27 20:13:39 -08:00
|
|
|
pub name: String,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The projectile this gun produces
|
2023-12-27 20:13:39 -08:00
|
|
|
pub projectile: Projectile,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// Average delay between projectiles, in seconds.
|
2023-12-28 17:04:41 -08:00
|
|
|
pub rate: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// Random variation of projectile delay, in seconds.
|
|
|
|
/// Each shot waits (rate += rate_rng).
|
2023-12-28 17:04:41 -08:00
|
|
|
pub rate_rng: f32,
|
2023-12-30 21:05:06 -08:00
|
|
|
|
|
|
|
/// How much space this gun uses
|
|
|
|
pub space: OutfitSpace,
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
|
|
|
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Represents a projectile that a [`Gun`] produces.
|
2023-12-27 20:13:39 -08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Projectile {
|
2023-12-29 15:46:09 -08:00
|
|
|
/// The projectile sprite
|
2023-12-30 11:25:51 -08:00
|
|
|
pub sprite_texture: TextureHandle,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The average size of this projectile
|
|
|
|
/// (height in game units)
|
2023-12-27 20:13:39 -08:00
|
|
|
pub size: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Random size variation
|
2023-12-28 17:04:41 -08:00
|
|
|
pub size_rng: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The speed of this projectile, in game units / second
|
2023-12-27 20:13:39 -08:00
|
|
|
pub speed: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Random speed variation
|
2023-12-28 17:04:41 -08:00
|
|
|
pub speed_rng: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The lifespan of this projectile.
|
|
|
|
/// It will vanish if it lives this long without hitting anything.
|
2023-12-27 20:13:39 -08:00
|
|
|
pub lifetime: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
/// Random lifetime variation
|
2023-12-28 17:04:41 -08:00
|
|
|
pub lifetime_rng: f32,
|
2023-12-29 15:46:09 -08:00
|
|
|
|
|
|
|
/// The damage this projectile does
|
2023-12-29 12:21:56 -08:00
|
|
|
pub damage: f32,
|
2024-01-01 15:41:47 -08:00
|
|
|
|
2024-01-03 13:19:10 -08:00
|
|
|
/// The force this projectile applies
|
|
|
|
pub force: f32,
|
|
|
|
|
2024-01-01 15:41:47 -08:00
|
|
|
/// 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: Deg<f32>,
|
2024-01-03 13:19:10 -08:00
|
|
|
|
|
|
|
/// The particle this projectile will spawn when it hits something
|
|
|
|
pub impact_particle: Option<ProjectileParticle>,
|
|
|
|
|
|
|
|
/// The particle this projectile will spawn when it expires
|
|
|
|
pub expire_particle: Option<ProjectileParticle>,
|
|
|
|
|
|
|
|
/// Collider parameters for this projectile
|
|
|
|
pub collider: ProjectileCollider,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The particle a projectile will spawn when it hits something
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct ProjectileParticle {
|
|
|
|
/// The texture to use for this particle.
|
|
|
|
/// This is most likely animated.
|
|
|
|
pub texture: TextureHandle,
|
|
|
|
|
|
|
|
/// How many seconds this particle should live
|
|
|
|
pub lifetime: f32,
|
|
|
|
|
|
|
|
/// How we should set this particle's velocity
|
|
|
|
pub inherit_velocity: ImpactInheritVelocity,
|
|
|
|
|
|
|
|
/// The height of this particle, in game units.
|
|
|
|
pub size: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn parse_projectile_particle(
|
|
|
|
ct: &Content,
|
|
|
|
p: Option<syntax::ProjectileParticle>,
|
|
|
|
) -> Result<Option<ProjectileParticle>> {
|
|
|
|
if let Some(impact) = p {
|
|
|
|
let impact_texture = match ct.texture_index.get(&impact.texture) {
|
|
|
|
None => bail!("impact texture `{}` doesn't exist", impact.texture),
|
|
|
|
Some(t) => *t,
|
|
|
|
};
|
|
|
|
|
|
|
|
let impact_lifetime = match impact.lifetime {
|
|
|
|
syntax::ParticleLifetime::Seconds(s) => s,
|
|
|
|
syntax::ParticleLifetime::Inherit(s) => {
|
|
|
|
if s == "inherit" {
|
|
|
|
let t = ct.get_texture(impact_texture);
|
|
|
|
t.fps * t.frames.len() as f32
|
|
|
|
} else {
|
|
|
|
bail!("bad impact lifetime, must be float or \"inherit\"",)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Some(ProjectileParticle {
|
|
|
|
texture: impact_texture,
|
|
|
|
lifetime: impact_lifetime,
|
|
|
|
inherit_velocity: impact.inherit_velocity,
|
|
|
|
size: impact.size,
|
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
|
|
|
|
2023-12-30 16:57:03 -08:00
|
|
|
impl crate::Build for Gun {
|
2023-12-30 10:58:17 -08:00
|
|
|
type InputSyntax = HashMap<String, syntax::Gun>;
|
|
|
|
|
|
|
|
fn build(gun: Self::InputSyntax, ct: &mut Content) -> Result<()> {
|
2023-12-27 20:13:39 -08:00
|
|
|
for (gun_name, gun) in gun {
|
2024-01-03 13:19:10 -08:00
|
|
|
let projectile_texture = match ct.texture_index.get(&gun.projectile.sprite_texture) {
|
2023-12-30 10:58:17 -08:00
|
|
|
None => bail!(
|
2024-01-03 13:19:10 -08:00
|
|
|
"In gun `{}`: projectile texture `{}` doesn't exist",
|
2023-12-30 10:58:17 -08:00
|
|
|
gun_name,
|
2023-12-30 11:25:51 -08:00
|
|
|
gun.projectile.sprite_texture
|
2023-12-30 10:58:17 -08:00
|
|
|
),
|
|
|
|
Some(t) => *t,
|
|
|
|
};
|
|
|
|
|
2024-01-03 13:19:10 -08:00
|
|
|
let impact_particle = parse_projectile_particle(ct, gun.projectile.impact)
|
|
|
|
.with_context(|| format!("In gun `{}`", gun_name))?;
|
|
|
|
|
|
|
|
let expire_particle = parse_projectile_particle(ct, gun.projectile.expire)
|
|
|
|
.with_context(|| format!("In gun `{}`", gun_name))?;
|
|
|
|
|
2023-12-30 10:58:17 -08:00
|
|
|
ct.guns.push(Self {
|
|
|
|
name: gun_name,
|
2023-12-30 21:05:06 -08:00
|
|
|
space: gun.space.into(),
|
2023-12-28 17:04:41 -08:00
|
|
|
rate: gun.rate,
|
|
|
|
rate_rng: gun.rate_rng,
|
2023-12-27 20:13:39 -08:00
|
|
|
projectile: Projectile {
|
2024-01-03 13:19:10 -08:00
|
|
|
force: gun.projectile.force,
|
|
|
|
sprite_texture: projectile_texture,
|
2023-12-27 20:13:39 -08:00
|
|
|
size: gun.projectile.size,
|
2023-12-28 17:04:41 -08:00
|
|
|
size_rng: gun.projectile.size_rng,
|
2023-12-27 20:13:39 -08:00
|
|
|
speed: gun.projectile.speed,
|
2023-12-28 17:04:41 -08:00
|
|
|
speed_rng: gun.projectile.speed_rng,
|
2023-12-27 20:13:39 -08:00
|
|
|
lifetime: gun.projectile.lifetime,
|
2023-12-28 17:04:41 -08:00
|
|
|
lifetime_rng: gun.projectile.lifetime_rng,
|
2023-12-29 12:21:56 -08:00
|
|
|
damage: gun.projectile.damage,
|
2024-01-01 15:41:47 -08:00
|
|
|
angle_rng: Deg(gun.projectile.angle_rng),
|
2024-01-03 13:19:10 -08:00
|
|
|
impact_particle,
|
|
|
|
expire_particle,
|
|
|
|
collider: gun.projectile.collider,
|
2023-12-27 20:13:39 -08:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-12-30 10:58:17 -08:00
|
|
|
return Ok(());
|
2023-12-27 20:13:39 -08:00
|
|
|
}
|
|
|
|
}
|