155 lines
3.8 KiB
Rust
Raw Normal View History

2024-01-05 12:09:59 -08:00
use anyhow::{Context, Result};
use serde::Deserialize;
use std::collections::HashMap;
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
pub(crate) mod syntax {
use anyhow::{bail, Result};
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 lifetime: EffectLifetime,
pub inherit_velocity: super::ImpactInheritVelocity,
pub size: f32,
}
// 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 {
EffectLifetime::Seconds(s) => s,
EffectLifetime::Inherit(s) => {
if s == "inherit" {
let sprite = content.get_sprite(sprite);
sprite.fps * sprite.frames.len() as f32
} else {
bail!("bad effect lifetime, must be float or \"inherit\"",)
}
}
};
let handle = EffectHandle {
index: content.effects.len(),
};
content.effects.push(super::Effect {
sprite,
lifetime,
handle,
inherit_velocity: self.inherit_velocity,
size: self.size,
});
return Ok(handle);
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum EffectLifetime {
Inherit(String),
Seconds(f32),
}
// 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),
},
})
}
}
}
/// How we should set an effect'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,
}
/// The particle a projectile will spawn when it hits something
#[derive(Debug, Clone)]
pub struct Effect {
/// The sprite to use for this particle.
/// This is most likely animated.
pub sprite: SpriteHandle,
/// This effect's handle
pub handle: EffectHandle,
/// 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,
}
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(());
}
}