fix(ui): address PR #49 review — 6 warnings + 2 suggestions (#122)

Warnings fixed:
- BBCode injection (line 153): escape [ → [lb] in server text before interpolation
- sort_custom on silent-drop (line 125): sort now only runs on actual insertion/replacement
- clip_contents: add clip_contents=true to MonologueDisplay Control (overflow guard)
- confrontation tick guard (main.gd): _last_confrontation_tick deduplicates same-tick signals
- GameState decoupling: show_monologue() reads lattice_profile once and passes it through
  _show_line() → _build_line_node(); renderer no longer reaches into autoload (D-020)
- Equal-priority eviction: >= tiebreak (was >); FIFO for equal-priority queue overflow

Suggestions fixed:
- Minimum duration clamp: maxf(duration, FADE_IN_SEC + 0.1) — line survives own fade-in
- _label_text bounds check: guard against empty _visible before indexing [0]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 19:06:26 +01:00
co-authored by Claude Sonnet 4.6
parent c71ba8ebb3
commit 677ca9b59d
4 changed files with 69 additions and 30 deletions
+27 -2
View File
@@ -23,6 +23,8 @@ func _make_display() -> Node:
func _label_text(d: Node) -> String:
if d._visible.is_empty():
return ""
var slot_node: Node = d._visible[0].node
return (slot_node.get_child(0) as RichTextLabel).text
@@ -456,10 +458,33 @@ func test_gamestate_monologue_replaced_by_next_snapshot() -> void:
# ---------------------------------------------------------------------------
# D-049 Z-layer / canvas scope compliance
# Monologue display must be parented to CANVAS_UI (CanvasLayer 20), not the
# world layer. This constant check ensures it hasn't silently drifted.
# MonologueDisplay must be parented to UILayer (CanvasLayer, layer=20).
# Two tests: (1) constant sanity, (2) scene tree structural verification.
# ---------------------------------------------------------------------------
func test_canvas_ui_constant_is_20() -> void:
## D-049: CANVAS_UI = 20 is the agreed HUD layer for monologue display.
## Sanity check — constant must not drift from the spec.
assert_that(Constants.CANVAS_UI).is_equal(20)
func test_monologue_display_parented_to_canvas_layer_20_in_main_scene() -> void:
## D-049: Structural verification — MonologueDisplay must be a direct child of
## UILayer (CanvasLayer, layer=20) in the live scene tree, not the world layer.
## Catches regressions where the node gets accidentally moved to InsertOverlay
## (layer=10) or ModalLayer (layer=30), or dropped into the world z-stack.
##
## Scene path verified: Game/UILayer/MonologueDisplay (main.tscn line 141).
if not ResourceLoader.exists("res://scenes/main.tscn"):
push_warning("TestMonologueDisplay: main.tscn not found — D-049 scene tree test skipped")
return
var scene: Node = load("res://scenes/main.tscn").instantiate()
auto_free(scene)
add_child(scene)
var mono: Node = scene.get_node_or_null("UILayer/MonologueDisplay")
assert_that(mono != null).is_true()
var parent: Node = mono.get_parent()
assert_that(parent is CanvasLayer).is_true()
assert_that((parent as CanvasLayer).layer).is_equal(Constants.CANVAS_UI)