update
This commit is contained in:
@@ -19,15 +19,14 @@ func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var h: Game.Player = scr.game.human
|
||||
for i in 3:
|
||||
scr.game.pick(h, 0)
|
||||
h.energy = 10
|
||||
scr.game.draw(h)
|
||||
scr.game.pick(h, 0)
|
||||
h.hand.pop_back()
|
||||
if scr.game.current() == h:
|
||||
scr.game.draw(h)
|
||||
var t0 := Time.get_ticks_usec()
|
||||
for i in 30:
|
||||
scr.refresh()
|
||||
print("refresh() mit 4 Handkarten: %.2f ms" % ((Time.get_ticks_usec() - t0) / 1000.0 / 30))
|
||||
print("refresh() mit 5 Handkarten: %.2f ms" % ((Time.get_ticks_usec() - t0) / 1000.0 / 30))
|
||||
scr.ai_delay = 0.2
|
||||
measuring = true
|
||||
var start := Time.get_ticks_msec()
|
||||
|
||||
@@ -73,16 +73,13 @@ func _act_host() -> void:
|
||||
_decide(game.players[seat])
|
||||
|
||||
|
||||
## Einfache Züge mit kurzer Bedenkzeit: erst eine offene Wahl treffen, dann das erste
|
||||
## spielbare Wesen ohne Ziel legen, dann angreifen, dann ziehen, sonst Zug beenden.
|
||||
## Einfache Züge mit kurzer Bedenkzeit: das erste spielbare Wesen legen, dann angreifen,
|
||||
## dann ziehen, sonst Zug beenden.
|
||||
func _decide(me: Game.Player) -> void:
|
||||
var g := game
|
||||
await get_tree().create_timer(0.25).timeout
|
||||
if game != g or (hosting and (net.game == null or net.game.current_idx != seat)):
|
||||
return # inzwischen ein neuer Zustand
|
||||
if not me.offer.is_empty():
|
||||
net.send_action({"type": "pick", "index": 0})
|
||||
return
|
||||
for i in me.hand.size():
|
||||
var card: Cards.Card = me.hand[i]
|
||||
if game.can_play(me, card)[0]:
|
||||
|
||||
@@ -25,7 +25,6 @@ func _initialize() -> void:
|
||||
var c := Game.Creature.new(Cards.by_id("f_wolf"), p, uid)
|
||||
uid += 1
|
||||
c.ready = true
|
||||
c.attacks_left = 1
|
||||
p.board.append(c)
|
||||
var h: Game.Player = g.players[0]
|
||||
h.energy = 10
|
||||
|
||||
@@ -43,7 +43,7 @@ var next_uid := 9000
|
||||
func put(p: Game.Player, cid: String, ready: bool = true, hidden: bool = false) -> Game.Creature:
|
||||
next_uid += 1
|
||||
var cr := Game.Creature.new(C(cid), p, next_uid)
|
||||
cr.attacks_left = cr.attacks_per_turn() if ready else 0
|
||||
cr.ready = ready
|
||||
cr.hidden = hidden
|
||||
p.board.append(cr)
|
||||
return cr
|
||||
@@ -58,7 +58,8 @@ func _initialize() -> void:
|
||||
test_wesen_und_angriff()
|
||||
test_elementvorteil()
|
||||
test_verdeckt()
|
||||
test_vier_elemente()
|
||||
test_elementboni()
|
||||
test_spott_und_regeneration()
|
||||
test_verbessern()
|
||||
test_verschmelzen()
|
||||
test_limits()
|
||||
@@ -94,12 +95,12 @@ func test_kartenpool() -> void:
|
||||
for e in Cards.ELEMENTS:
|
||||
check(Cards.all().any(func(c): return c.elements == [e]), "Element vorhanden: " + e)
|
||||
for c in Cards.all():
|
||||
check(c.health > 0 and c.cost == ceili((c.attack + c.health) / 2.0), "Kosten = (Angriff + Leben) / 2: " + c.id)
|
||||
check(c.health > 0 and c.cost == maxi(1, floori((2 * c.attack + c.health) / 3.0)), "Kosten = (2 · Angriff + Leben) / 3, abgerundet: " + c.id)
|
||||
check(c.keywords.is_empty() and c.text() == "", "Karten kommen ohne Eigenschaften: " + c.id)
|
||||
check(C("f_kobold").cost == 2 and C("ff_phoenix").cost == 7 and C("e_wall").cost == 2, "Kostenbeispiele")
|
||||
check(C("f_kobold").cost == 1 and C("f_wolf").cost == 2 and C("ff_phoenix").cost == 7 and C("e_wall").cost == 1 and C("f_drache").cost == 5, "Kostenbeispiele")
|
||||
var a := C("f_wolf")
|
||||
var b := C("f_wolf")
|
||||
a.keywords.append("Schild")
|
||||
a.keywords.append("Spott")
|
||||
check(b.keywords.is_empty() and Cards.by_id("f_wolf").keywords.is_empty(), "Exemplare teilen sich keine Eigenschaften")
|
||||
|
||||
|
||||
@@ -108,7 +109,6 @@ func test_kombos() -> void:
|
||||
check(Cards.combo_name(["Luft", "Feuer"]) == "Blitz", "Luft+Feuer = Blitz")
|
||||
check(not C("ff_phoenix").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.beaten_by("Feuer") == "Wasser" and Cards.beaten_by("Erde") == "Luft", "wer wen schlägt")
|
||||
|
||||
|
||||
## Mana: 5 im ersten Zug, jede Runde eins mehr, der Rest verfällt; Legen kostet die Kartenkosten.
|
||||
@@ -129,101 +129,78 @@ 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_wolf").cost, "Ausspielen kostet die Kartenkosten")
|
||||
check(g.can_play(h, h.hand[0])[1] == "Nicht genug Mana", "Drache (6) zu teuer bei %d Mana" % h.energy)
|
||||
check(g.can_play(h, h.hand[0])[1] == "Nicht genug Mana", "Drache (5) zu teuer bei %d Mana" % h.energy)
|
||||
check(Cards.fusion_value(4, 2) == 5 and Cards.fusion_value(8, 1) == 9 and Cards.fusion_value(2, 2) == 3 and Cards.fusion_value(0, 4) == 4 and Cards.fusion_value(8, 8) == 11, "Fusionswert: besserer Wert plus ein Drittel des schwächeren")
|
||||
|
||||
|
||||
## Ziehen: 1 Mana, drei vom gemeinsamen Stapel, eine behalten; der Rest unter den Stapel.
|
||||
## Start: fünf bezahlbare Karten, keine Wahl. Ziehen: 1 Mana, eine bezahlbare Karte vom Stapel.
|
||||
func test_ziehen() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 1
|
||||
var g := Game.new_battle(r)
|
||||
check(g.deck.size() == Cards.all().size() * DeckGen.COPIES - 2 * Game.START_OFFER, "Stapel: jede Karte dreimal, zehn schon ausgegeben")
|
||||
check(g.deck.size() == Cards.all().size() * DeckGen.COPIES - 2 * Game.START_HAND, "Stapel: jede Karte dreimal, zehn schon ausgegeben")
|
||||
for p in g.players:
|
||||
check(p.offer.size() == Game.START_OFFER and p.picks_left == Game.START_PICKS and p.hand.is_empty(), "Start: fünf zur Wahl, drei zu nehmen")
|
||||
check(p.offer.all(func(c): return c.cost <= Game.START_MANA + p.start_bonus), "nur Karten, die im ersten Zug spielbar sind")
|
||||
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 + p.start_bonus), "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()
|
||||
var deck_before := g.deck.size()
|
||||
var wanted: Cards.Card = first.offer[4]
|
||||
check(g.pick(first, 4) == "" and first.hand.size() == 1 and first.hand[0].id == wanted.id and first.hand[0] != wanted, "erste Startkarte genommen, als eigenes Exemplar")
|
||||
check(first.offer.size() == 4 and first.picks_left == 2, "vier bleiben, zwei noch zu nehmen")
|
||||
check(g.draw(first) == "Erst wählen" and g.can_play(first, first.hand[0])[1] == "Erst wählen", "bei offener Wahl geht nichts anderes")
|
||||
g.pick(first, 0)
|
||||
g.pick(first, 0)
|
||||
check(first.hand.size() == 3 and first.offer.is_empty() and g.deck.size() == deck_before + 2, "drei behalten, die anderen zwei liegen unter dem Stapel")
|
||||
check(g.draw(first) == "Deine Hand ist voll", "fünf Handkarten sind das Limit")
|
||||
first.hand.pop_back()
|
||||
var mana := first.energy
|
||||
check(g.draw(first) == "" and first.energy == mana - 1 and first.offer.size() == 3 and first.picks_left == 1, "gezogen: 1 Mana, drei zur Wahl")
|
||||
check(first.offer.all(func(c): return c.cost <= first.max_energy), "nur Karten bis zum Mana dieses Zugs")
|
||||
check(first.offer[0] != first.offer[1] and first.offer[1] != first.offer[2], "drei verschiedene Karten")
|
||||
check(g.pick(first, 1) == "" and first.hand.size() == 4 and first.offer.is_empty(), "eine genommen, Wahl zu")
|
||||
check(g.pick(first, 0) == "Keine Wahl offen", "ohne Wahl nichts zu nehmen")
|
||||
var deck_before := g.deck.size()
|
||||
check(g.draw(first) == "" and first.energy == mana - 1 and first.hand.size() == Game.START_HAND, "gezogen: 1 Mana, eine Karte")
|
||||
check(first.hand.back().cost <= first.max_energy and g.deck.size() == deck_before - 1, "bezahlbar; zu teure liegen wieder unten im Stapel")
|
||||
first.energy = 0
|
||||
first.hand.pop_back()
|
||||
check(g.draw(first) == "Nicht genug Mana", "ohne Mana kein Ziehen")
|
||||
first.energy = 5
|
||||
g.draw(first)
|
||||
g.pick(first, 0)
|
||||
check(first.hand.size() == Game.MAX_HAND and g.draw(first) == "Deine Hand ist voll", "fünf Handkarten sind das Limit")
|
||||
var second: Game.Player = g.enemy_of(first)
|
||||
check(g.pick(second, 0) == "" and second.hand.size() == 1, "wählen geht auch außerhalb des Zugs")
|
||||
g.end_turn() # jetzt ist der Zweite dran, seine Wahl ist noch offen
|
||||
g.end_turn() # sein Zugende nimmt den Rest von vorn
|
||||
check(second.hand.size() == 3 and second.offer.is_empty(), "Zugende nimmt offene Karten von vorn")
|
||||
check(g.draw(second) == "Nicht dein Zug", "nur im eigenen Zug")
|
||||
# Leerer Stapel wird neu gemischt
|
||||
g.deck = []
|
||||
var cur: Game.Player = g.current()
|
||||
cur.energy = 3
|
||||
cur.hand.pop_back() # Platz auf der Hand
|
||||
check(g.draw(cur) == "" and cur.offer.size() == 3 and g.deck.size() > 0 and g.log.any(func(l): return l.begins_with("Der Stapel wird neu gemischt")), "leerer Stapel: neu gemischt")
|
||||
# Mit viel Mana tauchen auch teure Karten auf; zu teure wandern unter den Stapel
|
||||
g.pick(cur, 0)
|
||||
cur.max_energy = 20
|
||||
cur.energy = 20
|
||||
var expensive_before: int = g.deck.filter(func(c): return c.cost > 8).size()
|
||||
g.draw(cur)
|
||||
g.pick(cur, 0)
|
||||
var bottom: Array = g.deck.slice(0, 5)
|
||||
check(expensive_before > 0 and (g.deck.size() + 1 + cur.offer.size()) > 0 and bottom.size() == 5, "Stapel bleibt vollständig")
|
||||
first.energy = 3
|
||||
check(g.draw(first) == "" and g.deck.size() > 0 and g.log.any(func(l): return l.begins_with("Der Stapel wird neu gemischt")), "leerer Stapel: neu gemischt")
|
||||
# Mit viel Mana tauchen auch teure Karten auf
|
||||
first.max_energy = 20
|
||||
first.energy = 20
|
||||
var seen_expensive := false
|
||||
for i in 12:
|
||||
first.hand = []
|
||||
g.draw(first)
|
||||
if first.hand[0].cost > 5:
|
||||
seen_expensive = true
|
||||
check(seen_expensive, "mit 20 Mana kommen auch teure Karten")
|
||||
|
||||
|
||||
## Gelegte Wesen greifen erst im nächsten Zug an; kein Durchschlag, keine Heilung zu Zugbeginn.
|
||||
func test_wesen_und_angriff() -> void:
|
||||
var s := setup(["f_wolf"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
check(g.play_card(h, h.hand[0]) == "" and h.board.size() == 1 and h.board[0].can_attack(), "gespielt und sofort angriffsbereit")
|
||||
check(g.play_card(h, h.hand[0]) == "" and h.board.size() == 1 and not h.board[0].can_attack(), "gespielt, greift noch nicht an")
|
||||
var wolf: Game.Creature = h.board[0]
|
||||
check(g.can_attack(wolf, o)[1] == "Flammenwolf greift erst im nächsten Zug an", "Meldung: erst nächsten Zug")
|
||||
g.begin_turn(h)
|
||||
check(wolf.ready and wolf.can_attack(), "nach einer Runde angriffsbereit")
|
||||
var e := put(o, "e_wall") # 0/4
|
||||
check(g.can_attack(wolf, o)[1] == "Erst die Wesen", "Leben erst ohne Wesen angreifbar")
|
||||
check(g.attack(wolf, e) == "" and e.health == 4 - 3 and wolf.health == 2, "Angriff: Schaden, kein Konter von 0 Angriff")
|
||||
check(g.can_attack(wolf, e)[1].ends_with("kann nicht angreifen"), "nur ein Angriff je Zug")
|
||||
g.begin_turn(o)
|
||||
check(e.health == 4, "zu Zugbeginn erholt sich das Wesen seines Besitzers ganz")
|
||||
check(e.health == 1, "keine Heilung zu Zugbeginn")
|
||||
g.begin_turn(h)
|
||||
var life := o.life
|
||||
g.attack(wolf, e)
|
||||
check(e.health == 1 and o.board.size() == 1, "Angriff: wieder nur 3 Schaden")
|
||||
e.health = 1
|
||||
check(o.board.is_empty() and g.log.any(func(l): return l == "Steinwall stirbt.") and o.life == life, "Wesen stirbt, nichts schlägt durch")
|
||||
g.begin_turn(h)
|
||||
g.attack(wolf, e)
|
||||
check(o.board.is_empty() and g.log.any(func(l): return l == "Steinwall stirbt."), "Wesen stirbt, Überschuss schlägt durch")
|
||||
g.begin_turn(h)
|
||||
var life0 := o.life
|
||||
check(g.attack(wolf, o) == "" and o.life == life0 - 3, "ohne Wesen trifft es den Helden")
|
||||
check(g.attack(wolf, o) == "" and o.life == life - 3, "ohne Wesen trifft es den Helden")
|
||||
# Gegenschlag
|
||||
var troll := put(o, "e_troll") # 3/6
|
||||
g.begin_turn(h)
|
||||
g.attack(wolf, troll)
|
||||
check(troll.health == 3 and wolf.health <= 0 and not (wolf in h.board), "Konter tötet den Wolf")
|
||||
# Durchschlag: was über das Leben des Wesens hinausgeht, trifft den Besitzer
|
||||
var drache := put(h, "f_drache") # 6/5
|
||||
var kobold := put(o, "f_kobold") # 2/1
|
||||
var life := o.life
|
||||
g.attack(drache, kobold)
|
||||
check(not (kobold in o.board) and o.life == life - 5 and g.log.any(func(l): return l == "5 Schaden schlagen durch auf Gegner."), "Durchschlag: 6 gegen 1 Leben, 5 gehen durch")
|
||||
var sch := put(o, "f_kobold")
|
||||
sch.shield = true
|
||||
life = o.life
|
||||
g.begin_turn(h)
|
||||
g.attack(drache, sch)
|
||||
check(sch.health == 1 and o.life == life, "Schild blockt alles, nichts schlägt durch")
|
||||
g.begin_turn(o)
|
||||
check(troll.health == 3, "der Troll bleibt verletzt")
|
||||
|
||||
|
||||
func test_elementvorteil() -> void:
|
||||
@@ -242,7 +219,7 @@ func test_elementvorteil() -> void:
|
||||
var wolf := put(o, "f_wolf") # Feuer 3/2
|
||||
var life: int = o.life
|
||||
g.attack(tiefen, wolf)
|
||||
check(wolf.health <= 0 and o.life == life - 3 and tiefen.health == 6 - 3, "Wasser gegen Feuer: +1 beim Zuschlagen, der Konter bleibt gleich")
|
||||
check(wolf.health <= 0 and o.life == life and tiefen.health == 6 - 3, "Wasser gegen Feuer: +1 beim Zuschlagen, der Konter bleibt gleich, nichts schlägt durch")
|
||||
var s3 := setup()
|
||||
var w3 := put(s3[1], "f_wolf")
|
||||
var t3 := put(s3[2], "w_tiefen")
|
||||
@@ -255,7 +232,8 @@ func test_elementvorteil() -> void:
|
||||
check(s2[2].life == 13, "gegen Helden gibt es keinen Vorteil")
|
||||
|
||||
|
||||
## Verdeckt: greift nicht an, zählt nicht für die Elementregel, wird beim Angriff aufgedeckt.
|
||||
## Verdeckt: greift nicht an, zählt nicht für die Elementboni, darf erst nach einer Runde aufgedeckt
|
||||
## werden, greift dann sofort an; ein Angriff deckt es auf.
|
||||
func test_verdeckt() -> void:
|
||||
var s := setup(["f_wolf", "f_kobold"])
|
||||
var g: Game = s[0]
|
||||
@@ -265,12 +243,13 @@ func test_verdeckt() -> void:
|
||||
check(g.play_card(h, h.hand[0], true) == "" and h.board[0].hidden and h.energy == mana - C("f_wolf").cost, "verdeckt gelegt, gleich teuer")
|
||||
var wolf: Game.Creature = h.board[0]
|
||||
check(g.log.back() == "Spieler legt ein Wesen verdeckt." and g.events.back().hidden, "Verlauf verrät die Karte nicht")
|
||||
check(g.reveal(h, wolf) == "Flammenwolf kann erst im nächsten Zug aufgedeckt werden" and wolf.hidden, "im Legezug nicht aufdecken")
|
||||
g.begin_turn(h)
|
||||
check(not wolf.can_attack() and g.can_attack(wolf, o)[1] == "Flammenwolf liegt verdeckt", "verdeckt greift nicht an")
|
||||
check(g.reveal(h, wolf) == "" and not wolf.hidden and wolf.can_attack(), "aufgedeckt: kann sofort angreifen")
|
||||
check(g.reveal(h, wolf) != "", "offen bleibt offen")
|
||||
# Der Gegner deckt beim Angriff auf
|
||||
var kobold := put(o, "f_kobold", true, true)
|
||||
var kobold := put(o, "f_kobold", false, true)
|
||||
o.hand = []
|
||||
check(g.attack(wolf, kobold) == "" and not kobold.hidden and kobold.health <= 0, "Angriff deckt auf und tötet")
|
||||
check(g.log.any(func(l): return l == "Feuerkobold wird aufgedeckt."), "Aufdecken steht im Verlauf")
|
||||
@@ -288,45 +267,104 @@ func test_verdeckt() -> void:
|
||||
check(own.card == "f_asche", "der Besitzer sieht seine verdeckte Karte")
|
||||
|
||||
|
||||
## Vier offene Wesen eines Elements: +1/+1 für das Element, -1/-1 für das, das es schlägt.
|
||||
func test_vier_elemente() -> void:
|
||||
## Elementboni: zwei eigene offene Wesen eines Elements geben den Sondereffekt (Erde Spott, Luft
|
||||
## Flink, Wasser Regeneration, Feuer Doppelschlag); drei auf dem Tisch geben dem Element +1/+1.
|
||||
func test_elementboni() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
var fire := []
|
||||
for i in 3:
|
||||
fire.append(put(h, "f_wolf"))
|
||||
var fire := [put(h, "f_wolf"), put(h, "f_kobold")]
|
||||
var bird := put(o, "l_wind") # Luft 1/1
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 0 and bird.aura == 0, "drei reichen nicht")
|
||||
var hidden_fire := put(o, "f_kobold", true, true)
|
||||
check(fire[0].aura == 0 and fire[1].aura == 0, "zwei reichen nicht für +1/+1")
|
||||
check(fire[0].has("Doppelschlag") and fire[0].attacks_per_turn() == 2 and fire[0].keywords() == ["Doppelschlag"], "zwei eigene Feuer: Doppelschlag für beide")
|
||||
var hidden_fire := put(o, "f_asche", true, true)
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 0, "verdeckte zählen nicht")
|
||||
check(g.element_counts()["Feuer"] == 3, "Zähler ohne Verdeckte")
|
||||
var fourth := put(o, "f_asche") # Feuer beim Gegner zählt auch
|
||||
check(fire[0].aura == 0 and g.element_counts()["Feuer"] == 2 and not hidden_fire.has("Doppelschlag"), "verdeckte zählen nicht und bekommen nichts")
|
||||
var third := put(o, "f_ritter") # Feuer beim Gegner zählt für den Tisch
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 3 and fourth.aura == 1, "vier Feuer: +1/+1 für alle Feuerwesen")
|
||||
check(bird.aura == -1 and bird.power() == 0 and bird.health == 0, "Luft wird geschlagen: -1/-1")
|
||||
g.cleanup()
|
||||
check(not (bird in o.board), "das 1/1 ist daran gestorben")
|
||||
g.reveal(o, hidden_fire) if g.current() == o else g._flip(hidden_fire)
|
||||
check(hidden_fire.aura == 1, "aufgedeckt zählt es und bekommt den Bonus sofort")
|
||||
o.board.erase(fourth)
|
||||
check(fire[0].aura == 1 and fire[0].power() == 4 and fire[0].health == 3 and third.aura == 1, "drei Feuer auf dem Tisch: +1/+1 für alle Feuerwesen")
|
||||
check(bird.aura == 0 and bird.health == 1, "kein Malus für das geschlagene Element")
|
||||
check(not third.has("Doppelschlag"), "der Gegner hat nur ein offenes Feuer: kein Sondereffekt")
|
||||
g._flip(hidden_fire)
|
||||
check(hidden_fire.aura == 1 and third.has("Doppelschlag") and hidden_fire.has("Doppelschlag"), "aufgedeckt zählt es sofort: Bonus und Paar")
|
||||
fire[0].health = 1
|
||||
o.board.erase(third)
|
||||
o.board.erase(hidden_fire)
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 0 and fire[0].health == 2, "Bonus geht wieder, Leben folgt")
|
||||
check(fire[0].aura == 0 and fire[0].max_health == 2 and fire[0].health == 1 and fire[1].health == 1, "Bonus geht wieder: nur das Höchstleben sinkt, Leben wird höchstens gekappt")
|
||||
# Vorschau
|
||||
check(g.forecast(h, C("f_kobold")) == 1 and g.forecast(h, C("l_wind")) == 0 and g.forecast(h, C("w_geist")) == 0, "Vorschau: Feuer würde den Stapel voll machen")
|
||||
check(g.forecast(h, C("f_asche")) == 1 and g.forecast(h, C("l_wind")) == 0, "Vorschau: Feuer würde den Stapel voll machen")
|
||||
# Merkmal: doppelt
|
||||
h.perks = ["element_doppelt"]
|
||||
put(h, "f_kobold")
|
||||
put(h, "f_asche")
|
||||
g.apply_auras()
|
||||
check(fire[0].aura == 2 and fire[0].power() == 5, "Elementbonus zählt doppelt")
|
||||
check(g.forecast(h, C("l_wind")) == -2, "Vorschau: Luft bekäme jetzt -2/-2")
|
||||
h.perks = []
|
||||
# Die anderen Paare
|
||||
var walls := [put(h, "e_wall"), put(h, "e_wall")]
|
||||
check(walls[0].has("Spott") and not fire[0].has("Spott"), "zwei Erde: Spott nur für Erdwesen")
|
||||
var winds := [put(h, "l_wind", false), put(h, "l_wind", false)]
|
||||
check(winds[0].has("Flink") and winds[0].can_attack(), "zwei Luft: Flink, frisch gelegt greift an")
|
||||
var geister := [put(h, "w_geist"), put(h, "w_geist")]
|
||||
check(geister[0].has("Regeneration"), "zwei Wasser: Regeneration")
|
||||
# Verschmolzene Wesen zählen für beide Elemente und bekommen beide Effekte und Boni
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
var h2: Game.Player = s2[1]
|
||||
var dampf := put(h2, "f_wolf")
|
||||
dampf.card = Cards.fused(C("f_wolf"), C("w_geist"))
|
||||
var fw := put(h2, "f_kobold")
|
||||
var ww := put(h2, "w_geist")
|
||||
g2.apply_auras()
|
||||
check(dampf.has("Doppelschlag") and dampf.has("Regeneration") and dampf.keywords() == ["Regeneration", "Doppelschlag"], "Dampf hat beide Sondereffekte")
|
||||
check(fw.has("Doppelschlag") and ww.has("Regeneration"), "und zählt für beide Paare")
|
||||
put(s2[2], "f_asche")
|
||||
put(s2[2], "w_reiter")
|
||||
g2.apply_auras()
|
||||
check(dampf.aura == 2 and fw.aura == 1 and ww.aura == 1, "drei Feuer und drei Wasser: Dampf bekommt +2/+2")
|
||||
|
||||
|
||||
## Verbessern: Eigenschaften für Mana, je Exemplar.
|
||||
## Spott zieht Angriffe auf sich; Regeneration heilt am Ende des eigenen Zugs.
|
||||
func test_spott_und_regeneration() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
var wolf := put(h, "f_wolf")
|
||||
var wall := put(o, "e_wall")
|
||||
var kobold := put(o, "f_kobold")
|
||||
wall.card.keywords.append("Spott")
|
||||
check(g.can_attack(wolf, kobold)[1] == "Erst die Wesen mit Spott" and g.can_attack(wolf, wall)[0], "Spott muss zuerst angegriffen werden")
|
||||
var secret := put(o, "e_troll", true, true)
|
||||
secret.card.keywords.append("Spott")
|
||||
check(not secret.has("Spott") and g.can_attack(wolf, kobold)[1] == "Erst die Wesen mit Spott", "verdeckter Spott zählt nicht")
|
||||
o.board.erase(secret)
|
||||
g.attack(wolf, wall)
|
||||
wall.health = 0
|
||||
g.cleanup()
|
||||
g.begin_turn(h)
|
||||
check(g.can_attack(wolf, kobold)[0], "ohne Spötter ist alles angreifbar")
|
||||
# Regeneration
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
var h2: Game.Player = s2[1]
|
||||
var reg := put(h2, "w_tiefen")
|
||||
reg.card.keywords.append("Regeneration")
|
||||
var plain := put(h2, "e_troll")
|
||||
var enemy := put(s2[2], "w_reiter")
|
||||
enemy.card.keywords.append("Regeneration")
|
||||
reg.health = 1
|
||||
plain.health = 1
|
||||
enemy.health = 1
|
||||
g2.end_turn()
|
||||
check(reg.health == reg.max_health and g2.log.any(func(l): return l == "Tiefenwächter regeneriert sich."), "Regeneration heilt am Zugende ganz")
|
||||
check(plain.health == 1 and enemy.health == 1, "ohne Regeneration und beim Gegner heilt nichts")
|
||||
check(g2.events.any(func(e): return e.type == "heal" and e.uid == reg.uid), "Ereignis fürs Heilen")
|
||||
|
||||
|
||||
## Verbessern: Eigenschaften für Mana, je Exemplar, auf Hand und Brett.
|
||||
func test_verbessern() -> void:
|
||||
var s := setup(["f_wolf"])
|
||||
var g: Game = s[0]
|
||||
@@ -334,24 +372,26 @@ func test_verbessern() -> void:
|
||||
var o: Game.Player = s[2]
|
||||
h.energy = 20
|
||||
var card: Cards.Card = h.hand[0]
|
||||
check(g.upgrade(h, card, "Schild") == "" and h.energy == 20 - 3 and "Schild" in h.hand[0].keywords, "Schild für die Handkarte gekauft")
|
||||
check(g.upgrade(h, card, "Spott") == "" and h.energy == 20 - 2 and "Spott" in h.hand[0].keywords, "Spott für die Handkarte gekauft")
|
||||
check(h.hand[0] != card and Cards.by_id("f_wolf").keywords.is_empty(), "eigenes Exemplar, der Pool bleibt sauber")
|
||||
check(g.upgrade(h, h.hand[0], "Schild") != "", "nicht doppelt")
|
||||
check(g.upgrade(h, h.hand[0], "Spott") != "", "nicht doppelt")
|
||||
g.play_card(h, h.hand[0])
|
||||
var wolf: Game.Creature = h.board[0]
|
||||
var after_play := h.energy
|
||||
check(wolf.shield and wolf.can_attack(), "mit Schild gelegt, greift sofort an")
|
||||
check(g.upgrade(h, wolf, "Doppelschlag") == "" and wolf.attacks_per_turn() == 2 and wolf.attacks_left == 2 and h.energy == after_play - 4, "Doppelschlag: zwei Angriffe")
|
||||
check(wolf.card.text() == "Schild, Doppelschlag.", "Kartentext zeigt die Eigenschaften")
|
||||
check(g.upgrade(h, wolf, "Doppelschlag") != "" and g.upgrade(h, put(o, "e_wall"), "Schild") != "" and g.upgrade(h, wolf, "Flink") != "", "nicht doppelt, nicht fremd, nicht unbekannt")
|
||||
h.energy = 2
|
||||
check(wolf.has("Spott") and not wolf.can_attack(), "mit Spott gelegt, greift noch nicht an")
|
||||
check(g.upgrade(h, wolf, "Flink") == "" and wolf.can_attack() and h.energy == after_play - 2, "Flink: greift sofort an")
|
||||
check(g.upgrade(h, wolf, "Doppelschlag") == "" and wolf.attacks_per_turn() == 2 and wolf.attacks_left() == 2 and h.energy == after_play - 6, "Doppelschlag: zwei Angriffe")
|
||||
check(g.upgrade(h, wolf, "Regeneration") == "" and h.energy == after_play - 9, "Regeneration kostet 3")
|
||||
check(wolf.card.text() == "Spott, Flink, Regeneration, Doppelschlag.", "Kartentext zeigt die Eigenschaften")
|
||||
check(g.upgrade(h, wolf, "Doppelschlag") != "" and g.upgrade(h, put(o, "e_wall"), "Spott") != "" and g.upgrade(h, wolf, "Schild") != "", "nicht doppelt, nicht fremd, nicht unbekannt")
|
||||
h.energy = 1
|
||||
var k2 := put(h, "f_kobold")
|
||||
check(g.upgrade(h, k2, "Schild") == "Nicht genug Mana", "zu teuer")
|
||||
check(g.upgrade(h, k2, "Spott") == "Nicht genug Mana", "zu teuer")
|
||||
h.perks = ["verbessern_billig"]
|
||||
check(g.upgrade(h, k2, "Schild") == "" and h.energy == 0, "Merkmal: Eigenschaften kosten 1 weniger")
|
||||
check(g.upgrade(h, k2, "Spott") == "" and h.energy == 0, "Merkmal: Eigenschaften kosten 1 weniger")
|
||||
|
||||
|
||||
## Verschmelzen: der bessere Wert von beidem plus 1, Kosten 1, 2, 4, 8 …
|
||||
## Verschmelzen: besserer Wert plus ein Drittel des schwächeren, Eigenschaften beider, Kosten 1, 2, 4, 8 …; auch auf der Hand.
|
||||
func test_verschmelzen() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
@@ -359,46 +399,60 @@ func test_verschmelzen() -> void:
|
||||
var a := put(h, "f_wolf") # 3/2
|
||||
var b := put(h, "w_tiefen") # 4/6
|
||||
b.health = 4
|
||||
a.card.keywords.append("Schild")
|
||||
a.card.keywords.append("Spott")
|
||||
check(Game.fusion_cost(a, b) == 1, "erste Verschmelzung kostet 1")
|
||||
check(g.fuse(h, a, b) == "" and h.board.size() == 1 and h.energy == 9, "verschmolzen")
|
||||
var f: Game.Creature = h.board[0]
|
||||
check(f.name == "Dampf" and f.attack == 5 and f.max_health == 7 and f.health == 5, "Dampf mit dem besseren Angriff und Leben plus 1")
|
||||
check("Schild" in f.card.keywords and f.card.fusions == 1 and f.card.cost == Cards.cost_of(5, 7), "Eigenschaften geerbt, einmal verschmolzen")
|
||||
check(f.card.elements == ["Feuer", "Wasser"], "beide Elemente")
|
||||
var c := put(h, "e_wall")
|
||||
check(f.name == "Dampf" and f.attack == 5 and f.max_health == 7 and f.health == 5, "Dampf: 4+1 Angriff, 6+1 Leben, aktuell 4+1")
|
||||
check("Spott" in f.card.keywords and f.card.fusions == 1 and f.card.cost == 5, "Eigenschaften geerbt, einmal verschmolzen, Kosten 5")
|
||||
check(f.card.elements == ["Feuer", "Wasser"] and f.ready, "beide Elemente, beide waren bereit")
|
||||
var c := put(h, "e_wall", false)
|
||||
check(Game.fusion_cost(f, c) == 2, "zweite Verschmelzung kostet 2")
|
||||
g.fuse(h, f, c)
|
||||
var f2: Game.Creature = h.board[0]
|
||||
check(f2.name == "Chimäre" and f2.card.fusions == 2 and h.energy == 7, "Chimäre aus drei Elementen")
|
||||
check(f2.name == "Chimäre" and f2.card.fusions == 2 and h.energy == 7 and not f2.ready, "Chimäre aus drei Elementen, nicht bereit, weil der Wall es nicht war")
|
||||
var d := put(h, "l_wind")
|
||||
check(Game.fusion_cost(f2, d) == 4, "dritte kostet 4")
|
||||
h.energy = 3
|
||||
check(g.fuse(h, f2, d) == "Nicht genug Mana", "zu teuer")
|
||||
check(g.can_fuse(h, f2, f2)[1] == "Zwei verschiedene Wesen wählen", "nicht mit sich selbst")
|
||||
check(g.can_fuse(h, f2, f2)[1] == "Zwei verschiedene Karten wählen", "nicht mit sich selbst")
|
||||
# Verdeckte verschmelzen offen
|
||||
var s2 := setup()
|
||||
var x := put(s2[1], "f_wolf", true, true)
|
||||
var y := put(s2[1], "f_kobold", true, true)
|
||||
s2[0].fuse(s2[1], x, y)
|
||||
check(not s2[1].board[0].hidden, "das Ergebnis liegt offen")
|
||||
# Elementbonus der Eltern zählt nicht doppelt; Dampf nimmt den besseren Elementwert
|
||||
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_wolf")
|
||||
var fb := put(h3, "w_tiefen")
|
||||
for i in 3:
|
||||
for i in 2:
|
||||
put(h3, "f_kobold")
|
||||
g3.apply_auras()
|
||||
check(fa.aura == 1 and fa.max_health == 3, "vier Feuer vor dem Verschmelzen")
|
||||
check(fa.aura == 1 and fa.max_health == 3, "drei Feuer vor dem Verschmelzen")
|
||||
g3.fuse(h3, fa, fb)
|
||||
var ff: Game.Creature = h3.board[0]
|
||||
check(ff.aura == 1 and ff.max_health == 8 and ff.health == 8 and ff.power() == 6, "nach dem Verschmelzen zählt der Bonus nur einmal")
|
||||
for i in 3:
|
||||
check(ff.aura == 1 and ff.max_health == 8 and ff.health == 8 and ff.power() == 6, "nach dem Verschmelzen zählt der Bonus nur einmal (7 + 1)")
|
||||
for i in 2:
|
||||
put(h3, "w_geist")
|
||||
g3.apply_auras()
|
||||
check(g3.element_counts()["Wasser"] == 4 and ff.aura == 1, "Wasser +1 und Feuer -1: Dampf nimmt den besseren Wert")
|
||||
check(g3.element_counts()["Wasser"] == 3 and ff.aura == 2 and ff.max_health == 9, "Feuer und Wasser voll: Dampf bekommt +2/+2")
|
||||
# Auf der Hand
|
||||
var s4 := setup(["f_wolf", "w_tiefen", "l_wind"])
|
||||
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_wall"))[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 == 9, "zwei Handkarten verschmolzen, 1 Mana")
|
||||
var hc: Cards.Card = h4.hand[0]
|
||||
check(hc.name == "Dampf" and hc.attack == 5 and hc.health == 7 and hc.cost == 5 and hc.fusions == 1, "Dampf auf der Hand mit 5/7 und Kosten 5")
|
||||
var lava := Cards.fused(C("f_asche"), C("f_kobold"), 4, 8)
|
||||
check(Cards.fused(lava, C("f_kobold")).attack == 5 and Cards.fused(lava, C("f_kobold")).health == 9, "ein Kobold macht Lava 4/8 zu 5/9, nie schlechter")
|
||||
check(g4.play_card(h4, hc) == "" and h4.energy == 4 and h4.board.back().name == "Dampf", "Ausspielen kostet die neuen Kosten")
|
||||
check(g4.log.any(func(l): return l == "Flammenwolf und Tiefenwächter verschmelzen auf der Hand zu Dampf (5/7)."), "Verlauf")
|
||||
|
||||
|
||||
func test_limits() -> void:
|
||||
@@ -422,7 +476,7 @@ func test_gleiche_regeln() -> void:
|
||||
var g := Game.new_battle(r)
|
||||
var first: Game.Player = g.players[0]
|
||||
check(g.current() == first and first.max_energy == Game.START_MANA and first.energy == Game.START_MANA, "Startspieler: 5 Mana, kein Bonus")
|
||||
check(first.offer.size() == Game.START_OFFER and first.hand.is_empty(), "jeder beginnt mit einer Wahl aus fünf und leerer Hand")
|
||||
check(g.players.all(func(p): return p.hand.size() == Game.START_HAND), "jeder beginnt mit fünf Karten")
|
||||
for p in g.players:
|
||||
check(p.life == Game.START_LIFE, "gleiches Leben")
|
||||
if p != first:
|
||||
@@ -474,7 +528,7 @@ func test_booster() -> void:
|
||||
for i in 50:
|
||||
var rolled := Profile.roll_booster(r)
|
||||
check(rolled.size() == 5, "5 Karten")
|
||||
check(rolled.any(func(x): return x.rarity != "gewöhnlich"), "mindestens eine seltene")
|
||||
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)
|
||||
@@ -490,7 +544,23 @@ func test_booster() -> void:
|
||||
p.collection["f_wolf"] = Cards.RARITIES.size() - 1
|
||||
check(p.upgrade("f_wolf") != "", "über die höchste Stufe hinaus geht nicht")
|
||||
p.collection.erase("f_wolf")
|
||||
check(p.dismantle("f_wolf") != "" and p.rarity("f_wolf") == "gewöhnlich", "gewöhnliche Karten gehören allen")
|
||||
check(p.dismantle("f_wolf") != "" and p.rarity("f_wolf") == "Common", "Common-Karten gehören allen")
|
||||
check(Profile.RARITY_WEIGHTS.values().reduce(func(a, b): return a + b, 0.0) == 100.0, "Chancen ergeben 100 %")
|
||||
check(Profile.DUST_UPGRADE["Rare"] == 100 and Profile.DUST_UPGRADE["Ultimate Rare"] == 1000 and Profile.DUST_DISMANTLE["Common"] == 50 and Profile.DUST_DISMANTLE["Ultimate Rare"] == 1000, "Staubpreise")
|
||||
# Verschmolzene Karten tragen die höhere Stufe der Eltern
|
||||
var s := setup(["f_wolf", "w_tiefen"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
h.looks = {"w_tiefen": 3}
|
||||
check(h.hand[0].rarity_in(h.looks) == "Common" and h.hand[1].rarity_in(h.looks) == "Ultra Rare", "Stufe aus der Sammlung")
|
||||
g.fuse(h, h.hand[0], h.hand[1])
|
||||
check(h.hand[0].look == 3 and h.hand[0].rarity_in(h.looks) == "Ultra Rare", "Fusion auf der Hand: die höhere Stufe gewinnt")
|
||||
var kobold := put(h, "f_kobold")
|
||||
g.play_card(h, h.hand[0])
|
||||
var fused: Game.Creature = h.board[1]
|
||||
g.fuse(h, fused, kobold)
|
||||
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(Cards.Card.from_dict(h.board[0].card.to_dict()).look == 3, "die Stufe reist mit der Karte")
|
||||
|
||||
|
||||
func test_spielstand() -> void:
|
||||
@@ -500,7 +570,7 @@ 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 p.collection.is_empty(), "Starterprofil: alle Karten gewöhnlich")
|
||||
check(p.coins == Profile.START_COINS and p.collection.is_empty(), "Starterprofil: alle Karten Common")
|
||||
check(FileAccess.file_exists(path), "Datei angelegt")
|
||||
p.coins = 999
|
||||
p.collection["f_wolf"] = 2
|
||||
@@ -525,8 +595,8 @@ func test_kopie() -> void:
|
||||
check(g2.players[1].life == 11 and o.life == 15, "Kopie verändert das Original nicht")
|
||||
g2.play_card(g2.players[0], g2.players[0].hand[0])
|
||||
check(h.hand.size() == 1 and g2.players[0].hand.is_empty(), "Hand der Kopie unabhängig")
|
||||
g2.upgrade(g2.players[0], a2, "Schild")
|
||||
check(not a.shield and a.card.keywords.is_empty(), "Verbessern in der Kopie lässt das Original unberührt")
|
||||
g2.upgrade(g2.players[0], a2, "Spott")
|
||||
check(not a.has("Spott") and a.card.keywords.is_empty() and a2.has("Spott"), "Verbessern in der Kopie lässt das Original unberührt")
|
||||
check(g2.log.is_empty() and g2.events.is_empty(), "Kopie schreibt keinen Verlauf")
|
||||
|
||||
|
||||
@@ -667,16 +737,15 @@ func test_merkmale() -> void:
|
||||
var s := setup(["f_wolf", "l_falke"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
h.perks = ["feuer_angriff", "wesen_angriff", "wesen_leben", "schild_wesen", "ziehen_gratis"]
|
||||
h.perks = ["feuer_angriff", "wesen_angriff", "wesen_leben", "alle_flink", "ziehen_gratis"]
|
||||
g.play_card(h, h.hand[0])
|
||||
var wolf: Game.Creature = h.board[0]
|
||||
check(wolf.power() == 3 + 1 + 1 and wolf.health == 3 and wolf.shield and wolf.can_attack(), "Feuer +1, Wesen +1/+1, Schild")
|
||||
check(wolf.power() == 3 + 1 + 1 and wolf.health == 3 and wolf.has("Flink") and wolf.can_attack(), "Feuer +1, Wesen +1/+1, flink")
|
||||
g.play_card(h, h.hand[0])
|
||||
check(h.board[1].power() == 3 + 1, "Luft: nur Wesen +1")
|
||||
g.deck = DeckGen.deck(g.rng)
|
||||
var mana := h.energy
|
||||
check(g.draw(h) == "" and h.energy == mana, "Merkmal: Ziehen kostet nichts")
|
||||
g.pick(h, 0)
|
||||
check(g.draw(h) == "" and h.energy == mana and h.hand.size() == 1, "Merkmal: Ziehen kostet nichts")
|
||||
for perk in Game.PERKS:
|
||||
check(Campaign.STAGES.any(func(st): return st.perk == perk), "Merkmal vergeben: " + perk)
|
||||
var r := RandomNumberGenerator.new()
|
||||
@@ -695,17 +764,18 @@ func test_merkmale() -> void:
|
||||
|
||||
|
||||
func test_ki_zwei_schritte() -> void:
|
||||
# Gegner hat 5 Leben und kein Wesen; der Wüstenkönig (5 Angriff) liegt auf der Hand:
|
||||
# erst ausspielen, dann mit ihm töten.
|
||||
var s := setup(["el_koenig"])
|
||||
# Gegner hat 5 Leben und kein Wesen; der Wüstenkönig (5 Angriff) liegt frisch auf dem Brett:
|
||||
# erst Flink kaufen, dann mit ihm töten.
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
h.turns = 2
|
||||
o.life = 5
|
||||
var king := put(h, "el_koenig", false)
|
||||
put(h, "f_wolf", false)
|
||||
var act := AI.choose_action(g, h)
|
||||
check(act.type == "play" and act.card == h.hand[0] and not act.face_down, "KI spielt zuerst den König, offen")
|
||||
check(act.type == "upgrade" and act.creature == king and act.keyword == "Flink", "KI kauft zuerst Flink für den König")
|
||||
AI.apply_action(g, h, act)
|
||||
var act2 := AI.choose_action(g, h)
|
||||
check(act2.type == "attack" and act2.target == o, "dann tödlicher Angriff")
|
||||
@@ -718,26 +788,26 @@ func test_snapshot() -> void:
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2]
|
||||
g.upgrade(h, h.hand[0], "Schild")
|
||||
g.upgrade(h, h.hand[0], "Spott")
|
||||
g.play_card(h, h.hand[0])
|
||||
put(o, "e_troll")
|
||||
var troll := put(o, "e_troll")
|
||||
troll.attacks_done = 1
|
||||
var hi := g.players.find(h)
|
||||
var oi := g.players.find(o)
|
||||
h.energy = 5
|
||||
o.energy = 5
|
||||
g.deck = DeckGen.deck(g.rng)
|
||||
g.draw(h)
|
||||
o.offer = g.draw_cards(3, 5)
|
||||
o.picks_left = 1
|
||||
var snap := g.snapshot(hi)
|
||||
var json: Dictionary = JSON.parse_string(JSON.stringify(snap))
|
||||
var g2 := Game.from_snapshot(json)
|
||||
check(g2.players.size() == 2 and g2.turn == g.turn and g2.current_idx == g.current_idx and g2.deck.size() == g.deck.size(), "Grunddaten und Stapelgröße")
|
||||
check(g2.players[hi].hand.is_empty(), "eigene Hand sichtbar")
|
||||
check(g2.players[hi].hand.size() == 1 and g2.players[hi].hand[0].id == h.hand[0].id, "eigene Hand sichtbar")
|
||||
check(g2.players[oi].hand.size() == 1 and g2.players[oi].hand[0].id != "w_frost", "fremde Hand nur als Anzahl")
|
||||
check(g2.players[oi].offer.is_empty() and g2.players[hi].offer.size() == Game.OFFER_SIZE and g2.players[hi].picks_left == 1, "eigene Wahl sichtbar, fremde nicht")
|
||||
var cr2 := g2.find_creature(h.board[0].uid)
|
||||
check(cr2 != null and cr2.card.id == "f_wolf" and cr2.owner() == g2.players[hi] and cr2.shield and "Schild" in cr2.card.keywords, "Wesen mit Besitzer, Zustand und gekaufter Eigenschaft")
|
||||
check(cr2 != null and cr2.card.id == "f_wolf" and cr2.owner() == g2.players[hi] and cr2.has("Spott") and not cr2.ready, "Wesen mit Besitzer, Zustand und gekaufter Eigenschaft")
|
||||
var troll2 := g2.find_creature(troll.uid)
|
||||
check(troll2.ready and troll2.attacks_done == 1, "Bereitschaft und Angriffe reisen mit")
|
||||
check(g2.events.size() == g.events.size() and g2.events.back().type == g.events.back().type, "Ereignisse übertragen")
|
||||
check(g2.draw(g2.players[oi]) == "Nicht dein Zug", "Kopie kennt die Regeln")
|
||||
var snap_o := g.snapshot(oi)
|
||||
@@ -773,9 +843,6 @@ func test_ki_zeitbudget() -> void:
|
||||
r.seed = 12
|
||||
var g := Game.new_battle(r)
|
||||
var h: Game.Player = g.players[0]
|
||||
for p in g.players:
|
||||
p.offer = []
|
||||
p.picks_left = 0
|
||||
h.energy = 30
|
||||
for i in Game.MAX_HAND:
|
||||
h.hand.append(C("f_ritter"))
|
||||
@@ -796,11 +863,11 @@ func test_ki_zeitbudget() -> void:
|
||||
func test_englische_karten() -> void:
|
||||
check(Cards.all().all(func(c): return c.name_en != ""), "alle Karten mit englischem Namen")
|
||||
var ritter := C("f_ritter")
|
||||
ritter.keywords = ["Schild", "Doppelschlag"]
|
||||
ritter.keywords = ["Spott", "Doppelschlag"]
|
||||
TranslationServer.set_locale("en")
|
||||
check(ritter.title() == "Blazing Knight" and ritter.text() == "Shield, Double Strike.", "Ritter auf Englisch: %s / %s" % [ritter.title(), ritter.text()])
|
||||
check(ritter.title() == "Blazing Knight" and ritter.text() == "Taunt, Double Strike.", "Ritter auf Englisch: %s / %s" % [ritter.title(), ritter.text()])
|
||||
TranslationServer.set_locale("de")
|
||||
check(ritter.title() == "Lodernder Ritter" and ritter.text() == "Schild, Doppelschlag.", "zurück auf Deutsch")
|
||||
check(ritter.title() == "Lodernder Ritter" and ritter.text() == "Spott, Doppelschlag.", "zurück auf Deutsch")
|
||||
|
||||
|
||||
## Voller Spielstand (viewer -1) stellt Hände und Bretter wieder her; Zuschauer (-2) sehen nichts Verborgenes.
|
||||
@@ -826,7 +893,7 @@ func test_kampf_speichern() -> void:
|
||||
guard += 1
|
||||
check(g2.winner != "", "der volle Spielstand ist bis zum Ende spielbar")
|
||||
var s2: Dictionary = g.snapshot(-2)
|
||||
check(s2.players.all(func(p): return not (p.hand is Array) and p.offer.is_empty()), "Zuschauer-Sicht ohne Hände und Wahlen")
|
||||
check(s2.players.all(func(p): return not (p.hand is Array)), "Zuschauer-Sicht ohne Hände")
|
||||
|
||||
|
||||
## Jeder tr("...")-Text in der Oberfläche hat einen englischen Eintrag.
|
||||
@@ -843,7 +910,7 @@ func test_uebersetzung_vollstaendig() -> void:
|
||||
key = line.substr(0, line.find(","))
|
||||
keys[key] = true
|
||||
var missing := []
|
||||
for k in Cards.COMBOS.values() + Cards.ELEMENTS + Cards.KEYWORDS + Cards.RARITIES + Cards.KEYWORD_TEXT.values() + Game.PERKS.values():
|
||||
for k in Cards.COMBOS.values() + Cards.ELEMENTS + Cards.KEYWORDS + Cards.RARITIES + Cards.KEYWORD_TEXT.values() + Game.PERKS.values() + Cards.ELEMENT_KEYWORD.values():
|
||||
if not keys.has(k):
|
||||
missing.append(k)
|
||||
var d := DirAccess.open("res://scripts/ui")
|
||||
|
||||
218
tests/ui_test.gd
218
tests/ui_test.gd
@@ -54,11 +54,11 @@ func right_click() -> void:
|
||||
await right_click_at(Vector2(5, 5))
|
||||
|
||||
|
||||
func right_click_at(pos: Vector2) -> void:
|
||||
func right_click_at(pos: Vector2, button: int = MOUSE_BUTTON_RIGHT) -> void:
|
||||
var down := InputEventMouseButton.new()
|
||||
down.position = pos
|
||||
down.global_position = pos
|
||||
down.button_index = MOUSE_BUTTON_RIGHT
|
||||
down.button_index = button
|
||||
down.pressed = true
|
||||
get_viewport().push_input(down)
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
@@ -219,20 +219,42 @@ func target_view(scr, valid: Array) -> Control:
|
||||
return null
|
||||
|
||||
|
||||
## Nimmt alle offenen Wahlen des Spielers (immer die erste Karte), auch über das Netz.
|
||||
func pick_all(scr) -> void:
|
||||
var guard := 0
|
||||
while scr.game != null and not scr.perspective.offer.is_empty() and guard < 60:
|
||||
guard += 1
|
||||
if scr.pick_views.is_empty():
|
||||
await frames(1)
|
||||
continue
|
||||
var n: int = scr.perspective.offer.size()
|
||||
await click(scr.pick_views[0])
|
||||
var w := 0
|
||||
while scr.game != null and scr.perspective.offer.size() == n and w < 120:
|
||||
await frames(1)
|
||||
w += 1
|
||||
## Zieht eine Karte oder Kachel auf eine andere und hält sie dort, bis die beiden verschmelzen.
|
||||
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 down := InputEventMouseButton.new()
|
||||
down.position = a
|
||||
down.global_position = a
|
||||
down.button_index = MOUSE_BUTTON_LEFT
|
||||
down.pressed = true
|
||||
get_viewport().push_input(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)
|
||||
await frames(1)
|
||||
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
|
||||
var up: InputEventMouseButton = down.duplicate()
|
||||
up.pressed = false
|
||||
up.position = b
|
||||
up.global_position = b
|
||||
get_viewport().push_input(up)
|
||||
await frames(2)
|
||||
|
||||
|
||||
## Legt, was bezahlbar ist.
|
||||
@@ -257,10 +279,9 @@ func human_turn(scr) -> void:
|
||||
if g.winner != "" or not g.can_draw(g.human)[0]:
|
||||
break
|
||||
var mana: int = g.human.energy
|
||||
var hand: int = g.human.hand.size()
|
||||
await click(scr.deck)
|
||||
check(g.human.energy == mana - Game.DRAW_COST and not scr.pick_views.is_empty(), "gezogen: drei zur Wahl")
|
||||
await pick_all(scr)
|
||||
check(scr.pick_views.is_empty(), "gewählt")
|
||||
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand + 1, "gezogen: eine Karte")
|
||||
for i in 12:
|
||||
var ready: Array = scr.own_views.filter(func(v): return v.creature.can_attack())
|
||||
if ready.is_empty() or g.winner != "":
|
||||
@@ -284,7 +305,7 @@ func human_turn(scr) -> void:
|
||||
targets.append(c)
|
||||
await settle(scr)
|
||||
await click(target_view(scr, targets))
|
||||
check(scr.selected == null and cr.attacks_left < cr.attacks_per_turn(), "Angriff ausgeführt")
|
||||
check(scr.selected == null and cr.attacks_done > 0, "Angriff ausgeführt")
|
||||
if g.winner == "":
|
||||
await click(scr.b_end)
|
||||
|
||||
@@ -293,7 +314,6 @@ func start_battle() -> void:
|
||||
await click(find_button("Einzelspieler"))
|
||||
check(screen() is OpponentSelectScreen, "Gegnerauswahl")
|
||||
await click(find_button("Starten"))
|
||||
await pick_all(screen())
|
||||
|
||||
|
||||
func test_gegnerauswahl() -> void:
|
||||
@@ -310,12 +330,9 @@ func test_gegnerauswahl() -> void:
|
||||
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(hard.game.human.offer.size() == Game.START_OFFER and hard.game.human.hand.is_empty() and hard.pick_layer.visible,
|
||||
"Start: fünf Karten zur Wahl, Hand leer")
|
||||
check(hard.game.human.offer.all(func(c): return c.cost <= Game.START_MANA + 1), "alle fünf sind im ersten Zug spielbar")
|
||||
await shot("05_startwahl")
|
||||
await pick_all(hard)
|
||||
check(hard.game.human.hand.size() == Game.START_PICKS and not hard.pick_layer.visible, "drei behalten, Wahl geschlossen")
|
||||
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 + 1), "alle fünf sind im ersten Zug spielbar")
|
||||
await shot("05_starthand")
|
||||
check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht oben in der Mitte")
|
||||
await shot("11_kampf")
|
||||
await give_up()
|
||||
@@ -323,7 +340,6 @@ func test_gegnerauswahl() -> void:
|
||||
await click(find_button("Leicht"))
|
||||
await click(find_button("Starten"))
|
||||
var easy = screen()
|
||||
await pick_all(easy)
|
||||
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(easy.game.players[0].max_energy == Game.START_MANA and easy.game.human.life == Game.START_LIFE, "gleiche Regeln für beide")
|
||||
@@ -393,21 +409,21 @@ func test_sammlung() -> void:
|
||||
await frames(1)
|
||||
var phoenix = scr.grid.views().filter(func(v): return v.card.id == "ff_phoenix")[0]
|
||||
await click(phoenix)
|
||||
check(scr.selected.id == "ff_phoenix" and p.rarity("ff_phoenix") == "gewöhnlich", "Karte gewählt, noch gewöhnlich")
|
||||
check(scr.selected.id == "ff_phoenix" and p.rarity("ff_phoenix") == "Common", "Karte gewählt, noch Common")
|
||||
await click(scr.b_upgrade)
|
||||
check(p.level("ff_phoenix") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["selten"], "auf selten aufgewertet")
|
||||
check(p.level("ff_phoenix") == 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 first: CardView = scr.grid.views()[0]
|
||||
check(first.card.id == "ff_phoenix" and first.rarity == "selten", "die aufgewertete Karte steht vorn in der Sammlung")
|
||||
var front: Array = scr.grid.views().slice(0, p.upgraded_count())
|
||||
check(front.all(func(v): return v.rarity != "Common") and front.any(func(v): return v.card.id == "ff_phoenix" and v.rarity == "Rare"), "aufgewertete Karten stehen vorn in der Sammlung, der Phönix als Rare dabei")
|
||||
scr.search_box.text = "Phönix"
|
||||
scr.search_box.text_changed.emit("Phönix")
|
||||
await frames(1)
|
||||
check(Profile.load_or_new(SAVE).level("ff_phoenix") == 1, "Sammlung gespeichert")
|
||||
var dust: int = p.dust
|
||||
await click(scr.b_dismantle)
|
||||
check(p.level("ff_phoenix") == 0 and p.dust == dust + Profile.DUST_DISMANTLE["selten"], "wieder zerlegt")
|
||||
check(p.level("ff_phoenix") == 0 and p.dust == dust + Profile.DUST_DISMANTLE["Rare"], "wieder zerlegt")
|
||||
await shot("20_sammlung")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
@@ -487,22 +503,21 @@ func test_kampf_steuerung() -> void:
|
||||
scr.ai_delay = 0.0
|
||||
await wait_for_human(scr)
|
||||
var g: Game = scr.game
|
||||
g.human.energy = 20 # die Wahl ist zufällig, hier soll nur die Steuerung geprüft werden
|
||||
g.human.hand = [Cards.by_id("f_wolf")]
|
||||
g.human.energy = 20 # die Starthand ist zufällig, hier soll nur die Steuerung geprüft werden
|
||||
g.human.hand = [Cards.by_id("f_wolf").copy()]
|
||||
scr.refresh()
|
||||
check(scr.hand_views.size() == 1 and scr.deck.enabled and scr.pick_views.is_empty(), "eine Handkarte, Stapel bereit, keine Wahl offen")
|
||||
# Ziehen: 1 Mana weg, drei zur Wahl, eine landet auf der Hand
|
||||
check(scr.hand_views.size() == 1 and scr.deck.enabled, "eine Handkarte, Stapel bereit")
|
||||
# Ziehen: 1 Mana weg, eine Karte landet auf der Hand
|
||||
var mana: int = g.human.energy
|
||||
await click(scr.deck)
|
||||
check(g.human.energy == mana - Game.DRAW_COST and scr.pick_views.size() == Game.OFFER_SIZE and scr.pick_layer.visible, "gezogen: drei Karten zur Wahl")
|
||||
var wanted: Cards.Card = scr.pick_views[1].card
|
||||
await click(scr.pick_views[1])
|
||||
check(g.human.hand.size() == 2 and g.human.hand[1].id == wanted.id and not scr.pick_layer.visible, "gewählt, liegt auf der Hand")
|
||||
# Ausspielen kostet die Kartenkosten
|
||||
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == 2 and scr.hand_views.size() == 2, "gezogen: eine Karte auf der Hand")
|
||||
check(g.human.hand[1].cost <= g.human.max_energy, "nur bezahlbare Karten")
|
||||
# Ausspielen kostet die Kartenkosten; das Wesen greift noch nicht an
|
||||
var board_before: int = g.human.board.size()
|
||||
mana = g.human.energy
|
||||
await key(KEY_1)
|
||||
check(g.human.board.size() == board_before + 1 and g.human.energy == mana - Cards.by_id("f_wolf").cost, "Wesen per Taste gespielt, Kosten bezahlt")
|
||||
check(not g.human.board.back().can_attack(), "frisch gelegt greift es noch nicht an")
|
||||
# Rückgängig: Wesen zurück auf die Hand, Mana zurück
|
||||
check(scr.b_undo.visible, "Rückgängig angeboten")
|
||||
await click(scr.b_undo)
|
||||
@@ -513,21 +528,16 @@ func test_kampf_steuerung() -> void:
|
||||
check(g.human.board.size() == board_before + 1, "erneut gespielt")
|
||||
# Ziehen per Taste
|
||||
mana = g.human.energy
|
||||
var hand_before: int = g.human.hand.size()
|
||||
await key(KEY_Z)
|
||||
check(g.human.energy == mana - Game.DRAW_COST and scr.pick_layer.visible, "Z zieht")
|
||||
await key(KEY_1)
|
||||
check(not scr.pick_layer.visible, "1 wählt die erste Karte")
|
||||
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand_before + 1, "Z zieht")
|
||||
await shot("22_kampf_steuerung")
|
||||
# Gamepad: A auf dem Stapel zieht, Steuerkreuz wählt die zweite Karte, A nimmt sie
|
||||
# Gamepad: A auf dem Stapel zieht
|
||||
await pad(JOY_BUTTON_DPAD_LEFT)
|
||||
check(scr.gp_zone == "deck" and scr.cursor_layer.get_child_count() == 1, "Gamepad-Zeiger auf dem Stapel")
|
||||
var hand_before: int = g.human.hand.size()
|
||||
hand_before = g.human.hand.size()
|
||||
await pad(JOY_BUTTON_A)
|
||||
check(scr.pick_layer.visible, "Gamepad A zieht")
|
||||
await pad(JOY_BUTTON_DPAD_RIGHT)
|
||||
check(scr.gp_zone == "pick" and scr.gp_index == 1, "Zeiger auf der zweiten Karte der Wahl")
|
||||
await pad(JOY_BUTTON_A)
|
||||
check(g.human.hand.size() == hand_before + 1 and not scr.pick_layer.visible, "Gamepad A nimmt die Karte")
|
||||
check(g.human.hand.size() == hand_before + 1, "Gamepad A zieht")
|
||||
scr.ai_delay = 0.75
|
||||
await key(KEY_SPACE)
|
||||
check(g.current() != g.human or g.turn >= 2, "Zug per Leertaste beendet")
|
||||
@@ -562,7 +572,6 @@ func test_kampf_grenzen() -> void:
|
||||
while g.human.hand.size() < Game.MAX_HAND and guard < 30:
|
||||
guard += 1
|
||||
await click(scr.deck)
|
||||
await pick_all(scr)
|
||||
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)
|
||||
@@ -573,20 +582,17 @@ func test_kampf_grenzen() -> void:
|
||||
guard += 1
|
||||
if g.human.hand.is_empty():
|
||||
await click(scr.deck)
|
||||
await pick_all(scr)
|
||||
await key(KEY_1)
|
||||
check(g.human.board.size() == Game.MAX_BOARD, "Feld mit %d Wesen voll" % Game.MAX_BOARD)
|
||||
if g.human.hand.is_empty():
|
||||
await click(scr.deck)
|
||||
await pick_all(scr)
|
||||
await key(KEY_1)
|
||||
check(scr.msg_label.text == tr("Dein Feld ist voll"), "Meldung bei vollem Feld: " + scr.msg_label.text)
|
||||
await shot("25_kampf_voll")
|
||||
# Verschmelzen macht wieder Platz
|
||||
# Verschmelzen per Ziehen macht wieder Platz
|
||||
var before: int = g.human.board.size()
|
||||
await click(scr.b_fuse)
|
||||
await click(scr.own_views[0])
|
||||
await click(scr.own_views[1])
|
||||
scr.hold_time = 0.3
|
||||
await drag_fuse(scr, scr.own_views[0], scr.own_views[1])
|
||||
check(g.human.board.size() == before - 1, "Verschmelzen macht einen Platz frei")
|
||||
# Ohne Mana kein Ziehen
|
||||
g.human.energy = 0
|
||||
@@ -596,12 +602,13 @@ func test_kampf_grenzen() -> void:
|
||||
await give_up()
|
||||
|
||||
|
||||
## Elementregel, verdeckt legen, Kartenmenü und Verschmelzen im Kampf.
|
||||
## Elementboni, verdeckt legen, Kartenmenü und Verschmelzen (per Ziehen und per Menü) im Kampf.
|
||||
func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
await start_battle()
|
||||
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()
|
||||
@@ -609,7 +616,7 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
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
|
||||
cr.attacks_left = 1
|
||||
cr.ready = true
|
||||
cr.hidden = hidden
|
||||
p.board.append(cr)
|
||||
return cr
|
||||
@@ -620,56 +627,70 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
var secret: Game.Creature = put.call(scr.opponent(), "f_kobold", true)
|
||||
g.human.hand = [Cards.by_id("f_kobold").copy(), Cards.by_id("l_wind").copy()]
|
||||
g.human.energy = 20
|
||||
g.human.looks = {"w_reiter": 3}
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.element_view.text() == "Feuer 2 · Wasser 1 · Erde 0 · Luft 1", "Zähler ohne das verdeckte Wesen: " + scr.element_view.text())
|
||||
check(scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 0, "fremdes verdecktes Wesen zeigt nur den Rücken, noch kein Bonus in Sicht")
|
||||
# verdeckt legen über das Kartenmenü (Rechtsklick auf die Handkarte)
|
||||
var menu_text := "Verdeckt legen ◆ %d" % Cards.by_id("f_kobold").cost
|
||||
check(scr.element_view.text() == "Feuer 2 (Doppelschlag) · Wasser 1 · Erde 0 · Luft 1", "Zähler ohne das verdeckte Wesen, Feuerpaar: " + scr.element_view.text())
|
||||
check(g.human.board[0].keywords() == ["Doppelschlag"] and scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 1, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken; der Kobold würde den Stapel voll machen")
|
||||
# Rechtsklick auf die Handkarte: Menü mit Verdeckt legen und Eigenschaften, ohne Verschmelzen
|
||||
await hover(scr.hand_views[0])
|
||||
await right_click_at(scr.hand_views[0].get_global_rect().get_center())
|
||||
check(scr.menu_layer.visible and find_button(menu_text, scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü mit „Verdeckt legen“")
|
||||
check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ 1", scr.menu_layer) != null and find_button("Spott ◆ 2", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü")
|
||||
check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
|
||||
await shot("23_kartenmenue")
|
||||
await click(find_button(menu_text, scr.menu_layer))
|
||||
await right_click()
|
||||
check(not scr.menu_layer.visible, "Rechtsklick schließt das Menü")
|
||||
# Mittlere Maustaste legt die Handkarte verdeckt
|
||||
var mana: int = g.human.energy
|
||||
await right_click_at(scr.hand_views[0].get_global_rect().get_center(), MOUSE_BUTTON_MIDDLE)
|
||||
var mine_hidden: Game.Creature = g.human.board[3]
|
||||
check(mine_hidden.hidden and not scr.menu_layer.visible and scr.element_view.text().begins_with("Feuer 2"), "verdeckt gelegt, zählt nicht")
|
||||
# aufdecken per Klick auf das eigene verdeckte Wesen (erst, wenn es aus der Hand angekommen ist)
|
||||
check(mine_hidden.hidden and g.human.energy == mana - 1 and scr.element_view.text().begins_with("Feuer 2"), "mittlere Maustaste legt verdeckt, zählt nicht")
|
||||
# Rechtsklick auf ein eigenes Wesen: Eigenschaften kaufen
|
||||
await right_click_at(scr.own_views[0].get_global_rect().get_center())
|
||||
check(scr.menu_layer.visible and find_button("Doppelschlag ◆ 4", scr.menu_layer) != null and find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "Wesen-Menü mit Eigenschaften, ohne Verschmelzen")
|
||||
var wolf: Game.Creature = g.human.board[0]
|
||||
await click(find_button("Spott ◆ %d" % Cards.KEYWORD_COST["Spott"], scr.menu_layer))
|
||||
check(wolf.has("Spott") and "Spott" in wolf.card.keywords and g.human.energy == mana - 1 - Cards.KEYWORD_COST["Spott"], "Spott gekauft")
|
||||
while scr.own_views[3].busy:
|
||||
await frames(1)
|
||||
await click(scr.own_views[3])
|
||||
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3"), "aufgedeckt, zählt jetzt")
|
||||
put.call(g.human, "f_kobold")
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.element_view.text().begins_with("Feuer 4 (+1/+1)") and "Luft 1 (-1/-1)" in scr.element_view.text(), "vier Feuer: Bonus und Malus angezeigt: " + scr.element_view.text())
|
||||
check(g.human.board[0].power() == 4 and falke.power() == 2, "Feuerwesen +1, Luftwesen -1")
|
||||
check(scr.hand_views[0].bonus == -1, "die Luftkarte auf der Hand zeigt -1")
|
||||
await shot("23b_vier_feuer")
|
||||
# Verbessern über das Menü
|
||||
var wolf: Game.Creature = g.human.board[0]
|
||||
scr.open_menu(wolf, Rect2(scr.own_views[0].slot, scr.own_views[0].size))
|
||||
var mana: int = g.human.energy
|
||||
await click(find_button("Schild ◆ %d" % Cards.KEYWORD_COST["Schild"], scr.menu_layer))
|
||||
check(wolf.shield and "Schild" in wolf.card.keywords and g.human.energy == mana - Cards.KEYWORD_COST["Schild"], "Schild gekauft")
|
||||
check(mine_hidden.hidden and scr.msg_label.text == "Feuerkobold kann erst im nächsten Zug aufgedeckt werden", "im Legezug nicht aufzudecken: " + scr.msg_label.text)
|
||||
mine_hidden.ready = true
|
||||
await click(scr.own_views[3])
|
||||
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3 (+1/+1)"), "aufgedeckt: drei Feuer, +1/+1: " + scr.element_view.text())
|
||||
check(wolf.power() == 4 and falke.power() == 3 and mine_hidden.can_attack(), "Feuerwesen +1, kein Malus für Luft, aufgedeckt greift sofort an")
|
||||
check(scr.hand_views[0].bonus == 0, "die Luftkarte auf der Hand zeigt keinen Bonus")
|
||||
await shot("23b_drei_feuer")
|
||||
# Angriffsziel: Elementvorteil, verdeckte ohne Anzeige, Held gesperrt
|
||||
await click(scr.own_views[0])
|
||||
check(scr.selected == wolf and scr.tiles[falke.uid].matchup == 1 and scr.tiles[secret.uid].matchup == 0, "Angreifer gewählt: +1 gegen Luft, nichts gegen Verdeckte")
|
||||
check(not g.can_attack(wolf, scr.opponent())[0], "Held nicht angreifbar, solange Wesen stehen")
|
||||
await click(scr.tiles[secret.uid])
|
||||
check(not secret.hidden and not (secret in scr.opponent().board), "Angriff deckt auf und tötet den Kobold")
|
||||
check(wolf.attacks_left() == 1, "Doppelschlag: ein Angriff bleibt")
|
||||
await right_click()
|
||||
# Verschmelzen
|
||||
await click(scr.b_fuse)
|
||||
check(scr.fusing and scr.b_fuse.text == "Abbrechen", "Verschmelzen ausgewählt")
|
||||
await click(scr.own_views[1])
|
||||
check(scr.fuse_first != null, "erstes Wesen gewählt")
|
||||
await shot("24_verschmelzen")
|
||||
# Verschmelzen per Ziehen: den Aschekrieger auf den Wellenreiter ziehen und kurz halten
|
||||
mana = g.human.energy
|
||||
await click(scr.own_views[2])
|
||||
check(g.human.board.any(func(c): return c.name == "Dampf") and g.human.energy == mana - 1, "zu Dampf verschmolzen, 1 Mana bezahlt")
|
||||
check(not scr.fusing, "Modus beendet")
|
||||
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 - 1, "zu Dampf verschmolzen, 1 Mana bezahlt")
|
||||
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 (Wellenreiter war Ultra Rare)")
|
||||
await shot("24_verschmelzen")
|
||||
# Verschmelzen auf der Hand per Ziehen
|
||||
g.human.hand = [Cards.by_id("f_kobold").copy(), Cards.by_id("f_kobold").copy(), Cards.by_id("l_wind").copy()]
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
await drag_fuse(scr, scr.hand_views[0], scr.hand_views[1])
|
||||
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 Kobolde 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)
|
||||
check(find_button("Verdeckt legen ◆ 2", scr.menu_layer) != null, "Gamepad-Menü: Verdeckt legen")
|
||||
await click(find_button("Verschmelzen mit … ◆ 2", 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")
|
||||
await give_up()
|
||||
|
||||
|
||||
@@ -701,7 +722,6 @@ func test_netzwerk() -> void:
|
||||
var b = screen()
|
||||
check(b is BattleScreen and b.is_online() and not b.is_client(), "Netzwerkkampf als Host")
|
||||
b.ai_delay = 0.0
|
||||
await pick_all(b)
|
||||
var rounds := 0
|
||||
while b.game != null and b.game.winner == "" and rounds < 2:
|
||||
if b.game.current() == b.perspective:
|
||||
@@ -807,7 +827,6 @@ func test_host_wechsel() -> void:
|
||||
await frames(1)
|
||||
w += 1
|
||||
check(b.game != null and main.net.i_am_successor and not main.net.backup.is_empty(), "Nachfolger kennt den vollen Spielstand")
|
||||
await pick_all(b)
|
||||
main.net.rejoin_seconds = 3.0
|
||||
_bot_quit(true)
|
||||
w = 0
|
||||
@@ -881,7 +900,6 @@ func test_kampagne() -> void:
|
||||
await click(find_button("Kämpfen"))
|
||||
var battle = screen()
|
||||
check(battle is BattleScreen and battle.stage == 0, "Kampagnenkampf gestartet")
|
||||
await pick_all(battle)
|
||||
check(battle.game.opponent.name == "Fenn", "Fenn ist der Gegner")
|
||||
check(battle.game.opponent.perks == ["feuer_angriff"] and battle.game.log.any(func(l): return l.begins_with("Fenn: Feuerwesen")), "Boss-Merkmal aktiv")
|
||||
await shot("16_kampagne_kampf")
|
||||
@@ -897,7 +915,6 @@ func test_kampagne() -> void:
|
||||
await shot("27_kampagne_kapitel2")
|
||||
await click(find_button("Kämpfen"))
|
||||
var b2 = screen()
|
||||
await pick_all(b2)
|
||||
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.game.human.perks == [main.profile.perk], "eigenes Merkmal im Kampf")
|
||||
await give_up()
|
||||
@@ -951,16 +968,17 @@ func test_kampf_abbrechen() -> void:
|
||||
# Kartenvorschau: aufgewertete Karten zeigen ihre Seltenheit
|
||||
g.human.energy = 10
|
||||
var wolf := Game.Creature.new(Cards.by_id("f_wolf"), g.human, 9000)
|
||||
wolf.ready = true
|
||||
g.human.board.append(wolf)
|
||||
g.human.hand.append(Cards.by_id("w_reiter"))
|
||||
g.human.looks["w_reiter"] = Cards.RARITIES.find("episch")
|
||||
g.human.looks["w_reiter"] = Cards.RARITIES.find("Super Rare")
|
||||
scr.refresh()
|
||||
var reiter_view = scr.hand_views.filter(func(v): return v.card.id == "w_reiter")[0]
|
||||
check(reiter_view.rarity == "episch", "Handkarte trägt die eigene Stufe")
|
||||
check(reiter_view.rarity == "Super Rare", "Handkarte trägt die eigene Stufe")
|
||||
await hover(scr.element_view) # erst weg von der Hand, damit das Betreten sicher ausgelöst wird
|
||||
await hover(reiter_view)
|
||||
var previews = scr.preview_holder.get_children().filter(func(c): return c is CardView)
|
||||
check(previews.size() == 1 and previews[0].rarity == "episch", "Vorschau ebenfalls")
|
||||
check(previews.size() == 1 and previews[0].rarity == "Super Rare", "Vorschau ebenfalls")
|
||||
await shot("21_kampf_vorschau")
|
||||
if not scr.own_views.is_empty():
|
||||
await click(scr.own_views[0])
|
||||
|
||||
Reference in New Issue
Block a user