mirror of
https://github.com/Earth-Genesis-Games/Ajedrez_Purgatorio.git
synced 2026-09-11 09:47:35 +00:00
- Duel flow: mating piece vs boss while board collapses (PurgatoryDuelManager/Rules/UI/MuerteQuotes) - GameState.PurgatoryDuel + CampaignManager rescue/restart hooks + SquareClick routing - Dice-based rescue of most valuable dead identity; final defeat restarts Chapter 1 - 6 SFX clips wired in Ch1/2/3 via AudioClipAssigner - OptionsMenuController runtime audio/graphics panel, wired to MainMenu - Tests migrated to Assets/Tests/EditMode subfolders + PurgatoryDuelRulesTests (200 total) - Editor tools: DiagAssign, SpeakerDatabaseSetup, PurgatoryCanvasMaintenance
167 lines
6.3 KiB
C#
167 lines
6.3 KiB
C#
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace AjedrezPurgatorio.EditorTools
|
|
{
|
|
/// <summary>
|
|
/// Puebla el SpeakerDatabase.asset con los retratos existentes y asigna el
|
|
/// asset a todos los DialogueUI de las escenas. Guarda los cambios.
|
|
/// Ejecutar con:
|
|
/// Unity -batchmode -projectPath . -executeMethod
|
|
/// AjedrezPurgatorio.EditorTools.SpeakerDatabaseSetup.PopulateAndAssign
|
|
///
|
|
/// Nota: 'muerte' y 'ricardo' no tienen PNG de retrato todavía; se registran
|
|
/// con portrait nulo para evitar warnings y facilitar añadir arte después.
|
|
/// </summary>
|
|
public static class SpeakerDatabaseSetup
|
|
{
|
|
private const string DatabasePath = "Assets/Game/Data/Resources/SpeakerDatabase.asset";
|
|
private const string PortraitsFolder = "Assets/Game/Data/Resources/Portraits";
|
|
|
|
private static readonly (string speaker, string portrait)[] Entries =
|
|
{
|
|
("narrator", "narrator"),
|
|
("muerte", null),
|
|
("ricardo", null),
|
|
("alfil", "alfil"),
|
|
("caballo", "caballo"),
|
|
("peon", "peon"),
|
|
("reina", "reina"),
|
|
("torre", "torre"),
|
|
("espejo", "espejo"),
|
|
("coleccionista", "coleccionista"),
|
|
("enemigo", "enemigo"),
|
|
("voz_nino", "voz_nino"),
|
|
};
|
|
|
|
private static readonly string[] SceneNames =
|
|
{
|
|
"MainMenu", "Chapter1", "Chapter2", "Chapter3",
|
|
};
|
|
|
|
public static void PopulateAndAssign()
|
|
{
|
|
try
|
|
{
|
|
int populated = PopulateDatabase();
|
|
int assigned = AssignToScenes();
|
|
|
|
Debug.Log($"[SpeakerDatabaseSetup] COMPLETO: {populated} speakers poblados, {assigned} DialogueUI actualizados.");
|
|
EditorApplication.Exit(0);
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Debug.LogError($"[SpeakerDatabaseSetup] FALLÓ: {ex}");
|
|
EditorApplication.Exit(1);
|
|
}
|
|
}
|
|
|
|
private static int PopulateDatabase()
|
|
{
|
|
var db = AssetDatabase.LoadAssetAtPath<SpeakerDatabase>(DatabasePath);
|
|
if (db == null)
|
|
throw new System.IO.FileNotFoundException($"No se encontró {DatabasePath}");
|
|
|
|
// Los PNG de Portraits pueden no estar importados en la AssetDatabase
|
|
// (p.ej. Library regenerada). Importar explícitamente sin Refresh global.
|
|
int imported = 0;
|
|
foreach (var (_, portraitFile) in Entries)
|
|
{
|
|
if (string.IsNullOrEmpty(portraitFile))
|
|
continue;
|
|
|
|
string path = $"{PortraitsFolder}/{portraitFile}.png";
|
|
if (!System.IO.File.Exists(path))
|
|
{
|
|
Debug.LogError($"[SpeakerDatabaseSetup] No existe en disco: {path}");
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
|
|
imported++;
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
Debug.LogError($"[SpeakerDatabaseSetup] Fallo importando {path}: {ex.Message}");
|
|
}
|
|
}
|
|
Debug.Log($"[SpeakerDatabaseSetup] {imported} retratos re-importados.");
|
|
|
|
var so = new SerializedObject(db);
|
|
SerializedProperty list = so.FindProperty("_speakers");
|
|
if (list == null)
|
|
throw new System.MissingMemberException("_speakers no encontrado en SpeakerDatabase.");
|
|
|
|
list.ClearArray();
|
|
|
|
int count = 0;
|
|
foreach (var (speaker, portraitFile) in Entries)
|
|
{
|
|
list.InsertArrayElementAtIndex(list.arraySize);
|
|
SerializedProperty element = list.GetArrayElementAtIndex(list.arraySize - 1);
|
|
|
|
element.FindPropertyRelative("speakerName").stringValue = speaker;
|
|
|
|
Sprite sprite = null;
|
|
if (!string.IsNullOrEmpty(portraitFile))
|
|
sprite = AssetDatabase.LoadAssetAtPath<Sprite>($"{PortraitsFolder}/{portraitFile}.png");
|
|
|
|
if (!string.IsNullOrEmpty(portraitFile) && sprite == null)
|
|
Debug.LogWarning($"[SpeakerDatabaseSetup] Portrait no encontrado para '{speaker}': {portraitFile}.png");
|
|
|
|
element.FindPropertyRelative("portrait").objectReferenceValue = sprite;
|
|
count++;
|
|
}
|
|
|
|
so.ApplyModifiedPropertiesWithoutUndo();
|
|
EditorUtility.SetDirty(db);
|
|
AssetDatabase.SaveAssets();
|
|
return count;
|
|
}
|
|
|
|
private static int AssignToScenes()
|
|
{
|
|
var db = AssetDatabase.LoadAssetAtPath<SpeakerDatabase>(DatabasePath);
|
|
int assignedTotal = 0;
|
|
|
|
// Nota: SerializedObject.ApplyModifiedProperties* no persiste este
|
|
// cambio en este proyecto (probado: el valor se pierde incluso en
|
|
// memoria tras Apply). Reflexión directa + SetDirty sí funciona.
|
|
var field = typeof(DialogueUI).GetField("_speakerDatabase",
|
|
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
|
|
|
foreach (string sceneName in SceneNames)
|
|
{
|
|
Scene scene = EditorSceneManager.OpenScene(
|
|
$"Assets/Scenes/{sceneName}.unity", OpenSceneMode.Single);
|
|
|
|
bool dirty = false;
|
|
|
|
foreach (DialogueUI ui in Object.FindObjectsByType<DialogueUI>(
|
|
FindObjectsInactive.Include, FindObjectsSortMode.None))
|
|
{
|
|
if (ui == null || field.GetValue(ui) as SpeakerDatabase != null)
|
|
continue;
|
|
|
|
field.SetValue(ui, db);
|
|
EditorUtility.SetDirty(ui);
|
|
assignedTotal++;
|
|
dirty = true;
|
|
}
|
|
|
|
if (dirty)
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
|
|
EditorSceneManager.SaveScene(scene);
|
|
}
|
|
|
|
return assignedTotal;
|
|
}
|
|
}
|
|
}
|