243 lines
16 KiB
C#
243 lines
16 KiB
C#
using System;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine;
|
||
|
||
[InitializeOnLoad]
|
||
public static class HandbookSceneValidation
|
||
{
|
||
public static void ConfigureFeedbackAndValidate()
|
||
{
|
||
try
|
||
{
|
||
var scene = EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");
|
||
var controller = UnityEngine.Object.FindFirstObjectByType<DepthsOfGreed.HandbookUI.HandbookController>(FindObjectsInactive.Include);
|
||
var serialized = new SerializedObject(controller);
|
||
serialized.FindProperty("successfulPlacementDelay").floatValue = 0.5f;
|
||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||
EditorSceneManager.SaveScene(scene);
|
||
Validate();
|
||
}
|
||
catch (Exception e) { Debug.LogException(e); EditorApplication.Exit(1); }
|
||
}
|
||
public static void SetupAndValidate()
|
||
{
|
||
try
|
||
{
|
||
const string dataFolder = "Assets/HandbookData";
|
||
const string towerFolder = dataFolder + "/TestTowers";
|
||
if (!AssetDatabase.IsValidFolder(dataFolder)) AssetDatabase.CreateFolder("Assets", "HandbookData");
|
||
if (!AssetDatabase.IsValidFolder(towerFolder)) AssetDatabase.CreateFolder(dataFolder, "TestTowers");
|
||
var names = new[] {
|
||
new[] { "Gewehr", "Minigun", "Sniper", "Granatenwerfer", "Howitzer", "Flammenwerfer" },
|
||
new[] { "Laser-Piercer", "Bouncer", "Pusher", "Knocker", "Laser-Beam" },
|
||
new[] { "Falle", "Generator", "Staubsauger", "Spiegel" }
|
||
};
|
||
var prefixes = new[] { "ammo", "energy", "special" };
|
||
var entries = new System.Collections.Generic.List<DepthsOfGreed.HandbookUI.HandbookTowerEntry>();
|
||
for (int category = 0; category < names.Length; category++)
|
||
for (int i = 0; i < names[category].Length; i++)
|
||
{
|
||
string path = towerFolder + "/" + prefixes[category] + "_" + i + "_" + names[category][i] + ".asset";
|
||
var entry = AssetDatabase.LoadAssetAtPath<DepthsOfGreed.HandbookUI.HandbookTowerEntry>(path);
|
||
if (!entry)
|
||
{
|
||
entry = ScriptableObject.CreateInstance<DepthsOfGreed.HandbookUI.HandbookTowerEntry>();
|
||
entry.id = prefixes[category] + "_" + i;
|
||
entry.displayName = names[category][i];
|
||
entry.category = (DepthsOfGreed.HandbookUI.HandbookTowerCategory)category;
|
||
entry.energyCost = 100 + i * 25;
|
||
entry.summary = category == 0 ? "Schaden: 20 · Schüsse/s: 2" : category == 1 ? "Energieverbrauch: 5/s" :
|
||
new[] { "Falle · Testwirkung", "Energieerzeugung · Testwert: 5/s", "Sammelfunktion · Testeintrag", "Umlenkung · Testeintrag" }[i];
|
||
entry.description = entry.displayName + " – Testeintrag\n\nHier steht später eine kurze Erklärung der Funktion. Kosten und Werte sind vorläufige Testdaten.\n\nKlicken: zurück zur Baukarte";
|
||
AssetDatabase.CreateAsset(entry, path);
|
||
}
|
||
entries.Add(entry);
|
||
}
|
||
const string catalogPath = dataFolder + "/TestTurmkatalog.asset";
|
||
var catalog = AssetDatabase.LoadAssetAtPath<DepthsOfGreed.HandbookUI.HandbookTowerCatalog>(catalogPath);
|
||
if (!catalog)
|
||
{
|
||
catalog = ScriptableObject.CreateInstance<DepthsOfGreed.HandbookUI.HandbookTowerCatalog>();
|
||
catalog.towers = entries.ToArray();
|
||
AssetDatabase.CreateAsset(catalog, catalogPath);
|
||
}
|
||
AssetDatabase.SaveAssets();
|
||
var scene = EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");
|
||
catalog = AssetDatabase.LoadAssetAtPath<DepthsOfGreed.HandbookUI.HandbookTowerCatalog>(catalogPath);
|
||
if (!catalog) throw new Exception("Catalog asset could not be loaded after scene switch.");
|
||
var controller = UnityEngine.Object.FindFirstObjectByType<DepthsOfGreed.HandbookUI.HandbookController>(FindObjectsInactive.Include);
|
||
var serialized = new SerializedObject(controller);
|
||
serialized.FindProperty("towerCatalog").objectReferenceValue = catalog;
|
||
serialized.FindProperty("successfulPlacementDelay").floatValue = 1f;
|
||
serialized.ApplyModifiedPropertiesWithoutUndo();
|
||
EditorSceneManager.SaveScene(scene);
|
||
Debug.Log("HANDBOOK_DATA_SETUP_OK: 15 editable tower assets and catalog connected; delay set to 1 second.");
|
||
Validate();
|
||
}
|
||
catch (Exception exception) { Debug.LogException(exception); EditorApplication.Exit(1); }
|
||
}
|
||
static HandbookSceneValidation()
|
||
{
|
||
if (SessionState.GetBool("HandbookValidationPending", false))
|
||
EditorApplication.playModeStateChanged += CheckPlayMode;
|
||
}
|
||
public static void Validate()
|
||
{
|
||
try
|
||
{
|
||
var scene = EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");
|
||
var controllers = UnityEngine.Object.FindObjectsByType<DepthsOfGreed.HandbookUI.HandbookController>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||
if (controllers.Length != 1) throw new Exception("Expected exactly one handbook controller.");
|
||
var serialized = new SerializedObject(controllers[0]);
|
||
foreach (var field in new[] { "closedBook", "openBook", "bookButton", "closeButton", "closedTabsRoot", "openTabsRoot", "towerCatalog", "testTowerPrefab" })
|
||
if (!serialized.FindProperty(field).objectReferenceValue) throw new Exception("Missing reference: " + field);
|
||
foreach (var field in new[] { "closedTabs", "openTabs", "pages" })
|
||
{
|
||
var array = serialized.FindProperty(field);
|
||
if (array.arraySize != 4) throw new Exception("Invalid array: " + field);
|
||
for (int i = 0; i < array.arraySize; i++)
|
||
if (!array.GetArrayElementAtIndex(i).objectReferenceValue) throw new Exception("Missing array reference: " + field);
|
||
}
|
||
foreach (var root in scene.GetRootGameObjects())
|
||
foreach (var transform in root.GetComponentsInChildren<Transform>(true))
|
||
if (GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(transform.gameObject) != 0)
|
||
throw new Exception("Missing script on " + transform.name);
|
||
Debug.Log("HANDBOOK_SCENE_VALIDATION_OK: Scene loaded; all controller references resolved; no missing scripts.");
|
||
EditorApplication.playModeStateChanged += CheckPlayMode;
|
||
SessionState.SetBool("HandbookValidationPending", true);
|
||
EditorApplication.EnterPlaymode();
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
Debug.LogException(exception);
|
||
EditorApplication.Exit(1);
|
||
}
|
||
}
|
||
|
||
private static void CheckPlayMode(PlayModeStateChange state)
|
||
{
|
||
if (state != PlayModeStateChange.EnteredPlayMode) return;
|
||
SessionState.SetBool("HandbookValidationPending", false);
|
||
EditorApplication.playModeStateChanged -= CheckPlayMode;
|
||
try
|
||
{
|
||
var controller = UnityEngine.Object.FindFirstObjectByType<DepthsOfGreed.HandbookUI.HandbookController>();
|
||
controller.Open(0);
|
||
var menu = UnityEngine.Object.FindFirstObjectByType<DepthsOfGreed.HandbookUI.BuildMenuController>();
|
||
if (!menu) throw new Exception("Build menu did not initialize.");
|
||
foreach (var pair in new[] { (0, 6), (1, 5), (2, 4) })
|
||
{
|
||
menu.SelectCategory(pair.Item1);
|
||
var cards = menu.GetComponentsInChildren<DepthsOfGreed.HandbookUI.TowerCardView>();
|
||
if (cards.Length != pair.Item2) throw new Exception("Wrong category card count.");
|
||
foreach (var card in cards)
|
||
if (card.Entry == null || !card.transform.Find("Front").gameObject.activeSelf || card.transform.Find("Back").gameObject.activeSelf)
|
||
throw new Exception("Invalid initial card state.");
|
||
}
|
||
menu.SelectCategory(0);
|
||
var first = menu.GetComponentsInChildren<DepthsOfGreed.HandbookUI.TowerCardView>()[0];
|
||
menu.ToggleCard(first);
|
||
double deadline = EditorApplication.timeSinceStartup + 0.6;
|
||
EditorApplication.CallbackFunction check = null;
|
||
check = () => {
|
||
if (EditorApplication.timeSinceStartup < deadline) return;
|
||
EditorApplication.update -= check;
|
||
try {
|
||
if (!first.transform.Find("Back").gameObject.activeSelf) throw new Exception("Card flip did not complete.");
|
||
menu.SelectCategory(1);
|
||
if (first.transform.Find("Back").gameObject.activeSelf) throw new Exception("Category did not reset card.");
|
||
controller.Close();
|
||
controller.Open(0);
|
||
if (menu.GetComponentsInChildren<DepthsOfGreed.HandbookUI.TowerCardView>().Length != 6)
|
||
throw new Exception("Closing did not restore the default build category.");
|
||
controller.Close();
|
||
foreach (var card in menu.GetComponentsInChildren<DepthsOfGreed.HandbookUI.TowerCardView>(true))
|
||
if (card.transform.Find("Back").gameObject.activeSelf) throw new Exception("Book close did not reset cards.");
|
||
controller.Open(3);
|
||
var indexMenu = UnityEngine.Object.FindFirstObjectByType<DepthsOfGreed.HandbookUI.HandbookIndexController>();
|
||
if (!indexMenu || indexMenu.VisibleEntryCount != 15 || indexMenu.SelectedEntry != null)
|
||
throw new Exception("Index initialization failed.");
|
||
indexMenu.SelectEntry(0);
|
||
if (!indexMenu.SelectedEntry) throw new Exception("Index selection failed.");
|
||
indexMenu.ListScroll.verticalNormalizedPosition = 0f;
|
||
controller.Open(0);
|
||
controller.Open(3);
|
||
if (indexMenu.SelectedEntry != null || indexMenu.ListScroll.verticalNormalizedPosition < 0.99f)
|
||
throw new Exception("Index re-entry did not reset selection and scroll.");
|
||
indexMenu.SelectFilter(2);
|
||
if (indexMenu.VisibleEntryCount != 0 || indexMenu.SelectedEntry != null)
|
||
throw new Exception("Enemy filter failed.");
|
||
indexMenu.SelectFilter(1);
|
||
if (indexMenu.VisibleEntryCount != 15) throw new Exception("Tower filter failed.");
|
||
indexMenu.SelectEntry(1);
|
||
controller.Close();
|
||
controller.Open(3);
|
||
if (indexMenu.SelectedEntry != null || indexMenu.ListScroll.verticalNormalizedPosition < 0.99f)
|
||
throw new Exception("Reopening index did not reset selection and scroll.");
|
||
controller.Close();
|
||
var placement = controller.GetComponent<DepthsOfGreed.HandbookUI.TestPlacementController>();
|
||
var entry = first.Entry;
|
||
if (!placement.enabled) throw new Exception("Placement initialization failed.");
|
||
controller.Open(0);
|
||
var validScreen = (Vector2)Camera.main.WorldToScreenPoint(new Vector3(3.5f, 0, 0));
|
||
if (!placement.Begin(entry, validScreen) || !placement.IsDragging) throw new Exception("Drag did not begin.");
|
||
placement.Cancel();
|
||
if (placement.IsDragging || placement.TowerCount != 0) throw new Exception("Cancel created a tower.");
|
||
if (placement.TryPlaceAt(entry, Vector3.zero, out _)) throw new Exception("Center must be invalid.");
|
||
if (placement.TryPlaceAt(entry, new Vector3(3.1f, 0, 0), out _)) throw new Exception("Base crosses inner wall.");
|
||
if (placement.TryPlaceAt(entry, new Vector3(3.9f, 0, 0), out _)) throw new Exception("Base crosses outer wall.");
|
||
int initialEnergy = placement.Energy;
|
||
if (!placement.Begin(entry, validScreen)) throw new Exception("Second drag did not begin.");
|
||
placement.End(validScreen);
|
||
if (placement.TowerCount != 1 || placement.IsDragging) throw new Exception("Drag release failed.");
|
||
if (!placement.IsWaitingForReturn) throw new Exception("Successful placement did not start delay.");
|
||
for (int i = 1; i < 8; i++) {
|
||
float angle = i * Mathf.PI / 4f;
|
||
if (!placement.TryPlaceAt(entry, new Vector3(Mathf.Cos(angle) * 3.5f, Mathf.Sin(angle) * 3.5f, 0), out string reason))
|
||
throw new Exception("Valid placement failed: " + reason);
|
||
}
|
||
if (placement.TowerCount != 8 || placement.Energy != initialEnergy - entry.energyCost * 8)
|
||
throw new Exception("Wrong tower count or resource deduction.");
|
||
if (placement.TryPlaceAt(entry, new Vector3(3.5f, 0, 0), out _)) throw new Exception("Overlap allowed.");
|
||
float freeAngle = Mathf.PI / 8f;
|
||
if (placement.TryPlaceAt(entry, new Vector3(Mathf.Cos(freeAngle) * 3.5f, Mathf.Sin(freeAngle) * 3.5f, 0), out string limitReason) || limitReason != "Turmlimit erreicht")
|
||
throw new Exception("Tower limit failed.");
|
||
double delayDeadline = EditorApplication.timeSinceStartup + 1.5;
|
||
EditorApplication.CallbackFunction delayCheck = null;
|
||
delayCheck = () => {
|
||
if (EditorApplication.timeSinceStartup < delayDeadline) return;
|
||
EditorApplication.update -= delayCheck;
|
||
try {
|
||
if (placement.IsWaitingForReturn) throw new Exception("Book delay never expired.");
|
||
var group = menu.GetComponentInParent<CanvasGroup>();
|
||
if (!group.blocksRaycasts) throw new Exception("Book input not restored after delay.");
|
||
if (placement.Begin(entry, validScreen)) throw new Exception("Limit should prevent drag.");
|
||
var toast = controller.transform.Find("FeedbackBox") ?? controller.transform.Find("Feedbackbox");
|
||
var message = controller.transform.Find("FeedbackMessage").GetComponent<TMPro.TMP_Text>();
|
||
if (!toast || !toast.gameObject.activeSelf || message.text != "Turmlimit erreicht")
|
||
throw new Exception("Limit notice did not show background and message.");
|
||
double toastDeadline = EditorApplication.timeSinceStartup + 2.9;
|
||
EditorApplication.CallbackFunction toastCheck = null;
|
||
toastCheck = () => {
|
||
if (EditorApplication.timeSinceStartup < toastDeadline) return;
|
||
EditorApplication.update -= toastCheck;
|
||
try {
|
||
if (toast.gameObject.activeSelf || message.text != "") throw new Exception("Notice did not disappear.");
|
||
controller.Close();
|
||
Debug.Log("HANDBOOK_RUNTIME_VALIDATION_OK: Category reset, 0.5-second placement delay, limit notice background and automatic dismissal passed; existing placement checks passed.");
|
||
EditorApplication.Exit(0);
|
||
} catch (Exception e) { Debug.LogException(e); EditorApplication.Exit(1); }
|
||
};
|
||
EditorApplication.update += toastCheck;
|
||
} catch (Exception e) { Debug.LogException(e); EditorApplication.Exit(1); }
|
||
};
|
||
EditorApplication.update += delayCheck;
|
||
} catch (Exception e) { Debug.LogException(e); EditorApplication.Exit(1); }
|
||
};
|
||
EditorApplication.update += check;
|
||
}
|
||
catch (Exception e) { Debug.LogException(e); EditorApplication.Exit(1); }
|
||
}
|
||
}
|