feat(client): ConsideringIndicator — rotates think-lines on a Timer
This commit is contained in:
44
client/scripts/ui/considering_indicator.gd
Normal file
44
client/scripts/ui/considering_indicator.gd
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
class_name ConsideringIndicator
|
||||||
|
extends Node
|
||||||
|
## Thin node shim (charter §13). While a call is in flight it rotates an authored
|
||||||
|
## ConsideringPhrases line onto a Label every ROTATE_SECONDS, reframing the wait
|
||||||
|
## as the GM thinking. The phrase logic is the pure, tested ConsideringPhrases;
|
||||||
|
## the Timer/tree wiring here is the untested shim (verified by running the
|
||||||
|
## harness). One instance persists on the harness so the rotation index is not
|
||||||
|
## reset per call (spec decision 4).
|
||||||
|
|
||||||
|
const ROTATE_SECONDS := 2.0
|
||||||
|
|
||||||
|
var _phrases: ConsideringPhrases
|
||||||
|
var _timer: Timer
|
||||||
|
var _label: Label
|
||||||
|
|
||||||
|
|
||||||
|
func _init(phrases: ConsideringPhrases = null) -> void:
|
||||||
|
_phrases = phrases if phrases != null else ConsideringPhrases.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_timer = Timer.new()
|
||||||
|
_timer.wait_time = ROTATE_SECONDS
|
||||||
|
_timer.one_shot = false
|
||||||
|
_timer.timeout.connect(_advance)
|
||||||
|
add_child(_timer)
|
||||||
|
|
||||||
|
|
||||||
|
func start(label: Label) -> void:
|
||||||
|
_label = label
|
||||||
|
_advance() # show a phrase immediately, don't wait ROTATE_SECONDS
|
||||||
|
_timer.start()
|
||||||
|
|
||||||
|
|
||||||
|
func stop() -> void:
|
||||||
|
_timer.stop()
|
||||||
|
if _label != null:
|
||||||
|
_label.text = ""
|
||||||
|
_label = null
|
||||||
|
|
||||||
|
|
||||||
|
func _advance() -> void:
|
||||||
|
if _label != null:
|
||||||
|
_label.text = _phrases.next()
|
||||||
19
client/tests/unit/test_considering_indicator.gd
Normal file
19
client/tests/unit/test_considering_indicator.gd
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
extends "res://addons/gut/test.gd"
|
||||||
|
|
||||||
|
const ConsideringIndicator = preload("res://scripts/ui/considering_indicator.gd")
|
||||||
|
const ConsideringPhrases = preload("res://scripts/ui/considering_phrases.gd")
|
||||||
|
|
||||||
|
|
||||||
|
func test_start_shows_first_phrase_and_stop_clears():
|
||||||
|
# Point phrases at a missing file so next() is deterministically LAST_RESORT.
|
||||||
|
var phrases = ConsideringPhrases.new("res://content/does_not_exist.json")
|
||||||
|
var ind = ConsideringIndicator.new(phrases)
|
||||||
|
add_child_autofree(ind) # _ready() creates the Timer child
|
||||||
|
var label := Label.new()
|
||||||
|
add_child_autofree(label)
|
||||||
|
|
||||||
|
ind.start(label)
|
||||||
|
assert_eq(label.text, ConsideringPhrases.LAST_RESORT) # first phrase shown at once
|
||||||
|
|
||||||
|
ind.stop()
|
||||||
|
assert_eq(label.text, "") # cleared on stop
|
||||||
Reference in New Issue
Block a user