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::tags::TagSet;
use serde::{Deserialize, Serialize};

/// Property key — interned string identifier for property lookups.
pub type PropertyKey = u32;

/// Typed property value.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PropertyValue {
    Bool(bool),
    Int(i32),
    Float(f32),
    Vec2([f32; 2]),
    Vec3([f32; 3]),
    Vec4([f32; 4]),
    Tag(String),
    Enum(String),
    String(String),
}

/// Property enum for material class and similar categorical values.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PropertyEnum {
    Material(super::materials::MaterialClass),
    Custom(String),
}

/// Property assignment — key-value pair for initialization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PropertyAssignment {
    pub key: String,
    pub value: PropertyValue,
}

/// Predicate for property-based rule evaluation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PropertyPredicate {
    GreaterThan(String, f32),
    LessThan(String, f32),
    EqualsBool(String, bool),
    EqualsEnum(String, String),
}

/// Semantic properties for an entity — traits, signals, receivers, and typed properties.
/// Uses Vec-based storage (sorted by key) instead of HashMap for hot-path access.
/// Physics property stores are typically small (<64 entries), so linear/binary search
/// outperforms HashMap due to better cache locality and lower per-lookup overhead.
#[derive(Debug, Clone, Default)]
pub struct SemanticProperties {
    pub traits: TagSet,
    pub signals: TagSet,
    pub receivers: TagSet,
    pub scalars: Vec<(PropertyKey, f32)>,
    pub vectors: Vec<(PropertyKey, [f32; 4])>,
    pub ints: Vec<(PropertyKey, i32)>,
    pub bools: Vec<(PropertyKey, bool)>,
    pub enums: Vec<(PropertyKey, PropertyEnum)>,
}

/// Insert or update a value in a sorted key-value Vec.
fn vec_insert<V>(entries: &mut Vec<(PropertyKey, V)>, key: PropertyKey, value: V) {
    match entries.binary_search_by_key(&key, |(k, _)| *k) {
        Ok(idx) => entries[idx].1 = value,
        Err(idx) => entries.insert(idx, (key, value)),
    }
}

/// Look up a value in a sorted key-value Vec.
fn vec_get<V: Copy>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<V> {
    entries
        .binary_search_by_key(&key, |(k, _)| *k)
        .ok()
        .map(|idx| entries[idx].1)
}

/// Look up a reference in a sorted key-value Vec.
fn vec_get_ref<V>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<&V> {
    entries
        .binary_search_by_key(&key, |(k, _)| *k)
        .ok()
        .map(|idx| &entries[idx].1)
}

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

    pub fn set_scalar(&mut self, key: PropertyKey, value: f32) {
        vec_insert(&mut self.scalars, key, value);
    }

    pub fn get_scalar(&self, key: PropertyKey) -> Option<f32> {
        vec_get(&self.scalars, key)
    }

    pub fn set_bool(&mut self, key: PropertyKey, value: bool) {
        vec_insert(&mut self.bools, key, value);
    }

    pub fn get_bool(&self, key: PropertyKey) -> Option<bool> {
        vec_get(&self.bools, key)
    }

    pub fn set_int(&mut self, key: PropertyKey, value: i32) {
        vec_insert(&mut self.ints, key, value);
    }

    pub fn get_int(&self, key: PropertyKey) -> Option<i32> {
        vec_get(&self.ints, key)
    }

    pub fn set_vec4(&mut self, key: PropertyKey, value: [f32; 4]) {
        vec_insert(&mut self.vectors, key, value);
    }

    pub fn get_vec4(&self, key: PropertyKey) -> Option<[f32; 4]> {
        vec_get(&self.vectors, key)
    }

    pub fn get_enum(&self, key: PropertyKey) -> Option<&PropertyEnum> {
        vec_get_ref(&self.enums, key)
    }

    pub fn set_enum(&mut self, key: PropertyKey, value: PropertyEnum) {
        vec_insert(&mut self.enums, key, value);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn semantic_properties_scalar_roundtrip() {
        let mut props = SemanticProperties::new();
        props.set_scalar(0, 42.0);
        assert_eq!(props.get_scalar(0), Some(42.0));
        assert_eq!(props.get_scalar(1), None);
    }

    #[test]
    fn semantic_properties_bool_roundtrip() {
        let mut props = SemanticProperties::new();
        props.set_bool(5, true);
        assert_eq!(props.get_bool(5), Some(true));
    }

    #[test]
    fn property_value_serde_roundtrip() {
        let val = PropertyValue::Float(3.14);
        let json = serde_json::to_string(&val).unwrap();
        let restored: PropertyValue = serde_json::from_str(&json).unwrap();
        assert_eq!(val, restored);
    }

    #[test]
    fn property_assignment_serde() {
        let pa = PropertyAssignment {
            key: "durability".to_string(),
            value: PropertyValue::Float(600.0),
        };
        let json = serde_json::to_string(&pa).unwrap();
        assert!(json.contains("durability"));
    }
}