saumwacht-godot/scripts/luneraria/ui/components/champion_panel.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

119 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name LuneChampionPanel
extends HBoxContainer
## Attack champions: target lane, the three champion cards (copper, hatched, "1 WELLE") and
## the own bookings ("Einsatz in Welle 15 – MID"). Champions are bought in planning n, fight
## once in wave n+1 and then vanish; they never join the permanent army.
signal command_requested(command: Dictionary)
var target_lane: String = "mid"
var cards: Dictionary = {}
var _lane_buttons: Dictionary = {}
var _bookings: VBoxContainer
var _count: Label
var _signature: String = ""
func _init() -> void:
add_theme_constant_override("separation", 10)
var lanes := VBoxContainer.new()
lanes.add_theme_constant_override("separation", 4)
add_child(lanes)
LuneUiTheme.label("ZIEL", "HudCaps", lanes)
for lane: String in LuneMap.LANES:
var button := LuneUiTheme.button(LuneMap.LANE_NAMES[lane].to_upper(), "HudToggle", lanes)
button.toggle_mode = true
button.custom_minimum_size = Vector2(86, 0)
button.set_pressed_no_signal(lane == target_lane)
button.pressed.connect(func() -> void:
target_lane = lane
_signature = ""
for other: String in _lane_buttons:
(_lane_buttons[other] as Button).set_pressed_no_signal(other == lane))
_lane_buttons[lane] = button
LuneTooltip.attach(button, func() -> Dictionary: return {"title": "Ziel-Lane %s" % LuneMap.LANE_NAMES[lane], "text": "Gebuchte Champions greifen in der nächsten Welle auf dieser Lane an."})
var row := HBoxContainer.new()
row.add_theme_constant_override("separation", 6)
add_child(row)
for champion_id: String in LuneContent.CHAMPIONS:
var spec: Dictionary = LuneContent.CHAMPIONS[champion_id]
var card := LuneUnitCard.new()
row.add_child(card)
var role_key: String = {"ch_schutz": "schutz", "ch_druck": "druck", "ch_belagerung": "siege"}.get(champion_id, "melee")
card.setup(champion_id, str(spec.model), str(spec.name), role_key, int(spec.cost), "", true)
card.pressed.connect(func() -> void: command_requested.emit({"type": "champion_book", "champion_id": champion_id, "lane": target_lane}))
cards[champion_id] = card
LuneTooltip.attach(card, _tip.bind(champion_id))
var right := VBoxContainer.new()
right.add_theme_constant_override("separation", 4)
right.size_flags_horizontal = Control.SIZE_EXPAND_FILL
right.custom_minimum_size.x = 200
add_child(right)
var head := HBoxContainer.new()
right.add_child(head)
LuneUiTheme.label("EINSÄTZE", "HudCaps", head).size_flags_horizontal = Control.SIZE_EXPAND_FILL
_count = LuneUiTheme.label("", "HudMuted", head)
_bookings = VBoxContainer.new()
_bookings.add_theme_constant_override("separation", 3)
right.add_child(_bookings)
var _model: LuneHudModel
func apply(model: LuneHudModel) -> void:
_model = model
var bookings: Array = model.own_bookings()
var fresh: int = (model.me().draft.new_bookings as Array).size() if model.planning() else 0
var gold: int = model.own_gold()
var signature: String = "%s|%d|%s|%s|%s|%d" % [str(bookings), gold, model.planning(), model.locked(), target_lane, model.state.round_number]
if signature == _signature:
return
_signature = signature
var can_act: bool = model.planning() and not model.locked()
for champion_id: String in cards:
var card: LuneUnitCard = cards[champion_id]
var cost: int = int(LuneContent.CHAMPIONS[champion_id].cost)
var why: String = ""
if not model.planning():
why = "Buchungen nur in der Planung."
elif model.locked():
why = "Bereit gemeldet – die Planung ist verbindlich."
elif fresh >= LuneContent.MAX_BOOKINGS:
why = "Höchstens %d Champions pro Runde." % LuneContent.MAX_BOOKINGS
elif gold < cost:
why = "Es fehlen %d Gold." % (cost - gold)
card.set_available(why.is_empty(), why, not can_act, gold < cost)
_count.text = "%d/%d diese Runde" % [fresh, LuneContent.MAX_BOOKINGS]
for child: Node in _bookings.get_children():
child.free()
if bookings.is_empty():
var hint := LuneUiTheme.label("Keine Champions gebucht. Kauf jetzt – Einsatz in Welle %d." % (model.state.round_number + (1 if model.planning() else 2)), "HudMuted", _bookings)
hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
return
for booking: Dictionary in bookings:
var line := HBoxContainer.new()
line.add_theme_constant_override("separation", 6)
_bookings.add_child(line)
LuneGlyph.make("hourglass", LuneUiTheme.CHAMPION, 14.0, line)
var running: bool = int(booking.deploy_round) == model.state.round_number
var texts := VBoxContainer.new()
texts.add_theme_constant_override("separation", -2)
texts.size_flags_horizontal = Control.SIZE_EXPAND_FILL
line.add_child(texts)
LuneUiTheme.label(str(LuneContent.CHAMPIONS[booking.champion_id].name), "HudText", texts)
LuneUiTheme.label("Einsatz in Welle %d – %s%s" % [int(booking.deploy_round), LuneMap.LANE_NAMES[booking.lane].to_upper(), " (jetzt)" if running else ""], "HudMuted", texts)
if bool(booking.get("fresh", false)):
var cancel := LuneUiTheme.button("×", "HudIconButton", line)
cancel.disabled = not can_act
var uid: String = str(booking.uid)
cancel.pressed.connect(func() -> void: command_requested.emit({"type": "champion_cancel", "uid": uid}))
LuneTooltip.attach(cancel, func() -> Dictionary: return {"title": "Buchung zurücknehmen", "text": "Volle Erstattung, solange die Planung läuft."})
func _tip(champion_id: String) -> Dictionary:
var spec: Dictionary = LuneContent.CHAMPIONS[champion_id]
var round_number: int = _model.state.round_number + 1 if _model != null else 2
var definition: Dictionary = LuneContent.champion_definition(champion_id, round_number, _model.state.balance if _model != null else LuneBalance.default_balance())
var card: LuneUnitCard = cards[champion_id]
return {"title": "%s · Angriffschampion" % spec.name, "glyph": "champion", "glyph_color": LuneUiTheme.CHAMPION,
"subtitle": "%s · %d Gold · Einsatz in Welle %d – %s" % [spec.role, int(spec.cost), round_number, LuneMap.LANE_NAMES[target_lane].to_upper()],
"stats": LuneStats.rows(definition), "text": str(spec.text),
"note": "Einmaliger Einsatz: kämpft eine Welle lang und verschwindet danach. Die Ankündigung ist für alle sichtbar; sein Tod bringt dem Gegner %d Gold." % int(definition.bounty),
"warn": card.reason}