Drei Helden mit Nähe-/Teilnahme-XP, Talenten und drei Itemplätzen, drei Angriffschampions mit Buchung n -> n+1, Bossgrotte mit Streitfall, ruhendem Boss, idempotenter Eroberung, einmaliger Entsendung für das Siegerteam, hohem Kopfgeld und Sperre bis Sieg+4. HUD-Reiter, Porträt aus echtem Modell, Grotten-Asset, Bots für alle Systeme. Regeln M2 73/73, M1 91/91, App M2 15/15 und M1 38/38, Bossszenarien dokumentiert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
4.9 KiB
GDScript
113 lines
4.9 KiB
GDScript
extends VBoxContainer
|
||
## Reinforcement tab: book attack champions for the next round, see this round's public
|
||
## announcements, and steer a conquered boss if you are its chooser.
|
||
|
||
signal command_requested(command: Dictionary)
|
||
|
||
var match_state: LuneMatch
|
||
var slot: int = 0
|
||
var _lane: String = "mid"
|
||
var _signature: String = "@"
|
||
|
||
func _ready() -> void:
|
||
add_theme_constant_override("separation", 8)
|
||
|
||
func _process(_delta: float) -> void:
|
||
if match_state == null or match_state.phase != LuneMatch.Phase.PLANNING:
|
||
return
|
||
var draft: Dictionary = match_state.players[slot].draft
|
||
var signature: String = JSON.stringify([draft.gold, draft.new_bookings, draft.boss_lane, match_state.round_number, _lane, match_state.boss])
|
||
if signature != _signature:
|
||
_signature = signature
|
||
_rebuild()
|
||
|
||
func _label(text_value: String, variation: String, size: int = 0) -> Label:
|
||
var label := Label.new()
|
||
label.text = text_value
|
||
label.theme_type_variation = variation
|
||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
if size > 0:
|
||
label.add_theme_font_size_override("font_size", size)
|
||
add_child(label)
|
||
return label
|
||
|
||
func _rebuild() -> void:
|
||
for child: Node in get_children():
|
||
child.free()
|
||
var draft: Dictionary = match_state.players[slot].draft
|
||
_label("ANGRIFFSCHAMPIONS · Kauf jetzt, Einsatz nächste Runde", "SectionLabel")
|
||
var lanes := HBoxContainer.new()
|
||
add_child(lanes)
|
||
var lane_label := Label.new()
|
||
lane_label.text = "Ziel-Lane"
|
||
lane_label.theme_type_variation = "MutedLabel"
|
||
lane_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
lanes.add_child(lane_label)
|
||
for lane: String in LuneMap.LANES:
|
||
var button := Button.new()
|
||
button.text = LuneMap.LANE_NAMES[lane]
|
||
button.theme_type_variation = "ChoiceButton"
|
||
button.toggle_mode = true
|
||
button.button_pressed = lane == _lane
|
||
button.pressed.connect(func() -> void: _lane = lane)
|
||
lanes.add_child(button)
|
||
for champion_id: String in LuneContent.CHAMPIONS:
|
||
var spec: Dictionary = LuneContent.CHAMPIONS[champion_id]
|
||
var row := HBoxContainer.new()
|
||
add_child(row)
|
||
var text := VBoxContainer.new()
|
||
text.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
text.add_theme_constant_override("separation", 0)
|
||
row.add_child(text)
|
||
var name := Label.new()
|
||
name.text = "%s · %s" % [spec.name, spec.role]
|
||
text.add_child(name)
|
||
var info := Label.new()
|
||
info.text = spec.text
|
||
info.theme_type_variation = "MutedLabel"
|
||
info.add_theme_font_size_override("font_size", 14)
|
||
info.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
text.add_child(info)
|
||
var book := Button.new()
|
||
book.text = "%d G" % int(spec.cost)
|
||
book.disabled = int(draft.gold) < int(spec.cost) or (draft.new_bookings as Array).size() >= LuneContent.MAX_BOOKINGS
|
||
book.tooltip_text = "%s für Runde %d auf %s buchen" % [spec.name, match_state.round_number + 1, LuneMap.LANE_NAMES[_lane]]
|
||
book.pressed.connect(func() -> void: command_requested.emit({"type": "champion_book", "champion_id": champion_id, "lane": _lane}))
|
||
row.add_child(book)
|
||
for booking: Dictionary in draft.new_bookings:
|
||
var row := HBoxContainer.new()
|
||
add_child(row)
|
||
var label := Label.new()
|
||
label.text = "Gebucht: %s · %s · Runde %d" % [LuneContent.CHAMPIONS[booking.champion_id].name, LuneMap.LANE_NAMES[booking.lane], match_state.round_number + 1]
|
||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
row.add_child(label)
|
||
var cancel := Button.new()
|
||
cancel.text = "×"
|
||
cancel.tooltip_text = "Buchung zurücknehmen (volle Erstattung)"
|
||
var uid: String = booking.uid
|
||
cancel.pressed.connect(func() -> void: command_requested.emit({"type": "champion_cancel", "uid": uid}))
|
||
row.add_child(cancel)
|
||
add_child(HSeparator.new())
|
||
_label("ANGEKÜNDIGT FÜR DIESE RUNDE", "SectionLabel")
|
||
var lines: PackedStringArray = []
|
||
for entry: Dictionary in match_state.announcements():
|
||
var team: String = "Blau" if int(entry.team) == 0 else "Rot"
|
||
if entry.kind == "boss":
|
||
lines.append("%s: Schlundkrone greift über %s an · Kopfgeld für die Verteidiger %d Gold" % [team, LuneMap.LANE_NAMES[entry.lane], int(entry.bounty)])
|
||
else:
|
||
lines.append("%s: %s auf %s" % [team, LuneContent.CHAMPIONS[entry.champion_id].name, LuneMap.LANE_NAMES[entry.lane]])
|
||
_label("\n".join(lines) if not lines.is_empty() else "Keine Angriffschampions und kein Boss angekündigt.", "MutedLabel", 15)
|
||
var claim: Dictionary = match_state.boss.claim
|
||
if not claim.is_empty() and int(claim.chooser_slot) == slot and int(claim.deploy_round) == match_state.round_number:
|
||
_label("DEIN BOSS · wähle die Angriffs-Lane", "SectionLabel")
|
||
var row := HBoxContainer.new()
|
||
add_child(row)
|
||
var chosen: String = str(draft.boss_lane) if not str(draft.boss_lane).is_empty() else str(claim.lane)
|
||
for lane: String in LuneMap.LANES:
|
||
var button := Button.new()
|
||
button.text = LuneMap.LANE_NAMES[lane]
|
||
button.theme_type_variation = "ChoiceButton"
|
||
button.toggle_mode = true
|
||
button.button_pressed = lane == chosen
|
||
button.pressed.connect(func() -> void: command_requested.emit({"type": "boss_lane", "lane": lane}))
|
||
row.add_child(button)
|