use std::any::TypeId;
use std::mem::discriminant;
use crate::anim::{ParamId, ParamKind, Params, SpecError};
use crate::sim::{Ability, World};
pub trait Animate {
const PARAMS: &'static [(&'static str, ParamKind)];
fn drive(&self, world: &World, params: &mut Params, ids: &[ParamId]);
}
#[derive(Clone)]
pub(crate) struct AnimHook {
pub ty: TypeId,
params: &'static [(&'static str, ParamKind)],
ids: Vec<ParamId>,
drive: fn(&World, &mut Params, &[ParamId]),
}
impl AnimHook {
pub fn of<T: Ability + Animate + 'static>() -> Self {
Self {
ty: TypeId::of::<T>(),
params: T::PARAMS,
ids: Vec::new(),
drive: |w, p, ids| {
if let Some(t) = w.ability::<T>() {
t.drive(w, p, ids);
}
},
}
}
pub fn lookup(&self, params: &Params) -> Result<Vec<ParamId>, SpecError> {
self.params
.iter()
.map(|&(name, kind)| {
let id = params
.id(name)
.ok_or_else(|| SpecError::UnknownParam(name.into()))?;
let declared = params.kind(id);
if discriminant(&declared) == discriminant(&kind) {
Ok(id)
} else {
Err(SpecError::WrongKind {
param: name.into(),
declared,
})
}
})
.collect()
}
pub fn resolve(&mut self, params: &Params) -> Result<(), SpecError> {
self.ids = self.lookup(params)?;
Ok(())
}
pub fn set_ids(&mut self, ids: Vec<ParamId>) {
self.ids = ids;
}
pub fn run(&self, world: &World, params: &mut Params) {
(self.drive)(world, params, &self.ids);
}
pub fn add(hooks: &mut Vec<AnimHook>, hook: AnimHook) {
if !hooks.iter().any(|h| h.ty == hook.ty) {
hooks.push(hook);
}
}
}