update
This commit is contained in:
418
tests/ui_test.gd
418
tests/ui_test.gd
@@ -117,10 +117,11 @@ func screen():
|
||||
return main.screen
|
||||
|
||||
|
||||
## Jeder Abschnitt startet im Menü, auch wenn ein vorheriger abgebrochen ist.
|
||||
## Jeder Abschnitt startet im Menü, auch wenn ein vorheriger abgebrochen ist; ohne Startdeck wird
|
||||
## Feuer gewählt.
|
||||
func ensure_menu() -> void:
|
||||
if main.net.connected:
|
||||
main.net.leave()
|
||||
if screen() is StartDeckScreen:
|
||||
await click(find_button("Feuer wählen"))
|
||||
if TranslationServer.get_locale().begins_with("en"):
|
||||
main.set_language("de")
|
||||
if not (screen() is MenuScreen):
|
||||
@@ -169,15 +170,11 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
main.sfx.muted = true # Tests bleiben still
|
||||
await frames(3)
|
||||
var suite := [test_menu, test_gamepad_menue, test_booster, test_gegnerauswahl, test_einstellungen,
|
||||
test_kampagne, test_sammlung, test_sprache, test_kampf_steuerung, test_kampf_elemente_und_verschmelzen, test_kampf_grenzen]
|
||||
var suite := [test_start, test_menu, test_gamepad_menue, test_booster, test_gegnerauswahl, test_einstellungen,
|
||||
test_kampagne, test_sammlung, test_deck, test_sprache, test_kampf_steuerung, test_kampf_elemente_und_verschmelzen, test_kampf_grenzen]
|
||||
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") != "":
|
||||
suite += [test_netzwerk, test_host_wechsel, test_zuschauer]
|
||||
suite.append(test_profil_zuruecksetzen)
|
||||
var only := OS.get_environment("DINGER_ONLY") # z. B. DINGER_ONLY=test_host_wechsel,test_zuschauer
|
||||
var only := OS.get_environment("DINGER_ONLY") # z. B. DINGER_ONLY=test_sammlung,test_kampf
|
||||
if only != "":
|
||||
suite = suite.filter(func(t: Callable): return t.get_method() in only.split(","))
|
||||
for t in suite:
|
||||
@@ -190,6 +187,18 @@ func _ready() -> void:
|
||||
get_tree().quit(1 if failed > 0 else 0)
|
||||
|
||||
|
||||
## Erster Start: eines von vier Startdecks wählen (ensure_menu hat schon eins gewählt, darum frisch).
|
||||
func test_start() -> void:
|
||||
main.reset_profile()
|
||||
await frames(2)
|
||||
check(screen() is StartDeckScreen and not main.profile.started(), "neues Profil: Startdeck wählen")
|
||||
await shot("00_startdeck")
|
||||
await click(find_button("Feuer wählen"))
|
||||
var p: Profile = main.profile
|
||||
check(screen() is MenuScreen and p.started() and p.deck.size() == Game.DECK_MIN and p.deck.filter(func(id): return Cards.by_id(id).elements[0] == "Feuer").size() == 25, "Feuer gewählt: 40 Karten, 25 davon Feuer")
|
||||
check(Profile.load_or_new(SAVE).started(), "gespeichert")
|
||||
|
||||
|
||||
func test_menu() -> void:
|
||||
check(screen() is MenuScreen, "Menü sichtbar")
|
||||
check(find_button("Einzelspieler") != null and find_button("Beenden") != null, "Menüknöpfe vorhanden")
|
||||
@@ -213,8 +222,8 @@ func test_booster() -> void:
|
||||
await shot("04_booster_offen")
|
||||
check(Profile.load_or_new(SAVE).coins == main.profile.coins, "Kauf gespeichert")
|
||||
for res in scr.cards:
|
||||
if res.upgraded:
|
||||
check(main.profile.level(res.card.id) == Cards.RARITIES.find(res.rarity), "aufgewertete Karte trägt ihre Stufe")
|
||||
if res.new or res.upgraded:
|
||||
check(main.profile.owns(res.card.id) and main.profile.level(res.card.id) == Cards.RARITIES.find(res.rarity), "neue oder aufgewertete Karte trägt ihre Stufe")
|
||||
else:
|
||||
check(res.dust > 0, "sonst gibt es Staub")
|
||||
check(scr.b_buy.disabled == (main.profile.coins < Profile.BOOSTER_COST), "Kaufknopf folgt dem Münzstand")
|
||||
@@ -255,6 +264,11 @@ func drag_fuse(scr, from: Control, to: Control) -> void:
|
||||
m.button_mask = MOUSE_BUTTON_MASK_LEFT
|
||||
get_viewport().push_input(m)
|
||||
await frames(1)
|
||||
if scr.drag_ghost == null or scr.drag_target == null:
|
||||
var fo = from.card if from is CardView else from.creature
|
||||
var to_o = to.card if to is CardView else to.creature
|
||||
print("DEBUG drag: ghost=", scr.drag_ghost, " target=", scr.drag_target, " press=", scr.press_target, " dran=", scr.my_turn(),
|
||||
" overlay=", scr.any_overlay(), " mana=", scr.game.human.energy, " kann=", scr.game.can_fuse(scr.game.human, fo, to_o), " from=", from.get_global_rect(), " to=", to.get_global_rect())
|
||||
check(scr.drag_ghost != null and scr.drag_target != null, "Karte schwebt über einem Verschmelzungsziel")
|
||||
var waited := 0
|
||||
while scr.drag_ghost != null and waited < 240:
|
||||
@@ -297,7 +311,7 @@ func human_turn(scr) -> void:
|
||||
var mana: int = g.human.energy
|
||||
var hand: int = g.human.hand.size()
|
||||
var cost: int = g.draw_cost(g.human)
|
||||
await click(scr.offer_views[0])
|
||||
await click(scr.deck)
|
||||
check(g.human.energy == mana - cost and g.human.hand.size() == hand + 1, "gezogen: eine Karte")
|
||||
var tried := [] # Wesen ohne lohnendes Ziel (0 Angriff ohne Vorteil) nicht wieder versuchen
|
||||
for i in 12:
|
||||
@@ -329,7 +343,7 @@ func human_turn(scr) -> void:
|
||||
await click(target_view(scr, targets))
|
||||
check(scr.selected == null and cr.attacks_done > 0, "Angriff ausgeführt")
|
||||
if g.winner == "":
|
||||
await click(scr.b_end)
|
||||
await key(KEY_SPACE)
|
||||
|
||||
|
||||
func start_battle() -> void:
|
||||
@@ -385,11 +399,14 @@ func leave_battle() -> void:
|
||||
|
||||
func give_up() -> void:
|
||||
var scr = screen()
|
||||
await click(find_button("Aufgeben"))
|
||||
check(scr.confirm.visible, "Aufgeben fragt nach")
|
||||
await key(KEY_ESCAPE)
|
||||
check(scr.options.visible, "Esc öffnet die Optionen")
|
||||
await click(find_button("Aufgeben", scr.options))
|
||||
check(scr.confirm.visible and not scr.options.visible, "Aufgeben fragt nach")
|
||||
await click(find_button("Weiterspielen"))
|
||||
check(not scr.confirm.visible and screen() == scr, "Weiterspielen bleibt im Kampf")
|
||||
await click(find_button("Aufgeben"))
|
||||
await key(KEY_ESCAPE)
|
||||
await click(find_button("Aufgeben", scr.options))
|
||||
await click(find_button("Ja, aufgeben"))
|
||||
check(screen() is MenuScreen, "Aufgeben führt ins Menü")
|
||||
|
||||
@@ -419,33 +436,75 @@ func test_einstellungen() -> void:
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
|
||||
## Deck: eigene Karten rein und raus, Auffüllen nach Filter, Speichern. Beginnt mit dem frischen Startdeck.
|
||||
func test_deck() -> void:
|
||||
main.profile.choose_start("Feuer")
|
||||
main.save()
|
||||
await click(find_button("Deck"))
|
||||
var scr = screen()
|
||||
check(scr is DeckScreen, "Deck geöffnet")
|
||||
var p: Profile = main.profile
|
||||
check(scr.grid.views().size() == p.collection.size() and scr.count_label.text == "Stapel: %d Karten (%d bis %d)" % [p.deck.size(), Game.DECK_MIN, Game.DECK_MAX], "alle eigenen Karten, Zähler stimmt")
|
||||
var views: Array = scr.grid.views()
|
||||
check(views.all(func(v): return not v.dim) and p.deck.size() == Game.DECK_MIN, "nach dem Start sind alle eigenen Karten im Stapel")
|
||||
var first_id: String = views[0].card.id
|
||||
await click(views[0])
|
||||
check(not (first_id in p.deck) and p.deck.size() == Game.DECK_MIN - 1 and scr.grid.views().any(func(v): return v.card.id == first_id and v.dim), "Klick nimmt die Karte aus dem Stapel, sie wird dunkel")
|
||||
var back_view = scr.grid.views().filter(func(v): return v.card.id == first_id)[0]
|
||||
scr.grid.ensure_control_visible(back_view) # sie liegt jetzt hinter den Stapelkarten, außerhalb des Sichtbereichs
|
||||
await frames(2)
|
||||
await click(back_view)
|
||||
check(first_id in p.deck and p.deck.size() == Game.DECK_MIN, "Klick nimmt sie wieder hinein")
|
||||
await shot("19_deck")
|
||||
await click(find_button("Leeren"))
|
||||
check(p.deck.is_empty(), "geleert")
|
||||
await click(find_button("Wasser"))
|
||||
await click(find_button("Auffüllen"))
|
||||
check(p.deck.size() == 5 and p.deck.all(func(id): return Cards.by_id(id).elements[0] == "Wasser"), "Auffüllen hält sich an den Filter: nur die fünf Wasserkarten")
|
||||
await click(find_button("Alle"))
|
||||
await click(find_button("Auffüllen"))
|
||||
check(p.deck.size() == Game.DECK_MIN and Profile.load_or_new(SAVE).deck == p.deck, "ohne Filter alles, gespeichert")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
|
||||
|
||||
func test_sammlung() -> void:
|
||||
await click(find_button("Sammlung"))
|
||||
var scr = screen()
|
||||
check(scr is CollectionScreen, "Sammlung geöffnet")
|
||||
var p: Profile = main.profile
|
||||
check(scr.grid.views().size() == Cards.all().size(), "alle Karten gehören dir")
|
||||
check(scr.grid.views().size() == Cards.all().size(), "alle Karten des Spiels sind zu sehen")
|
||||
check(scr.grid.views().slice(0, p.collection.size()).all(func(v): return not v.dim) and scr.grid.views().back().dim, "eigene Karten vorn, fehlende abgedunkelt")
|
||||
p.dust = 5000
|
||||
scr.search_box.text = "Phönix"
|
||||
scr.search_box.text_changed.emit("Phönix")
|
||||
scr.search_box.text = "Lodernder Funke"
|
||||
scr.search_box.text_changed.emit("Lodernder Funke")
|
||||
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") == "Common", "Karte gewählt, noch Common")
|
||||
var funke = scr.grid.views().filter(func(v): return v.card.id == "f_2_1")[0]
|
||||
await click(funke)
|
||||
check(scr.selected.id == "f_2_1" and p.rarity("f_2_1") == "Common" and scr.b_upgrade.visible, "eigene Karte gewählt, noch Common")
|
||||
await click(scr.b_upgrade)
|
||||
check(p.level("ff_phoenix") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["Rare"], "auf Rare aufgewertet")
|
||||
check(p.level("f_2_1") == 1 and p.dust == 5000 - Profile.DUST_UPGRADE["Rare"], "auf Rare aufgewertet")
|
||||
scr.search_box.text = ""
|
||||
scr.search_box.text_changed.emit("")
|
||||
await frames(1)
|
||||
var front: 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")
|
||||
var front = scr.grid.views().filter(func(v): return v.card.id == "f_2_1")[0]
|
||||
check(front.rarity == "Rare" and front.card.attack == 3 and front.card.health == 2 and scr.grid.views()[0].rarity != "Common", "die Rare zeigt +1/+1, aufgewertete stehen vorn: %s %d/%d" % [front.rarity, front.card.attack, front.card.health])
|
||||
check(Profile.load_or_new(SAVE).level("f_2_1") == 1, "Sammlung gespeichert")
|
||||
scr.search_box.text = "Lodernder Funke"
|
||||
scr.search_box.text_changed.emit("Lodernder Funke")
|
||||
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["Rare"], "wieder zerlegt")
|
||||
check(p.level("f_2_1") == 0 and p.dust == dust + Profile.DUST_DISMANTLE["Rare"], "wieder zerlegt")
|
||||
# Herstellen einer fehlenden Karte
|
||||
scr.search_box.text = "Infernalischer Drache"
|
||||
scr.search_box.text_changed.emit("Infernalischer Drache")
|
||||
await frames(1)
|
||||
await click(scr.grid.views().filter(func(v): return v.card.id == "f_5_5")[0])
|
||||
check(scr.b_craft.visible and not scr.b_upgrade.visible, "fehlende Karte: nur Herstellen")
|
||||
dust = p.dust
|
||||
await click(scr.b_craft)
|
||||
check(p.owns("f_5_5") and p.dust == dust - Profile.CRAFT_COST, "hergestellt")
|
||||
await shot("20_sammlung")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() is MenuScreen, "zurück im Menü")
|
||||
@@ -489,7 +548,9 @@ func test_profil_zuruecksetzen() -> void:
|
||||
check(not scr.confirm.visible, "abgebrochen")
|
||||
await click(find_button("Profil zurücksetzen"))
|
||||
await click(find_button("Ja, zurücksetzen"))
|
||||
check(screen() is MenuScreen and main.profile.coins == Profile.START_COINS and main.profile.collection.is_empty(), "Profil zurückgesetzt")
|
||||
check(screen() is StartDeckScreen and main.profile.coins == Profile.START_COINS and not main.profile.started(), "Profil zurückgesetzt: Startdeck neu wählen")
|
||||
await click(find_button("Luft wählen"))
|
||||
check(screen() is MenuScreen and main.profile.deck.filter(func(id): return Cards.by_id(id).elements[0] == "Luft").size() == 25, "Luft gewählt")
|
||||
check(Profile.load_or_new(SAVE).coins == Profile.START_COINS, "zurückgesetzt gespeichert")
|
||||
|
||||
|
||||
@@ -526,26 +587,26 @@ func test_kampf_steuerung() -> void:
|
||||
await wait_for_human(scr)
|
||||
var g: Game = scr.game
|
||||
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()]
|
||||
g.human.hand = [Cards.by_id("f_3_2").copy()]
|
||||
scr.refresh()
|
||||
check(scr.hand_views.size() == 1 and scr.draw_enabled and scr.offer_views.size() == Game.OFFER, "eine Handkarte, drei offene auf dem Stapel")
|
||||
# Ziehen: die gewählte offene Karte landet auf der Hand, das erste Ziehen ist frei
|
||||
check(scr.hand_views.size() == 1 and scr.deck.enabled, "eine Handkarte, Stapel bereit")
|
||||
# Ziehen: eine Karte vom eigenen Stapel, das erste Ziehen ist frei
|
||||
var mana: int = g.human.energy
|
||||
var wanted: Cards.Card = g.human.offer[1]
|
||||
await click(scr.offer_views[1])
|
||||
check(g.human.energy == mana and g.human.hand.size() == 2 and scr.hand_views.size() == 2 and g.human.hand[1].id == wanted.id, "gezogen: die gewählte Karte, frei")
|
||||
check(g.human.offer.size() == Game.OFFER and g.human.offer.all(func(c): return c.cost <= g.human.max_energy) and g.draw_cost(g.human) == Game.DRAW_COST, "eine bezahlbare neue liegt nach, das nächste Ziehen kostet 2")
|
||||
var deck_before: int = g.human.deck.size()
|
||||
await click(scr.deck)
|
||||
check(g.human.energy == mana and g.human.hand.size() == 2 and scr.hand_views.size() == 2 and g.human.deck.size() == deck_before - 1, "gezogen: eine Karte, frei")
|
||||
check(g.draw_cost(g.human) == 1 and scr.deck.cost == 1, "das nächste Ziehen kostet 1")
|
||||
# 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(g.human.board.size() == board_before + 1 and g.human.energy == mana - Cards.by_id("f_3_2").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)
|
||||
g = scr.game
|
||||
check(g.human.board.size() == board_before and g.human.hand.any(func(c): return c.id == "f_wolf") and g.human.energy == mana, "Rückgängig stellt Hand und Mana her")
|
||||
check(g.human.board.size() == board_before and g.human.hand.any(func(c): return c.id == "f_3_2") and g.human.energy == mana, "Rückgängig stellt Hand und Mana her")
|
||||
check(not scr.b_undo.visible and g.log.back() == "Rückgängig.", "Rückgängig verbraucht")
|
||||
await key(KEY_1)
|
||||
check(g.human.board.size() == board_before + 1, "erneut gespielt")
|
||||
@@ -553,7 +614,7 @@ func test_kampf_steuerung() -> void:
|
||||
mana = g.human.energy
|
||||
var hand_before: int = g.human.hand.size()
|
||||
await key(KEY_Z)
|
||||
check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == hand_before + 1, "Z zieht die erste offene Karte für 2")
|
||||
check(g.human.energy == mana - 1 and g.human.hand.size() == hand_before + 1, "Z zieht, das zweite Ziehen für 1")
|
||||
await shot("22_kampf_steuerung")
|
||||
# Gamepad: A auf dem Stapel zieht
|
||||
await pad(JOY_BUTTON_DPAD_LEFT)
|
||||
@@ -565,7 +626,7 @@ func test_kampf_steuerung() -> void:
|
||||
await key(KEY_SPACE)
|
||||
check(g.current() != g.human or g.turn >= 2, "Zug per Leertaste beendet")
|
||||
await frames(2)
|
||||
check(scr.b_skip.visible and not scr.b_end.visible, "Überspringen-Knopf während des Gegnerzugs")
|
||||
check(scr.b_skip.visible, "Überspringen-Knopf während des Gegnerzugs")
|
||||
await click(scr.b_skip)
|
||||
var w := 0
|
||||
while g.current() != g.human and g.winner == "" and w < 120:
|
||||
@@ -574,9 +635,15 @@ func test_kampf_steuerung() -> void:
|
||||
check(g.current() == g.human and w < 60, "Gegnerzug übersprungen (%d Bilder)" % w)
|
||||
scr.ai_delay = 0.0
|
||||
await key(KEY_ESCAPE)
|
||||
check(scr.confirm.visible, "Esc öffnet Aufgeben")
|
||||
check(scr.options.visible and find_button("Hauptmenü", scr.options) != null and find_button("Beenden", scr.options) != null, "Esc öffnet die Optionen")
|
||||
await key(KEY_ESCAPE)
|
||||
check(not scr.confirm.visible, "Esc schließt den Dialog")
|
||||
check(not scr.options.visible, "Esc schließt die Optionen")
|
||||
# Einstellungen aus dem Kampf und zurück
|
||||
await key(KEY_ESCAPE)
|
||||
await click(find_button("Einstellungen", scr.options))
|
||||
check(screen() is SettingsScreen and is_instance_valid(scr), "Einstellungen geöffnet, der Kampf bleibt erhalten")
|
||||
await click(find_button("Zurück"))
|
||||
check(screen() == scr and scr.game == g and not scr.options.visible, "zurück im selben Kampf")
|
||||
await give_up()
|
||||
|
||||
|
||||
@@ -594,21 +661,21 @@ func test_kampf_grenzen() -> void:
|
||||
var guard := 0
|
||||
while g.human.hand.size() < Game.MAX_HAND and guard < 30:
|
||||
guard += 1
|
||||
await click(scr.offer_views[0])
|
||||
await click(scr.deck)
|
||||
check(g.human.hand.size() == Game.MAX_HAND, "Hand auf %d gefüllt" % Game.MAX_HAND)
|
||||
check(not scr.draw_enabled, "bei voller Hand lässt sich nichts ziehen")
|
||||
await click(scr.offer_views[0])
|
||||
check(not scr.deck.enabled, "bei voller Hand lässt sich nichts ziehen")
|
||||
await click(scr.deck)
|
||||
check(scr.msg_label.text == tr("Deine Hand ist voll"), "Meldung bei voller Hand: " + scr.msg_label.text)
|
||||
# Feld voll spielen
|
||||
guard = 0
|
||||
while g.human.board.size() < Game.MAX_BOARD and guard < 30:
|
||||
guard += 1
|
||||
if g.human.hand.is_empty():
|
||||
await click(scr.offer_views[0])
|
||||
await click(scr.deck)
|
||||
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.offer_views[0])
|
||||
await click(scr.deck)
|
||||
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")
|
||||
@@ -619,10 +686,10 @@ func test_kampf_grenzen() -> void:
|
||||
check(g.human.board.size() == before - 1, "Verschmelzen macht einen Platz frei")
|
||||
# Ohne Mana kein Ziehen
|
||||
g.human.energy = 0
|
||||
g.human.drew = true
|
||||
g.human.draws = 1
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
check(not scr.draw_enabled, "ohne Mana kein zweites Ziehen")
|
||||
check(not scr.deck.enabled, "ohne Mana kein zweites Ziehen")
|
||||
await give_up()
|
||||
|
||||
|
||||
@@ -646,27 +713,27 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
cr.hidden = hidden
|
||||
p.board.append(cr)
|
||||
return cr
|
||||
var wolf_cr: Game.Creature = put.call(g.human, "f_wolf")
|
||||
wolf_cr.health = 4 # übersteht die Konter von Falke und Kobold, damit der Test ihn weiter nutzen kann
|
||||
var wolf_cr: Game.Creature = put.call(g.human, "f_3_2")
|
||||
wolf_cr.health = 4 # übersteht die Konter von Falke und Funke, damit der Test ihn weiter nutzen kann
|
||||
wolf_cr.max_health = 4
|
||||
put.call(g.human, "f_asche")
|
||||
put.call(g.human, "w_reiter")
|
||||
var falke: Game.Creature = put.call(scr.opponent(), "l_falke")
|
||||
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()]
|
||||
put.call(g.human, "f_4_3")
|
||||
put.call(g.human, "w_3_3")
|
||||
var falke: Game.Creature = put.call(scr.opponent(), "l_3_1")
|
||||
var secret: Game.Creature = put.call(scr.opponent(), "f_2_1", true)
|
||||
g.human.hand = [Cards.by_id("f_2_1").copy(), Cards.by_id("l_1_1").copy()]
|
||||
g.human.energy = 20
|
||||
g.human.looks = {"w_reiter": 3}
|
||||
g.human.looks = {"w_3_3": 3}
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(2)
|
||||
check(scr.element_view.text() == "Feuer 2/0 (Doppelschlag) · Wasser 1/0 · Erde 0/0 · Luft 0/0", "Zähler: offene eigene Wesen und ausgespielte Karten, Feuerpaar: " + scr.element_view.text())
|
||||
check(scr.opp_element_view.text() == "Feuer 0/0 · Wasser 0/0 · Erde 0/0 · Luft 1/0", "die Leiste des Gegners zählt nur seine offenen Wesen: " + scr.opp_element_view.text())
|
||||
check(scr.element_view.text() == "Feuer 2/0 (Doppelschlag) · Wasser 1/0", "Zähler: nur Elemente mit Wesen oder gespielten Karten, Feuerpaar: " + scr.element_view.text())
|
||||
check(scr.opp_element_view.text() == "Luft 1/0", "die Liste des Gegners zählt nur seine offenen Wesen: " + scr.opp_element_view.text())
|
||||
check(g.human.board[0].keywords() == ["Doppelschlag"] and scr.tiles[secret.uid].secret and scr.hand_views[0].bonus == 0, "Feuerpaar gibt Doppelschlag; fremdes Verdecktes zeigt den Rücken; noch kein Bonus in Sicht")
|
||||
# Rechtsklick auf die Handkarte: Menü mit Verdeckt legen, ohne Verschmelzen
|
||||
await hover(scr.hand_views[0])
|
||||
await right_click_at(scr.hand_views[0].get_global_rect().get_center())
|
||||
check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and find_button("Abwerfen +1 ◆", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü mit Verdeckt legen und Abwerfen")
|
||||
check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
|
||||
check(find_button("Verschmelzen mit …", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü")
|
||||
await shot("23_kartenmenue")
|
||||
await right_click()
|
||||
check(not scr.menu_layer.visible, "Rechtsklick schließt das Menü")
|
||||
@@ -685,16 +752,16 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
while scr.own_views[3].busy:
|
||||
await frames(1)
|
||||
await click(scr.own_views[3])
|
||||
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)
|
||||
check(mine_hidden.hidden and scr.msg_label.text == "Lodernder Funke kann erst im nächsten Zug aufgedeckt werden", "im Legezug nicht aufzudecken: " + scr.msg_label.text)
|
||||
mine_hidden.ready = true
|
||||
await click(scr.own_views[3])
|
||||
check(not mine_hidden.hidden and scr.element_view.text().begins_with("Feuer 3/1 (Doppelschlag)"), "aufgedeckt: drei offene Feuer, eine ausgespielt: " + scr.element_view.text())
|
||||
check(wolf.power() == 3 and falke.power() == 3 and mine_hidden.can_attack(), "unter 10 Mana kein Bonus, aufgedeckt greift sofort an")
|
||||
g.human.played["Feuer"] = 10
|
||||
check(wolf.power() == 3 and falke.power() == 3 and mine_hidden.can_attack(), "unter fünf Karten kein Bonus, aufgedeckt greift sofort an")
|
||||
g.human.played["Feuer"] = 5
|
||||
g.apply_auras()
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
check(scr.element_view.text().begins_with("Feuer 3/10 (Doppelschlag) (+1)") and wolf.power() == 4 and falke.power() == 3, "10 Mana Feuer: +1 Angriff für die eigenen Feuerwesen: " + scr.element_view.text())
|
||||
check(scr.element_view.text().begins_with("Feuer 3/5 (Doppelschlag) (+1/+1)") and wolf.power() == 4 and falke.power() == 3, "fünf Feuerkarten: +1/+1 für die eigenen Feuerwesen: " + scr.element_view.text())
|
||||
check(scr.hand_views[0].bonus == 0, "die Luftkarte auf der Hand zeigt keinen Bonus")
|
||||
await shot("23b_vier_feuer")
|
||||
# Angriffsziel: Elementvorteil, verdeckte ohne Anzeige, Held gesperrt
|
||||
@@ -714,220 +781,26 @@ func test_kampf_elemente_und_verschmelzen() -> void:
|
||||
mana = g.human.energy
|
||||
var before: int = g.human.board.size()
|
||||
await drag_fuse(scr, scr.own_views[1], scr.own_views[2])
|
||||
check(g.human.board.size() == before - 1 and g.human.board.any(func(c): return c.name == "Dampf") and g.human.energy == mana - 1, "zu Dampf verschmolzen, 1 Mana bezahlt")
|
||||
check(g.human.board.size() == before - 1 and g.human.board.any(func(c): return c.name == "Dampf") and g.human.energy == mana - 3, "zu Dampf verschmolzen, 3 Mana bezahlt (halbe Manasumme)")
|
||||
var dampf: Game.Creature = g.human.board.filter(func(c): return c.name == "Dampf")[0]
|
||||
check(scr.tiles[dampf.uid].rarity() == "Ultra Rare" and dampf.card.look == 3, "die Fusion trägt die höhere Stufe (Wellenreiter war Ultra Rare)")
|
||||
check(scr.tiles[dampf.uid].rarity() == "Ultra Rare" and dampf.card.look == 3, "die Fusion trägt die höhere Stufe (der Reiter war Ultra Rare)")
|
||||
await shot("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()]
|
||||
g.human.hand = [Cards.by_id("f_2_1").copy(), Cards.by_id("f_2_1").copy(), Cards.by_id("l_1_1").copy()]
|
||||
scr.refresh()
|
||||
await frames(1)
|
||||
await drag_fuse(scr, scr.hand_views[0], scr.hand_views[1])
|
||||
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")
|
||||
check(g.human.hand.size() == 2 and g.human.hand[0].name == "Inferno" and g.human.hand[0].attack == 3 and g.human.hand[0].health == 2, "zwei Funken auf der Hand zu Inferno 3/2")
|
||||
# Gamepad-Menü (Y) bietet Verdeckt legen und Verschmelzen mit …
|
||||
scr.open_menu(g.human.hand[0], Rect2(scr.hand_views[0].position, scr.hand_views[0].size), true)
|
||||
check(find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and g.human.hand[0].cost == 2, "Gamepad-Menü: Verdeckt legen, Inferno kostet 2")
|
||||
await click(find_button("Verschmelzen mit … ◆ 2", scr.menu_layer))
|
||||
await click(find_button("Verschmelzen mit …", scr.menu_layer))
|
||||
check(scr.fuse_first == g.human.hand[0], "Partner wählen")
|
||||
await click(scr.hand_views[1])
|
||||
check(g.human.hand.size() == 1 and g.human.hand[0].name == "Blitz", "Inferno und Windgeist zu Blitz")
|
||||
await give_up()
|
||||
|
||||
|
||||
func test_netzwerk() -> void:
|
||||
await click(find_button("Netzwerkspiel"))
|
||||
var scr = screen()
|
||||
check(scr is LobbyScreen, "Lobby geöffnet")
|
||||
await click(find_button("Spiel eröffnen"))
|
||||
check(main.net.is_host and main.net.connected, "Spiel eröffnet")
|
||||
if FileAccess.file_exists("user://bot_quit"):
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path("user://bot_quit"))
|
||||
var args := ["--headless", "--path", ProjectSettings.globalize_path("res://"), "tests/net_bot.tscn", "--", "--ip=127.0.0.1", "--name=Bot"]
|
||||
var pid := OS.create_process(OS.get_executable_path(), args)
|
||||
check(pid > 0, "Bot-Prozess gestartet")
|
||||
var wait := 0
|
||||
while main.net.names.size() < 2 and wait < 600:
|
||||
await frames(1)
|
||||
wait += 1
|
||||
if main.net.names.size() < 2:
|
||||
print("HINWEIS: Bot kam nicht, zweiter Versuch")
|
||||
OS.kill(pid)
|
||||
pid = OS.create_process(OS.get_executable_path(), args)
|
||||
while main.net.names.size() < 2 and wait < 1500:
|
||||
await frames(1)
|
||||
wait += 1
|
||||
check(main.net.names.size() == 2, "Bot ist beigetreten (Pfad %s)" % main.net.get_path())
|
||||
await shot("23_lobby")
|
||||
await click(find_button("Start"))
|
||||
var b = screen()
|
||||
check(b is BattleScreen and b.is_online() and not b.is_client(), "Netzwerkkampf als Host")
|
||||
b.ai_delay = 0.0
|
||||
var rounds := 0
|
||||
while b.game != null and b.game.winner == "" and rounds < 2:
|
||||
if b.game.current() == b.perspective:
|
||||
if b.game.current() == b.perspective:
|
||||
await click(b.b_end)
|
||||
var w2 := 0
|
||||
while b.game.current() != b.perspective and b.game.winner == "" and w2 < 900:
|
||||
await frames(1)
|
||||
w2 += 1
|
||||
rounds += 1
|
||||
if b.game.winner != "":
|
||||
print("DEBUG Netz: Sieger=", b.game.winner, " Verlauf=", b.game.log.slice(maxi(0, b.game.log.size() - 12)))
|
||||
check(b.game.turn >= 2 and b.game.winner == "", "Runden gespielt, Partie läuft (Zug %d)" % b.game.turn)
|
||||
check(b.game.log.any(func(l): return l.begins_with("Bot kauft") or l.begins_with("Bot spielt")), "Bot hat gehandelt")
|
||||
await shot("24_netzwerk_kampf")
|
||||
# Bot verlässt geordnet: Platz bleibt frei, Züge werden übersprungen
|
||||
var quit_file := "user://bot_quit"
|
||||
var f := FileAccess.open(quit_file, FileAccess.WRITE)
|
||||
f.store_string("x")
|
||||
f.close()
|
||||
var w3 := 0
|
||||
while main.net.waiting.is_empty() and w3 < 600:
|
||||
await frames(1)
|
||||
w3 += 1
|
||||
check(main.net.waiting.has("Bot") and b.game.log.any(func(l): return "Verbindung verloren" in l), "Platz wird freigehalten")
|
||||
var turn_before: int = b.game.turn
|
||||
for i in 2:
|
||||
if b.game.current() == b.perspective and b.game.winner == "":
|
||||
await click(b.b_end)
|
||||
var w4 := 0
|
||||
while b.game.current() != b.perspective and b.game.winner == "" and w4 < 300:
|
||||
await frames(1)
|
||||
w4 += 1
|
||||
check(b.game.turn >= turn_before + 1 and b.game.log.any(func(l): return "übersprungen" in l), "Züge des fehlenden Spielers werden übersprungen")
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(quit_file))
|
||||
await frames(5)
|
||||
var pid2 := OS.create_process(OS.get_executable_path(), args)
|
||||
var w5 := 0
|
||||
while not main.net.waiting.is_empty() and w5 < 900:
|
||||
await frames(1)
|
||||
w5 += 1
|
||||
check(main.net.waiting.is_empty() and main.net.seats.size() == 2 and b.game.log.any(func(l): return l == "Bot ist zurück."), "Bot wieder verbunden, Platz zurück")
|
||||
var turn2: int = b.game.turn
|
||||
for i in 2:
|
||||
if b.game.current() == b.perspective and b.game.winner == "":
|
||||
await click(b.b_end)
|
||||
var w6 := 0
|
||||
while b.game.current() != b.perspective and b.game.winner == "" and w6 < 900:
|
||||
await frames(1)
|
||||
w6 += 1
|
||||
check(b.game.turn >= turn2 + 1, "Partie läuft nach der Rückkehr weiter")
|
||||
OS.kill(pid2)
|
||||
await leave_battle()
|
||||
check(screen() is MenuScreen and not main.net.connected, "Netzwerk getrennt, zurück im Menü")
|
||||
|
||||
|
||||
func _bot_args(extra: Array) -> Array:
|
||||
return ["--headless", "--path", ProjectSettings.globalize_path("res://"), "tests/net_bot.tscn", "--"] + extra
|
||||
|
||||
|
||||
func _bot_quit(on: bool) -> void:
|
||||
var path := "user://bot_quit"
|
||||
if on:
|
||||
var f := FileAccess.open(path, FileAccess.WRITE)
|
||||
f.store_string("x")
|
||||
f.close()
|
||||
elif FileAccess.file_exists(path):
|
||||
DirAccess.remove_absolute(ProjectSettings.globalize_path(path))
|
||||
|
||||
|
||||
## Verbindet sich mit dem Bot-Host (mehrere Versuche, bis er lauscht).
|
||||
func _join_bot_host(watch: bool) -> void:
|
||||
await click(find_button("Netzwerkspiel"))
|
||||
var lobby = screen()
|
||||
lobby.ip_box.text = "127.0.0.1"
|
||||
for attempt in 8:
|
||||
await click(find_button("Zuschauen" if watch else "Beitreten"))
|
||||
var w := 0
|
||||
while not main.net.connected and w < 180:
|
||||
await frames(1)
|
||||
w += 1
|
||||
if main.net.connected:
|
||||
return
|
||||
main.net.leave()
|
||||
await frames(30)
|
||||
|
||||
|
||||
## Der Host (ein Bot) verschwindet: der einzige Mitspieler übernimmt und spielt zu Ende.
|
||||
func test_host_wechsel() -> void:
|
||||
_bot_quit(false)
|
||||
var pid := OS.create_process(OS.get_executable_path(), _bot_args(["--host", "--name=BotHost"]))
|
||||
check(pid > 0, "Bot-Host gestartet")
|
||||
await frames(30)
|
||||
await _join_bot_host(false)
|
||||
var w := 0
|
||||
while not (screen() is BattleScreen) and w < 900:
|
||||
await frames(1)
|
||||
w += 1
|
||||
var b = screen()
|
||||
check(b is BattleScreen and b.is_client(), "als Mitspieler beim Bot-Host im Kampf")
|
||||
w = 0
|
||||
while (b.game == null or main.net.backup.is_empty()) and w < 600:
|
||||
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")
|
||||
main.net.rejoin_seconds = 3.0
|
||||
_bot_quit(true)
|
||||
w = 0
|
||||
while not main.net.is_host and w < 900:
|
||||
await frames(1)
|
||||
w += 1
|
||||
check(main.net.is_host and b.game == main.net.game and screen() == b and b.game.log.any(func(l): return "übernimmt als Host" in l),
|
||||
"Host-Wechsel: Mitspieler führt das Spiel weiter")
|
||||
check(main.net.waiting.has("BotHost"), "Platz des alten Hosts bleibt zunächst frei")
|
||||
await shot("34_host_wechsel")
|
||||
# Der alte Host kehrt nicht zurück: ein Zug wird übersprungen, nach der Frist scheidet er aus
|
||||
b.ai_delay = 0.0
|
||||
if b.game.current() == b.perspective:
|
||||
await click(b.b_end)
|
||||
w = 0
|
||||
while b.game.winner == "" and w < 900:
|
||||
await frames(1)
|
||||
w += 1
|
||||
check(b.game.log.any(func(l): return "übersprungen" in l), "Zug des fehlenden Hosts übersprungen")
|
||||
check(b.game.winner != "" and b.game.winner_player == b.perspective and b.game.log.any(func(l): return "kehrt nicht zurück" in l),
|
||||
"nach der Frist scheidet der alte Host aus, Partie gewonnen (Sieger '%s', Zug %d)" % [b.game.winner, b.game.turn])
|
||||
await leave_battle()
|
||||
_bot_quit(false)
|
||||
check(screen() is MenuScreen and not main.net.connected, "nach dem Host-Wechsel zurück im Menü")
|
||||
|
||||
|
||||
## Zuschauer: sieht die Partie zweier Bots ohne Hände und ohne eigene Züge.
|
||||
func test_zuschauer() -> void:
|
||||
_bot_quit(false)
|
||||
var pid_h := OS.create_process(OS.get_executable_path(), _bot_args(["--host", "--name=BotHost"]))
|
||||
await frames(30)
|
||||
var pid_c := OS.create_process(OS.get_executable_path(), _bot_args(["--ip=127.0.0.1", "--name=Bot"]))
|
||||
check(pid_h > 0 and pid_c > 0, "zwei Bots gestartet")
|
||||
await _join_bot_host(true)
|
||||
var w := 0
|
||||
while not (screen() is BattleScreen) and w < 1200:
|
||||
await frames(1)
|
||||
w += 1
|
||||
var b = screen()
|
||||
check(b is BattleScreen and b.spectating(), "als Zuschauer im Kampf")
|
||||
w = 0
|
||||
while (b.game == null or b.game.turn < 2) and w < 900:
|
||||
await frames(1)
|
||||
w += 1
|
||||
check(b.game != null and b.game.turn >= 2 and b.hand_views.is_empty() and not b.b_end.visible,
|
||||
"Zuschauer sieht die Partie ohne Hände und ohne Züge (Zug %d)" % (b.game.turn if b.game != null else 0))
|
||||
await shot("35_zuschauer")
|
||||
check(not b.b_end.visible and not b.b_undo.visible, "Zuschauer hat keine Zugknöpfe")
|
||||
if screen() is BattleScreen: # die Bots spielen schnell, die Partie kann schon vorbei sein
|
||||
await leave_battle()
|
||||
else:
|
||||
var w2 := 0
|
||||
while not (screen() is MenuScreen) and w2 < 300:
|
||||
await frames(1)
|
||||
w2 += 1
|
||||
check(screen() is MenuScreen and not main.net.connected, "Zuschauer zurück im Menü")
|
||||
_bot_quit(true)
|
||||
await frames(60)
|
||||
_bot_quit(false)
|
||||
|
||||
|
||||
func test_kampagne() -> void:
|
||||
await click(find_button("Kampagne"))
|
||||
var scr = screen()
|
||||
@@ -978,7 +851,7 @@ func test_kampf(shot_turn: int) -> void:
|
||||
scr.smart_ai = false
|
||||
g.opponent.life = 1
|
||||
for i in 3:
|
||||
var drache := Game.Creature.new(Cards.by_id("f_drache").copy(), g.human, 9100 + i)
|
||||
var drache := Game.Creature.new(Cards.by_id("f_5_5").copy(), g.human, 9100 + i)
|
||||
drache.ready = true
|
||||
g.human.board.append(drache)
|
||||
scr.refresh()
|
||||
@@ -1015,13 +888,13 @@ func test_kampf_abbrechen() -> void:
|
||||
check(g.opponent != null and g.human.max_energy == Game.START_MANA, "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)
|
||||
var wolf := Game.Creature.new(Cards.by_id("f_3_2"), 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("Super Rare")
|
||||
g.human.hand.append(Cards.by_id("w_3_3"))
|
||||
g.human.looks["w_3_3"] = Cards.RARITIES.find("Super Rare")
|
||||
scr.refresh()
|
||||
var reiter_view = scr.hand_views.filter(func(v): return v.card.id == "w_reiter")[0]
|
||||
var reiter_view = scr.hand_views.filter(func(v): return v.card.id == "w_3_3")[0]
|
||||
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)
|
||||
@@ -1037,13 +910,14 @@ 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(scr.b_log)
|
||||
await key(KEY_ESCAPE)
|
||||
await click(find_button("Verlauf", scr.options))
|
||||
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)
|
||||
await key(KEY_SPACE)
|
||||
var wait := 0
|
||||
while g.current() != g.human and g.winner == "" and wait < 600:
|
||||
await frames(1)
|
||||
|
||||
Reference in New Issue
Block a user