use super::collision::CollisionPolicy;
use super::emitter::{BudgetPolicy, InitPolicy, ReplicationPolicy, SimPolicy, SpawnPolicy, SpawnShape};
use super::promotion::PromotionPolicy;
use super::render_mode::RepresentationMode;
use super::render_policy::RenderPolicy;
use super::semantic_binding::SemanticBinding;
use serde::{Deserialize, Serialize};
pub mod combat;
pub mod core_motion;
pub mod destruction;
pub mod dream;
pub mod environment;
pub mod hazard;
pub mod impact;
pub mod particle;
pub mod promoted;
pub mod propagation;
pub mod surface;
pub mod ui_feedback;
pub mod weather;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PresetFamily {
CoreMotion,
Combat,
Impact,
Destruction,
Environment,
Weather,
Hazard,
Propagation,
Particle,
Promoted,
Surface,
UIFeedback,
Dream,
}
impl PresetFamily {
pub const ALL: &[PresetFamily] = &[
Self::CoreMotion,
Self::Combat,
Self::Impact,
Self::Destruction,
Self::Environment,
Self::Weather,
Self::Hazard,
Self::Propagation,
Self::Particle,
Self::Promoted,
Self::Surface,
Self::UIFeedback,
Self::Dream,
];
pub fn name(&self) -> &'static str {
match self {
Self::CoreMotion => "Core Motion",
Self::Combat => "Combat",
Self::Impact => "Impact",
Self::Destruction => "Destruction",
Self::Environment => "Environment",
Self::Weather => "Weather",
Self::Hazard => "Hazard",
Self::Propagation => "Propagation",
Self::Particle => "Particle",
Self::Promoted => "Promoted",
Self::Surface => "Surface",
Self::UIFeedback => "UI Feedback",
Self::Dream => "Dream",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuthorityProfile {
VisualOnly,
EventReconstructable,
PromotionCapable,
AuthoritativeSurfaceRecognized,
AuthoritativeSurfaceEffect,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PresetCatalogEntry {
pub id: String,
pub name: String,
pub family: PresetFamily,
pub authority: AuthorityProfile,
pub semantics: SemanticBinding,
pub shape: SpawnShape,
pub spawn: SpawnPolicy,
pub initialize: InitPolicy,
pub simulate: SimPolicy,
pub collision: CollisionPolicy,
pub render: RenderPolicy,
pub promotion: PromotionPolicy,
pub budgets: BudgetPolicy,
pub replication: ReplicationPolicy,
pub supported_views: Vec<RepresentationMode>,
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct PresetRegistry {
pub entries: Vec<PresetCatalogEntry>,
}
impl PresetRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn with_builtins() -> Self {
let mut entries = Vec::new();
entries.extend(core_motion::catalog());
entries.extend(combat::catalog());
entries.extend(impact::catalog());
entries.extend(destruction::catalog());
entries.extend(environment::catalog());
entries.extend(weather::catalog());
entries.extend(hazard::catalog());
entries.extend(propagation::catalog());
entries.extend(particle::catalog());
entries.extend(promoted::catalog());
entries.extend(surface::catalog());
entries.extend(ui_feedback::catalog());
entries.extend(dream::catalog());
Self { entries }
}
pub fn find(&self, id: &str) -> Option<&PresetCatalogEntry> {
self.entries.iter().find(|e| e.id == id)
}
pub fn by_family(&self, family: PresetFamily) -> Vec<&PresetCatalogEntry> {
self.entries.iter().filter(|e| e.family == family).collect()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preset_family_all() {
assert_eq!(PresetFamily::ALL.len(), 13);
}
#[test]
fn preset_registry_builtins_no_duplicates() {
let reg = PresetRegistry::with_builtins();
let mut ids: Vec<&str> = reg.entries.iter().map(|e| e.id.as_str()).collect();
let before = ids.len();
ids.sort();
ids.dedup();
assert_eq!(before, ids.len(), "duplicate preset IDs found");
}
#[test]
fn preset_registry_all_families_represented() {
let reg = PresetRegistry::with_builtins();
for &family in PresetFamily::ALL {
let count = reg.by_family(family).len();
assert!(count > 0, "no presets for family {:?}", family);
}
}
#[test]
fn preset_registry_find() {
let reg = PresetRegistry::with_builtins();
assert!(reg.find("core_idle_motes").is_some());
}
}