saumwacht-godot/scripts/luneraria/ui/components/event_feed.gd
Martin Stickelbroeck 1b540f2a55 G3 (Zwischenstand): neues Ingame-HUD; Projekt wechselt nach Unity
Nicht abgenommen: Nach dem ersten Spieltest wurde entschieden, Champions of
Luneraria nach Unity 6 zu portieren. Dieser Stand ist die Referenz für das
HUD im Unity-Projekt (../luneraria-unity).

- HUD neu aus Komponenten mit eigenem Theme, Porträts aus den echten Modellen
  (einmal gerendert, zwischengespeichert), Lane-Übersicht mit deterministischer
  Bedrohung aus öffentlichem Zustand, Bossgrotte, nächste Welle, Ereignisse,
  Minimap mit zusammengefassten Armeen, Held/Items/Talente, Einheitendetails,
  Angriffschampions, Bereit-Sperre, UI-Größe, Team-Formen statt nur Farben
- Privatsphäre über LuneHudModel: fremdes Gold und fremde Entwürfe erscheinen nie
- Keine Regeländerung: Regeln 91/91, M2 73/73, Netz 43/43; verify_g3 715/715;
  verify_m1 38/38 und verify_m2 14/14 mit angepassten UI-Treibern
- Spieltest bis Welle 8 über die echte Oberfläche, Befunde in G3-HUD.md
- HELDEN-KONZEPT.md: vom Projektinhaber vorgegebene Helden (nicht umgesetzt)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 19:59:55 +02:00

72 lines
2.9 KiB
GDScript

class_name LuneEventFeed
extends VBoxContainer
## Short event list: tower falls, champions, boss, critical Ancients, round results. At most
## five entries, newest on top; each fades after a while. A click on an entry with a place
## moves the camera there. It never lists every kill.
signal focus_requested(target: Dictionary)
const LIMIT: int = 5
const LIFETIME: float = 14.0
const LEVEL_COLORS: Dictionary = {"info": LuneUiTheme.INFO, "good": LuneUiTheme.GOOD, "warn": LuneUiTheme.WARN, "danger": LuneUiTheme.DANGER}
var entries: Array[Dictionary] = []
var _clock: float = 0.0
func _init() -> void:
add_theme_constant_override("separation", 3)
mouse_filter = Control.MOUSE_FILTER_IGNORE
## level: info, good, warn, danger. target: {} or {"lane": …} / {"point": Vector2} / {"camera": name}.
func push(text_value: String, level: String = "info", glyph: String = "", target: Dictionary = {}) -> void:
for entry: Dictionary in entries:
if entry.text == text_value and _clock - float(entry.born) < 4.0:
return
var button := Button.new()
button.theme_type_variation = "HudRow"
button.focus_mode = Control.FOCUS_NONE
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND if not target.is_empty() else Control.CURSOR_ARROW
button.custom_minimum_size = Vector2(0, 30)
var row := HBoxContainer.new()
row.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
row.offset_left = 8
row.offset_right = -8
row.add_theme_constant_override("separation", 7)
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
button.add_child(row)
var tint: Color = LEVEL_COLORS.get(level, LuneUiTheme.INFO)
LuneGlyph.make(glyph if not glyph.is_empty() else "star", tint, 15.0, row)
var label := LuneUiTheme.label(text_value, "HudText", row)
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
label.clip_text = true
label.add_theme_color_override("font_color", tint.lerp(LuneUiTheme.TEXT, .45))
button.pressed.connect(func() -> void:
if not target.is_empty():
focus_requested.emit(target))
LuneTooltip.attach(button, func() -> Dictionary: return {"title": text_value, "note": "Klick: Kamera dorthin" if not target.is_empty() else ""})
add_child(button)
move_child(button, 0)
entries.push_front({"text": text_value, "level": level, "born": _clock, "node": button})
while entries.size() > LIMIT:
var old: Dictionary = entries.pop_back()
(old.node as Node).queue_free()
visible = true
func clear_all() -> void:
for entry: Dictionary in entries:
(entry.node as Node).queue_free()
entries.clear()
func _process(delta: float) -> void:
_clock += delta
for index: int in range(entries.size() - 1, -1, -1):
var entry: Dictionary = entries[index]
var age: float = _clock - float(entry.born)
var node: Control = entry.node
if age > LIFETIME:
node.queue_free()
entries.remove_at(index)
elif age > LIFETIME - 2.0:
node.modulate.a = clampf((LIFETIME - age) / 2.0, 0.0, 1.0)
visible = not entries.is_empty()