update
This commit is contained in:
@@ -68,6 +68,7 @@ func _initialize() -> void:
|
||||
test_elementboni()
|
||||
test_spott_und_regeneration()
|
||||
test_verschmelzen()
|
||||
test_ki_verschmelzen()
|
||||
test_limits()
|
||||
test_gleiche_regeln()
|
||||
test_zugreihenfolge()
|
||||
@@ -95,20 +96,21 @@ func _initialize() -> void:
|
||||
quit(1 if failed > 0 else 0)
|
||||
|
||||
|
||||
## 120 Karten: je Element alle Werte von 0/1 bis 5/5, die ID verrät die Werte.
|
||||
## 150 Karten: je Element und für Neutral alle Werte von 0/1 bis 5/5, die ID verrät die Werte.
|
||||
func test_kartenpool() -> void:
|
||||
check(Cards.all().size() == 120, "120 Karten")
|
||||
check(Cards.all().all(func(c): return c.elements.size() == 1 and c.elements[0] in Cards.ELEMENTS), "jede Karte hat genau ein Element")
|
||||
for e in Cards.ELEMENTS:
|
||||
check(Cards.all().size() == 150, "150 Karten")
|
||||
check(Cards.all().all(func(c): return c.elements.size() == 1 and (c.elements[0] in Cards.ELEMENTS or c.elements[0] == Cards.NEUTRAL)), "jede Karte hat genau ein Element oder ist neutral")
|
||||
for e in Cards.ELEMENTS + [Cards.NEUTRAL]:
|
||||
check(Cards.all().filter(func(c): return c.elements == [e]).size() == 30, "30 Karten je Element: " + e)
|
||||
var names := {}
|
||||
for c in Cards.all():
|
||||
var parts: PackedStringArray = c.id.split("_")
|
||||
check(c.attack == int(parts[1]) and c.health == int(parts[2]) and c.attack <= 5 and c.health >= 1 and c.health <= 5, "ID passt zu den Werten: " + c.id)
|
||||
check(c.cost == floori((2 * c.attack + c.health) / 3.0), "Kosten = (2 · Angriff + Leben) / 3, abgerundet: " + c.id)
|
||||
check(c.keywords.is_empty() and c.text() == "" and c.name_en != "", "Karten kommen ohne Eigenschaften, mit englischem Namen: " + c.id)
|
||||
check(c.keywords.is_empty() and c.text() == ("Neutral." if c.is_neutral() else "") and c.name_en != "", "Karten kommen ohne Eigenschaften (außer Neutral), mit englischem Namen: " + c.id)
|
||||
names[c.name] = true
|
||||
check(names.size() == 120, "alle Namen verschieden")
|
||||
check(names.size() == 150, "alle Namen verschieden")
|
||||
check(C("n_3_2").is_neutral() and C("n_3_2").text() == "Neutral." and not C("f_3_2").is_neutral(), "neutrale Karten tragen die Eigenschaft Neutral")
|
||||
check(C("f_0_2").cost == 0 and C("f_2_1").cost == 1 and C("f_3_2").cost == 2 and C("f_5_5").cost == 5 and C("e_0_4").cost == 1, "Kostenbeispiele, 0/2 kostet nichts")
|
||||
check(Cards.cost_of(20, 20) == 10, "Kosten enden bei 10")
|
||||
check(C("f_3_2").name == "Brennender Kobold" and C("w_4_5").name == "Flutender Leviathan", "Namen aus Eigenschaft und Wesen")
|
||||
@@ -123,6 +125,7 @@ func test_kombos() -> void:
|
||||
check(Cards.combo_name(["Luft", "Feuer"]) == "Blitz", "Luft+Feuer = Blitz")
|
||||
check(not C("f_5_5").is_combo() and Cards.fusion_name(["Feuer"]) == "Inferno", "Kombos gibt es nur beim Verschmelzen")
|
||||
check(Cards.combo_name(["Feuer", "Wasser", "Erde"]) == "Chimäre", "drei Elemente sind eine Chimäre")
|
||||
check(Cards.combo_name(["Neutral", "Feuer"]) == "Feuerritter" and Cards.combo_name(["Neutral", "Neutral"]) == "Veteran", "Neutral verschmolzen: Elementritter, zweimal neutral: Veteran")
|
||||
|
||||
|
||||
## Mana: 1 im ersten Zug, jede Runde eins mehr, der Rest verfällt; Legen kostet die Kartenkosten.
|
||||
@@ -144,7 +147,7 @@ func test_mana_und_kosten() -> void:
|
||||
h.energy = 5
|
||||
check(g.play_card(h, h.hand[0]) == "" and h.energy == 5 - C("f_3_2").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) == 5 and Cards.fusion_value(8, 8) == 11, "Fusionswert: besserer Wert plus ein Drittel des schwächeren, mindestens 1")
|
||||
check(Cards.fusion_value(4, 2) == 4 and Cards.fusion_value(8, 1) == 6 and Cards.fusion_value(2, 2) == 3 and Cards.fusion_value(0, 4) == 3 and Cards.fusion_value(8, 8) == 9, "Fusionswert: aufgerundete halbe Summe plus eins")
|
||||
|
||||
|
||||
## Stapel: 40 verschiedene Karten, zwölf davon im ersten Zug bezahlbar; Kampagnengegner bekommen
|
||||
@@ -172,10 +175,17 @@ func test_stapel() -> void:
|
||||
var others := sd.filter(func(id): return Cards.by_id(id).elements[0] == o)
|
||||
check(others.size() == 5 and others.all(func(id): return Cards.by_id(id).cost <= Game.START_MANA), "Startdeck %s: fünf billige %s" % [e, o])
|
||||
var filled := DeckGen.fill(["f_0_1"], ["f_0_1", "f_2_1", "e_0_4", "l_1_1", "w_5_5"], 3)
|
||||
check(filled == ["f_0_1", "w_5_5", "e_0_4"], "Auffüllen: teuerste zuerst, dann nach ID, nichts doppelt, bis zur Grenze")
|
||||
check(filled == ["f_0_1", "w_5_5", "e_0_4"], "Auffüllen ohne Stufen: teuerste zuerst, dann nach ID, nichts doppelt, bis zur Grenze")
|
||||
filled = DeckGen.fill(["f_0_1"], ["f_0_1", "f_2_1", "e_0_4", "l_1_1", "w_5_5"], 4, {"l_1_1": 2, "e_0_4": 1, "f_2_1": 1})
|
||||
check(filled == ["f_0_1", "l_1_1", "e_0_4", "f_2_1"], "Auffüllen: wertvollste zuerst, bei gleichem Wert die teuerste")
|
||||
# Kartenwert: Grundkarten 100, je Stufe +100; Fusionen aus ihren Werten
|
||||
check(Cards.all().all(func(c): return c.value() == 100), "jede Grundkarte ist 100 wert")
|
||||
check(C("l_1_1").leveled(4).value() == 500 and C("w_4_5").leveled(2).value() == 300, "Ultimate Rare 500, Super Rare 300")
|
||||
check(Cards.fused(C("f_3_2"), C("w_4_5")).value() == 100 and Cards.fused(C("f_3_2"), C("w_4_5"), 3).value() == 400, "Dampf: 100, in Stufe 3 400")
|
||||
check(DeckGen.strength(["f_0_1", "l_1_1", "w_4_5"], {"l_1_1": 4, "w_4_5": 2}) == 300 and DeckGen.strength([], {}) == 0 and DeckGen.strength(DeckGen.start_deck("Feuer"), {}) == 100, "Stärke: Mittelwert 300, leer 0, Startdeck 100")
|
||||
|
||||
|
||||
## Start: fünf im ersten Zug bezahlbare Karten vom eigenen Stapel. Ziehen bringt eine Karte;
|
||||
## Start: die obersten fünf Karten vom eigenen Stapel, ungefiltert. Ziehen bringt eine Karte;
|
||||
## die erste im Zug ist frei, dann 1, 2, 3 … Mana.
|
||||
func test_ziehen() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
@@ -184,13 +194,12 @@ func test_ziehen() -> void:
|
||||
for p in g.players:
|
||||
check(p.deck.size() == Game.DECK_MIN - Game.START_HAND, "jeder hat seinen Stapel, fünf Karten sind auf der Hand")
|
||||
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")
|
||||
var first: Game.Player = g.current()
|
||||
check(g.draw(first) == "Deine Hand ist voll", "fünf Handkarten sind das Limit")
|
||||
first.hand.pop_back()
|
||||
for i in 8:
|
||||
first.hand.append(first.draw_from_deck())
|
||||
var deck_before := first.deck.size()
|
||||
check(g.draw_cost(first) == 0 and g.draw(first) == "" and first.energy == Game.START_MANA and first.deck.size() == deck_before - 1, "erstes Ziehen frei, eine Karte vom eigenen Stapel")
|
||||
check(g.draw_cost(first) == 0 and g.draw(first) == "" and first.hand.size() == Game.START_HAND + 9 and first.energy == Game.START_MANA and first.deck.size() == deck_before - 1, "erstes Ziehen frei, eine Karte vom eigenen Stapel, auch mit dreizehn auf der Hand")
|
||||
first.hand.pop_back()
|
||||
check(g.draw_cost(first) == 1 and g.draw(first) == "" and first.energy == 0, "zweites Ziehen kostet 1")
|
||||
first.hand.pop_back()
|
||||
@@ -210,11 +219,11 @@ func test_ziehen() -> void:
|
||||
g.end_turn()
|
||||
check(first.life == Game.START_LIFE - 3, "und dann 2, 3 …")
|
||||
# Der eigene Stapel aus dem Spielstand
|
||||
var ids := ["f_0_1", "f_0_2", "w_0_1", "w_0_2", "e_0_1", "e_0_2", "l_0_1", "l_0_2", "f_5_5", "w_5_5"]
|
||||
var ids := ["f_5_5", "w_5_5", "e_5_5", "l_5_5", "f_4_5", "w_4_5", "e_0_1", "e_0_2", "l_0_1", "l_0_2"]
|
||||
var g2 := Game.new_battle(r, {"deck": ids})
|
||||
var seen := g2.human.deck + g2.human.hand.map(func(c): return c.id)
|
||||
check(seen.size() == ids.size() and seen.all(func(id): return id in ids), "der Mensch spielt mit seinem Stapel")
|
||||
check(g2.human.hand.all(func(c): return c.cost <= Game.START_MANA), "die teuren bleiben im Stapel")
|
||||
check(g2.human.hand.any(func(c): return c.cost > Game.START_MANA), "die Starthand ist ungefiltert: teure Karten kommen mit (eigenes Deck, eigene Verantwortung)")
|
||||
|
||||
|
||||
## Gelegte Wesen greifen erst im nächsten Zug an; kein Durchschlag, keine Heilung zu Zugbeginn.
|
||||
@@ -257,6 +266,10 @@ func test_elementvorteil() -> void:
|
||||
check(Cards.matchup(["Erde"], dampf) == 0 and Cards.matchup(["Wasser"], dampf) == 0, "auch als Verteidiger zählt das bessere Element")
|
||||
check(Cards.matchup(dampf, ["Wasser", "Erde"]) == -1 and Cards.matchup(["Wasser", "Erde"], dampf) == 1, "beide Elemente geschlagen: Nachteil")
|
||||
check(Cards.matchup(dampf, []) == 0 and Cards.matchup([], dampf) == 0, "unbekannte Elemente: kein Vorteil")
|
||||
check(Cards.matchup(["Neutral"], ["Feuer"]) == 0 and Cards.matchup(["Wasser"], ["Neutral"]) == 0, "Neutral: nie im Vorteil oder Nachteil")
|
||||
var feuerritter := ["Feuer", "Neutral"]
|
||||
check(Cards.matchup(feuerritter, ["Luft"]) == 1 and Cards.matchup(feuerritter, ["Wasser"]) == 0 and Cards.matchup(["Wasser"], feuerritter) == 0, "Feuerritter: Vorteil bleibt, die Schwäche ist weg")
|
||||
# Ohne Stapelbonus bewirkt das Element nichts
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
@@ -265,31 +278,42 @@ func test_elementvorteil() -> void:
|
||||
var wolf := put(o, "f_3_2") # Feuer 3/2
|
||||
var life: int = o.life
|
||||
g.attack(tiefen, wolf)
|
||||
check(wolf.health <= 0 and o.life == life and tiefen.health == 5 - 2, "Wasser gegen Feuer: +1 beim Zuschlagen, der Konter macht 1 weniger, nichts schlägt durch")
|
||||
check(wolf.health <= 0 and o.life == life and tiefen.health == 5 - 3, "Wasser gegen Feuer ohne Stapelbonus: 4 Schaden, Konter 3, nichts schlägt durch")
|
||||
# Im Vorteil zählt der Stapelbonus des Ziels nicht; beim Konter fehlt dem Ziel sein Angriffsbonus
|
||||
var s3 := setup()
|
||||
var w3 := put(s3[1], "f_3_2")
|
||||
var t3 := put(s3[2], "w_4_5")
|
||||
s3[0].attack(w3, t3)
|
||||
check(t3.health == 5 - 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 w3 := put(s3[1], "w_4_5")
|
||||
var f3 := put(s3[2], "f_3_2")
|
||||
s3[2].played["Feuer"] = 6
|
||||
s3[0].apply_auras()
|
||||
check(f3.aura == 2 and f3.power() == 5 and f3.max_health == 4 and Game.matchup_bonus(w3, f3) == 2 and Game.matchup_bonus(f3, w3) == -2, "Feuer mit +2/+2: Wasser schlägt 2 mehr, Feuer 2 weniger")
|
||||
s3[0].attack(w3, f3)
|
||||
check(f3.health <= 0 and w3.health == 5 - 3, "Wasser trifft für 4 + 2 durch den Bonus, der Konter kommt ohne Bonus: 3")
|
||||
# Im Nachteil fehlt der eigene Angriffsbonus; der Konter geht durch den eigenen Lebensbonus
|
||||
var s4 := setup()
|
||||
var funke := put(s4[1], "f_0_2") # Feuer 0/2
|
||||
var hauch := put(s4[2], "l_0_2") # Luft 0/2
|
||||
var wall := put(s4[2], "e_0_4") # Erde 0/4
|
||||
check(s4[0].can_attack(funke, wall)[1] == "Schwelender Kobold 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 f4 := put(s4[1], "f_3_2")
|
||||
var w4 := put(s4[2], "w_4_5")
|
||||
s4[1].played["Feuer"] = 6
|
||||
s4[0].apply_auras()
|
||||
s4[0].attack(f4, w4)
|
||||
check(w4.health == 5 - 3 and f4.health <= 0, "Feuer mit +2/+2 greift Wasser an: schlägt ohne Bonus für 3, der Konter 4 + 2 geht durch den Bonus")
|
||||
# 0 Angriff: nur der Stapelbonus des Ziels gibt Schaden
|
||||
var s5 := setup()
|
||||
var hauch2 := put(s5[1], "l_0_2")
|
||||
var funke2 := put(s5[2], "f_0_2")
|
||||
check(s5[0].attack(hauch2, funke2) == "Leiser Bote würde keinen Schaden machen" and funke2.health == 2, "im Nachteil nichts")
|
||||
var wall2 := put(s5[2], "e_0_4")
|
||||
var funke := put(s5[1], "f_0_2") # Feuer 0/2
|
||||
var hauch := put(s5[2], "l_0_2") # Luft 0/2
|
||||
check(s5[0].can_attack(funke, hauch)[1] == "Schwelender Kobold würde keinen Schaden machen", "0 Angriff, Ziel ohne Bonus: kein Angriff")
|
||||
s5[2].played["Luft"] = 3
|
||||
s5[0].apply_auras()
|
||||
check(hauch.power() == 1 and hauch.health == 3 and s5[0].attack(funke, hauch) == "" and hauch.health == 2 and funke.health == 2, "Ziel mit +1/+1: 0 Angriff trifft durch den Bonus für 1, der Konter 1 − 1 bleibt aus")
|
||||
var wall := put(s5[2], "e_0_4")
|
||||
var geist5 := put(s5[1], "w_2_2") # Wasser 2/2
|
||||
check(s5[0].attack(geist5, wall2) == "" and wall2.health == 4 - 1 and geist5.health == 2 - 1, "Erdhüter kontert mit Elementvorteil für 1")
|
||||
check(s5[0].attack(geist5, wall) == "" and wall.health == 2 and geist5.health == 2, "Erdhüter ohne Bonus kontert nicht")
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
var geist := put(s2[1], "w_2_2") # Wasser 2/2
|
||||
var geist := put(s2[1], "w_2_2")
|
||||
s2[1].played["Wasser"] = 3
|
||||
g2.apply_auras()
|
||||
g2.attack(geist, s2[2])
|
||||
check(s2[2].life == 13, "gegen Helden gibt es keinen Vorteil")
|
||||
check(s2[2].life == 12, "gegen Helden gibt es keinen Vorteil, nur den eigenen Bonus")
|
||||
|
||||
|
||||
## Verdeckt: greift nicht an, zählt nicht für die Elementboni, darf erst nach einer Runde aufgedeckt
|
||||
@@ -321,7 +345,7 @@ func test_verdeckt() -> void:
|
||||
|
||||
|
||||
## Elementboni: zwei eigene offene Wesen eines Elements geben den Sondereffekt (Erde Spott, Luft
|
||||
## Flink, Wasser Regeneration, Feuer Doppelschlag); je fünf ausgespielte Karten eines Elements geben
|
||||
## Flink, Wasser Regeneration, Feuer Doppelschlag); je drei ausgespielte Karten eines Elements geben
|
||||
## den eigenen Wesen des Elements +1/+1. Jeder Spieler zählt für sich.
|
||||
func test_elementboni() -> void:
|
||||
var s := setup(["f_4_3", "f_4_3"])
|
||||
@@ -336,29 +360,29 @@ func test_elementboni() -> void:
|
||||
var hidden_fire := put(o, "f_4_3", true, true)
|
||||
g.apply_auras()
|
||||
check(not hidden_fire.has("Doppelschlag") and o.element_count("Feuer") == 0, "verdeckte zählen nicht fürs Paar")
|
||||
# Ausgespielte Karten zählen; je fünf gibt es +1/+1 für die eigenen Wesen des Elements
|
||||
h.played["Feuer"] = 4
|
||||
g.play_card(h, h.hand[0]) # Flammender Wolf: die fünfte Feuerkarte
|
||||
check(int(h.played["Feuer"]) == 5 and fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 3 and h.board[2].aura == 1, "fünf Feuerkarten ausgespielt: +1/+1 für alle eigenen Feuerwesen")
|
||||
# Ausgespielte Karten zählen; je drei gibt es +1/+1 für die eigenen Wesen des Elements
|
||||
h.played["Feuer"] = 2
|
||||
g.play_card(h, h.hand[0]) # Flammender Wolf: die dritte Feuerkarte
|
||||
check(int(h.played["Feuer"]) == 3 and fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 3 and h.board[2].aura == 1, "drei Feuerkarten ausgespielt: +1/+1 für alle eigenen Feuerwesen")
|
||||
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 == 0 and int(o.played["Feuer"]) == 0, "aufgedeckt, aber nie ausgespielt gezählt: kein Bonus")
|
||||
g.play_card(h, h.hand[0], true) # zweiter Wolf verdeckt
|
||||
var secret: Game.Creature = h.board.back()
|
||||
check(secret.hidden and secret.aura == 0 and int(h.played["Feuer"]) == 6, "verdeckt gelegt zählt als ausgespielt, der Bonus kommt erst offen")
|
||||
check(secret.hidden and secret.aura == 0 and int(h.played["Feuer"]) == 4, "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 == 4, "aufgedeckt kommt der Bonus")
|
||||
# Vorschau
|
||||
h.played["Luft"] = 4
|
||||
check(g.forecast(h, C("l_1_1")) == 1 and g.forecast(h, C("w_2_2")) == 0, "Vorschau: der Hauch wäre die fünfte Luftkarte und bekäme +1/+1")
|
||||
h.played["Luft"] = 2
|
||||
check(g.forecast(h, C("l_1_1")) == 1 and g.forecast(h, C("w_2_2")) == 0, "Vorschau: der Hauch wäre die dritte Luftkarte und bekäme +1/+1")
|
||||
# Merkmal: doppelt
|
||||
h.perks = ["element_doppelt"]
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 2 and fire[0].power() == 5 and fire[0].max_health == 4, "Elementbonus zählt doppelt")
|
||||
h.perks = []
|
||||
h.played["Feuer"] = 10
|
||||
h.played["Feuer"] = 6
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 2 and fire[0].power() == 5 and fire[0].max_health == 4, "zehn Feuerkarten: +2/+2")
|
||||
check(fire[0].aura == 2 and fire[0].power() == 5 and fire[0].max_health == 4, "sechs Feuerkarten: +2/+2")
|
||||
fire[0].health = 1
|
||||
h.played["Feuer"] = 0
|
||||
g.apply_auras()
|
||||
@@ -383,10 +407,21 @@ 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")
|
||||
h2.played["Feuer"] = 5
|
||||
h2.played["Wasser"] = 5
|
||||
h2.played["Feuer"] = 3
|
||||
h2.played["Wasser"] = 3
|
||||
g2.apply_auras()
|
||||
check(dampf.aura == 2 and fw.aura == 1 and ww.aura == 1, "fünf Feuer und fünf Wasser: Dampf bekommt +2/+2")
|
||||
check(dampf.aura == 2 and fw.aura == 1 and ww.aura == 1, "drei Feuer und drei Wasser: Dampf bekommt +2/+2")
|
||||
# Neutral: kein Paar, aber ein eigener Zähler
|
||||
var s7 := setup(["n_2_2", "n_2_2"])
|
||||
var g7: Game = s7[0]
|
||||
var h7: Game.Player = s7[1]
|
||||
g7.play_card(h7, h7.hand[0])
|
||||
g7.play_card(h7, h7.hand[0])
|
||||
check(h7.played["Neutral"] == 2 and h7.board[0].keywords().is_empty() and h7.board[0].aura == 0, "zwei Neutrale: kein Paar, der Zähler steht auf 2, noch kein Bonus")
|
||||
check(g7.forecast(h7, C("n_5_5")) == 1 and g7.forecast(h7, C("f_5_5")) == 0, "Vorschau: die dritte Neutrale bringt +1/+1, nur für Neutrale")
|
||||
h7.played["Neutral"] = 3
|
||||
g7.apply_auras()
|
||||
check(h7.board.all(func(c): return c.aura == 1 and c.health == 3), "drei Neutrale: +1/+1 für die neutralen Wesen")
|
||||
|
||||
|
||||
## Abwerfen: eine Handkarte loswerden, dafür 1 Mana.
|
||||
@@ -442,68 +477,77 @@ 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")
|
||||
|
||||
|
||||
## Verschmelzen: besserer Wert plus ein Drittel des schwächeren, Eigenschaften beider, Kosten die
|
||||
## halbe Manasumme; auch auf der Hand.
|
||||
## Verschmelzen: kostenlos, Hand und Brett auch gemischt; das Ergebnis kommt unverletzt auf die
|
||||
## Hand. Je Wert die aufgerundete halbe Grundwertsumme plus eins, Kosten daraus, dann die höhere
|
||||
## Stufe der Eltern obendrauf.
|
||||
func test_verschmelzen() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var a := put(h, "f_3_2") # 3/2, Kosten 2
|
||||
var b := put(h, "w_4_5") # 4/5, Kosten 4
|
||||
b.health = 4
|
||||
b.health = 1
|
||||
a.card.keywords.append("Spott")
|
||||
check(Game.fusion_cost(a, b) == 3, "Verschmelzen kostet die halbe Manasumme: (2 + 4) / 2")
|
||||
check(g.fuse(h, a, b) == "" and h.board.size() == 1 and h.energy == 7, "verschmolzen")
|
||||
var f: Game.Creature = h.board[0]
|
||||
check(f.name == "Dampf" and f.attack == 5 and f.max_health == 6 and f.health == 5, "Dampf: 4+1 Angriff, 5+1 Leben, aktuell 4+1")
|
||||
check("Spott" in f.card.keywords and f.card.cost == 5, "Eigenschaften geerbt, Kosten aus den neuen Werten")
|
||||
check(f.card.elements == ["Feuer", "Wasser"] and f.ready, "beide Elemente, beide waren bereit")
|
||||
var c := put(h, "e_0_4", false)
|
||||
check(Game.fusion_cost(f, c) == 3, "Dampf (5) mit Hüter (1): 3 Mana")
|
||||
g.fuse(h, f, c)
|
||||
var f2: Game.Creature = h.board[0]
|
||||
check(f2.name == "Chimäre" and h.energy == 4 and not f2.ready, "Chimäre aus drei Elementen, nicht bereit, weil der Hüter es nicht war")
|
||||
var d := put(h, "l_1_1")
|
||||
check(Game.fusion_cost(f2, d) == ceili((f2.card.cost + 1) / 2.0), "Kosten folgen den Karten")
|
||||
h.energy = 1
|
||||
check(g.fuse(h, f2, d) == "Nicht genug Mana", "zu teuer")
|
||||
check(g.can_fuse(h, f2, f2)[1] == "Zwei verschiedene Karten wählen", "nicht mit sich selbst")
|
||||
check(Game.fusion_cost(put(h, "f_0_2"), put(h, "w_0_2")) == 0, "zwei Karten für 0 verschmelzen umsonst")
|
||||
# Verdeckte verschmelzen offen
|
||||
var s2 := setup()
|
||||
var x := put(s2[1], "f_3_2", true, true)
|
||||
var y := put(s2[1], "f_2_1", true, true)
|
||||
s2[0].fuse(s2[1], x, y)
|
||||
check(not s2[1].board[0].hidden and s2[1].board[0].name == "Inferno", "das Ergebnis liegt offen")
|
||||
# Elementbonus der Eltern zählt nicht doppelt; Dampf bekommt die Boni beider Elemente
|
||||
var s3 := setup()
|
||||
var g3: Game = s3[0]
|
||||
var h3: Game.Player = s3[1]
|
||||
var fa := put(h3, "f_3_2")
|
||||
var fb := put(h3, "w_4_5")
|
||||
h3.played["Feuer"] = 5
|
||||
g3.apply_auras()
|
||||
check(fa.aura == 1 and fa.power() == 4 and fa.max_health == 3, "fünf Feuerkarten ausgespielt vor dem Verschmelzen")
|
||||
g3.fuse(h3, fa, fb)
|
||||
var ff: Game.Creature = h3.board[0]
|
||||
check(ff.aura == 1 and ff.max_health == 7 and ff.health == 7 and ff.power() == 6, "nach dem Verschmelzen zählt der Bonus nur einmal (6 + 1)")
|
||||
h3.played["Wasser"] = 5
|
||||
g3.apply_auras()
|
||||
check(ff.aura == 2 and ff.power() == 7 and ff.max_health == 8, "Feuer und Wasser voll: Dampf bekommt +2/+2")
|
||||
# Auf der Hand
|
||||
var s4 := setup(["f_3_2", "w_4_5", "l_1_1"])
|
||||
check(g.fuse(h, a, b) == "" and h.board.is_empty() and h.hand.size() == 1 and h.energy == 10, "beide Wesen verlassen das Brett, das Ergebnis liegt auf der Hand, kostenlos")
|
||||
var f: Cards.Card = h.hand[0]
|
||||
check(f.name == "Dampf" and f.attack == 5 and f.health == 5 and f.cost == 5, "Dampf: ⌈7/2⌉ + 1 = 5 Angriff und Leben, Kosten (10 + 5) / 3 = 5, die Verletzung ist weg")
|
||||
check("Spott" in f.keywords and f.elements == ["Feuer", "Wasser"] and f.look == 0, "Eigenschaften und Elemente beider, Stufe Common")
|
||||
check(g.play_card(h, f) == "" and h.energy == 5 and h.board[0].health == 5 and not h.board[0].ready, "ausgespielt für die neuen Kosten, unverletzt, erst nächsten Zug bereit")
|
||||
check(h.played["Feuer"] == 1 and h.played["Wasser"] == 1, "zählt beim Legen für beide Elemente")
|
||||
# Wesen mit Handkarte, auch verdeckt; stark plus schwach ergibt weniger als das starke
|
||||
var dampf: Game.Creature = h.board[0]
|
||||
var secret := put(h, "e_0_4", false, true)
|
||||
h.hand = [C("l_1_1")]
|
||||
check(g.fuse(h, dampf, h.hand[0]) == "" and h.hand.size() == 1 and h.board == [secret], "Wesen mit Handkarte verschmolzen")
|
||||
var chi: Cards.Card = h.hand[0]
|
||||
check(chi.name == "Chimäre" and chi.attack == 4 and chi.health == 4 and chi.cost == 4, "Chimäre aus drei Elementen: ⌈6/2⌉ + 1 = 4/4, schwächer als Dampf, dafür frisch")
|
||||
check(g.fuse(h, h.hand[0], secret) == "" and h.hand[0].name == "Chimäre" and h.hand[0].health == 5 and h.board.is_empty(), "auch mit einem verdeckten Wesen")
|
||||
check(g.can_fuse(h, h.hand[0], h.hand[0])[1] == "Zwei verschiedene Karten wählen", "nicht mit sich selbst")
|
||||
check(g.can_fuse(h, h.hand[0], put(s[2], "f_2_1"))[1] == "Nur eigene Karten" and g.can_fuse(h, h.hand[0], C("f_2_1"))[1] == "Nur eigene Karten", "fremde Wesen und fremde Karten nicht")
|
||||
h.energy = 0
|
||||
check(g.can_fuse(h, h.hand[0], put(h, "f_2_1"))[0], "ohne Mana geht es trotzdem")
|
||||
# Stufe: aus den Grundwerten gerechnet, dann die höhere Stufe obendrauf
|
||||
var s4 := setup(["f_3_2", "l_1_1"])
|
||||
var g4: Game = s4[0]
|
||||
var h4: Game.Player = s4[1]
|
||||
var hand_a: Cards.Card = h4.hand[0]
|
||||
var hand_b: Cards.Card = h4.hand[1]
|
||||
check(g4.can_fuse(h4, hand_a, put(h4, "e_0_4"))[1] == "Nur Hand mit Hand oder Brett mit Brett", "Hand und Brett mischen sich nicht")
|
||||
check(g4.fuse(h4, hand_a, hand_b) == "" and h4.hand.size() == 2 and h4.energy == 7, "zwei Handkarten verschmolzen, 3 Mana")
|
||||
var hc: Cards.Card = h4.hand[0]
|
||||
check(hc.name == "Dampf" and hc.attack == 5 and hc.health == 6 and hc.cost == 5, "Dampf auf der Hand mit 5/6 und Kosten 5")
|
||||
var lava := Cards.fused(C("f_4_3"), C("f_2_1"), 4, 8)
|
||||
check(Cards.fused(lava, C("f_2_1")).attack == 5 and Cards.fused(lava, C("f_2_1")).health == 9, "ein Funke macht Lava 4/8 zu 5/9, nie schlechter")
|
||||
check(g4.play_card(h4, hc) == "" and h4.energy == 7 - 5 and h4.board.back().name == "Dampf", "Ausspielen kostet die neuen Kosten")
|
||||
check(g4.log.any(func(l): return l == "Brennender Kobold und Flutender Leviathan verschmelzen auf der Hand zu Dampf (5/6)."), "Verlauf")
|
||||
h4.hand[1] = h4.hand[1].leveled(4) # Hauch in Ultimate Rare: 5/5 für 1 Mana
|
||||
check(h4.hand[1].attack == 5 and h4.hand[1].base_attack() == 1 and h4.hand[1].cost == 1, "der Stufenbonus liegt auf den Grundwerten")
|
||||
check(g4.fuse(h4, h4.hand[0], h4.hand[1]) == "" and h4.hand.size() == 1, "auf der Hand verschmolzen")
|
||||
var blitz: Cards.Card = h4.hand[0]
|
||||
check(blitz.name == "Blitz" and blitz.look == 4 and blitz.base_attack() == 3 and blitz.base_health() == 3 and blitz.attack == 7 and blitz.health == 7 and blitz.cost == 3, "Blitz: Grundwerte ⌈4/2⌉ + 1 = 3/3, Kosten 3, Stufe 4 macht daraus 7/7")
|
||||
check(g4.log.any(func(l): return l == "Brennender Kobold und Flüchtiger Hauch verschmelzen zu Blitz (7/7) auf der Hand."), "Verlauf")
|
||||
# Bild
|
||||
var lava := Cards.fused(C("f_4_3"), C("f_2_1"))
|
||||
check(lava.name == "Inferno" and lava.attack == 4 and lava.health == 3 and lava.art == "f_4_3" and lava.art_card().id == "f_4_3" and Cards.fused(C("w_2_2"), lava).art == "f_4_3", "das Bild trägt der stärkere Elternteil, auch über Verschmelzungen hinweg")
|
||||
check(Cards.fused(C("f_3_2"), C("w_3_2")).art == "f_3_2" and C("f_3_2").art_card().id == "f_3_2" and Cards.Card.from_dict(lava.to_dict()).art == "f_4_3", "bei Gleichstand die erste Karte; Grundkarten zeigen sich selbst; das Bild überlebt das Speichern")
|
||||
|
||||
|
||||
## KI: heilt ein Wesen mit höchstens halbem Leben per Fusion mit der billigsten Handkarte, wenn
|
||||
## das Ergebnis bezahlbar bleibt; die Vorausschau kennt Fusionen mit Handkarten und bildet sie
|
||||
## zwischen Kopie und Original ab.
|
||||
func test_ki_verschmelzen() -> void:
|
||||
var s := setup(["f_2_1", "w_4_5"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var hurt := put(h, "e_3_5")
|
||||
hurt.health = 2
|
||||
var act := AI.choose_action_greedy(g, h)
|
||||
check(act.type == "fuse" and act.creature == hurt and act.other == h.hand[0], "gierig: das halb tote Wesen mit dem Funken verschmelzen")
|
||||
h.energy = 1
|
||||
act = AI.choose_action_greedy(g, h)
|
||||
check(act.type == "play", "ohne Mana fürs Ergebnis keine Heilfusion, sondern legen")
|
||||
h.energy = 10
|
||||
var cands := AI.candidate_actions(g, h)
|
||||
check(cands.any(func(a): return a.type == "fuse" and a.creature == hurt and a.other == h.hand[0]), "Vorausschau: Wesen plus Handkarte als Kandidat")
|
||||
check(cands.any(func(a): return a.type == "fuse" and a.creature is Cards.Card and a.other is Cards.Card), "Vorausschau: zwei Handkarten als Kandidat")
|
||||
var fuse_act: Dictionary = cands.filter(func(a): return a.type == "fuse" and a.creature == hurt)[0]
|
||||
var g2 := g.clone()
|
||||
AI.apply_on_clone(g2, g, fuse_act)
|
||||
check(g2.players[0].board.is_empty() and g2.players[0].hand.size() == 2 and h.board == [hurt] and h.hand.size() == 2, "auf der Kopie verschmolzen, das Original bleibt")
|
||||
var back := AI.map_action(g, g2, fuse_act)
|
||||
check(back.creature == hurt and back.other == h.hand[0], "zurück aufs Original abgebildet")
|
||||
hurt.health = 5
|
||||
check(not AI.candidate_actions(g, h).any(func(a): return a.type == "fuse" and a.creature == hurt), "unverletzt: kein Heilkandidat")
|
||||
|
||||
|
||||
func test_limits() -> void:
|
||||
@@ -516,9 +560,9 @@ func test_limits() -> void:
|
||||
check(g.can_play(h, h.hand[0])[1] == "Dein Feld ist voll", "Feld voll")
|
||||
h.hand = []
|
||||
h.deck = ["f_2_1"]
|
||||
for i in Game.MAX_HAND:
|
||||
for i in 12:
|
||||
h.hand.append(C("f_2_1"))
|
||||
check(g.draw(h) == "Deine Hand ist voll" and h.hand.size() == Game.MAX_HAND, "volle Hand zieht nicht")
|
||||
check(g.draw(h) == "" and h.hand.size() == 13, "keine Grenze für Handkarten")
|
||||
check(g.play_card(h, C("f_2_1")) == "Nicht auf der Hand", "fremde Karte lässt sich nicht spielen")
|
||||
|
||||
|
||||
@@ -643,8 +687,8 @@ func test_booster() -> void:
|
||||
g.play_card(h, h.hand[0])
|
||||
var fused: Game.Creature = h.board[1]
|
||||
h.energy = 10
|
||||
check(g.fuse(h, fused, kobold) == "", "auf dem Brett verschmolzen")
|
||||
check(h.board[0].card.look == 3 and h.board[0].card.rarity_in(h.looks) == "Ultra Rare", "auch auf dem Brett und über zwei Fusionen")
|
||||
check(g.fuse(h, fused, kobold) == "" and h.board.is_empty(), "zwei Wesen vom Brett verschmolzen")
|
||||
check(h.hand[0].look == 3 and h.hand[0].rarity_in(h.looks) == "Ultra Rare", "die Stufe bleibt über zwei Fusionen")
|
||||
|
||||
|
||||
## Sammlung und Deck: Start mit einem Startdeck, Herstellen für Staub, Zerlegen entfernt
|
||||
@@ -943,7 +987,7 @@ func test_ki_zeitbudget() -> void:
|
||||
var g := Game.new_battle(r)
|
||||
var h: Game.Player = g.players[0]
|
||||
h.energy = 30
|
||||
for i in Game.MAX_HAND:
|
||||
for i in 7:
|
||||
h.hand.append(C("f_4_3"))
|
||||
for p in g.players:
|
||||
for i in 3:
|
||||
|
||||
166
tests/ui_test.gd
166
tests/ui_test.gd
@@ -3,6 +3,7 @@ extends Node
|
||||
## DINGER_SHOTS=/pfad/zum/ordner godot --path . tests/ui_test.tscn
|
||||
|
||||
const SAVE := "user://test_ui_spielstand.json"
|
||||
const DEVICE := 7 # Gerätenummer der Testereignisse; alles andere fängt der InputFilter ab
|
||||
|
||||
var main: Control
|
||||
var shots := ""
|
||||
@@ -20,6 +21,21 @@ func check(cond: bool, msg: String) -> void:
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
## Lässt nur Ereignisse des Tests durch; echte Maus und Tastatur des Nutzers werden abgefangen,
|
||||
## bevor Knöpfe oder Bildschirme sie sehen. Sitzt als letzter Knoten im Baum und bekommt _input
|
||||
## darum als Erster.
|
||||
class InputFilter extends Node:
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.device != DEVICE:
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
## Testereignis an das Fenster, mit der eigenen Gerätenummer.
|
||||
func push(ev: InputEvent) -> void:
|
||||
ev.device = DEVICE
|
||||
get_viewport().push_input(ev)
|
||||
|
||||
|
||||
func frames(n: int = 2) -> void:
|
||||
for i in n:
|
||||
await get_tree().process_frame
|
||||
@@ -39,10 +55,10 @@ func click_at(pos: Vector2) -> void:
|
||||
down.global_position = pos
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
get_viewport().push_input(down)
|
||||
push(down)
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
get_viewport().push_input(up)
|
||||
push(up)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -63,11 +79,11 @@ func double_click_at(pos: Vector2) -> void:
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
down.double_click = true
|
||||
get_viewport().push_input(down)
|
||||
push(down)
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
up.double_click = false
|
||||
get_viewport().push_input(up)
|
||||
push(up)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -77,10 +93,10 @@ func right_click_at(pos: Vector2, button: int = MOUSE_BUTTON_RIGHT) -> void:
|
||||
down.global_position = pos
|
||||
down.button_index = button
|
||||
down.pressed = true
|
||||
get_viewport().push_input(down)
|
||||
push(down)
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
get_viewport().push_input(up)
|
||||
push(up)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -88,11 +104,11 @@ func key(code: int) -> void:
|
||||
var down := InputEventKey.new()
|
||||
down.keycode = code
|
||||
down.pressed = true
|
||||
get_viewport().push_input(down)
|
||||
push(down)
|
||||
var up := InputEventKey.new()
|
||||
up.keycode = code
|
||||
up.pressed = false
|
||||
get_viewport().push_input(up)
|
||||
push(up)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -100,7 +116,7 @@ func pad(button: int) -> void:
|
||||
var ev := InputEventJoypadButton.new()
|
||||
ev.button_index = button
|
||||
ev.pressed = true
|
||||
get_viewport().push_input(ev)
|
||||
push(ev)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -109,7 +125,7 @@ func hover(node: Control) -> void:
|
||||
var ev := InputEventMouseMotion.new()
|
||||
ev.position = node.get_global_rect().get_center()
|
||||
ev.global_position = ev.position
|
||||
get_viewport().push_input(ev)
|
||||
push(ev)
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -168,6 +184,7 @@ func _ready() -> void:
|
||||
main = load("res://scenes/main.tscn").instantiate()
|
||||
main.save_path = SAVE
|
||||
add_child(main)
|
||||
add_child(InputFilter.new()) # nach main, damit er im Baum zuletzt steht
|
||||
main.sfx.muted = true # Tests bleiben still
|
||||
await frames(3)
|
||||
var suite := [test_start, test_menu, test_gamepad_menue, test_booster, test_gegnerauswahl, test_einstellungen,
|
||||
@@ -244,46 +261,65 @@ func target_view(scr, valid: Array) -> Control:
|
||||
return null
|
||||
|
||||
|
||||
## Zieht eine Karte oder Kachel auf eine andere und hält sie dort, bis die beiden verschmelzen.
|
||||
## Zieht eine Karte oder Kachel auf eine andere und lässt sie dort los; danach wartet das Ergebnis
|
||||
## über dem Ziel auf accept_fuse oder „Abbrechen“.
|
||||
func drag_fuse(scr, from: Control, to: Control) -> void:
|
||||
await frames(20) # Kacheln sind noch unterwegs (Einfliegen, Nachrücken)
|
||||
await settle(scr)
|
||||
var a := from.get_global_rect().get_center()
|
||||
var b := to.get_global_rect().get_center()
|
||||
var fo = from.card if from is CardView else from.creature
|
||||
var to_o = to.card if to is CardView else to.creature
|
||||
var down := InputEventMouseButton.new()
|
||||
down.position = a
|
||||
down.global_position = a
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
get_viewport().push_input(down)
|
||||
push(down)
|
||||
await frames(1)
|
||||
for i in range(1, 6):
|
||||
var m := InputEventMouseMotion.new()
|
||||
m.position = a.lerp(b, i / 5.0)
|
||||
m.global_position = m.position
|
||||
m.button_mask = MOUSE_BUTTON_MASK_LEFT
|
||||
get_viewport().push_input(m)
|
||||
push(m)
|
||||
await frames(1)
|
||||
if scr.drag_ghost == null or scr.drag_target == null:
|
||||
var fo = from.card if from is CardView else from.creature
|
||||
var to_o = to.card if to is CardView else to.creature
|
||||
print("DEBUG drag: ghost=", scr.drag_ghost, " target=", scr.drag_target, " press=", scr.press_target, " dran=", scr.my_turn(),
|
||||
" overlay=", scr.any_overlay(), " mana=", scr.game.human.energy, " kann=", scr.game.can_fuse(scr.game.human, fo, to_o), " from=", from.get_global_rect(), " to=", to.get_global_rect())
|
||||
check(scr.drag_ghost != null and scr.drag_target != null, "Karte schwebt über einem Verschmelzungsziel")
|
||||
var waited := 0
|
||||
while scr.drag_ghost != null and waited < 240:
|
||||
var still := InputEventMouseMotion.new() # die Maus bleibt auf dem Ziel, auch wenn die echte zuckt
|
||||
still.position = b
|
||||
still.global_position = b
|
||||
still.button_mask = MOUSE_BUTTON_MASK_LEFT
|
||||
get_viewport().push_input(still)
|
||||
await frames(1)
|
||||
waited += 1
|
||||
check(scr.drag_ghost != null and scr.drag_target == to_o and to.drop, "Karte schwebt über einem Verschmelzungsziel, das golden gerahmt ist")
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
up.position = b
|
||||
up.global_position = b
|
||||
get_viewport().push_input(up)
|
||||
push(up)
|
||||
await frames(2)
|
||||
var want: Cards.Card = Game.fusion_result(scr.game.human, fo, to_o)
|
||||
var shown: Array = scr.menu_layer.get_children().filter(func(c): return c is CardView)
|
||||
check(scr.fuse_pending == [fo, to_o] and scr.menu_layer.visible and shown.size() == 1 and shown[0].card.name == want.name and shown[0].card.attack == want.attack and shown[0].card.health == want.health, "nach dem Loslassen schwebt %s (%d/%d) über dem Ziel und wartet auf Bestätigung" % [want.name, want.attack, want.health])
|
||||
check(shown[0].position.y + shown[0].size.y < to.get_global_rect().position.y and not to.drop, "das Ergebnis steht über dem Ziel, der goldene Rahmen ist weg")
|
||||
|
||||
|
||||
## Bestätigt die offene Verschmelzungsfrage; die neue Karte gleitet von oben in die Hand.
|
||||
func accept_fuse(scr) -> void:
|
||||
var pair: Array = scr.fuse_pending
|
||||
check(pair != null, "eine Verschmelzung wartet")
|
||||
var before: Array = scr.game.human.hand.duplicate()
|
||||
var btn := find_button("Verschmelzen", scr.menu_layer)
|
||||
check(btn != null, "Knopf Verschmelzen")
|
||||
var at := btn.get_global_rect().get_center()
|
||||
var down := InputEventMouseButton.new()
|
||||
down.position = at
|
||||
down.global_position = at
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
push(down)
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
push(up)
|
||||
await frames(1)
|
||||
var fresh: Array = scr.hand_views.filter(func(v): return not (v.card in before))
|
||||
check(fresh.size() == 1 and (fresh[0].position.y < BattleLayout.HAND_Y - 40 or not scr.fx.enabled), "die neue Karte gleitet von oben in die Hand")
|
||||
await frames(2)
|
||||
|
||||
|
||||
@@ -367,7 +403,8 @@ func test_gegnerauswahl() -> void:
|
||||
var opp: Game.Player = hard.game.opponent
|
||||
check(opp.max_life == Profile.DIFFICULTIES.schwer.life and hard.smart_ai, "Schwer: Gegner mit %d Leben, Vorausschau-KI" % Profile.DIFFICULTIES.schwer.life)
|
||||
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")
|
||||
var mine: Array = main.profile.deck_cards()
|
||||
check(hard.game.human.hand.all(func(c): return c.id in mine), "die Starthand kommt ungefiltert vom eigenen Stapel")
|
||||
await shot("05_starthand")
|
||||
check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht links oben")
|
||||
await shot("11_kampf")
|
||||
@@ -445,6 +482,14 @@ func test_deck() -> void:
|
||||
check(scr is DeckScreen, "Deck geöffnet")
|
||||
var p: Profile = main.profile
|
||||
check(scr.grid.views().size() == p.collection.size() and scr.count_label.text == "Stapel: %d Karten (%d bis %d)" % [p.deck.size(), Game.DECK_MIN, Game.DECK_MAX], "alle eigenen Karten, Zähler stimmt")
|
||||
check(scr.strength_label.text == "Stärke 100", "Startdeck aus Grundkarten: Stärke 100")
|
||||
p.collection[p.deck[0]] = 4
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.strength_label.text == "Stärke %d" % roundi((39 * 100 + 500) / 40.0), "eine Ultimate Rare hebt die Stärke: " + scr.strength_label.text)
|
||||
p.collection[p.deck[0]] = 0
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
var views: Array = scr.grid.views()
|
||||
check(views.all(func(v): return not v.dim) and p.deck.size() == Game.DECK_MIN, "nach dem Start sind alle eigenen Karten im Stapel")
|
||||
var first_id: String = views[0].card.id
|
||||
@@ -518,6 +563,7 @@ func test_gamepad_menue() -> void:
|
||||
var down := InputEventJoypadButton.new()
|
||||
down.button_index = JOY_BUTTON_DPAD_DOWN
|
||||
down.pressed = true
|
||||
down.device = DEVICE
|
||||
Input.parse_input_event(down)
|
||||
await frames(2)
|
||||
var f2 := get_viewport().gui_get_focus_owner()
|
||||
@@ -527,12 +573,12 @@ func test_gamepad_menue() -> void:
|
||||
var accept := InputEventJoypadButton.new()
|
||||
accept.button_index = JOY_BUTTON_A
|
||||
accept.pressed = true
|
||||
get_viewport().push_input(accept)
|
||||
push(accept)
|
||||
await frames(1)
|
||||
var release := InputEventJoypadButton.new()
|
||||
release.button_index = JOY_BUTTON_A
|
||||
release.pressed = false
|
||||
get_viewport().push_input(release)
|
||||
push(release)
|
||||
await frames(3)
|
||||
check(not (screen() is MenuScreen) or target_text == "Beenden", "A öffnet den fokussierten Bildschirm (%s)" % target_text)
|
||||
await hover(main)
|
||||
@@ -657,17 +703,13 @@ func test_kampf_grenzen() -> void:
|
||||
g.human.energy = 100
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
# Hand voll ziehen
|
||||
var guard := 0
|
||||
while g.human.hand.size() < Game.MAX_HAND and guard < 30:
|
||||
guard += 1
|
||||
# Keine Grenze für Handkarten: acht weitere ziehen
|
||||
var hand_before: int = g.human.hand.size()
|
||||
for i in 8:
|
||||
await click(scr.deck)
|
||||
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(scr.msg_label.text == tr("Deine Hand ist voll"), "Meldung bei voller Hand: " + scr.msg_label.text)
|
||||
check(g.human.hand.size() == hand_before + 8 and scr.deck.enabled and scr.hand_views.size() == hand_before + 8, "acht Karten nachgezogen, Ziehen bleibt möglich")
|
||||
# Feld voll spielen
|
||||
guard = 0
|
||||
var guard := 0
|
||||
while g.human.board.size() < Game.MAX_BOARD and guard < 30:
|
||||
guard += 1
|
||||
if g.human.hand.is_empty():
|
||||
@@ -681,9 +723,10 @@ func test_kampf_grenzen() -> void:
|
||||
await shot("25_kampf_voll")
|
||||
# Verschmelzen per Ziehen macht wieder Platz
|
||||
var before: int = g.human.board.size()
|
||||
scr.hold_time = 0.3
|
||||
var hand_n: int = g.human.hand.size()
|
||||
await drag_fuse(scr, scr.own_views[0], scr.own_views[1])
|
||||
check(g.human.board.size() == before - 1, "Verschmelzen macht einen Platz frei")
|
||||
await accept_fuse(scr)
|
||||
check(g.human.board.size() == before - 2 and g.human.hand.size() == hand_n + 1, "Verschmelzen macht zwei Plätze frei, das Ergebnis liegt auf der Hand")
|
||||
# Ohne Mana kein Ziehen
|
||||
g.human.energy = 0
|
||||
g.human.draws = 1
|
||||
@@ -699,12 +742,11 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
var scr = screen()
|
||||
check(scr is BattleScreen, "Kampf gestartet")
|
||||
scr.ai_delay = 0.0
|
||||
scr.hold_time = 0.3
|
||||
await wait_for_human(scr)
|
||||
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:
|
||||
for e in Cards.ELEMENTS + [Cards.NEUTRAL]:
|
||||
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)
|
||||
@@ -756,18 +798,23 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
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 (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 fünf Karten kein Bonus, aufgedeckt greift sofort an")
|
||||
g.human.played["Feuer"] = 5
|
||||
check(wolf.power() == 3 and falke.power() == 3 and mine_hidden.can_attack(), "unter drei Karten kein Bonus, aufgedeckt greift sofort an")
|
||||
g.human.played["Feuer"] = 3
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
check(scr.element_view.text().begins_with("Feuer 3/5 (Doppelschlag) (+1/+1)") and wolf.power() == 4 and falke.power() == 3, "fünf Feuerkarten: +1/+1 für die eigenen Feuerwesen: " + scr.element_view.text())
|
||||
check(scr.element_view.text().begins_with("Feuer 3/3 (Doppelschlag) (+1/+1)") and wolf.power() == 4 and falke.power() == 3, "drei Feuerkarten: +1/+1 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_vier_feuer")
|
||||
# Angriffsziel: Elementvorteil, verdeckte ohne Anzeige, Held gesperrt
|
||||
# Angriffsziel: Elementvorteil schlägt durch den Stapelbonus des Ziels, verdeckte ohne Anzeige, Held gesperrt
|
||||
scr.opponent().played["Luft"] = 3
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
check(falke.aura == 1 and falke.power() == 4 and scr.opp_element_view.text() == "Luft 1/3 (+1/+1)", "der Gegner hat drei Luftkarten gespielt, der Falke trägt +1/+1: " + scr.opp_element_view.text())
|
||||
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")
|
||||
check(scr.tiles[falke.uid].shown_stats() == [falke.power() - 1, falke.health], "der Falke zeigt seinen Konter gegen den Wolf (1 weniger) und sein echtes Leben")
|
||||
check(scr.selected == wolf and scr.tiles[falke.uid].matchup == 1 and scr.tiles[secret.uid].matchup == 0, "Angreifer gewählt: +1 gegen den Falken (sein Bonus zählt nicht), nichts gegen Verdeckte")
|
||||
check(scr.tiles[falke.uid].shown_stats() == [falke.power() - 1, falke.health], "der Falke zeigt seinen Konter gegen den Wolf (ohne seinen Bonus) und sein echtes Leben")
|
||||
await hover(scr.tiles[falke.uid])
|
||||
check(scr.tiles[falke.uid].doomed and not scr.own_views[0].doomed and scr.own_views[0].shown_stats()[0] == wolf.power() + 1, "über dem Falken: er stirbt, der Wolf nicht, der Wolf schlägt mit +1 zu")
|
||||
await shot("23c_angreifer_gewaehlt")
|
||||
@@ -777,19 +824,27 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
check(wolf.attacks_left() == 1, "Doppelschlag: ein Angriff bleibt")
|
||||
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
|
||||
# Verschmelzen per Ziehen: den Aschekrieger auf den Wellenreiter ziehen, erst abbrechen, dann bestätigen
|
||||
mana = g.human.energy
|
||||
var before: int = g.human.board.size()
|
||||
await drag_fuse(scr, scr.own_views[1], scr.own_views[2])
|
||||
check(g.human.board.size() == before - 1 and g.human.board.any(func(c): return c.name == "Dampf") and g.human.energy == mana - 3, "zu Dampf verschmolzen, 3 Mana bezahlt (halbe Manasumme)")
|
||||
var dampf: Game.Creature = g.human.board.filter(func(c): return c.name == "Dampf")[0]
|
||||
check(scr.tiles[dampf.uid].rarity() == "Ultra Rare" and dampf.card.look == 3, "die Fusion trägt die höhere Stufe (der Reiter war Ultra Rare)")
|
||||
await shot("24a_verschmelzen_frage")
|
||||
await click(find_button("Abbrechen", scr.menu_layer))
|
||||
check(scr.fuse_pending == null and not scr.menu_layer.visible and g.human.board.size() == before and g.human.energy == mana, "Abbrechen lässt beide Wesen und das Mana, wie sie waren")
|
||||
await drag_fuse(scr, scr.own_views[1], scr.own_views[2])
|
||||
await accept_fuse(scr)
|
||||
var dampf_cards: Array = g.human.hand.filter(func(c): return c.name == "Dampf")
|
||||
check(g.human.board.size() == before - 2 and dampf_cards.size() == 1 and g.human.energy == mana, "beide Wesen verlassen das Brett, Dampf liegt auf der Hand, kostenlos")
|
||||
var dampf: Cards.Card = dampf_cards[0]
|
||||
var dampf_views: Array = scr.hand_views.filter(func(v): return v.card == dampf)
|
||||
check(dampf.look == 3 and dampf.attack == 8 and dampf.health == 7 and dampf.cost == 4 and dampf_views.size() == 1 and dampf_views[0].rarity == "Ultra Rare", "Dampf 5/4 für 4 Mana aus den Grundwerten, Stufe 3 des Reiters macht 8/7")
|
||||
await shot("24_verschmelzen")
|
||||
# Verschmelzen auf der Hand per Ziehen
|
||||
g.human.hand = [Cards.by_id("f_2_1").copy(), Cards.by_id("f_2_1").copy(), Cards.by_id("l_1_1").copy()]
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
await drag_fuse(scr, scr.hand_views[0], scr.hand_views[1])
|
||||
await accept_fuse(scr)
|
||||
check(g.human.hand.size() == 2 and g.human.hand[0].name == "Inferno" and g.human.hand[0].attack == 3 and g.human.hand[0].health == 2, "zwei Funken auf der Hand zu Inferno 3/2")
|
||||
# Gamepad-Menü (Y) bietet Verdeckt legen und Verschmelzen mit …
|
||||
scr.open_menu(g.human.hand[0], Rect2(scr.hand_views[0].position, scr.hand_views[0].size), true)
|
||||
@@ -797,7 +852,12 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
await click(find_button("Verschmelzen mit …", scr.menu_layer))
|
||||
check(scr.fuse_first == g.human.hand[0], "Partner wählen")
|
||||
await click(scr.hand_views[1])
|
||||
check(g.human.hand.size() == 1 and g.human.hand[0].name == "Blitz", "Inferno und Windgeist zu Blitz")
|
||||
check(g.human.hand.size() == 1 and g.human.hand[0].name == "Blitz", "Inferno und Hauch zu Blitz")
|
||||
# Handkarte mit Wesen: Blitz auf den Kobold ziehen
|
||||
var board_n: int = g.human.board.size()
|
||||
await drag_fuse(scr, scr.hand_views[0], scr.own_views[0])
|
||||
await accept_fuse(scr)
|
||||
check(g.human.board.size() == board_n - 1 and g.human.hand.size() == 1 and g.human.hand[0].name == "Blitz" and g.human.hand[0].base_attack() == 4, "Handkarte und Wesen verschmelzen: der Kobold verlässt das Brett, Blitz 4/4 bleibt auf der Hand")
|
||||
await give_up()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user