refactor(ui): convert HUD status + interaction prompt to implant (#786, #788)

HUD: merge TimeDisplay into ImplantPanel (time/health/perception in one
panel), remove dead tscn nodes, move HUD to InsertOverlay for D-051 bloom.
Interaction prompt: convert from PanelContainer+StyleBoxFlat to Control
with ImplantPanel+ImplantDataRow, fix panel height for 42px minimum.
Tests updated for new scene structure and public API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 12:51:58 +02:00
co-authored by Claude Opus 4.6
parent 3c7231e275
commit 283518d332
8 changed files with 76 additions and 97 deletions
+20 -3
View File
@@ -1,9 +1,10 @@
extends Control
## HUD — implant-styled status panel (D-169, D-170).
## Displays health and perception mode. Time is handled by TimeDisplay.
## Merged panel: Time, Health, Perception mode. Time row replaces standalone TimeDisplay (#786).
var _panel: ImplantPanel
var _time_row: ImplantDataRow
var _health_row: ImplantDataRow
var _perception_row: ImplantDataRow
@@ -22,13 +23,14 @@ func _ready() -> void:
_panel.custom_minimum_size.x = 200.0
_panel.size.x = 200.0
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
_panel.position = Vector2(16, 80) # below TimeDisplay (occupies 16-70)
_panel.position = Vector2(16, 16) # top-left; replaces standalone TimeDisplay
add_child(_panel)
_panel.move_child(_panel.get_node("Content"), 0)
_time_row = ImplantDataRow.new("--:--")
_health_row = ImplantDataRow.new("Health: 100")
_perception_row = ImplantDataRow.new("Mode: Baseline")
_panel.add_component(_time_row)
_panel.add_component(_health_row)
_panel.add_component(_perception_row)
@@ -46,3 +48,18 @@ func update_from_hud_data(data: Dictionary) -> void:
func update_health(health: int) -> void:
_health_row.text = UIStrings.get_text("hud.health") + ": " + str(health)
func update_from_state() -> void:
var gt: Dictionary = GameState.game_time
if gt.is_empty():
return
var tod: int = int(gt.get("time_of_day", 0))
var day: int = int(gt.get("day", 0))
var phase: String = str(gt.get("day_phase", ""))
_time_row.text = "%s · %s · D%d" % [Constants.format_game_time(tod), phase, day + 1]
## Returns the time row text. Used by tests after standalone TimeDisplay was removed.
func get_time_text() -> String:
return _time_row.text if _time_row else "--:--"
+4 -27
View File
@@ -1,4 +1,4 @@
[gd_scene load_steps=3 format=3 uid="uid://cq1y5w3hmxr8b"]
[gd_scene load_steps=2 format=3 uid="uid://cq1y5w3hmxr8b"]
[ext_resource type="Script" path="res://ui/hud.gd" id="1_hud"]
[ext_resource type="PackedScene" path="res://ui/star_map.tscn" id="2_starmap"]
@@ -13,32 +13,9 @@ grow_vertical = 2
mouse_filter = 2
script = ExtResource("1_hud")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 100.0
grow_horizontal = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 16
theme_override_constants/margin_right = 16
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="HealthLabel" type="Label" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Health: 100"
[node name="PerceptionLabel" type="Label" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Mode: Baseline"
[node name="TimeLabel" type="Label" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Time: 08:00"
; #674: Star map insert — D-170 z-index layer managed, not visibility toggled
; #674: Star map — full-screen insert overlay, hidden until player activates (M key / insert UI).
; Toggled via star_map.toggle_visible() from main.gd.
; TODO: migrate to HudGroups.open_app("implant/map/starchart") per D-170.
[node name="StarMap" parent="." instance=ExtResource("2_starmap")]
visible = false
+19 -15
View File
@@ -1,4 +1,4 @@
extends PanelContainer
extends Control
# Interaction prompt — displays available interaction verb for nearby entity.
# Server-driven: shows when GameState.nearby_interactions is non-empty,
@@ -17,24 +17,28 @@ var _current_target_id: int = -1
# OQ-07 (#522): when false, prompt is suppressed (z-layer 6 insert overlay only)
var _insert_active: bool = true
@onready var prompt_label: Label = $MarginContainer/PromptLabel
var _panel: ImplantPanel
var _row: ImplantDataRow
func _ready() -> void:
modulate.a = 0.0
visible = false
_is_showing = false
# D-169: Apply implant theme
var t := load("res://ui/implant/default_implant.tres") as ImplantTheme
if t:
var style := StyleBoxFlat.new()
style.bg_color = t.panel_bg
style.border_color = t.separator
style.set_border_width_all(int(t.border_width))
style.set_content_margin_all(t.padding)
add_theme_stylebox_override("panel", style)
prompt_label.add_theme_color_override("font_color", t.text_primary)
prompt_label.add_theme_font_size_override("font_size", t.font_body)
mouse_filter = Control.MOUSE_FILTER_IGNORE
var theme_res := load("res://ui/implant/default_implant.tres") as ImplantTheme
_panel = ImplantPanel.new()
_panel.name = "PromptPanel"
_panel.theme_resource = theme_res
_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE
_panel.set_anchors_preset(Control.PRESET_FULL_RECT)
add_child(_panel)
_row = ImplantDataRow.new("", theme_res.text_primary if theme_res else Color.WHITE)
_row.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_panel.add_component(_row)
func _process(_delta: float) -> void:
@@ -63,8 +67,8 @@ func _show_prompt(interaction: Dictionary) -> void:
var verb_label: String = verbs[0].get("label", "")
var display_text: String = "E - %s" % verb_label
if _current_target_id != target_id or prompt_label.text != display_text:
prompt_label.text = display_text
if _current_target_id != target_id or _row.text != display_text:
_row.set_content(display_text)
_current_target_id = target_id
if not _is_showing:
+4 -15
View File
@@ -2,28 +2,17 @@
[ext_resource type="Script" path="res://ui/interaction_prompt.gd" id="1_prompt"]
[node name="InteractionPrompt" type="PanelContainer"]
; Interaction prompt — implant-styled (D-169), positioned above DialogueBox, 16px gap
[node name="InteractionPrompt" type="Control"]
anchors_preset = 7
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 200.0
offset_top = -240.0
offset_top = -264.0
offset_right = -200.0
offset_bottom = -210.0
offset_bottom = -222.0
grow_horizontal = 2
grow_vertical = 0
mouse_filter = 2
script = ExtResource("1_prompt")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 8
[node name="PromptLabel" type="Label" parent="MarginContainer"]
layout_mode = 2
horizontal_alignment = 1
text = ""