update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
extends Node
|
||||
## Fensterprobe: Kosten von refresh() und längste Bildzeit während KI-Zügen (5 Gegner).
|
||||
## Fensterprobe: Kosten von refresh() und längste Bildzeit während KI-Zügen.
|
||||
|
||||
var main: Node
|
||||
var max_dt := 0.0
|
||||
@@ -14,7 +14,7 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var scr: BattleScreen = BattleScreen.new(5)
|
||||
var scr: BattleScreen = BattleScreen.new()
|
||||
main.show_screen(scr)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
@@ -23,7 +23,7 @@ func _ready() -> void:
|
||||
var t0 := Time.get_ticks_usec()
|
||||
for i in 30:
|
||||
scr.refresh()
|
||||
print("refresh() mit 5 Gegnern und 8 Handkarten: %.2f ms" % ((Time.get_ticks_usec() - t0) / 1000.0 / 30))
|
||||
print("refresh() mit 8 Handkarten: %.2f ms" % ((Time.get_ticks_usec() - t0) / 1000.0 / 30))
|
||||
scr.ai_delay = 0.2
|
||||
measuring = true
|
||||
var start := Time.get_ticks_msec()
|
||||
|
||||
@@ -38,7 +38,7 @@ func _ready() -> void:
|
||||
net.host()
|
||||
net.lobby_changed.connect(func():
|
||||
if net.game == null and net.names.size() >= 2:
|
||||
net.start_battle(0))
|
||||
net.start_battle())
|
||||
net.action_applied.connect(func():
|
||||
net.broadcast()
|
||||
_act_host.call_deferred())
|
||||
|
||||
@@ -15,12 +15,9 @@ func _initialize() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 3
|
||||
print("Ladenangebot: %.2f ms" % ms(func(): DeckGen.shop_cards(r, 5), 50))
|
||||
print("Vorschau 1..5 Gegner: %.1f ms" % ms(func():
|
||||
for n in range(1, 6):
|
||||
for i in n:
|
||||
DeckGen.random_opponent(i + 1)))
|
||||
for n in [1, 3, 5]:
|
||||
var g := Game.new_battle(n, r)
|
||||
print("Gegner erzeugen: %.2f ms" % ms(func(): DeckGen.random_opponent(), 20))
|
||||
for n in [1]:
|
||||
var g := Game.new_battle(r)
|
||||
# ein volles Brett für alle, damit die Entscheidung teuer wird
|
||||
var uid := 5000
|
||||
for p in g.players:
|
||||
@@ -41,7 +38,7 @@ func _initialize() -> void:
|
||||
var t_budget := ms(func(): AI.choose_action(g, h))
|
||||
var cands := AI.candidate_actions(g, h).size()
|
||||
print("KI-Entscheidung, %d Gegner, %d Kandidaten: ohne Budget %.0f ms, mit Budget %.0f ms" % [n, cands, t_full, t_budget])
|
||||
print("Spielkopie: %.3f ms" % ms(func(): Game.new_battle(5, r).clone(), 20))
|
||||
print("Spielkopie: %.3f ms" % ms(func(): Game.new_battle(r).clone(), 20))
|
||||
print("Musik erzeugen (läuft im Hintergrund-Thread): %.0f ms" % ms(func(): Sfx.new()._music()))
|
||||
print("Snapshot 6 Spieler: %.2f ms" % ms(func(): Game.new_battle(5, r).snapshot(0), 10))
|
||||
print("Snapshot: %.2f ms" % ms(func(): Game.new_battle(r).snapshot(0), 10))
|
||||
quit()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends SceneTree
|
||||
## Balance-Simulation, die KI spielt auch den Menschen. Bei gleichen Regeln
|
||||
## ist etwa 1/(Gegner+1) Siege zu erwarten:
|
||||
## sind etwa 50 % Siege zu erwarten:
|
||||
## godot --headless --path . -s tests/simulate.gd
|
||||
|
||||
|
||||
@@ -9,25 +9,37 @@ func _initialize() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 0
|
||||
var t0 := Time.get_ticks_msec()
|
||||
print("Alle gierig (Erwartung etwa 1/(Gegner+1)):")
|
||||
for n in range(1, Game.MAX_OPPONENTS + 1):
|
||||
_run(n, games, r, true, true)
|
||||
print("Beide gierig (Erwartung etwa 50 %):")
|
||||
_run(games, r, true, true)
|
||||
print("Du mit Vorausschau, Gegner gierig:")
|
||||
for n in [1, 2, 3]:
|
||||
_run(n, games / 2, r, false, true)
|
||||
print("Alle mit Vorausschau:")
|
||||
_run(1, games / 2, r, false, false)
|
||||
_run(2, games / 4, r, false, false)
|
||||
_run(games / 2, r, false, true)
|
||||
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]:
|
||||
_campaign(stage, games / 4, r)
|
||||
print("Dauer %.1f s" % ((Time.get_ticks_msec() - t0) / 1000.0))
|
||||
quit()
|
||||
|
||||
|
||||
func _run(n_opp: int, games: int, r: RandomNumberGenerator,
|
||||
human_greedy: bool, opp_greedy: bool) -> 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)
|
||||
while g.winner == "":
|
||||
AI.take_turn(g, g.current(), false)
|
||||
assert(g.turn < 300, "Spiel endet nicht")
|
||||
if g.winner == "human":
|
||||
wins += 1
|
||||
var st := Campaign.stage(stage)
|
||||
print("Stufe %2d %-12s %2d Leben, %d Merkmale: Siege %3d%%" % [stage + 1, st.name, int(st.life), st.perks.size(), wins * 100 / games])
|
||||
|
||||
|
||||
func _run(games: int, r: RandomNumberGenerator, human_greedy: bool, opp_greedy: bool) -> void:
|
||||
var wins := 0
|
||||
var turns := 0
|
||||
for i in games:
|
||||
var g := Game.new_battle(n_opp, r)
|
||||
var g := Game.new_battle(r)
|
||||
while g.winner == "":
|
||||
var p := g.current()
|
||||
AI.take_turn(g, p, human_greedy if p.is_human else opp_greedy)
|
||||
@@ -35,4 +47,4 @@ func _run(n_opp: int, games: int, r: RandomNumberGenerator,
|
||||
if g.winner == "human":
|
||||
wins += 1
|
||||
turns += g.turn
|
||||
print("gegen %d: Siege %3d%% Ø Züge %.1f" % [n_opp, wins * 100 / games, float(turns) / games])
|
||||
print("Siege %3d%% Ø Züge %.1f" % [wins * 100 / games, float(turns) / games])
|
||||
|
||||
@@ -18,25 +18,23 @@ func C(id: String) -> Cards.Card:
|
||||
return Cards.by_id(id)
|
||||
|
||||
|
||||
func setup(human_hand: Array = [], opp_hand: Array = [], n_opp: int = 1) -> Array:
|
||||
## Duell mit vollem Mana; der Gegner hat 15 Leben. Liefert [Spiel, Spieler, Gegner].
|
||||
func setup(human_hand: Array = [], opp_hand: Array = []) -> Array:
|
||||
var human := Game.Player.new("Spieler", true, 30)
|
||||
var opps := []
|
||||
for i in n_opp:
|
||||
opps.append(Game.Player.new("Gegner %d" % (i + 1), false, 15))
|
||||
var o := Game.Player.new("Gegner", false, 15)
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 0
|
||||
var g := Game.new(human, opps, r)
|
||||
var g := Game.new(human, o, r)
|
||||
g.turn = 1
|
||||
human.max_energy = 10
|
||||
human.energy = 10
|
||||
for cid in human_hand:
|
||||
human.hand.append(C(cid))
|
||||
for o in opps:
|
||||
o.max_energy = 10
|
||||
o.energy = 10
|
||||
for cid in opp_hand:
|
||||
o.hand.append(C(cid))
|
||||
return [g, human, opps]
|
||||
o.max_energy = 10
|
||||
o.energy = 10
|
||||
for cid in opp_hand:
|
||||
o.hand.append(C(cid))
|
||||
return [g, human, o]
|
||||
|
||||
|
||||
var next_uid := 9000
|
||||
@@ -60,7 +58,6 @@ func _initialize() -> void:
|
||||
test_wesen_spielen()
|
||||
test_flink()
|
||||
test_waechter()
|
||||
test_waechter_nur_eigener_besitzer()
|
||||
test_kampf_beidseitig()
|
||||
test_schild()
|
||||
test_doppelschlag()
|
||||
@@ -76,15 +73,13 @@ func _initialize() -> void:
|
||||
test_mana_effekt()
|
||||
test_gleiche_regeln()
|
||||
test_zugreihenfolge()
|
||||
test_tote_gegner()
|
||||
test_jeder_gegen_jeden()
|
||||
test_gegnerzahl()
|
||||
test_sieg_niederlage()
|
||||
test_booster()
|
||||
test_spielstand()
|
||||
test_resonanz()
|
||||
test_elementvorteil()
|
||||
test_feld()
|
||||
test_vorschau()
|
||||
test_verschmelzen()
|
||||
test_feldkombos()
|
||||
test_schild_effekt_und_sonne()
|
||||
@@ -141,7 +136,7 @@ func test_flink() -> void:
|
||||
var s := setup(["l_falke"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
g.play_card(h, C("l_falke"))
|
||||
check(g.attack(h.board[0], o) == "", "Flink greift sofort an")
|
||||
check(o.life == 12, "Falke 3 Schaden")
|
||||
@@ -151,7 +146,7 @@ func test_waechter() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_wolf")
|
||||
var w := put(o, "e_wall")
|
||||
check(g.attack(a, o) != "", "Wächter blockt Angriff auf Held")
|
||||
@@ -159,20 +154,11 @@ func test_waechter() -> void:
|
||||
check(w.health == 1 and a.health == 2, "Schaden verrechnet")
|
||||
|
||||
|
||||
func test_waechter_nur_eigener_besitzer() -> void:
|
||||
var s := setup([], [], 2)
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var a := put(h, "f_wolf")
|
||||
put(s[2][0], "e_wall")
|
||||
check(g.attack(a, s[2][1]) == "" and s[2][1].life == 12, "Wächter schützt nur Besitzer")
|
||||
|
||||
|
||||
func test_kampf_beidseitig() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_asche")
|
||||
var d := put(o, "w_reiter")
|
||||
g.attack(a, d)
|
||||
@@ -184,7 +170,7 @@ func test_schild() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var sh := put(o, "ee_bastion")
|
||||
var a := put(h, "f_asche")
|
||||
g.attack(a, sh)
|
||||
@@ -198,7 +184,7 @@ func test_doppelschlag() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "l_schlange")
|
||||
g.attack(a, o)
|
||||
g.attack(a, o)
|
||||
@@ -210,7 +196,7 @@ func test_einfrieren() -> void:
|
||||
var s := setup([], ["w_frost"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_wolf")
|
||||
g.end_turn()
|
||||
check(g.play_card(o, C("w_frost"), a) == "", "Frostmagier spielen")
|
||||
@@ -226,30 +212,29 @@ func test_effekt_ziel() -> void:
|
||||
var s := setup(["w_frost", "f_ritter"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
check(g.play_card(h, C("w_frost")) == "" and h.board.size() == 1, "ohne Ziel kommt das Wesen trotzdem, der Effekt verpufft")
|
||||
g.play_card(h, C("f_ritter"), o)
|
||||
check(o.life == 14 and h.board.size() == 2, "Ritter macht 1 Schaden an den Helden")
|
||||
|
||||
|
||||
func test_flaechenschaden() -> void:
|
||||
var s := setup(["ff_phoenix"], [], 2)
|
||||
var s := setup(["ff_phoenix"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o1: Game.Player = s[2][0]
|
||||
var o2: Game.Player = s[2][1]
|
||||
put(o1, "f_kobold")
|
||||
put(o2, "e_golem")
|
||||
var o: Game.Player = s[2]
|
||||
put(o, "f_kobold")
|
||||
put(o, "e_golem")
|
||||
g.play_card(h, C("ff_phoenix"))
|
||||
check(o1.life == 11 and o2.life == 11, "Helden 4 Schaden")
|
||||
check(o1.board.is_empty() and o2.board[0].health == 1, "Wesen getroffen")
|
||||
check(o.life == 11, "Held 4 Schaden")
|
||||
check(o.board.size() == 1 and o.board[0].health == 1, "Wesen getroffen, Kobold tot")
|
||||
|
||||
|
||||
func test_zurueck_auf_hand() -> void:
|
||||
var s := setup(["wl_nebel"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var d := put(o, "e_troll")
|
||||
g.play_card(h, C("wl_nebel"), d)
|
||||
check(o.board.is_empty() and C("e_troll") in o.hand, "zurück auf Hand")
|
||||
@@ -285,11 +270,10 @@ func test_limits() -> void:
|
||||
|
||||
## Grenzfälle, die schon einmal falsch waren.
|
||||
func test_grenzfaelle() -> void:
|
||||
var s := setup(["f_wolf"], [], 2)
|
||||
var s := setup(["f_wolf"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o1: Game.Player = s[2][0]
|
||||
var o2: Game.Player = s[2][1]
|
||||
var o1: Game.Player = s[2]
|
||||
# Nur Karten von der eigenen Hand
|
||||
check(not g.can_play(h, C("e_troll"))[0] and g.play_card(h, C("e_troll")) == "Nicht auf der Hand", "fremde Karte lässt sich nicht spielen")
|
||||
check(g.play_card(h, C("f_wolf")) == "" and h.board.size() == 1, "eigene Handkarte geht")
|
||||
@@ -298,7 +282,7 @@ func test_grenzfaelle() -> void:
|
||||
o1.life = 0
|
||||
g.cleanup()
|
||||
check(o1.board.is_empty() and not o1.alive() and g.log.any(func(l): return l.find("verschwindet mit") >= 0), "besiegter Spieler räumt sein Feld")
|
||||
check(not (o1 in g.enemies_of(h)) and o2 in g.enemies_of(h), "Besiegte sind keine Ziele mehr")
|
||||
check(g.enemies_of(h).is_empty(), "Besiegte sind keine Ziele mehr")
|
||||
check(dead != null, "Wesen war vorher da")
|
||||
# Verschmolzene Karte auf der Hand übersteht den Spielstand
|
||||
var a := put(h, "f_kobold")
|
||||
@@ -314,10 +298,10 @@ func test_grenzfaelle() -> void:
|
||||
## Zweite Runde Grenzfälle: Konter ohne Angriff, Chimäre, Ereignisse mit verschmolzener Karte,
|
||||
## ausgeschiedener Netzwerkspieler mitten im eigenen Zug.
|
||||
func test_grenzfaelle_2() -> void:
|
||||
var s := setup([], [], 2)
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o1: Game.Player = s[2][0]
|
||||
var o1: Game.Player = s[2]
|
||||
# Ein Wesen ohne Angriff schlägt nicht zurück, auch nicht mit Elementvorteil
|
||||
var wall := put(o1, "e_wall") # Erde 0/4, Erde schlägt Wasser
|
||||
var reiter := put(h, "w_reiter") # Wasser 3/3
|
||||
@@ -347,10 +331,10 @@ func test_grenzfaelle_2() -> void:
|
||||
net.game = g
|
||||
net.rejoin_seconds = 0.0
|
||||
net.waiting = {"Spieler": {"seat": g.players.find(h), "since": 0.0}}
|
||||
o1.is_human = true # noch ein Mensch am Tisch, sonst wäre die Partie schlicht verloren
|
||||
o1.is_human = true # ein Duell zweier Menschen
|
||||
check(g.current() == h, "Spieler ist dran")
|
||||
net.tick()
|
||||
check(not h.alive() and h.board.is_empty() and g.current() != h, "Ausgeschiedener räumt das Feld und der Zug geht weiter")
|
||||
check(not h.alive() and h.board.is_empty() and g.winner != "" and g.winner_player == o1, "Ausgeschiedener räumt das Feld, der andere gewinnt")
|
||||
net.free()
|
||||
|
||||
|
||||
@@ -421,7 +405,7 @@ func test_mana_effekt() -> void:
|
||||
func test_gleiche_regeln() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 0
|
||||
var g := Game.new_battle(3, r)
|
||||
var g := Game.new_battle(r)
|
||||
var first: Game.Player = g.players[0]
|
||||
check(g.current() == first and first.max_energy == 1 and first.energy == 1, "Startspieler: 1 Mana, kein Bonus")
|
||||
check(first.shop.size() == Game.SHOP_SIZE and first.hand.is_empty(), "jeder beginnt mit einem Laden und leerer Hand")
|
||||
@@ -432,9 +416,9 @@ func test_gleiche_regeln() -> void:
|
||||
g.end_turn()
|
||||
var second: Game.Player = g.players[1]
|
||||
check(g.current() == second and second.max_energy == 1 and second.energy == 2, "Nachzieher: 1 Mana plus Ausgleich")
|
||||
for i in 4:
|
||||
for i in 2:
|
||||
g.end_turn()
|
||||
check(g.current() == second and second.max_energy == 2, "Ausgleich nur einmal")
|
||||
check(g.current() == second and second.max_energy == 2 and second.energy == 4, "Ausgleich nur einmal: 2 übrig plus 2 Einkommen")
|
||||
|
||||
|
||||
func test_zugreihenfolge() -> void:
|
||||
@@ -442,9 +426,9 @@ func test_zugreihenfolge() -> void:
|
||||
for seed in 12:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = seed
|
||||
var g := Game.new_battle(2, r)
|
||||
var g := Game.new_battle(r)
|
||||
starters[g.players[0].name] = true
|
||||
check(g.players.size() == 3 and g.human in g.players and g.opponents.size() == 2, "alle am Tisch")
|
||||
check(g.players.size() == 2 and g.human in g.players and g.opponent in g.players, "beide am Tisch")
|
||||
for i in g.players.size():
|
||||
check(g.players[i].order == i, "Reihenfolge nummeriert")
|
||||
check(g.log[0].begins_with("Zugreihenfolge:"), "Reihenfolge im Verlauf")
|
||||
@@ -453,58 +437,15 @@ func test_zugreihenfolge() -> void:
|
||||
check(starters.size() > 1, "Startspieler wechselt (%d verschiedene)" % starters.size())
|
||||
|
||||
|
||||
func test_tote_gegner() -> void:
|
||||
var s := setup([], [], 2)
|
||||
var g: Game = s[0]
|
||||
s[2][0].life = 0
|
||||
g.end_turn()
|
||||
check(g.current() == s[2][1], "toter Gegner übersprungen")
|
||||
g.end_turn()
|
||||
check(g.current() == s[1] and g.turn == 2, "zurück beim Spieler, Zug 2")
|
||||
|
||||
|
||||
func test_jeder_gegen_jeden() -> void:
|
||||
var s := setup([], [], 2)
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o1: Game.Player = s[2][0]
|
||||
var o2: Game.Player = s[2][1]
|
||||
check(g.enemies_of(o1) == [h, o2], "Gegner bekämpft Spieler und anderen Gegner")
|
||||
check(g.enemies_of(h) == [o1, o2], "Spieler bekämpft alle Gegner")
|
||||
g.end_turn()
|
||||
o1.hand = []
|
||||
var a := put(o1, "f_wolf")
|
||||
check(g.can_attack(a, o2)[0], "Gegner darf Gegner angreifen")
|
||||
check(g.attack(a, o2) == "" and o2.life == 12, "Gegner trifft Gegner")
|
||||
check(AI.choose_action(g, o1).type == "buy", "KI kauft aus ihrem Laden")
|
||||
o1.shop = []
|
||||
check(AI.choose_action(g, o1).type == "end", "ohne Angebot und ohne Angriff endet der Zug")
|
||||
o2.life = 2
|
||||
var b := put(o1, "f_kobold")
|
||||
check(AI.choose_action(g, o1).target == o2, "KI nutzt Lethal gegen anderen Gegner")
|
||||
|
||||
|
||||
func test_gegnerzahl() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 2
|
||||
for n in range(1, Game.MAX_OPPONENTS + 1):
|
||||
var g := Game.new_battle(n, r)
|
||||
check(g.opponents.size() == n, "%d Gegner" % n)
|
||||
check(g.players.all(func(o): return o.life == Game.START_LIFE), "gleiches Leben bei %d" % n)
|
||||
check(g.players[0].max_energy == 1 and g.current() == g.players[0], "Startspieler bei %d" % n)
|
||||
check(g.human.shop.size() == Game.SHOP_SIZE, "Laden bei %d" % n)
|
||||
check(Profile.REWARD_WIN.has(n), "Belohnung bei %d" % n)
|
||||
|
||||
|
||||
func test_sieg_niederlage() -> void:
|
||||
var s := setup([], [], 2)
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
g.damage(s[2][0], 99)
|
||||
g.damage(s[2], 14)
|
||||
g.check_end()
|
||||
check(g.winner == "", "noch offen")
|
||||
g.damage(s[2][1], 99)
|
||||
g.damage(s[2], 1)
|
||||
g.check_end()
|
||||
check(g.winner == "human", "Sieg")
|
||||
check(g.winner == "human" and g.winner_player == s[1], "Sieg")
|
||||
var s2 := setup()
|
||||
var g2: Game = s2[0]
|
||||
g2.damage(s2[1], 30)
|
||||
@@ -562,7 +503,7 @@ func test_ki_lethal() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
o.life = 3
|
||||
var a := put(h, "f_asche")
|
||||
put(o, "w_reiter")
|
||||
@@ -573,23 +514,20 @@ func test_ki_lethal() -> void:
|
||||
func test_ki_spiele_enden() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 7
|
||||
for n in [1, 3, 5]:
|
||||
for i in 6:
|
||||
var pool: Array = Cards.ELEMENTS.duplicate()
|
||||
Game.shuffle(pool, r)
|
||||
var g := Game.new_battle(n, r)
|
||||
var guard := 0
|
||||
while g.winner == "" and guard < 300:
|
||||
AI.take_turn(g, g.current(), i % 2 == 0)
|
||||
guard += 1
|
||||
check(g.winner != "", "Spiel endet (%d Gegner)" % n)
|
||||
for i in 12:
|
||||
var g := Game.new_battle(r)
|
||||
var guard := 0
|
||||
while g.winner == "" and guard < 300:
|
||||
AI.take_turn(g, g.current(), i % 2 == 0)
|
||||
guard += 1
|
||||
check(g.winner != "", "Spiel endet (Partie %d)" % i)
|
||||
|
||||
|
||||
func test_resonanz() -> void:
|
||||
var s := setup(["f_wolf", "f_kobold", "e_wall"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
g.play_card(h, C("f_wolf"))
|
||||
check(h.board[0].power() == 3, "allein keine Resonanz")
|
||||
g.play_card(h, C("f_kobold"))
|
||||
@@ -621,7 +559,7 @@ func test_elementvorteil() -> void:
|
||||
var s := setup(["f_ritter"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var mine := put(h, "w_reiter") # Wasser 3/3
|
||||
var his := put(o, "f_wolf") # Feuer 3/2
|
||||
g.attack(mine, his)
|
||||
@@ -633,7 +571,7 @@ func test_elementvorteil() -> void:
|
||||
s = setup(["f_ritter"])
|
||||
g = s[0]
|
||||
h = s[1]
|
||||
o = s[2][0]
|
||||
o = s[2]
|
||||
var air := put(o, "l_laeufer") # Luft 3/4, Feuer schlägt Luft
|
||||
g.play_card(h, C("f_ritter"), air)
|
||||
check(air.health == 2, "Feuereffekt gegen Luft: 1 Schaden mehr")
|
||||
@@ -644,7 +582,7 @@ func test_feld() -> void:
|
||||
var s := setup(["f_ritter"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
check(g.field_element == "" and int(g.field_charge["Feuer"]) == 0, "das Feld beginnt neutral")
|
||||
var wolf := put(h, "f_wolf")
|
||||
for i in Cards.FIELD_CHARGE - 1:
|
||||
@@ -669,11 +607,34 @@ func test_feld() -> void:
|
||||
|
||||
|
||||
## Verschmelzen: zwei eigene Wesen werden eins, Werte Durchschnitt plus 1.
|
||||
## Vorschau fürs Ausspielen: Resonanz, Feldbonus und Feldkombo mit Partner.
|
||||
func test_vorschau() -> void:
|
||||
var s := setup(["f_kobold", "e_wall"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var f := g.forecast(h, C("f_kobold"))
|
||||
check(f.resonance == 0 and f.field == 0 and f.combo == "" and f.partner == null, "leeres Feld: keine Vorschau")
|
||||
var wolf := put(h, "f_wolf")
|
||||
put(h, "f_asche")
|
||||
var reiter := put(h, "w_reiter")
|
||||
f = g.forecast(h, C("f_kobold"))
|
||||
check(f.resonance == 2 and f.combo == "Dampf" and f.partner == reiter, "zwei Feuerpartner geben +2, das Wasserwesen die Kombo Dampf")
|
||||
f = g.forecast(h, C("e_wall"))
|
||||
check(f.resonance == 0 and f.combo == "Lava" and f.partner == wolf, "Erde: kein Partner, Kombo Lava mit dem ersten Feuerwesen")
|
||||
g.field_element = "Feuer"
|
||||
check(g.forecast(h, C("f_kobold")).field == 1 and g.forecast(h, C("e_wall")).field == 0, "der Feldbonus zählt nur fürs eigene Element")
|
||||
h.perks.append("resonanz_doppelt")
|
||||
check(g.forecast(h, C("f_kobold")).resonance == 4, "doppelte Resonanz verdoppelt die Vorschau")
|
||||
var real := g.play_card(h, C("f_kobold"))
|
||||
var kobold: Game.Creature = h.board[-1]
|
||||
check(real == "" and kobold.power() == 2 + 4 + 1, "das Ausspielen bringt genau den vorhergesagten Angriff")
|
||||
|
||||
|
||||
func test_verschmelzen() -> void:
|
||||
var s := setup()
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_wolf") # Feuer 3/2, Kosten 2
|
||||
var b := put(h, "w_reiter") # Wasser 3/3, Kosten 2
|
||||
check(Cards.fusion_cost(a.card, b.card) == 2 and Cards.fusion_cost(C("f_kobold"), C("f_asche")) == 2, "Kosten: Durchschnitt, aufgerundet")
|
||||
@@ -719,7 +680,7 @@ func test_feldkombos() -> void:
|
||||
var s := setup(["w_reiter"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
put(h, "f_wolf")
|
||||
var weak := put(o, "f_kobold")
|
||||
var strong := put(o, "e_troll")
|
||||
@@ -730,20 +691,19 @@ func test_feldkombos() -> void:
|
||||
s = setup(["e_golem"])
|
||||
g = s[0]
|
||||
h = s[1]
|
||||
o = s[2][0]
|
||||
o = s[2]
|
||||
put(h, "f_wolf")
|
||||
var k := put(o, "f_kobold")
|
||||
var big := put(o, "e_troll")
|
||||
g.play_card(h, C("e_golem"))
|
||||
check(k in o.board and big.health == 5, "Lava trifft nur das stärkste Wesen")
|
||||
# Blitz: 2 Schaden an den schwächsten Gegner
|
||||
s = setup(["l_wind"], [], 2)
|
||||
# Blitz: 2 Schaden an den Gegner
|
||||
s = setup(["l_wind"])
|
||||
g = s[0]
|
||||
h = s[1]
|
||||
put(h, "f_wolf")
|
||||
s[2][1].life = 5
|
||||
g.play_card(h, C("l_wind"))
|
||||
check(s[2][1].life == 3 and s[2][0].life == 15, "Blitz trifft den schwächsten Gegner")
|
||||
check(s[2].life == 13, "Blitz trifft den Gegner")
|
||||
# Schlamm heilt, Sturm gibt Mana, Sand gibt +1/+1
|
||||
s = setup(["e_wall", "l_wind", "w_reiter"])
|
||||
g = s[0]
|
||||
@@ -773,7 +733,7 @@ func test_feldkombos() -> void:
|
||||
s = setup(["fe_golem"])
|
||||
g = s[0]
|
||||
h = s[1]
|
||||
o = s[2][0]
|
||||
o = s[2]
|
||||
put(h, "w_reiter")
|
||||
var t := put(o, "e_troll")
|
||||
g.play_card(h, C("fe_golem"))
|
||||
@@ -784,7 +744,7 @@ func test_schild_effekt_und_sonne() -> void:
|
||||
var s := setup(["el_duenen", "fff_sonne"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_wolf")
|
||||
g.play_card(h, C("el_duenen"), a)
|
||||
check(a.shield, "Dünenläufer gibt Schild")
|
||||
@@ -792,7 +752,7 @@ func test_schild_effekt_und_sonne() -> void:
|
||||
s = setup(["fff_sonne"])
|
||||
g = s[0]
|
||||
h = s[1]
|
||||
o = s[2][0]
|
||||
o = s[2]
|
||||
check(C("fff_sonne").elements == ["Feuer"] and C("fff_sonne").cost == 9, "Kern der Sonne ist ein teures Feuerwesen")
|
||||
var troll := put(o, "e_troll")
|
||||
g.play_card(h, C("fff_sonne"))
|
||||
@@ -803,7 +763,7 @@ func test_kopie() -> void:
|
||||
var s := setup(["f_wolf"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
var a := put(h, "f_asche")
|
||||
var g2 := g.clone()
|
||||
var a2 := g2.find_creature(a.uid)
|
||||
@@ -823,7 +783,7 @@ func test_ki_besser_als_gierig() -> void:
|
||||
for i in games:
|
||||
var pool: Array = Cards.ELEMENTS.duplicate()
|
||||
Game.shuffle(pool, r)
|
||||
var g := Game.new_battle(1, r)
|
||||
var g := Game.new_battle(r)
|
||||
# abwechselnd beginnt die schlaue oder die gierige KI
|
||||
var smart_is_human := i % 2 == 0
|
||||
var guard := 0
|
||||
@@ -855,22 +815,22 @@ func test_kampagne() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 4
|
||||
check(Campaign.STAGES.size() == 10, "zehn Stufen")
|
||||
var opps := Campaign.opponents(4)
|
||||
check(opps.size() == 3 and opps[0].name == "Vulkar" and opps[0].title == "Herr der Lava", "Stufe 5: Vulkar mit 2 Begleitern")
|
||||
check(opps[0].elements == ["Feuer", "Erde"] and opps[0].shop.all(func(c): return c.elements.all(func(e): return e in ["Feuer", "Erde"])), "Boss handelt nur mit seinen Elementen")
|
||||
var g := Game.new_battle_with(opps, r)
|
||||
check(g.opponents.size() == 3 and g.human.life == Game.START_LIFE, "Kampagnenkampf gestartet")
|
||||
var boss := Campaign.opponent(4)
|
||||
check(boss.name == "Vulkar" and boss.title == "Herr der Lava" and boss.life == 32, "Stufe 5: Vulkar mit 32 Leben")
|
||||
check(boss.elements == ["Feuer", "Erde"] and boss.shop.all(func(c): return c.elements.all(func(e): return e in ["Feuer", "Erde"])), "Boss handelt nur mit seinen Elementen")
|
||||
var g := Game.new_battle_with(boss, r)
|
||||
check(g.opponent == boss and g.human.life == Game.START_LIFE, "Kampagnenkampf gestartet")
|
||||
var p := Profile.starter()
|
||||
var reward := p.record_battle(true, 1, 0)
|
||||
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, 1, 0)
|
||||
check(p.campaign_stage == 1 and reward == Profile.REWARD_WIN[1], "Wiederholung gibt normale Belohnung")
|
||||
reward = p.record_battle(false, 2, 1)
|
||||
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")
|
||||
p.campaign_stage = 9
|
||||
p.record_battle(true, 5, 9)
|
||||
p.record_battle(true, 9)
|
||||
check(p.campaign_stage == 10, "Kampagne abgeschlossen")
|
||||
var sim := Game.new_battle_with(Campaign.opponents(0), 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)
|
||||
@@ -916,7 +876,7 @@ func test_merkmale() -> void:
|
||||
var s := setup(["f_wolf", "l_falke", "e_wall"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
h.perks = ["feuer_angriff", "flink_angriff", "waechter_leben", "schild_wesen", "alle_flink", "resonanz_doppelt"]
|
||||
g.play_card(h, C("f_wolf"))
|
||||
var wolf: Game.Creature = h.board[0]
|
||||
@@ -932,15 +892,15 @@ func test_merkmale() -> void:
|
||||
check(Campaign.STAGES.any(func(st): return st.perk == perk), "Merkmal vergeben: " + perk)
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 2
|
||||
var mara: Game.Player = Campaign.opponents(1)[0]
|
||||
check(mara.life == 35 and mara.perks == ["leben_35"], "Mara hat 35 Leben")
|
||||
var g3 := Game.new_battle_with(Campaign.opponents(9), r, true)
|
||||
var archi: Game.Player = g3.opponents[0]
|
||||
var mara: Game.Player = Game.new_battle_with(Campaign.opponent(1), r).opponent
|
||||
check(mara.life == 35 and mara.max_life == 35 and mara.perks == ["leben_35"], "Mara hat mit ihrem Merkmal 35 Leben")
|
||||
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 == 3 + 1, "Archimagier beginnt mit 3 Mana (plus Ausgleich)")
|
||||
var nyx: Game.Player = Campaign.opponents(5)[0]
|
||||
var g4 := Game.new_battle_with([nyx], r, true)
|
||||
var nyx: Game.Player = Campaign.opponent(5)
|
||||
var g4 := Game.new_battle_with(nyx, r, true)
|
||||
g4.end_turn()
|
||||
# Einkommen 1, dazu 1 Mana für ihr Merkmal und 1 als Ausgleich für den späteren Start
|
||||
check(nyx.max_energy == 1 and nyx.energy == 3, "Nyx bekommt 1 Mana mehr als ihr Einkommen")
|
||||
@@ -952,7 +912,7 @@ func test_ki_zwei_schritte() -> void:
|
||||
var s := setup(["el_koenig"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
h.turns = 2
|
||||
o.life = 5
|
||||
put(h, "f_wolf", false)
|
||||
@@ -966,10 +926,10 @@ func test_ki_zwei_schritte() -> void:
|
||||
|
||||
|
||||
func test_snapshot() -> void:
|
||||
var s := setup(["f_wolf"], ["w_frost"], 2)
|
||||
var s := setup(["f_wolf"], ["w_frost"])
|
||||
var g: Game = s[0]
|
||||
var h: Game.Player = s[1]
|
||||
var o: Game.Player = s[2][0]
|
||||
var o: Game.Player = s[2]
|
||||
g.play_card(h, C("f_wolf"))
|
||||
put(o, "e_troll")
|
||||
var hi := g.players.find(h)
|
||||
@@ -979,7 +939,7 @@ func test_snapshot() -> void:
|
||||
var snap := g.snapshot(hi)
|
||||
var json: Dictionary = JSON.parse_string(JSON.stringify(snap))
|
||||
var g2 := Game.from_snapshot(json)
|
||||
check(g2.players.size() == 3 and g2.turn == g.turn and g2.current_idx == g.current_idx, "Grunddaten")
|
||||
check(g2.players.size() == 2 and g2.turn == g.turn and g2.current_idx == g.current_idx, "Grunddaten")
|
||||
check(g2.players[hi].hand.is_empty(), "eigene Hand sichtbar")
|
||||
check(g2.players[oi].hand.size() == 1 and g2.players[oi].hand[0] != C("w_frost"), "fremde Hand nur als Anzahl")
|
||||
check(g2.players[oi].shop.is_empty() and g2.players[hi].shop.size() == Game.SHOP_SIZE, "eigener Laden sichtbar, fremder nicht")
|
||||
@@ -1002,17 +962,17 @@ func test_kapitel_zwei_und_merkmalwahl() -> void:
|
||||
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 opps := Campaign.opponents(19)
|
||||
check(opps.size() == 5 and opps[0].name == "Archimagier" and opps[0].perks.size() == 3, "letzte Stufe: Archimagier mit drei Merkmalen, voller Tisch")
|
||||
var archi2 := Campaign.opponent(19)
|
||||
check(archi2.name == "Archimagier" and archi2.perks.size() == 3 and archi2.life == 34, "letzte Stufe von Kapitel 2: Archimagier mit drei Merkmalen und 34 Leben")
|
||||
var p := Profile.starter()
|
||||
check(not p.perk_unlocked(), "Merkmalwahl erst nach Kapitel 1")
|
||||
p.campaign_stage = 10
|
||||
check(p.perk_unlocked(), "nach Kapitel 1 freigeschaltet")
|
||||
p.perk = "feuer_angriff"
|
||||
var g := Game.new_battle(1, r, {"human_perks": [p.perk]})
|
||||
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, 5, 19)
|
||||
p.record_battle(true, 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, "Version 5 gelesen")
|
||||
@@ -1021,7 +981,7 @@ func test_kapitel_zwei_und_merkmalwahl() -> void:
|
||||
func test_ki_zeitbudget() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 12
|
||||
var g := Game.new_battle(5, r)
|
||||
var g := Game.new_battle(r)
|
||||
var h: Game.Player = g.players[0]
|
||||
h.energy = 10
|
||||
h.max_energy = 10
|
||||
@@ -1058,7 +1018,7 @@ func test_englische_karten() -> void:
|
||||
func test_kampf_speichern() -> void:
|
||||
var r := RandomNumberGenerator.new()
|
||||
r.seed = 7
|
||||
var g := Game.new_battle(2, r)
|
||||
var g := Game.new_battle(r)
|
||||
g.start()
|
||||
AI.take_turn(g, g.current())
|
||||
var d: Dictionary = JSON.parse_string(JSON.stringify(g.snapshot(-1)))
|
||||
@@ -1075,7 +1035,7 @@ func test_kampf_speichern() -> void:
|
||||
while g2.winner == "" and guard < 300:
|
||||
AI.take_turn(g2, g2.current())
|
||||
guard += 1
|
||||
check(g2.winner != "", "fortgesetzte Partie ist bis zum Ende spielbar")
|
||||
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.shop.is_empty()), "Zuschauer-Sicht ohne Hände und Läden")
|
||||
|
||||
|
||||
148
tests/ui_test.gd
148
tests/ui_test.gd
@@ -26,6 +26,7 @@ func frames(n: int = 2) -> void:
|
||||
func shot(name: String) -> void:
|
||||
if shots == "":
|
||||
return
|
||||
await frames(10) # Einblenden und Austeilen abwarten
|
||||
await RenderingServer.frame_post_draw
|
||||
get_viewport().get_texture().get_image().save_png(shots.path_join(name + ".png"))
|
||||
|
||||
@@ -142,18 +143,15 @@ func _ready() -> void:
|
||||
for suffix in ["", ".bak", ".corrupt"]:
|
||||
if FileAccess.file_exists(SAVE + suffix):
|
||||
DirAccess.remove_absolute(abs + suffix)
|
||||
if FileAccess.file_exists(SAVE.get_basename() + "_kampf.json"):
|
||||
DirAccess.remove_absolute(abs.get_basename() + "_kampf.json")
|
||||
main = load("res://scenes/main.tscn").instantiate()
|
||||
main.save_path = SAVE
|
||||
add_child(main)
|
||||
await frames(3)
|
||||
var fast := OS.get_environment("DINGER_FAST") != ""
|
||||
var suite := [test_menu, test_gamepad_menue, test_booster, test_gegnerauswahl, test_einstellungen,
|
||||
test_kampagne, test_sammlung, test_sprache, test_kampf_steuerung, test_kampf_feld_und_verschmelzen, test_kampf_grenzen,
|
||||
test_kampf_fortsetzen]
|
||||
test_kampagne, test_sammlung, test_sprache, test_kampf_steuerung, test_kampf_feld_und_verschmelzen, test_kampf_grenzen]
|
||||
if not fast:
|
||||
suite += [test_kampf.bind(2, 3), test_kampf_abbrechen]
|
||||
suite += [test_kampf.bind(3), test_kampf_abbrechen]
|
||||
# Die Netzwerktests starten Bot-Prozesse und warten auf Verbindungen; das dauert Minuten.
|
||||
# Sie laufen nur auf Wunsch: DINGER_NETZ=1 (oder gezielt über DINGER_ONLY).
|
||||
if OS.get_environment("DINGER_NETZ") != "":
|
||||
@@ -254,7 +252,7 @@ func human_turn(scr) -> void:
|
||||
"' Zug Mensch=", g.current().is_human, " kann=", cr.can_attack(), " pending=", scr.pending, " selected=", scr.selected)
|
||||
check(scr.selected == cr, "Wesen gewählt")
|
||||
var targets := []
|
||||
for o in g.opponents:
|
||||
for o in [g.opponent]:
|
||||
if o.alive() and g.can_attack(cr, o)[0]:
|
||||
targets.append(o)
|
||||
for c in o.board:
|
||||
@@ -267,11 +265,9 @@ func human_turn(scr) -> void:
|
||||
await click(scr.b_end)
|
||||
|
||||
|
||||
func start_battle(n_opp: int) -> void:
|
||||
func start_battle() -> void:
|
||||
await click(find_button("Einzelspieler"))
|
||||
check(screen() is OpponentSelectScreen, "Gegnerauswahl")
|
||||
screen().count_slider.value = n_opp
|
||||
await frames(1)
|
||||
await click(find_button("Starten"))
|
||||
|
||||
|
||||
@@ -279,51 +275,33 @@ func test_gegnerauswahl() -> void:
|
||||
await click(find_button("Einzelspieler"))
|
||||
var sel = screen()
|
||||
check(sel is OpponentSelectScreen, "Gegnerauswahl geöffnet")
|
||||
check(find_button("Neu würfeln") == null and find_button("1 Gegner") == null and find_button("Starten") != null, "kein Würfeln und keine Gegnerknöpfe, nur Starten")
|
||||
check(find_button("Feuer") == null and find_button("Luft") == null, "keine Elementwahl mehr")
|
||||
check(sel.count_slider.min_value == 1 and sel.count_slider.max_value == Game.MAX_OPPONENTS and sel.count_slider.step == 1, "Gegnerzahl als Schieberegler von 1 bis %d" % Game.MAX_OPPONENTS)
|
||||
check(sel.opponents() == 1 and sel.count_label.text == "1 Gegner" and "Duell" in sel.info_label.text, "Regler beginnt beim Duell")
|
||||
var rect: Rect2 = sel.count_slider.get_global_rect()
|
||||
await click_at(Vector2(rect.end.x - 4, rect.get_center().y)) # ganz rechts anklicken
|
||||
check(sel.opponents() == Game.MAX_OPPONENTS and sel.count_label.text == "%d Gegner" % Game.MAX_OPPONENTS, "Mausklick am rechten Rand stellt %d Gegner ein" % Game.MAX_OPPONENTS)
|
||||
sel.count_slider.grab_focus()
|
||||
await key(KEY_LEFT)
|
||||
check(sel.opponents() == Game.MAX_OPPONENTS - 1, "Pfeiltaste links nimmt einen Gegner weg")
|
||||
sel.count_slider.value = 4
|
||||
await frames(1)
|
||||
check(sel.opponents() == 4 and sel.count_label.text == "4 Gegner" and "5 am Tisch" in sel.info_label.text
|
||||
and str(Profile.REWARD_WIN[4]) in sel.info_label.text, "Regler auf 4: fünf am Tisch mit Belohnung")
|
||||
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")
|
||||
await shot("07_gegnerauswahl")
|
||||
sel.count_slider.value = 1
|
||||
await frames(1)
|
||||
await click(find_button("Starten"))
|
||||
var hard = screen()
|
||||
check(hard is BattleScreen and hard.game.opponents.size() == 1, "Kampf mit einem Gegner gestartet")
|
||||
var opp: Game.Player = hard.game.opponents[0]
|
||||
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.shop.size() == Game.SHOP_SIZE and hard.game.human.hand.is_empty(),
|
||||
"Start mit vollem Laden und leerer Hand")
|
||||
check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht oben in der Mitte")
|
||||
await shot("11_kampf")
|
||||
await give_up()
|
||||
await click(find_button("Einzelspieler"))
|
||||
await click(find_button("Leicht"))
|
||||
await click(find_button("Starten"))
|
||||
var easy = screen()
|
||||
opp = easy.game.opponents[0]
|
||||
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 == 1 and easy.game.human.life == Game.START_LIFE, "gleiche Regeln für beide")
|
||||
await give_up()
|
||||
await click(find_button("Einzelspieler"))
|
||||
await click(find_button("Normal"))
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
for n in [1, 5]:
|
||||
await start_battle(n)
|
||||
var scr = screen()
|
||||
check(scr is BattleScreen and scr.game.opponents.size() == n, "Kampf gegen %d gestartet" % n)
|
||||
check(scr.game.players[0].max_energy == 1 and scr.game.opponents[0].life == Game.START_LIFE, "gleiche Regeln gegen %d" % n)
|
||||
await shot("11_kampf_%d" % n)
|
||||
await give_up()
|
||||
|
||||
|
||||
## Kampf verlassen: nach Spielende über den Ergebnisdialog, sonst per Aufgeben.
|
||||
@@ -348,32 +326,6 @@ func give_up() -> void:
|
||||
check(screen() is MenuScreen, "Aufgeben führt ins Menü")
|
||||
|
||||
|
||||
## Kampf speichern, im Menü fortsetzen, Aufgeben löscht die Datei.
|
||||
func test_kampf_fortsetzen() -> void:
|
||||
await start_battle(1)
|
||||
var scr = screen()
|
||||
scr.ai_delay = 0.0
|
||||
await wait_for_human(scr)
|
||||
var kampf: String = main.battle_save_path()
|
||||
check(FileAccess.file_exists(kampf), "Kampf wird laufend gespeichert")
|
||||
await click(scr.b_end)
|
||||
await wait_for_human(scr)
|
||||
var turn: int = scr.game.turn
|
||||
var life: int = scr.game.human.life
|
||||
var hand: int = scr.game.human.hand.size()
|
||||
await click(find_button("Aufgeben"))
|
||||
await click(find_button("Speichern und zurück"))
|
||||
check(screen() is MenuScreen and find_button("Kampf fortsetzen") != null, "Speichern führt ins Menü mit Fortsetzen-Knopf")
|
||||
await click(find_button("Kampf fortsetzen"))
|
||||
var scr2 = screen()
|
||||
check(scr2 is BattleScreen and scr2.game.turn == turn and scr2.game.human.life == life and scr2.game.human.hand.size() == hand
|
||||
and scr2.game.current() == scr2.game.human, "Kampf fortgesetzt mit gleichem Zug, Leben und Hand")
|
||||
scr2.ai_delay = 0.0
|
||||
await shot("30_kampf_fortgesetzt")
|
||||
await give_up()
|
||||
check(not FileAccess.file_exists(kampf) and find_button("Kampf fortsetzen") == null, "Aufgeben löscht den gespeicherten Kampf")
|
||||
|
||||
|
||||
func test_einstellungen() -> void:
|
||||
await click(find_button("Einstellungen"))
|
||||
var scr = screen()
|
||||
@@ -414,6 +366,14 @@ func test_sammlung() -> void:
|
||||
check(scr.selected.id == "ff_phoenix" and p.rarity("ff_phoenix") == "gewöhnlich", "Karte gewählt, noch gewöhnlich")
|
||||
await click(scr.b_upgrade)
|
||||
check(p.level("ff_phoenix") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["selten"], "auf selten 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")
|
||||
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)
|
||||
@@ -491,7 +451,7 @@ func test_sprache() -> void:
|
||||
|
||||
## Steuerung im Kampf: Tastatur, Rückgängig, Gamepad, Überspringen, Esc.
|
||||
func test_kampf_steuerung() -> void:
|
||||
await start_battle(1)
|
||||
await start_battle()
|
||||
var scr = screen()
|
||||
check(scr is BattleScreen, "Kampf gestartet")
|
||||
scr.ai_delay = 0.0
|
||||
@@ -551,7 +511,7 @@ func test_kampf_steuerung() -> void:
|
||||
|
||||
## Grenzen im Kampf: volle Hand, volles Feld, kein Mana.
|
||||
func test_kampf_grenzen() -> void:
|
||||
await start_battle(1)
|
||||
await start_battle()
|
||||
var scr = screen()
|
||||
scr.ai_delay = 0.0
|
||||
await wait_for_human(scr)
|
||||
@@ -603,26 +563,45 @@ func test_kampf_grenzen() -> void:
|
||||
|
||||
## Feldanzeige und Verschmelzen im Kampf.
|
||||
func test_kampf_feld_und_verschmelzen() -> void:
|
||||
await start_battle(1)
|
||||
await start_battle()
|
||||
var scr = screen()
|
||||
check(scr is BattleScreen, "Kampf gestartet")
|
||||
scr.ai_delay = 0.0
|
||||
await wait_for_human(scr)
|
||||
var g: Game = scr.game
|
||||
g.human.board.clear()
|
||||
for id in ["f_wolf", "w_reiter"]:
|
||||
var cr := Game.Creature.new(Cards.by_id(id), g.human, g.next_uid)
|
||||
var put := func(p: Game.Player, id: String) -> Game.Creature:
|
||||
var cr := Game.Creature.new(Cards.by_id(id), p, g.next_uid)
|
||||
g.next_uid += 1
|
||||
g.human.board.append(cr)
|
||||
cr.ready = true
|
||||
cr.attacks_left = 1
|
||||
p.board.append(cr)
|
||||
return cr
|
||||
put.call(g.human, "f_wolf")
|
||||
put.call(g.human, "w_reiter")
|
||||
var falke: Game.Creature = put.call(scr.others()[0], "l_falke")
|
||||
g.human.hand = [Cards.by_id("f_kobold")]
|
||||
g.human.energy = 10
|
||||
g.field_charge["Wasser"] = 2
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.field_view.text() == "Feuer 0/4 · Wasser 2/4 · Erde 0/4 · Luft 0/4", "neutrales Feld zeigt die Ladung: " + scr.field_view.text())
|
||||
for i in Cards.FIELD_CHARGE:
|
||||
g.charge_field(Cards.by_id("f_kobold"))
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.field_label.text.begins_with("Vulkan"), "Feldanzeige nennt das Feld: " + scr.field_label.text)
|
||||
check(scr.field_view.text().begins_with("Vulkan: +1 Angriff"), "Feldanzeige nennt das Feld: " + scr.field_view.text())
|
||||
check(g.human.board[0].field_bonus == 1 and g.human.board[0].power() == 4, "das Feuerwesen bekommt +1 Angriff")
|
||||
check(scr.hand_views[0].bonus == 2 and scr.hand_views[0].tag == "Dampf", "die Handkarte zeigt +2 (Resonanz und Feld) und die Kombo Dampf")
|
||||
check(scr.b_fuse.visible and scr.b_fuse.text == "Verschmelzen", "Verschmelzen wird angeboten")
|
||||
await shot("23_feld")
|
||||
await hover(scr.hand_views[0])
|
||||
check(scr.tiles[g.human.board[1].uid].mark == "Dampf" and "Dampf" in scr.info_label.text, "beim Zeigen auf die Handkarte ist der Kombo-Partner markiert")
|
||||
await click(scr.own_views[0])
|
||||
check(scr.selected == g.human.board[0] and scr.tiles[falke.uid].matchup == 1, "Angreifer gewählt: das Luftwesen zeigt +1 für Feuer")
|
||||
await shot("23b_angriffsziel")
|
||||
await right_click() # Auswahl wieder aufheben
|
||||
check(scr.selected == null and scr.tiles[falke.uid].matchup == 0, "Auswahl aufgehoben, Vorteil verschwindet")
|
||||
await click(scr.b_fuse)
|
||||
check(scr.fusing and scr.b_fuse.text == "Abbrechen", "Verschmelzen ausgewählt")
|
||||
await click(scr.own_views[0])
|
||||
@@ -834,13 +813,15 @@ func test_kampagne() -> void:
|
||||
var scr = screen()
|
||||
check(scr is CampaignScreen, "Kampagne geöffnet")
|
||||
await shot("15_kampagne")
|
||||
check(find_button("Gesperrt") != null and find_button("Kämpfen") != null, "Stufen gesperrt und offen")
|
||||
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")
|
||||
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")
|
||||
await click(find_button("Kämpfen"))
|
||||
var battle = screen()
|
||||
check(battle is BattleScreen and battle.stage == 0, "Kampagnenkampf gestartet")
|
||||
check(battle.game.opponents.size() == 1 and battle.game.opponents[0].name == "Fenn", "Fenn ist der Gegner")
|
||||
check(battle.game.opponents[0].perks == ["feuer_angriff"] and battle.game.log.any(func(l): return l.begins_with("Fenn: Feuerwesen")), "Boss-Merkmal aktiv")
|
||||
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")
|
||||
await give_up()
|
||||
# Kapitel 1 als abgeschlossen setzen: Kapitel 2 und Merkmalwahl
|
||||
@@ -854,7 +835,7 @@ 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.opponents[0].perks.size() == 2, "Kapitel-2-Boss mit zwei Merkmalen")
|
||||
check(b2 is BattleScreen and b2.stage == 10 and b2.game.opponent.perks.size() == 2 and b2.game.opponent.life == 30, "Kapitel-2-Boss mit zwei Merkmalen und 30 Leben")
|
||||
check(b2.game.human.perks == [main.profile.perk], "eigenes Merkmal im Kampf")
|
||||
await give_up()
|
||||
main.profile.campaign_stage = 0
|
||||
@@ -862,13 +843,13 @@ func test_kampagne() -> void:
|
||||
main.save()
|
||||
|
||||
|
||||
func test_kampf(n_opp: int, shot_turn: int) -> void:
|
||||
await start_battle(n_opp)
|
||||
func test_kampf(shot_turn: int) -> void:
|
||||
await start_battle()
|
||||
var scr = screen()
|
||||
check(scr is BattleScreen, "Kampfbildschirm")
|
||||
scr.ai_delay = 0.0
|
||||
var g: Game = scr.game
|
||||
check(g.opponents.size() == n_opp and g.log[0].begins_with("Zugreihenfolge"), "Spiel gestartet, Reihenfolge ausgelost")
|
||||
check(g.players.size() == 2 and g.log[0].begins_with("Zugreihenfolge"), "Spiel gestartet, Reihenfolge ausgelost")
|
||||
await wait_for_human(scr)
|
||||
check(g.current() == g.human, "Spieler ist dran")
|
||||
check(scr.hand_views.size() == g.human.hand.size(), "Handkarten sichtbar")
|
||||
@@ -898,12 +879,12 @@ func test_kampf(n_opp: int, shot_turn: int) -> void:
|
||||
|
||||
|
||||
func test_kampf_abbrechen() -> void:
|
||||
await start_battle(5)
|
||||
await start_battle()
|
||||
var scr = screen()
|
||||
scr.ai_delay = 0.0
|
||||
var g: Game = scr.game
|
||||
await wait_for_human(scr)
|
||||
check(g.opponents.size() == 5 and g.human.max_energy == 1, "5 Gegner")
|
||||
check(g.opponent != null and g.human.max_energy == 1, "Duell läuft")
|
||||
# Kartenvorschau: aufgewertete Karten zeigen ihre Seltenheit
|
||||
g.human.energy = 10
|
||||
var wolf := Game.Creature.new(Cards.by_id("f_wolf"), g.human, 9000)
|
||||
@@ -913,7 +894,7 @@ func test_kampf_abbrechen() -> void:
|
||||
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")
|
||||
await hover(scr.log_box) # erst weg von der Hand, damit das Betreten sicher ausgelöst wird
|
||||
await hover(scr.field_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")
|
||||
@@ -930,11 +911,10 @@ func test_kampf_abbrechen() -> void:
|
||||
print("DEBUG Hover: overlay=", scr.any_overlay(), " hand=", scr.hand_views.size(),
|
||||
" dran=", g.current().name, " Sieger=", g.winner, " hovered=", get_viewport().gui_get_hovered_control())
|
||||
check(scr.preview_holder.get_child_count() > 0, "Vorschau bei Hover")
|
||||
await click(find_button("Lupe"))
|
||||
check(scr.zoom_player == scr.others()[0], "Lupe öffnet die erste Spalte")
|
||||
await shot("12_lupe")
|
||||
await click(find_button("Schließen"))
|
||||
check(scr.zoom_player == null, "Lupe geschlossen")
|
||||
await click(scr.b_log)
|
||||
check(scr.log_overlay.visible and scr.any_overlay(), "Verlauf öffnet als Überlagerung")
|
||||
await key(KEY_ESCAPE)
|
||||
check(not scr.log_overlay.visible, "Esc schließt den Verlauf")
|
||||
# Runden laufen lassen, damit die Gegner ihre Züge machen
|
||||
for round in 4:
|
||||
await click(scr.b_end)
|
||||
@@ -946,4 +926,4 @@ func test_kampf_abbrechen() -> void:
|
||||
and g.log.any(func(l): return " greift " in l)
|
||||
check(ai_attacked, "Gegner spielen Karten und greifen an")
|
||||
check(scr.tiles.size() == g.players.reduce(func(acc, p): return acc + p.board.size(), 0), "eine Kachel je Wesen")
|
||||
await shot("10_kampf_fuenf")
|
||||
await shot("10_kampf_runden")
|
||||
|
||||
Reference in New Issue
Block a user