dreamwell-engine 1.0.0

Dreamwell pure-logic engine library — transforms, hierarchy, canon pipeline, spatial math, hashing, tile rules, validation, waymark schema, material/lighting descriptors. No SpacetimeDB dependency.
Documentation
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;

/// Preset family grouping.
#[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",
        }
    }
}

/// Authority profile — determines how physics results interact with game state.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum AuthorityProfile {
    VisualOnly,
    EventReconstructable,
    PromotionCapable,
    AuthoritativeSurfaceRecognized,
    AuthoritativeSurfaceEffect,
}

/// Catalog entry — complete preset definition.
#[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>,
}

/// Registry of all preset catalog entries.
#[derive(Debug, Clone, Default)]
pub struct PresetRegistry {
    pub entries: Vec<PresetCatalogEntry>,
}

impl PresetRegistry {
    pub fn new() -> Self {
        Self::default()
    }

    /// Load all built-in presets.
    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());
    }
}