Initial Void Bastion 0.20 prototype
This commit is contained in:
commit
7362ca0f02
251 files changed
+29688
No files matched your search
@@ -0,0 +1,101 @@
|
||||
extends SceneTree
|
||||
|
||||
var matched_a = null
|
||||
var matched_b = null
|
||||
var snapshot_a: Dictionary = {}
|
||||
var snapshot_b: Dictionary = {}
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var nakama = root.get_node("Nakama")
|
||||
var test_host := OS.get_environment("VOID_BASTION_TEST_HOST")
|
||||
var test_scheme := OS.get_environment("VOID_BASTION_TEST_SCHEME")
|
||||
var test_port_text := OS.get_environment("VOID_BASTION_TEST_PORT")
|
||||
if test_host.is_empty():
|
||||
test_host = "192.168.178.20"
|
||||
if test_scheme.is_empty():
|
||||
test_scheme = "http"
|
||||
var test_port := int(test_port_text) if not test_port_text.is_empty() else 7350
|
||||
var client_a: NakamaClient = nakama.create_client("voidbastion-public-v1", test_host, test_port, test_scheme, 5, NakamaLogger.LOG_LEVEL.ERROR)
|
||||
var client_b: NakamaClient = nakama.create_client("voidbastion-public-v1", test_host, test_port, test_scheme, 5, NakamaLogger.LOG_LEVEL.ERROR)
|
||||
var suffix := str(Time.get_ticks_usec())
|
||||
var session_a: NakamaSession = await client_a.authenticate_device_async("voidbastion_godot_test_a_" + suffix, "godot_a_" + suffix, true)
|
||||
var session_b: NakamaSession = await client_b.authenticate_device_async("voidbastion_godot_test_b_" + suffix, "godot_b_" + suffix, true)
|
||||
assert(not session_a.is_exception() and session_a.is_valid(), "Godot-Client A muss ein Gastkonto anmelden können: %s" % session_a)
|
||||
assert(not session_b.is_exception() and session_b.is_valid(), "Godot-Client B muss ein Gastkonto anmelden können: %s" % session_b)
|
||||
|
||||
var info = await client_a.rpc_async(session_a, "server_info", "{}")
|
||||
assert(not info.is_exception(), "Die Godot-Anbindung muss Server-RPCs erreichen.")
|
||||
var info_data = JSON.parse_string(info.payload)
|
||||
assert(info_data is Dictionary and info_data.get("status") == "online", "Der Server muss seinen Onlinestatus melden.")
|
||||
assert(info_data.get("version") == "0.20", "Der Server muss die aktuelle Runtime 0.20 geladen haben: %s" % info_data)
|
||||
|
||||
var run_payload := JSON.stringify({"map": 1, "difficulty": 1, "mode": "mission", "wave": 4, "score": 4700, "client_version": "0.19-integration"})
|
||||
var submitted = await client_a.rpc_async(session_a, "submit_run", run_payload)
|
||||
assert(not submitted.is_exception(), "Ein plausibles Ergebnis muss serverseitig angenommen werden.")
|
||||
var records = await client_a.list_leaderboard_records_async(session_a, "mission_map1_difficulty1", null, null, 10)
|
||||
assert(not records.is_exception() and records.records.size() >= 1, "Das Ergebnis muss über das Godot-SDK aus der Rangliste lesbar sein.")
|
||||
|
||||
var socket_a: NakamaSocket = nakama.create_socket_from(client_a)
|
||||
var socket_b: NakamaSocket = nakama.create_socket_from(client_b)
|
||||
assert(not (await socket_a.connect_async(session_a, true, 5)).is_exception(), "Spieler A benötigt eine Echtzeitverbindung.")
|
||||
assert(not (await socket_b.connect_async(session_b, true, 5)).is_exception(), "Spieler B benötigt eine Echtzeitverbindung.")
|
||||
socket_a.received_matchmaker_matched.connect(func(value): matched_a = value)
|
||||
socket_b.received_matchmaker_matched.connect(func(value): matched_b = value)
|
||||
socket_a.received_match_state.connect(func(value): _capture_snapshot(value, true))
|
||||
socket_b.received_match_state.connect(func(value): _capture_snapshot(value, false))
|
||||
var props := {"game": "void_bastion", "mode": "quick"}
|
||||
var production_query := "+properties.game:void_bastion +properties.mode:quick +properties.difficulty:1"
|
||||
var ticket_a = await socket_a.add_matchmaker_async(production_query, 2, 2, props, {"difficulty": 1.0})
|
||||
var ticket_b = await socket_b.add_matchmaker_async(production_query, 2, 2, props, {"difficulty": 1.0})
|
||||
assert(not ticket_a.is_exception() and not ticket_b.is_exception(), "Beide Spieler müssen die Mitspielersuche starten können.")
|
||||
for attempt in 300:
|
||||
if matched_a != null and matched_b != null:
|
||||
break
|
||||
await create_timer(0.05).timeout
|
||||
assert(matched_a != null and matched_b != null, "Der Server muss zwei passende Spieler vermitteln.")
|
||||
var match_a = await socket_a.join_matched_async(matched_a)
|
||||
var match_b = await socket_b.join_matched_async(matched_b)
|
||||
assert(not match_a.is_exception() and not match_b.is_exception(), "Beide Spieler müssen der autoritativen Koop-Partie beitreten können.")
|
||||
assert(match_a.match_id == match_b.match_id, "Beide Spieler müssen dieselbe Koop-Partie erhalten.")
|
||||
for attempt in 100:
|
||||
if bool(snapshot_a.get("started", false)) and bool(snapshot_b.get("started", false)):
|
||||
break
|
||||
await create_timer(0.05).timeout
|
||||
assert(bool(snapshot_a.get("started", false)) and bool(snapshot_b.get("started", false)), "Beide Clients müssen den gestarteten Serverzustand empfangen.")
|
||||
var slot_a := int((snapshot_a.get("slots", {}) as Dictionary).get(session_a.user_id, -1))
|
||||
var slot_b := int((snapshot_b.get("slots", {}) as Dictionary).get(session_b.user_id, -1))
|
||||
assert(slot_a != slot_b and slot_a in [0, 1] and slot_b in [0, 1], "Der Server muss beiden Spielern unterschiedliche Seiten zuweisen.")
|
||||
var build_a := {"tower_type": 0, "x": 200.0 if slot_a == 0 else 2760.0, "y": 200.0}
|
||||
var build_b := {"tower_type": 1, "x": 200.0 if slot_b == 0 else 2760.0, "y": 200.0}
|
||||
await socket_a.send_match_state_async(match_a.match_id, 10, JSON.stringify(build_a))
|
||||
await socket_b.send_match_state_async(match_b.match_id, 10, JSON.stringify(build_b))
|
||||
await socket_a.send_match_state_async(match_a.match_id, 13, "{}")
|
||||
for attempt in 120:
|
||||
if int(snapshot_a.get("wave", 0)) == 1 and (snapshot_a.get("towers", []) as Array).size() == 2 and int(snapshot_b.get("wave", 0)) == 1:
|
||||
break
|
||||
await create_timer(0.05).timeout
|
||||
assert(int(snapshot_a.get("wave", 0)) == 1, "Der Server muss die gemeinsame Welle starten.")
|
||||
assert((snapshot_a.get("towers", []) as Array).size() == 2, "Beide autorisierten Bauaufträge müssen serverseitig sichtbar sein.")
|
||||
assert(JSON.stringify(snapshot_a.get("towers", [])) == JSON.stringify(snapshot_b.get("towers", [])), "Beide Clients müssen denselben Turmzustand erhalten.")
|
||||
assert(int(snapshot_a.get("reactor_lives", 0)) == int(snapshot_b.get("reactor_lives", -1)), "Beide Clients müssen dieselben gemeinsamen Reaktorleben sehen.")
|
||||
socket_a.close()
|
||||
socket_b.close()
|
||||
print("VOID_BASTION_ONLINE_INTEGRATION_OK")
|
||||
quit()
|
||||
|
||||
|
||||
func _capture_snapshot(message, client_a: bool) -> void:
|
||||
if int(message.op_code) != 1:
|
||||
return
|
||||
var parsed = JSON.parse_string(message.data)
|
||||
if not parsed is Dictionary:
|
||||
return
|
||||
if client_a:
|
||||
snapshot_a = parsed
|
||||
else:
|
||||
snapshot_b = parsed
|
||||
@@ -0,0 +1 @@
|
||||
uid://crlx6df50d631
|
||||
@@ -0,0 +1,199 @@
|
||||
extends SceneTree
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _path_length(points: PackedVector2Array) -> float:
|
||||
var total := 0.0
|
||||
for index in points.size() - 1:
|
||||
total += points[index].distance_to(points[index + 1])
|
||||
return total
|
||||
|
||||
|
||||
func _assert_grid_aligned_path(points: PackedVector2Array, map_name: String) -> void:
|
||||
for point in points:
|
||||
assert(is_zero_approx(fposmod(point.x - 40.0, 80.0)), "%s: Jeder Wegpunkt muss horizontal im Zentrum einer Rasterzelle liegen." % map_name)
|
||||
assert(is_zero_approx(fposmod(point.y - 40.0, 80.0)), "%s: Jeder Wegpunkt muss vertikal im Zentrum einer Rasterzelle liegen." % map_name)
|
||||
for index in points.size() - 1:
|
||||
var delta := points[index + 1] - points[index]
|
||||
assert(is_zero_approx(delta.x) or is_zero_approx(delta.y), "%s: Jede Straßenstrecke muss waagerecht oder senkrecht durchs Raster verlaufen." % map_name)
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
var packed_scene: PackedScene = load("res://main.tscn")
|
||||
var game := packed_scene.instantiate()
|
||||
root.add_child(game)
|
||||
await process_frame
|
||||
var ads_manager = game.get_node("AdsManager")
|
||||
var account_manager = game.get_node("AccountManager")
|
||||
var multiplayer_manager = game.get_node("MultiplayerManager")
|
||||
multiplayer_manager.reset_coop_session(500, 30)
|
||||
assert(multiplayer_manager.player_credits == [500, 500], "Beide Koop-Spieler benötigen getrennte, gleich hohe Startressourcen.")
|
||||
assert(multiplayer_manager.spend_player_credits(0, 120), "Spieler 1 muss aus seinem eigenen Guthaben bauen können.")
|
||||
assert(multiplayer_manager.player_credits[0] == 380 and multiplayer_manager.player_credits[1] == 500, "Ausgaben eines Spielers dürfen das Guthaben des Mitspielers nicht verändern.")
|
||||
multiplayer_manager.damage_shared_reactor(4)
|
||||
assert(multiplayer_manager.shared_reactor_lives == 26, "Beide Seiten müssen dasselbe Reaktorleben verwenden.")
|
||||
var left_path := PackedVector2Array([Vector2(200, 300), Vector2(700, 400)])
|
||||
var right_path: PackedVector2Array = multiplayer_manager.mirrored_path(left_path)
|
||||
assert(right_path[0] == Vector2(1400, 300) and right_path[1] == Vector2(900, 400), "Die zweite Koop-Kartenhälfte muss exakt gespiegelt werden.")
|
||||
multiplayer_manager.begin_search("quick")
|
||||
assert(multiplayer_manager.matchmaking_state == "waiting_for_service", "Die Spielersuche benötigt einen sichtbaren vorbereiteten Zustand.")
|
||||
multiplayer_manager.cancel_search()
|
||||
assert(multiplayer_manager.matchmaking_state == "idle", "Eine laufende Spielersuche muss abgebrochen werden können.")
|
||||
game.call("_handle_pointer", Vector2(1100, 730), false)
|
||||
assert(game.get("game_mode") == "multiplayer_menu", "Der Multiplayerbereich muss direkt aus dem Hauptmenü erreichbar sein.")
|
||||
game.call("_handle_pointer", Vector2(400, 695), false)
|
||||
assert(game.get("game_mode") == "matchmaking", "Schnelles Spiel muss in den vorbereiteten Suchzustand wechseln.")
|
||||
game.call("_handle_pointer", Vector2(800, 680), false)
|
||||
assert(game.get("game_mode") == "multiplayer_menu", "Die Spielersuche muss über die sichtbare Schaltfläche abgebrochen werden können.")
|
||||
game.call("_handle_pointer", Vector2(140, 60), false)
|
||||
assert(game.get("game_mode") == "menu", "Der Multiplayerbereich benötigt einen funktionierenden Zurück-Button.")
|
||||
game.call("_handle_pointer", Vector2(1100, 730), false)
|
||||
game.call("_handle_pointer", Vector2(1300, 695), false)
|
||||
assert(game.get("game_mode") == "playing" and game.get("coop_mode") and game.get("coop_ai_mode"), "Der KI-Koop-Modus muss direkt spielbar gestartet werden können.")
|
||||
assert(game.get("coop_paths").size() == 2, "Die Koop-Arena benötigt zwei gespiegelte Gegnerwege.")
|
||||
assert(game.get("coop_paths")[0][0].x == -40.0 and game.get("coop_paths")[1][0].x == 3000.0, "Die Gegner müssen von beiden äußeren Rändern des verdoppelten Spielfelds angreifen.")
|
||||
assert(game.get("coop_paths")[0][-1] == game.get("coop_paths")[1][-1], "Beide gespiegelten Wege müssen am gemeinsamen Reaktor zusammentreffen.")
|
||||
assert(game.get("towers").size() >= 2, "Der Computergegner muss selbstständig eine Startverteidigung bauen.")
|
||||
for ai_tower in game.get("towers"):
|
||||
assert(int(ai_tower.get("owner", -1)) == 1 and ai_tower.pos.x > 1480.0, "KI-Türme dürfen nur auf der vollständigen gespiegelten KI-Seite stehen.")
|
||||
assert(multiplayer_manager.player_credits[0] > multiplayer_manager.player_credits[1], "Die KI muss ihre Baukosten ausschließlich aus dem eigenen Guthaben bezahlen.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(440, 200), 0), "Der Spieler benötigt gültige Bauplätze auf der linken Kartenhälfte.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(1240, 200), 0), "Die eigene Koop-Seite muss nahezu die volle Breite einer Einzelspielerkarte bieten.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(2040, 200), 0), "Der Spieler darf die gespiegelte Kartenhälfte der KI nicht bebauen.")
|
||||
game.call("_toggle_coop_side")
|
||||
assert(game.get("camera_offset").x == 1360.0, "Die Ansicht muss ohne Verkleinerung auf die vollständige KI-Seite umschalten können.")
|
||||
game.call("_toggle_coop_side")
|
||||
assert(game.get("camera_offset") == Vector2.ZERO, "Die Ansicht muss wieder zur eigenen vollständigen Kartenhälfte zurückkehren.")
|
||||
game.call("_start_wave")
|
||||
assert(game.get("enemies_to_spawn") == 18, "Im Koop-Modus muss die erste Welle beide Gegnerseiten versorgen.")
|
||||
game.call("_spawn_enemy")
|
||||
game.call("_spawn_enemy")
|
||||
assert(game.get("enemies")[-2].route_id != game.get("enemies")[-1].route_id, "Gegner müssen abwechselnd auf beiden Koop-Routen erscheinen.")
|
||||
for coop_tick in 100:
|
||||
game.call("_process", 0.1)
|
||||
assert(game.get("next_enemy_id") > 3, "Beide Koop-Routen müssen auch während der laufenden Simulation Gegner erzeugen.")
|
||||
game.call("_return_to_menu")
|
||||
assert(not account_manager.player_id.is_empty(), "Beim ersten Start muss automatisch ein stabiles Gastprofil angelegt werden.")
|
||||
assert(not account_manager.display_name.is_empty(), "Das Gastprofil benötigt einen sichtbaren Pilotennamen.")
|
||||
account_manager.storage_enabled = false
|
||||
var previous_test_score: int = account_manager.get_map_best_score(0, false)
|
||||
assert(account_manager.record_run(0, 0, false, 3, previous_test_score + 1000), "Ein neuer lokaler Highscore muss gespeichert werden.")
|
||||
assert(account_manager.get_map_best_wave(0, false) >= 3, "Die Profilansicht muss den gespeicherten Missionsbestwert lesen können.")
|
||||
assert(not ads_manager.mobile_ads_supported, "Auf Windows müssen Anzeigen vollständig deaktiviert bleiben.")
|
||||
ads_manager.missions_since_interstitial = 0
|
||||
ads_manager.last_interstitial_time = 0
|
||||
assert(not ads_manager.is_interstitial_due(1000), "Vor drei Missionen darf keine Vollbildwerbung fällig sein.")
|
||||
ads_manager.missions_since_interstitial = 3
|
||||
assert(ads_manager.is_interstitial_due(1000), "Frühestens nach drei Missionen darf eine Vollbildwerbung fällig sein.")
|
||||
ads_manager.last_interstitial_time = 800
|
||||
assert(not ads_manager.is_interstitial_due(1000), "Die 15-Minuten-Sperre zwischen Vollbildanzeigen muss greifen.")
|
||||
ads_manager.last_interstitial_time = 0
|
||||
assert(game.get("path_points").size() == 8, "Bastion Prime sollte aus sieben klaren Rasterstrecken bestehen.")
|
||||
_assert_grid_aligned_path(game.get("path_points"), "Bastion Prime")
|
||||
assert(_path_length(game.get("path_points")) >= 4000.0, "Die Bastion-Route sollte den sichtbaren Bauraum ausnutzen.")
|
||||
assert(game.get("camera_offset") == Vector2.ZERO, "Die ganze Karte muss ohne Verschieben sichtbar bleiben.")
|
||||
assert(game.get("path_cells").size() > 45, "Straße, Gegnerlinie und Bausperre müssen dieselbe lange Rasterzellenkette verwenden.")
|
||||
assert(game.call("_is_path_cell", Vector2(600, 120)), "Eine Zelle auf der Route muss als Wegzelle erkannt werden.")
|
||||
assert(not game.call("_is_path_cell", Vector2(600, 200)), "Die benachbarte Rasterreihe darf nicht zur ein Feld hohen Route gehören.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(600, 120)), "Auf einer Wegzelle darf kein Turm gebaut werden.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(600, 200)), "Direkt neben der ein Feld hohen Route muss gebaut werden können.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(600, 280)), "Zwischen den beiden oberen Wegschleifen von Bastion Prime muss gebaut werden können.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(200, 280)), "Der linke Plattformbereich von Bastion Prime soll als Baufläche nutzbar sein.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(200, 680)), "Außerhalb der Wegzellen soll jede geeignete Plattformzelle bebaubar sein.")
|
||||
game.call("_select_map", 1)
|
||||
assert(game.get("selected_map") == 1, "Die zweite Karte sollte auswählbar sein.")
|
||||
assert(game.get("path_points").size() == 8, "Die Helios-Werft sollte aus sieben klaren Rasterstrecken bestehen.")
|
||||
_assert_grid_aligned_path(game.get("path_points"), "Helios-Werft")
|
||||
assert(_path_length(game.get("path_points")) >= 4400.0, "Die Werft-Route sollte mehrere Feuerlinien bilden.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(600, 280)), "Auf der oberen Helios-Fahrbahn darf nicht gebaut werden.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(600, 360)), "Auf Helios muss zwischen der oberen und mittleren Fahrbahn gebaut werden können.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(600, 440)), "Auf der mittleren Helios-Fahrbahn darf nicht gebaut werden.")
|
||||
assert(game.call("_is_valid_build_position", Vector2(600, 520)), "Auf Helios muss auch die innere Plattform zwischen den unteren Fahrbahnen nutzbar sein.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(600, 680)), "Auf der unteren Helios-Fahrbahn darf nicht gebaut werden.")
|
||||
assert(game.call("_is_in_reactor_zone", Vector2(1480, 440)), "Der Helios-Reaktor muss als Sperrzone erkannt werden.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(1480, 440)), "Auf dem Helios-Reaktor darf nicht gebaut werden.")
|
||||
game.call("_select_map", 0)
|
||||
assert(game.call("_is_in_reactor_zone", Vector2(1480, 440)), "Der Bastion-Reaktor muss als Sperrzone erkannt werden.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(1480, 440)), "Auf dem Bastion-Reaktor darf nicht gebaut werden.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(40, 680)), "Im freien Weltraum darf kein Bauplatz liegen.")
|
||||
game.call("_select_map", 2)
|
||||
assert(game.get("path_points").size() == 6, "Pyrax sollte aus fünf klaren Rasterstrecken bestehen.")
|
||||
_assert_grid_aligned_path(game.get("path_points"), "Pyrax-Raffinerie")
|
||||
assert(_path_length(game.get("path_points")) >= 2480.0, "Die Pyrax-Route sollte den Bauraum weiträumig erschließen.")
|
||||
assert(game.call("_is_in_reactor_zone", Vector2(1440, 360)), "Der Pyrax-Hauptreaktor muss als Sperrzone erkannt werden.")
|
||||
assert(game.call("_is_in_reactor_zone", Vector2(680, 360)), "Auch die offene Pyrax-Reaktorkammer muss gesperrt sein.")
|
||||
assert(not game.call("_is_valid_build_position", Vector2(1440, 360)), "Auf dem Pyrax-Reaktor darf nicht gebaut werden.")
|
||||
game.call("_select_map", 0)
|
||||
assert(is_equal_approx(game.call("_tower_range", 0, 1), 190.0), "Die Bauvorschau muss die echte Turmreichweite verwenden.")
|
||||
assert(game.call("_tower_damage", 0, 2) > game.call("_tower_damage", 0, 1), "Ein Upgrade muss den Schaden sichtbar erhöhen.")
|
||||
assert(game.call("_tower_shots_per_second", 0, 2) > game.call("_tower_shots_per_second", 0, 1), "Ein Upgrade muss die Feuerrate sichtbar erhöhen.")
|
||||
assert(is_equal_approx(game.call("_tower_aim_angle", 0, Vector2.ZERO, Vector2.RIGHT), PI), "Der Kinetik-Sprite muss für sein links liegendes Geschütz um 180 Grad korrigiert werden.")
|
||||
assert(is_equal_approx(game.call("_tower_aim_angle", 1, Vector2.ZERO, Vector2.RIGHT), 0.0), "Andere Türme dürfen durch die Kinetik-Korrektur nicht gedreht werden.")
|
||||
game.set("selected_difficulty", 0)
|
||||
game.call("_start_game")
|
||||
assert(game.get("credits") == 450 and game.get("reactor_lives") == 20, "Leicht muss dem bisherigen Balancing entsprechen.")
|
||||
game.call("_handle_pointer", Vector2(1110, 45), false)
|
||||
assert(game.get("game_mode") == "menu", "Der Zurück-Button muss eine laufende Runde abbrechen und zum Kartenmenü führen.")
|
||||
assert(game.get("towers").is_empty() and game.get("enemies").is_empty(), "Beim Abbruch müssen alle Einheiten der laufenden Runde entfernt werden.")
|
||||
game.call("_start_game")
|
||||
game.set("selected_tower_type", 0)
|
||||
var build_position := Vector2.ZERO
|
||||
for x in range(120, 1480, 80):
|
||||
for y in range(120, 740, 80):
|
||||
var candidate: Vector2 = game.call("_snap_to_grid", Vector2(x, y))
|
||||
if game.call("_is_valid_build_position", candidate):
|
||||
build_position = candidate
|
||||
break
|
||||
if build_position != Vector2.ZERO:
|
||||
break
|
||||
assert(build_position != Vector2.ZERO, "Auf der vollständig sichtbaren Karte muss es sinnvolle Bauplätze geben.")
|
||||
game.call("_handle_pointer", build_position, true)
|
||||
assert(game.get("towers").is_empty(), "Der erste Fingertipp soll nur die Reichweite zur Kontrolle anzeigen.")
|
||||
game.call("_handle_pointer", build_position, true)
|
||||
assert(game.get("towers").size() == 1, "Ein Turm sollte platziert worden sein.")
|
||||
assert(game.get("towers")[0].pos == build_position, "Der Turm sollte im Bauraster einrasten.")
|
||||
assert(game.get("credits") == 330, "Die Turmkosten müssen abgezogen werden.")
|
||||
game.call("_start_wave")
|
||||
assert(game.get("enemies_to_spawn") == 9, "Leicht muss die bisherige Gegnerzahl der ersten Welle beibehalten.")
|
||||
for index in 100:
|
||||
game.call("_process", 0.1)
|
||||
assert(game.get("wave") == 1, "Die erste Welle sollte gestartet sein.")
|
||||
assert(game.get("next_enemy_id") > 1, "Die Welle sollte Gegner erzeugen.")
|
||||
game.set("selected_difficulty", 1)
|
||||
game.call("_start_game")
|
||||
game.call("_start_wave")
|
||||
assert(game.get("enemies_to_spawn") > 9, "Mittel muss mehr Gegner als Leicht erzeugen.")
|
||||
game.set("selected_difficulty", 2)
|
||||
game.call("_start_game")
|
||||
game.set("wave", 1)
|
||||
game.set("enemies_to_spawn", 2)
|
||||
game.call("_spawn_enemy")
|
||||
assert(game.get("enemies")[-1].max_hp > 100.0, "Schwer muss die Gegnerhülle deutlich verstärken.")
|
||||
game.set("selected_difficulty", 0)
|
||||
game.call("_start_game")
|
||||
game.set("wave", 4)
|
||||
game.call("_start_wave")
|
||||
game.set("enemies_to_spawn", 1)
|
||||
game.call("_spawn_enemy")
|
||||
var boss: Dictionary = game.get("enemies")[-1]
|
||||
assert(boss.boss, "Jede fünfte Welle sollte ein Kommandoschiff erzeugen.")
|
||||
assert(boss.type == 2, "Das Kommandoschiff sollte die schwere Schiffsklasse verwenden.")
|
||||
game.call("_start_game")
|
||||
var crystals_before: int = game.get("core_crystals")
|
||||
game.set("wave", 15)
|
||||
game.set("wave_active", true)
|
||||
game.set("enemies_to_spawn", 0)
|
||||
game.get("enemies").clear()
|
||||
game.call("_update_wave", 0.1)
|
||||
assert(game.get("game_mode") == "endless_offer", "Nach Welle 15 muss die Wahl zum Endlosmodus erscheinen.")
|
||||
assert(game.get("core_crystals") == crystals_before + 5, "Eine abgeschlossene Karte muss Kernkristalle vergeben.")
|
||||
game.call("_start_endless_mode")
|
||||
assert(game.get("endless_mode") and game.get("game_mode") == "playing", "Der Endlosmodus muss freiwillig startbar sein.")
|
||||
game.call("_game_over")
|
||||
assert(game.get("game_mode") == "endless_result", "Das Ende des Endlosmodus darf nicht als gescheiterte Mission erscheinen.")
|
||||
assert(game.get("core_crystals") == crystals_before + 5, "Der Kartenbonus darf pro Mission nur einmal vergeben werden.")
|
||||
print("VOID_BASTION_SMOKE_TEST_OK")
|
||||
quit()
|
||||
@@ -0,0 +1 @@
|
||||
uid://d3434nraofcsi
|
||||
Reference in new issue
Block a user