Kampf fortsetzen, Gamepad im Deckbau, Deck-Codes, Kapitel 3, Elementbooster, englische Karten, Host-Wechsel und Zuschauer

- KI-Bewertung mit Bedrohung und Führenden-Bremse (62:38 gegen die alte im Duell, gleichauf zu viert)
- Balance: Sand +1/+1 statt Schild, Erde+Luft und Erde geschwächt, Wasser angehoben, Messlauf mit N=200 möglich
- Einzelspieler-Kämpfe werden laufend gespeichert (voller Spielstand, viewer -1) und im Menü fortgesetzt
- Karten, Deckzeilen und Kartenrücken fokussierbar: Gamepad in Deckbau, Werkstatt und Booster
- Kartennamen (name_en) und Kartentexte übersetzbar, Übersetzungstest deckt cards.gd mit ab
- Deck-Code DINGER1:id*anzahl mit Zwischenablage; acht neue Karten; Kampagne Kapitel 3; Elementbooster 120 Münzen
- Netzwerk: Nachfolge-Host mit vollem Spielstand, Neuverbindung der anderen, Zuschauer (Sicht -2); Bot kann hosten
- Große Schrift: Kartennamen, Kartentext, Knopfleisten, Gegnervorschau, Dialogtext
- Anleitung 1 garantiert ein 1-Energie-Wesen; Tests: DINGER_ONLY für einzelne Oberflächentests
- Oberflächentest auch auf Xephyr mit llvmpipe (wie Xvfb in der CI) grün

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPgKvy5W9WRNFHjkm5QsHx
This commit is contained in:
Team3
2026-09-16 18:50:09 +02:00
parent b6d5916a8d
commit 4c6bf96eeb
32 changed files with 1355 additions and 174 deletions

View File

@@ -10,19 +10,31 @@ var border = null # Color oder null (dann Seltenheitsfarbe)
var dim := false
func _init(c: Cards.Card, sz: Vector2) -> void:
## focusable: per Gamepad oder Tab anwählbar (Sammlung, Booster); A bzw. Enter wirkt wie ein Klick.
func _init(c: Cards.Card, sz: Vector2, focusable: bool = false) -> void:
card = c
custom_minimum_size = sz
size = sz
mouse_filter = Control.MOUSE_FILTER_STOP
mouse_entered.connect(func(): hover.emit(card, true))
mouse_exited.connect(func(): hover.emit(card, false))
if focusable:
focus_mode = Control.FOCUS_ALL
focus_entered.connect(func():
hover.emit(card, true)
queue_redraw())
focus_exited.connect(func():
hover.emit(card, false)
queue_redraw())
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
accept_event()
clicked.emit(self)
elif has_focus() and event.is_action_pressed("ui_accept"):
accept_event()
clicked.emit(self)
func _draw() -> void:
@@ -41,10 +53,11 @@ func _draw() -> void:
draw_circle(cc, r, Color("14141c"))
Style.text(self, str(card.cost), f_stat, Style.GOLD, cc, "center", true)
# Name und Kombo
var nm := Style.fit(card.name, true, f_name, size.x - 2 * r - 12)
Style.text(self, nm, f_name, Style.TEXT, Vector2(size.x / 2 + r / 2.0, 4), "midtop", true)
var name_left := 2 * r + 10
var nm := Style.fit(card.title(), true, f_name, size.x - name_left - 6)
Style.text(self, nm, f_name, Style.TEXT, Vector2((name_left + size.x - 6) / 2.0, 4), "midtop", true)
if mid:
var sub: String = (card.combo() + " · " + card.kind) if card.is_combo() else card.kind
var sub: String = (tr(card.combo()) + " · " + tr(card.kind)) if card.is_combo() else tr(card.kind)
Style.text(self, sub, f_text, Color("e6e6e6"), Vector2(size.x / 2, 2 * r + 4), "midtop")
# Textfeld: Höhe folgt dem umbrochenen Text und der Schriftgröße, das Bild bekommt den Rest
var tsize := Style.fs(f_text)
@@ -60,11 +73,14 @@ func _draw() -> void:
var gsize: float = minf(band_bottom - band_top, size.x * 0.42)
Style.glyph(self, card, Vector2(size.x / 2, (band_top + band_bottom) / 2), gsize)
draw_rect(box, Color(0.06, 0.06, 0.09, 0.67))
var max_lines := maxi(1, int((box.size.y - 6) / Style.FONT.get_height(tsize))) # große Schrift: lieber kürzen als überlaufen
draw_multiline_string(Style.FONT, Vector2(box.position.x + 4, box.position.y + 4 + Style.FONT.get_ascent(tsize)),
card.text(), HORIZONTAL_ALIGNMENT_LEFT, box.size.x - 8, tsize, -1, Style.TEXT)
card.text(), HORIZONTAL_ALIGNMENT_LEFT, box.size.x - 8, tsize, max_lines, Style.TEXT)
# Werte
if card.is_creature():
Style.text(self, str(card.attack), f_stat, Style.YELLOW, Vector2(10, size.y - 6), "bottomleft", true)
Style.text(self, str(card.health), f_stat, Style.RED, Vector2(size.x - 10, size.y - 6), "bottomright", true)
if dim:
draw_rect(rect, Color(0, 0, 0, 0.47))
if has_focus():
Style.border(self, rect, Style.GOLD, 4, 8)