2024-01-05 12:09:59 -08:00
|
|
|
use anyhow::{Context, Result};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
|
|
|
|
|
|
|
|
pub(crate) mod syntax {
|
|
|
|
use anyhow::{bail, Result};
|
2024-01-12 22:47:40 -08:00
|
|
|
use galactica_util::to_radians;
|
2024-01-05 12:09:59 -08:00
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
use crate::{Content, ContentBuildContext, EffectHandle};
|
|
|
|
// Raw serde syntax structs.
|
|
|
|
// These are never seen by code outside this crate.
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
pub struct Effect {
|
|
|
|
pub sprite: String,
|
|
|
|
pub size: f32,
|
2024-01-06 16:01:02 -08:00
|
|
|
pub size_rng: Option<f32>,
|
|
|
|
pub lifetime: TextOrFloat,
|
|
|
|
pub lifetime_rng: Option<f32>,
|
|
|
|
pub angle: Option<f32>,
|
|
|
|
pub angle_rng: Option<f32>,
|
|
|
|
pub angvel: Option<f32>,
|
|
|
|
pub angvel_rng: Option<f32>,
|
|
|
|
pub velocity_scale_parent: Option<f32>,
|
|
|
|
pub velocity_scale_parent_rng: Option<f32>,
|
|
|
|
pub velocity_scale_target: Option<f32>,
|
|
|
|
pub velocity_scale_target_rng: Option<f32>,
|
|
|
|
pub direction_rng: Option<f32>,
|
2024-01-07 12:15:34 -08:00
|
|
|
pub fade: Option<f32>,
|
|
|
|
pub fade_rng: Option<f32>,
|
2024-01-06 16:01:02 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum TextOrFloat {
|
|
|
|
Text(String),
|
|
|
|
Float(f32),
|
2024-01-05 12:09:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// We implement building here instead of in super::Effect because
|
|
|
|
// effects may be defined inline (see EffectReference).
|
|
|
|
impl Effect {
|
|
|
|
pub fn add_to(
|
|
|
|
self,
|
|
|
|
_build_context: &mut ContentBuildContext,
|
|
|
|
content: &mut Content,
|
|
|
|
) -> Result<EffectHandle> {
|
|
|
|
let sprite = match content.sprite_index.get(&self.sprite) {
|
|
|
|
None => bail!("sprite `{}` doesn't exist", self.sprite),
|
|
|
|
Some(t) => *t,
|
|
|
|
};
|
|
|
|
|
|
|
|
let lifetime = match self.lifetime {
|
2024-01-06 16:01:02 -08:00
|
|
|
TextOrFloat::Float(f) => f,
|
|
|
|
TextOrFloat::Text(s) => {
|
2024-01-05 12:09:59 -08:00
|
|
|
if s == "inherit" {
|
|
|
|
let sprite = content.get_sprite(sprite);
|
2024-01-07 12:15:34 -08:00
|
|
|
sprite.frame_duration * sprite.frames.len() as f32
|
2024-01-05 12:09:59 -08:00
|
|
|
} else {
|
|
|
|
bail!("bad effect lifetime, must be float or \"inherit\"",)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let handle = EffectHandle {
|
|
|
|
index: content.effects.len(),
|
|
|
|
};
|
|
|
|
content.effects.push(super::Effect {
|
|
|
|
handle,
|
2024-01-06 16:01:02 -08:00
|
|
|
sprite,
|
2024-01-05 12:09:59 -08:00
|
|
|
size: self.size,
|
2024-01-06 16:01:02 -08:00
|
|
|
size_rng: self.size_rng.unwrap_or(0.0),
|
|
|
|
lifetime,
|
|
|
|
lifetime_rng: self.lifetime_rng.unwrap_or(0.0),
|
2024-01-12 22:47:40 -08:00
|
|
|
angle: to_radians(self.angle.unwrap_or(0.0) / 2.0),
|
|
|
|
angle_rng: to_radians(self.angle_rng.unwrap_or(0.0) / 2.0),
|
|
|
|
angvel: to_radians(self.angvel.unwrap_or(0.0)),
|
|
|
|
angvel_rng: to_radians(self.angvel_rng.unwrap_or(0.0)),
|
2024-01-06 16:01:02 -08:00
|
|
|
velocity_scale_parent: self.velocity_scale_parent.unwrap_or(0.0),
|
|
|
|
velocity_scale_parent_rng: self.velocity_scale_parent_rng.unwrap_or(0.0),
|
|
|
|
velocity_scale_target: self.velocity_scale_target.unwrap_or(0.0),
|
|
|
|
velocity_scale_target_rng: self.velocity_scale_target_rng.unwrap_or(0.0),
|
2024-01-07 12:15:34 -08:00
|
|
|
direction_rng: self.direction_rng.unwrap_or(0.0) / 2.0,
|
|
|
|
fade: self.fade.unwrap_or(0.0),
|
|
|
|
fade_rng: self.fade_rng.unwrap_or(0.0),
|
2024-01-05 12:09:59 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
return Ok(handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This isn't used here, but is pulled in by other content items.
|
|
|
|
/// A reference to an effect by name, or an inline definition.
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
#[serde(untagged)]
|
|
|
|
pub enum EffectReference {
|
|
|
|
Label(String),
|
|
|
|
Effect(Effect),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EffectReference {
|
|
|
|
pub fn to_handle(
|
|
|
|
self,
|
|
|
|
build_context: &mut ContentBuildContext,
|
|
|
|
content: &mut Content,
|
|
|
|
) -> Result<EffectHandle> {
|
|
|
|
// We do not insert anything into build_context here,
|
|
|
|
// since inline effects cannot be referenced by name.
|
|
|
|
Ok(match self {
|
|
|
|
Self::Effect(e) => e.add_to(build_context, content)?,
|
|
|
|
Self::Label(l) => match build_context.effect_index.get(&l) {
|
|
|
|
Some(h) => *h,
|
|
|
|
None => bail!("no effect named `{}`", l),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The particle a projectile will spawn when it hits something
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Effect {
|
2024-01-06 16:01:02 -08:00
|
|
|
/// This effect's handle
|
|
|
|
pub handle: EffectHandle,
|
|
|
|
|
2024-01-05 12:09:59 -08:00
|
|
|
/// The sprite to use for this particle.
|
|
|
|
pub sprite: SpriteHandle,
|
|
|
|
|
2024-01-06 16:01:02 -08:00
|
|
|
/// The height of this particle, in game units.
|
|
|
|
pub size: f32,
|
|
|
|
|
|
|
|
/// Random size variation
|
|
|
|
pub size_rng: f32,
|
2024-01-05 12:09:59 -08:00
|
|
|
|
|
|
|
/// How many seconds this particle should live
|
|
|
|
pub lifetime: f32,
|
|
|
|
|
2024-01-06 16:01:02 -08:00
|
|
|
/// Random lifetime variation
|
|
|
|
pub lifetime_rng: f32,
|
2024-01-05 12:09:59 -08:00
|
|
|
|
2024-01-12 22:47:40 -08:00
|
|
|
/// The angle this particle points once spawned, in radians
|
|
|
|
pub angle: f32,
|
2024-01-06 16:01:02 -08:00
|
|
|
|
2024-01-12 22:47:40 -08:00
|
|
|
/// Random angle variation, in radians
|
2024-01-07 12:15:34 -08:00
|
|
|
pub angle_rng: f32,
|
2024-01-06 16:01:02 -08:00
|
|
|
|
2024-01-12 22:47:40 -08:00
|
|
|
/// How fast this particle spins, in radians/sec
|
|
|
|
pub angvel: f32,
|
2024-01-06 16:01:02 -08:00
|
|
|
|
|
|
|
/// Random angvel variation
|
2024-01-07 12:15:34 -08:00
|
|
|
pub angvel_rng: f32,
|
2024-01-06 16:01:02 -08:00
|
|
|
|
|
|
|
/// The amount of this particle's parent's velocity to inherit
|
|
|
|
pub velocity_scale_parent: f32,
|
|
|
|
|
|
|
|
/// Parent velocity random variation
|
|
|
|
pub velocity_scale_parent_rng: f32,
|
|
|
|
|
|
|
|
/// The amount of this particle's parent's target velocity to inherit.
|
|
|
|
/// If there is no target, this is zero.
|
|
|
|
pub velocity_scale_target: f32,
|
|
|
|
|
|
|
|
/// Target velocity random variation
|
|
|
|
pub velocity_scale_target_rng: f32,
|
|
|
|
|
|
|
|
/// Travel direction random variation
|
2024-01-07 12:15:34 -08:00
|
|
|
pub direction_rng: f32,
|
|
|
|
|
|
|
|
/// Fade this effect out over this many seconds as it ends
|
|
|
|
pub fade: f32,
|
|
|
|
|
|
|
|
/// Random fade ariation
|
|
|
|
pub fade_rng: f32,
|
2024-01-05 12:09:59 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl crate::Build for Effect {
|
|
|
|
type InputSyntaxType = HashMap<String, syntax::Effect>;
|
|
|
|
|
|
|
|
fn build(
|
|
|
|
effects: Self::InputSyntaxType,
|
|
|
|
build_context: &mut ContentBuildContext,
|
|
|
|
content: &mut Content,
|
|
|
|
) -> Result<()> {
|
|
|
|
for (effect_name, effect) in effects {
|
|
|
|
let h = effect
|
|
|
|
.add_to(build_context, content)
|
|
|
|
.with_context(|| format!("while evaluating effect `{}`", effect_name))?;
|
|
|
|
build_context.effect_index.insert(effect_name, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|