146 lines
7.6 KiB
C#
146 lines
7.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using HotUpdate.Gameplay;
|
|
using Newtonsoft.Json;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
public static class VerifyMainMenuMusic
|
|
{
|
|
public static string Preflight()
|
|
{
|
|
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
|
return JsonConvert.SerializeObject(new { scene.path, scene.isDirty,
|
|
scenes = Enumerable.Range(0, UnityEngine.SceneManagement.SceneManager.sceneCount)
|
|
.Select(i => UnityEngine.SceneManagement.SceneManager.GetSceneAt(i))
|
|
.Select(s => new { s.path, s.isDirty }).ToArray() });
|
|
}
|
|
|
|
public static string PrepareBoot()
|
|
{
|
|
var previous = EditorSceneManager.playModeStartScene;
|
|
SessionState.SetString("AIGame.Music.PreviousPlayScene", previous == null ? "" : AssetDatabase.GetAssetPath(previous));
|
|
EditorSceneManager.playModeStartScene = AssetDatabase.LoadAssetAtPath<SceneAsset>("Assets/Scenes/Boot.unity");
|
|
return "Boot selected for Play Mode only; existing edit scenes preserved: " + Preflight();
|
|
}
|
|
|
|
public static string RestoreScenes()
|
|
{
|
|
Check(!EditorApplication.isPlaying, "Exit Play Mode before restore");
|
|
string path = SessionState.GetString("AIGame.Music.PreviousPlayScene", "");
|
|
EditorSceneManager.playModeStartScene = string.IsNullOrEmpty(path) ? null : AssetDatabase.LoadAssetAtPath<SceneAsset>(path);
|
|
SessionState.EraseString("AIGame.Music.PreviousPlayScene");
|
|
return Preflight();
|
|
}
|
|
|
|
static void Check(bool ok, string message) { if (!ok) throw new Exception(message); }
|
|
static async Task Until(Func<bool> condition, string message, int seconds = 25)
|
|
{
|
|
var deadline = DateTime.UtcNow.AddSeconds(seconds);
|
|
while (!condition())
|
|
{
|
|
Check(DateTime.UtcNow < deadline, message);
|
|
await Task.Delay(100);
|
|
}
|
|
}
|
|
|
|
public static async Task<string> Play()
|
|
{
|
|
Check(EditorApplication.isPlaying, "Play Mode required");
|
|
const string musicKey = "AIGame.Settings.music", sfxKey = "AIGame.Settings.sfx";
|
|
bool hadMusic = PlayerPrefs.HasKey(musicKey), hadSfx = PlayerPrefs.HasKey(sfxKey);
|
|
int musicValue = PlayerPrefs.GetInt(musicKey, 1), sfxValue = PlayerPrefs.GetInt(sfxKey, 1);
|
|
float listenerVolume = AudioListener.volume;
|
|
PrototypeFlow flow = null;
|
|
try
|
|
{
|
|
await Until(() => UnityEngine.Object.FindObjectOfType<MainMenuMusic>() != null, "Music not loaded by bootstrap");
|
|
var music = UnityEngine.Object.FindObjectOfType<MainMenuMusic>();
|
|
var source = music.Source;
|
|
Check(source != null && source.clip.name == "MeadowBreeze_MainMenu_Loop", "Wrong selected song");
|
|
Check(source.loop && !source.playOnAwake && source.spatialBlend == 0, "Invalid authored audio source");
|
|
PrototypeClientPrefs.Music = true;
|
|
PrototypeClientPrefs.Sfx = true;
|
|
await Until(() => source.isPlaying, "Main menu music did not start");
|
|
float before = source.time;
|
|
await Task.Delay(300);
|
|
Check(source.time > before, "Playback samples did not advance");
|
|
flow = UnityEngine.Object.FindObjectOfType<PrototypeFlow>();
|
|
await Until(() => flow != null && flow.Game != null && !flow.IsBusy, "Gameplay not ready for navigation");
|
|
flow.Handle("Home");
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
before = source.time;
|
|
flow.Handle("Settings");
|
|
await Until(() => flow.Page == "UI_PrototypeSettings", "Settings did not open");
|
|
Check(source.isPlaying && source.time >= before - 0.1f, "Opening settings restarted or stopped music");
|
|
var button = UnityEngine.Object.FindObjectsOfType<Button>().Single(b => b.name == "Btn_ToggleMusic");
|
|
button.onClick.Invoke();
|
|
await Task.Delay(100);
|
|
Check(!PrototypeClientPrefs.Music && !source.isPlaying && PrototypeClientPrefs.Sfx, "Music off did not pause independently of SFX");
|
|
float pausedAt = source.time;
|
|
await Task.Delay(180);
|
|
Check(Math.Abs(source.time - pausedAt) < 0.05f, "Muted music kept advancing");
|
|
button.onClick.Invoke();
|
|
await Until(() => source.isPlaying, "Music toggle did not resume");
|
|
Check(source.time >= pausedAt - 0.1f, "Music toggle restarted the song");
|
|
flow.Handle("Home");
|
|
await Until(() => flow.Page == "Home", "Did not return home");
|
|
Check(UnityEngine.Object.FindObjectsOfType<MainMenuMusic>().Length == 1, "Duplicate music player");
|
|
}
|
|
PrototypeClientPrefs.Sfx = false;
|
|
Check(source.isPlaying && AudioListener.volume > 0, "SFX off muted enabled music");
|
|
PrototypeClientPrefs.Music = false;
|
|
Check(!source.isPlaying && AudioListener.volume == 0, "Both audio channels off failed");
|
|
PrototypeClientPrefs.Music = true;
|
|
await Until(() => source.isPlaying, "Music on with SFX off failed");
|
|
|
|
flow.Handle("Pinball");
|
|
await Until(() => flow.Page == "UI_PrototypePinball", "Pinball did not open");
|
|
Check(!source.isPlaying, "Camp music continued in pinball gameplay");
|
|
PrototypeClientPrefs.Music = false;
|
|
PrototypeClientPrefs.Music = true;
|
|
Check(!source.isPlaying, "Preference change resumed music in gameplay");
|
|
flow.Handle("Home");
|
|
await Until(() => source.isPlaying, "Returning home did not resume music");
|
|
|
|
// Exercise the battle playback gate without starting a server battle.
|
|
music.SetInMenu(false);
|
|
Check(!source.isPlaying, "Gameplay playback gate failed");
|
|
music.SetInMenu(true);
|
|
await Until(() => source.isPlaying, "Menu playback gate did not resume");
|
|
music.SendMessage("OnApplicationPause", true);
|
|
Check(!source.isPlaying, "Background pause did not pause music");
|
|
music.SendMessage("OnApplicationPause", false);
|
|
await Until(() => source.isPlaying, "Foreground resume failed");
|
|
|
|
source.time = source.clip.length - 0.35f;
|
|
await Until(() => source.isPlaying && source.time < 2, "Clip did not loop", 5);
|
|
var report = new {
|
|
result = "PASS", selected = source.clip.name, source.clip.length,
|
|
startup = true, settingsToggleCycles = 3, resumesWithoutRestart = true,
|
|
independentSfxToggle = true, singlePlayer = true, pinballPauseAndReturn = true,
|
|
gameplayGate = true, applicationPauseAndResume = true, loopWrap = true,
|
|
preferencesRestoredInFinally = true, verifiedUtc = DateTime.UtcNow.ToString("o")
|
|
};
|
|
string json = JsonConvert.SerializeObject(report, Formatting.Indented);
|
|
string repo = Directory.GetParent(Application.dataPath).Parent.FullName;
|
|
File.WriteAllText(Path.Combine(repo, "Doc/Design/Sound/MainMenuSoft/playback_validation.json"), json);
|
|
return json;
|
|
}
|
|
finally
|
|
{
|
|
if (hadMusic) PlayerPrefs.SetInt(musicKey, musicValue); else PlayerPrefs.DeleteKey(musicKey);
|
|
if (hadSfx) PlayerPrefs.SetInt(sfxKey, sfxValue); else PlayerPrefs.DeleteKey(sfxKey);
|
|
PlayerPrefs.Save();
|
|
PrototypeClientPrefs.Apply();
|
|
AudioListener.volume = listenerVolume;
|
|
if (flow != null) flow.Handle("Home");
|
|
}
|
|
}
|
|
}
|