using System.Collections.Generic; using System.IO; using TMPro; using UnityEditor; using UnityEngine; using UnityEngine.AI; using UnityEngine.UI; using WarGame.Data; using WarGame.Deployment; using WarGame.Managers; using WarGame.UI; using WarGame.Units; namespace WarGame.EditorTools { [InitializeOnLoad] public static class AutoBattlerSetupHelper { private const string DataPath = "Assets/Data/Units"; private const string PrefabPath = "Assets/Prefabs/Units"; private const string UIPath = "Assets/Prefabs/UI"; static AutoBattlerSetupHelper() { EditorApplication.delayCall += CheckInitialSetup; } private static void CheckInitialSetup() { if (!File.Exists("Assets/Prefabs/Units/PF_BaseUnit.prefab")) { SetupBatch(); } } [MenuItem("Tools/WarGame/Setup Complete AutoBattler (Assets + Scene)", false, 1)] public static void SetupCompleteAutoBattler() { ExecuteSetup(isInteractive: true); } public static void SetupBatch() { ExecuteSetup(isInteractive: false); } public static void ExecuteSetup(bool isInteractive) { EnsureDirectories(); // 1. Create Unit Prefabs GameObject unitBasePrefab = CreateOrUpdateUnitPrefab(); GameObject cardPrefab = CreateOrUpdateShopCardPrefab(); // 2. Create UnitData ScriptableObjects List playerUnits = CreatePlayerUnitData(unitBasePrefab); List enemyUnits = CreateEnemyUnitData(unitBasePrefab); // 3. Setup Scene Hierarchy SetupSceneHierarchy(playerUnits, enemyUnits, cardPrefab, unitBasePrefab); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); if (isInteractive) { EditorUtility.DisplayDialog( "WarGame Setup Complete", "Successfully generated all ScriptableObjects, Prefabs, UI, and Scene Managers!\n\n" + "• Player Units: Swordsman, Archer, Knight\n" + "• Enemy Types: Bandit Warrior, Bandit Archer\n" + "• Advanced Tactics: Flanking (Lv.2), Feint Retreat (Lv.3)\n" + "• HUD & Shop: Live Unit Counts, Economy, Battle Results\n\n" + "You can now press Play to test recruitment, leveling, and auto-battles!", "Awesome!" ); } else { Debug.Log("[WarGame] AutoBattler setup completed successfully."); } } private static void EnsureDirectories() { if (!Directory.Exists(DataPath)) Directory.CreateDirectory(DataPath); if (!Directory.Exists(PrefabPath)) Directory.CreateDirectory(PrefabPath); if (!Directory.Exists(UIPath)) Directory.CreateDirectory(UIPath); AssetDatabase.Refresh(); } private static GameObject CreateOrUpdateUnitPrefab() { string prefabFilePath = $"{PrefabPath}/PF_BaseUnit.prefab"; GameObject existing = AssetDatabase.LoadAssetAtPath(prefabFilePath); if (existing != null) return existing; // Create temporary instance GameObject root = new GameObject("PF_BaseUnit"); // Add Collider & NavMeshAgent CapsuleCollider collider = root.AddComponent(); collider.center = new Vector3(0, 1f, 0); collider.radius = 0.5f; collider.height = 2f; NavMeshAgent agent = root.AddComponent(); agent.speed = 3.5f; agent.angularSpeed = 360f; agent.acceleration = 12f; agent.stoppingDistance = 1.2f; agent.radius = 0.5f; agent.height = 2f; // Visual mesh (Capsule) GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Capsule); visual.name = "Visual"; visual.transform.SetParent(root.transform); visual.transform.localPosition = new Vector3(0, 1f, 0); visual.transform.localScale = new Vector3(0.9f, 0.9f, 0.9f); Object.DestroyImmediate(visual.GetComponent()); // remove inner collider Renderer renderer = visual.GetComponent(); // Forward direction indicator (Nose / visor) GameObject visor = GameObject.CreatePrimitive(PrimitiveType.Cube); visor.name = "Visor"; visor.transform.SetParent(visual.transform); visor.transform.localPosition = new Vector3(0, 0.4f, 0.45f); visor.transform.localScale = new Vector3(0.5f, 0.2f, 0.3f); Object.DestroyImmediate(visor.GetComponent()); // Health Bar World Canvas GameObject healthBarObj = new GameObject("HealthBar_Canvas"); healthBarObj.transform.SetParent(root.transform); healthBarObj.transform.localPosition = new Vector3(0, 2.3f, 0); Canvas canvas = healthBarObj.AddComponent(); canvas.renderMode = RenderMode.WorldSpace; RectTransform canvasRect = canvas.GetComponent(); canvasRect.sizeDelta = new Vector2(1.5f, 0.6f); healthBarObj.transform.localScale = new Vector3(0.015f, 0.015f, 0.015f); // Slider Background GameObject sliderObj = new GameObject("HealthSlider"); sliderObj.transform.SetParent(healthBarObj.transform); RectTransform sliderRect = sliderObj.AddComponent(); sliderRect.sizeDelta = new Vector2(100f, 15f); sliderRect.anchoredPosition = new Vector2(0, 5f); Slider slider = sliderObj.AddComponent(); // Slider Background Image GameObject bgObj = new GameObject("Background"); bgObj.transform.SetParent(sliderObj.transform); RectTransform bgRect = bgObj.AddComponent(); bgRect.anchorMin = Vector2.zero; bgRect.anchorMax = Vector2.one; bgRect.sizeDelta = Vector2.zero; Image bgImg = bgObj.AddComponent(); bgImg.color = new Color(0.15f, 0.15f, 0.15f, 0.85f); // Fill Area & Fill GameObject fillArea = new GameObject("Fill Area"); fillArea.transform.SetParent(sliderObj.transform); RectTransform fillAreaRect = fillArea.AddComponent(); fillAreaRect.anchorMin = Vector2.zero; fillAreaRect.anchorMax = Vector2.one; fillAreaRect.sizeDelta = Vector2.zero; GameObject fillObj = new GameObject("Fill"); fillObj.transform.SetParent(fillArea.transform); RectTransform fillRect = fillObj.AddComponent(); fillRect.anchorMin = Vector2.zero; fillRect.anchorMax = Vector2.one; fillRect.sizeDelta = Vector2.zero; Image fillImg = fillObj.AddComponent(); fillImg.color = new Color(0.2f, 0.8f, 0.2f); slider.targetGraphic = fillImg; slider.fillRect = fillRect; slider.value = 1f; // Level Text GameObject levelTextObj = new GameObject("LevelText"); levelTextObj.transform.SetParent(healthBarObj.transform); RectTransform levelRect = levelTextObj.AddComponent(); levelRect.anchoredPosition = new Vector2(-60f, 5f); levelRect.sizeDelta = new Vector2(50f, 20f); TextMeshProUGUI levelText = levelTextObj.AddComponent(); levelText.text = "Lv.1"; levelText.fontSize = 18; levelText.alignment = TextAlignmentOptions.Center; levelText.color = Color.yellow; // Unit Name Text GameObject nameTextObj = new GameObject("NameText"); nameTextObj.transform.SetParent(healthBarObj.transform); RectTransform nameRect = nameTextObj.AddComponent(); nameRect.anchoredPosition = new Vector2(0, 25f); nameRect.sizeDelta = new Vector2(150f, 20f); TextMeshProUGUI nameText = nameTextObj.AddComponent(); nameText.text = "Unit"; nameText.fontSize = 16; nameText.alignment = TextAlignmentOptions.Center; nameText.color = Color.white; // Tactic Status Text GameObject tacticTextObj = new GameObject("TacticText"); tacticTextObj.transform.SetParent(healthBarObj.transform); RectTransform tacticRect = tacticTextObj.AddComponent(); tacticRect.anchoredPosition = new Vector2(0, 48f); tacticRect.sizeDelta = new Vector2(180f, 22f); TextMeshProUGUI tacticText = tacticTextObj.AddComponent(); tacticText.text = "FLANKING!"; tacticText.fontSize = 18; tacticText.fontStyle = FontStyles.Bold; tacticText.alignment = TextAlignmentOptions.Center; tacticText.color = Color.yellow; tacticTextObj.SetActive(false); // Setup UnitHealthBar component UnitHealthBar healthBarComp = healthBarObj.AddComponent(); SerializedObject hbSo = new SerializedObject(healthBarComp); hbSo.FindProperty("healthSlider").objectReferenceValue = slider; hbSo.FindProperty("fillImage").objectReferenceValue = fillImg; hbSo.FindProperty("levelText").objectReferenceValue = levelText; hbSo.FindProperty("tacticStatusText").objectReferenceValue = tacticText; hbSo.FindProperty("unitNameText").objectReferenceValue = nameText; hbSo.ApplyModifiedProperties(); // Setup Unit component Unit unitComp = root.AddComponent(); SerializedObject unitSo = new SerializedObject(unitComp); unitSo.FindProperty("agent").objectReferenceValue = agent; unitSo.FindProperty("healthBar").objectReferenceValue = healthBarComp; unitSo.FindProperty("mainRenderer").objectReferenceValue = renderer; unitSo.ApplyModifiedProperties(); // Save as Prefab GameObject prefabAsset = PrefabUtility.SaveAsPrefabAsset(root, prefabFilePath); Object.DestroyImmediate(root); return prefabAsset; } private static GameObject CreateOrUpdateShopCardPrefab() { string prefabFilePath = $"{UIPath}/PF_ShopCard.prefab"; GameObject existing = AssetDatabase.LoadAssetAtPath(prefabFilePath); if (existing != null) return existing; GameObject cardRoot = new GameObject("PF_ShopCard"); RectTransform rootRect = cardRoot.AddComponent(); rootRect.sizeDelta = new Vector2(230f, 130f); Image bg = cardRoot.AddComponent(); bg.color = new Color(0.12f, 0.15f, 0.2f, 0.95f); // Unit Name GameObject nameObj = new GameObject("UnitName"); nameObj.transform.SetParent(cardRoot.transform); RectTransform nameRect = nameObj.AddComponent(); nameRect.anchorMin = new Vector2(0f, 1f); nameRect.anchorMax = new Vector2(1f, 1f); nameRect.pivot = new Vector2(0.5f, 1f); nameRect.anchoredPosition = new Vector2(10f, -8f); nameRect.sizeDelta = new Vector2(-20f, 24f); TextMeshProUGUI nameText = nameObj.AddComponent(); nameText.text = "Swordsman"; nameText.fontSize = 18; nameText.fontStyle = FontStyles.Bold; nameText.color = Color.white; // Class Type GameObject classObj = new GameObject("ClassType"); classObj.transform.SetParent(cardRoot.transform); RectTransform classRect = classObj.AddComponent(); classRect.anchorMin = new Vector2(0f, 1f); classRect.anchorMax = new Vector2(1f, 1f); classRect.pivot = new Vector2(0.5f, 1f); classRect.anchoredPosition = new Vector2(10f, -32f); classRect.sizeDelta = new Vector2(-20f, 18f); TextMeshProUGUI classText = classObj.AddComponent(); classText.text = "Melee Infantry"; classText.fontSize = 13; classText.color = new Color(0.6f, 0.8f, 1f); // Stats Text GameObject statsObj = new GameObject("StatsText"); statsObj.transform.SetParent(cardRoot.transform); RectTransform statsRect = statsObj.AddComponent(); statsRect.anchorMin = new Vector2(0f, 1f); statsRect.anchorMax = new Vector2(1f, 1f); statsRect.pivot = new Vector2(0.5f, 1f); statsRect.anchoredPosition = new Vector2(10f, -52f); statsRect.sizeDelta = new Vector2(-20f, 18f); TextMeshProUGUI statsText = statsObj.AddComponent(); statsText.text = "HP: 120 | ATK: 18 | DEF: 3"; statsText.fontSize = 12; statsText.color = new Color(0.85f, 0.85f, 0.85f); // Tactics Text GameObject tacticsObj = new GameObject("TacticsText"); tacticsObj.transform.SetParent(cardRoot.transform); RectTransform tacticsRect = tacticsObj.AddComponent(); tacticsRect.anchorMin = new Vector2(0f, 1f); tacticsRect.anchorMax = new Vector2(1f, 1f); tacticsRect.pivot = new Vector2(0.5f, 1f); tacticsRect.anchoredPosition = new Vector2(10f, -72f); tacticsRect.sizeDelta = new Vector2(-20f, 18f); TextMeshProUGUI tacticsText = tacticsObj.AddComponent(); tacticsText.text = "Tactics: [Lv.2: Flanking] [Lv.3: Feint]"; tacticsText.fontSize = 11; tacticsText.color = new Color(1f, 0.85f, 0.3f); // Buy Button GameObject btnObj = new GameObject("BuyButton"); btnObj.transform.SetParent(cardRoot.transform); RectTransform btnRect = btnObj.AddComponent(); btnRect.anchorMin = new Vector2(0f, 0f); btnRect.anchorMax = new Vector2(1f, 0f); btnRect.pivot = new Vector2(0.5f, 0f); btnRect.anchoredPosition = new Vector2(0f, 8f); btnRect.sizeDelta = new Vector2(-20f, 28f); Image btnImg = btnObj.AddComponent(); btnImg.color = new Color(0.2f, 0.6f, 0.25f); Button btn = btnObj.AddComponent