// Cause chain tracking system // Production component for information provenance (D-030) // Tracks monologue triggers, journal entries, debug causality use bevy_ecs::prelude::*; use serde::{Deserialize, Serialize}; #[derive(Component, Debug, Clone, Serialize, Deserialize)] pub struct CauseChain { pub causes: Vec, } impl CauseChain { pub fn new() -> Self { Self { causes: Vec::new() } } pub fn with_cause(mut self, cause: Cause) -> Self { self.causes.push(cause); self } pub fn push(&mut self, cause: Cause) { self.causes.push(cause); } pub fn latest(&self) -> Option<&Cause> { self.causes.last() } } impl Default for CauseChain { fn default() -> Self { Self::new() } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Cause { pub tick: u64, pub kind: CauseKind, pub description: String, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub enum CauseKind { Observed, Heard, Informed, Inferred, Background, } #[cfg(test)] mod tests { use super::*; #[test] fn chain_preserves_order() { let chain = CauseChain::new() .with_cause(Cause { tick: 1, kind: CauseKind::Observed, description: "Saw NPC".into(), }) .with_cause(Cause { tick: 5, kind: CauseKind::Inferred, description: "NPC still inside".into(), }); assert_eq!(chain.causes.len(), 2); assert_eq!(chain.latest().unwrap().tick, 5); assert_eq!(chain.latest().unwrap().kind, CauseKind::Inferred); } }