update
Some checks failed
Tests / headless (push) Has been cancelled
Tests / fenster-schnell (push) Has been cancelled
Tests / fenster-voll (push) Has been cancelled
Tests / balance (push) Has been cancelled

This commit is contained in:
team3
2026-10-08 14:28:52 +02:00
parent 5bc8569717
commit c245c7af87
21 changed files with 496 additions and 347 deletions

View File

@@ -12,6 +12,8 @@ func check(cond: bool, msg: String) -> void:
else:
failed += 1
printerr("FEHLER: " + msg)
print("%d Prüfungen bestanden, Abbruch beim ersten Fehler" % passed)
quit(1)
func C(id: String) -> Cards.Card:
@@ -55,6 +57,7 @@ func _initialize() -> void:
test_kombos()
test_mana_und_kosten()
test_ziehen()
test_abwerfen()
test_wesen_und_angriff()
test_elementvorteil()
test_verdeckt()
@@ -129,64 +132,44 @@ func test_mana_und_kosten() -> void:
h.energy = 5
check(g.play_card(h, h.hand[0]) == "" and h.energy == 5 - C("f_wolf").cost, "Ausspielen kostet die Kartenkosten")
check(g.can_play(h, h.hand[0])[1] == "Nicht genug Mana", "Drache (5) zu teuer bei %d Mana" % h.energy)
check(Cards.fusion_value(4, 2) == 5 and Cards.fusion_value(8, 1) == 9 and Cards.fusion_value(2, 2) == 3 and Cards.fusion_value(0, 4) == 4 and Cards.fusion_value(8, 8) == 11, "Fusionswert: besserer Wert plus ein Drittel des schwächeren")
check(Cards.fusion_value(4, 2) == 5 and Cards.fusion_value(8, 1) == 9 and Cards.fusion_value(2, 2) == 3 and Cards.fusion_value(0, 4) == 5 and Cards.fusion_value(8, 8) == 11, "Fusionswert: besserer Wert plus ein Drittel des schwächeren, mindestens 1")
## Start: fünf im ersten Zug bezahlbare Karten, keine Wahl. Jeder Zug beginnt mit einer Karte;
## Ziehen: 2 Mana, eine weitere bezahlbare Karte vom Stapel.
## Start: fünf im ersten Zug bezahlbare Karten, drei offene auf dem Stapel. Ziehen: eine der drei
## wählen, eine neue rückt nach; das erste Ziehen im Zug ist frei, jedes weitere kostet 2 Mana.
func test_ziehen() -> void:
var r := RandomNumberGenerator.new()
r.seed = 1
var g := Game.new_battle(r)
check(g.deck.size() == Cards.all().size() * DeckGen.COPIES - 2 * Game.START_HAND, "Stapel: jede Karte dreimal, zehn schon ausgegeben")
check(g.deck.size() == Cards.all().size() * DeckGen.COPIES - 2 * (Game.START_HAND + Game.OFFER), "Stapel: jede Karte dreimal, Hände und Angebote schon ausgegeben")
for p in g.players:
check(p.hand.size() == Game.START_HAND, "Start: fünf Karten auf der Hand")
check(p.hand.all(func(c): return c.cost <= Game.START_MANA), "nur Karten, die im ersten Zug spielbar sind")
check(p.hand.all(func(c): return c != Cards.by_id(c.id)), "eigene Exemplare")
check(p.offer.size() == Game.OFFER and p.offer.all(func(c): return c.cost <= Game.START_MANA), "drei offene Karten auf dem Stapel, alle bezahlbar")
var first: Game.Player = g.current()
check(g.draw(first) == "Deine Hand ist voll", "fünf Handkarten sind das Limit")
first.hand.pop_back()
check(g.draw(first) == "Nicht genug Mana", "im ersten Zug reicht das Mana nicht zum Ziehen")
first.energy = 3
first.max_energy = 3
var mana := first.energy
var wanted: Cards.Card = first.offer[1]
var deck_before := g.deck.size()
check(g.draw(first) == "" and first.energy == mana - Game.DRAW_COST and first.hand.size() == Game.START_HAND, "gezogen: 2 Mana, eine Karte")
check(first.hand.back().cost <= first.max_energy and g.deck.size() == deck_before - 1, "bezahlbar; zu teure liegen wieder unten im Stapel")
first.energy = 0
check(g.draw_cost(first) == 0 and g.draw(first, 1) == "" and first.energy == Game.START_MANA and first.hand.back().id == wanted.id, "erstes Ziehen frei: die gewählte Karte kommt auf die Hand")
check(first.offer.size() == Game.OFFER and first.offer[1] != wanted and first.offer[1].cost <= first.max_energy and g.deck.size() == deck_before - 1, "eine bezahlbare neue Karte rückt an die Stelle")
first.hand.pop_back()
check(g.draw(first) == "Nicht genug Mana", "ohne Mana kein Ziehen")
check(g.draw_cost(first) == Game.DRAW_COST and g.draw(first) == "Nicht genug Mana", "zweites Ziehen kostet 2, im ersten Zug zu teuer")
first.energy = 3
check(g.draw(first, 0) == "" and first.energy == 3 - Game.DRAW_COST, "mit Mana: 2 bezahlt")
var second: Game.Player = g.enemy_of(first)
check(g.draw(second) == "Nicht dein Zug", "nur im eigenen Zug")
# Leerer Stapel wird neu gemischt
g.deck = []
first.hand.pop_back()
first.energy = 3
check(g.draw(first) == "" and g.deck.size() > 0 and g.log.any(func(l): return l.begins_with("Der Stapel wird neu gemischt")), "leerer Stapel: neu gemischt")
# Mit viel Mana tauchen auch teure Karten auf
first.max_energy = 20
first.energy = 20
var seen_expensive := false
for i in 12:
first.hand = []
first.energy = 20
g.draw(first)
if first.hand[0].cost > 5:
seen_expensive = true
check(seen_expensive, "mit 20 Mana kommen auch teure Karten")
# Zugbeginn: eine Karte gratis, solange die Hand nicht voll ist
var g5 := Game.new_battle(r)
var p1: Game.Player = g5.current()
var p2: Game.Player = g5.enemy_of(p1)
g5.end_turn()
check(p2.hand.size() == Game.START_HAND, "volle Hand: zu Zugbeginn kommt nichts")
p2.hand.pop_back()
p2.hand.pop_back()
g5.end_turn()
p1.hand.pop_back()
g5.end_turn()
check(p2.hand.size() == Game.START_HAND - 1 and p2.hand.back().cost <= p2.max_energy, "Zugbeginn: eine bezahlbare Karte gezogen")
g5.end_turn()
check(p1.hand.size() == Game.START_HAND, "auch der Startspieler zieht zu Zugbeginn")
# Nächster Zug: nichts kommt von selbst, das erste Ziehen ist wieder frei
var hand_now := first.hand.size()
g.end_turn()
g.end_turn()
check(g.current() == first and first.hand.size() == hand_now and g.draw_cost(first) == 0, "neuer Zug: keine Karte von selbst, das erste Ziehen wieder frei")
## Gelegte Wesen greifen erst im nächsten Zug an; kein Durchschlag, keine Heilung zu Zugbeginn.
@@ -243,6 +226,20 @@ func test_elementvorteil() -> void:
var t3 := put(s3[2], "w_tiefen")
s3[0].attack(w3, t3)
check(t3.health == 6 - 2 and w3.health == 2 - 5, "Feuer gegen Wasser: 1 weniger Schaden, der Konter macht 1 mehr")
# 0 Angriff: nur mit Elementvorteil geht etwas
var s4 := setup()
var funke := put(s4[1], "f_funke") # Feuer 0/2
var hauch := put(s4[2], "l_hauch") # Luft 0/2
var wall := put(s4[2], "e_wall") # Erde 0/4
check(s4[0].can_attack(funke, wall)[1] == "Glutfunke würde keinen Schaden machen", "0 Angriff ohne Vorteil: kein Angriff")
check(s4[0].attack(funke, hauch) == "" and hauch.health == 1 and funke.health == 2, "0 Angriff mit Vorteil: 1 Schaden, kein Konter")
var s5 := setup()
var hauch2 := put(s5[1], "l_hauch")
var funke2 := put(s5[2], "f_funke")
check(s5[0].attack(hauch2, funke2) == "Windhauch würde keinen Schaden machen" and funke2.health == 2, "im Nachteil nichts")
var wall2 := put(s5[2], "e_wall")
var geist5 := put(s5[1], "w_geist") # Wasser 2/2
check(s5[0].attack(geist5, wall2) == "" and wall2.health == 4 - 1 and geist5.health == 2 - 1, "Steinwall kontert mit Elementvorteil für 1")
var s2 := setup()
var g2: Game = s2[0]
var geist := put(s2[1], "w_geist") # Wasser 2/2
@@ -286,46 +283,52 @@ func test_verdeckt() -> void:
## Elementboni: zwei eigene offene Wesen eines Elements geben den Sondereffekt (Erde Spott, Luft
## Flink, Wasser Regeneration, Feuer Doppelschlag); drei auf dem Tisch geben dem Element +1/+1.
## Flink, Wasser Regeneration, Feuer Doppelschlag); je 10 Mana an ausgespielten Karten eines Elements geben
## den eigenen Wesen des Elements +1 Angriff. Jeder Spieler zählt für sich.
func test_elementboni() -> void:
var s := setup()
var s := setup(["f_asche", "f_ritter"])
var g: Game = s[0]
var h: Game.Player = s[1]
var o: Game.Player = s[2]
var fire := [put(h, "f_wolf"), put(h, "f_kobold")]
var bird := put(o, "l_wind") # Luft 1/1
g.apply_auras()
check(fire[0].aura == 0 and fire[1].aura == 0, "zwei reichen nicht für +1/+1")
check(fire[0].aura == 0 and int(h.played["Feuer"]) == 0, "nur gelegt, nicht ausgespielt: kein Bonus")
check(fire[0].has("Doppelschlag") and fire[0].attacks_per_turn() == 2 and fire[0].keywords() == ["Doppelschlag"], "zwei eigene Feuer: Doppelschlag für beide")
var hidden_fire := put(o, "f_asche", true, true)
g.apply_auras()
check(fire[0].aura == 0 and g.element_counts()["Feuer"] == 2 and not hidden_fire.has("Doppelschlag"), "verdeckte zählen nicht und bekommen nichts")
var third := put(o, "f_ritter") # Feuer beim Gegner zählt für den Tisch
g.apply_auras()
check(fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 3 and third.aura == 1, "drei Feuer auf dem Tisch: +1/+1 für alle Feuerwesen")
check(bird.aura == 0 and bird.health == 1, "kein Malus für das geschlagene Element")
check(not third.has("Doppelschlag"), "der Gegner hat nur ein offenes Feuer: kein Sondereffekt")
check(not hidden_fire.has("Doppelschlag") and o.element_count("Feuer") == 0, "verdeckte zählen nicht fürs Paar")
# Ausgespielte Karten zählen; je vier gibt es +1 Angriff für die eigenen Wesen des Elements
h.played["Feuer"] = 7
g.play_card(h, h.hand[0]) # Aschekrieger für 3: macht 10 Mana voll
check(int(h.played["Feuer"]) == 10 and fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 2 and h.board[2].aura == 1, "10 Mana Feuer ausgespielt: +1 Angriff für alle eigenen Feuerwesen, Leben bleibt")
check(bird.aura == 0 and bird.health == 1 and hidden_fire.aura == 0, "der Gegner zählt für sich: nichts")
g._flip(hidden_fire)
check(hidden_fire.aura == 1 and third.has("Doppelschlag") and hidden_fire.has("Doppelschlag"), "aufgedeckt zählt es sofort: Bonus und Paar")
fire[0].health = 1
o.board.erase(third)
o.board.erase(hidden_fire)
g.apply_auras()
check(fire[0].aura == 0 and fire[0].max_health == 2 and fire[0].health == 1 and fire[1].health == 1, "Bonus geht wieder: nur das Höchstleben sinkt, Leben wird höchstens gekappt")
check(hidden_fire.aura == 0 and int(o.played["Feuer"]) == 0, "aufgedeckt, aber nie ausgespielt gezählt: kein Bonus")
g.play_card(h, h.hand[0], true) # Ritter verdeckt
var secret: Game.Creature = h.board.back()
check(secret.hidden and secret.aura == 0 and int(h.played["Feuer"]) == 13, "verdeckt gelegt zählt als ausgespielt, der Bonus kommt erst offen")
g._flip(secret)
check(secret.aura == 1 and secret.power() == 5 and secret.health == 3, "aufgedeckt kommt der Bonus")
# Vorschau
check(g.forecast(h, C("f_asche")) == 1 and g.forecast(h, C("l_wind")) == 0, "Vorschau: Feuer würde den Stapel voll machen")
h.played["Luft"] = 9
check(g.forecast(h, C("l_wind")) == 1 and g.forecast(h, C("w_geist")) == 0, "Vorschau: der Windgeist macht 10 Mana Luft voll und bekäme +1")
# Merkmal: doppelt
h.perks = ["element_doppelt"]
put(h, "f_asche")
g.apply_auras()
check(fire[0].aura == 2 and fire[0].power() == 5, "Elementbonus zählt doppelt")
h.perks = []
h.played["Feuer"] = 20
g.apply_auras()
check(fire[0].aura == 2 and fire[0].power() == 5 and fire[0].max_health == 2, "20 Mana Feuer: +2 Angriff")
# Die anderen Paare
var walls := [put(h, "e_wall"), put(h, "e_wall")]
check(walls[0].has("Spott") and not fire[0].has("Spott"), "zwei Erde: Spott nur für Erdwesen")
var winds := [put(h, "l_wind", false), put(h, "l_wind", false)]
var s5 := setup()
var walls := [put(s5[1], "e_wall"), put(s5[1], "e_wall")]
check(walls[0].has("Spott") and not put(s5[1], "f_wolf").has("Spott"), "zwei Erde: Spott nur für Erdwesen")
var winds := [put(s5[1], "l_wind", false), put(s5[1], "l_wind", false)]
check(winds[0].has("Flink") and winds[0].can_attack(), "zwei Luft: Flink, frisch gelegt greift an")
var geister := [put(h, "w_geist"), put(h, "w_geist")]
var s6 := setup()
var geister := [put(s6[1], "w_geist"), put(s6[1], "w_geist")]
check(geister[0].has("Regeneration"), "zwei Wasser: Regeneration")
# Verschmolzene Wesen zählen für beide Elemente und bekommen beide Effekte und Boni
var s2 := setup()
@@ -338,10 +341,25 @@ func test_elementboni() -> void:
g2.apply_auras()
check(dampf.has("Doppelschlag") and dampf.has("Regeneration") and dampf.keywords() == ["Regeneration", "Doppelschlag"], "Dampf hat beide Sondereffekte")
check(fw.has("Doppelschlag") and ww.has("Regeneration"), "und zählt für beide Paare")
put(s2[2], "f_asche")
put(s2[2], "w_reiter")
h2.played["Feuer"] = 10
h2.played["Wasser"] = 10
g2.apply_auras()
check(dampf.aura == 2 and fw.aura == 1 and ww.aura == 1, "drei Feuer und drei Wasser: Dampf bekommt +2/+2")
check(dampf.aura == 2 and fw.aura == 1 and ww.aura == 1, "10 Mana Feuer und 10 Mana Wasser: Dampf bekommt +2 Angriff")
## Abwerfen: eine Handkarte loswerden, dafür 1 Mana.
func test_abwerfen() -> void:
var s := setup(["f_wolf", "f_kobold"])
var g: Game = s[0]
var h: Game.Player = s[1]
var o: Game.Player = s[2]
var card: Cards.Card = h.hand[0]
check(g.discard(h, card) == "" and h.hand.size() == 1 and h.energy == 10 + Game.DISCARD_GAIN and not (card in h.hand), "abgeworfen: Karte weg, 1 Mana mehr")
check(g.log.back() == "Spieler wirft Flammenwolf ab.", "Verlauf")
check(g.discard(h, card) == "Nicht auf der Hand", "nicht zweimal")
h.energy = 0
check(g.discard(h, h.hand[0]) == "" and h.energy == 1, "auch ohne Mana, bringt eins")
check(g.discard(o, C("f_wolf")) == "Nicht dein Zug", "nur im eigenen Zug")
## Spott zieht Angriffe auf sich; Regeneration heilt am Ende des eigenen Zugs.
@@ -419,17 +437,15 @@ func test_verschmelzen() -> void:
var h3: Game.Player = s3[1]
var fa := put(h3, "f_wolf")
var fb := put(h3, "w_tiefen")
for i in 2:
put(h3, "f_kobold")
h3.played["Feuer"] = 10
g3.apply_auras()
check(fa.aura == 1 and fa.max_health == 3, "drei Feuer vor dem Verschmelzen")
check(fa.aura == 1 and fa.power() == 4 and fa.max_health == 2, "10 Mana Feuer ausgespielt vor dem Verschmelzen")
g3.fuse(h3, fa, fb)
var ff: Game.Creature = h3.board[0]
check(ff.aura == 1 and ff.max_health == 8 and ff.health == 8 and ff.power() == 6, "nach dem Verschmelzen zählt der Bonus nur einmal (7 + 1)")
for i in 2:
put(h3, "w_geist")
check(ff.aura == 1 and ff.max_health == 7 and ff.health == 7 and ff.power() == 6, "nach dem Verschmelzen: 5 + 1 Angriff, Leben ohne Bonus")
h3.played["Wasser"] = 10
g3.apply_auras()
check(g3.element_counts()["Wasser"] == 3 and ff.aura == 2 and ff.max_health == 9, "Feuer und Wasser voll: Dampf bekommt +2/+2")
check(ff.aura == 2 and ff.power() == 7 and ff.max_health == 7, "Feuer und Wasser voll: Dampf bekommt +2 Angriff")
# Auf der Hand
var s4 := setup(["f_wolf", "w_tiefen", "l_wind"])
var g4: Game = s4[0]
@@ -653,6 +669,8 @@ func test_spielstand_migration() -> void:
check(p.coins == 300 and p.dust == 70 and p.campaign_stage == 2, "alter Stand gelesen")
check(p.collection.is_empty() and p.perk == "", "Sammlung auf Standard, unbekanntes Merkmal verworfen")
check(is_equal_approx(p.settings.volume, 0.3) and p.settings.animations, "Einstellungen übernommen")
var fresh := Profile.new()
check(fresh.settings.volume == 0.0 and fresh.settings.music == 0.0, "Ton und Musik sind standardmäßig aus")
p.collection["f_wolf"] = 3
var d := p.to_dict()
check(d.version == Profile.SAVE_VERSION, "Version gespeichert")
@@ -731,6 +749,8 @@ func test_merkmale() -> void:
g.play_card(h, h.hand[0])
check(h.board[1].power() == 3 + 1, "Luft: nur Wesen +1")
g.deck = DeckGen.deck(g.rng)
h.offer = g.draw_cards(Game.OFFER)
h.drew = true # das erste Ziehen wäre ohnehin frei
var mana := h.energy
check(g.draw(h) == "" and h.energy == mana and h.hand.size() == 1, "Merkmal: Ziehen kostet nichts")
for perk in Game.PERKS:
@@ -784,6 +804,7 @@ func test_snapshot() -> void:
h.energy = 5
o.energy = 5
g.deck = DeckGen.deck(g.rng)
h.offer = g.draw_cards(Game.OFFER)
g.draw(h)
var snap := g.snapshot(hi)
var json: Dictionary = JSON.parse_string(JSON.stringify(snap))
@@ -793,6 +814,8 @@ func test_snapshot() -> void:
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)
check(cr2 != null and cr2.card.id == "f_wolf" and cr2.owner() == g2.players[hi] and not cr2.ready, "Wesen mit Besitzer und Zustand")
check(int(g2.players[hi].played["Feuer"]) == C("f_wolf").cost, "ausgespieltes Elementmana reist mit")
check(g2.players[hi].offer.size() == Game.OFFER and snap.players[oi].offer.is_empty(), "das eigene Angebot reist mit, das des Gegners bleibt verborgen")
var troll2 := g2.find_creature(troll.uid)
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")

View File

@@ -16,6 +16,8 @@ func check(cond: bool, msg: String) -> void:
else:
failed += 1
printerr("FEHLER: " + msg)
print("%d Prüfungen bestanden, Abbruch beim ersten Fehler" % passed)
get_tree().quit(1)
func frames(n: int = 2) -> void:
@@ -54,6 +56,21 @@ func right_click() -> void:
await right_click_at(Vector2(5, 5))
func double_click_at(pos: Vector2) -> void:
var down := InputEventMouseButton.new()
down.position = pos
down.global_position = pos
down.button_index = MOUSE_BUTTON_LEFT
down.pressed = true
down.double_click = true
get_viewport().push_input(down)
var up: InputEventMouseButton = down.duplicate()
up.pressed = false
up.double_click = false
get_viewport().push_input(up)
await frames(2)
func right_click_at(pos: Vector2, button: int = MOUSE_BUTTON_RIGHT) -> void:
var down := InputEventMouseButton.new()
down.position = pos
@@ -150,12 +167,11 @@ func _ready() -> void:
main = load("res://scenes/main.tscn").instantiate()
main.save_path = SAVE
add_child(main)
main.sfx.muted = true # Tests bleiben still
await frames(3)
var fast := OS.get_environment("DINGER_FAST") != ""
var suite := [test_menu, test_gamepad_menue, test_booster, test_gegnerauswahl, test_einstellungen,
test_kampagne, test_sammlung, test_sprache, test_kampf_steuerung, test_kampf_elemente_und_verschmelzen, test_kampf_grenzen]
if not fast:
suite += [test_kampf.bind(3), test_kampf_abbrechen]
suite += [test_kampf.bind(3), test_kampf_abbrechen]
# Die Netzwerktests starten Bot-Prozesse und warten auf Verbindungen; das dauert Minuten.
# Sie laufen nur auf Wunsch: DINGER_NETZ=1 (oder gezielt über DINGER_ONLY).
if OS.get_environment("DINGER_NETZ") != "":
@@ -280,13 +296,19 @@ func human_turn(scr) -> void:
break
var mana: int = g.human.energy
var hand: int = g.human.hand.size()
await click(scr.deck)
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand + 1, "gezogen: eine Karte")
var cost: int = g.draw_cost(g.human)
await click(scr.offer_views[0])
check(g.human.energy == mana - cost and g.human.hand.size() == hand + 1, "gezogen: eine Karte")
var tried := [] # Wesen ohne lohnendes Ziel (0 Angriff ohne Vorteil) nicht wieder versuchen
for i in 12:
var ready: Array = scr.own_views.filter(func(v): return v.creature.can_attack())
var ready: Array = scr.own_views.filter(func(v): return v.creature.can_attack() and not (v.creature in tried))
if ready.is_empty() or g.winner != "":
break
var cr: Game.Creature = ready[0].creature
var any_target: bool = g.can_attack(cr, g.opponent)[0] or g.opponent.board.any(func(c): return g.can_attack(cr, c)[0])
if not any_target:
tried.append(cr)
continue
await settle(scr)
await hover(ready[0])
var under = get_viewport().gui_get_hovered_control()
@@ -333,7 +355,7 @@ func test_gegnerauswahl() -> void:
check(hard.game.human.hand.size() == Game.START_HAND and hard.hand_views.size() == Game.START_HAND, "Start: fünf Karten auf der Hand")
check(hard.game.human.hand.all(func(c): return c.cost <= Game.START_MANA), "alle fünf sind im ersten Zug spielbar")
await shot("05_starthand")
check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht oben in der Mitte")
check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht links oben")
await shot("11_kampf")
await give_up()
await click(find_button("Einzelspieler"))
@@ -479,7 +501,7 @@ func test_sprache() -> void:
await frames(2)
check(TranslationServer.get_locale().begins_with("en") and main.profile.settings.language == "en", "Sprache auf Englisch")
check(TranslationServer.translate("Zurück") == "Back" and TranslationServer.translate("Einzelspieler") == "Single player", "Übersetzung geladen")
check(TranslationServer.translate("Zug %d · Du bist dran") == "Turn %d · Your turn" and TranslationServer.translate("Nicht genug Mana") == "Not enough mana", "dynamische Texte übersetzt")
check(TranslationServer.translate("Abwerfen +%d ◆") == "Discard +%d ◆" and TranslationServer.translate("Nicht genug Mana") == "Not enough mana", "dynamische Texte übersetzt")
check(find_button("Back") != null, "Knöpfe übersetzt")
await shot("26_englisch")
await click(find_button("Back"))
@@ -506,12 +528,13 @@ func test_kampf_steuerung() -> void:
g.human.energy = 20 # die Starthand ist zufällig, hier soll nur die Steuerung geprüft werden
g.human.hand = [Cards.by_id("f_wolf").copy()]
scr.refresh()
check(scr.hand_views.size() == 1 and scr.deck.enabled, "eine Handkarte, Stapel bereit")
# Ziehen: 2 Mana weg, eine Karte landet auf der Hand
check(scr.hand_views.size() == 1 and scr.draw_enabled and scr.offer_views.size() == Game.OFFER, "eine Handkarte, drei offene auf dem Stapel")
# Ziehen: die gewählte offene Karte landet auf der Hand, das erste Ziehen ist frei
var mana: int = g.human.energy
await click(scr.deck)
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == 2 and scr.hand_views.size() == 2, "gezogen: eine Karte auf der Hand")
check(g.human.hand[1].cost <= g.human.max_energy, "nur bezahlbare Karten")
var wanted: Cards.Card = g.human.offer[1]
await click(scr.offer_views[1])
check(g.human.energy == mana and g.human.hand.size() == 2 and scr.hand_views.size() == 2 and g.human.hand[1].id == wanted.id, "gezogen: die gewählte Karte, frei")
check(g.human.offer.size() == Game.OFFER and g.human.offer.all(func(c): return c.cost <= g.human.max_energy) and g.draw_cost(g.human) == Game.DRAW_COST, "eine bezahlbare neue liegt nach, das nächste Ziehen kostet 2")
# Ausspielen kostet die Kartenkosten; das Wesen greift noch nicht an
var board_before: int = g.human.board.size()
mana = g.human.energy
@@ -530,7 +553,7 @@ func test_kampf_steuerung() -> void:
mana = g.human.energy
var hand_before: int = g.human.hand.size()
await key(KEY_Z)
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand_before + 1, "Z zieht")
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand_before + 1, "Z zieht die erste offene Karte für 2")
await shot("22_kampf_steuerung")
# Gamepad: A auf dem Stapel zieht
await pad(JOY_BUTTON_DPAD_LEFT)
@@ -571,21 +594,21 @@ func test_kampf_grenzen() -> void:
var guard := 0
while g.human.hand.size() < Game.MAX_HAND and guard < 30:
guard += 1
await click(scr.deck)
await click(scr.offer_views[0])
check(g.human.hand.size() == Game.MAX_HAND, "Hand auf %d gefüllt" % Game.MAX_HAND)
check(not scr.deck.enabled, "bei voller Hand lässt sich nichts ziehen")
await click(scr.deck)
check(not scr.draw_enabled, "bei voller Hand lässt sich nichts ziehen")
await click(scr.offer_views[0])
check(scr.msg_label.text == tr("Deine Hand ist voll"), "Meldung bei voller Hand: " + scr.msg_label.text)
# Feld voll spielen
guard = 0
while g.human.board.size() < Game.MAX_BOARD and guard < 30:
guard += 1
if g.human.hand.is_empty():
await click(scr.deck)
await click(scr.offer_views[0])
await key(KEY_1)
check(g.human.board.size() == Game.MAX_BOARD, "Feld mit %d Wesen voll" % Game.MAX_BOARD)
if g.human.hand.is_empty():
await click(scr.deck)
await click(scr.offer_views[0])
await key(KEY_1)
check(scr.msg_label.text == tr("Dein Feld ist voll"), "Meldung bei vollem Feld: " + scr.msg_label.text)
await shot("25_kampf_voll")
@@ -596,9 +619,10 @@ func test_kampf_grenzen() -> void:
check(g.human.board.size() == before - 1, "Verschmelzen macht einen Platz frei")
# Ohne Mana kein Ziehen
g.human.energy = 0
g.human.drew = true
scr.refresh()
await frames(1)
check(not scr.deck.enabled, "ohne Mana kein Ziehen")
check(not scr.draw_enabled, "ohne Mana kein zweites Ziehen")
await give_up()
@@ -613,6 +637,8 @@ func test_kampf_elemente_und_verschmelzen() -> void:
var g: Game = scr.game
g.human.board.clear()
scr.opponent().board.clear() # was die KI im ersten Zug gelegt hat, stört die Zähler
for e in Cards.ELEMENTS:
scr.opponent().played[e] = 0
var put := func(p: Game.Player, id: String, hidden: bool = false) -> Game.Creature:
var cr := Game.Creature.new(Cards.by_id(id).copy(), p, g.next_uid)
g.next_uid += 1
@@ -620,7 +646,9 @@ func test_kampf_elemente_und_verschmelzen() -> void:
cr.hidden = hidden
p.board.append(cr)
return cr
put.call(g.human, "f_wolf")
var wolf_cr: Game.Creature = put.call(g.human, "f_wolf")
wolf_cr.health = 4 # übersteht die Konter von Falke und Kobold, damit der Test ihn weiter nutzen kann
wolf_cr.max_health = 4
put.call(g.human, "f_asche")
put.call(g.human, "w_reiter")
var falke: Game.Creature = put.call(scr.opponent(), "l_falke")
@@ -631,21 +659,25 @@ func test_kampf_elemente_und_verschmelzen() -> void:
g.apply_auras()
scr.refresh()
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(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(scr.element_view.text() == "Feuer 2/0 (Doppelschlag) · Wasser 1/0 · Erde 0/0 · Luft 0/0", "Zähler: offene eigene Wesen und ausgespielte Karten, Feuerpaar: " + scr.element_view.text())
check(scr.opp_element_view.text() == "Feuer 0/0 · Wasser 0/0 · Erde 0/0 · Luft 1/0", "die Leiste des Gegners zählt nur seine offenen Wesen: " + scr.opp_element_view.text())
check(g.human.board[0].keywords() == ["Doppelschlag"] and scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 0, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken; noch kein Bonus in Sicht")
# Rechtsklick auf die Handkarte: Menü mit Verdeckt legen, ohne Verschmelzen
await hover(scr.hand_views[0])
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, "Rechtsklick öffnet das Kartenmenü")
check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and find_button("Abwerfen +1 ◆", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü mit Verdeckt legen und Abwerfen")
check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
await shot("23_kartenmenue")
await right_click()
check(not scr.menu_layer.visible, "Rechtsklick schließt das Menü")
await right_click_at(Vector2(1150, 300)) # leere Fläche
check(scr.menu_layer.visible and find_button("Zug beenden", scr.menu_layer) != null, "Rechtsklick ins Leere bietet Zug beenden an")
await right_click()
# Mittlere Maustaste legt die Handkarte verdeckt
var mana: int = g.human.energy
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]
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/1"), "mittlere Maustaste legt verdeckt: zählt als ausgespielt, nicht als offen")
# 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())
check(not scr.menu_layer.visible, "kein Menü ohne Einträge")
@@ -656,10 +688,15 @@ func test_kampf_elemente_und_verschmelzen() -> void:
check(mine_hidden.hidden and scr.msg_label.text == "Feuerkobold kann erst im nächsten Zug aufgedeckt werden", "im Legezug nicht aufzudecken: " + scr.msg_label.text)
mine_hidden.ready = true
await click(scr.own_views[3])
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3 (+1/+1)"), "aufgedeckt: drei Feuer, +1/+1: " + scr.element_view.text())
check(wolf.power() == 4 and falke.power() == 3 and mine_hidden.can_attack(), "Feuerwesen +1, kein Malus für Luft, aufgedeckt greift sofort an")
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3/1 (Doppelschlag)"), "aufgedeckt: drei offene Feuer, eine ausgespielt: " + scr.element_view.text())
check(wolf.power() == 3 and falke.power() == 3 and mine_hidden.can_attack(), "unter 10 Mana kein Bonus, aufgedeckt greift sofort an")
g.human.played["Feuer"] = 10
g.apply_auras()
scr.refresh()
await frames(1)
check(scr.element_view.text().begins_with("Feuer 3/10 (Doppelschlag) (+1)") and wolf.power() == 4 and falke.power() == 3, "10 Mana Feuer: +1 Angriff für die eigenen Feuerwesen: " + scr.element_view.text())
check(scr.hand_views[0].bonus == 0, "die Luftkarte auf der Hand zeigt keinen Bonus")
await shot("23b_drei_feuer")
await shot("23b_vier_feuer")
# Angriffsziel: Elementvorteil, verdeckte ohne Anzeige, Held gesperrt
await click(scr.own_views[0])
check(scr.selected == wolf and scr.tiles[falke.uid].matchup == 1 and scr.tiles[secret.uid].matchup == 0, "Angreifer gewählt: +1 gegen Luft, nichts gegen Verdeckte")
@@ -671,7 +708,8 @@ func test_kampf_elemente_und_verschmelzen() -> void:
await click(scr.tiles[secret.uid])
check(not secret.hidden and not (secret in scr.opponent().board), "Angriff deckt auf und tötet den Kobold")
check(wolf.attacks_left() == 1, "Doppelschlag: ein Angriff bleibt")
await right_click()
await double_click_at(scr.own_views[0].get_global_rect().get_center())
check(scr.msg_label.text == "Erst die Wesen" and scr.opponent().life == 30, "Doppelklick zielt auf das Leben, hier stehen noch Wesen: " + scr.msg_label.text)
# Verschmelzen per Ziehen: den Aschekrieger auf den Wellenreiter ziehen und kurz halten
mana = g.human.energy
var before: int = g.human.board.size()
@@ -936,6 +974,14 @@ func test_kampf(shot_turn: int) -> void:
check(g.current() == g.human, "Spieler ist dran")
check(scr.hand_views.size() == g.human.hand.size(), "Handkarten sichtbar")
await shot("07_kampf_start")
# Kurze Partie: einfache KI, Gegner fast tot, drei bereite Drachen räumen auf
scr.smart_ai = false
g.opponent.life = 1
for i in 3:
var drache := Game.Creature.new(Cards.by_id("f_drache").copy(), g.human, 9100 + i)
drache.ready = true
g.human.board.append(drache)
scr.refresh()
var coins: int = main.profile.coins
var guard := 0
while g.winner == "" and guard < 60: