433 lines
27 KiB
GDScript
433 lines
27 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._bgm_stream_for("battle") == null:
|
|
failures.append("battle BGM stream should load before combat starts")
|
|
for sfx_key in [
|
|
"ui_click",
|
|
"menu_confirm",
|
|
"menu_cancel",
|
|
"footstep",
|
|
"slash",
|
|
"bow_release",
|
|
"hit_heavy",
|
|
"guard_block",
|
|
"skill_cast",
|
|
"fire_skill",
|
|
"item_pickup",
|
|
"victory_sting",
|
|
"defeat_sting"
|
|
]:
|
|
if scene._sfx_stream_for(sfx_key) == null:
|
|
failures.append("SFX stream should load: %s" % sfx_key)
|
|
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_menu_surface_texture_rect == null or scene.title_menu_surface_texture_rect.texture == null:
|
|
failures.append("title screen should use a generated command-board surface behind the menu")
|
|
else:
|
|
if not bool(scene.title_menu_surface_texture_rect.get_meta("generated_title_menu_surface", false)):
|
|
failures.append("title command board should carry generated surface metadata")
|
|
if scene.title_menu_surface_texture_rect.texture.get_width() < 1024 or scene.title_menu_surface_texture_rect.texture.get_height() < 1024:
|
|
failures.append("title command board should keep high-resolution generated pixels")
|
|
if scene.title_menu_surface_texture_rect.custom_minimum_size.y < 860.0:
|
|
failures.append("title command board should extend beyond the viewport for a full ceremonial frame")
|
|
if scene.title_panel != null and str(scene.title_panel.get_meta("panel_style_variant", "")) != "transparent_overlay":
|
|
failures.append("title menu panel should be a transparent content layer over generated art")
|
|
if scene.title_caption_label == null or scene.title_caption_label.text != "조조전":
|
|
failures.append("title screen should present the campaign title in Korean")
|
|
var title_story_feature: Node = null
|
|
if scene.title_screen_root != null:
|
|
title_story_feature = scene.title_screen_root.find_child("TitleStoryFeaturePanel", true, false)
|
|
if title_story_feature == null:
|
|
failures.append("title screen should feature a large generated opening story panel")
|
|
else:
|
|
var feature_image := title_story_feature.find_child("TitleStoryFeatureImage", true, false) as TextureRect
|
|
var feature_title := title_story_feature.find_child("TitleStoryFeatureTitle", true, false) as Label
|
|
var feature_body := title_story_feature.find_child("TitleStoryFeatureBody", true, false) as Label
|
|
if feature_image == null or feature_image.texture == null:
|
|
failures.append("title story feature should load generated opening artwork")
|
|
elif feature_image.custom_minimum_size.x < 430.0 or feature_image.custom_minimum_size.y < 160.0:
|
|
failures.append("title story feature should lead with a larger generated artwork frame: %s" % str(feature_image.custom_minimum_size))
|
|
if feature_title == null or feature_title.text != "황건의 난":
|
|
failures.append("title story feature should name the first opening beat")
|
|
if feature_body == null or not feature_body.text.contains("조조") or not feature_body.text.contains("황건의 난"):
|
|
failures.append("title story feature should summarize the first opening beat with a short Korean teaser")
|
|
var feature_frame := title_story_feature.find_child("TitleStoryFeatureImageFrame", true, false) as PanelContainer
|
|
if feature_frame == null or not bool(feature_frame.get_meta("generated_panel_texture", false)):
|
|
failures.append("title story feature artwork should sit inside a generated frame")
|
|
var title_story_strip: Node = null
|
|
if scene.title_screen_root != null:
|
|
title_story_strip = scene.title_screen_root.find_child("TitleStoryPreviewStrip", true, false)
|
|
if title_story_strip == null:
|
|
failures.append("title screen should preview the four generated opening story panels")
|
|
else:
|
|
var story_cards: Array = title_story_strip.find_children("TitleStoryPreviewCard*", "", true, false)
|
|
if story_cards.size() != 4:
|
|
failures.append("title story preview should expose four story beats: %d" % story_cards.size())
|
|
for story_card in story_cards:
|
|
if not story_card is PanelContainer:
|
|
failures.append("title story preview cards should use generated panel frames")
|
|
elif not bool((story_card as PanelContainer).get_meta("generated_panel_texture", false)):
|
|
failures.append("title story preview card should use a generated panel texture")
|
|
var story_frame := (story_card as Node).find_child("TitleStoryPreviewImageFrame", true, false) as PanelContainer
|
|
var story_image := (story_card as Node).find_child("TitleStoryPreviewImage", true, false) as TextureRect
|
|
var story_label := (story_card as Node).find_child("TitleStoryPreviewLabel", true, false) as Label
|
|
if story_frame == null or not bool(story_frame.get_meta("generated_panel_texture", false)):
|
|
failures.append("title story preview image should sit inside a generated frame")
|
|
if story_image == null or story_image.texture == null:
|
|
failures.append("title story preview card should load generated artwork")
|
|
elif story_image.custom_minimum_size.y < 64.0:
|
|
failures.append("title story preview image should stay large enough to read as artwork: %s" % str(story_image.custom_minimum_size))
|
|
if story_label == null or story_label.text.strip_edges().is_empty():
|
|
failures.append("title story preview card should name its story beat")
|
|
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")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_start_button, "new", "title start")
|
|
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")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_load_button, "load", "title load")
|
|
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")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_settings_button, "settings", "title 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()
|
|
if scene.title_audio_reset_button == null or scene.title_audio_reset_button.text != "소리 켜기":
|
|
failures.append("title settings should expose a one-click audio recovery button")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_audio_reset_button, "status", "title audio reset")
|
|
scene._on_title_audio_reset_pressed()
|
|
if not scene.title_bgm_enabled or not scene.title_sfx_enabled:
|
|
failures.append("title audio recovery should re-enable BGM and SFX")
|
|
if scene.title_bgm_volume_percent != 100 or scene.title_sfx_volume_percent != 100:
|
|
failures.append("title audio recovery should restore audible volume defaults")
|
|
if scene.bgm_player == null or scene.bgm_player.stream == null or scene.current_bgm_key.is_empty():
|
|
failures.append("title audio recovery should leave a playable BGM stream selected")
|
|
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("조조") or 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.begins_with("장면 ") 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_root == null or scene.opening_prologue_root.find_child("OpeningChapterSeal", true, false) == null:
|
|
failures.append("opening prologue should anchor the generated story art with a chapter seal")
|
|
var opening_story_strip: Node = null
|
|
if scene.opening_prologue_root != null:
|
|
opening_story_strip = scene.opening_prologue_root.find_child("OpeningStoryBeatStrip", true, false)
|
|
if opening_story_strip == null:
|
|
failures.append("opening prologue should show a generated four-beat story strip")
|
|
else:
|
|
var opening_story_cards: Array = opening_story_strip.find_children("OpeningStoryBeatCard*", "", true, false)
|
|
if opening_story_cards.size() != 4:
|
|
failures.append("opening story strip should expose four generated story beats: %d" % opening_story_cards.size())
|
|
var current_count := 0
|
|
var current_index := 0
|
|
for opening_story_card in opening_story_cards:
|
|
var card_node := opening_story_card as Node
|
|
var beat_frame := card_node.find_child("OpeningStoryBeatImageFrame", true, false) as PanelContainer
|
|
var beat_seal := card_node.find_child("OpeningStoryBeatSeal", true, false) as PanelContainer
|
|
var beat_image := card_node.find_child("OpeningStoryBeatImage", true, false) as TextureRect
|
|
var beat_label := card_node.find_child("OpeningStoryBeatLabel", true, false) as Label
|
|
if beat_frame == null or not bool(beat_frame.get_meta("generated_panel_texture", false)):
|
|
failures.append("opening story beat image should sit inside a generated frame")
|
|
if beat_seal == null or not bool(beat_seal.get_meta("generated_panel_texture", false)):
|
|
failures.append("opening story beat should use a generated seal number")
|
|
if beat_image == null or beat_image.texture == null:
|
|
failures.append("opening story beat card should load generated artwork")
|
|
elif beat_image.custom_minimum_size.x < 116.0:
|
|
failures.append("opening story beat artwork should stay readable in the strip: %s" % str(beat_image.custom_minimum_size))
|
|
if beat_label == null or beat_label.text.strip_edges().is_empty():
|
|
failures.append("opening story beat card should name its scene")
|
|
if bool(card_node.get_meta("current_story_beat", false)):
|
|
current_count += 1
|
|
current_index = opening_story_cards.find(opening_story_card) + 1
|
|
if current_count != 1 or current_index != 1:
|
|
failures.append("opening story strip should highlight the first story beat on entry")
|
|
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")
|
|
elif scene.opening_prologue_scene_texture_rect.custom_minimum_size.x < 1200 or scene.opening_prologue_scene_texture_rect.custom_minimum_size.y < 700:
|
|
failures.append("opening prologue story artwork should fill the first screen instead of sitting in a small frame")
|
|
if scene.opening_prologue_body_label != null and scene.opening_prologue_body_label.custom_minimum_size.x < 680:
|
|
failures.append("opening prologue body text should have enough caption width for Korean copy")
|
|
var opening_caption: PanelContainer = null
|
|
if scene.opening_prologue_root != null:
|
|
opening_caption = scene.opening_prologue_root.find_child("OpeningCaptionScroll", true, false) as PanelContainer
|
|
if opening_caption == null:
|
|
failures.append("opening prologue should keep a bottom scroll caption over the artwork")
|
|
elif opening_caption.custom_minimum_size.y > 124.0 or opening_caption.position.y < 540.0:
|
|
failures.append("opening prologue caption should stay low and compact so the image leads: %s at %s" % [str(opening_caption.custom_minimum_size), str(opening_caption.position)])
|
|
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")
|
|
var second_story_card: Node = null
|
|
if scene.opening_prologue_root != null:
|
|
second_story_card = scene.opening_prologue_root.find_child("OpeningStoryBeatCard2", true, false)
|
|
if second_story_card == null or not bool(second_story_card.get_meta("current_story_beat", false)):
|
|
failures.append("opening story strip should highlight the second beat after advancing")
|
|
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")
|
|
var audio_scene = BattleSceneScript.new()
|
|
audio_scene._ready()
|
|
audio_scene._enter_campaign_from_title()
|
|
audio_scene._start_battle_from_briefing()
|
|
if audio_scene.current_bgm_key != "battle" or audio_scene.bgm_player == null or audio_scene.bgm_player.stream == null:
|
|
failures.append("battle start should switch from menu music to an audible battle BGM stream")
|
|
audio_scene._show_title_menu()
|
|
if audio_scene.current_bgm_key != "menu" or audio_scene.bgm_player == null or audio_scene.bgm_player.stream == null:
|
|
failures.append("returning to title should restore the menu BGM stream")
|
|
audio_scene.free()
|
|
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")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_load_auto_button, "campaign", "title automatic load slot")
|
|
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")
|
|
else:
|
|
_check_title_menu_button_art(failures, scene.title_load_manual_button, "save", "title manual load slot")
|
|
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")
|
|
if scene.settings_audio_reset_button == null or scene.settings_audio_reset_button.text != "소리 켜기":
|
|
failures.append("in-game settings should expose a one-click audio recovery button")
|
|
scene._on_settings_bgm_toggled(false)
|
|
scene._on_settings_sfx_toggled(false)
|
|
scene._on_settings_bgm_volume_changed(20.0)
|
|
scene._on_settings_sfx_volume_changed(15.0)
|
|
scene._on_settings_audio_reset_pressed()
|
|
if not scene.title_bgm_enabled or not scene.title_sfx_enabled:
|
|
failures.append("in-game audio recovery should re-enable BGM and SFX")
|
|
if scene.title_bgm_volume_percent != 100 or scene.title_sfx_volume_percent != 100:
|
|
failures.append("in-game audio recovery should restore audible volume defaults")
|
|
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 _check_title_menu_button_art(failures: Array[String], button: Button, expected_icon: String, context: String) -> void:
|
|
if button == null:
|
|
failures.append("%s button missing" % context)
|
|
return
|
|
if not bool(button.get_meta("title_menu_button", false)):
|
|
failures.append("%s should be configured as a generated title menu button" % context)
|
|
if str(button.get_meta("command_icon", "")) != expected_icon:
|
|
failures.append("%s should keep command icon metadata `%s`, got `%s`" % [context, expected_icon, str(button.get_meta("command_icon", ""))])
|
|
if button.icon == null:
|
|
failures.append("%s should expose generated icon art" % context)
|
|
elif button.icon.get_width() < 128 or button.icon.get_height() < 128:
|
|
failures.append("%s should use high-resolution generated icon art, got %dx%d" % [context, button.icon.get_width(), button.icon.get_height()])
|
|
var stylebox := button.get_theme_stylebox("normal")
|
|
if not (stylebox is StyleBoxTexture):
|
|
failures.append("%s should use a generated button texture surface" % context)
|
|
return
|
|
var style := stylebox as StyleBoxTexture
|
|
if style.texture == null:
|
|
failures.append("%s generated button surface is missing its texture" % context)
|
|
elif style.texture.get_width() < 512 or style.texture.get_height() < 192:
|
|
failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()])
|
|
if str(button.tooltip_text).strip_edges().is_empty():
|
|
failures.append("%s should describe its command through a tooltip" % context)
|
|
|
|
|
|
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
|