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

@@ -33,7 +33,7 @@ const FIELD_COMBO_TEXT := {
"Blitz": "2 Schaden an den schwächsten Gegner",
"Schlamm": "Heile dich um 3",
"Sturm": "Ziehe 1 Karte",
"Sand": "Das neue Wesen erhält Schild",
"Sand": "Das neue Wesen erhält +1/+1",
}
const RESONANCE_TEXT := "Resonanz: Ein Wesen bekommt Angriff für eigene Wesen mit gleichem Element: +1, +2, +3, +3 bei ein bis vier Partnern."
const RARITIES: Array[String] = ["gewöhnlich", "selten", "episch", "legendär", "mythisch"]
@@ -82,7 +82,7 @@ static func field_combo_effect(name: String) -> Effect:
"Sturm":
return Effect.new("draw", "none", 1)
"Sand":
return Effect.new("shield", "self_creature")
return Effect.new("buff", "self_creature", 1, 1)
return null
@@ -119,62 +119,73 @@ class Effect extends RefCounted:
static func from_dict(d: Dictionary) -> Effect:
return Effect.new(d.kind, d.get("target", "none"), int(d.get("amount", 0)), int(d.get("amount2", 0)), str(d.get("card_id", "")))
func text() -> String:
## Kartentext in der aktuellen Sprache. trigger: bei Fallen der Auslöser (Wortwahl).
func text(trigger: String = "") -> String:
var n := str(amount)
if target == "trigger_source":
var played := trigger == "creature_played"
match kind:
"damage":
return n + " Schaden an den Angreifer"
if played:
return tr("%s Schaden an dieses Wesen") % n
return tr("%s Schaden an den Angreifer") % n
"freeze":
return "Der Angreifer wird eingefroren"
if played:
return tr("Es wird eingefroren")
return tr("Der Angreifer wird eingefroren")
"bounce":
return "Es geht zurück auf die Hand"
return tr("Es geht zurück auf die Hand")
match kind:
"cancel":
return "Der Angriff verpufft"
return tr("Der Angriff verpufft")
"summon":
return "Beschwöre einen " + Cards.by_id(card_id).name
return tr("Beschwöre einen %s") % Cards.by_id(card_id).title()
"damage":
match target:
"enemy_creature":
return n + " Schaden an ein gegnerisches Wesen"
return tr("%s Schaden an ein gegnerisches Wesen") % n
"enemy_any":
return n + " Schaden an ein Ziel"
return tr("%s Schaden an ein Ziel") % n
"all_enemy_creatures":
return n + " Schaden an alle gegnerischen Wesen"
return tr("%s Schaden an alle gegnerischen Wesen") % n
"all_enemy_heroes":
return n + " Schaden an alle Gegner"
return tr("%s Schaden an alle Gegner") % n
"all_enemies":
return n + " Schaden an alle Gegner und ihre Wesen"
return tr("%s Schaden an alle Gegner und ihre Wesen") % n
"heal":
return "Heile dich um " + n
return tr("Heile dich um %s") % n
"draw":
return "Ziehe 1 Karte" if amount == 1 else "Ziehe " + n + " Karten"
if amount == 1:
return tr("Ziehe 1 Karte")
return tr("Ziehe %s Karten") % n
"freeze":
if target == "enemy_creature":
return "Friere ein gegnerisches Wesen ein"
return "Friere alle gegnerischen Wesen ein"
return tr("Friere ein gegnerisches Wesen ein")
if target == "strongest_enemy_creature":
return tr("Friere das stärkste gegnerische Wesen ein")
return tr("Friere alle gegnerischen Wesen ein")
"bounce":
if target == "enemy_creature":
return "Ein gegnerisches Wesen zurück auf die Hand"
return "Alle gegnerischen Wesen zurück auf die Hand"
return tr("Ein gegnerisches Wesen zurück auf die Hand")
return tr("Alle gegnerischen Wesen zurück auf die Hand")
"buff":
var b := "+%d/+%d" % [amount, amount2]
if target == "friendly_creature":
return "Ein befreundetes Wesen erhält " + b
return "Alle befreundeten Wesen erhalten " + b
return tr("Ein befreundetes Wesen erhält %s") % b
return tr("Alle befreundeten Wesen erhalten %s") % b
"shield":
if target == "friendly_creature":
return "Ein befreundetes Wesen erhält Schild"
return tr("Ein befreundetes Wesen erhält Schild")
if target == "self_creature":
return "Erhält Schild"
return "Alle befreundeten Wesen erhalten Schild"
return tr("Erhält Schild")
return tr("Alle befreundeten Wesen erhalten Schild")
return ""
class Card extends RefCounted:
var id: String
var name: String
var name: String # deutscher Name (auch im Verlauf und in Tests)
var name_en := "" # englischer Name
var elements: Array
var cost: int
var rarity: String
@@ -192,7 +203,7 @@ class Card extends RefCounted:
return kind == "Falle"
func to_dict() -> Dictionary:
var d := {"id": id, "name": name, "elements": elements, "cost": cost, "rarity": rarity, "kind": kind}
var d := {"id": id, "name": name, "name_en": name_en, "elements": elements, "cost": cost, "rarity": rarity, "kind": kind}
if is_creature():
d["attack"] = attack
d["health"] = health
@@ -208,6 +219,7 @@ class Card extends RefCounted:
var c := Card.new()
c.id = d.id
c.name = d.name
c.name_en = str(d.get("name_en", ""))
c.elements = Array(d.elements)
c.cost = int(d.cost)
c.rarity = d.rarity
@@ -220,6 +232,12 @@ class Card extends RefCounted:
c.trigger = str(d.get("trigger", ""))
return c
## Anzeigename in der aktuellen Sprache.
func title() -> String:
if name_en != "" and TranslationServer.get_locale().begins_with("en"):
return name_en
return name
func is_combo() -> bool:
return elements.size() >= 2
@@ -238,19 +256,19 @@ class Card extends RefCounted:
var kws: Array[String] = []
for k in Cards.KEYWORDS:
if k in keywords:
kws.append(k)
kws.append(tr(k))
parts.append(", ".join(kws) + ".")
if not effects.is_empty():
var fx: Array[String] = []
for f in effects:
fx.append(f.text())
fx.append(f.text(trigger))
var s := ", ".join(fx) + "."
if is_trap():
if trigger == "creature_played":
s = s.replace("an den Angreifer", "an dieses Wesen").replace("Der Angreifer wird", "Es wird")
parts.append("Falle. %s: %s" % [Cards.TRIGGER_TEXT[trigger], s])
parts.append(tr("Falle. %s: %s") % [tr(Cards.TRIGGER_TEXT[trigger]), s])
elif is_creature():
parts.append(tr("Beim Ausspielen: %s") % s)
else:
parts.append(("Beim Ausspielen: " + s) if is_creature() else s)
parts.append(s)
return " ".join(parts)