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>
123 lines
5.7 KiB
GDScript
123 lines
5.7 KiB
GDScript
class_name LuneNextWave
|
||
extends PanelContainer
|
||
## What the coming wave brings: monster types with counts (identical for both sides and every
|
||
## lane), new types, announced attack champions and an announced boss. Dangers for the own
|
||
## team are highlighted. In combat the panel shrinks to one line about the following wave.
|
||
|
||
var _title: Label
|
||
var _round: Label
|
||
var _rows: VBoxContainer
|
||
var _extra: VBoxContainer
|
||
var _note: Label
|
||
var _signature: String = ""
|
||
var compact: bool = false
|
||
|
||
func _init() -> void:
|
||
theme_type_variation = "HudPanelTight"
|
||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||
var column := VBoxContainer.new()
|
||
column.add_theme_constant_override("separation", 4)
|
||
column.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
add_child(column)
|
||
var head := HBoxContainer.new()
|
||
head.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
column.add_child(head)
|
||
_title = LuneUiTheme.label("NÄCHSTE WELLE", "HudCaps", head)
|
||
_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
_round = LuneUiTheme.label("", "HudValue", head)
|
||
_rows = VBoxContainer.new()
|
||
_rows.add_theme_constant_override("separation", 2)
|
||
_rows.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
column.add_child(_rows)
|
||
_note = LuneUiTheme.label("", "HudMuted", column)
|
||
_note.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
_extra = VBoxContainer.new()
|
||
_extra.add_theme_constant_override("separation", 3)
|
||
_extra.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
column.add_child(_extra)
|
||
|
||
func apply(wave: Dictionary, own_team: int, fighting: bool) -> void:
|
||
var signature: String = "%s|%d|%s" % [str(wave), own_team, fighting]
|
||
if signature == _signature:
|
||
return
|
||
_signature = signature
|
||
compact = fighting
|
||
_title.text = "DANACH" if fighting else "NÄCHSTE WELLE"
|
||
_round.text = "WELLE %d" % int(wave.round)
|
||
for child: Node in _rows.get_children():
|
||
child.free()
|
||
for child: Node in _extra.get_children():
|
||
child.free()
|
||
var monsters: Array = wave.monsters
|
||
if fighting:
|
||
var parts: PackedStringArray = []
|
||
for monster: Dictionary in monsters:
|
||
parts.append("%d× %s%s" % [int(monster.count), monster.name, " (neu)" if monster.new else ""])
|
||
_note.text = ", ".join(parts) + " je Lane"
|
||
return
|
||
for monster: Dictionary in monsters:
|
||
var row := HBoxContainer.new()
|
||
row.add_theme_constant_override("separation", 7)
|
||
row.mouse_filter = Control.MOUSE_FILTER_PASS
|
||
_rows.add_child(row)
|
||
var portrait := LunePortraitRect.new()
|
||
portrait.custom_minimum_size = Vector2(30, 30)
|
||
row.add_child(portrait)
|
||
portrait.show_model(str(monster.id))
|
||
var count := LuneUiTheme.label("%d×" % int(monster.count), "HudValue", row)
|
||
count.custom_minimum_size.x = 30
|
||
var title := LuneUiTheme.label(str(monster.name), "HudText", row)
|
||
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
title.clip_text = true
|
||
title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||
LuneGlyph.make("armor", LuneUiTheme.MUTED, 13.0, row)
|
||
LuneUiTheme.label(DataCatalog.ARMOR_NAMES[int(monster.armor)], "HudMuted", row)
|
||
if monster.new:
|
||
var badge := LuneStatusBadge.new()
|
||
badge.set_state("NEU", LuneUiTheme.WARN)
|
||
row.add_child(badge)
|
||
var data: Dictionary = monster
|
||
LuneTooltip.attach(row, func() -> Dictionary: return {"title": str(data.name), "subtitle": "Monster der Welle %d" % int(wave.round),
|
||
"stats": [["heart", "Leben", LuneUiTheme.number(data.hp)], ["sword", "Schaden/Treffer", LuneUiTheme.decimal(data.damage)],
|
||
["range", "Reichweite", "%s m" % LuneUiTheme.decimal(data.range)], ["armor", "Rüstung", DataCatalog.ARMOR_NAMES[int(data.armor)]]],
|
||
"note": "Beide Teams erhalten auf jeder Lane dieselbe Welle."})
|
||
# Counter hint as in Legion TD 2: the armour most of the wave wears and the damage type
|
||
# that hits it for 130 % (LuneUnit.DAMAGE_MATRIX).
|
||
var armour_count: Array[int] = [0, 0, 0]
|
||
for monster: Dictionary in monsters:
|
||
armour_count[int(monster.armor)] += int(monster.count)
|
||
var main_armour: int = armour_count.find(armour_count.max())
|
||
var counter: int = 0
|
||
for attack: int in range(3):
|
||
if is_equal_approx(float(LuneUnit.DAMAGE_MATRIX[attack][main_armour]), 1.30):
|
||
counter = attack
|
||
_note.text = "%d Monster je Lane · beide Seiten gleich\nMeist %s: %s trifft stark" % [int(wave.count), DataCatalog.ARMOR_NAMES[main_armour], DataCatalog.DAMAGE_NAMES[counter]]
|
||
var champions: Array = wave.champions
|
||
var boss: Dictionary = wave.boss
|
||
if champions.is_empty() and boss.is_empty():
|
||
return
|
||
LuneUiTheme.label("ANGEKÜNDIGT", "HudCaps", _extra)
|
||
for entry: Dictionary in champions:
|
||
var hostile: bool = not bool(entry.own)
|
||
var row := HBoxContainer.new()
|
||
row.add_theme_constant_override("separation", 6)
|
||
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_extra.add_child(row)
|
||
LuneGlyph.make("champion", LuneUiTheme.DANGER if hostile else LuneUiTheme.CHAMPION, 15.0, row)
|
||
var text := LuneUiTheme.label("%s → %s" % [entry.name, LuneMap.LANE_NAMES[entry.lane].to_upper()], "HudText", row)
|
||
text.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
var badge := LuneStatusBadge.new()
|
||
badge.set_state("GEGEN EUCH" if hostile else "FÜR EUCH", LuneUiTheme.DANGER if hostile else LuneUiTheme.GOOD)
|
||
row.add_child(badge)
|
||
if not boss.is_empty():
|
||
var hostile_boss: bool = not bool(boss.own)
|
||
var row := HBoxContainer.new()
|
||
row.add_theme_constant_override("separation", 6)
|
||
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
_extra.add_child(row)
|
||
LuneGlyph.make("crown", LuneUiTheme.DANGER if hostile_boss else LuneUiTheme.team_color(own_team), 16.0, row)
|
||
var text := LuneUiTheme.label("Schlundkrone → %s" % LuneMap.LANE_NAMES[boss.lane].to_upper(), "HudText", row)
|
||
text.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
var badge := LuneStatusBadge.new()
|
||
badge.set_state("GEGEN EUCH" if hostile_boss else "FÜR EUCH", LuneUiTheme.DANGER if hostile_boss else LuneUiTheme.GOOD, "crown")
|
||
row.add_child(badge)
|