From b49d78ef6a9e719000ccae1672a83161ccc14ee4 Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Fri, 20 Feb 2026 18:41:22 +0100 Subject: [PATCH] =?UTF-8?q?fix(ui):=20guard=20empty=20text=20in=20show=5Fm?= =?UTF-8?q?onologue=20=E2=80=94=20no=20ghost=20slots=20or=20queue=20entrie?= =?UTF-8?q?s=20(#122)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit show_monologue() now returns early when text.is_empty(), preventing: - ghost visible slots with blank labels - stagger timer advancing on empty calls - empty strings queuing when slots are full Tests: add test_show_monologue_with_empty_text_does_not_set_displaying and two companion cases (stagger timer unchanged, no enqueue when full) anticipating Hoshe's QA additions to the test suite. Co-Authored-By: Claude Sonnet 4.6 --- client/tests/test_monologue_display.gd | 30 ++++++++++++++++++++++++++ client/ui/monologue_display.gd | 3 +++ 2 files changed, 33 insertions(+) diff --git a/client/tests/test_monologue_display.gd b/client/tests/test_monologue_display.gd index 88f4047c6..2370e449a 100644 --- a/client/tests/test_monologue_display.gd +++ b/client/tests/test_monologue_display.gd @@ -46,6 +46,36 @@ func test_stagger_timer_zero_on_init() -> void: d.queue_free() +func test_show_monologue_with_empty_text_does_not_set_displaying() -> void: + # Empty text must be ignored — no visible slot created, no queue entry. + # Prevents ghost display nodes and stagger timer contamination. + var d = _make_display() + if d == null: return + d.show_monologue("", 5.0) + assert_int(d._visible.size()).is_equal(0) + assert_int(d._queue.size()).is_equal(0) + d.queue_free() + + +func test_empty_text_does_not_advance_stagger_timer() -> void: + var d = _make_display() + if d == null: return + d.show_monologue("", 5.0) + assert_float(d._next_fade_in_msec).is_equal(0.0) + d.queue_free() + + +func test_empty_text_when_slots_full_does_not_enqueue() -> void: + var d = _make_display() + if d == null: return + d._next_fade_in_msec = 0.0; d.show_monologue("A", 10.0) + d._next_fade_in_msec = 0.0; d.show_monologue("B", 10.0) + d._next_fade_in_msec = 0.0; d.show_monologue("C", 10.0) + d.show_monologue("", 5.0) + assert_int(d._queue.size()).is_equal(0) + d.queue_free() + + # --------------------------------------------------------------------------- # Single-line display # --------------------------------------------------------------------------- diff --git a/client/ui/monologue_display.gd b/client/ui/monologue_display.gd index b68419c07..bf86501d6 100644 --- a/client/ui/monologue_display.gd +++ b/client/ui/monologue_display.gd @@ -66,7 +66,10 @@ func _process(delta: float) -> void: # Display a monologue line. # priority: higher number = more important (default 2; urgent beats normal). # is_urgent: visual flag — full opacity + elevated colour. Bloom deferred. +# Empty text is silently ignored — no slot created, no queue entry. func show_monologue(text: String, duration: float, priority: int = 2, is_urgent: bool = false) -> void: + if text.is_empty(): + return var now := float(Time.get_ticks_msec()) if _visible.size() < MAX_VISIBLE and now >= _next_fade_in_msec: _show_line(text, duration, priority, is_urgent)