Files

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");
}
}
}