update
This commit is contained in:
@@ -22,9 +22,9 @@ Der Spielstand liegt unter `user://spielstand.json`
|
|||||||
|
|
||||||
- 81 Karten in `data/karten.json`, alle Wesen, jede mit genau einem der vier Elemente; Kombos sind die Namen verschmolzener Wesen
|
- 81 Karten in `data/karten.json`, alle Wesen, jede mit genau einem der vier Elemente; Kombos sind die Namen verschmolzener Wesen
|
||||||
- Karten haben nur Element, Angriff und Leben; die Kosten sind zwei Drittel Angriff plus ein Drittel Leben, abgerundet; je Element gibt es ein 0/2-Wesen für 0 Mana
|
- Karten haben nur Element, Angriff und Leben; die Kosten sind zwei Drittel Angriff plus ein Drittel Leben, abgerundet; je Element gibt es ein 0/2-Wesen für 0 Mana
|
||||||
- Mana: 1 im ersten Zug, jede Runde eins mehr ohne Grenze; was übrig bleibt, verfällt. Mana geht in Ziehen, Legen, Verbessern und Verschmelzen
|
- Mana: 1 im ersten Zug, jede Runde eins mehr ohne Grenze; was übrig bleibt, verfällt. Mana geht in Ziehen, Legen und Verschmelzen
|
||||||
- Kein Deckbau: beide ziehen vom gemeinsamen Stapel (jede Karte dreimal). Zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was das Mana dieses Zugs hergibt, der Rest wandert unter den Stapel
|
- Kein Deckbau: beide ziehen vom gemeinsamen Stapel (jede Karte dreimal). Zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was das Mana dieses Zugs hergibt, der Rest wandert unter den Stapel
|
||||||
- Eigenschaften: Spott (2, muss zuerst angegriffen werden), Flink (2, greift sofort an), Regeneration (3, heilt am Zugende ganz) und Doppelschlag (4) lassen sich für Mana auf jede Hand- oder Brettkarte kaufen (Rechtsklick oder Y). Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink; verletzte Wesen heilen nicht von selbst
|
- Eigenschaften gibt es nur über Elementpaare (siehe Elementboni): Spott (muss zuerst angegriffen werden), Flink (greift sofort an), Regeneration (heilt am Zugende ganz), Doppelschlag (greift zweimal je Zug). Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink; verletzte Wesen heilen nicht von selbst
|
||||||
- Verdeckt legen (mittlere Maustaste auf die Handkarte oder im Kartenmenü): gleich teuer, das Wesen greift nicht an und zählt nicht für die Elementboni; aufdecken ist kostenlos, frühestens im nächsten Zug, danach greift es sofort an; ein Angriff deckt es auf
|
- Verdeckt legen (mittlere Maustaste auf die Handkarte oder im Kartenmenü): gleich teuer, das Wesen greift nicht an und zählt nicht für die Elementboni; aufdecken ist kostenlos, frühestens im nächsten Zug, danach greift es sofort an; ein Angriff deckt es auf
|
||||||
- Jede Karte gehört jedem von Anfang an. Gesammelt wird die Seltenheitsstufe, und die ist reine Optik wie bei Yu-Gi-Oh: Common matt, Rare silberner Name und Rahmen mit Glanzstreifen, Super Rare Holo-Bild, Ultra Rare goldener Name und Rahmen mit Holo-Bild, Ultimate Rare Relief, Holo auf Bild und Rahmen, Funkeln und schillernde Farben; auch auf dem Feld für alle sichtbar, Werte bleiben gleich. Verschmolzene Wesen tragen die höhere Stufe ihrer Eltern
|
- Jede Karte gehört jedem von Anfang an. Gesammelt wird die Seltenheitsstufe, und die ist reine Optik wie bei Yu-Gi-Oh: Common matt, Rare silberner Name und Rahmen mit Glanzstreifen, Super Rare Holo-Bild, Ultra Rare goldener Name und Rahmen mit Holo-Bild, Ultimate Rare Relief, Holo auf Bild und Rahmen, Funkeln und schillernde Farben; auch auf dem Feld für alle sichtbar, Werte bleiben gleich. Verschmolzene Wesen tragen die höhere Stufe ihrer Eltern
|
||||||
- Booster (100 Münzen): fünf Karten mit gewürfelter Stufe (50/25/15/8/2 %, mindestens eine Rare), höhere werten die Karte auf, der Rest wird zu Staub (50/100/200/500/1000). Aufwerten kostet 100/200/500/1000 Staub, eine Stufe abgeben bringt den Zerlegewert zurück
|
- Booster (100 Münzen): fünf Karten mit gewürfelter Stufe (50/25/15/8/2 %, mindestens eine Rare), höhere werten die Karte auf, der Rest wird zu Staub (50/100/200/500/1000). Aufwerten kostet 100/200/500/1000 Staub, eine Stufe abgeben bringt den Zerlegewert zurück
|
||||||
@@ -70,7 +70,7 @@ godot --headless --path . --export-release Windows build/dinger-windows.exe
|
|||||||
## Aufbau
|
## Aufbau
|
||||||
|
|
||||||
- `scripts/cards.gd` – Elemente, Kombos, Kartenklassen; Pool aus `data/karten.json`
|
- `scripts/cards.gd` – Elemente, Kombos, Kartenklassen; Pool aus `data/karten.json`
|
||||||
- `scripts/game.gd` – Spielregeln: Züge, Mana, Ziehen, verdeckte Wesen, Elementboni, Kampf, Verbessern, Verschmelzen
|
- `scripts/game.gd` – Spielregeln: Züge, Mana, Ziehen, verdeckte Wesen, Elementboni, Kampf, Verschmelzen
|
||||||
- `scripts/deckgen.gd` – gemeinsamer Stapel und Gegner
|
- `scripts/deckgen.gd` – gemeinsamer Stapel und Gegner
|
||||||
- `scripts/netstate.gd` – Zustandsbilder fürs Netzwerk
|
- `scripts/netstate.gd` – Zustandsbilder fürs Netzwerk
|
||||||
- `scripts/ai.gd` – KI mit Vorausschau (plus gierige Vergleichs-KI)
|
- `scripts/ai.gd` – KI mit Vorausschau (plus gierige Vergleichs-KI)
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
- Booster öffnen (Chancen 50/25/15/8/2 %, mindestens eine Rare): höhere Stufen werten Karten auf, der Rest wird zu Staub (50/100/200/500/1000)
|
- Booster öffnen (Chancen 50/25/15/8/2 %, mindestens eine Rare): höhere Stufen werten Karten auf, der Rest wird zu Staub (50/100/200/500/1000)
|
||||||
- Staub wertet gezielt auf (100/200/500/1000), eine Stufe abgeben bringt den Zerlegewert zurück
|
- Staub wertet gezielt auf (100/200/500/1000), eine Stufe abgeben bringt den Zerlegewert zurück
|
||||||
- Kein Deckbau: alle ziehen vom gemeinsamen Stapel (jede Karte dreimal); zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was man sich in diesem Zug leisten kann
|
- Kein Deckbau: alle ziehen vom gemeinsamen Stapel (jede Karte dreimal); zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was man sich in diesem Zug leisten kann
|
||||||
- Mana für Ziehen, Legen (Kosten = 2/3 Angriff + 1/3 Leben, abgerundet; 0/2 kostet 0), Verbessern und Verschmelzen; 1 im ersten Zug, dann je Runde eins mehr; der Rest verfällt; höchstens 5 Handkarten
|
- Mana für Ziehen, Legen (Kosten = 2/3 Angriff + 1/3 Leben, abgerundet; 0/2 kostet 0), und Verschmelzen; 1 im ersten Zug, dann je Runde eins mehr; der Rest verfällt; höchstens 5 Handkarten
|
||||||
- Karten haben nur Angriff und Leben; Eigenschaften kauft man per Rechtsklick für Mana dazu, auf Hand und Brett: Spott 2 (muss zuerst angegriffen werden), Flink 2 (greift sofort an), Regeneration 3 (heilt sich am Zugende ganz), Doppelschlag 4
|
- Karten haben nur Angriff und Leben; Eigenschaften kommen allein über Elementpaare: Spott (muss zuerst angegriffen werden), Flink (greift sofort an), Regeneration (heilt am Zugende ganz), Doppelschlag (greift zweimal je Zug)
|
||||||
- Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink (auch im ersten Zug); verletzte Wesen heilen nicht von selbst
|
- Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink (auch im ersten Zug); verletzte Wesen heilen nicht von selbst
|
||||||
- Wesen lassen sich verdeckt legen (mittlere Maustaste oder Kartenmenü); aufdecken ist frei, frühestens im nächsten Zug, dann greift es sofort an; ein Angriff deckt auf
|
- Wesen lassen sich verdeckt legen (mittlere Maustaste oder Kartenmenü); aufdecken ist frei, frühestens im nächsten Zug, dann greift es sofort an; ein Angriff deckt auf
|
||||||
- Leben erst ohne Wesen angreifbar; Wesen mit Spott zuerst; nichts schlägt durch
|
- Leben erst ohne Wesen angreifbar; Wesen mit Spott zuerst; nichts schlägt durch
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Stand: September 2026. Kein Enddesign, sondern die Richtung mit umgesetztem Entw
|
|||||||
## Karte
|
## Karte
|
||||||
|
|
||||||
- Kostenkristall oben links, Bild (Elementsymbol) oben, Namensband auf 36 % Höhe, darunter
|
- Kostenkristall oben links, Bild (Elementsymbol) oben, Namensband auf 36 % Höhe, darunter
|
||||||
Eigenschaften als Symbole (Ausrufezeichen = Spott, Doppelpfeil = Flink, Kreuz = Regeneration, gekreuzte Klingen = Doppelschlag; gekaufte weiß, vom Element in dessen Farbe), unten Angriff (gelb) und Leben (rot) als Kreise, dazwischen der Edelstein der
|
Eigenschaften als Symbole (Ausrufezeichen = Spott, Doppelpfeil = Flink, Kreuz = Regeneration, gekreuzte Klingen = Doppelschlag, in der Farbe des Elements, das sie gibt), unten Angriff (gelb) und Leben (blau) als Kreise, dazwischen der Edelstein der
|
||||||
Stufe. Karten haben keinen Text: nur Element, Angriff, Leben.
|
Stufe. Karten haben keinen Text: nur Element, Angriff, Leben.
|
||||||
- Große Karten (Vorschau, Sammlung, Booster) schreiben das Wort neben das Symbol.
|
- Große Karten (Vorschau, Sammlung, Booster) schreiben das Wort neben das Symbol.
|
||||||
- Die Hand sind bis zu fünf große Karten; Details kommen aus der schwebenden Vorschau.
|
- Die Hand sind bis zu fünf große Karten; Details kommen aus der schwebenden Vorschau.
|
||||||
@@ -65,8 +65,7 @@ Nur, was gerade zählt, ist zu sehen. Von unten nach oben:
|
|||||||
- Zugbeginn: Band „Dein Zug“ quer über die Mitte, Ton.
|
- Zugbeginn: Band „Dein Zug“ quer über die Mitte, Ton.
|
||||||
- Ausspielen: die Kachel fliegt von der Hand auf ihren Platz und federt ein.
|
- Ausspielen: die Kachel fliegt von der Hand auf ihren Platz und federt ein.
|
||||||
- Angriff: Ausfallschritt, roter Rahmen am Ziel, kurzes Zittern des Bretts, Schadenszahl.
|
- Angriff: Ausfallschritt, roter Rahmen am Ziel, kurzes Zittern des Bretts, Schadenszahl.
|
||||||
- Aufdecken: „Aufgedeckt!“ am Wesen; Verbessern: die Eigenschaft steigt grün auf; Regeneration am
|
- Aufdecken: „Aufgedeckt!“ am Wesen; Regeneration am Zugende steigt grün auf.
|
||||||
Zugende ebenso.
|
|
||||||
- Karten heben sich beim Zeigen an, Knöpfe federn beim Drücken, Bildschirme blenden ein.
|
- Karten heben sich beim Zeigen an, Knöpfe federn beim Drücken, Bildschirme blenden ein.
|
||||||
- Booster: Karten drehen sich auf, Stufen ab Rare blitzen in ihrer Farbe auf.
|
- Booster: Karten drehen sich auf, Stufen ab Rare blitzen in ihrer Farbe auf.
|
||||||
- Alles hängt an der Einstellung „Animationen“.
|
- Alles hängt an der Einstellung „Animationen“.
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ static func evaluate(g: Game, p: Game.Player) -> float:
|
|||||||
# --- Vorausschau ---------------------------------------------------------------
|
# --- Vorausschau ---------------------------------------------------------------
|
||||||
|
|
||||||
const MAX_TARGETS := 4 # je Angreifer nur die aussichtsreichsten Ziele prüfen
|
const MAX_TARGETS := 4 # je Angreifer nur die aussichtsreichsten Ziele prüfen
|
||||||
static var allow_upgrade := true # zum Messen: Verbessern erwägen
|
|
||||||
const DEEPEN := 8 # so viele beste Erstschritte bekommen Folgeschritte
|
const DEEPEN := 8 # so viele beste Erstschritte bekommen Folgeschritte
|
||||||
const FOLLOW_UPS := 10 # Folgeschritte je vertieftem Kandidat
|
const FOLLOW_UPS := 10 # Folgeschritte je vertieftem Kandidat
|
||||||
static var time_budget_ms := 150 # Zeitbudget je Entscheidung; danach der beste bisherige Zug
|
static var time_budget_ms := 150 # Zeitbudget je Entscheidung; danach der beste bisherige Zug
|
||||||
@@ -99,18 +98,9 @@ static func candidate_actions(game: Game, p: Game.Player, prune: bool = false) -
|
|||||||
for card in p.hand:
|
for card in p.hand:
|
||||||
if game.can_play(p, card)[0]:
|
if game.can_play(p, card)[0]:
|
||||||
out.append({"type": "play", "card": card, "face_down": false})
|
out.append({"type": "play", "card": card, "face_down": false})
|
||||||
var has_target := not game.enemies_of(p).is_empty()
|
|
||||||
for cr in p.board:
|
for cr in p.board:
|
||||||
if cr.hidden:
|
if cr.hidden and game.can_reveal(p, cr)[0]:
|
||||||
if game.can_reveal(p, cr)[0]:
|
|
||||||
out.append({"type": "reveal", "creature": cr})
|
out.append({"type": "reveal", "creature": cr})
|
||||||
elif allow_upgrade and has_target:
|
|
||||||
# Nur Verbesserungen, die sich noch in diesem Zug auszahlen können: Doppelschlag für
|
|
||||||
# ein Wesen, das gleich angreift; Flink für eines, das sonst erst nächsten Zug dürfte.
|
|
||||||
if cr.can_attack() and game.can_upgrade(p, cr, "Doppelschlag")[0]:
|
|
||||||
out.append({"type": "upgrade", "creature": cr, "keyword": "Doppelschlag"})
|
|
||||||
if not cr.ready and not cr.has("Flink") and cr.power() > 0 and game.can_upgrade(p, cr, "Flink")[0]:
|
|
||||||
out.append({"type": "upgrade", "creature": cr, "keyword": "Flink"})
|
|
||||||
var pairs := []
|
var pairs := []
|
||||||
for i in p.board.size():
|
for i in p.board.size():
|
||||||
for j in range(i + 1, p.board.size()):
|
for j in range(i + 1, p.board.size()):
|
||||||
@@ -144,8 +134,6 @@ static func apply_on_clone(g2: Game, game: Game, action: Dictionary) -> void:
|
|||||||
g2.play_card(p2, action.card, bool(action.get("face_down", false)))
|
g2.play_card(p2, action.card, bool(action.get("face_down", false)))
|
||||||
"reveal":
|
"reveal":
|
||||||
g2.reveal(p2, g2.find_creature(action.creature.uid))
|
g2.reveal(p2, g2.find_creature(action.creature.uid))
|
||||||
"upgrade":
|
|
||||||
g2.upgrade(p2, g2.find_creature(action.creature.uid), action.keyword)
|
|
||||||
"fuse":
|
"fuse":
|
||||||
g2.fuse(p2, g2.find_creature(action.creature.uid), g2.find_creature(action.other.uid))
|
g2.fuse(p2, g2.find_creature(action.creature.uid), g2.find_creature(action.other.uid))
|
||||||
_:
|
_:
|
||||||
@@ -269,10 +257,6 @@ static func choose_action_greedy(game: Game, p: Game.Player) -> Dictionary:
|
|||||||
var t = pick_attack_target(game, p, cr)
|
var t = pick_attack_target(game, p, cr)
|
||||||
if t != null:
|
if t != null:
|
||||||
return {"type": "attack", "creature": cr, "target": t}
|
return {"type": "attack", "creature": cr, "target": t}
|
||||||
if not game.enemies_of(p).is_empty():
|
|
||||||
var strongest = _best(p.board.filter(func(c): return not c.hidden and game.can_upgrade(p, c, "Doppelschlag")[0]), func(c): return c.power())
|
|
||||||
if strongest != null:
|
|
||||||
return {"type": "upgrade", "creature": strongest, "keyword": "Doppelschlag"}
|
|
||||||
if game.can_draw(p)[0]:
|
if game.can_draw(p)[0]:
|
||||||
return {"type": "draw"}
|
return {"type": "draw"}
|
||||||
return {"type": "end"}
|
return {"type": "end"}
|
||||||
@@ -307,8 +291,6 @@ static func apply_action(game: Game, p: Game.Player, action: Dictionary) -> void
|
|||||||
game.draw(p)
|
game.draw(p)
|
||||||
"reveal":
|
"reveal":
|
||||||
game.reveal(p, action.creature)
|
game.reveal(p, action.creature)
|
||||||
"upgrade":
|
|
||||||
game.upgrade(p, action.creature, action.keyword)
|
|
||||||
"attack":
|
"attack":
|
||||||
game.attack(action.creature, action.target)
|
game.attack(action.creature, action.target)
|
||||||
"fuse":
|
"fuse":
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const STAGES := [
|
|||||||
{"name": "Nyx", "title": "Sturmruferin", "life": 26, "reward": 200, "perk": "extra_mana"},
|
{"name": "Nyx", "title": "Sturmruferin", "life": 26, "reward": 200, "perk": "extra_mana"},
|
||||||
{"name": "Ignis", "title": "Inferno-Priester", "life": 26, "reward": 250, "perk": "element_doppelt"},
|
{"name": "Ignis", "title": "Inferno-Priester", "life": 26, "reward": 250, "perk": "element_doppelt"},
|
||||||
{"name": "Thalassa", "title": "Tiefseekönigin", "life": 26, "reward": 250, "perk": "alle_flink"},
|
{"name": "Thalassa", "title": "Tiefseekönigin", "life": 26, "reward": 250, "perk": "alle_flink"},
|
||||||
{"name": "Zephyra", "title": "Blitzkaiserin", "life": 28, "reward": 300, "perk": "verbessern_billig"},
|
{"name": "Zephyra", "title": "Blitzkaiserin", "life": 28, "reward": 300, "perk": "alle_flink"},
|
||||||
{"name": "Archimagier", "title": "Meister der Elemente", "life": 28, "reward": 500, "perk": "start_energie"},
|
{"name": "Archimagier", "title": "Meister der Elemente", "life": 28, "reward": 500, "perk": "start_energie"},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ const STAGES2 := [
|
|||||||
{"name": "Fenn", "title": "Flammenzähmer", "life": 22, "reward": 300, "perks": ["feuer_angriff", "start_energie"]},
|
{"name": "Fenn", "title": "Flammenzähmer", "life": 22, "reward": 300, "perks": ["feuer_angriff", "start_energie"]},
|
||||||
{"name": "Mara", "title": "Herrin der Gezeiten", "life": 22, "reward": 300, "perks": ["leben_35", "extra_mana"]},
|
{"name": "Mara", "title": "Herrin der Gezeiten", "life": 22, "reward": 300, "perks": ["leben_35", "extra_mana"]},
|
||||||
{"name": "Borin", "title": "Fels der Ewigkeit", "life": 24, "reward": 350, "perks": ["wesen_angriff", "alle_flink"]},
|
{"name": "Borin", "title": "Fels der Ewigkeit", "life": 24, "reward": 350, "perks": ["wesen_angriff", "alle_flink"]},
|
||||||
{"name": "Liska", "title": "Sturmläuferin", "life": 24, "reward": 350, "perks": ["ziehen_gratis", "verbessern_billig"]},
|
{"name": "Liska", "title": "Sturmläuferin", "life": 24, "reward": 350, "perks": ["ziehen_gratis", "wesen_angriff"]},
|
||||||
{"name": "Vulkar", "title": "Aschekönig", "life": 24, "reward": 400, "perks": ["wesen_leben", "feuer_angriff"]},
|
{"name": "Vulkar", "title": "Aschekönig", "life": 24, "reward": 400, "perks": ["wesen_leben", "feuer_angriff"]},
|
||||||
{"name": "Nyx", "title": "Gewitterruferin", "life": 26, "reward": 400, "perks": ["extra_mana", "ziehen_gratis"]},
|
{"name": "Nyx", "title": "Gewitterruferin", "life": 26, "reward": 400, "perks": ["extra_mana", "ziehen_gratis"]},
|
||||||
{"name": "Ignis", "title": "Inferno-Erzpriester", "life": 26, "reward": 450, "perks": ["element_doppelt", "wesen_leben"]},
|
{"name": "Ignis", "title": "Inferno-Erzpriester", "life": 26, "reward": 450, "perks": ["element_doppelt", "wesen_leben"]},
|
||||||
{"name": "Thalassa", "title": "Kaiserin der Tiefe", "life": 26, "reward": 450, "perks": ["alle_flink", "leben_35"]},
|
{"name": "Thalassa", "title": "Kaiserin der Tiefe", "life": 26, "reward": 450, "perks": ["alle_flink", "leben_35"]},
|
||||||
{"name": "Zephyra", "title": "Blitzkaiserin", "life": 28, "reward": 500, "perks": ["verbessern_billig", "start_energie"]},
|
{"name": "Zephyra", "title": "Blitzkaiserin", "life": 28, "reward": 500, "perks": ["alle_flink", "start_energie"]},
|
||||||
{"name": "Archimagier", "title": "Herr aller Elemente", "life": 28, "reward": 800, "perks": ["start_energie", "element_doppelt"]},
|
{"name": "Archimagier", "title": "Herr aller Elemente", "life": 28, "reward": 800, "perks": ["start_energie", "element_doppelt"]},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -36,12 +36,12 @@ const STAGES3 := [
|
|||||||
{"name": "Fenn", "title": "Aschefürst", "life": 20, "reward": 600, "perks": ["feuer_angriff", "start_energie", "wesen_leben"]},
|
{"name": "Fenn", "title": "Aschefürst", "life": 20, "reward": 600, "perks": ["feuer_angriff", "start_energie", "wesen_leben"]},
|
||||||
{"name": "Mara", "title": "Sturmflut", "life": 20, "reward": 600, "perks": ["leben_35", "extra_mana", "alle_flink"]},
|
{"name": "Mara", "title": "Sturmflut", "life": 20, "reward": 600, "perks": ["leben_35", "extra_mana", "alle_flink"]},
|
||||||
{"name": "Borin", "title": "Berg selbst", "life": 22, "reward": 700, "perks": ["wesen_angriff", "alle_flink", "leben_35"]},
|
{"name": "Borin", "title": "Berg selbst", "life": 22, "reward": 700, "perks": ["wesen_angriff", "alle_flink", "leben_35"]},
|
||||||
{"name": "Liska", "title": "Windbraut", "life": 22, "reward": 700, "perks": ["ziehen_gratis", "verbessern_billig", "extra_mana"]},
|
{"name": "Liska", "title": "Windbraut", "life": 22, "reward": 700, "perks": ["ziehen_gratis", "wesen_angriff", "extra_mana"]},
|
||||||
{"name": "Vulkar", "title": "Magmakaiser", "life": 22, "reward": 800, "perks": ["wesen_leben", "feuer_angriff", "wesen_angriff"]},
|
{"name": "Vulkar", "title": "Magmakaiser", "life": 22, "reward": 800, "perks": ["wesen_leben", "feuer_angriff", "wesen_angriff"]},
|
||||||
{"name": "Nyx", "title": "Orkanmutter", "life": 24, "reward": 800, "perks": ["extra_mana", "ziehen_gratis", "element_doppelt"]},
|
{"name": "Nyx", "title": "Orkanmutter", "life": 24, "reward": 800, "perks": ["extra_mana", "ziehen_gratis", "element_doppelt"]},
|
||||||
{"name": "Ignis", "title": "Sonnenpriester", "life": 24, "reward": 900, "perks": ["element_doppelt", "wesen_leben", "start_energie"]},
|
{"name": "Ignis", "title": "Sonnenpriester", "life": 24, "reward": 900, "perks": ["element_doppelt", "wesen_leben", "start_energie"]},
|
||||||
{"name": "Thalassa", "title": "Herz der Tiefe", "life": 24, "reward": 900, "perks": ["alle_flink", "leben_35", "element_doppelt"]},
|
{"name": "Thalassa", "title": "Herz der Tiefe", "life": 24, "reward": 900, "perks": ["alle_flink", "leben_35", "element_doppelt"]},
|
||||||
{"name": "Zephyra", "title": "Gewitterkaiserin", "life": 26, "reward": 1000, "perks": ["verbessern_billig", "start_energie", "wesen_leben"]},
|
{"name": "Zephyra", "title": "Gewitterkaiserin", "life": 26, "reward": 1000, "perks": ["alle_flink", "start_energie", "wesen_leben"]},
|
||||||
{"name": "Archimagier", "title": "Ewiger Meister", "life": 26, "reward": 1200, "perks": ["start_energie", "element_doppelt", "leben_35"]},
|
{"name": "Archimagier", "title": "Ewiger Meister", "life": 26, "reward": 1200, "perks": ["start_energie", "element_doppelt", "leben_35"]},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -34,10 +34,9 @@ const RARITY_COLORS := {
|
|||||||
"Ultra Rare": Color("f0aa28"),
|
"Ultra Rare": Color("f0aa28"),
|
||||||
"Ultimate Rare": Color("ff5a8c"),
|
"Ultimate Rare": Color("ff5a8c"),
|
||||||
}
|
}
|
||||||
# Eigenschaften haben Karten nicht von Haus aus; sie werden für Mana dazugekauft (Game.upgrade)
|
# Eigenschaften haben Karten nicht von Haus aus; sie kommen allein vom Elementpaar der eigenen
|
||||||
# oder kommen vom Elementpaar der eigenen Seite (ELEMENT_KEYWORD, siehe Game.Creature.has).
|
# Seite (ELEMENT_KEYWORD, siehe Game.Creature.has).
|
||||||
const KEYWORDS: Array[String] = ["Spott", "Flink", "Regeneration", "Doppelschlag"]
|
const KEYWORDS: Array[String] = ["Spott", "Flink", "Regeneration", "Doppelschlag"]
|
||||||
const KEYWORD_COST := {"Spott": 2, "Flink": 2, "Regeneration": 3, "Doppelschlag": 4}
|
|
||||||
const KEYWORD_TEXT := {
|
const KEYWORD_TEXT := {
|
||||||
"Spott": "Muss zuerst angegriffen werden",
|
"Spott": "Muss zuerst angegriffen werden",
|
||||||
"Flink": "Greift sofort an",
|
"Flink": "Greift sofort an",
|
||||||
@@ -158,8 +157,8 @@ class Card extends RefCounted:
|
|||||||
var elements: Array
|
var elements: Array
|
||||||
var attack: int = 0
|
var attack: int = 0
|
||||||
var health: int = 0
|
var health: int = 0
|
||||||
var cost: int = 0 # abgeleitet: (2 · Angriff + Leben) / 3, abgerundet, mindestens 1
|
var cost: int = 0 # abgeleitet: (2 · Angriff + Leben) / 3, abgerundet
|
||||||
var keywords: Array = [] # dazugekauft, je Exemplar
|
var keywords: Array = [] # feste Eigenschaften der Karte (Elementpaare geben ihre zur Laufzeit)
|
||||||
var fusions := 0 # wie oft verschmolzen (Verschmelzen kostet 1, 2, 4, 8 …)
|
var fusions := 0 # wie oft verschmolzen (Verschmelzen kostet 1, 2, 4, 8 …)
|
||||||
var look := -1 # Seltenheitsstufe verschmolzener Karten (die höhere der Eltern); -1: aus der Sammlung des Besitzers
|
var look := -1 # Seltenheitsstufe verschmolzener Karten (die höhere der Eltern); -1: aus der Sammlung des Besitzers
|
||||||
|
|
||||||
@@ -211,7 +210,7 @@ class Card extends RefCounted:
|
|||||||
func fusion_cost() -> int:
|
func fusion_cost() -> int:
|
||||||
return 1 << fusions
|
return 1 << fusions
|
||||||
|
|
||||||
## Kartentext: die gekauften Eigenschaften.
|
## Kartentext: die festen Eigenschaften.
|
||||||
func text() -> String:
|
func text() -> String:
|
||||||
if keywords.is_empty():
|
if keywords.is_empty():
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class_name Game
|
class_name Game
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
## Spielregeln: Duell, Mana je Zug, Ziehen aus dem gemeinsamen Stapel, verdeckte Wesen,
|
## Spielregeln: Duell, Mana je Zug, Ziehen aus dem gemeinsamen Stapel, verdeckte Wesen,
|
||||||
## Elementboni, Verschmelzen und Verbessern.
|
## Elementboni und Verschmelzen.
|
||||||
|
|
||||||
const MAX_BOARD := 5
|
const MAX_BOARD := 5
|
||||||
const MAX_HAND := 5
|
const MAX_HAND := 5
|
||||||
@@ -21,7 +21,6 @@ const PERKS := {
|
|||||||
"extra_mana": "Bekommt jeden Zug 1 Mana mehr",
|
"extra_mana": "Bekommt jeden Zug 1 Mana mehr",
|
||||||
"leben_35": "Beginnt mit 5 Leben mehr",
|
"leben_35": "Beginnt mit 5 Leben mehr",
|
||||||
"alle_flink": "Wesen greifen sofort an",
|
"alle_flink": "Wesen greifen sofort an",
|
||||||
"verbessern_billig": "Eigenschaften kosten 1 weniger",
|
|
||||||
"start_energie": "Hat im ersten Zug 2 Mana mehr",
|
"start_energie": "Hat im ersten Zug 2 Mana mehr",
|
||||||
"element_doppelt": "Elementbonus zählt doppelt",
|
"element_doppelt": "Elementbonus zählt doppelt",
|
||||||
}
|
}
|
||||||
@@ -65,8 +64,8 @@ class Creature extends RefCounted:
|
|||||||
func owner():
|
func owner():
|
||||||
return owner_ref.get_ref()
|
return owner_ref.get_ref()
|
||||||
|
|
||||||
## Eigenschaft: gekauft, als Sondereffekt des Elementpaars der eigenen Seite oder als
|
## Eigenschaft: als Sondereffekt des Elementpaars der eigenen Seite, aus der Karte
|
||||||
## Boss-Merkmal. Verdeckte Wesen haben keine.
|
## (verschmolzene erben) oder als Boss-Merkmal. Verdeckte Wesen haben keine.
|
||||||
func has(kw: String) -> bool:
|
func has(kw: String) -> bool:
|
||||||
if hidden:
|
if hidden:
|
||||||
return false
|
return false
|
||||||
@@ -322,12 +321,6 @@ func draw_cost(p: Player) -> int:
|
|||||||
return 0 if "ziehen_gratis" in p.perks else DRAW_COST
|
return 0 if "ziehen_gratis" in p.perks else DRAW_COST
|
||||||
|
|
||||||
|
|
||||||
## Preis einer Eigenschaft für p.
|
|
||||||
func upgrade_cost(p: Player, keyword: String) -> int:
|
|
||||||
var c := int(Cards.KEYWORD_COST[keyword])
|
|
||||||
return maxi(1, c - 1) if "verbessern_billig" in p.perks else c
|
|
||||||
|
|
||||||
|
|
||||||
func draw(p: Player) -> String:
|
func draw(p: Player) -> String:
|
||||||
var ok := can_draw(p)
|
var ok := can_draw(p)
|
||||||
if not ok[0]:
|
if not ok[0]:
|
||||||
@@ -455,43 +448,6 @@ func _flip(cr: Creature) -> void:
|
|||||||
apply_auras()
|
apply_auras()
|
||||||
|
|
||||||
|
|
||||||
# --- Verbessern: Eigenschaften für Mana -------------------------------------
|
|
||||||
|
|
||||||
## target: eigenes Wesen auf dem Brett oder eigene Handkarte.
|
|
||||||
func can_upgrade(p: Player, target, keyword: String) -> Array:
|
|
||||||
if winner != "" or p != current():
|
|
||||||
return [false, "Nicht dein Zug"]
|
|
||||||
if not Cards.KEYWORD_COST.has(keyword):
|
|
||||||
return [false, "Unbekannte Eigenschaft"]
|
|
||||||
var card: Cards.Card = target.card if target is Creature else target
|
|
||||||
if target is Creature:
|
|
||||||
if not (target in p.board):
|
|
||||||
return [false, "Nur eigene Wesen"]
|
|
||||||
elif not (target in p.hand):
|
|
||||||
return [false, "Nicht auf der Hand"]
|
|
||||||
if keyword in card.keywords:
|
|
||||||
return [false, "%s hat schon %s" % [card.name, keyword]]
|
|
||||||
if p.energy < upgrade_cost(p, keyword):
|
|
||||||
return [false, "Nicht genug Mana"]
|
|
||||||
return [true, ""]
|
|
||||||
|
|
||||||
|
|
||||||
func upgrade(p: Player, target, keyword: String) -> String:
|
|
||||||
var ok := can_upgrade(p, target, keyword)
|
|
||||||
if not ok[0]:
|
|
||||||
return ok[1]
|
|
||||||
p.energy -= upgrade_cost(p, keyword)
|
|
||||||
var card: Cards.Card = (target.card if target is Creature else target).copy() # Kopien (KI) teilen sich Karten
|
|
||||||
card.keywords.append(keyword)
|
|
||||||
if target is Creature:
|
|
||||||
target.card = card
|
|
||||||
else:
|
|
||||||
p.hand[p.hand.find(target)] = card
|
|
||||||
say("%s bekommt %s." % [card.name, keyword])
|
|
||||||
emit({"type": "upgrade", "player": players.find(p), "keyword": keyword, "uid": target.uid if target is Creature else -1})
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
# --- Verschmelzen ------------------------------------------------------------
|
# --- Verschmelzen ------------------------------------------------------------
|
||||||
|
|
||||||
static func card_of(x) -> Cards.Card:
|
static func card_of(x) -> Cards.Card:
|
||||||
|
|||||||
@@ -398,12 +398,6 @@ func _apply(id: int, action: Dictionary) -> void:
|
|||||||
var cr := game.find_creature(int(action.get("uid", -1)))
|
var cr := game.find_creature(int(action.get("uid", -1)))
|
||||||
if cr != null:
|
if cr != null:
|
||||||
game.reveal(p, cr)
|
game.reveal(p, cr)
|
||||||
"upgrade":
|
|
||||||
var cr := game.find_creature(int(action.get("uid", -1)))
|
|
||||||
var hi := int(action.get("hand", -1))
|
|
||||||
var target = cr if cr != null else (p.hand[hi] if hi >= 0 and hi < p.hand.size() else null)
|
|
||||||
if target != null:
|
|
||||||
game.upgrade(p, target, str(action.get("keyword", "")))
|
|
||||||
"attack":
|
"attack":
|
||||||
var cr := game.find_creature(int(action.get("uid", -1)))
|
var cr := game.find_creature(int(action.get("uid", -1)))
|
||||||
var t = resolve_ref(action.get("target"))
|
var t = resolve_ref(action.get("target"))
|
||||||
|
|||||||
@@ -40,10 +40,6 @@ func play_events(evs: Array, centers: Dictionary) -> void:
|
|||||||
"reveal":
|
"reveal":
|
||||||
sfx("combo")
|
sfx("combo")
|
||||||
popup(tr("Aufgedeckt!"), Style.GOLD, screen.target_center({"kind": "creature", "uid": ev.uid}), 0.0)
|
popup(tr("Aufgedeckt!"), Style.GOLD, screen.target_center({"kind": "creature", "uid": ev.uid}), 0.0)
|
||||||
"upgrade":
|
|
||||||
sfx("heal")
|
|
||||||
if ev.uid >= 0:
|
|
||||||
popup("+" + tr(ev.keyword), Style.GREEN, screen.target_center({"kind": "creature", "uid": ev.uid}), 0.0)
|
|
||||||
"fuse":
|
"fuse":
|
||||||
sfx("combo")
|
sfx("combo")
|
||||||
popup("Verschmolzen!", Style.PURPLE, screen.target_center({"kind": "player", "index": ev.player}), 0.0)
|
popup("Verschmolzen!", Style.PURPLE, screen.target_center({"kind": "player", "index": ev.player}), 0.0)
|
||||||
|
|||||||
@@ -661,21 +661,6 @@ func do_reveal(cr: Game.Creature) -> void:
|
|||||||
after_action()
|
after_action()
|
||||||
|
|
||||||
|
|
||||||
## Kauft eine Eigenschaft für eine Handkarte oder ein eigenes Wesen.
|
|
||||||
func do_upgrade(target, keyword: String) -> void:
|
|
||||||
clear_undo()
|
|
||||||
if is_client():
|
|
||||||
if target is Game.Creature:
|
|
||||||
net.send_action({"type": "upgrade", "uid": target.uid, "keyword": keyword})
|
|
||||||
else:
|
|
||||||
net.send_action({"type": "upgrade", "hand": perspective.hand.find(target), "keyword": keyword})
|
|
||||||
return
|
|
||||||
var err := game.upgrade(perspective, target, keyword)
|
|
||||||
if err != "":
|
|
||||||
say(tr(err))
|
|
||||||
after_action()
|
|
||||||
|
|
||||||
|
|
||||||
## Kartenmenü neben einer Handkarte oder einem eigenen Wesen. full: auch verschmelzen anbieten
|
## Kartenmenü neben einer Handkarte oder einem eigenen Wesen. full: auch verschmelzen anbieten
|
||||||
## (Gamepad, das nicht ziehen kann).
|
## (Gamepad, das nicht ziehen kann).
|
||||||
func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
||||||
@@ -700,13 +685,9 @@ func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
|||||||
fuse_first = target
|
fuse_first = target
|
||||||
say(tr("Ziel wählen"))
|
say(tr("Ziel wählen"))
|
||||||
refresh(), not partners.is_empty()])
|
refresh(), not partners.is_empty()])
|
||||||
for kw in Cards.KEYWORDS:
|
if entries.is_empty(): # offenes eigenes Wesen per Maus: nichts zu wählen
|
||||||
var k: String = kw
|
close_menu()
|
||||||
var ok := game.can_upgrade(perspective, target, k)
|
return
|
||||||
var text := "%s ◆ %d" % [tr(k), game.upgrade_cost(perspective, k)]
|
|
||||||
if k in card.keywords:
|
|
||||||
text = "%s ✓" % tr(k)
|
|
||||||
entries.append([text, func(): do_upgrade(target, k), ok[0]])
|
|
||||||
var w := 230.0
|
var w := 230.0
|
||||||
var h := 18.0 + entries.size() * 44 + 34
|
var h := 18.0 + entries.size() * 44 + 34
|
||||||
var x := clampf(anchor.end.x + 8, 10, 1270 - w)
|
var x := clampf(anchor.end.x + 8, 10, 1270 - w)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func _draw() -> void:
|
|||||||
var by := Style.fs(fs) + 13
|
var by := Style.fs(fs) + 13
|
||||||
draw_rect(Rect2(size.x - bw - 4, by, bw, 16), Color(0.05, 0.05, 0.08, 0.75))
|
draw_rect(Rect2(size.x - bw - 4, by, bw, 16), Color(0.05, 0.05, 0.08, 0.75))
|
||||||
Style.text(self, badge, 10, Style.GREEN if creature.aura > 0 else Style.RED, Vector2(size.x - 4 - bw / 2, by + 8), "center", true)
|
Style.text(self, badge, 10, Style.GREEN if creature.aura > 0 else Style.RED, Vector2(size.x - 4 - bw / 2, by + 8), "center", true)
|
||||||
# Eigenschaften als Symbole: gekaufte weiß, die vom Elementpaar in der Elementfarbe
|
# Eigenschaften als Symbole in der Farbe des Elements, das sie gibt
|
||||||
var icon := 7.0 if small else 9.0
|
var icon := 7.0 if small else 9.0
|
||||||
var kws := creature.keywords()
|
var kws := creature.keywords()
|
||||||
var avail := size.x - (bw + 8 if badge != "" else 0.0)
|
var avail := size.x - (bw + 8 if badge != "" else 0.0)
|
||||||
@@ -89,7 +89,6 @@ func _draw() -> void:
|
|||||||
var y := Style.fs(fs) + 14 + icon
|
var y := Style.fs(fs) + 14 + icon
|
||||||
for k in kws:
|
for k in kws:
|
||||||
var col := Color("f2f2f6")
|
var col := Color("f2f2f6")
|
||||||
if not (k in creature.card.keywords):
|
|
||||||
for e in creature.card.elements:
|
for e in creature.card.elements:
|
||||||
if Cards.ELEMENT_KEYWORD.get(e, "") == k:
|
if Cards.ELEMENT_KEYWORD.get(e, "") == k:
|
||||||
col = Style.lighter(Cards.ELEMENT_COLORS[e], 0.45)
|
col = Style.lighter(Cards.ELEMENT_COLORS[e], 0.45)
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ func _initialize() -> void:
|
|||||||
test_verdeckt()
|
test_verdeckt()
|
||||||
test_elementboni()
|
test_elementboni()
|
||||||
test_spott_und_regeneration()
|
test_spott_und_regeneration()
|
||||||
test_verbessern()
|
|
||||||
test_verschmelzen()
|
test_verschmelzen()
|
||||||
test_limits()
|
test_limits()
|
||||||
test_gleiche_regeln()
|
test_gleiche_regeln()
|
||||||
@@ -383,33 +382,6 @@ func test_spott_und_regeneration() -> void:
|
|||||||
check(g2.events.any(func(e): return e.type == "heal" and e.uid == reg.uid), "Ereignis fürs Heilen")
|
check(g2.events.any(func(e): return e.type == "heal" and e.uid == reg.uid), "Ereignis fürs Heilen")
|
||||||
|
|
||||||
|
|
||||||
## Verbessern: Eigenschaften für Mana, je Exemplar, auf Hand und Brett.
|
|
||||||
func test_verbessern() -> void:
|
|
||||||
var s := setup(["f_wolf"])
|
|
||||||
var g: Game = s[0]
|
|
||||||
var h: Game.Player = s[1]
|
|
||||||
var o: Game.Player = s[2]
|
|
||||||
h.energy = 20
|
|
||||||
var card: Cards.Card = h.hand[0]
|
|
||||||
check(g.upgrade(h, card, "Spott") == "" and h.energy == 20 - 2 and "Spott" in h.hand[0].keywords, "Spott für die Handkarte gekauft")
|
|
||||||
check(h.hand[0] != card and Cards.by_id("f_wolf").keywords.is_empty(), "eigenes Exemplar, der Pool bleibt sauber")
|
|
||||||
check(g.upgrade(h, h.hand[0], "Spott") != "", "nicht doppelt")
|
|
||||||
g.play_card(h, h.hand[0])
|
|
||||||
var wolf: Game.Creature = h.board[0]
|
|
||||||
var after_play := h.energy
|
|
||||||
check(wolf.has("Spott") and not wolf.can_attack(), "mit Spott gelegt, greift noch nicht an")
|
|
||||||
check(g.upgrade(h, wolf, "Flink") == "" and wolf.can_attack() and h.energy == after_play - 2, "Flink: greift sofort an")
|
|
||||||
check(g.upgrade(h, wolf, "Doppelschlag") == "" and wolf.attacks_per_turn() == 2 and wolf.attacks_left() == 2 and h.energy == after_play - 6, "Doppelschlag: zwei Angriffe")
|
|
||||||
check(g.upgrade(h, wolf, "Regeneration") == "" and h.energy == after_play - 9, "Regeneration kostet 3")
|
|
||||||
check(wolf.card.text() == "Spott, Flink, Regeneration, Doppelschlag.", "Kartentext zeigt die Eigenschaften")
|
|
||||||
check(g.upgrade(h, wolf, "Doppelschlag") != "" and g.upgrade(h, put(o, "e_wall"), "Spott") != "" and g.upgrade(h, wolf, "Schild") != "", "nicht doppelt, nicht fremd, nicht unbekannt")
|
|
||||||
h.energy = 1
|
|
||||||
var k2 := put(h, "f_kobold")
|
|
||||||
check(g.upgrade(h, k2, "Spott") == "Nicht genug Mana", "zu teuer")
|
|
||||||
h.perks = ["verbessern_billig"]
|
|
||||||
check(g.upgrade(h, k2, "Spott") == "" and h.energy == 0, "Merkmal: Eigenschaften kosten 1 weniger")
|
|
||||||
|
|
||||||
|
|
||||||
## Verschmelzen: besserer Wert plus ein Drittel des schwächeren, Eigenschaften beider, Kosten 1, 2, 4, 8 …; auch auf der Hand.
|
## Verschmelzen: besserer Wert plus ein Drittel des schwächeren, Eigenschaften beider, Kosten 1, 2, 4, 8 …; auch auf der Hand.
|
||||||
func test_verschmelzen() -> void:
|
func test_verschmelzen() -> void:
|
||||||
var s := setup()
|
var s := setup()
|
||||||
@@ -612,8 +584,6 @@ func test_kopie() -> void:
|
|||||||
check(g2.players[1].life == 11 and o.life == 15, "Kopie verändert das Original nicht")
|
check(g2.players[1].life == 11 and o.life == 15, "Kopie verändert das Original nicht")
|
||||||
g2.play_card(g2.players[0], g2.players[0].hand[0])
|
g2.play_card(g2.players[0], g2.players[0].hand[0])
|
||||||
check(h.hand.size() == 1 and g2.players[0].hand.is_empty(), "Hand der Kopie unabhängig")
|
check(h.hand.size() == 1 and g2.players[0].hand.is_empty(), "Hand der Kopie unabhängig")
|
||||||
g2.upgrade(g2.players[0], a2, "Spott")
|
|
||||||
check(not a.has("Spott") and a.card.keywords.is_empty() and a2.has("Spott"), "Verbessern in der Kopie lässt das Original unberührt")
|
|
||||||
check(g2.log.is_empty() and g2.events.is_empty(), "Kopie schreibt keinen Verlauf")
|
check(g2.log.is_empty() and g2.events.is_empty(), "Kopie schreibt keinen Verlauf")
|
||||||
|
|
||||||
|
|
||||||
@@ -781,21 +751,22 @@ func test_merkmale() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func test_ki_zwei_schritte() -> void:
|
func test_ki_zwei_schritte() -> void:
|
||||||
# Gegner hat 5 Leben und kein Wesen; der Wüstenkönig (5 Angriff) liegt frisch auf dem Brett:
|
# Gegner hat 5 Leben und einen Kobold davor; der Wüstenkönig (5 Angriff) und der Wolf sind bereit:
|
||||||
# erst Flink kaufen, dann mit ihm töten.
|
# erst den Kobold mit dem Wolf wegräumen, dann mit dem König töten.
|
||||||
var s := setup()
|
var s := setup()
|
||||||
var g: Game = s[0]
|
var g: Game = s[0]
|
||||||
var h: Game.Player = s[1]
|
var h: Game.Player = s[1]
|
||||||
var o: Game.Player = s[2]
|
var o: Game.Player = s[2]
|
||||||
h.turns = 2
|
h.turns = 2
|
||||||
o.life = 5
|
o.life = 5
|
||||||
var king := put(h, "el_koenig", false)
|
var king := put(h, "el_koenig")
|
||||||
put(h, "f_wolf", false)
|
var wolf := put(h, "f_wolf")
|
||||||
|
var blocker := put(o, "f_kobold")
|
||||||
var act := AI.choose_action(g, h)
|
var act := AI.choose_action(g, h)
|
||||||
check(act.type == "upgrade" and act.creature == king and act.keyword == "Flink", "KI kauft zuerst Flink für den König")
|
check(act.type == "attack" and act.creature == wolf and act.target == blocker, "KI räumt zuerst mit dem Wolf den Kobold weg")
|
||||||
AI.apply_action(g, h, act)
|
AI.apply_action(g, h, act)
|
||||||
var act2 := AI.choose_action(g, h)
|
var act2 := AI.choose_action(g, h)
|
||||||
check(act2.type == "attack" and act2.target == o, "dann tödlicher Angriff")
|
check(act2.type == "attack" and act2.creature == king and act2.target == o, "dann tödlicher Angriff mit dem König")
|
||||||
AI.apply_action(g, h, act2)
|
AI.apply_action(g, h, act2)
|
||||||
check(g.winner == "human", "Zwei-Schritt-Plan gewinnt")
|
check(g.winner == "human", "Zwei-Schritt-Plan gewinnt")
|
||||||
|
|
||||||
@@ -805,7 +776,6 @@ func test_snapshot() -> void:
|
|||||||
var g: Game = s[0]
|
var g: Game = s[0]
|
||||||
var h: Game.Player = s[1]
|
var h: Game.Player = s[1]
|
||||||
var o: Game.Player = s[2]
|
var o: Game.Player = s[2]
|
||||||
g.upgrade(h, h.hand[0], "Spott")
|
|
||||||
g.play_card(h, h.hand[0])
|
g.play_card(h, h.hand[0])
|
||||||
var troll := put(o, "e_troll")
|
var troll := put(o, "e_troll")
|
||||||
troll.attacks_done = 1
|
troll.attacks_done = 1
|
||||||
@@ -822,7 +792,7 @@ func test_snapshot() -> void:
|
|||||||
check(g2.players[hi].hand.size() == 1 and g2.players[hi].hand[0].id == h.hand[0].id, "eigene Hand sichtbar")
|
check(g2.players[hi].hand.size() == 1 and g2.players[hi].hand[0].id == h.hand[0].id, "eigene Hand sichtbar")
|
||||||
check(g2.players[oi].hand.size() == 1 and g2.players[oi].hand[0].id != "w_frost", "fremde Hand nur als Anzahl")
|
check(g2.players[oi].hand.size() == 1 and g2.players[oi].hand[0].id != "w_frost", "fremde Hand nur als Anzahl")
|
||||||
var cr2 := g2.find_creature(h.board[0].uid)
|
var cr2 := g2.find_creature(h.board[0].uid)
|
||||||
check(cr2 != null and cr2.card.id == "f_wolf" and cr2.owner() == g2.players[hi] and cr2.has("Spott") and not cr2.ready, "Wesen mit Besitzer, Zustand und gekaufter Eigenschaft")
|
check(cr2 != null and cr2.card.id == "f_wolf" and cr2.owner() == g2.players[hi] and not cr2.ready, "Wesen mit Besitzer und Zustand")
|
||||||
var troll2 := g2.find_creature(troll.uid)
|
var troll2 := g2.find_creature(troll.uid)
|
||||||
check(troll2.ready and troll2.attacks_done == 1, "Bereitschaft und Angriffe reisen mit")
|
check(troll2.ready and troll2.attacks_done == 1, "Bereitschaft und Angriffe reisen mit")
|
||||||
check(g2.events.size() == g.events.size() and g2.events.back().type == g.events.back().type, "Ereignisse übertragen")
|
check(g2.events.size() == g.events.size() and g2.events.back().type == g.events.back().type, "Ereignisse übertragen")
|
||||||
@@ -871,12 +841,12 @@ func test_ki_zeitbudget() -> void:
|
|||||||
var act := AI.choose_action(g, h)
|
var act := AI.choose_action(g, h)
|
||||||
var ms := Time.get_ticks_msec() - t0
|
var ms := Time.get_ticks_msec() - t0
|
||||||
AI.time_budget_ms = 150
|
AI.time_budget_ms = 150
|
||||||
check(act.type in ["play", "attack", "upgrade", "fuse", "draw", "end"] and ms < 80, "Zeitbudget greift (%d ms)" % ms)
|
check(act.type in ["play", "attack", "fuse", "draw", "end"] and ms < 80, "Zeitbudget greift (%d ms)" % ms)
|
||||||
var v := Profile.from_dict(JSON.parse_string(JSON.stringify(Profile.starter().to_dict())))
|
var v := Profile.from_dict(JSON.parse_string(JSON.stringify(Profile.starter().to_dict())))
|
||||||
check(v.settings.has("big_font") and v.coins == Profile.START_COINS, "Starterstand gelesen")
|
check(v.settings.has("big_font") and v.coins == Profile.START_COINS, "Starterstand gelesen")
|
||||||
|
|
||||||
|
|
||||||
## Englisch: jede Karte hat einen englischen Namen, gekaufte Eigenschaften werden übersetzt.
|
## Englisch: jede Karte hat einen englischen Namen, Eigenschaften werden übersetzt.
|
||||||
func test_englische_karten() -> void:
|
func test_englische_karten() -> void:
|
||||||
check(Cards.all().all(func(c): return c.name_en != ""), "alle Karten mit englischem Namen")
|
check(Cards.all().all(func(c): return c.name_en != ""), "alle Karten mit englischem Namen")
|
||||||
var ritter := C("f_ritter")
|
var ritter := C("f_ritter")
|
||||||
|
|||||||
@@ -633,10 +633,10 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
|||||||
await frames(2)
|
await frames(2)
|
||||||
check(scr.element_view.text() == "Feuer 2 (Doppelschlag) · Wasser 1 · Erde 0 · Luft 1", "Zähler ohne das verdeckte Wesen, Feuerpaar: " + scr.element_view.text())
|
check(scr.element_view.text() == "Feuer 2 (Doppelschlag) · Wasser 1 · Erde 0 · Luft 1", "Zähler ohne das verdeckte Wesen, Feuerpaar: " + scr.element_view.text())
|
||||||
check(g.human.board[0].keywords() == ["Doppelschlag"] and scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 1, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken; der Kobold würde den Stapel voll machen")
|
check(g.human.board[0].keywords() == ["Doppelschlag"] and scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 1, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken; der Kobold würde den Stapel voll machen")
|
||||||
# Rechtsklick auf die Handkarte: Menü mit Verdeckt legen und Eigenschaften, ohne Verschmelzen
|
# Rechtsklick auf die Handkarte: Menü mit Verdeckt legen, ohne Verschmelzen
|
||||||
await hover(scr.hand_views[0])
|
await hover(scr.hand_views[0])
|
||||||
await right_click_at(scr.hand_views[0].get_global_rect().get_center())
|
await right_click_at(scr.hand_views[0].get_global_rect().get_center())
|
||||||
check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and find_button("Spott ◆ 2", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü")
|
check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü")
|
||||||
check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
|
check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
|
||||||
await shot("23_kartenmenue")
|
await shot("23_kartenmenue")
|
||||||
await right_click()
|
await right_click()
|
||||||
@@ -646,12 +646,10 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
|||||||
await right_click_at(scr.hand_views[0].get_global_rect().get_center(), MOUSE_BUTTON_MIDDLE)
|
await right_click_at(scr.hand_views[0].get_global_rect().get_center(), MOUSE_BUTTON_MIDDLE)
|
||||||
var mine_hidden: Game.Creature = g.human.board[3]
|
var mine_hidden: Game.Creature = g.human.board[3]
|
||||||
check(mine_hidden.hidden and g.human.energy == mana - 1 and scr.element_view.text().begins_with("Feuer 2"), "mittlere Maustaste legt verdeckt, zählt nicht")
|
check(mine_hidden.hidden and g.human.energy == mana - 1 and scr.element_view.text().begins_with("Feuer 2"), "mittlere Maustaste legt verdeckt, zählt nicht")
|
||||||
# Rechtsklick auf ein eigenes Wesen: Eigenschaften kaufen
|
# Rechtsklick auf ein eigenes offenes Wesen: nichts zu wählen, also kein Menü
|
||||||
await right_click_at(scr.own_views[0].get_global_rect().get_center())
|
await right_click_at(scr.own_views[0].get_global_rect().get_center())
|
||||||
check(scr.menu_layer.visible and find_button("Doppelschlag ◆ 4", scr.menu_layer) != null and find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "Wesen-Menü mit Eigenschaften, ohne Verschmelzen")
|
check(not scr.menu_layer.visible, "kein Menü ohne Einträge")
|
||||||
var wolf: Game.Creature = g.human.board[0]
|
var wolf: Game.Creature = g.human.board[0]
|
||||||
await click(find_button("Spott ◆ %d" % Cards.KEYWORD_COST["Spott"], scr.menu_layer))
|
|
||||||
check(wolf.has("Spott") and "Spott" in wolf.card.keywords and g.human.energy == mana - 1 - Cards.KEYWORD_COST["Spott"], "Spott gekauft")
|
|
||||||
while scr.own_views[3].busy:
|
while scr.own_views[3].busy:
|
||||||
await frames(1)
|
await frames(1)
|
||||||
await click(scr.own_views[3])
|
await click(scr.own_views[3])
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ Feuerwesen haben +1 Angriff,Fire creatures have +1 attack
|
|||||||
Wesen kommen mit +1 Leben,Creatures enter with +1 health
|
Wesen kommen mit +1 Leben,Creatures enter with +1 health
|
||||||
Bekommt jeden Zug 1 Mana mehr,Gets 1 extra mana each turn
|
Bekommt jeden Zug 1 Mana mehr,Gets 1 extra mana each turn
|
||||||
Ziehen kostet kein Mana,Drawing costs no mana
|
Ziehen kostet kein Mana,Drawing costs no mana
|
||||||
Eigenschaften kosten 1 weniger,Traits cost 1 less
|
|
||||||
Spott,Taunt
|
Spott,Taunt
|
||||||
Regeneration,Regeneration
|
Regeneration,Regeneration
|
||||||
Muss zuerst angegriffen werden,Must be attacked first
|
Muss zuerst angegriffen werden,Must be attacked first
|
||||||
|
|||||||
|
Binary file not shown.
Reference in New Issue
Block a user