class_name NarrationBook extends Control ## The permanent DM narration book (mock 2a, right panel). §2: text — it consumes ## /dm/narrate prose and never treats it as truth; only harvested [FACT] tags are ## written back to the canon log (§11). Prose-only this milestone: the numbered ## response choices and the "describe your own action" line are faithful but inert ## (no action system yet); one in-world refire affordance re-runs narration. ## ## Layout is authored in NarrationBook.tscn (editor-previewable). This script only ## binds the header, drives the narrate loop, and swaps prose text. var last_degraded: bool = false var _service: DmService var _log: CanonLog var _state: ShellState var _indicator: ConsideringIndicator @onready var _header: Label = $Margin/Col/Header @onready var _prose: RichTextLabel = $Margin/Col/Prose @onready var _degraded_tag: Label = $Margin/Col/DegradedTag @onready var _refire: Button = $Margin/Col/Refire @onready var _status: Label = $Margin/Col/Status func _ready() -> void: # The considering indicator is a non-visual Node (a Timer + label driver), so # it stays code-created rather than authored in the scene. _indicator = ConsideringIndicator.new() add_child(_indicator) _refire.pressed.connect(_on_refire_pressed) func setup(service: DmService, log: CanonLog, state: ShellState) -> void: _service = service _log = log _state = state _header.text = "%s · %s" % [state.round_label, state.location_label] func narrate() -> void: if _service == null or _log == null: return _refire.disabled = true _indicator.start(_status) var result: NarrateResult = await _service.narrate(_log) _indicator.stop() show_prose(result) for f in result.facts: _log.add_fact(f) # §11: code applies the state write, explicitly _refire.disabled = false func show_prose(result: NarrateResult) -> void: _prose.text = result.display_text last_degraded = result.degraded _degraded_tag.visible = result.degraded func _on_refire_pressed() -> void: narrate()