Randoloot/tests/save_settings_test.gd
2026-10-27 22:20:01 +01:00

150 lines
7.9 KiB
GDScript

extends Node
var failures: Array[String] = []
func check(condition: bool, message: String) -> void:
if not condition:
failures.append(message)
push_error(message)
func _ready() -> void:
run.call_deferred()
func run() -> void:
GameState.start_new_run()
var game: Node = load("res://scenes/game/game.tscn").instantiate()
get_tree().root.add_child(game)
get_tree().current_scene = game
await get_tree().process_frame
await get_tree().process_frame
game.set_physics_process(false)
var ship: Node = game.get_node("PlayerShip")
ship.set_physics_process(false)
var inventory: Node = ship.get_node("ShipInventory")
GameState.captain.credits = 4321
GameState.captain.minerals.iron = 321
GameState.captain.expedition.alien_skills["general"] = true
GameState.captain.expedition.crew["cabin_1"] = {"name":"Test","level":4}
GameState.captain.skill_offers.assign(["electrician", "lucky"])
GameState.captain.skill_cooldowns["electrician"] = 12.0
inventory.ammunition = 123
inventory.unlocked_weapon_slots = 2
inventory.weapon_slots[0].damage_min = 13.5
inventory.weapon_slots[0].damage_max = 29.25
inventory.weapon_slots[0].special_orbs.assign(["piercing"])
ship.position = Vector2(840,610)
ship.hull = 37.0
GameState.cleared_systems[GameState.system_level] = true
GameState.cleared_systems[2] = true
GameState.explored_pois["%d:1" % GameState.system_seed] = true
GameState.captain.skill_durations["reactor_overload"] = 4.0
inventory.extension_cooldowns["test"] = 5.0
GameState.captain.expedition.laboratory_project = "machine_gun"
GameState.captain.expedition.laboratory_duration = 600
GameState.captain.expedition.laboratory_remaining = 387.5
GameState.captain.expedition.alien_project = "hull"
GameState.captain.expedition.alien_duration = 600
GameState.captain.expedition.alien_remaining = 245.25
var enemies_before: int = game.get_remaining_enemies()
check(GameState.save_game() == OK,"save writes successfully")
check(GameState.has_save(),"continue detects valid save")
var saved := GameState.SAVES.read_save()
check(saved.captain.credits == 4321,"captain credits serialized")
check(saved.inventory.ammunition == 123,"ammunition serialized")
GameState.captain.credits = 8765
check(GameState.save_game() == OK,"second save replaces first")
check(GameState.SAVES.read_save(GameState.SAVES.BACKUP).captain.credits == 4321,"backup retains previous save")
game.free()
check(GameState.continue_game(),"save restores state")
check(GameState.captain.credits == 8765,"latest credits restored")
check(GameState.captain.expedition.laboratory_project == "machine_gun" and is_equal_approx(GameState.captain.expedition.laboratory_remaining,387.5),"SAVE preserves laboratory project and exact remaining time")
check(GameState.captain.expedition.alien_project == "hull" and is_equal_approx(GameState.captain.expedition.alien_remaining,245.25),"SAVE preserves alien project and exact remaining time")
check(GameState.captain.expedition.crew.cabin_1.level == 4,"crew restored")
check(GameState.captain.expedition.alien_skills.general,"research restored")
check(not GameState.cleared_systems.has(GameState.system_level),"current level encounters restart")
check(GameState.cleared_systems.has(2),"other level progress retained")
check(not GameState.explored_pois.has("%d:1" % GameState.system_seed),"current level exploration restarts")
game = load("res://scenes/game/game.tscn").instantiate()
get_tree().root.add_child(game)
get_tree().current_scene = game
await get_tree().process_frame
await get_tree().process_frame
ship = game.get_node("PlayerShip")
inventory = ship.get_node("ShipInventory")
check(inventory.ammunition == 123,"inventory restored into live ship")
check(inventory.unlocked_weapon_slots == 2,"unlocked slots restored")
check(is_equal_approx(inventory.weapon_slots[0].damage_min,13.5),"individual item damage restored")
check(is_equal_approx(inventory.weapon_slots[0].damage_max,29.25),"individual item roll restored")
check("piercing" in inventory.weapon_slots[0].special_orbs,"item orb effects restored")
check(ship.position.distance_to(game.system_data.player_start) < 1,"load starts at level spawn")
check(is_equal_approx(ship.hull,ship.max_hull),"load restores full hull")
check(game.get_remaining_enemies() == enemies_before,"enemy population restored")
check(GameState.captain.skill_cooldowns.is_empty(),"load clears skill cooldowns")
check(GameState.captain.skill_durations.is_empty(),"load clears temporary effects")
check(inventory.extension_cooldowns.is_empty(),"load clears extension cooldowns")
# Surface-save normalization must leave the active live expedition untouched.
var progress: Resource = GameState.captain.expedition
progress.active = true
progress.cargo = {"iron":7}
progress.cargo_credits = 23
check(GameState.save_game() == OK,"surface save succeeds")
check(progress.active and progress.cargo.get("iron",0) == 7,"surface save does not alter live cargo")
saved = GameState.SAVES.read_save()
check(not saved.captain.expedition.active,"surface save resumes in orbit")
check(saved.captain.minerals.iron == 328,"surface cargo retained")
check(GameState.captain.minerals.iron == 321,"snapshot does not modify live captain")
progress.active = false
for code in GameSettings.LOCALES:
GameSettings.locale = code
GameSettings.apply()
check(not str(TranslationServer.translate("Zapisz grę")).is_empty(),"language available: "+code)
if code != "pl":
check(str(TranslationServer.translate("Zapisz grę")) != "Zapisz grę","menu is translated: "+code)
check(str(TranslationServer.translate("Kredyty: 123")) != "Kredyty: 123","formatted strings translated: "+code)
else:
check(str(TranslationServer.translate("Zapisz grę")) == "Zapisz grę","Polish menu stays Polish")
check(str(TranslationServer.translate("Kredyty: 123")) == "Kredyty: 123","Polish formatted text stays Polish")
GameSettings.locale = "en"
GameSettings.apply()
check(str(TranslationServer.translate("Kredyty: 123")) == "Credits: 123","formatted numeric value retained")
check(str(TranslationServer.translate("Działko plazmowe · poziom 3")) == "Plasma cannon · level 3","formatted phrase and captured item name translated")
GameSettings.volume = 0.0
GameSettings.apply()
check(AudioServer.is_bus_mute(0),"master mute applied")
check(GameSettings.save_settings() == OK,"settings saved")
var config := ConfigFile.new()
check(config.load(GameSettings.PATH) == OK,"settings readable from disk")
check(config.get_value("language","locale") == "en","language persisted")
check(is_zero_approx(config.get_value("audio","master")),"volume persisted")
var options := CanvasLayer.new()
options.set_script(load("res://scripts/ui/options_dialog.gd"))
get_tree().root.add_child(options)
check(options.get("_language").item_count == 5,"all five languages in options")
options._close()
await get_tree().process_frame
var pause_menu := game.get_node("SessionMenu")
var escape := InputEventKey.new()
escape.keycode = KEY_ESCAPE
escape.pressed = true
Input.parse_input_event(escape)
await get_tree().process_frame
check(get_tree().paused,"session menu pauses game")
check(pause_menu._save(),"manual save button works")
pause_menu._open_options()
check(not pause_menu._dialog.visible,"options temporarily hides pause dialog")
pause_menu._options._close()
await get_tree().process_frame
check(pause_menu._dialog.visible and get_tree().paused,"closing options restores paused menu")
pause_menu._close()
check(not get_tree().paused,"session menu resumes game")
# Damaged primary file must fall back to the last verified backup.
var file := FileAccess.open(GameState.SAVES.PATH,FileAccess.WRITE)
file.store_string("corrupted test save")
file.close()
check(GameState.has_save(),"backup offered when primary corrupted")
check(GameState.continue_game(),"corrupted primary falls back to backup")
game.queue_free()
await get_tree().process_frame
print("SAVE_SETTINGS_TEST: ", "PASS" if failures.is_empty() else "FAIL", " (",failures.size()," failures)")
get_tree().quit(0 if failures.is_empty() else 1)