Files
aibis-dream/Assets/Scripts/Utility/PeipeiMemoryJumpTool.cs
T

329 lines
12 KiB
C#

#if UNITY_EDITOR || DEVELOPMENT_BUILD
using System.Collections;
using System.IO;
using AibisDream.SaveSystem;
using UnityEngine;
namespace AibisDream
{
public class PeipeiMemoryJumpTool : MonoBehaviour
{
private const float UiMargin = 16f;
private const float DevButtonWidth = 96f;
private const float DevButtonHeight = 34f;
private const float MenuWidth = 300f;
private const float MaxMenuHeight = 560f;
private const float RowHeight = 30f;
private const float HeaderHeight = 24f;
private const float StatusWidth = 360f;
private const float StatusHeight = 28f;
private const string SaveRootFolder = "AllOurBrokenParts";
private const string TestSaveFolder = "testsavs";
private const string SnapshotFileName = "snapshot";
private static readonly DevSaveEntry[] Peipei1Saves =
{
new("PP1 01 Intro", "20260629_191732_683_"),
new("PP1 02 Enter Room", "20260629_191737_198_"),
new("PP1 03 Body UF", "20260629_191745_604_"),
new("PP1 04 UF Deep", "20260629_191747_751_"),
new("PP1 05 Body Vision", "20260629_191751_564_"),
new("PP1 06 Memory Start", "20260629_191806_710_"),
new("PP1 07 Memory Check", "20260629_191807_884_"),
new("PP1 08 Analysis", "20260629_191947_383_"),
new("PP1 09 Pre Talk", "20260629_191953_429_"),
new("PP1 10 Surgery", "20260629_191957_937_"),
new("PP1 11 CutLine", "20260629_192000_426_"),
new("PP1 12 Recheck", "20260629_192000_744_"),
new("PP1 13 Recheck Vision", "20260629_192002_368_"),
new("PP1 14 Ending Talk", "20260629_192010_965_")
};
private static readonly DevSaveEntry[] Peipei2Saves =
{
new("PP2 01 Start", "20260630_142906_700_"),
new("PP2 02 Intro", "20260630_142906_849_"),
new("PP2 03 First Heat", "20260630_142915_334_"),
new("PP2 04 Heat System", "20260630_142918_072_"),
new("PP2 05 Body Check", "20260630_142921_707_"),
new("PP2 06 UF Locked", "20260630_142923_878_"),
new("PP2 07 UF Shell", "20260630_142928_045_"),
new("PP2 08 UF Check", "20260630_142931_587_"),
new("PP2 09 Eye View", "20260630_142938_533_"),
new("PP2 10 Analysis", "20260630_142956_453_"),
new("PP2 11 Pre Talk", "20260630_143007_149_"),
new("PP2 12 Stop Image", "20260630_143014_716_"),
new("PP2 13 Stop Check", "20260630_143020_779_"),
new("PP2 14 Cool Fail Talk", "20260630_143042_615_"),
new("PP2 15 Remove UF", "20260630_143043_024_"),
new("PP2 16 Pre Talk 2", "20260630_143044_410_"),
new("PP2 17 Surgery", "20260630_143058_329_"),
new("PP2 18 Memory Plugin", "20260630_143100_380_"),
new("PP2 19 Ending Talk", "20260630_143118_278_")
};
private static PeipeiMemoryJumpTool instance;
private bool isJumping;
private bool menuOpen;
private string status = "";
private float statusVisibleUntil;
private Vector2 menuScroll;
private GUIStyle buttonStyle;
private GUIStyle headerStyle;
private GUIStyle menuBoxStyle;
private GUIStyle statusStyle;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void Bootstrap()
{
if (instance != null)
return;
var existing = FindObjectOfType<PeipeiMemoryJumpTool>();
if (existing != null)
{
instance = existing;
DontDestroyOnLoad(existing.gameObject);
return;
}
var toolObject = new GameObject("[Dev] Peipei Jump Tool");
DontDestroyOnLoad(toolObject);
instance = toolObject.AddComponent<PeipeiMemoryJumpTool>();
}
private void OnGUI()
{
EnsureStyles();
float buttonX = Screen.width - UiMargin - DevButtonWidth;
float buttonY = Screen.height - UiMargin - DevButtonHeight;
var devButtonRect = new Rect(buttonX, buttonY, DevButtonWidth, DevButtonHeight);
if (menuOpen)
DrawCommandMenu(devButtonRect);
string label = isJumping ? "Busy..." : "Dev";
if (GUI.Button(devButtonRect, label, buttonStyle))
menuOpen = !menuOpen;
if (!string.IsNullOrEmpty(status) && Time.unscaledTime < statusVisibleUntil)
{
float menuHeight = GetMenuHeight();
float statusY = menuOpen
? buttonY - menuHeight - StatusHeight - UiMargin
: buttonY - StatusHeight - 8f;
var statusRect = new Rect(
Screen.width - UiMargin - StatusWidth,
Mathf.Max(UiMargin, statusY),
StatusWidth,
StatusHeight);
GUI.Label(statusRect, status, statusStyle);
}
}
private void DrawCommandMenu(Rect devButtonRect)
{
float menuHeight = GetMenuHeight();
float menuX = Screen.width - UiMargin - MenuWidth;
float menuY = Mathf.Max(UiMargin, devButtonRect.y - menuHeight - 8f);
var menuRect = new Rect(menuX, menuY, MenuWidth, menuHeight);
GUI.Box(menuRect, GUIContent.none, menuBoxStyle);
var viewRect = new Rect(0f, 0f, MenuWidth - 28f, GetContentHeight());
var scrollRect = new Rect(
menuRect.x + 6f,
menuRect.y + 6f,
menuRect.width - 12f,
menuRect.height - 12f);
menuScroll = GUI.BeginScrollView(scrollRect, menuScroll, viewRect);
float y = 0f;
DrawHeader(ref y, "Peipei 1");
DrawSaveEntries(ref y, Peipei1Saves);
y += 6f;
DrawHeader(ref y, "Peipei 2");
DrawSaveEntries(ref y, Peipei2Saves);
GUI.EndScrollView();
}
private void DrawHeader(ref float y, string label)
{
GUI.Label(new Rect(0f, y, MenuWidth - 32f, HeaderHeight), label, headerStyle);
y += HeaderHeight;
}
private void DrawSaveEntries(ref float y, DevSaveEntry[] entries)
{
foreach (var entry in entries)
{
var rect = new Rect(0f, y, MenuWidth - 32f, RowHeight);
DrawCommandButton(rect, entry.Label, !isJumping, () => StartCoroutine(LoadDevSave(entry)));
y += RowHeight;
}
}
private static float GetMenuHeight()
{
return Mathf.Min(MaxMenuHeight, GetContentHeight() + 12f);
}
private static float GetContentHeight()
{
return HeaderHeight * 2f
+ RowHeight * (Peipei1Saves.Length + Peipei2Saves.Length)
+ 6f;
}
private void DrawCommandButton(Rect rect, string label, bool enabled, System.Action action)
{
GUI.enabled = enabled;
if (GUI.Button(rect, label, buttonStyle))
{
menuOpen = false;
action?.Invoke();
}
GUI.enabled = true;
}
private IEnumerator LoadDevSave(DevSaveEntry entry)
{
if (isJumping)
yield break;
string savePath = ResolveSnapshotPath(entry);
if (string.IsNullOrEmpty(savePath))
{
SetStatus($"Save missing: {entry.Label}", 5f);
yield break;
}
isJumping = true;
if (NeedsResetToMainMenu())
{
SetStatus($"Resetting: {entry.Label}");
GameManager.Instance.QuitGame();
yield return WaitForMainMenuReady();
}
SetStatus($"Loading: {entry.Label}");
yield return SaveRestoreOrchestrator.RestoreFromFile(savePath);
SetStatus($"Loaded: {entry.Label}", 3f);
isJumping = false;
}
private static bool NeedsResetToMainMenu()
{
var gameManager = GameManager.Instance;
if (gameManager == null)
{
return false;
}
if (gameManager.state.isInGame)
{
return true;
}
var sceneLoader = SceneLoader.Instance;
return sceneLoader != null && !string.IsNullOrEmpty(sceneLoader.CurrentSceneName);
}
private static IEnumerator WaitForMainMenuReady()
{
const float timeoutSeconds = 15f;
float elapsed = 0f;
while (elapsed < timeoutSeconds)
{
var gameManager = GameManager.Instance;
var sceneLoader = SceneLoader.Instance;
bool sceneCleared = sceneLoader == null || string.IsNullOrEmpty(sceneLoader.CurrentSceneName);
bool notInGame = gameManager == null || !gameManager.state.isInGame;
if (sceneCleared && notInGame)
{
yield break;
}
elapsed += Time.unscaledDeltaTime;
yield return null;
}
Debug.LogWarning("[PeipeiMemoryJumpTool] 等待主界面超时,继续读档。");
}
private static string ResolveSnapshotPath(DevSaveEntry entry)
{
string archiveRoot = Path.Combine(Application.persistentDataPath, SaveRootFolder, TestSaveFolder);
if (!Directory.Exists(archiveRoot))
return null;
var matches = Directory.GetDirectories(archiveRoot, entry.DirectoryPrefix + "*");
if (matches.Length == 0)
return null;
string path = Path.Combine(matches[0], SnapshotFileName);
return File.Exists(path + ".json") ? path : null;
}
private void SetStatus(string message, float visibleSeconds = 30f)
{
status = message;
statusVisibleUntil = Time.unscaledTime + visibleSeconds;
Debug.Log($"[PeipeiMemoryJumpTool] {message}");
}
private void EnsureStyles()
{
if (buttonStyle != null && statusStyle != null)
return;
buttonStyle = new GUIStyle(GUI.skin.button)
{
fontSize = 14,
alignment = TextAnchor.MiddleCenter
};
headerStyle = new GUIStyle(GUI.skin.label)
{
fontSize = 13,
fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(6, 0, 3, 0)
};
menuBoxStyle = new GUIStyle(GUI.skin.box)
{
padding = new RectOffset(6, 6, 6, 6)
};
statusStyle = new GUIStyle(GUI.skin.box)
{
fontSize = 13,
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(8, 8, 4, 4)
};
}
private readonly struct DevSaveEntry
{
public readonly string Label;
public readonly string DirectoryPrefix;
public DevSaveEntry(string label, string directoryPrefix)
{
Label = label;
DirectoryPrefix = directoryPrefix;
}
}
}
}
#endif