//! Owner des dispatch-relevanten Spielzustands und der Borrow-View, durch //! die Signal-Actions und Ink-Story-Steps darauf zugreifen. //! //! `Game` hält die Slots, die Actions lesen oder mutieren dürfen. //! `ActionCtx` ist der schmale `&mut`-View; er wird über `Game::action_ctx()` //! konstruiert und durch die Dispatch-Pipeline gereicht. //! //! Verben, deren Ziel-Subsystem (Szene, Audio, Renderer) noch nicht //! existiert oder den Kern nichts angeht, produzieren [`Effect`]-Werte //! statt direkt zu wirken. Frontends konsumieren die Queue: die CLI druckt //! sie, die Engine führt sie später aus. So bleibt der Kern headless //! testbar und kennt keines der Subsysteme. use crate::ink; use crate::kv::Store; use crate::signals::Signals; /// Von Actions angeforderte Wirkung außerhalb des Kerns. Die Queue in /// `Game::effects` gehört nach jedem Dispatch geleert (konsumiert). #[derive(Debug, Clone, PartialEq, Eq)] pub enum Effect { /// Instance unsichtbar + kollisionslos + nicht mehr interagierbar machen. HideObject(String), /// WAV unter `assets/audio/` als SFX abspielen. PlaySound(String), /// Palette/Farbschema unter `assets/palettes/` laden. LoadPalette(String), } pub struct Game { pub kv: Store, pub signals: Signals, pub story: Option, pub effects: Vec, } pub struct ActionCtx<'a> { pub kv: &'a mut Store, pub signals: &'a Signals, pub story: &'a mut Option, pub effects: &'a mut Vec, /// Auslöser-Identifier: bei Objekt-Interaktion der volle Instance-Name /// (mit Blender-Suffix, z.B. `Mushroom.005`). `$self` in Action-Args /// wird damit substituiert. `None` bei Signalen ohne Quell-Instance /// (z.B. `init`, Story-Tags). pub instance_name: Option, } impl Game { pub fn new(signals: Signals) -> Self { Self { kv: Store::new(), signals, story: None, effects: Vec::new() } } pub fn action_ctx(&mut self, instance_name: Option) -> ActionCtx<'_> { ActionCtx { kv: &mut self.kv, signals: &self.signals, story: &mut self.story, effects: &mut self.effects, instance_name, } } }