Files
wds/src/engine/story_ctrl.rs
T
2026-06-13 02:57:22 +02:00

34 lines
1.6 KiB
Rust

//! Story-Lifecycle: Ink-Skript in den Story-Slot laden und schrittweise
//! treiben. Frontend-agnostisch — anders als in irl3d, wo `StoryState`
//! direkt in UI-Panels gegossen wurde, konsumiert hier der Aufrufer
//! (CLI-REPL jetzt, Panel-UI später) den Zustand selbst.
//!
//! Tags eines Schritts werden als `Vec<String>` zurückgegeben — der
//! Aufrufer entscheidet, ob/wie er sie via `signals::dispatch` weiterreicht
//! (vermeidet den Modul-Zyklus signals → story_ctrl → signals).
use crate::engine::assets;
use crate::engine::game::ActionCtx;
use crate::engine::ink::{Story, StoryState};
/// Lädt ein Ink-Skript (Pfad relativ zu `assets/interactions/`) in den
/// Story-Slot. Der erste Schritt passiert noch nicht — das Frontend ruft
/// dafür `advance(None, …)`.
pub fn start(script: &str, ctx: &mut ActionCtx) {
let path = assets::path(&format!("assets/interactions/{script}"));
*ctx.story = Some(Story::load(&path, ctx.kv));
}
/// Treibt die laufende Story einen Schritt weiter. `sel` ist die
/// Choice-Auswahl bzw. `None` beim ersten Schritt oder Text-Weiterklicken.
/// Bei `End` wird der Story-Slot geleert. Rückgabe: neuer Zustand (oder
/// `None` wenn gar keine Story läuft) plus Tags zum Dispatchen.
pub fn advance(sel: Option<usize>, ctx: &mut ActionCtx) -> (Option<StoryState>, Vec<String>) {
let Some(st) = ctx.story.as_mut() else { return (None, Vec::new()); };
if let Some(i) = sel { st.choose(i); }
let state = st.cont(ctx.kv);
let tags = st.take_tags();
if matches!(state, StoryState::End) { *ctx.story = None; }
(Some(state), tags)
}