use super::errors::{AuthoringError, WaymarkError};
use crate::physics::properties::PropertyValue;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FunctionScope {
Universe,
Galaxy,
Sector,
World,
Realm,
Region,
Area,
Location,
Room,
Point,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FunctionCategory {
Physics,
Traversal,
Combat,
Simulation,
Dreamwell,
Topology,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UniversalFunction {
pub key: String,
pub category: FunctionCategory,
pub scope: FunctionScope,
pub default_value: PropertyValue,
pub description: String,
}
#[derive(Debug, Clone, Default)]
pub struct FunctionRegistry {
functions: Vec<UniversalFunction>,
key_index: HashMap<String, usize>,
}
impl FunctionRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn with_builtins() -> Self {
let mut reg = Self::new();
let builtins = vec![
(
"gravity.local",
FunctionCategory::Physics,
FunctionScope::Region,
PropertyValue::Float(9.81),
"Local gravity acceleration",
),
(
"gravity.planetary",
FunctionCategory::Physics,
FunctionScope::World,
PropertyValue::Float(9.81),
"Planetary gravity",
),
(
"atmosphere.density",
FunctionCategory::Physics,
FunctionScope::World,
PropertyValue::Float(1.0),
"Atmospheric density multiplier",
),
(
"pressure.local",
FunctionCategory::Physics,
FunctionScope::Room,
PropertyValue::Float(1.0),
"Local air pressure",
),
(
"temperature.ambient",
FunctionCategory::Physics,
FunctionScope::Region,
PropertyValue::Float(20.0),
"Ambient temperature",
),
(
"fluid.viscosity",
FunctionCategory::Physics,
FunctionScope::Area,
PropertyValue::Float(1.0),
"Fluid viscosity multiplier",
),
(
"pathing.walkability",
FunctionCategory::Traversal,
FunctionScope::Area,
PropertyValue::Float(1.0),
"Walkability multiplier",
),
(
"pathing.flightAllowed",
FunctionCategory::Traversal,
FunctionScope::Region,
PropertyValue::Bool(false),
"Flight allowed",
),
(
"pathing.swimAllowed",
FunctionCategory::Traversal,
FunctionScope::Area,
PropertyValue::Bool(false),
"Swimming allowed",
),
(
"damage.falloff",
FunctionCategory::Combat,
FunctionScope::Area,
PropertyValue::Float(1.0),
"Damage falloff rate",
),
(
"shield.rechargeRate",
FunctionCategory::Combat,
FunctionScope::Region,
PropertyValue::Float(1.0),
"Shield recharge rate",
),
(
"projectile.arcScale",
FunctionCategory::Combat,
FunctionScope::Region,
PropertyValue::Float(1.0),
"Projectile arc scale",
),
(
"time.flow",
FunctionCategory::Simulation,
FunctionScope::Universe,
PropertyValue::Float(1.0),
"Time flow rate",
),
(
"time.scale",
FunctionCategory::Simulation,
FunctionScope::Realm,
PropertyValue::Float(1.0),
"Time scale multiplier",
),
(
"weather.pattern",
FunctionCategory::Simulation,
FunctionScope::Region,
PropertyValue::String("clear".into()),
"Weather pattern",
),
(
"resource.respawnRate",
FunctionCategory::Simulation,
FunctionScope::Region,
PropertyValue::Float(1.0),
"Resource respawn rate",
),
(
"corruption.spreadRate",
FunctionCategory::Dreamwell,
FunctionScope::Region,
PropertyValue::Float(0.0),
"Corruption spread rate",
),
(
"dreammatter.condenseRate",
FunctionCategory::Dreamwell,
FunctionScope::Realm,
PropertyValue::Float(0.0),
"Dreammatter condensation rate",
),
(
"timeline.branchPolicy",
FunctionCategory::Dreamwell,
FunctionScope::Universe,
PropertyValue::String("signed_canonical".into()),
"Timeline branch policy",
),
(
"attestation.visibility",
FunctionCategory::Dreamwell,
FunctionScope::Universe,
PropertyValue::Bool(true),
"Attestation visibility",
),
(
"topology.transitionMode",
FunctionCategory::Topology,
FunctionScope::Location,
PropertyValue::String("seamless_stream".into()),
"Topology transition mode",
),
(
"topology.scalePresentation",
FunctionCategory::Topology,
FunctionScope::Area,
PropertyValue::String("continuous".into()),
"Scale presentation mode",
),
(
"topology.sceneBinding",
FunctionCategory::Topology,
FunctionScope::Location,
PropertyValue::String("default".into()),
"Scene binding mode",
),
];
for (key, cat, scope, val, desc) in builtins {
let _ = reg.register(UniversalFunction {
key: key.into(),
category: cat,
scope,
default_value: val,
description: desc.into(),
});
}
reg
}
pub fn register(&mut self, func: UniversalFunction) -> Result<(), WaymarkError> {
if self.key_index.contains_key(&func.key) {
return Err(WaymarkError::Authoring(AuthoringError::UnknownFunction(format!(
"duplicate function key: {}",
func.key
))));
}
let idx = self.functions.len();
self.key_index.insert(func.key.clone(), idx);
self.functions.push(func);
Ok(())
}
pub fn find(&self, key: &str) -> Option<&UniversalFunction> {
self.key_index.get(key).map(|&i| &self.functions[i])
}
pub fn by_category(&self, cat: FunctionCategory) -> Vec<&UniversalFunction> {
self.functions.iter().filter(|f| f.category == cat).collect()
}
pub fn by_scope(&self, scope: FunctionScope) -> Vec<&UniversalFunction> {
self.functions.iter().filter(|f| f.scope == scope).collect()
}
pub fn validate_key(&self, key: &str) -> Result<(), WaymarkError> {
if self.key_index.contains_key(key) {
Ok(())
} else {
Err(WaymarkError::Authoring(AuthoringError::UnknownFunction(key.into())))
}
}
pub fn len(&self) -> usize {
self.functions.len()
}
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
}
}
impl FunctionScope {
pub const ALL: &[FunctionScope] = &[
Self::Universe,
Self::Galaxy,
Self::Sector,
Self::World,
Self::Realm,
Self::Region,
Self::Area,
Self::Location,
Self::Room,
Self::Point,
];
}
impl FunctionCategory {
pub const ALL: &[FunctionCategory] = &[
Self::Physics,
Self::Traversal,
Self::Combat,
Self::Simulation,
Self::Dreamwell,
Self::Topology,
];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builtins_count() {
let reg = FunctionRegistry::with_builtins();
assert_eq!(reg.len(), 23);
}
#[test]
fn find_gravity() {
let reg = FunctionRegistry::with_builtins();
let f = reg.find("gravity.local").unwrap();
assert_eq!(f.category, FunctionCategory::Physics);
}
#[test]
fn by_category_physics() {
let reg = FunctionRegistry::with_builtins();
let phys = reg.by_category(FunctionCategory::Physics);
assert_eq!(phys.len(), 6);
}
#[test]
fn validate_key_valid() {
let reg = FunctionRegistry::with_builtins();
assert!(reg.validate_key("gravity.local").is_ok());
}
#[test]
fn validate_key_invalid() {
let reg = FunctionRegistry::with_builtins();
assert!(reg.validate_key("nonexistent.key").is_err());
}
#[test]
fn scope_all_count() {
assert_eq!(FunctionScope::ALL.len(), 10);
}
#[test]
fn category_all_count() {
assert_eq!(FunctionCategory::ALL.len(), 6);
}
}