feat: SetupChapterScenes editor script — one-click scene wiring

Menu: Ajedrez Purgatorio > Setup Chapter Scenes

Creates and wires ALL serialized references in chapter scenes:
- Singletons: GameStateManager, CampaignManager, AIController, PurgatoryManager
- Gameplay: PieceIdentityManager, DiceSystem, PieceSelectionFeedback
- UI: GameHUD (turn text, check indicator, pause button, captured containers),
  DialogueUI (panel, text, speaker name/portrait, continue indicator),
  PauseMenu (panel, continue/retry/main menu buttons),
  PromotionUI (panel, 4 piece buttons),
  PieceTooltip (name, role, relationship)
- Purgatory: PurgatoryOfferUI, DiceRollUI, DiceResultUI
- Dead Kings: DeadKingRevealUI, DeadKingInputUI
- Assigns CampaignState, CampaignConfig assets to relevant components
- Cleans garbage objects (SquareClick at wrong positions)
This commit is contained in:
2026-08-17 18:18:55 -03:00
parent 67048ddcb0
commit 29446f95eb
+809
View File
@@ -0,0 +1,809 @@
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using TMPro;
using System.IO;
using AjedrezPurgatorio.Data;
using AjedrezPurgatorio.Meta;
using AjedrezPurgatorio.UI;
/// <summary>
/// Comprehensive scene setup for chapter scenes.
/// Wires all serialized references, creates missing UI, cleans garbage objects.
/// Run via menu: Ajedrez Purgatorio > Setup Chapter Scenes
/// </summary>
public static class SetupChapterScenes
{
private static readonly string[] CHAPTER_SCENES = {
"Assets/Scenes/Chapter1.unity",
"Assets/Scenes/Chapter2.unity",
"Assets/Scenes/Chapter3.unity"
};
private const string CAMPAIGN_STATE_PATH = "Assets/Game/Data/Resources/CampaignState.asset";
private const string CAMPAIGN_CONFIG_PATH = "Assets/Game/Data/CampaignConfig.asset";
private const string DEAD_KING_POOL_PATH = "Assets/Game/Data/Resources/DeadKingPool.asset";
[MenuItem("Ajedrez Purgatorio/Setup Chapter Scenes")]
public static void Execute()
{
Debug.Log("═══════════════════════════════════════════════");
Debug.Log(" SETUP CHAPTER SCENES ");
Debug.Log("═══════════════════════════════════════════════");
// Load shared assets
var campaignState = AssetDatabase.LoadAssetAtPath<CampaignState>(CAMPAIGN_STATE_PATH);
var campaignConfig = AssetDatabase.LoadAssetAtPath<CampaignConfig>(CAMPAIGN_CONFIG_PATH);
var deadKingPool = AssetDatabase.LoadAssetAtPath<DeadKingPool>(DEAD_KING_POOL_PATH);
if (campaignState == null)
Debug.LogError($"CampaignState not found at {CAMPAIGN_STATE_PATH}");
if (campaignConfig == null)
Debug.LogError($"CampaignConfig not found at {CAMPAIGN_CONFIG_PATH}");
foreach (var scenePath in CHAPTER_SCENES)
{
if (!File.Exists(scenePath))
{
Debug.LogWarning($"Scene not found: {scenePath}. Skipping.");
continue;
}
Debug.Log($"\n── Setting up {Path.GetFileNameWithoutExtension(scenePath)}...");
EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
SetupSingletons(campaignState, campaignConfig, deadKingPool);
SetupGameplayComponents();
SetupUIComponents(campaignState);
CleanupGarbage();
EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene());
Debug.Log($" ✅ {Path.GetFileNameWithoutExtension(scenePath)} saved.");
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("\n═══════════════════════════════════════════════");
Debug.Log(" ✅ ALL CHAPTER SCENES SETUP COMPLETE ");
Debug.Log("═══════════════════════════════════════════════");
}
// ══════════════════════════════════════════════════════
// SINGLETONS & CORE COMPONENTS
// ══════════════════════════════════════════════════════
static void SetupSingletons(CampaignState cs, CampaignConfig cc, DeadKingPool dkp)
{
// GameStateManager
EnsureSingleton<GameStateManager>("GameStateManager");
// CampaignManager — may already exist from DontDestroyOnLoad, but we need one in scene for Inspector refs
var cm = EnsureSingleton<CampaignManager>("CampaignManager");
if (cm != null)
{
var so = new SerializedObject(cm);
SetAssetProp(so, "_campaignConfig", cc);
SetAssetProp(so, "_campaignState", cs);
so.ApplyModifiedPropertiesWithoutUndo();
}
// AIController
EnsureSingleton<AIController>("AIController");
// BoardManager
var bm = Object.FindFirstObjectByType<BoardManager>();
if (bm == null)
{
Debug.LogWarning(" ⚠ BoardManager not found in scene. Board won't generate.");
}
}
static T EnsureSingleton<T>(string name) where T : MonoBehaviour
{
var existing = Object.FindFirstObjectByType<T>();
if (existing != null) return existing;
var go = new GameObject(name);
var comp = go.AddComponent<T>();
Debug.Log($" ✅ Created {name}");
return comp;
}
// ══════════════════════════════════════════════════════
// GAMEPLAY COMPONENTS
// ══════════════════════════════════════════════════════
static void SetupGameplayComponents()
{
// PieceIdentityManager
var pim = EnsureSingleton<PieceIdentityManager>("PieceIdentityManager");
if (pim != null)
{
var cs = AssetDatabase.LoadAssetAtPath<CampaignState>(CAMPAIGN_STATE_PATH);
var so = new SerializedObject(pim);
SetAssetProp(so, "_campaignState", cs);
so.ApplyModifiedPropertiesWithoutUndo();
}
// PurgatoryManager
var pm = EnsureSingleton<PurgatoryManager>("PurgatoryManager");
if (pm != null)
{
var cs = AssetDatabase.LoadAssetAtPath<CampaignState>(CAMPAIGN_STATE_PATH);
var so = new SerializedObject(pm);
SetAssetProp(so, "_campaignState", cs);
// DiceSystem — create child or find
var ds = Object.FindFirstObjectByType<DiceSystem>();
if (ds == null)
{
var dsGo = new GameObject("DiceSystem");
ds = dsGo.AddComponent<DiceSystem>();
Debug.Log(" ✅ Created DiceSystem");
}
SetObjProp(so, "_diceSystem", ds);
// Purgatory UIs
SetObjProp(so, "_offerUI", FindOrCreatePurgatoryOfferUI());
SetObjProp(so, "_diceRollUI", FindOrCreateDiceRollUI());
SetObjProp(so, "_resultUI", FindOrCreateDiceResultUI());
so.ApplyModifiedPropertiesWithoutUndo();
}
// PieceSelectionFeedback
EnsureSingleton<PieceSelectionFeedback>("PieceSelectionFeedback");
}
// ══════════════════════════════════════════════════════
// UI COMPONENTS
// ══════════════════════════════════════════════════════
static void SetupUIComponents(CampaignState cs)
{
// Find or create main UI Canvas
var uiCanvas = GameObject.Find("UICanvas");
if (uiCanvas == null)
{
uiCanvas = new GameObject("UICanvas");
var canvas = uiCanvas.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 100;
var scaler = uiCanvas.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
uiCanvas.AddComponent<GraphicRaycaster>();
Debug.Log(" ✅ Created UICanvas");
}
SetupGameHUD(uiCanvas);
SetupDialogueUI(uiCanvas);
SetupPauseMenu(uiCanvas);
SetupPromotionUI(uiCanvas);
SetupPieceTooltip(uiCanvas);
SetupDeadKingUIs(cs);
}
static void SetupGameHUD(GameObject canvas)
{
var hud = Object.FindFirstObjectByType<GameHUD>();
if (hud == null)
{
var hudGo = new GameObject("GameHUD");
hudGo.transform.SetParent(canvas.transform, false);
var rt = hudGo.AddComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = rt.offsetMax = Vector2.zero;
// Turn text
var turnText = CreateTMPChild("TurnText", hudGo.transform, "Turno: Blancas", 24f);
AnchorNorm(turnText.rectTransform, 0.02f, 0.92f, 0.20f, 0.98f);
// Check indicator
var checkGO = new GameObject("CheckIndicator");
checkGO.transform.SetParent(hudGo.transform, false);
var checkRT = checkGO.AddComponent<RectTransform>();
AnchorNorm(checkRT, 0.35f, 0.92f, 0.65f, 0.98f);
var checkText = checkGO.AddComponent<TextMeshProUGUI>();
checkText.text = "¡JAQUE!";
checkText.fontSize = 28f;
checkText.color = new Color(1f, 0.3f, 0.3f);
checkText.fontStyle = FontStyles.Bold;
checkText.alignment = TextAlignmentOptions.Center;
checkGO.SetActive(false);
// Move count
var moveText = CreateTMPChild("MoveCountText", hudGo.transform, "Movimientos: 0", 18f);
AnchorNorm(moveText.rectTransform, 0.80f, 0.92f, 0.98f, 0.98f);
// Captured pieces containers
var whiteCap = CreateContainer("WhiteCapturedContainer", hudGo.transform);
AnchorNorm(whiteCap.GetComponent<RectTransform>(), 0.02f, 0.02f, 0.48f, 0.10f);
var blackCap = CreateContainer("BlackCapturedContainer", hudGo.transform);
AnchorNorm(blackCap.GetComponent<RectTransform>(), 0.52f, 0.02f, 0.98f, 0.10f);
// Pause button
var pauseBtn = CreateButton("PauseButton", hudGo.transform, "||");
pauseBtn.GetComponent<Image>().color = new Color(0.3f, 0.3f, 0.3f, 0.8f);
AnchorNorm(pauseBtn.GetComponent<RectTransform>(), 0.94f, 0.92f, 0.98f, 0.98f);
// Add component and wire
hud = hudGo.AddComponent<GameHUD>();
var so = new SerializedObject(hud);
SetObjProp(so, "_turnText", turnText);
SetObjProp(so, "_checkIndicator", checkGO);
SetObjProp(so, "_whiteCapturedContainer", whiteCap.GetComponent<RectTransform>());
SetObjProp(so, "_blackCapturedContainer", blackCap.GetComponent<RectTransform>());
SetObjProp(so, "_pauseButton", pauseBtn.GetComponent<Button>());
SetObjProp(so, "_moveCountText", moveText);
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created GameHUD with all references");
}
}
static void SetupDialogueUI(GameObject canvas)
{
var dialogue = Object.FindFirstObjectByType<DialogueUI>();
if (dialogue == null)
{
var dialogueGo = new GameObject("DialogueUI");
dialogueGo.transform.SetParent(canvas.transform, false);
var rt = dialogueGo.AddComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = rt.offsetMax = Vector2.zero;
// Panel
var panel = CreatePanel("DialoguePanel", dialogueGo.transform, new Vector2(1600, 300), new Color(0.05f, 0.03f, 0.08f, 0.95f));
AnchorNorm(panel.GetComponent<RectTransform>(), 0.15f, 0.02f, 0.85f, 0.20f);
// Speaker name
var speakerName = CreateTMPChild("SpeakerName", panel.transform, "", 22f);
speakerName.color = new Color(1f, 0.85f, 0.2f);
speakerName.fontStyle = FontStyles.Bold;
AnchorNorm(speakerName.rectTransform, 0.02f, 0.70f, 0.30f, 0.95f);
// Speaker portrait
var portraitGO = new GameObject("SpeakerPortrait");
portraitGO.transform.SetParent(panel.transform, false);
var portraitRT = portraitGO.AddComponent<RectTransform>();
AnchorNorm(portraitRT, 0.02f, 0.05f, 0.18f, 0.65f);
var portraitImg = portraitGO.AddComponent<Image>();
portraitImg.color = new Color(0.2f, 0.2f, 0.2f);
// Dialogue text
var dialogueText = CreateTMPChild("DialogueText", panel.transform, "", 20f);
dialogueText.color = new Color(0.9f, 0.9f, 0.9f);
dialogueText.enableWordWrapping = true;
AnchorNorm(dialogueText.rectTransform, 0.20f, 0.05f, 0.96f, 0.65f);
// Continue indicator
var continueInd = CreateTMPChild("ContinueIndicator", panel.transform, "▼", 18f);
continueInd.color = new Color(0.7f, 0.7f, 0.7f);
continueInd.alignment = TextAlignmentOptions.MidlineRight;
AnchorNorm(continueInd.rectTransform, 0.90f, 0.70f, 0.98f, 0.95f);
panel.SetActive(false);
dialogue = dialogueGo.AddComponent<DialogueUI>();
var so = new SerializedObject(dialogue);
SetObjProp(so, "_dialoguePanel", panel);
SetObjProp(so, "_dialogueText", dialogueText);
SetObjProp(so, "_speakerNameText", speakerName);
SetObjProp(so, "_speakerPortrait", portraitImg);
SetObjProp(so, "_continueIndicator", continueInd);
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created DialogueUI with all references");
}
}
static void SetupPauseMenu(GameObject canvas)
{
var pause = Object.FindFirstObjectByType<PauseMenuController>();
if (pause == null)
{
var pauseGo = new GameObject("PauseMenu");
pauseGo.transform.SetParent(canvas.transform, false);
var rt = pauseGo.AddComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = rt.offsetMax = Vector2.zero;
// Panel
var panel = CreatePanel("PausePanel", pauseGo.transform, new Vector2(500, 400), new Color(0.08f, 0.05f, 0.12f, 0.95f));
// Title
var title = CreateTMPChild("PauseTitle", panel.transform, "PAUSA", 32f);
title.fontStyle = FontStyles.Bold;
title.alignment = TextAlignmentOptions.Center;
AnchorNorm(title.rectTransform, 0.1f, 0.82f, 0.9f, 0.95f);
// Buttons
var continueBtn = CreateButton("ContinueButton", panel.transform, "CONTINUAR");
AnchorNorm(continueBtn.GetComponent<RectTransform>(), 0.15f, 0.60f, 0.85f, 0.74f);
var retryBtn = CreateButton("RetryButton", panel.transform, "REINTENTAR");
retryBtn.GetComponent<Image>().color = new Color(0.35f, 0.25f, 0.08f);
AnchorNorm(retryBtn.GetComponent<RectTransform>(), 0.15f, 0.38f, 0.85f, 0.52f);
var mainMenuBtn = CreateButton("MainMenuButton", panel.transform, "MENÚ PRINCIPAL");
mainMenuBtn.GetComponent<Image>().color = new Color(0.35f, 0.08f, 0.08f);
AnchorNorm(mainMenuBtn.GetComponent<RectTransform>(), 0.15f, 0.16f, 0.85f, 0.30f);
panel.SetActive(false);
pause = pauseGo.AddComponent<PauseMenuController>();
var so = new SerializedObject(pause);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_continueButton", continueBtn.GetComponent<Button>());
SetObjProp(so, "_retryButton", retryBtn.GetComponent<Button>());
SetObjProp(so, "_mainMenuButton", mainMenuBtn.GetComponent<Button>());
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created PauseMenu with all references");
}
}
static void SetupPromotionUI(GameObject canvas)
{
var promo = Object.FindFirstObjectByType<PromotionUI>();
if (promo == null)
{
var promoGo = new GameObject("PromotionUI");
promoGo.transform.SetParent(canvas.transform, false);
var rt = promoGo.AddComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.offsetMin = rt.offsetMax = Vector2.zero;
var panel = CreatePanel("PromotionPanel", promoGo.transform, new Vector2(800, 200), new Color(0.08f, 0.06f, 0.12f, 0.95f));
var queenBtn = CreateButton("QueenButton", panel.transform, "Reina");
AnchorNorm(queenBtn.GetComponent<RectTransform>(), 0.02f, 0.1f, 0.24f, 0.9f);
var rookBtn = CreateButton("RookButton", panel.transform, "Torre");
AnchorNorm(rookBtn.GetComponent<RectTransform>(), 0.26f, 0.1f, 0.48f, 0.9f);
var bishopBtn = CreateButton("BishopButton", panel.transform, "Alfil");
AnchorNorm(bishopBtn.GetComponent<RectTransform>(), 0.52f, 0.1f, 0.74f, 0.9f);
var knightBtn = CreateButton("KnightButton", panel.transform, "Caballo");
AnchorNorm(knightBtn.GetComponent<RectTransform>(), 0.78f, 0.1f, 0.98f, 0.9f);
panel.SetActive(false);
promo = promoGo.AddComponent<PromotionUI>();
var so = new SerializedObject(promo);
SetObjProp(so, "_promotionPanel", panel);
SetObjProp(so, "_queenButton", queenBtn.GetComponent<Button>());
SetObjProp(so, "_rookButton", rookBtn.GetComponent<Button>());
SetObjProp(so, "_bishopButton", bishopBtn.GetComponent<Button>());
SetObjProp(so, "_knightButton", knightBtn.GetComponent<Button>());
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created PromotionUI with all references");
}
}
static void SetupPieceTooltip(GameObject canvas)
{
var tooltip = Object.FindFirstObjectByType<PieceTooltip>();
if (tooltip == null)
{
var tooltipGo = new GameObject("PieceTooltip");
tooltipGo.transform.SetParent(canvas.transform, false);
var rt = tooltipGo.AddComponent<RectTransform>();
rt.sizeDelta = new Vector2(300, 120);
var panel = CreatePanel("TooltipPanel", tooltipGo.transform, new Vector2(300, 120), new Color(0.05f, 0.03f, 0.08f, 0.92f));
var nameText = CreateTMPChild("PieceName", panel.transform, "", 18f);
nameText.color = new Color(1f, 0.85f, 0.2f);
nameText.fontStyle = FontStyles.Bold;
AnchorNorm(nameText.rectTransform, 0.05f, 0.60f, 0.95f, 0.95f);
var roleText = CreateTMPChild("PieceRole", panel.transform, "", 14f);
roleText.color = new Color(0.8f, 0.8f, 0.8f);
AnchorNorm(roleText.rectTransform, 0.05f, 0.30f, 0.95f, 0.60f);
var relText = CreateTMPChild("PieceRelationship", panel.transform, "", 12f);
relText.color = new Color(0.6f, 0.7f, 0.9f);
relText.fontStyle = FontStyles.Italic;
AnchorNorm(relText.rectTransform, 0.05f, 0.05f, 0.95f, 0.30f);
panel.SetActive(false);
tooltip = tooltipGo.AddComponent<PieceTooltip>();
Debug.Log(" ✅ Created PieceTooltip");
}
}
static void SetupDeadKingUIs(CampaignState cs)
{
// DeadKingRevealUI
var reveal = Object.FindFirstObjectByType<DeadKingRevealUI>();
if (reveal == null)
{
CreateDeadKingRevealUI();
}
// DeadKingInputUI
var input = Object.FindFirstObjectByType<DeadKingInputUI>();
if (input == null)
{
CreateDeadKingInputUI();
}
}
// ══════════════════════════════════════════════════════
// PURGATORY UI FACTORIES
// ══════════════════════════════════════════════════════
static PurgatoryOfferUI FindOrCreatePurgatoryOfferUI()
{
var existing = Object.FindFirstObjectByType<PurgatoryOfferUI>();
if (existing != null) return existing;
var canvas = FindOrCreateCanvas("PurgatoryCanvas", 110);
var panel = CreatePanel("PurgatoryOfferPanel", canvas.transform, new Vector2(600, 400), new Color(0.1f, 0.05f, 0.15f, 0.95f));
var title = CreateTMPChild("OfferTitle", panel.transform, "PURGATORIO", 32f);
title.color = new Color(0.9f, 0.6f, 0.1f);
title.fontStyle = FontStyles.Bold;
title.alignment = TextAlignmentOptions.Center;
AnchorNorm(title.rectTransform, 0.05f, 0.80f, 0.95f, 0.95f);
var desc = CreateTMPChild("OfferDescription", panel.transform, "Una pieza ha sido capturada...\n¿Aceptas el desafío del Purgatorio?", 18f);
desc.color = new Color(0.8f, 0.8f, 0.8f);
desc.alignment = TextAlignmentOptions.Center;
AnchorNorm(desc.rectTransform, 0.05f, 0.45f, 0.95f, 0.78f);
var acceptBtn = CreateButton("AcceptButton", panel.transform, "ACEPTAR");
acceptBtn.GetComponent<Image>().color = new Color(0.15f, 0.4f, 0.15f);
AnchorNorm(acceptBtn.GetComponent<RectTransform>(), 0.05f, 0.08f, 0.47f, 0.25f);
var declineBtn = CreateButton("DeclineButton", panel.transform, "RECHAZAR");
declineBtn.GetComponent<Image>().color = new Color(0.4f, 0.1f, 0.1f);
AnchorNorm(declineBtn.GetComponent<RectTransform>(), 0.53f, 0.08f, 0.95f, 0.25f);
var pieceName = CreateTMPChild("PieceName", panel.transform, "", 20f);
pieceName.color = new Color(1f, 0.85f, 0.2f);
pieceName.alignment = TextAlignmentOptions.Center;
AnchorNorm(pieceName.rectTransform, 0.05f, 0.28f, 0.95f, 0.43f);
panel.SetActive(false);
var comp = panel.AddComponent<PurgatoryOfferUI>();
var so = new SerializedObject(comp);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_pieceNameText", pieceName);
SetObjProp(so, "_acceptButton", acceptBtn.GetComponent<Button>());
SetObjProp(so, "_declineButton", declineBtn.GetComponent<Button>());
SetObjProp(so, "_titleText", title);
SetObjProp(so, "_descriptionText", desc);
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created PurgatoryOfferUI");
return comp;
}
static DiceRollUI FindOrCreateDiceRollUI()
{
var existing = Object.FindFirstObjectByType<DiceRollUI>();
if (existing != null) return existing;
var canvas = FindOrCreateCanvas("PurgatoryCanvas", 110);
var panel = CreatePanel("DiceRollPanel", canvas.transform, new Vector2(600, 400), new Color(0.08f, 0.04f, 0.12f, 0.95f));
var title = CreateTMPChild("RollTitle", panel.transform, "LANZAMIENTO DE DADOS", 28f);
title.color = new Color(0.9f, 0.6f, 0.1f);
title.fontStyle = FontStyles.Bold;
title.alignment = TextAlignmentOptions.Center;
AnchorNorm(title.rectTransform, 0.05f, 0.82f, 0.95f, 0.95f);
var playerResult = CreateTMPChild("PlayerResult", panel.transform, "Jugador: -", 24f);
playerResult.color = new Color(0.3f, 0.8f, 0.3f);
playerResult.alignment = TextAlignmentOptions.Center;
AnchorNorm(playerResult.rectTransform, 0.05f, 0.55f, 0.95f, 0.70f);
var deathResult = CreateTMPChild("DeathResult", panel.transform, "La Muerte: -", 24f);
deathResult.color = new Color(0.9f, 0.2f, 0.2f);
deathResult.alignment = TextAlignmentOptions.Center;
AnchorNorm(deathResult.rectTransform, 0.05f, 0.35f, 0.95f, 0.50f);
panel.SetActive(false);
var comp = panel.AddComponent<DiceRollUI>();
var so = new SerializedObject(comp);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_titleText", title);
SetObjProp(so, "_playerResultText", playerResult);
SetObjProp(so, "_deathResultText", deathResult);
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created DiceRollUI");
return comp;
}
static DiceResultUI FindOrCreateDiceResultUI()
{
var existing = Object.FindFirstObjectByType<DiceResultUI>();
if (existing != null) return existing;
var canvas = FindOrCreateCanvas("PurgatoryCanvas", 110);
var panel = CreatePanel("DiceResultPanel", canvas.transform, new Vector2(600, 400), new Color(0.08f, 0.04f, 0.12f, 0.95f));
var title = CreateTMPChild("ResultTitle", panel.transform, "RESULTADO", 32f);
title.fontStyle = FontStyles.Bold;
title.alignment = TextAlignmentOptions.Center;
AnchorNorm(title.rectTransform, 0.05f, 0.80f, 0.95f, 0.95f);
var desc = CreateTMPChild("ResultDescription", panel.transform, "", 20f);
desc.alignment = TextAlignmentOptions.Center;
AnchorNorm(desc.rectTransform, 0.05f, 0.40f, 0.95f, 0.78f);
var closeBtn = CreateButton("CloseButton", panel.transform, "CERRAR");
AnchorNorm(closeBtn.GetComponent<RectTransform>(), 0.25f, 0.08f, 0.75f, 0.22f);
panel.SetActive(false);
var comp = panel.AddComponent<DiceResultUI>();
var so = new SerializedObject(comp);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_titleText", title);
SetObjProp(so, "_descriptionText", desc);
SetObjProp(so, "_closeButton", closeBtn.GetComponent<Button>());
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created DiceResultUI");
return comp;
}
// ══════════════════════════════════════════════════════
// DEAD KING UI FACTORIES (from CreateChapterScenes pattern)
// ══════════════════════════════════════════════════════
static void CreateDeadKingRevealUI()
{
var canvas = FindOrCreateCanvas("PurgatoryCanvas", 120);
var panel = CreatePanel("DeadKingRevealPanel", canvas.transform, new Vector2(700, 500), new Color(0.08f, 0.02f, 0.02f, 0.97f));
var titleTMP = CreateTMPChild("RevealTitle", panel.transform, "UN CAIDO REGRESA", 36f);
titleTMP.color = new Color(0.9f, 0.1f, 0.1f);
titleTMP.fontStyle = FontStyles.Bold;
titleTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(titleTMP.rectTransform, 0.05f, 0.84f, 0.95f, 0.97f);
var nameTMP = CreateTMPChild("DeadKingName", panel.transform, "Nombre del Rey Caido", 28f);
nameTMP.color = new Color(1f, 0.85f, 0.2f);
nameTMP.fontStyle = FontStyles.Bold;
nameTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(nameTMP.rectTransform, 0.05f, 0.68f, 0.95f, 0.82f);
var quoteTMP = CreateTMPChild("DeadKingQuote", panel.transform, "\"...\"", 22f);
quoteTMP.color = new Color(0.7f, 0.7f, 0.85f);
quoteTMP.fontStyle = FontStyles.Italic;
quoteTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(quoteTMP.rectTransform, 0.08f, 0.48f, 0.92f, 0.66f);
var statsTMP = CreateTMPChild("DeadKingStats", panel.transform, "Capitulo alcanzado: ?\nPiezas perdidas: ?", 18f);
statsTMP.color = new Color(0.75f, 0.75f, 0.75f);
statsTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(statsTMP.rectTransform, 0.05f, 0.30f, 0.95f, 0.46f);
var continueGO = CreateButton("ContinueButton", panel.transform, "CONTINUAR");
continueGO.GetComponent<Image>().color = new Color(0.18f, 0.36f, 0.18f);
AnchorNorm(continueGO.GetComponent<RectTransform>(), 0.3f, 0.06f, 0.7f, 0.18f);
panel.SetActive(false);
var comp = panel.AddComponent<DeadKingRevealUI>();
var so = new SerializedObject(comp);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_playerNameText", nameTMP);
SetObjProp(so, "_messageText", quoteTMP);
SetObjProp(so, "_statsText", statsTMP);
SetObjProp(so, "_continueButton", continueGO.GetComponent<Button>());
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created DeadKingRevealUI");
}
static void CreateDeadKingInputUI()
{
var canvas = FindOrCreateCanvas("PurgatoryCanvas", 120);
var panel = CreatePanel("DeadKingInputPanel", canvas.transform, new Vector2(700, 500), new Color(0.05f, 0.02f, 0.08f, 0.97f));
var titleTMP = CreateTMPChild("TitleText", panel.transform, "INSCRIBE TU NOMBRE", 36f);
titleTMP.color = new Color(0.9f, 0.1f, 0.1f);
titleTMP.fontStyle = FontStyles.Bold;
titleTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(titleTMP.rectTransform, 0.05f, 0.84f, 0.95f, 0.97f);
var nameLabel = CreateTMPChild("NameLabel", panel.transform, "Tu nombre:", 18f);
nameLabel.color = new Color(0.8f, 0.8f, 0.8f);
AnchorNorm(nameLabel.rectTransform, 0.08f, 0.72f, 0.92f, 0.80f);
var nameInputGO = new GameObject("NameInputField");
nameInputGO.transform.SetParent(panel.transform, false);
var nameInputRT = nameInputGO.AddComponent<RectTransform>();
AnchorNorm(nameInputRT, 0.08f, 0.62f, 0.92f, 0.72f);
var nameInputImg = nameInputGO.AddComponent<Image>();
nameInputImg.color = new Color(0.15f, 0.1f, 0.15f);
var nameInput = nameInputGO.AddComponent<TMP_InputField>();
nameInput.characterLimit = 20;
var msgLabel = CreateTMPChild("MessageLabel", panel.transform, "Tu mensaje:", 18f);
msgLabel.color = new Color(0.8f, 0.8f, 0.8f);
AnchorNorm(msgLabel.rectTransform, 0.08f, 0.48f, 0.92f, 0.56f);
var msgInputGO = new GameObject("MessageInputField");
msgInputGO.transform.SetParent(panel.transform, false);
var msgInputRT = msgInputGO.AddComponent<RectTransform>();
AnchorNorm(msgInputRT, 0.08f, 0.28f, 0.92f, 0.48f);
var msgInputImg = msgInputGO.AddComponent<Image>();
msgInputImg.color = new Color(0.15f, 0.1f, 0.15f);
var msgInput = msgInputGO.AddComponent<TMP_InputField>();
msgInput.characterLimit = 100;
var submitGO = CreateButton("SubmitButton", panel.transform, "INSCRIBIR");
submitGO.GetComponent<Image>().color = new Color(0.35f, 0.08f, 0.08f);
AnchorNorm(submitGO.GetComponent<RectTransform>(), 0.25f, 0.06f, 0.75f, 0.18f);
var feedbackTMP = CreateTMPChild("FeedbackText", panel.transform, "", 20f);
feedbackTMP.color = new Color(0.3f, 0.9f, 0.3f);
feedbackTMP.alignment = TextAlignmentOptions.Center;
AnchorNorm(feedbackTMP.rectTransform, 0.08f, 0.20f, 0.92f, 0.28f);
panel.SetActive(false);
var comp = panel.AddComponent<DeadKingInputUI>();
var so = new SerializedObject(comp);
SetObjProp(so, "_panel", panel);
SetObjProp(so, "_nameInputField", nameInput);
SetObjProp(so, "_messageInputField", msgInput);
SetObjProp(so, "_submitButton", submitGO.GetComponent<Button>());
SetObjProp(so, "_feedbackText", feedbackTMP);
so.ApplyModifiedPropertiesWithoutUndo();
Debug.Log(" ✅ Created DeadKingInputUI");
}
// ══════════════════════════════════════════════════════
// CLEANUP
// ══════════════════════════════════════════════════════
static void CleanupGarbage()
{
// Remove garbage SquareClick objects at wrong positions
var allGOs = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
int cleaned = 0;
foreach (var go in allGOs)
{
if (go == null) continue;
// Remove objects far from origin that look like garbage
if (go.transform.position.magnitude > 20f && go.GetComponent<BoardManager>() == null)
{
if (go.name.Contains("Square") || go.name.Contains("Click") || go.name.Contains("square"))
{
Debug.Log($" 🗑 Cleaned garbage: {go.name} at {go.transform.position}");
Object.DestroyImmediate(go);
cleaned++;
}
}
}
if (cleaned > 0)
Debug.Log($" ✅ Cleaned {cleaned} garbage objects");
}
// ══════════════════════════════════════════════════════
// HELPERS
// ══════════════════════════════════════════════════════
static GameObject FindOrCreateCanvas(string name, int sortOrder)
{
var existing = GameObject.Find(name);
if (existing != null) return existing;
var go = new GameObject(name);
var canvas = go.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = sortOrder;
var scaler = go.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
go.AddComponent<GraphicRaycaster>();
return go;
}
static GameObject CreatePanel(string name, Transform parent, Vector2 size, Color color)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = color;
var rt = go.GetComponent<RectTransform>();
rt.anchorMin = rt.anchorMax = rt.pivot = new Vector2(0.5f, 0.5f);
rt.sizeDelta = size;
rt.anchoredPosition = Vector2.zero;
return go;
}
static TextMeshProUGUI CreateTMPChild(string name, Transform parent, string text, float size)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var tmp = go.AddComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = size;
tmp.color = Color.white;
tmp.enableWordWrapping = true;
return tmp;
}
static GameObject CreateButton(string name, Transform parent, string label)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var img = go.AddComponent<Image>();
img.color = new Color(0.22f, 0.12f, 0.08f);
var btn = go.AddComponent<Button>();
var col = btn.colors;
col.highlightedColor = new Color(0.45f, 0.3f, 0.18f);
col.pressedColor = new Color(0.12f, 0.06f, 0.03f);
btn.colors = col;
var tGO = new GameObject("Text (TMP)");
tGO.transform.SetParent(go.transform, false);
var tmp = tGO.AddComponent<TextMeshProUGUI>();
tmp.text = label;
tmp.fontSize = 26f;
tmp.fontStyle = FontStyles.Bold;
tmp.color = Color.white;
tmp.alignment = TextAlignmentOptions.Center;
var tRT = tGO.GetComponent<RectTransform>();
tRT.anchorMin = Vector2.zero;
tRT.anchorMax = Vector2.one;
tRT.offsetMin = tRT.offsetMax = Vector2.zero;
return go;
}
static GameObject CreateContainer(string name, Transform parent)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var rt = go.AddComponent<RectTransform>();
var hl = go.AddComponent<HorizontalLayoutGroup>();
hl.spacing = 4f;
hl.childAlignment = TextAnchor.MiddleLeft;
hl.childForceExpandWidth = false;
hl.childForceExpandHeight = false;
return go;
}
static void AnchorNorm(RectTransform rt, float xMin, float yMin, float xMax, float yMax)
{
rt.anchorMin = new Vector2(xMin, yMin);
rt.anchorMax = new Vector2(xMax, yMax);
rt.offsetMin = rt.offsetMax = Vector2.zero;
}
static void SetObjProp(SerializedObject so, string field, Object val)
{
var p = so.FindProperty(field);
if (p != null)
p.objectReferenceValue = val;
else
Debug.LogWarning($" ⚠ Property '{field}' not found on {so.targetObject?.GetType().Name}");
}
static void SetAssetProp(SerializedObject so, string field, Object asset)
{
if (asset == null) return;
var p = so.FindProperty(field);
if (p != null)
p.objectReferenceValue = asset;
}
}