update
This commit is contained in:
1 parent
f2c609a08b
commit
1d563b9eec
34 files changed
+5486
-643
No files matched your search
+2
-2
@@ -16,7 +16,7 @@ func _initialize() -> void:
|
||||
print("Beide mit Vorausschau:")
|
||||
_run(games / 2, r, false, false)
|
||||
print("Kampagne (du mit Vorausschau), je Stufe:")
|
||||
for stage in [0, 4, 9, 14, 19, 24, 29]:
|
||||
for stage in [0, 4, 9, 19, 29, 39, 49]:
|
||||
_campaign(stage, games / 4, r)
|
||||
print("Dauer %.1f s" % ((Time.get_ticks_msec() - t0) / 1000.0))
|
||||
quit()
|
||||
@@ -25,7 +25,7 @@ func _initialize() -> void:
|
||||
func _campaign(stage: int, games: int, r: RandomNumberGenerator) -> void:
|
||||
var wins := 0
|
||||
for i in games:
|
||||
var g := Game.new_battle_with(Campaign.opponent(stage), r)
|
||||
var g := Game.new_battle_with(Campaign.opponent(stage), r, false, {"deck": DeckGen.element_deck(Cards.NEUTRAL)}) # du mit der Startsammlung
|
||||
while g.winner == "":
|
||||
AI.take_turn(g, g.current(), false)
|
||||
assert(g.turn < 300, "Spiel endet nicht")
|
||||
|
||||
+198
-134
@@ -89,6 +89,8 @@ func _initialize() -> void:
|
||||
test_merkmale()
|
||||
test_ki_zwei_schritte()
|
||||
test_kapitel_zwei_und_merkmalwahl()
|
||||
test_einzelspieler_gegner()
|
||||
test_skilltree()
|
||||
test_ki_zeitbudget()
|
||||
test_englische_karten()
|
||||
test_uebersetzung_vollstaendig()
|
||||
@@ -96,20 +98,21 @@ func _initialize() -> void:
|
||||
quit(1 if failed > 0 else 0)
|
||||
|
||||
|
||||
## 150 Karten: je Element und für Neutral alle Werte von 0/1 bis 5/5, die ID verrät die Werte.
|
||||
## 550 Karten: je Element und für Neutral alle Werte von 0/1 bis 10/10, die ID verrät die Werte.
|
||||
func test_kartenpool() -> void:
|
||||
check(Cards.all().size() == 150, "150 Karten")
|
||||
check(Cards.all().size() == 550, "550 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)
|
||||
check(Cards.all().filter(func(c): return c.elements == [e]).size() == 110, "110 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.attack == int(parts[1]) and c.health == int(parts[2]) and c.attack <= 10 and c.health >= 1 and c.health <= 10, "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() == ("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() == 150, "alle Namen verschieden")
|
||||
check(names.size() == 550, "alle Namen verschieden")
|
||||
check(C("f_10_10").cost == 10 and C("n_6_1").name == "Eiserner Knappe" and C("w_0_10").name == "Stiller Urleviathan", "10/10 kostet 10; neue Eigenschaften und Wesen ab 6")
|
||||
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")
|
||||
@@ -146,12 +149,13 @@ func test_mana_und_kosten() -> void:
|
||||
check(h.energy == Game.START_MANA + 21, "keine Obergrenze")
|
||||
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(h.cards_played == 1 and g.clone().human.cards_played == 1, "ausgespielte Karte gezählt, auch in der Kopie")
|
||||
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) == 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
|
||||
## Stapel mit Kostendeckel und ab Kapitel 2 höhere Stufen; Startdecks je Element; Auffüllen.
|
||||
## Zufällige Stapel; Elementstapel der Kampagne in der Stufe des Kapitels; Auffüllen.
|
||||
func test_stapel() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 5
|
||||
@@ -160,29 +164,45 @@ func test_stapel() -> void:
|
||||
var uniq := {}
|
||||
for id in d:
|
||||
uniq[id] = true
|
||||
check(d.size() == Game.DECK_MIN and uniq.size() == Game.DECK_MIN, "40 verschiedene Karten")
|
||||
check(d.size() == Game.DECK_MIN and uniq.size() == Game.DECK_MIN, "30 verschiedene Karten")
|
||||
check(d.filter(func(id): return Cards.by_id(id).cost <= Game.START_MANA).size() >= DeckGen.CHEAP_IN_RANDOM, "mindestens zwölf billige")
|
||||
var easy := DeckGen.opponent_deck(r, 1)
|
||||
check(easy.all(func(id): return Cards.by_id(id).cost <= 3), "Kapitel 1: nur bis Kosten 3")
|
||||
var neutral := DeckGen.element_deck(Cards.NEUTRAL)
|
||||
check(neutral.size() == 110 and neutral.all(func(id): return Cards.by_id(id).is_neutral()), "alle 110 neutralen Karten")
|
||||
var start := DeckGen.curve_deck(Cards.NEUTRAL, r)
|
||||
var per_cost := {}
|
||||
var uniq2 := {}
|
||||
for id in start:
|
||||
per_cost[Cards.by_id(id).cost] = int(per_cost.get(Cards.by_id(id).cost, 0)) + 1
|
||||
uniq2[id] = true
|
||||
check(start.size() == Game.DECK_MIN and start.all(func(id): return Cards.by_id(id).is_neutral()) and per_cost.get(0, 0) == 2 and per_cost.get(3, 0) == 6 and per_cost.get(7, 0) == 1 and not per_cost.has(8), "Kurvenstapel: 30 Neutrale nach Manakurve (2 zu 0, 6 zu 3, 1 zu 7, nichts darüber)")
|
||||
check(uniq2.size() == Game.DECK_MIN and DeckGen.curve_deck(Cards.NEUTRAL, r) != start, "nichts doppelt, jedes Mal anders gewürfelt")
|
||||
var capped := DeckGen.curve_deck("Feuer", r, 3)
|
||||
check(capped.size() == Game.DECK_MIN and capped.all(func(id): return Cards.by_id(id).cost <= 3 and Cards.by_id(id).elements[0] == "Feuer"), "Kostendeckel 3: 30 Feuerkarten bis Kosten 3")
|
||||
var fenn := Campaign.opponent(0, r)
|
||||
check(fenn.deck.size() == Game.DECK_MIN and fenn.deck.all(func(id): return Cards.by_id(id).is_neutral() and Cards.by_id(id).cost <= 3) and fenn.looks.values().all(func(l): return l == 0), "Kapitel 1, Stufe 1: 30 Neutrale bis Kosten 3 in Common")
|
||||
var boss := Campaign.opponent(15, r)
|
||||
check(boss.deck.size() == Game.DECK_MIN and boss.deck.all(func(id): return Cards.by_id(id).cost <= 5) and boss.looks.values().all(func(l): return l == 1), "Kapitel 2: bis Kosten 5, Stufe Rare")
|
||||
for e in Cards.ELEMENTS:
|
||||
var sd := DeckGen.start_deck(e)
|
||||
var main := sd.filter(func(id): return Cards.by_id(id).elements[0] == e)
|
||||
check(sd.size() == Game.DECK_MIN and main.size() == 25 and main.all(func(id): return Cards.by_id(id).attack <= 4), "Startdeck %s: 25 Karten des Elements bis Angriff 4" % e)
|
||||
for o in Cards.ELEMENTS:
|
||||
if o != e:
|
||||
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])
|
||||
check(boss.deck.size() == Game.DECK_MIN and boss.deck.all(func(id): return Cards.by_id(id).elements[0] == "Feuer" and Cards.by_id(id).cost <= 6) and boss.looks.values().all(func(l): return l == 0), "Kapitel 2, Stufe 6: Feuer bis Kosten 6 in Common")
|
||||
check(Campaign.stage(0).max_cost == 3 and Campaign.stage(9).max_cost == 10 and Campaign.stage(14).max_cost == 5, "Kostendeckel steigt je Stufe von 3 auf 10")
|
||||
for c in Campaign.CHAPTER_ELEMENTS.size():
|
||||
var e: String = Campaign.CHAPTER_ELEMENTS[c]
|
||||
var o := Campaign.opponent(c * Campaign.STAGES_PER_CHAPTER + 9, r)
|
||||
check(o.deck.all(func(id): return Cards.by_id(id).elements[0] == e) and o.looks.values().all(func(l): return l == maxi(0, c - 1)), "Boss von Kapitel %d: nur %s in Stufe %d" % [c + 1, e, maxi(0, c - 1)])
|
||||
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 ohne Stufen: teuerste zuerst, dann nach ID, nichts doppelt, bis zur Grenze")
|
||||
check(filled == ["f_0_1", "e_0_4", "f_2_1"], "Auffüllen ohne Stufen: gleicher Wert, dann die billigeren, nach ID, nichts doppelt, bis zur Grenze")
|
||||
var full := DeckGen.fill([], neutral, Game.DECK_MIN)
|
||||
var curve_counts: Array[int] = []
|
||||
curve_counts.resize(11)
|
||||
curve_counts.fill(0)
|
||||
for id in full:
|
||||
curve_counts[Cards.by_id(id).cost] += 1
|
||||
check(full.size() == Game.DECK_MIN and curve_counts == DeckGen.CURVE, "Auffüllen aus 110 Neutralen folgt der Manakurve")
|
||||
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")
|
||||
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.element_deck(Cards.NEUTRAL), {}) == 100, "Stärke: Mittelwert 300, leer 0, Startsammlung 100")
|
||||
|
||||
|
||||
## Start: die obersten fünf Karten vom eigenen Stapel, ungefiltert. Ziehen bringt eine Karte;
|
||||
@@ -269,51 +289,42 @@ func test_elementvorteil() -> void:
|
||||
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
|
||||
# Schaden: im Vorteil doppelt, im Nachteil halb (aufgerundet), gegen Helden nichts
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
var tiefen := put(h, "w_4_5") # Wasser 4/5
|
||||
var wolf := put(o, "f_3_2") # Feuer 3/2
|
||||
check(Game.matchup_bonus(tiefen, wolf) == 4 and Game.matchup_bonus(wolf, tiefen) == -1, "Wasser schlägt Feuer doppelt (4 → 8), Feuer kontert halb (3 → 2)")
|
||||
var life: int = o.life
|
||||
g.attack(tiefen, wolf)
|
||||
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], "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
|
||||
check(wolf.health <= 0 and o.life == life and tiefen.health == 5 - 2, "8 Schaden töten den Wolf, der Konter bringt 2, nichts schlägt durch")
|
||||
# Nachteil beim Angriff: halb aufgerundet; der Konter des Ziels kommt doppelt
|
||||
var s4 := setup()
|
||||
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
|
||||
check(w4.health == 5 - 2 and f4.health <= 0, "Feuer trifft Wasser für 2 (3 halbiert, aufgerundet), der Konter 4 × 2 tötet")
|
||||
# Rundung und Null
|
||||
var s5 := setup()
|
||||
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, wall) == "" and wall.health == 2 and geist5.health == 2, "Erdhüter ohne Bonus kontert nicht")
|
||||
check(s5[0].can_attack(funke, hauch)[1] == "Schwelender Kobold würde keinen Schaden machen", "0 Angriff: auch im Vorteil kein Schaden")
|
||||
var wall := put(s5[2], "w_5_5")
|
||||
check(Game.matchup_bonus(put(s5[1], "f_1_1"), wall) == 0 and Game.matchup_bonus(put(s5[1], "f_2_1"), wall) == -1 and Game.matchup_bonus(put(s5[1], "f_5_1"), wall) == -2, "halbiert aufgerundet: 1 bleibt 1, 2 wird 1, 5 wird 3")
|
||||
check(not s5[0].can_attack(put(s5[1], "l_0_2"), put(s5[2], "e_0_4"))[0], "0 Angriff im Vorteil (Luft gegen Erde): kein Angriff")
|
||||
# Helden haben kein Element
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
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 == 12, "gegen Helden gibt es keinen Vorteil, nur den eigenen Bonus")
|
||||
s2[0].attack(geist, s2[2])
|
||||
check(s2[2].life == 15 - 2, "gegen Helden gibt es keinen Vorteil")
|
||||
# Merkmal: kein Nachteil
|
||||
var s6 := setup()
|
||||
var f6 := put(s6[1], "f_3_2")
|
||||
var w6 := put(s6[2], "w_4_5")
|
||||
s6[1].perks = ["kein_nachteil"]
|
||||
check(Game.matchup_bonus(f6, w6) == 0 and Game.matchup_bonus(w6, f6) == 4, "Merkmal: kein Nachteil für die eigenen Wesen, der Gegner behält seinen Vorteil")
|
||||
|
||||
|
||||
## Verdeckt: greift nicht an, zählt nicht für die Elementboni, darf erst nach einer Runde aufgedeckt
|
||||
@@ -344,9 +355,8 @@ func test_verdeckt() -> void:
|
||||
check(seen.card.id == "?" and seen.attack == 2, "die KI-Kopie kennt verdeckte Wesen nicht")
|
||||
|
||||
|
||||
## Elementboni: zwei eigene offene Wesen eines Elements geben den Sondereffekt (Erde Spott, Luft
|
||||
## 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.
|
||||
## Elementpaare: zwei eigene offene Wesen eines Elements geben den Sondereffekt (Erde Spott, Luft
|
||||
## Flink, Wasser Regeneration, Feuer Doppelschlag). Jeder Spieler zählt für sich, verdeckte nicht.
|
||||
func test_elementboni() -> void:
|
||||
var s := setup(["f_4_3", "f_4_3"])
|
||||
var g: Game = s[0]
|
||||
@@ -354,39 +364,17 @@ func test_elementboni() -> void:
|
||||
var o: Game.Player = s[2]
|
||||
var fire := [put(h, "f_3_2"), put(h, "f_2_1")]
|
||||
var bird := put(o, "l_1_1") # Luft 1/1
|
||||
g.apply_auras()
|
||||
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")
|
||||
check(not bird.has("Flink") and bird.power() == 1 and bird.health == 1, "der Gegner zählt für sich: nichts")
|
||||
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 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]) # Flammender Wolf
|
||||
check(h.board[2].has("Doppelschlag") and h.board[2].power() == 4 and h.board[2].health == 3, "ausgespielt: Werte wie auf der Karte, dazu der Effekt")
|
||||
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"]) == 4, "verdeckt gelegt zählt als ausgespielt, der Bonus kommt erst offen")
|
||||
check(secret.hidden and not secret.has("Doppelschlag") and h.element_count("Feuer") == 3, "verdeckt gelegt: ohne Effekt, zählt nicht")
|
||||
g._flip(secret)
|
||||
check(secret.aura == 1 and secret.power() == 5 and secret.health == 4, "aufgedeckt kommt der Bonus")
|
||||
# Vorschau
|
||||
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"] = 6
|
||||
g.apply_auras()
|
||||
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()
|
||||
check(fire[0].aura == 0 and fire[0].max_health == 2 and fire[0].health == 1, "Bonus weg: nur das Höchstleben sinkt")
|
||||
check(secret.has("Doppelschlag") and h.element_count("Feuer") == 4, "aufgedeckt zählt es und bekommt den Effekt")
|
||||
# Die anderen Paare
|
||||
var s5 := setup()
|
||||
var walls := [put(s5[1], "e_0_4"), put(s5[1], "e_0_4")]
|
||||
@@ -396,33 +384,22 @@ func test_elementboni() -> void:
|
||||
var s6 := setup()
|
||||
var geister := [put(s6[1], "w_2_2"), put(s6[1], "w_2_2")]
|
||||
check(geister[0].has("Regeneration"), "zwei Wasser: Regeneration")
|
||||
# Verschmolzene Wesen zählen für beide Elemente und bekommen beide Effekte und Boni
|
||||
# Verschmolzene Wesen zählen für beide Elemente und bekommen beide Effekte
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
var h2: Game.Player = s2[1]
|
||||
var dampf := put(h2, "f_3_2")
|
||||
dampf.card = Cards.fused(C("f_3_2"), C("w_2_2"))
|
||||
var fw := put(h2, "f_2_1")
|
||||
var ww := put(h2, "w_2_2")
|
||||
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"] = 3
|
||||
h2.played["Wasser"] = 3
|
||||
g2.apply_auras()
|
||||
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
|
||||
# Neutral: kein Paar
|
||||
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")
|
||||
|
||||
check(h7.board.size() == 2 and h7.board[0].keywords().is_empty() and h7.board[0].health == 2, "zwei Neutrale: kein Paar, nichts ändert sich")
|
||||
|
||||
## Abwerfen: eine Handkarte loswerden, dafür 1 Mana.
|
||||
func test_abwerfen() -> void:
|
||||
@@ -493,7 +470,6 @@ func test_verschmelzen() -> void:
|
||||
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)
|
||||
@@ -639,27 +615,35 @@ func test_booster() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 3
|
||||
for i in 50:
|
||||
var rolled := Profile.roll_booster(r)
|
||||
check(rolled.size() == 5, "5 Karten")
|
||||
var rolled := Profile.roll_booster(r, "Feuer")
|
||||
check(rolled.size() == 5 and rolled.all(func(x): return x.card.elements[0] == "Feuer"), "5 Feuerkarten")
|
||||
check(rolled.any(func(x): return x.rarity != "Common"), "mindestens eine Rare")
|
||||
var p := Profile.new()
|
||||
p.coins = Profile.BOOSTER_COST
|
||||
var got := p.open_booster(r)
|
||||
check(p.coins == 0 and got.size() == 5, "Booster kostet %d" % Profile.BOOSTER_COST)
|
||||
p.coins = Profile.ELEMENT_BOOSTER_COST
|
||||
check(p.open_booster("Feuer", r).is_empty() and p.coins == Profile.ELEMENT_BOOSTER_COST and not p.booster_unlocked("Feuer") and p.booster_unlocked(Cards.NEUTRAL), "Feuer-Booster erst ab Kapitel 2, neutral immer")
|
||||
p.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
check(p.booster_unlocked("Feuer") and not p.booster_unlocked("Wasser"), "Kapitel 2 offen: Feuer ja, Wasser nein")
|
||||
var got := p.open_booster("Feuer", r)
|
||||
check(p.coins == 0 and got.size() == 5 and got.all(func(x): return x.card.elements[0] == "Feuer"), "Feuer-Booster kostet %d" % Profile.ELEMENT_BOOSTER_COST)
|
||||
check(got.all(func(x): return p.owns(x.card.id) and x.card.id in p.deck), "alle fünf sind jetzt in Sammlung und Stapel")
|
||||
var fresh := got.filter(func(x): return x.new)
|
||||
check(not fresh.is_empty() and fresh.all(func(x): return x.dust == 0 and not x.upgraded), "neue Karten: kein Staub, kein Aufwerten")
|
||||
check(p.dust == got.reduce(func(a, x): return a + int(x.dust), 0), "Doppelte werden zu Staub")
|
||||
check(p.open_booster(r).is_empty(), "ohne Münzen kein Booster")
|
||||
check(p.open_booster("Feuer", r).is_empty(), "ohne Münzen kein Booster")
|
||||
p.coins = Profile.BOOSTER_COST
|
||||
check(p.open_booster(Cards.NEUTRAL, r).size() == 5 and p.coins == 0, "neutraler Booster kostet %d" % Profile.BOOSTER_COST)
|
||||
p.coins = Profile.ELEMENT_BOOSTER_COST * 3
|
||||
check(p.open_boosters("Feuer", 4, r).is_empty() and p.coins == Profile.ELEMENT_BOOSTER_COST * 3, "vier Packs: zu teuer, nichts passiert")
|
||||
check(p.open_boosters("Feuer", 3, r).size() == 15 and p.coins == 0, "drei Packs auf einmal: 15 Karten, alles bezahlt")
|
||||
p.collection = {"f_3_2": 0}
|
||||
p.deck = []
|
||||
for i in Game.DECK_MAX:
|
||||
p.deck.append("x%d" % i) # voll: neue Karten passen nicht mehr hinein
|
||||
p.coins = Profile.BOOSTER_COST * 20
|
||||
p.coins = Profile.ELEMENT_BOOSTER_COST * 20
|
||||
var dust_before := p.dust
|
||||
var upgraded := false
|
||||
for i in 20:
|
||||
for res in p.open_booster(r):
|
||||
for res in p.open_booster("Feuer", r):
|
||||
if res.card.id == "f_3_2" and res.upgraded:
|
||||
upgraded = true
|
||||
check(res.dust == Profile.DUST_DISMANTLE[Cards.RARITIES[Cards.RARITIES.find(res.rarity) - 1]] or res.dust > 0, "die alte Version wird zu Staub")
|
||||
@@ -691,13 +675,11 @@ func test_booster() -> void:
|
||||
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
|
||||
## Common-Karten, der Stapel hält 40 bis 60 eigene Karten.
|
||||
## Sammlung und Deck: Start mit den neutralen Karten, Herstellen für Staub, Zerlegen entfernt
|
||||
## Common-Karten, der Stapel hält 30 bis 60 eigene Karten.
|
||||
func test_sammlung_und_deck() -> void:
|
||||
var p := Profile.starter()
|
||||
check(not p.started() and p.collection.is_empty() and p.deck.is_empty(), "neues Profil: noch kein Startdeck")
|
||||
p.choose_start("Feuer")
|
||||
check(p.started() and p.collection.size() == Game.DECK_MIN and p.deck == DeckGen.start_deck("Feuer") and p.deck.all(func(id): return p.owns(id)), "Startdeck Feuer: 40 Karten in Sammlung und Stapel")
|
||||
check(p.collection.size() == 110 and p.deck.size() == Game.DECK_MIN and p.deck.all(func(id): return p.owns(id) and p.level(id) == 0 and Cards.by_id(id).is_neutral()), "neues Profil: alle 110 Neutralen in Common in der Sammlung, 30 im Stapel")
|
||||
check(p.toggle_deck("f_5_5") == "Karte nicht in der Sammlung", "fremde Karte nicht in den Stapel")
|
||||
var first: String = p.deck[0]
|
||||
check(p.toggle_deck(first) == "" and p.deck.size() == Game.DECK_MIN - 1, "Karte aus dem Stapel genommen")
|
||||
@@ -708,7 +690,7 @@ func test_sammlung_und_deck() -> void:
|
||||
p.deck = DeckGen.fill(p.deck, p.collection.keys(), Game.DECK_MAX)
|
||||
var outside: Array = p.collection.keys().filter(func(id): return not (id in p.deck))
|
||||
check(p.toggle_deck(outside[0]) == "Der Stapel ist voll (%d Karten)" % Game.DECK_MAX, "60 ist voll")
|
||||
p.choose_start("Feuer")
|
||||
p = Profile.starter()
|
||||
p.dust = Profile.CRAFT_COST - 1
|
||||
check(p.craft("f_5_5") == "Nicht genug Staub (%d nötig)" % Profile.CRAFT_COST, "Herstellen braucht Staub")
|
||||
p.dust = Profile.CRAFT_COST
|
||||
@@ -717,7 +699,7 @@ func test_sammlung_und_deck() -> void:
|
||||
var in_deck: String = p.deck[0]
|
||||
check(p.dismantle(in_deck) == "" and not p.owns(in_deck) and not (in_deck in p.deck) and p.dust == Profile.DUST_DISMANTLE["Common"], "Common zerlegt: raus aus Sammlung und Stapel, Staub dafür")
|
||||
var filled := p.deck_cards()
|
||||
check(filled.size() == Game.DECK_MIN and filled.all(func(id): return p.owns(id)) and filled.slice(0, p.deck.size()) == p.deck and "f_5_5" in filled, "zum Kampf wird der Stapel mit eigenen Karten bis 40 aufgefüllt")
|
||||
check(filled.size() == Game.DECK_MIN and filled.all(func(id): return p.owns(id)) and filled.slice(0, p.deck.size()) == p.deck, "zum Kampf wird der Stapel mit eigenen Karten bis 30 aufgefüllt")
|
||||
p.deck = []
|
||||
check(p.deck_cards().size() == Game.DECK_MIN, "leerer Stapel: Standard aus der Sammlung")
|
||||
var d := Profile.from_dict(JSON.parse_string(JSON.stringify(p.to_dict())))
|
||||
@@ -731,12 +713,11 @@ func test_spielstand() -> void:
|
||||
if FileAccess.file_exists(path + suffix):
|
||||
DirAccess.remove_absolute(abs + suffix)
|
||||
var p := Profile.load_or_new(path)
|
||||
check(p.coins == Profile.START_COINS and not p.started(), "Starterprofil: Startdeck noch zu wählen")
|
||||
p.choose_start("Wasser")
|
||||
check(p.collection.size() == Game.DECK_MIN and p.deck.size() == Game.DECK_MIN, "Startdeck gewählt")
|
||||
check(p.coins == Profile.START_COINS and p.collection.size() == 110 and p.deck.size() == Game.DECK_MIN, "Starterprofil: neutrale Karten")
|
||||
check(FileAccess.file_exists(path), "Datei angelegt")
|
||||
p.coins = 999
|
||||
p.collection["f_3_2"] = 2
|
||||
p.collection["f_0_1"] = 0
|
||||
p.deck = ["f_3_2", "f_0_1"]
|
||||
p.save(path)
|
||||
var p2 := Profile.load_or_new(path)
|
||||
@@ -829,7 +810,7 @@ func test_spielstand_migration() -> void:
|
||||
"collection": {"f_kobold": 2}, "decks": [], "settings": {"volume": 0.3}, "perk": "resonanz_doppelt"}
|
||||
var p := Profile.from_dict(alt)
|
||||
check(p.coins == 300 and p.dust == 70 and p.campaign_stage == 2, "alter Stand gelesen")
|
||||
check(not p.started() and p.deck.is_empty() and p.perk == "", "alte Sammlung verworfen, Startdeck wird neu gewählt, unbekanntes Merkmal verworfen")
|
||||
check(p.deck.size() == Game.DECK_MIN and p.collection.size() == 110 and p.perk == "", "alte Sammlung verworfen, neutrale Startsammlung, 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")
|
||||
@@ -845,23 +826,26 @@ func test_spielstand_migration() -> void:
|
||||
func test_kampagne() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 4
|
||||
check(Campaign.STAGES.size() == 10, "zehn Stufen")
|
||||
var boss := Campaign.opponent(4, r)
|
||||
check(boss.name == "Vulkar" and boss.title == "Herr der Lava" and boss.life == 24, "Stufe 5: Vulkar mit 24 Leben")
|
||||
check(Campaign.STAGES_PER_CHAPTER == 10 and Campaign.total() == 50, "fünf Kapitel mit je zehn Stufen")
|
||||
var boss := Campaign.opponent(4)
|
||||
check(boss.name == "Vulkar" and boss.title == "Söldnerfürst" and boss.life == 24, "Stufe 5: Vulkar mit 24 Leben")
|
||||
check(boss.deck.size() == Game.DECK_MIN and boss.looks.values().all(func(l): return l == 0), "Kapitel 1: Stapel in Common")
|
||||
var g := Game.new_battle_with(boss, r)
|
||||
check(g.opponent == boss and g.human.life == Game.START_LIFE, "Kampagnenkampf gestartet")
|
||||
check(Profile.battle_coins(false, 10, 20, 8) == 190 and Profile.battle_coins(true, 10, 20, 8) == 380, "Münzen: 5 je Zug, Karte und schnellem Zug, Sieg verdoppelt")
|
||||
check(Profile.battle_coins(true, 10, 20, 8, 1.5) == 570 and Profile.battle_coins(true, 10, 20, 8, 2.0) == 760 and Profile.battle_coins(false, 5, 10, 4, 2.0) == 190, "Schwierigkeit ×1,5 und ×2")
|
||||
check(Profile.DIFFICULTIES.leicht.reward == 1.0 and Profile.DIFFICULTIES.normal.reward == 1.5 and Profile.DIFFICULTIES.schwer.reward == 2.0, "Münzfaktoren 1 / 1,5 / 2")
|
||||
var p := Profile.starter()
|
||||
var reward := p.record_battle(true, 0)
|
||||
check(p.campaign_stage == 1 and reward == Campaign.STAGES[0].reward, "Stufe 1 abgeschlossen")
|
||||
reward = p.record_battle(true, 0)
|
||||
check(p.campaign_stage == 1 and reward == Profile.REWARD_WIN, "Wiederholung gibt normale Belohnung")
|
||||
reward = p.record_battle(false, 1)
|
||||
check(p.campaign_stage == 1 and reward == Profile.REWARD_LOSS, "Niederlage schaltet nichts frei")
|
||||
var reward := p.record_battle(true, 380, 0)
|
||||
check(p.campaign_stage == 1 and reward == 380 + Campaign.stage(0).reward and p.coins == Profile.START_COINS + reward, "Stufe 1 abgeschlossen: Münzen plus Stufenbelohnung")
|
||||
reward = p.record_battle(true, 380, 0)
|
||||
check(p.campaign_stage == 1 and reward == 380, "Wiederholung nur die Münzen des Kampfs")
|
||||
reward = p.record_battle(false, 190, 1)
|
||||
check(p.campaign_stage == 1 and reward == 190, "Niederlage schaltet nichts frei")
|
||||
p.campaign_stage = 9
|
||||
p.record_battle(true, 9)
|
||||
p.record_battle(true, 380, 9)
|
||||
check(p.campaign_stage == 10, "Kampagne abgeschlossen")
|
||||
var sim := Game.new_battle_with(Campaign.opponent(0, r), r)
|
||||
var sim := Game.new_battle_with(Campaign.opponent(0), r)
|
||||
var guard := 0
|
||||
while sim.winner == "" and guard < 300:
|
||||
AI.take_turn(sim, sim.current(), true)
|
||||
@@ -918,17 +902,17 @@ func test_merkmale() -> void:
|
||||
var mana := h.energy
|
||||
check(g.draw_cost(h) == 0 and g.draw(h) == "" and h.energy == mana and g.draw_cost(h) == 1, "Merkmal: ein Ziehen mehr umsonst")
|
||||
for perk in Game.PERKS:
|
||||
check(Campaign.STAGES.any(func(st): return st.perk == perk), "Merkmal vergeben: " + perk)
|
||||
check(range(Campaign.total()).any(func(i): return perk in Campaign.stage(i).perks), "Merkmal vergeben: " + perk)
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 2
|
||||
var mara: Game.Player = Game.new_battle_with(Campaign.opponent(1, r), r).opponent
|
||||
var mara: Game.Player = Game.new_battle_with(Campaign.opponent(1), r).opponent
|
||||
check(mara.life == 22 + 5 and mara.max_life == 27 and mara.perks == ["leben_35"], "Mara hat mit ihrem Merkmal 5 Leben mehr")
|
||||
var g3 := Game.new_battle_with(Campaign.opponent(9, r), r, true)
|
||||
var g3 := Game.new_battle_with(Campaign.opponent(9), r, true)
|
||||
var archi: Game.Player = g3.opponent
|
||||
check(g3.players[0] == g3.human, "feste Reihenfolge: Mensch beginnt")
|
||||
g3.end_turn()
|
||||
check(g3.current() == archi and archi.energy == Game.START_MANA + 2, "Archimagier hat im ersten Zug 2 Mana mehr")
|
||||
var nyx: Game.Player = Campaign.opponent(5, r)
|
||||
var nyx: Game.Player = Campaign.opponent(5)
|
||||
var g4 := Game.new_battle_with(nyx, r, true)
|
||||
g4.end_turn()
|
||||
check(nyx.max_energy == Game.START_MANA and nyx.energy == Game.START_MANA + 1, "Nyx bekommt 1 Mana mehr je Zug")
|
||||
@@ -956,17 +940,19 @@ func test_ki_zwei_schritte() -> void:
|
||||
|
||||
|
||||
func test_kapitel_zwei_und_merkmalwahl() -> void:
|
||||
check(Campaign.total() == 30 and Campaign.chapter(9) == 1 and Campaign.chapter(10) == 2 and Campaign.chapter(29) == 3, "dreißig Stufen in drei Kapiteln")
|
||||
check(Campaign.stage(25).perks.size() == 3 and Campaign.stage(25).name == "Nyx", "Kapitel 3 mit drei Merkmalen")
|
||||
check(Campaign.total() == 50 and Campaign.chapter(9) == 1 and Campaign.chapter(10) == 2 and Campaign.chapter(49) == 5, "fünfzig Stufen in fünf Kapiteln")
|
||||
check(Campaign.stage(25).perks.size() == 2 and Campaign.stage(25).name == "Nyx" and Campaign.stage(25).element == "Wasser", "Kapitel 3: Wasser, zwei Merkmale")
|
||||
check(Campaign.stage(45).perks.size() == 3 and Campaign.stage(40).element == "Erde" and Campaign.stage(49).reward == 1500 and Campaign.stage(10).reward == 350 and Campaign.stage(9).reward == 500, "Kapitel 5: Erde, drei Merkmale; Belohnungen 350 bis 1500, Boss 1 gibt 500")
|
||||
check(Campaign.stage(0).perks == ["feuer_angriff"] and Campaign.stage(23).perks == ["ziehen_gratis", "kein_nachteil"] and Campaign.stage(49).perks == ["start_energie", "wesen_angriff", "extra_mana"], "Merkmale je drei Plätze versetzt, nichts doppelt")
|
||||
for i in Campaign.total():
|
||||
var st := Campaign.stage(i)
|
||||
check(st.has("perks") and st.perks.all(func(k): return Game.PERKS.has(k)), "Stufe %d hat gültige Merkmale" % (i + 1))
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 8
|
||||
var archi2 := Campaign.opponent(19, r)
|
||||
check(archi2.name == "Archimagier" and archi2.perks.size() == 2 and archi2.life == 28, "letzte Stufe von Kapitel 2: Archimagier mit zwei Merkmalen und 28 Leben")
|
||||
check(archi2.looks.values().all(func(l): return l == 1), "Kapitel 2: seine Karten sind Rare")
|
||||
check(Campaign.opponent(25, r).looks.values().all(func(l): return l == 2), "Kapitel 3: Super Rare")
|
||||
var archi2 := Campaign.opponent(19)
|
||||
check(archi2.name == "Archimagier" and archi2.perks.size() == 1 and archi2.life == 28, "letzte Stufe von Kapitel 2: Archimagier mit einem Merkmal und 28 Leben")
|
||||
check(archi2.looks.values().all(func(l): return l == 0) and archi2.deck.all(func(id): return Cards.by_id(id).elements[0] == "Feuer"), "Kapitel 2: seine Karten sind Feuer in Common")
|
||||
check(Campaign.opponent(25).looks.values().all(func(l): return l == 1) and Campaign.opponent(45).looks.values().all(func(l): return l == 3), "Kapitel 3 Rare, Kapitel 5 Ultra Rare")
|
||||
var p := Profile.starter()
|
||||
check(not p.perk_unlocked(), "Merkmalwahl erst nach Kapitel 1")
|
||||
p.campaign_stage = 10
|
||||
@@ -975,12 +961,90 @@ func test_kapitel_zwei_und_merkmalwahl() -> void:
|
||||
var g := Game.new_battle(r, {"human_perks": [p.perk]})
|
||||
check(g.human.perks == ["feuer_angriff"] and g.log.any(func(l): return l.begins_with("Spieler: Feuerwesen")), "eigenes Merkmal wirkt und steht im Verlauf")
|
||||
p.campaign_stage = 19
|
||||
p.record_battle(true, 19)
|
||||
p.record_battle(true, 380, 19)
|
||||
check(p.campaign_stage == 20, "Kapitel 2 abgeschlossen")
|
||||
var v := Profile.from_dict(JSON.parse_string(JSON.stringify(p.to_dict())))
|
||||
check(v.perk == "feuer_angriff" and v.campaign_stage == 20, "gespeichert und gelesen")
|
||||
|
||||
|
||||
## Einzelspieler: der Gegner spielt ein offenes Element in einer Stufe relativ zu deiner Deckstärke.
|
||||
func test_einzelspieler_gegner() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 21
|
||||
var p := Profile.starter()
|
||||
check(p.deck_level() == 0 and p.unlocked_elements() == [Cards.NEUTRAL] and p.unlocked_cards().size() == 110 and p.unlocked_cards().all(func(c): return c.is_neutral()), "Start: Stufe Common, nur Neutral offen, 110 Karten sichtbar")
|
||||
check(Profile.DIFFICULTIES.leicht.level == -1 and Profile.DIFFICULTIES.normal.level == 0 and Profile.DIFFICULTIES.schwer.level == 1, "Versatz -1 / 0 / +1")
|
||||
p.settings.difficulty = "schwer"
|
||||
var o := p.single_opponent(r)
|
||||
check(o.deck.size() == Game.DECK_MIN and o.deck.all(func(id): return Cards.by_id(id).is_neutral()) and o.looks.values().all(func(l): return l == 1) and o.life == Game.START_LIFE, "Schwer: 30 Neutrale, eine Stufe über dir, 30 Leben")
|
||||
check(o.perks.size() == 1 and Game.PERKS.has(o.perks[0]) and o.title == Cards.NEUTRAL, "Schwer: ein zufälliges Merkmal, Element als Titel")
|
||||
p.settings.difficulty = "leicht"
|
||||
o = p.single_opponent(r)
|
||||
check(o.looks.values().all(func(l): return l == 0) and o.perks.is_empty(), "Leicht: nicht unter Common, kein Merkmal")
|
||||
for id in p.collection:
|
||||
p.collection[id] = 3
|
||||
check(p.deck_level() == 3 and p.opponent_level() == 2, "Ultra-Rare-Deck: Stufe 3, Leicht eine darunter")
|
||||
p.settings.difficulty = "normal"
|
||||
check(p.opponent_level() == 3 and p.single_opponent(r).looks.values().all(func(l): return l == 3), "Normal: deine Stufe")
|
||||
for id in p.deck.slice(0, 3):
|
||||
p.collection[id] = 4
|
||||
check(DeckGen.strength(p.deck_cards(), p.collection) == 410 and p.deck_level() == 3, "410 rundet auf Stufe 3")
|
||||
p.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
check(p.unlocked_elements() == [Cards.NEUTRAL, "Feuer"] and p.unlocked_cards().size() == 220, "Kapitel 2 offen: Feuer dazu, 220 Karten sichtbar")
|
||||
var seen := {}
|
||||
for i in 30:
|
||||
var e: String = Cards.by_id(p.single_opponent(r).deck[0]).elements[0]
|
||||
seen[e] = true
|
||||
check(seen.keys().size() == 2 and seen.has("Feuer") and seen.has(Cards.NEUTRAL), "Gegner würfelt aus Neutral und Feuer")
|
||||
p.campaign_stage = Campaign.total()
|
||||
check(p.unlocked_elements() == Array(Campaign.CHAPTER_ELEMENTS), "alles offen: alle fünf")
|
||||
var g := Game.new_battle_with(p.single_opponent(r), r, false, {"deck": p.deck_cards(), "looks": p.looks()})
|
||||
check(g.opponent.deck.size() + g.opponent.hand.size() == Game.DECK_MIN and g.opponent.hand.all(func(c): return c.look == 3), "im Kampf: 30 Karten in Stufe 3")
|
||||
|
||||
|
||||
## Skilltree: Münzen sind Erfahrung, je Level ein Punkt, Skills wirken auf Booster, Staub, Münzen und Kampf.
|
||||
func test_skilltree() -> void:
|
||||
check(Skills.xp_for_level(1) == 100 and Skills.xp_for_level(2) == 300 and Skills.xp_for_level(5) == 1500 and Skills.level_for(0) == 0 and Skills.level_for(299) == 1 and Skills.level_for(300) == 2, "Level brauchen 100, 300, 600 … Erfahrung")
|
||||
var p := Profile.starter()
|
||||
check(p.player_level() == 0 and p.skill_points() == 0 and p.learn("packglueck") == "Kein Skillpunkt frei", "Start: Level 0, kein Punkt")
|
||||
var reward := p.record_battle(true, 380)
|
||||
check(reward == 380 and p.xp == 380 and p.player_level() == 2 and p.skill_points() == 2 and p.take_notices() == ["Level 2 erreicht: +2 Skillpunkte"], "380 Münzen: Level 2, zwei Punkte, Meldung")
|
||||
check(p.learn("packglueck") == "" and p.skill("packglueck") == 1 and p.skill_points() == 1, "Packglück gelernt")
|
||||
check(p.learn("gratisziehen") == "" and p.learn("gratisziehen") == "Schon auf der höchsten Stufe" and p.skill_points() == 0, "Freies Ziehen hat nur eine Stufe")
|
||||
check(p.battle_perks() == ["ziehen_gratis"], "Kampfskill wird im Kampf zum Merkmal")
|
||||
p.perk = "leben_35"
|
||||
p.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
check(p.battle_perks() == ["leben_35", "ziehen_gratis"], "das gewählte Merkmal kommt dazu")
|
||||
p.xp = 100000
|
||||
for i in 3:
|
||||
p.learn("muenzen")
|
||||
p.learn("staub")
|
||||
p.learn("packpreis")
|
||||
p.learn("packpreis")
|
||||
check(is_equal_approx(p.coin_factor(), 1.3) and p.pack_price(Cards.NEUTRAL) == 80 and p.pack_price("Feuer") == 160 and p.dust_gain(100) == 175, "Münzsegen ×1,3, Händler −20 %, Staubsammler +75 %")
|
||||
var coins := p.coins
|
||||
check(p.record_battle(false, 200) == 260 and p.coins == coins + 260, "260 statt 200 Münzen")
|
||||
p.coins = 80
|
||||
check(p.open_booster(Cards.NEUTRAL).size() == 5 and p.coins == 0, "neutraler Booster kostet mit Händler 80")
|
||||
var rr := RandomNumberGenerator.new()
|
||||
rr.seed = 9
|
||||
var commons := 0
|
||||
var commons0 := 0
|
||||
for i in 200:
|
||||
commons += Profile.roll_booster(rr, Cards.NEUTRAL, 3).filter(func(x): return x.rarity == "Common").size()
|
||||
commons0 += Profile.roll_booster(rr, Cards.NEUTRAL, 0).filter(func(x): return x.rarity == "Common").size()
|
||||
check(commons < commons0 * 0.7, "Packglück 3: deutlich weniger Common (%d statt %d)" % [commons, commons0])
|
||||
p.collection["n_0_1"] = 1
|
||||
var dust := p.dust
|
||||
check(p.dismantle("n_0_1") == "" and p.dust == dust + 175, "Zerlegen einer Rare mit Staubsammler 3: 175 statt 100")
|
||||
var d := Profile.from_dict(JSON.parse_string(JSON.stringify(p.to_dict())))
|
||||
check(d.xp == p.xp and d.skills == p.skills and d.skill_points() == p.skill_points(), "Erfahrung und Skills gespeichert")
|
||||
var bad := Profile.from_dict({"version": Profile.SAVE_VERSION, "xp": 100, "skills": {"gibt_es_nicht": 2, "packglueck": 9}})
|
||||
check(bad.skill("packglueck") == 3 and bad.skills.size() == 1 and bad.player_level() == 1, "unbekannte Skills fliegen raus, Stufen gedeckelt")
|
||||
check(Skills.pack_weights(0) == PackedFloat32Array([45, 25, 15, 10, 5]) and Skills.pack_weights(3) == PackedFloat32Array([21, 34, 21, 16, 8]), "Packglück 3: Chancen 21/34/21/16/8")
|
||||
check(Skills.effect("packglueck", 1) == "Chancen 37/28/17/12/6 %" and Skills.effect("packpreis", 2) == "Neutral 80, Element 160 Münzen" and Skills.effect("startmana", 1) == "erster Zug: 3 Mana" and Skills.effect("gratisziehen", 0) == "eine Karte je Zug gratis", "Wirkungstexte in Zahlen")
|
||||
|
||||
|
||||
func test_ki_zeitbudget() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 12
|
||||
|
||||
+157
-68
@@ -133,11 +133,8 @@ func screen():
|
||||
return main.screen
|
||||
|
||||
|
||||
## Jeder Abschnitt startet im Menü, auch wenn ein vorheriger abgebrochen ist; ohne Startdeck wird
|
||||
## Feuer gewählt.
|
||||
## Jeder Abschnitt startet im Menü, auch wenn ein vorheriger abgebrochen ist.
|
||||
func ensure_menu() -> void:
|
||||
if screen() is StartDeckScreen:
|
||||
await click(find_button("Feuer wählen"))
|
||||
if TranslationServer.get_locale().begins_with("en"):
|
||||
main.set_language("de")
|
||||
if not (screen() is MenuScreen):
|
||||
@@ -188,7 +185,7 @@ func _ready() -> void:
|
||||
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,
|
||||
test_kampagne, test_sammlung, test_deck, test_sprache, test_kampf_steuerung, test_kampf_elemente_und_verschmelzen, test_kampf_grenzen]
|
||||
test_kampagne, test_sammlung, test_skilltree, test_cheats, test_deck, test_sprache, test_kampf_steuerung, test_kampf_elemente_und_verschmelzen, test_kampf_grenzen]
|
||||
suite += [test_kampf.bind(3), test_kampf_abbrechen]
|
||||
suite.append(test_profil_zuruecksetzen)
|
||||
var only := OS.get_environment("DINGER_ONLY") # z. B. DINGER_ONLY=test_sammlung,test_kampf
|
||||
@@ -204,16 +201,14 @@ func _ready() -> void:
|
||||
get_tree().quit(1 if failed > 0 else 0)
|
||||
|
||||
|
||||
## Erster Start: eines von vier Startdecks wählen (ensure_menu hat schon eins gewählt, darum frisch).
|
||||
## Neues Profil: die neutralen Karten als Sammlung und Stapel, direkt ins Menü.
|
||||
func test_start() -> void:
|
||||
main.reset_profile()
|
||||
await frames(2)
|
||||
check(screen() is StartDeckScreen and not main.profile.started(), "neues Profil: Startdeck wählen")
|
||||
await shot("00_startdeck")
|
||||
await click(find_button("Feuer wählen"))
|
||||
var p: Profile = main.profile
|
||||
check(screen() is MenuScreen and p.started() and p.deck.size() == Game.DECK_MIN and p.deck.filter(func(id): return Cards.by_id(id).elements[0] == "Feuer").size() == 25, "Feuer gewählt: 40 Karten, 25 davon Feuer")
|
||||
check(Profile.load_or_new(SAVE).started(), "gespeichert")
|
||||
check(screen() is MenuScreen and p.collection.size() == 110 and p.deck.size() == Game.DECK_MIN and p.deck.all(func(id): return Cards.by_id(id).is_neutral()), "neues Profil: 110 Neutrale, 30 im Stapel")
|
||||
await shot("00_start")
|
||||
check(Profile.load_or_new(SAVE).collection.size() == 110, "gespeichert")
|
||||
|
||||
|
||||
func test_menu() -> void:
|
||||
@@ -244,6 +239,32 @@ func test_booster() -> void:
|
||||
else:
|
||||
check(res.dust > 0, "sonst gibt es Staub")
|
||||
check(scr.b_buy.disabled == (main.profile.coins < Profile.BOOSTER_COST), "Kaufknopf folgt dem Münzstand")
|
||||
check(not scr.pack_tiles["Feuer"].visible and scr.pack_tiles[Cards.NEUTRAL].visible and scr.pack_tiles[Cards.NEUTRAL].position.x == 530, "Feuer-Pack bis Kapitel 2 unsichtbar, Neutral mittig")
|
||||
check(scr.summary_label.text.ends_with("Staub") and scr.pack_tiles[Cards.NEUTRAL].pulse, "Fazit steht, das gekaufte Pack pulsiert")
|
||||
var saved_coins: int = main.profile.coins
|
||||
main.profile.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
main.profile.coins = Profile.ELEMENT_BOOSTER_COST
|
||||
scr.refresh()
|
||||
check(scr.pack_tiles["Feuer"].visible and not scr.pack_buttons["Feuer"].disabled and "200" in scr.pack_tiles["Feuer"].hint and not scr.pack_tiles["Wasser"].visible, "Kapitel 2 offen: Feuer kaufbar, Wasser unsichtbar")
|
||||
await click(scr.pack_buttons["Feuer"])
|
||||
check(main.profile.coins == 0 and scr.cards.size() == 5 and scr.cards.all(func(res): return res.card.elements[0] == "Feuer"), "Feuer-Booster: fünf Feuerkarten")
|
||||
await click(scr.b_all)
|
||||
await shot("04b_booster_feuer")
|
||||
main.profile.coins = Profile.BOOSTER_COST * 5
|
||||
scr.refresh()
|
||||
check(not scr.pack_tiles[Cards.NEUTRAL].buy_buttons[1].disabled and scr.pack_tiles[Cards.NEUTRAL].buy_buttons[2].disabled, "×5 bezahlbar, ×10 nicht")
|
||||
await click(scr.pack_tiles[Cards.NEUTRAL].buy_buttons[1])
|
||||
check(scr.cards.size() == 25 and main.profile.coins == 0 and scr.revealed.is_empty() and scr.slots.get_children().filter(func(c): return c is BoosterScreen.CardBack).size() == 25, "fünf Packs auf einmal: 25 verdeckte Karten")
|
||||
check(Cards.RARITIES.find(scr.cards[0].rarity) >= Cards.RARITIES.find(scr.cards[24].rarity), "beste Stufe zuerst")
|
||||
await frames(30)
|
||||
await shot("04c_booster_fuenf_zu")
|
||||
await click(scr.b_all)
|
||||
check(scr.revealed.size() == 25 and scr.summary_label.text.begins_with(str(scr.cards.filter(func(x): return x.new).size())) and "Staub" in scr.summary_label.text, "alle aufgedeckt, Fazit zählt neu und Staub")
|
||||
await frames(40)
|
||||
await shot("04d_booster_fuenf_offen")
|
||||
main.profile.campaign_stage = 0
|
||||
main.profile.coins = saved_coins
|
||||
main.save()
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
@@ -395,13 +416,13 @@ func test_gegnerauswahl() -> void:
|
||||
check(find_button("Neu würfeln") == null and find_button("Feuer") == null and find_button("Starten") != null, "nur Schwierigkeit und Starten")
|
||||
await click(find_button("Schwer"))
|
||||
check(main.profile.settings.difficulty == "schwer" and Profile.load_or_new(SAVE).settings.difficulty == "schwer", "Schwer gewählt und gespeichert")
|
||||
check(str(Profile.DIFFICULTIES.schwer.life) in sel.info_label.text and str(int(Profile.REWARD_WIN * 1.5)) in sel.info_label.text, "Leben des Gegners und Belohnung stehen dabei")
|
||||
check("Rare" in sel.info_label.text and "Merkmal" in sel.info_label.text and "×2" in sel.info_label.text, "Schwer: Gegnerkarten Rare, Merkmal und Münzfaktor stehen dabei: " + sel.info_label.text)
|
||||
await shot("07_gegnerauswahl")
|
||||
await click(find_button("Starten"))
|
||||
var hard = screen()
|
||||
check(hard is BattleScreen and hard.game.players.size() == 2, "Duell gestartet")
|
||||
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(opp.max_life == Game.START_LIFE and hard.smart_ai and opp.perks.size() == 1 and opp.looks.values().all(func(l): return l == 1) and opp.title == Cards.NEUTRAL, "Schwer: 30 Leben, Vorausschau-KI, ein Merkmal, Neutral in Rare")
|
||||
check(hard.game.human.hand.size() == Game.START_HAND and hard.hand_views.size() == Game.START_HAND, "Start: fünf Karten auf der Hand")
|
||||
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")
|
||||
@@ -414,7 +435,7 @@ func test_gegnerauswahl() -> void:
|
||||
await click(find_button("Starten"))
|
||||
var easy = screen()
|
||||
opp = easy.game.opponent
|
||||
check(opp.max_life == Profile.DIFFICULTIES.leicht.life and not easy.smart_ai, "Leicht: Gegner mit %d Leben, einfache KI" % Profile.DIFFICULTIES.leicht.life)
|
||||
check(opp.max_life == Game.START_LIFE and not easy.smart_ai and opp.perks.is_empty() and opp.looks.values().all(func(l): return l == 0), "Leicht: 30 Leben, einfache KI, kein Merkmal, Common")
|
||||
check(easy.game.players[0].max_energy == Game.START_MANA and easy.game.human.life == Game.START_LIFE, "gleiche Regeln für beide")
|
||||
await give_up()
|
||||
await click(find_button("Einzelspieler"))
|
||||
@@ -473,28 +494,89 @@ func test_einstellungen() -> void:
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
|
||||
## Deck: eigene Karten rein und raus, Auffüllen nach Filter, Speichern. Beginnt mit dem frischen Startdeck.
|
||||
func test_deck() -> void:
|
||||
main.profile.choose_start("Feuer")
|
||||
## Skilltree: Level und Punkte aus der Erfahrung, Lernen verbraucht Punkte, Knöpfe folgen dem Stand.
|
||||
func test_skilltree() -> void:
|
||||
main.profile.xp = Skills.xp_for_level(3)
|
||||
main.profile.skills = {}
|
||||
main.save()
|
||||
await click(find_button("Skilltree"))
|
||||
var scr = screen()
|
||||
check(scr is SkillScreen and scr.level_label.text == "Level 3" and scr.points_label.text == "Skillpunkte: 3", "Skilltree: Level 3, drei Punkte")
|
||||
check(scr.learn_buttons.size() == Skills.SKILLS.size() and not scr.learn_buttons["packglueck"].disabled and scr.xp_label.text == "0 / 400 EP bis Level 4", "alle Skills mit Lernen-Knopf, Fortschritt bis Level 4")
|
||||
check(scr.now_labels["packglueck"].text == "Jetzt: Chancen 45/25/15/10/5 %" and scr.next_labels["packglueck"].text == "Nächste Stufe: Chancen 37/28/17/12/6 %", "Wirkung jetzt und nächste Stufe in Zahlen")
|
||||
await click(scr.learn_buttons["packglueck"])
|
||||
check(main.profile.skill("packglueck") == 1 and scr.points_label.text == "Skillpunkte: 2" and scr.rank_labels["packglueck"].text == "Stufe 1 / 3", "Packglück gelernt, Punkt verbraucht")
|
||||
await click(scr.learn_buttons["gratisziehen"])
|
||||
check(scr.learn_buttons["gratisziehen"].disabled and scr.rank_labels["gratisziehen"].text == "Stufe 1 / 1" and scr.next_labels["gratisziehen"].text == "Höchste Stufe erreicht", "Freies Ziehen voll: Knopf aus")
|
||||
await shot("28_skilltree")
|
||||
check(Profile.load_or_new(SAVE).skill("packglueck") == 1, "gespeichert")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen and screen().coll_label.text.ends_with("Level 3"), "zurück im Menü, Level steht dabei")
|
||||
main.profile.xp = 0
|
||||
main.profile.skills = {}
|
||||
main.save()
|
||||
|
||||
|
||||
## Cheats: Knöpfe setzen Münzen, Level, Kapitel und Sammlung sofort und speichern.
|
||||
func test_cheats() -> void:
|
||||
await click(find_button("Cheats"))
|
||||
var scr = screen()
|
||||
check(scr is CheatScreen and scr.status_label.text.begins_with("Münzen %d" % main.profile.coins), "Cheats geöffnet, Stand steht oben")
|
||||
var coins: int = main.profile.coins
|
||||
await click(find_button("+1.000 Münzen"))
|
||||
check(main.profile.coins == coins + 1000 and Profile.load_or_new(SAVE).coins == coins + 1000 and scr.msg_label.text == "Gespeichert", "+1.000 Münzen, gespeichert")
|
||||
await click(find_button("+1 Level"))
|
||||
check(main.profile.player_level() == 1 and main.profile.skill_points() == 1, "+1 Level: ein Skillpunkt")
|
||||
await click(find_button("Kapitel 3 frei"))
|
||||
check(main.profile.campaign_stage == 20 and main.profile.unlocked_elements().size() == 3, "Kapitel 3 frei: Neutral, Feuer, Wasser")
|
||||
await click(find_button("Alle Karten Ultimate Rare"))
|
||||
check(main.profile.collection.size() == Cards.all().size() and main.profile.level("f_5_5") == 4, "alle 550 Karten in Ultimate Rare")
|
||||
await shot("29_cheats")
|
||||
await click(find_button("Sammlung zurücksetzen"))
|
||||
check(main.profile.collection.size() == 110 and main.profile.deck.size() == Game.DECK_MIN, "Sammlung wieder am Start")
|
||||
await click(find_button("Kampagne zurücksetzen"))
|
||||
await click(find_button("Erfahrung auf 0"))
|
||||
check(main.profile.campaign_stage == 0 and main.profile.xp == 0, "Kampagne und Erfahrung zurück")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
|
||||
## Deck: eigene Karten rein und raus, Auffüllen nach Filter, Speichern. Beginnt mit einem frischen Profil.
|
||||
func test_deck() -> void:
|
||||
main.reset_profile()
|
||||
await frames(2)
|
||||
await click(find_button("Deck"))
|
||||
var scr = screen()
|
||||
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")
|
||||
check(scr.strength_label.text == "Stärke 100", "Startsammlung 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)
|
||||
check(scr.strength_label.text == "Stärke %d" % roundi((29 * 100 + 500) / 30.0), "eine Ultimate Rare hebt die Stärke: " + scr.strength_label.text)
|
||||
check(scr.grid.views()[0].card.id == p.deck[0] and scr.grid.views()[0].rarity == "Ultimate Rare", "höchste Stufe steht vorn")
|
||||
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")
|
||||
check(views.size() == 110 and views.slice(0, Game.DECK_MIN).all(func(v): return not v.dim and v.marked) and views[Game.DECK_MIN].dim and not views[Game.DECK_MIN].marked and p.deck.size() == Game.DECK_MIN, "Stapelkarten vorn mit Haken, der Rest dunkel")
|
||||
check(scr.curve.counts.size() == 11 and scr.curve.counts.reduce(func(a, v): return a + v, 0) == Game.DECK_MIN and scr.curve.counts[0] == 2, "Manakurve: 30 Karten verteilt, zwei zu 0 Mana")
|
||||
check(scr.share.counts == {Cards.NEUTRAL: Game.DECK_MIN} and scr.share.shown() == [Cards.NEUTRAL], "Elementanteile: nur Neutral")
|
||||
check(views[0].card.cost <= views[1].card.cost and views[Game.DECK_MIN].card.cost <= views[Game.DECK_MIN + 1].card.cost, "innerhalb von Stapel und Rest nach Kosten")
|
||||
await click(scr.cost_buttons[3])
|
||||
views = scr.grid.views()
|
||||
check(scr.cost_filter == 3 and views.size() == 15 and views.all(func(v): return v.card.cost == 3), "Manafilter 3: nur die 15 Karten zu 3 Mana")
|
||||
await click(scr.cost_buttons[3])
|
||||
views = scr.grid.views()
|
||||
check(scr.cost_filter == -1 and views.size() == 110, "derselbe Knopf hebt den Manafilter auf")
|
||||
check(scr.element_buttons.size() == 5 and scr.element_buttons["Feuer"].tooltip_text == "Feuer" and not scr.element_buttons["Feuer"].visible and scr.element_buttons[Cards.NEUTRAL].visible and scr.element_buttons[Cards.NEUTRAL].position.x == 108, "Elementfilter als Symbolknöpfe, nur offene Elemente")
|
||||
await hover(views[2])
|
||||
check(scr.preview_holder.get_child_count() == 1 and scr.preview_holder.get_child(0).card.id == views[2].card.id, "Zeigen stellt die Karte groß in die Vorschau")
|
||||
var first_id: String = views[0].card.id
|
||||
await click(views[0])
|
||||
check(not (first_id in p.deck) and p.deck.size() == Game.DECK_MIN - 1 and scr.grid.views().any(func(v): return v.card.id == first_id and v.dim), "Klick nimmt die Karte aus dem Stapel, sie wird dunkel")
|
||||
check(not (first_id in p.deck) and p.deck.size() == Game.DECK_MIN - 1 and scr.grid.views().any(func(v): return v.card.id == first_id and v.dim and not v.marked), "Klick nimmt die Karte aus dem Stapel, sie wird dunkel und verliert den Haken")
|
||||
check(scr.curve.counts.reduce(func(a, v): return a + v, 0) == Game.DECK_MIN - 1 and scr.msg_label.text == "Klick auf eine Karte: rein oder raus.", "Manakurve folgt, Hinweis steht")
|
||||
var back_view = scr.grid.views().filter(func(v): return v.card.id == first_id)[0]
|
||||
scr.grid.ensure_control_visible(back_view) # sie liegt jetzt hinter den Stapelkarten, außerhalb des Sichtbereichs
|
||||
await frames(2)
|
||||
@@ -503,12 +585,21 @@ func test_deck() -> void:
|
||||
await shot("19_deck")
|
||||
await click(find_button("Leeren"))
|
||||
check(p.deck.is_empty(), "geleert")
|
||||
await click(find_button("Wasser"))
|
||||
for id in ["w_0_1", "w_0_2", "w_0_3", "w_0_4", "w_0_5"]:
|
||||
p.collection[id] = 0
|
||||
main.profile.campaign_stage = 2 * Campaign.STAGES_PER_CHAPTER # Wasser offen
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
check(scr.element_buttons["Wasser"].visible and scr.element_buttons["Wasser"].position.x == 108 + 2 * 40, "Kapitel 3 offen: Wasserfilter an dritter Stelle")
|
||||
await click(scr.element_buttons["Wasser"])
|
||||
await click(find_button("Auffüllen"))
|
||||
check(p.deck.size() == 5 and p.deck.all(func(id): return Cards.by_id(id).elements[0] == "Wasser"), "Auffüllen hält sich an den Filter: nur die fünf Wasserkarten")
|
||||
check(scr.share.shown() == ["Wasser"] and scr.share.counts["Wasser"] == 5, "Elementanteile: fünf Wasser")
|
||||
await click(find_button("Alle"))
|
||||
await click(find_button("Auffüllen"))
|
||||
check(p.deck.size() == Game.DECK_MIN and Profile.load_or_new(SAVE).deck == p.deck, "ohne Filter alles, gespeichert")
|
||||
check(p.deck.size() == Game.DECK_MAX and Profile.load_or_new(SAVE).deck == p.deck, "ohne Filter bis 60, gespeichert")
|
||||
main.profile.campaign_stage = 0
|
||||
main.save()
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
@@ -518,30 +609,38 @@ func test_sammlung() -> void:
|
||||
var scr = screen()
|
||||
check(scr is CollectionScreen, "Sammlung geöffnet")
|
||||
var p: Profile = main.profile
|
||||
check(scr.grid.views().size() == Cards.all().size(), "alle Karten des Spiels sind zu sehen")
|
||||
check(scr.grid.views().slice(0, p.collection.size()).all(func(v): return not v.dim) and scr.grid.views().back().dim, "eigene Karten vorn, fehlende abgedunkelt")
|
||||
check(scr.grid.views().size() == 110 and scr.grid.views().all(func(v): return v.card.is_neutral()), "nur die Karten offener Elemente: zu Beginn die 110 Neutralen")
|
||||
check(find_button("Feuer") == null and find_button("Neutral") != null, "Filter nur für offene Elemente")
|
||||
check(scr.grid.views().all(func(v): return not v.dim), "zu Beginn gehören dir alle gezeigten Karten")
|
||||
p.dust = 5000
|
||||
scr.search_box.text = "Lodernder Funke"
|
||||
scr.search_box.text_changed.emit("Lodernder Funke")
|
||||
scr.search_box.text = "Tapferer Knappe"
|
||||
scr.search_box.text_changed.emit("Tapferer Knappe")
|
||||
await frames(1)
|
||||
var funke = scr.grid.views().filter(func(v): return v.card.id == "f_2_1")[0]
|
||||
var funke = scr.grid.views().filter(func(v): return v.card.id == "n_2_1")[0]
|
||||
await click(funke)
|
||||
check(scr.selected.id == "f_2_1" and p.rarity("f_2_1") == "Common" and scr.b_upgrade.visible, "eigene Karte gewählt, noch Common")
|
||||
check(scr.selected.id == "n_2_1" and p.rarity("n_2_1") == "Common" and scr.b_upgrade.visible, "eigene Karte gewählt, noch Common")
|
||||
await click(scr.b_upgrade)
|
||||
check(p.level("f_2_1") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["Rare"], "auf Rare aufgewertet")
|
||||
check(p.level("n_2_1") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["Rare"], "auf Rare aufgewertet")
|
||||
scr.search_box.text = ""
|
||||
scr.search_box.text_changed.emit("")
|
||||
await frames(1)
|
||||
var front = scr.grid.views().filter(func(v): return v.card.id == "f_2_1")[0]
|
||||
var front = scr.grid.views().filter(func(v): return v.card.id == "n_2_1")[0]
|
||||
check(front.rarity == "Rare" and front.card.attack == 3 and front.card.health == 2 and scr.grid.views()[0].rarity != "Common", "die Rare zeigt +1/+1, aufgewertete stehen vorn: %s %d/%d" % [front.rarity, front.card.attack, front.card.health])
|
||||
check(Profile.load_or_new(SAVE).level("f_2_1") == 1, "Sammlung gespeichert")
|
||||
scr.search_box.text = "Lodernder Funke"
|
||||
scr.search_box.text_changed.emit("Lodernder Funke")
|
||||
check(Profile.load_or_new(SAVE).level("n_2_1") == 1, "Sammlung gespeichert")
|
||||
scr.search_box.text = "Tapferer Knappe"
|
||||
scr.search_box.text_changed.emit("Tapferer Knappe")
|
||||
await frames(1)
|
||||
var dust: int = p.dust
|
||||
await click(scr.b_dismantle)
|
||||
check(p.level("f_2_1") == 0 and p.dust == dust + Profile.DUST_DISMANTLE["Rare"], "wieder zerlegt")
|
||||
# Herstellen einer fehlenden Karte
|
||||
check(p.level("n_2_1") == 0 and p.dust == dust + Profile.DUST_DISMANTLE["Rare"], "wieder zerlegt")
|
||||
# Herstellen einer fehlenden Karte (Feuer: erst ab Kapitel 2 zu sehen)
|
||||
scr.search_box.text = ""
|
||||
scr.search_box.text_changed.emit("")
|
||||
main.profile.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
var owned_shown: int = scr.grid.views().filter(func(v): return not v.dim).size()
|
||||
check(scr.grid.views().size() == 220 and owned_shown < 220 and scr.grid.views().slice(0, owned_shown).all(func(v): return not v.dim) and scr.grid.views().slice(owned_shown).all(func(v): return v.dim), "Kapitel 2 offen: auch Feuer zu sehen, eigene vorn, fehlende abgedunkelt")
|
||||
scr.search_box.text = "Infernalischer Drache"
|
||||
scr.search_box.text_changed.emit("Infernalischer Drache")
|
||||
await frames(1)
|
||||
@@ -551,6 +650,8 @@ func test_sammlung() -> void:
|
||||
await click(scr.b_craft)
|
||||
check(p.owns("f_5_5") and p.dust == dust - Profile.CRAFT_COST, "hergestellt")
|
||||
await shot("20_sammlung")
|
||||
main.profile.campaign_stage = 0
|
||||
main.save()
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
@@ -594,9 +695,7 @@ func test_profil_zuruecksetzen() -> void:
|
||||
check(not scr.confirm.visible, "abgebrochen")
|
||||
await click(find_button("Profil zurücksetzen"))
|
||||
await click(find_button("Ja, zurücksetzen"))
|
||||
check(screen() is StartDeckScreen and main.profile.coins == Profile.START_COINS and not main.profile.started(), "Profil zurückgesetzt: Startdeck neu wählen")
|
||||
await click(find_button("Luft wählen"))
|
||||
check(screen() is MenuScreen and main.profile.deck.filter(func(id): return Cards.by_id(id).elements[0] == "Luft").size() == 25, "Luft gewählt")
|
||||
check(screen() is MenuScreen and main.profile.coins == Profile.START_COINS and main.profile.collection.size() == 110 and main.profile.campaign_stage == 0, "Profil zurückgesetzt: neutrale Startsammlung, zurück im Menü")
|
||||
check(Profile.load_or_new(SAVE).coins == Profile.START_COINS, "zurückgesetzt gespeichert")
|
||||
|
||||
|
||||
@@ -745,9 +844,7 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
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 + [Cards.NEUTRAL]:
|
||||
scr.opponent().played[e] = 0
|
||||
scr.opponent().board.clear() # was die KI im ersten Zug gelegt hat, stört die Paare
|
||||
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
|
||||
@@ -765,12 +862,11 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
g.human.hand = [Cards.by_id("f_2_1").copy(), Cards.by_id("l_1_1").copy()]
|
||||
g.human.energy = 20
|
||||
g.human.looks = {"w_3_3": 3}
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.element_view.text() == "Feuer 2/0 (Doppelschlag) · Wasser 1/0", "Zähler: nur Elemente mit Wesen oder gespielten Karten, Feuerpaar: " + scr.element_view.text())
|
||||
check(scr.opp_element_view.text() == "Luft 1/0", "die Liste 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")
|
||||
check(scr.element_view.text() == "Feuer 2 (Doppelschlag) · Wasser 1", "Liste: nur Elemente mit offenen Wesen, Feuerpaar: " + scr.element_view.text())
|
||||
check(scr.opp_element_view.text() == "Luft 1", "die Liste 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, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken")
|
||||
# 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())
|
||||
@@ -786,7 +882,7 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
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/1"), "mittlere Maustaste legt verdeckt: zählt als ausgespielt, nicht als offen")
|
||||
check(mine_hidden.hidden and g.human.energy == mana - 1 and scr.element_view.text().begins_with("Feuer 2 (Doppelschlag)"), "mittlere Maustaste legt verdeckt: zählt 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")
|
||||
@@ -797,26 +893,15 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
check(mine_hidden.hidden and scr.msg_label.text == "Lodernder Funke 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 (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 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/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 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())
|
||||
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3 (Doppelschlag)"), "aufgedeckt: drei offene Feuer: " + scr.element_view.text())
|
||||
check(wolf.power() == 3 and falke.power() == 3 and mine_hidden.can_attack(), "Werte bleiben die der Karten, aufgedeckt greift sofort an")
|
||||
await shot("23b_drei_feuer")
|
||||
# Angriffsziel: Feuer gegen Luft schlägt doppelt, der Falke kontert halb; 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 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")
|
||||
check(scr.selected == wolf and scr.tiles[falke.uid].matchup == -1 and scr.tiles[secret.uid].matchup == 0, "Angreifer gewählt: der Falke kontert halbiert, nichts gegen Verdeckte")
|
||||
check(scr.tiles[falke.uid].shown_stats() == [2, falke.health], "der Falke zeigt seinen Konter gegen den Wolf: 3 halbiert, aufgerundet 2")
|
||||
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")
|
||||
check(scr.tiles[falke.uid].doomed and not scr.own_views[0].doomed and scr.own_views[0].shown_stats()[0] == wolf.power() * 2, "über dem Falken: er stirbt, der Wolf nicht, der Wolf schlägt doppelt zu")
|
||||
await shot("23c_angreifer_gewaehlt")
|
||||
check(not g.can_attack(wolf, scr.opponent())[0], "Held nicht angreifbar, solange Wesen stehen")
|
||||
await click(scr.tiles[secret.uid])
|
||||
@@ -866,7 +951,8 @@ func test_kampagne() -> void:
|
||||
var scr = screen()
|
||||
check(scr is CampaignScreen, "Kampagne geöffnet")
|
||||
await shot("15_kampagne")
|
||||
check(scr.node_buttons.filter(func(b): return b.disabled).size() == Campaign.STAGES.size() - 1 and find_button("Kämpfen") != null, "neun Stationen gesperrt, die erste offen")
|
||||
check(scr.node_buttons.filter(func(b): return b.disabled).size() == Campaign.STAGES_PER_CHAPTER - 1 and find_button("Kämpfen") != null, "neun Stationen gesperrt, die erste offen")
|
||||
check(find_button("Kapitel 5") != null and find_button("Kapitel 5").disabled, "fünf Kapitel, das letzte gesperrt")
|
||||
await click(scr.node_buttons[3])
|
||||
check(scr.selected == 0, "gesperrte Station lässt sich nicht wählen")
|
||||
check(find_button("Kapitel 2").disabled and scr.perk_box == null, "Kapitel 2 und Merkmal noch gesperrt")
|
||||
@@ -878,7 +964,7 @@ func test_kampagne() -> void:
|
||||
await shot("16_kampagne_kampf")
|
||||
await give_up()
|
||||
# Kapitel 1 als abgeschlossen setzen: Kapitel 2 und Merkmalwahl
|
||||
main.profile.campaign_stage = Campaign.STAGES.size()
|
||||
main.profile.campaign_stage = Campaign.STAGES_PER_CHAPTER
|
||||
await click(find_button("Kampagne"))
|
||||
var scr2 = screen()
|
||||
check(scr2.chapter == 2 and scr2.perk_box != null, "Kapitel 2 offen, Merkmal wählbar")
|
||||
@@ -888,7 +974,8 @@ func test_kampagne() -> void:
|
||||
await shot("27_kampagne_kapitel2")
|
||||
await click(find_button("Kämpfen"))
|
||||
var b2 = screen()
|
||||
check(b2 is BattleScreen and b2.stage == 10 and b2.game.opponent.perks.size() == 2 and b2.game.opponent.life == 22, "Kapitel-2-Boss mit zwei Merkmalen und 22 Leben")
|
||||
check(b2 is BattleScreen and b2.stage == 10 and b2.game.opponent.perks.size() == 1 and b2.game.opponent.life == 22, "Kapitel-2-Gegner mit einem Merkmal und 22 Leben")
|
||||
check(b2.game.opponent.deck.all(func(id): return Cards.by_id(id).elements[0] == "Feuer") and b2.game.opponent.looks.values().all(func(l): return l == 0), "Kapitel 2: nur Feuer in Common")
|
||||
check(b2.game.human.perks == [main.profile.perk], "eigenes Merkmal im Kampf")
|
||||
await give_up()
|
||||
main.profile.campaign_stage = 0
|
||||
@@ -934,6 +1021,8 @@ func test_kampf(shot_turn: int) -> void:
|
||||
await frames(3)
|
||||
await shot("09_kampf_ende")
|
||||
check(scr.reward > 0 and main.profile.coins >= coins + scr.reward, "Belohnung gutgeschrieben")
|
||||
check(scr.fast_turns > 0 and scr.reward == Profile.battle_coins(g.winner == "human", g.human.turns, g.human.cards_played, scr.fast_turns, main.profile.difficulty().reward), "Münzen aus Zügen, Karten und schnellen Zügen")
|
||||
check(scr.detail_label.visible and tr("%d Züge") % g.human.turns in scr.detail_label.text and "= " in scr.detail_label.text, "Aufschlüsselung im Ergebnisbild")
|
||||
check(Profile.load_or_new(SAVE).coins == main.profile.coins, "Belohnung gespeichert")
|
||||
await click(find_button("Zurück zum Menü"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
Reference in new issue
Block a user