fix(ui): address PR #115 review — 8 items

- Fix stale test path UILayer/HUD → InsertOverlay/HUD
- Fix D-record citation D-051 → D-049 in main.tscn
- Add null guards to hud.gd update methods (pre-_ready safety)
- Minimap: dirty-flag queue_redraw instead of per-frame
- Remove redundant _panel.size.x, debug print
- Fix DebugOverlay/GauntletHUD positioning comments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 15:45:47 +02:00
co-authored by Claude Opus 4.6
parent 8c239d522c
commit 879ef6092f
4 changed files with 32 additions and 13 deletions
+3 -3
View File
@@ -144,12 +144,12 @@ layer = 10
; #264: Journal panel — knowledge graph review, toggle J key, read-only (D-041)
[node name="JournalPanel" parent="InsertOverlay" instance=ExtResource("25_journal")]
; HUD — implant-styled status panel (D-169, D-170). On InsertOverlay for bloom per D-051.
; HUD — implant-styled status panel (D-169, D-170). On InsertOverlay per D-049 (z-layer 6).
[node name="HUD" parent="InsertOverlay" instance=ExtResource("6_hud")]
; --- UI layer (CanvasLayer 20) ---
; Monologue, cursor, stance — always visible, not affected by fog or camera.
; HUD moved to InsertOverlay (layer 10) for bloom treatment per D-051.
; HUD moved to InsertOverlay (layer 10) per D-049 (insert z-layer 6).
[node name="UILayer" type="CanvasLayer" parent="."]
layer = 20
@@ -168,7 +168,7 @@ layer = 20
[node name="InventoryGrid" parent="UILayer" instance=ExtResource("12_inv")]
; #511: F3 debug overlay — real-time game state, toggled by F3
; Position: below merged HUD status panel (starts at y:16, ~80px), 10px gap
; Position: y:150 — below merged HUD status panel (y:16, ~80px tall), with gap
[node name="DebugOverlay" type="Control" parent="UILayer"]
anchors_preset = 0
offset_left = 16
+6 -6
View File
@@ -6,14 +6,14 @@
## D-051 (diegetic insert display)
##
## Implementation: client/ui/time_display.gd — draw-based Control (removed from main scene #786;
## time display merged into HUD ImplantPanel at UILayer/HUD, see test_hud_* tests below).
## time display merged into HUD ImplantPanel at InsertOverlay/HUD, see test_hud_* tests below).
## Format function: Constants.format_game_time(time_of_day: int) -> String (extracted for
## testability from time_display.gd).
##
## Private state access: Tests read _time_str, _phase_str, _day_str, _has_data directly
## because time_display.gd is draw-based (no Label nodes to inspect). This is an accepted
## test pattern for draw-based UI — the private vars ARE the rendered output contract.
## Scene integration tests now check UILayer/HUD.get_time_text() instead.
## Scene integration tests now check InsertOverlay/HUD.get_time_text() instead.
class_name TestTimeDisplaySprint17
extends GdUnitTestSuite
@@ -273,16 +273,16 @@ func test_sim_bridge_day_phase_is_valid() -> void:
# -------------------------------------------------------------------------
# Scene: HUD panel at UILayer/HUD (time row merged in, TimeDisplay removed #786)
# Scene: HUD panel at InsertOverlay/HUD (time row merged in, TimeDisplay removed #786)
# -------------------------------------------------------------------------
func test_hud_panel_exists_in_ui_layer() -> void:
func test_hud_panel_exists_in_insert_overlay() -> void:
var scene := MAIN_SCENE
var instance = scene.instantiate()
auto_free(instance)
add_child(instance)
assert_that(instance.get_node_or_null("UILayer/HUD")).is_not_null()
assert_that(instance.get_node_or_null("InsertOverlay/HUD")).is_not_null()
func test_hud_time_row_updates_after_process() -> void:
var scene := MAIN_SCENE
@@ -296,7 +296,7 @@ func test_hud_time_row_updates_after_process() -> void:
})
instance._process(0.016)
var hud = instance.get_node_or_null("UILayer/HUD")
var hud = instance.get_node_or_null("InsertOverlay/HUD")
assert_that(hud).is_not_null()
assert_that(hud.get_time_text()).is_equal("12:00 · Afternoon · D1")
+6 -3
View File
@@ -21,7 +21,6 @@ func _ready() -> void:
_panel.name = "StatusPanel"
_panel.theme_resource = theme_res
_panel.custom_minimum_size.x = 200.0
_panel.size.x = 200.0
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
_panel.position = Vector2(16, 16) # top-left; replaces standalone TimeDisplay
add_child(_panel)
@@ -34,10 +33,10 @@ func _ready() -> void:
_panel.add_component(_health_row)
_panel.add_component(_perception_row)
print("HUD: Initialized")
func update_from_hud_data(data: Dictionary) -> void:
if not _perception_row:
return
if data.has("perception_mode"):
var prefix := UIStrings.get_text("hud.perception_mode_prefix")
if prefix.is_empty():
@@ -47,10 +46,14 @@ func update_from_hud_data(data: Dictionary) -> void:
func update_health(health: int) -> void:
if not _health_row:
return
_health_row.text = UIStrings.get_text("hud.health") + ": " + str(health)
func update_from_state() -> void:
if not _time_row:
return
var gt: Dictionary = GameState.game_time
if gt.is_empty():
return
+17 -1
View File
@@ -38,6 +38,11 @@ var _insert_active: bool = true
# D-169: StyleBoxFlat matching ImplantPanel aesthetic — drawn as container frame behind the circle.
var _container_style: StyleBoxFlat = null
# Dirty flag — only queue_redraw() when player position or POI list changes.
var _dirty: bool = true
var _last_player_pos: Vector2 = Vector2(INF, INF)
var _last_poi_count: int = -1
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
@@ -57,7 +62,16 @@ func _init_container_style() -> void:
func _process(_delta: float) -> void:
if _insert_active:
if not _insert_active:
return
var pos := GameState.player_position
var poi_count := GameState.discovered_pois.size()
if pos != _last_player_pos or poi_count != _last_poi_count:
_last_player_pos = pos
_last_poi_count = poi_count
_dirty = true
if _dirty:
_dirty = false
queue_redraw()
@@ -66,6 +80,8 @@ func _process(_delta: float) -> void:
func set_insert_active(active: bool) -> void:
_insert_active = active
visible = active
if active:
_dirty = true
func _draw() -> void: