Files
heros/tools/smoke_title_menu.gd

245 lines
14 KiB
GDScript

extends SceneTree
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
const SAVE_PATH := "user://campaign_save.json"
const MANUAL_SAVE_PATH := "user://campaign_manual_save.json"
const SETTINGS_PATH := "user://heros_settings.json"
func _init() -> void:
var save_backup := _backup_user_file(SAVE_PATH)
var manual_backup := _backup_user_file(MANUAL_SAVE_PATH)
var settings_backup := _backup_user_file(SETTINGS_PATH)
var failures: Array[String] = []
_clear_user_file(SAVE_PATH)
_clear_user_file(MANUAL_SAVE_PATH)
_clear_user_file(SETTINGS_PATH)
_check_title_menu_start_load_and_settings(failures)
if not _restore_user_file(SAVE_PATH, save_backup):
failures.append("could not restore campaign save after title menu smoke")
if not _restore_user_file(MANUAL_SAVE_PATH, manual_backup):
failures.append("could not restore manual campaign save after title menu smoke")
if not _restore_user_file(SETTINGS_PATH, settings_backup):
failures.append("could not restore title settings after title menu smoke")
if failures.is_empty():
print("title menu smoke ok")
quit(0)
return
for failure in failures:
push_error(failure)
quit(1)
func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
scene._ready()
if scene.title_screen_root == null or not scene.title_screen_root.visible:
failures.append("title screen should be visible on startup")
if scene.bgm_player == null or scene.bgm_player.stream == null or scene.current_bgm_key != "menu":
failures.append("title screen should start with an audible menu BGM stream")
elif scene.bgm_player.volume_db < -12.0:
failures.append("title menu BGM should be loud enough to hear by default: %.1fdb" % scene.bgm_player.volume_db)
if scene._sfx_stream_for("ui_click") == null or scene._sfx_stream_for("slash") == null or scene._sfx_stream_for("victory_sting") == null:
failures.append("core UI, combat, and result SFX streams should load")
if scene.title_background_texture_rect == null or scene.title_background_texture_rect.texture == null:
failures.append("title screen should show a high-resolution battlefield backdrop")
elif BattleSceneScript.TITLE_BACKGROUND_PATH == BattleSceneScript.DEFAULT_BATTLE_BACKGROUND_PATH:
failures.append("title screen should use a dedicated cinematic background, not the first battle map backdrop")
if scene.title_portrait_texture_rect == null or scene.title_portrait_texture_rect.texture == null:
failures.append("title screen should show Cao Cao portrait art")
if scene.title_panel == null or scene.title_panel.position.x > 100.0:
failures.append("title menu panel should sit on the left side of the cinematic title screen")
if scene.title_caption_label == null or scene.title_caption_label.text != "조조전":
failures.append("title screen should present the campaign title in Korean")
if scene.briefing_panel == null or scene.briefing_panel.visible:
failures.append("briefing should stay hidden behind the title screen")
if scene.title_start_button == null or scene.title_start_button.text != "처음부터 시작":
failures.append("title screen should expose Start from Beginning")
if scene.title_load_button == null or scene.title_load_button.text != "게임 로드":
failures.append("title screen should expose Game Load")
elif not scene.title_load_button.disabled:
failures.append("Game Load should be disabled when no save exists")
if scene.title_load_panel == null:
failures.append("title screen should create a load-slot selection panel")
elif scene.title_load_panel.visible:
failures.append("load-slot selection panel should stay hidden until Game Load is pressed")
if scene.title_settings_button == null or scene.title_settings_button.text != "환경 설정":
failures.append("title screen should expose Settings")
scene._on_title_settings_pressed()
if scene.title_settings_panel == null or not scene.title_settings_panel.visible:
failures.append("title settings panel should open from the title screen")
if scene.title_start_button != null and scene.title_start_button.visible:
failures.append("title settings should replace the main title commands while open")
if scene.title_bgm_toggle == null or scene.title_sfx_toggle == null or scene.title_edge_scroll_toggle == null:
failures.append("title settings should expose BGM, SFX, and edge-scroll toggles")
if scene.title_bgm_volume_slider == null or scene.title_sfx_volume_slider == null:
failures.append("title settings should expose BGM and SFX volume sliders")
scene._on_title_bgm_toggled(false)
if scene.title_bgm_enabled:
failures.append("BGM toggle should disable background music")
scene._on_title_bgm_volume_changed(45.0)
if scene.title_bgm_volume_percent != 45:
failures.append("BGM volume slider should update the saved BGM volume percent")
scene._on_title_sfx_toggled(false)
if scene.title_sfx_enabled:
failures.append("SFX toggle should disable sound effects")
scene._on_title_sfx_volume_changed(35.0)
if scene.title_sfx_volume_percent != 35:
failures.append("SFX volume slider should update the saved SFX volume percent")
scene._on_title_edge_scroll_toggled(false)
if scene.title_edge_scroll_enabled:
failures.append("edge-scroll toggle should disable edge scrolling")
if scene._update_edge_scroll(0.25):
failures.append("edge scrolling should not move while disabled in settings")
var persisted_scene = BattleSceneScript.new()
persisted_scene._ready()
if persisted_scene.title_bgm_enabled or persisted_scene.title_sfx_enabled or persisted_scene.title_edge_scroll_enabled:
failures.append("title settings toggles should persist into a new scene instance")
if persisted_scene.title_bgm_volume_percent != 45 or persisted_scene.title_sfx_volume_percent != 35:
failures.append("title settings volume values should persist into a new scene instance")
persisted_scene.free()
scene._on_title_settings_back_pressed()
if scene.title_settings_panel != null and scene.title_settings_panel.visible:
failures.append("settings back should hide the title settings panel")
scene._on_title_start_pressed()
if scene.title_screen_root != null and scene.title_screen_root.visible:
failures.append("starting a new game should hide the title screen")
if not scene._is_opening_prologue_visible():
failures.append("starting a new campaign should show the opening prologue before briefing")
if scene.briefing_panel != null and scene.briefing_panel.visible:
failures.append("opening prologue should hold back the first briefing until advanced")
if scene.opening_prologue_title_label == null or scene.opening_prologue_body_label == null:
failures.append("opening prologue should expose title and story text")
elif not scene.opening_prologue_body_label.text.contains("조조"):
failures.append("opening prologue should first explain Cao Cao before the Yellow Turban battle")
if scene.opening_prologue_step_label == null or not scene.opening_prologue_step_label.text.ends_with("/4"):
failures.append("opening prologue should give Cao Cao, decision, Xiahou Dun, and Yingchuan their own beats")
if scene.opening_prologue_scene_texture_rect == null or scene.opening_prologue_scene_texture_rect.texture == null:
failures.append("opening prologue should show generated story artwork")
elif scene.opening_prologue_scene_texture_rect.custom_minimum_size.x <= scene.opening_prologue_scene_texture_rect.custom_minimum_size.y:
failures.append("opening prologue story artwork should use a wide cinematic frame")
if scene.opening_prologue_body_label != null and scene.opening_prologue_body_label.custom_minimum_size.x < 500:
failures.append("opening prologue body text should have enough caption width for Korean copy")
var first_story_texture: Texture2D = null
if scene.opening_prologue_scene_texture_rect != null:
first_story_texture = scene.opening_prologue_scene_texture_rect.texture
scene._on_opening_prologue_next_pressed()
if scene.opening_prologue_scene_texture_rect == null or scene.opening_prologue_scene_texture_rect.texture == first_story_texture:
failures.append("opening prologue should change story artwork per page")
if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "맹덕의 결심":
failures.append("opening prologue should move from Cao Cao background to his decision")
elif not scene.opening_prologue_body_label.text.contains("황건") or not scene.opening_prologue_body_label.text.contains("작은 군세"):
failures.append("opening prologue decision page should explain why Cao Cao raises troops")
scene._on_opening_prologue_next_pressed()
if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "하후돈의 합류":
failures.append("opening prologue should then show Xiahou Dun joining Cao Cao")
elif not scene.opening_prologue_body_label.text.contains("하후돈") or not scene.opening_prologue_body_label.text.contains("첫 군령"):
failures.append("opening prologue Xiahou Dun page should link the relationship to the first command")
scene._on_opening_prologue_next_pressed()
if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "영천으로":
failures.append("opening prologue should end by pointing toward Yingchuan")
var prologue_guard := 0
while scene._is_opening_prologue_visible() and prologue_guard < 8:
scene._on_opening_prologue_next_pressed()
prologue_guard += 1
if scene._is_opening_prologue_visible():
failures.append("opening prologue should close after advancing through its pages")
if scene.briefing_panel == null or not scene.briefing_panel.visible:
failures.append("finishing the opening prologue should enter the first briefing")
if not scene.campaign_state.has_save():
failures.append("starting a new game should create the main campaign save")
if not scene.campaign_state.save_manual_game():
failures.append("manual checkpoint should be writable for title load selection")
scene._show_title_menu()
scene._update_title_menu_state()
if scene.title_load_button == null or scene.title_load_button.disabled:
failures.append("Game Load should become available after a campaign save exists")
scene._on_title_load_pressed()
if scene.title_load_panel == null or not scene.title_load_panel.visible:
failures.append("Game Load should open a load-slot selection panel instead of loading immediately")
if scene.title_start_button != null and scene.title_start_button.visible:
failures.append("load-slot selection should replace the main title commands while open")
if scene.title_load_auto_button == null or scene.title_load_auto_button.disabled:
failures.append("load-slot selection should enable the automatic campaign save")
elif not scene.title_load_auto_button.text.contains("자동 기록"):
failures.append("automatic load slot should be labeled in Korean")
if scene.title_load_manual_button == null or scene.title_load_manual_button.disabled:
failures.append("load-slot selection should enable the manual checkpoint save")
elif not scene.title_load_manual_button.text.contains("수동 봉인"):
failures.append("manual load slot should be labeled in Korean")
scene._show_title_menu()
if scene.title_load_panel != null and scene.title_load_panel.visible:
failures.append("showing the title menu again should reset the load-slot panel")
scene._on_title_load_pressed()
scene._on_title_load_back_pressed()
if scene.title_load_panel != null and scene.title_load_panel.visible:
failures.append("load-slot back should close the title load panel")
scene._on_title_load_pressed()
scene._on_title_load_auto_pressed()
if scene.title_screen_root != null and scene.title_screen_root.visible:
failures.append("loading a saved game should hide the title screen")
if scene._is_opening_prologue_visible():
failures.append("loading a saved game should skip the new-campaign opening prologue")
if scene.briefing_panel == null or not scene.briefing_panel.visible:
failures.append("loading a saved game should enter the current campaign briefing")
if scene.top_settings_button == null:
failures.append("top toolbar should expose an in-game Settings icon")
scene._on_settings_pressed()
if scene.settings_panel == null or not scene.settings_panel.visible:
failures.append("top Settings icon should open the in-game settings panel")
if scene.settings_bgm_toggle == null or scene.settings_sfx_toggle == null or scene.settings_edge_scroll_toggle == null:
failures.append("in-game settings panel should expose BGM, SFX, and edge-scroll toggles")
if scene.settings_bgm_volume_slider == null or scene.settings_sfx_volume_slider == null:
failures.append("in-game settings panel should expose BGM and SFX volume sliders")
scene._on_settings_bgm_toggled(true)
scene._on_settings_sfx_toggled(true)
scene._on_settings_edge_scroll_toggled(true)
scene._on_settings_bgm_volume_changed(70.0)
scene._on_settings_sfx_volume_changed(60.0)
if not scene.title_bgm_enabled or not scene.title_sfx_enabled or not scene.title_edge_scroll_enabled:
failures.append("in-game settings toggles should update the shared settings state")
if scene.title_bgm_volume_percent != 70 or scene.title_sfx_volume_percent != 60:
failures.append("in-game settings sliders should update the shared volume state")
scene._hide_settings_panel()
scene._on_new_campaign_pressed()
if not scene._is_opening_prologue_visible():
failures.append("top New Campaign should also show the opening prologue")
scene._on_opening_prologue_skip_pressed()
if scene.briefing_panel == null or not scene.briefing_panel.visible:
failures.append("skipping the top New Campaign prologue should enter the first briefing")
scene.free()
func _backup_user_file(path: String) -> Dictionary:
if not FileAccess.file_exists(path):
return {"exists": false, "text": ""}
return {
"exists": true,
"text": FileAccess.get_file_as_string(path)
}
func _restore_user_file(path: String, backup: Dictionary) -> bool:
if bool(backup.get("exists", false)):
var file := FileAccess.open(path, FileAccess.WRITE)
if file == null:
return false
file.store_string(str(backup.get("text", "")))
return true
return _clear_user_file(path)
func _clear_user_file(path: String) -> bool:
if not FileAccess.file_exists(path):
return true
return DirAccess.remove_absolute(ProjectSettings.globalize_path(path)) == OK