using System.Collections.Generic; using System.IO; using OTGIntegrated.Abilities; using OTGIntegrated.Combat; using OTGIntegrated.Core; using OTGIntegrated.Crafting; using OTGIntegrated.Dialogue; using OTGIntegrated.Items; using OTGIntegrated.Player; using OTGIntegrated.Quests; using OTGIntegrated.Save; using OTGIntegrated.Stats; using OTGIntegrated.Talents; using OTGIntegrated.UI; using OTGIntegrated.Weapons; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; using PlayerInventory = OTGIntegrated.Inventory.Inventory; using InventoryUI = OTGIntegrated.Inventory.InventoryUI; public static class Lab8SceneBuilder { private const string ScenePath = "Assets/_Scenes/Lab08_IntegratedRPG.unity"; private static Font regularFont; private static Font boldFont; private static Dictionary materials; [MenuItem("Tools/Technical Game Design/Lab8/Create Integrated RPG Scene")] public static void CreateIntegratedRPGScene() { EnsureProjectFolders(); regularFont = InstallUbuntuFont("Ubuntu-Regular.ttf", false); boldFont = InstallUbuntuFont("Ubuntu-Bold.ttf", true); if (boldFont == null) { boldFont = regularFont; } materials = CreateMaterials(); DataSet data = CreateData(); Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); scene.name = "Lab08_IntegratedRPG"; CreateLighting(); CreateGroundAndZones(); Camera mainCamera = CreateCamera(); PlayerBundle player = CreatePlayer(mainCamera, data); CraftingSystem craftingSystem = CreateVillage(data); QuestManager questManager = CreateQuestManager(data); DialogueManager dialogueManager = CreateDialogueManager(); SaveSystem saveSystem = CreateSaveSystem(data); CreateResources(data); CreateGoblinCamp(data); CreateMagicCircle(); UIManager uiManager = CreateUI(player, questManager, craftingSystem, dialogueManager, saveSystem); GameManager gameManager = CreateGameManager(player, questManager, dialogueManager, craftingSystem, uiManager, saveSystem); EditorUtility.SetDirty(gameManager); EditorUtility.SetDirty(uiManager); EditorSceneManager.SaveScene(scene, ScenePath); AddSceneToBuildSettings(ScenePath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"Lab08 integrated RPG scene created: {ScenePath}"); } [MenuItem("Tools/Technical Game Design/Lab8/Validate Integrated RPG Scene")] public static void ValidateIntegratedRPGScene() { if (!File.Exists(ToSystemPath(ScenePath))) { throw new System.Exception($"Scene is missing: {ScenePath}"); } Scene scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); int missingScripts = CountMissingScripts(scene); if (missingScripts > 0) { throw new System.Exception($"Missing scripts found: {missingScripts}"); } Require(Object.FindObjectOfType() != null, "GameManager missing"); Require(Object.FindObjectOfType() != null, "PlayerController missing"); Require(Object.FindObjectOfType() != null, "Inventory missing"); Require(Object.FindObjectOfType() != null, "PlayerStats missing"); Require(Object.FindObjectOfType() != null, "QuestManager missing"); Require(Object.FindObjectOfType() != null, "DialogueManager missing"); Require(Object.FindObjectOfType() != null, "UIManager missing"); Require(Object.FindObjectOfType() != null, "SaveSystem missing"); Require(Object.FindObjectsOfType().Length >= 5, "Goblin enemies missing"); Require(Object.FindObjectsOfType().Length >= 10, "Resource nodes missing"); Require(Object.FindObjectOfType() != null, "EventSystem missing"); Font expectedFont = AssetDatabase.LoadAssetAtPath("Assets/_Fonts/Ubuntu-Regular.ttf"); if (expectedFont != null) { foreach (Text text in Resources.FindObjectsOfTypeAll()) { if (text.gameObject.scene == scene) { Require(text.font == expectedFont || text.font == AssetDatabase.LoadAssetAtPath("Assets/_Fonts/Ubuntu-Bold.ttf"), $"Text does not use Ubuntu font: {text.name}"); } } } Debug.Log("Lab08 validation passed: core scene objects, systems, enemies, resources, UI and fonts are present."); } private sealed class DataSet { public readonly List allItems = new List(); public ItemData wood; public ItemData stone; public ItemData ironOre; public ItemData herb; public ItemData crystal; public ItemData goblinFang; public ItemData basicSwordItem; public ItemData ironAxeItem; public ItemData healthPotion; public ItemData manaPotion; public WeaponData basicSwordWeapon; public WeaponData ironAxeWeapon; public RecipeData recipeIronAxe; public RecipeData recipeHealthPotion; public RecipeData recipeManaPotion; public QuestData questGatherWood; public QuestData questKillGoblins; public QuestData questCraftAxe; public DialogueNode elderDialogue; public DialogueNode guardDialogue; public DialogueNode blacksmithDialogue; public DialogueNode mageDialogue; public AbilityData fireball; public AbilityData heal; public AbilityData blink; public TalentData toughness; public TalentData strength; public TalentData agility; public TalentData arcaneFlow; public TalentData advancedStrength; } private sealed class PlayerBundle { public GameObject root; public PlayerController controller; public PlayerStats stats; public PlayerInventory inventory; public LevelSystem levelSystem; public WeaponController weaponController; public WeaponManager weaponManager; public AbilityManager abilityManager; public TalentManager talentManager; public PlayerInteraction interaction; } private static void EnsureProjectFolders() { string[] folders = { "Assets/_Scenes", "Assets/_Scripts", "Assets/_Scripts/Core", "Assets/_Scripts/Player", "Assets/_Scripts/Stats", "Assets/_Scripts/Inventory", "Assets/_Scripts/Items", "Assets/_Scripts/Crafting", "Assets/_Scripts/Dialogue", "Assets/_Scripts/Quests", "Assets/_Scripts/Combat", "Assets/_Scripts/Weapons", "Assets/_Scripts/Abilities", "Assets/_Scripts/Talents", "Assets/_Scripts/UI", "Assets/_Scripts/Save", "Assets/_Data", "Assets/_Data/Items", "Assets/_Data/Recipes", "Assets/_Data/Quests", "Assets/_Data/Dialogue", "Assets/_Data/Abilities", "Assets/_Data/Talents", "Assets/_Data/Weapons", "Assets/_Prefabs", "Assets/_Prefabs/UI", "Assets/_Prefabs/Characters", "Assets/_Prefabs/Items", "Assets/_Prefabs/Effects", "Assets/_Materials", "Assets/_Fonts", "Assets/Editor" }; for (int i = 0; i < folders.Length; i++) { Directory.CreateDirectory(folders[i]); } AssetDatabase.Refresh(); } private static Font InstallUbuntuFont(string destinationName, bool bold) { string destination = "Assets/_Fonts/" + destinationName; string[] candidates = bold ? new[] { "/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf", "/usr/share/fonts/truetype/ubuntu/Ubuntu-Bold.ttf", "/usr/share/fonts/TTF/Ubuntu-B.ttf", "/usr/share/fonts/TTF/Ubuntu-Bold.ttf" } : new[] { "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", "/usr/share/fonts/truetype/ubuntu/Ubuntu-Regular.ttf", "/usr/share/fonts/TTF/Ubuntu-R.ttf", "/usr/share/fonts/TTF/Ubuntu-Regular.ttf" }; string source = FindExistingFile(candidates); if (string.IsNullOrEmpty(source)) { source = FindFontRecursive(bold ? new[] { "Ubuntu-B.ttf", "Ubuntu-Bold.ttf" } : new[] { "Ubuntu-R.ttf", "Ubuntu-Regular.ttf" }); } if (string.IsNullOrEmpty(source)) { Debug.LogWarning("Ubuntu Font was not found. Arial fallback will be used."); return Resources.GetBuiltinResource("Arial.ttf"); } File.Copy(source, destination, true); AssetDatabase.ImportAsset(destination, ImportAssetOptions.ForceUpdate); Font font = AssetDatabase.LoadAssetAtPath(destination); return font != null ? font : Resources.GetBuiltinResource("Arial.ttf"); } private static string FindExistingFile(string[] candidates) { for (int i = 0; i < candidates.Length; i++) { if (File.Exists(candidates[i])) { return candidates[i]; } } return null; } private static string FindFontRecursive(string[] fileNames) { string[] roots = { "/usr/share/fonts", "/usr/share/fonts/truetype/ubuntu", "/usr/share/fonts/TTF" }; for (int r = 0; r < roots.Length; r++) { if (!Directory.Exists(roots[r])) { continue; } for (int f = 0; f < fileNames.Length; f++) { string[] matches = Directory.GetFiles(roots[r], fileNames[f], SearchOption.AllDirectories); if (matches.Length > 0) { return matches[0]; } } } return null; } private static Dictionary CreateMaterials() { return new Dictionary { ["Ground"] = CreateMaterial("Ground", new Color(0.28f, 0.46f, 0.25f, 1f)), ["Village"] = CreateMaterial("Village", new Color(0.54f, 0.48f, 0.38f, 1f)), ["Wood"] = CreateMaterial("Wood", new Color(0.45f, 0.28f, 0.13f, 1f)), ["Leaves"] = CreateMaterial("Leaves", new Color(0.18f, 0.48f, 0.18f, 1f)), ["Stone"] = CreateMaterial("Stone", new Color(0.42f, 0.43f, 0.45f, 1f)), ["Iron"] = CreateMaterial("Iron", new Color(0.42f, 0.48f, 0.52f, 1f)), ["Herb"] = CreateMaterial("Herb", new Color(0.1f, 0.62f, 0.24f, 1f)), ["Crystal"] = CreateMaterial("Crystal", new Color(0.18f, 0.84f, 0.95f, 1f), true), ["Player"] = CreateMaterial("Player", new Color(0.16f, 0.36f, 0.84f, 1f)), ["Elder"] = CreateMaterial("Elder", new Color(0.78f, 0.62f, 0.34f, 1f)), ["Guard"] = CreateMaterial("Guard", new Color(0.2f, 0.36f, 0.62f, 1f)), ["Blacksmith"] = CreateMaterial("Blacksmith", new Color(0.5f, 0.2f, 0.16f, 1f)), ["Mage"] = CreateMaterial("Mage", new Color(0.35f, 0.22f, 0.72f, 1f)), ["Goblin"] = CreateMaterial("Goblin", new Color(0.24f, 0.55f, 0.18f, 1f)), ["Camp"] = CreateMaterial("Camp", new Color(0.45f, 0.16f, 0.12f, 1f)), ["Fire"] = CreateMaterial("Fire", new Color(1f, 0.32f, 0.05f, 1f), true), ["Mana"] = CreateMaterial("Mana", new Color(0.36f, 0.25f, 0.95f, 1f), true), ["Workbench"] = CreateMaterial("Workbench", new Color(0.35f, 0.22f, 0.12f, 1f)) }; } private static Material CreateMaterial(string name, Color color, bool emission = false) { string path = $"Assets/_Materials/M_{name}.mat"; Material material = AssetDatabase.LoadAssetAtPath(path); if (material == null) { material = new Material(Shader.Find("Standard")); AssetDatabase.CreateAsset(material, path); } material.color = color; material.SetFloat("_Glossiness", emission ? 0.35f : 0.08f); if (emission) { material.EnableKeyword("_EMISSION"); material.SetColor("_EmissionColor", color * 1.5f); } else { material.DisableKeyword("_EMISSION"); } EditorUtility.SetDirty(material); return material; } private static DataSet CreateData() { DataSet data = new DataSet(); data.wood = CreateItem("Wood", "Wood", "Древесина для строительства и крафта.", 20, ItemType.Resource, new Color(0.55f, 0.33f, 0.16f, 1f)); data.stone = CreateItem("Stone", "Stone", "Камень из каменоломни.", 20, ItemType.Resource, new Color(0.52f, 0.53f, 0.55f, 1f)); data.ironOre = CreateItem("IronOre", "Iron Ore", "Руда для кузнечных рецептов.", 10, ItemType.Resource, new Color(0.42f, 0.48f, 0.52f, 1f)); data.herb = CreateItem("Herb", "Herb", "Лечебная трава.", 20, ItemType.Resource, new Color(0.1f, 0.65f, 0.28f, 1f)); data.crystal = CreateItem("Crystal", "Crystal", "Кристалл с магической энергией.", 10, ItemType.Resource, new Color(0.2f, 0.9f, 1f, 1f)); data.goblinFang = CreateItem("GoblinFang", "Goblin Fang", "Трофей с гоблина.", 20, ItemType.Quest, new Color(0.75f, 0.72f, 0.54f, 1f)); data.basicSwordItem = CreateItem("BasicSword", "Basic Sword", "Простой меч героя.", 1, ItemType.Weapon, new Color(0.72f, 0.78f, 0.84f, 1f)); data.ironAxeItem = CreateItem("IronAxe", "Iron Axe", "Тяжёлый железный топор.", 1, ItemType.Weapon, new Color(0.68f, 0.7f, 0.74f, 1f)); data.healthPotion = CreateItem("HealthPotion", "Health Potion", "Зелье восстановления здоровья.", 5, ItemType.Consumable, new Color(0.86f, 0.16f, 0.2f, 1f)); data.manaPotion = CreateItem("ManaPotion", "Mana Potion", "Зелье восстановления маны.", 5, ItemType.Consumable, new Color(0.16f, 0.32f, 0.9f, 1f)); data.allItems.AddRange(new[] { data.wood, data.stone, data.ironOre, data.herb, data.crystal, data.goblinFang, data.basicSwordItem, data.ironAxeItem, data.healthPotion, data.manaPotion }); data.basicSwordWeapon = CreateWeapon("BasicSword", "Basic Sword", 10f, 0.75f, 2.2f, false, 0f, "Базовое оружие ближнего боя."); data.ironAxeWeapon = CreateWeapon("IronAxe", "Iron Axe", 18f, 0.95f, 2.4f, false, 0f, "Скрафченный топор с повышенным уроном."); data.recipeIronAxe = CreateRecipe("Recipe_IronAxe", "Iron Axe", "Соберите рукоять и металлическую голову топора.", data.ironAxeItem, 1, IngredientOf(data.wood, 2), IngredientOf(data.stone, 2), IngredientOf(data.ironOre, 1)); data.recipeHealthPotion = CreateRecipe("Recipe_HealthPotion", "Health Potion", "Простое лечебное зелье.", data.healthPotion, 1, IngredientOf(data.herb, 2), IngredientOf(data.crystal, 1)); data.recipeManaPotion = CreateRecipe("Recipe_ManaPotion", "Mana Potion", "Зелье для восстановления магической энергии.", data.manaPotion, 1, IngredientOf(data.crystal, 2), IngredientOf(data.herb, 1)); data.questGatherWood = CreateQuest("Quest_GatherWood", "Помощь старосте", "Соберите 5 древесины для укрепления деревни.", "Elder", "Elder", 50, 1, new QuestGoal { goalType = QuestGoalType.CollectItem, targetItem = data.wood, requiredAmount = 5 }); data.questKillGoblins = CreateQuest("Quest_KillGoblins", "Очистка лагеря", "Уничтожьте 3 гоблинов возле деревни.", "Guard", "Guard", 100, 0, new QuestGoal { goalType = QuestGoalType.KillEnemy, enemyId = "Goblin", requiredAmount = 3 }); data.questKillGoblins.rewardItems = new List { new QuestRewardItem { item = data.ironOre, amount = 2 } }; EditorUtility.SetDirty(data.questKillGoblins); data.questCraftAxe = CreateQuest("Quest_CraftAxe", "Работа кузнеца", "Скрафтите железный топор.", "Blacksmith", "Blacksmith", 50, 0, new QuestGoal { goalType = QuestGoalType.CraftItem, targetItem = data.ironAxeItem, requiredAmount = 1 }); data.questCraftAxe.rewardItems = new List { new QuestRewardItem { item = data.healthPotion, amount = 1 } }; EditorUtility.SetDirty(data.questCraftAxe); data.elderDialogue = CreateDialogue("Dialogue_Elder", "Староста", "Деревня держится, но нам нужна древесина для укрепления стен. Поможешь?"); data.guardDialogue = CreateDialogue("Dialogue_Guard", "Стражник", "Гоблины снова заняли лагерь у дороги. Разберись с ними, когда будешь готов."); data.blacksmithDialogue = CreateDialogue("Dialogue_Blacksmith", "Кузнец", "Верстак рядом. Если принесёшь материалы, сможешь сделать железный топор."); data.mageDialogue = CreateDialogue("Dialogue_Mage", "Маг", "Способности привязаны к клавишам 1, 2 и 3. Таланты открываются на K и усиливают твои характеристики."); GameObject fireballPrefab = CreateFireballPrefab(); GameObject healPrefab = CreateHealPrefab(); GameObject blinkPrefab = CreateBlinkPrefab(); data.fireball = CreateAbility("Fireball", "Fireball", "Летящий огненный снаряд, наносящий 25 + Damage.", 15f, 2f, 25f, 14f, fireballPrefab, new Color(0.95f, 0.22f, 0.08f, 1f)); data.heal = CreateAbility("Heal", "Heal", "Восстанавливает 30 HP.", 20f, 5f, 30f, 0f, healPrefab, new Color(0.22f, 0.9f, 0.36f, 1f)); data.blink = CreateAbility("Blink", "Blink", "Телепорт вперёд с проверкой препятствий.", 20f, 6f, 0f, 7f, blinkPrefab, new Color(0.48f, 0.38f, 1f, 1f)); data.toughness = CreateTalent("Toughness", "Toughness", "MaxHealth +20", new Vector2(-330f, 130f), new[] { new StatModifier(StatType.MaxHealth, ModifierType.FlatAdd, 20f) }, null); data.strength = CreateTalent("Strength", "Strength", "Damage +5", new Vector2(-110f, 130f), new[] { new StatModifier(StatType.Damage, ModifierType.FlatAdd, 5f) }, null); data.agility = CreateTalent("Agility", "Agility", "MoveSpeed +1", new Vector2(110f, 130f), new[] { new StatModifier(StatType.MoveSpeed, ModifierType.FlatAdd, 1f) }, null); data.arcaneFlow = CreateTalent("ArcaneFlow", "Arcane Flow", "MaxMana +25", new Vector2(330f, 130f), new[] { new StatModifier(StatType.MaxMana, ModifierType.FlatAdd, 25f) }, null); data.advancedStrength = CreateTalent("AdvancedStrength", "Advanced Strength", "Damage +10%", new Vector2(-110f, -80f), new[] { new StatModifier(StatType.Damage, ModifierType.PercentAdd, 0.1f) }, new[] { data.strength }); return data; } private static ItemData CreateItem(string id, string displayName, string description, int maxStack, ItemType type, Color color) { ItemData item = LoadOrCreate($"Assets/_Data/Items/{id}.asset"); item.itemId = id; item.displayName = displayName; item.description = description; item.maxStack = maxStack; item.itemType = type; item.itemColor = color; item.icon = CreateIcon("Assets/_Data/Items", id + "_Icon", color); EditorUtility.SetDirty(item); return item; } private static WeaponData CreateWeapon(string id, string displayName, float damage, float rate, float range, bool ranged, float manaCost, string description) { WeaponData weapon = LoadOrCreate($"Assets/_Data/Weapons/{id}.asset"); weapon.weaponId = id; weapon.displayName = displayName; weapon.damage = damage; weapon.attackRate = rate; weapon.range = range; weapon.isRanged = ranged; weapon.manaCost = manaCost; weapon.description = description; EditorUtility.SetDirty(weapon); return weapon; } private static Ingredient IngredientOf(ItemData item, int amount) { return new Ingredient { item = item, amount = amount }; } private static RecipeData CreateRecipe(string id, string displayName, string description, ItemData result, int resultAmount, params Ingredient[] ingredients) { RecipeData recipe = LoadOrCreate($"Assets/_Data/Recipes/{id}.asset"); recipe.recipeId = id; recipe.displayName = displayName; recipe.description = description; recipe.resultItem = result; recipe.resultAmount = resultAmount; recipe.ingredients = ingredients; EditorUtility.SetDirty(recipe); return recipe; } private static QuestData CreateQuest(string id, string displayName, string description, string giverNpcId, string turnInNpcId, int rewardExp, int rewardTalentPoints, QuestGoal goal) { QuestData quest = LoadOrCreate($"Assets/_Data/Quests/{id}.asset"); quest.questId = id; quest.displayName = displayName; quest.description = description; quest.giverNpcId = giverNpcId; quest.turnInNpcId = turnInNpcId; quest.rewardExperience = rewardExp; quest.rewardTalentPoints = rewardTalentPoints; quest.goals = new List { goal }; quest.rewardItems = quest.rewardItems ?? new List(); EditorUtility.SetDirty(quest); return quest; } private static DialogueNode CreateDialogue(string id, string speaker, string line) { DialogueNode node = LoadOrCreate($"Assets/_Data/Dialogue/{id}.asset"); node.nodeId = id; node.speakerName = speaker; node.npcLine = line; node.responses = new[] { new DialogueResponse { responseText = "Расскажи подробнее", endsDialogue = false } }; EditorUtility.SetDirty(node); return node; } private static AbilityData CreateAbility(string id, string displayName, string description, float mana, float cooldown, float damage, float range, GameObject effectPrefab, Color color) { AbilityData ability = LoadOrCreate($"Assets/_Data/Abilities/{id}.asset"); ability.abilityId = id; ability.displayName = displayName; ability.description = description; ability.manaCost = mana; ability.cooldown = cooldown; ability.damage = damage; ability.range = range; ability.effectPrefab = effectPrefab; ability.icon = CreateIcon("Assets/_Data/Abilities", id + "_Icon", color); EditorUtility.SetDirty(ability); return ability; } private static TalentData CreateTalent(string id, string displayName, string description, Vector2 position, StatModifier[] modifiers, TalentData[] prerequisites) { TalentData talent = LoadOrCreate($"Assets/_Data/Talents/{id}.asset"); talent.talentId = id; talent.displayName = displayName; talent.description = description; talent.cost = 1; talent.treePosition = position; talent.modifiers = modifiers; talent.prerequisites = prerequisites; talent.icon = CreateIcon("Assets/_Data/Talents", id + "_Icon", new Color(0.82f, 0.64f, 0.22f, 1f)); EditorUtility.SetDirty(talent); return talent; } private static T LoadOrCreate(string path) where T : ScriptableObject { T asset = AssetDatabase.LoadAssetAtPath(path); if (asset == null) { asset = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(asset, path); } return asset; } private static Sprite CreateIcon(string folder, string name, Color color) { Directory.CreateDirectory(folder); string path = $"{folder}/{name}.png"; Texture2D texture = new Texture2D(64, 64, TextureFormat.RGBA32, false); Color dark = Color.Lerp(Color.black, color, 0.3f); for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { Vector2 p = new Vector2(x - 31.5f, y - 31.5f); float t = Mathf.Clamp01(1f - p.magnitude / 38f); Color pixel = Color.Lerp(dark, color, t); if (Mathf.Abs(p.x) < 4f || Mathf.Abs(p.y) < 4f) { pixel = Color.Lerp(pixel, Color.white, 0.3f); } texture.SetPixel(x, y, pixel); } } texture.Apply(); File.WriteAllBytes(path, texture.EncodeToPNG()); Object.DestroyImmediate(texture); AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); TextureImporter importer = AssetImporter.GetAtPath(path) as TextureImporter; if (importer != null) { importer.textureType = TextureImporterType.Sprite; importer.mipmapEnabled = false; importer.spritePixelsPerUnit = 64f; importer.SaveAndReimport(); } return AssetDatabase.LoadAssetAtPath(path); } private static GameObject CreateFireballPrefab() { GameObject root = new GameObject("PF_FireballEffect"); SphereCollider collider = root.AddComponent(); collider.radius = 0.35f; collider.isTrigger = true; root.AddComponent(); GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Sphere); visual.name = "Visual"; visual.transform.SetParent(root.transform, false); visual.transform.localScale = Vector3.one * 0.65f; Object.DestroyImmediate(visual.GetComponent()); visual.GetComponent().sharedMaterial = materials["Fire"]; return SavePrefab(root, "Assets/_Prefabs/Effects/PF_FireballEffect.prefab"); } private static GameObject CreateHealPrefab() { GameObject root = new GameObject("PF_HealEffect"); root.AddComponent(); return SavePrefab(root, "Assets/_Prefabs/Effects/PF_HealEffect.prefab"); } private static GameObject CreateBlinkPrefab() { GameObject root = new GameObject("PF_BlinkEffect"); root.AddComponent(); return SavePrefab(root, "Assets/_Prefabs/Effects/PF_BlinkEffect.prefab"); } private static GameObject SavePrefab(GameObject root, string path) { GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, path); Object.DestroyImmediate(root); return prefab; } private static void CreateLighting() { RenderSettings.ambientLight = new Color(0.55f, 0.58f, 0.62f, 1f); GameObject lightObject = new GameObject("Directional Light"); Light light = lightObject.AddComponent(); light.type = LightType.Directional; light.intensity = 1.05f; lightObject.transform.rotation = Quaternion.Euler(50f, -35f, 0f); } private static void CreateGroundAndZones() { GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.name = "Integrated RPG Village Ground"; ground.transform.localScale = new Vector3(8.5f, 1f, 8.5f); ground.GetComponent().sharedMaterial = materials["Ground"]; CreateZoneMarker("Central Village", new Vector3(0f, 0.03f, 0f), new Vector3(20f, 0.04f, 20f), materials["Village"]); CreateZoneMarker("Forest", new Vector3(-28f, 0.04f, 18f), new Vector3(24f, 0.04f, 24f), materials["Leaves"]); CreateZoneMarker("Quarry", new Vector3(28f, 0.04f, 18f), new Vector3(24f, 0.04f, 24f), materials["Stone"]); CreateZoneMarker("Goblin Camp", new Vector3(27f, 0.04f, -27f), new Vector3(24f, 0.04f, 24f), materials["Camp"]); CreateZoneMarker("Magic Circle", new Vector3(-28f, 0.04f, -26f), new Vector3(24f, 0.04f, 24f), materials["Mana"]); CreateSign("Forest", new Vector3(-11f, 0.1f, 8f)); CreateSign("Quarry", new Vector3(11f, 0.1f, 8f)); CreateSign("Goblin Camp", new Vector3(14f, 0.1f, -10f)); CreateSign("Magic Circle", new Vector3(-14f, 0.1f, -10f)); } private static void CreateZoneMarker(string name, Vector3 position, Vector3 scale, Material material) { GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Cube); marker.name = name + " Zone Tint"; marker.transform.position = position; marker.transform.localScale = scale; marker.GetComponent().sharedMaterial = material; Object.DestroyImmediate(marker.GetComponent()); } private static Camera CreateCamera() { GameObject cameraObject = new GameObject("Main Camera"); Camera camera = cameraObject.AddComponent(); camera.tag = "MainCamera"; camera.fieldOfView = 48f; camera.transform.position = new Vector3(0f, 14f, -14f); camera.transform.rotation = Quaternion.Euler(55f, 0f, 0f); return camera; } private static PlayerBundle CreatePlayer(Camera camera, DataSet data) { PlayerBundle bundle = new PlayerBundle(); GameObject player = GameObject.CreatePrimitive(PrimitiveType.Capsule); player.name = "Player"; player.tag = "Player"; player.transform.position = new Vector3(0f, 1f, -2f); player.GetComponent().sharedMaterial = materials["Player"]; CharacterController characterController = player.AddComponent(); characterController.height = 2f; characterController.radius = 0.42f; characterController.center = Vector3.zero; Object.DestroyImmediate(player.GetComponent()); bundle.root = player; bundle.stats = player.AddComponent(); bundle.inventory = player.AddComponent(); bundle.levelSystem = player.AddComponent(); bundle.weaponController = null; WeaponController weaponController = player.AddComponent(); bundle.weaponManager = player.AddComponent(); bundle.abilityManager = player.AddComponent(); bundle.talentManager = player.AddComponent(); bundle.controller = player.AddComponent(); bundle.interaction = player.AddComponent(); GameObject attackOrigin = new GameObject("AttackOrigin"); attackOrigin.transform.SetParent(player.transform, false); attackOrigin.transform.localPosition = new Vector3(0f, 0.6f, 0.85f); weaponController.attackOrigin = attackOrigin.transform; bundle.weaponController = weaponController; GameObject castOrigin = new GameObject("CastOrigin"); castOrigin.transform.SetParent(player.transform, false); castOrigin.transform.localPosition = new Vector3(0f, 1.25f, 0.75f); bundle.abilityManager.castOrigin = castOrigin.transform; bundle.abilityManager.learnedAbilities = new List { data.fireball, data.heal, data.blink }; GameObject cameraRoot = new GameObject("PlayerCameraRoot"); bundle.controller.cameraRoot = cameraRoot.transform; bundle.controller.playerCamera = camera; bundle.weaponManager.weaponController = weaponController; bundle.weaponManager.startingWeapon = data.basicSwordWeapon; bundle.weaponManager.availableWeapons = new List { data.basicSwordWeapon, data.ironAxeWeapon }; bundle.weaponManager.itemUnlocks = new List { new WeaponUnlock { item = data.ironAxeItem, weapon = data.ironAxeWeapon } }; bundle.talentManager.allTalents = new List { data.toughness, data.strength, data.agility, data.arcaneFlow, data.advancedStrength }; bundle.talentManager.availableTalentPoints = 0; return bundle; } private static CraftingSystem CreateVillage(DataSet data) { CreateNpc("NPC_Starosta", "Elder", "Староста", data.elderDialogue, data.questGatherWood, data.questGatherWood, false, new Vector3(-3f, 1f, 3f), materials["Elder"]); CreateNpc("NPC_Guard", "Guard", "Стражник", data.guardDialogue, data.questKillGoblins, data.questKillGoblins, false, new Vector3(3f, 1f, 3f), materials["Guard"]); CreateNpc("NPC_Blacksmith", "Blacksmith", "Кузнец", data.blacksmithDialogue, data.questCraftAxe, data.questCraftAxe, true, new Vector3(6f, 1f, -2f), materials["Blacksmith"]); CreateNpc("NPC_Mage", "Mage", "Маг", data.mageDialogue, null, null, false, new Vector3(-6f, 1f, -2f), materials["Mage"]); CreateBuilding("Elder House", new Vector3(-6f, 1f, 7f), new Vector3(4f, 2f, 3f)); CreateBuilding("Guard Tower", new Vector3(6f, 1.4f, 7f), new Vector3(2.8f, 2.8f, 2.8f)); CreateBuilding("Blacksmith Shed", new Vector3(9f, 1f, -5f), new Vector3(4f, 2f, 3f)); GameObject workbench = GameObject.CreatePrimitive(PrimitiveType.Cube); workbench.name = "Workbench"; workbench.transform.position = new Vector3(7f, 0.55f, -4f); workbench.transform.localScale = new Vector3(2.2f, 0.7f, 1.1f); workbench.GetComponent().sharedMaterial = materials["Workbench"]; CraftingSystem crafting = workbench.AddComponent(); crafting.availableRecipes = new List { data.recipeIronAxe, data.recipeHealthPotion, data.recipeManaPotion }; return crafting; } private static void CreateNpc(string objectName, string npcId, string displayName, DialogueNode dialogue, QuestData offered, QuestData completion, bool crafting, Vector3 position, Material material) { GameObject npc = GameObject.CreatePrimitive(PrimitiveType.Capsule); npc.name = objectName; npc.transform.position = position; npc.GetComponent().sharedMaterial = material; NPCInteract interact = npc.AddComponent(); interact.npcId = npcId; interact.displayName = displayName; interact.startNode = dialogue; interact.offeredQuest = offered; interact.completionQuest = completion; interact.canOpenCrafting = crafting; CreateNameplate(npc.transform, displayName, new Color(1f, 0.84f, 0.35f, 1f)); } private static void CreateBuilding(string name, Vector3 position, Vector3 scale) { GameObject building = GameObject.CreatePrimitive(PrimitiveType.Cube); building.name = name; building.transform.position = position; building.transform.localScale = scale; building.GetComponent().sharedMaterial = materials["Village"]; } private static QuestManager CreateQuestManager(DataSet data) { GameObject root = new GameObject("QuestManager"); QuestManager manager = root.AddComponent(); manager.allQuests = new List { data.questGatherWood, data.questKillGoblins, data.questCraftAxe }; return manager; } private static DialogueManager CreateDialogueManager() { GameObject root = new GameObject("DialogueManager"); return root.AddComponent(); } private static SaveSystem CreateSaveSystem(DataSet data) { GameObject root = new GameObject("SaveSystem"); SaveSystem saveSystem = root.AddComponent(); saveSystem.itemCatalog = data.allItems; return saveSystem; } private static void CreateResources(DataSet data) { for (int i = 0; i < 7; i++) { Vector3 pos = new Vector3(-34f + i * 2.5f, 0.55f, 15f + (i % 3) * 3f); CreateResource("Wood_Node_" + i, PrimitiveType.Cylinder, pos, new Vector3(0.8f, 0.7f, 0.8f), materials["Wood"], data.wood); CreateTree(new Vector3(pos.x + 1.2f, 0f, pos.z + 1.4f)); } for (int i = 0; i < 5; i++) { CreateResource("Stone_Node_" + i, PrimitiveType.Sphere, new Vector3(24f + i * 2.4f, 0.55f, 17f + (i % 2) * 3f), Vector3.one * 1.1f, materials["Stone"], data.stone); } for (int i = 0; i < 4; i++) { CreateResource("IronOre_Node_" + i, PrimitiveType.Cube, new Vector3(29f + i * 2.2f, 0.55f, 25f), Vector3.one * 1f, materials["Iron"], data.ironOre); } for (int i = 0; i < 5; i++) { CreateResource("Herb_Node_" + i, PrimitiveType.Capsule, new Vector3(-35f + i * 3f, 0.45f, 28f), new Vector3(0.45f, 0.45f, 0.45f), materials["Herb"], data.herb); } for (int i = 0; i < 5; i++) { CreateResource("Crystal_Node_" + i, PrimitiveType.Cylinder, new Vector3(-34f + i * 3f, 0.75f, -29f), new Vector3(0.6f, 1.2f, 0.6f), materials["Crystal"], data.crystal); } for (int i = 0; i < 9; i++) { CreatePrimitive("Quarry Rock " + i, PrimitiveType.Cube, new Vector3(22f + (i % 3) * 4f, 0.5f, 21f + (i / 3) * 3f), new Vector3(2f, 1f, 1.4f), materials["Stone"]); } } private static void CreateResource(string name, PrimitiveType primitive, Vector3 position, Vector3 scale, Material material, ItemData item) { GameObject resource = GameObject.CreatePrimitive(primitive); resource.name = name; resource.transform.position = position; resource.transform.localScale = scale; resource.GetComponent().sharedMaterial = material; ResourceNode node = resource.AddComponent(); node.itemData = item; node.amount = 1; node.respawnCooldown = 0f; } private static void CreateGoblinCamp(DataSet data) { Vector3[] positions = { new Vector3(22f, 1f, -24f), new Vector3(28f, 1f, -23f), new Vector3(33f, 1f, -28f), new Vector3(24f, 1f, -32f), new Vector3(31f, 1f, -34f) }; for (int i = 0; i < positions.Length; i++) { GameObject goblin = GameObject.CreatePrimitive(PrimitiveType.Capsule); goblin.name = "Goblin_" + (i + 1); goblin.transform.position = positions[i]; goblin.GetComponent().sharedMaterial = materials["Goblin"]; Enemy enemy = goblin.AddComponent(); enemy.enemyId = "Goblin"; enemy.displayName = "Goblin"; enemy.maxHealth = 50f; enemy.damage = 5f; enemy.expReward = 20; enemy.dropItems = new List { new EnemyDrop { item = data.goblinFang, amount = 1 } }; CreateNameplate(goblin.transform, "Goblin", new Color(1f, 0.25f, 0.22f, 1f)); } CreatePrimitive("Campfire", PrimitiveType.Cylinder, new Vector3(28f, 0.25f, -29f), new Vector3(1.2f, 0.3f, 1.2f), materials["Fire"]); for (int i = 0; i < 6; i++) { CreatePrimitive("Camp Crate " + i, PrimitiveType.Cube, new Vector3(20f + i * 2.4f, 0.45f, -36f), Vector3.one * 0.9f, materials["Wood"]); } } private static void CreateMagicCircle() { GameObject circle = GameObject.CreatePrimitive(PrimitiveType.Cylinder); circle.name = "Magic Circle"; circle.transform.position = new Vector3(-28f, 0.08f, -26f); circle.transform.localScale = new Vector3(5.5f, 0.08f, 5.5f); circle.GetComponent().sharedMaterial = materials["Mana"]; Object.DestroyImmediate(circle.GetComponent()); for (int i = 0; i < 6; i++) { float angle = i * Mathf.PI * 2f / 6f; Vector3 pos = new Vector3(-28f + Mathf.Cos(angle) * 5f, 0.75f, -26f + Mathf.Sin(angle) * 5f); CreatePrimitive("Mana Crystal " + i, PrimitiveType.Cylinder, pos, new Vector3(0.5f, 1.3f, 0.5f), materials["Crystal"]); } for (int i = 0; i < 3; i++) { GameObject dummy = GameObject.CreatePrimitive(PrimitiveType.Capsule); dummy.name = "Training Dummy " + (i + 1); dummy.transform.position = new Vector3(-34f + i * 5f, 1f, -19f); dummy.GetComponent().sharedMaterial = materials["Wood"]; Damageable damageable = dummy.AddComponent(); damageable.displayName = "Training Dummy"; damageable.maxHealth = 100f; } } private static UIManager CreateUI(PlayerBundle player, QuestManager questManager, CraftingSystem craftingSystem, DialogueManager dialogueManager, SaveSystem saveSystem) { GameObject canvasObject = new GameObject("HUDCanvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); Canvas canvas = canvasObject.GetComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; CanvasScaler scaler = canvasObject.GetComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920f, 1080f); scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight; scaler.matchWidthOrHeight = 0.5f; EnsureEventSystem(); UIManager uiManager = canvasObject.AddComponent(); NotificationUI notificationUI = CreateNotificationStack(canvasObject.transform); InteractionPromptUI promptUI = CreateInteractionPrompt(canvasObject.transform); player.interaction.promptUI = promptUI; HUDController hud = CreateHud(canvasObject.transform); InventoryUI inventoryUI = CreateInventoryPanel(canvasObject.transform, out GameObject inventoryPanel); CraftingUI craftingUI = CreateCraftingPanel(canvasObject.transform, out GameObject craftingPanel); QuestLogUI questLogUI = CreateQuestLogPanel(canvasObject.transform, out GameObject questPanel); TalentTreeUI talentTreeUI = CreateTalentTreePanel(canvasObject.transform, out GameObject talentPanel); DialogueUI dialogueUI = CreateDialoguePanel(canvasObject.transform, out GameObject dialoguePanel); GameObject pausePanel = CreatePausePanel(canvasObject.transform, out Button resume, out Button saveButton, out Button loadButton, out Button resetButton); uiManager.inventoryPanel = inventoryPanel; uiManager.craftingPanel = craftingPanel; uiManager.questLogPanel = questPanel; uiManager.talentTreePanel = talentPanel; uiManager.dialoguePanel = dialoguePanel; uiManager.pausePanel = pausePanel; uiManager.inventoryUI = inventoryUI; uiManager.craftingUI = craftingUI; uiManager.questLogUI = questLogUI; uiManager.talentTreeUI = talentTreeUI; uiManager.hudController = hud; uiManager.resumeButton = resume; uiManager.saveButton = saveButton; uiManager.loadButton = loadButton; uiManager.resetSaveButton = resetButton; dialogueManager.dialogueUI = dialogueUI; inventoryPanel.SetActive(false); craftingPanel.SetActive(false); questPanel.SetActive(false); talentPanel.SetActive(false); dialoguePanel.SetActive(false); pausePanel.SetActive(false); return uiManager; } private static HUDController CreateHud(Transform canvas) { GameObject hudObject = new GameObject("HUDController"); hudObject.transform.SetParent(canvas, false); HUDController hud = hudObject.AddComponent(); GameObject status = CreatePanel(canvas, "Player Status Panel", new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(24f, 24f), new Vector2(320f, 130f), UITheme.Panel); hud.hpFill = CreateBar(status.transform, "HP", new Vector2(18f, 78f), new Color(0.8f, 0.15f, 0.14f, 1f), out hud.hpText); hud.manaFill = CreateBar(status.transform, "Mana", new Vector2(18f, 44f), new Color(0.15f, 0.35f, 0.88f, 1f), out hud.manaText); hud.expFill = CreateBar(status.transform, "EXP", new Vector2(18f, 10f), new Color(0.86f, 0.58f, 0.24f, 1f), out hud.expText); hud.levelText = CreateText(status.transform, "LevelText", "Level 1", 16, TextAnchor.MiddleLeft, new Vector2(18f, 104f), new Vector2(130f, 22f), boldFont); GameObject tracker = CreatePanel(canvas, "Quest Tracker", new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -24f), new Vector2(420f, 140f), UITheme.Panel); hud.questTitleText = CreateText(tracker.transform, "QuestTitle", "Активная цель", 20, TextAnchor.UpperLeft, new Vector2(18f, -14f), new Vector2(380f, 28f), boldFont); hud.questBodyText = CreateText(tracker.transform, "QuestBody", "Нет активных квестов", 16, TextAnchor.UpperLeft, new Vector2(18f, -48f), new Vector2(380f, 82f), regularFont); GameObject abilityBar = CreatePanel(canvas, "Ability Bar", new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 24f), new Vector2(340f, 95f), UITheme.Panel); hud.abilitySlots = new AbilityHudSlot[3]; for (int i = 0; i < 3; i++) { hud.abilitySlots[i] = CreateAbilitySlot(abilityBar.transform, i, new Vector2(-108f + i * 108f, 8f)); } GameObject weaponPanel = CreatePanel(canvas, "Weapon Panel", new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-24f, 24f), new Vector2(260f, 90f), UITheme.Panel); hud.weaponNameText = CreateText(weaponPanel.transform, "WeaponName", "Weapon: -", 16, TextAnchor.UpperLeft, new Vector2(16f, -12f), new Vector2(230f, 24f), boldFont); hud.weaponDamageText = CreateText(weaponPanel.transform, "WeaponDamage", "Damage: -", 16, TextAnchor.UpperLeft, new Vector2(16f, -38f), new Vector2(230f, 22f), regularFont); hud.weaponHintText = CreateText(weaponPanel.transform, "WeaponHint", "LMB — Attack", 14, TextAnchor.UpperLeft, new Vector2(16f, -62f), new Vector2(230f, 20f), regularFont); return hud; } private static Image CreateBar(Transform parent, string name, Vector2 anchored, Color fillColor, out Text valueText) { GameObject root = new GameObject(name + "Bar", typeof(RectTransform), typeof(Image)); root.transform.SetParent(parent, false); RectTransform rect = root.GetComponent(); rect.anchorMin = new Vector2(0f, 0f); rect.anchorMax = new Vector2(0f, 0f); rect.pivot = new Vector2(0f, 0f); rect.anchoredPosition = anchored; rect.sizeDelta = new Vector2(284f, 22f); root.GetComponent().color = new Color(0.03f, 0.035f, 0.045f, 0.9f); GameObject fillObject = new GameObject("Fill", typeof(RectTransform), typeof(Image)); fillObject.transform.SetParent(root.transform, false); RectTransform fillRect = fillObject.GetComponent(); fillRect.anchorMin = Vector2.zero; fillRect.anchorMax = Vector2.one; fillRect.offsetMin = Vector2.zero; fillRect.offsetMax = Vector2.zero; Image fill = fillObject.GetComponent(); fill.color = fillColor; fill.type = Image.Type.Filled; fill.fillMethod = Image.FillMethod.Horizontal; fill.fillOrigin = 0; fill.fillAmount = 1f; valueText = CreateText(root.transform, name + "Text", name, 14, TextAnchor.MiddleCenter, Vector2.zero, new Vector2(284f, 22f), regularFont); valueText.rectTransform.anchorMin = Vector2.zero; valueText.rectTransform.anchorMax = Vector2.one; valueText.rectTransform.offsetMin = Vector2.zero; valueText.rectTransform.offsetMax = Vector2.zero; return fill; } private static AbilityHudSlot CreateAbilitySlot(Transform parent, int index, Vector2 anchored) { GameObject root = CreatePanel(parent, "AbilitySlot_" + (index + 1), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), anchored, new Vector2(82f, 78f), new Color(0.08f, 0.09f, 0.12f, 0.92f)); AbilityHudSlot slot = new AbilityHudSlot(); slot.iconImage = root.GetComponent(); slot.keyText = CreateText(root.transform, "Key", (index + 1).ToString(), 14, TextAnchor.UpperLeft, new Vector2(7f, -4f), new Vector2(24f, 22f), boldFont); slot.nameText = CreateText(root.transform, "Name", "-", 13, TextAnchor.LowerCenter, new Vector2(0f, 2f), new Vector2(78f, 22f), regularFont); slot.manaText = CreateText(root.transform, "Mana", "-", 13, TextAnchor.UpperRight, new Vector2(-7f, -4f), new Vector2(34f, 22f), regularFont); GameObject overlayObject = new GameObject("CooldownOverlay", typeof(RectTransform), typeof(Image)); overlayObject.transform.SetParent(root.transform, false); RectTransform overlayRect = overlayObject.GetComponent(); overlayRect.anchorMin = Vector2.zero; overlayRect.anchorMax = Vector2.one; overlayRect.offsetMin = Vector2.zero; overlayRect.offsetMax = Vector2.zero; slot.cooldownOverlay = overlayObject.GetComponent(); slot.cooldownOverlay.color = new Color(0f, 0f, 0f, 0.58f); slot.cooldownOverlay.type = Image.Type.Filled; slot.cooldownOverlay.fillMethod = Image.FillMethod.Radial360; slot.cooldownOverlay.fillAmount = 0f; slot.cooldownText = CreateText(root.transform, "CooldownText", string.Empty, 18, TextAnchor.MiddleCenter, Vector2.zero, new Vector2(82f, 78f), boldFont); slot.cooldownText.rectTransform.anchorMin = Vector2.zero; slot.cooldownText.rectTransform.anchorMax = Vector2.one; slot.cooldownText.rectTransform.offsetMin = Vector2.zero; slot.cooldownText.rectTransform.offsetMax = Vector2.zero; return slot; } private static NotificationUI CreateNotificationStack(Transform canvas) { GameObject root = new GameObject("Notification Stack", typeof(RectTransform), typeof(VerticalLayoutGroup)); root.transform.SetParent(canvas, false); RectTransform rect = root.GetComponent(); rect.anchorMin = new Vector2(0.5f, 1f); rect.anchorMax = new Vector2(0.5f, 1f); rect.pivot = new Vector2(0.5f, 1f); rect.anchoredPosition = new Vector2(0f, -28f); rect.sizeDelta = new Vector2(540f, 260f); VerticalLayoutGroup layout = root.GetComponent(); layout.spacing = 8f; layout.childAlignment = TextAnchor.UpperCenter; layout.childControlWidth = false; layout.childControlHeight = false; NotificationUI notificationUI = root.AddComponent(); notificationUI.stackRoot = rect; notificationUI.uiFont = regularFont; return notificationUI; } private static InteractionPromptUI CreateInteractionPrompt(Transform canvas) { GameObject root = CreatePanel(canvas, "Interaction Prompt", new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 136f), new Vector2(520f, 42f), new Color(0.03f, 0.04f, 0.06f, 0.86f)); Text text = CreateText(root.transform, "PromptText", "E — взаимодействие", 17, TextAnchor.MiddleCenter, Vector2.zero, new Vector2(520f, 42f), boldFont); text.rectTransform.anchorMin = Vector2.zero; text.rectTransform.anchorMax = Vector2.one; text.rectTransform.offsetMin = Vector2.zero; text.rectTransform.offsetMax = Vector2.zero; InteractionPromptUI prompt = root.AddComponent(); prompt.root = root; prompt.promptText = text; root.SetActive(false); return prompt; } private static InventoryUI CreateInventoryPanel(Transform canvas, out GameObject panel) { panel = CreatePanel(canvas, "InventoryPanel", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(900f, 620f), UITheme.Panel); InventoryUI ui = panel.AddComponent(); ui.panelRoot = panel; ui.uiFont = regularFont; ui.titleText = CreateText(panel.transform, "Title", "Инвентарь", 30, TextAnchor.UpperLeft, new Vector2(28f, -22f), new Vector2(280f, 42f), boldFont); GameObject grid = new GameObject("SlotGrid", typeof(RectTransform), typeof(GridLayoutGroup)); grid.transform.SetParent(panel.transform, false); RectTransform gridRect = grid.GetComponent(); gridRect.anchorMin = new Vector2(0f, 1f); gridRect.anchorMax = new Vector2(0f, 1f); gridRect.pivot = new Vector2(0f, 1f); gridRect.anchoredPosition = new Vector2(30f, -92f); gridRect.sizeDelta = new Vector2(500f, 390f); GridLayoutGroup layout = grid.GetComponent(); layout.cellSize = new Vector2(90f, 90f); layout.spacing = new Vector2(10f, 10f); layout.constraint = GridLayoutGroup.Constraint.FixedColumnCount; layout.constraintCount = 5; ui.slotGrid = grid.transform; ui.descriptionText = CreateText(panel.transform, "Description", "Выберите предмет.", 17, TextAnchor.UpperLeft, new Vector2(560f, -96f), new Vector2(300f, 360f), regularFont); ui.hintText = CreateText(panel.transform, "Hint", "I — закрыть", 15, TextAnchor.LowerLeft, new Vector2(30f, 22f), new Vector2(500f, 28f), regularFont); ui.closeButton = CreateButton(panel.transform, "CloseButton", "Закрыть", new Vector2(760f, -560f), new Vector2(110f, 38f)); return ui; } private static CraftingUI CreateCraftingPanel(Transform canvas, out GameObject panel) { panel = CreatePanel(canvas, "CraftingPanel", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1050f, 650f), UITheme.Panel); CraftingUI ui = panel.AddComponent(); ui.panelRoot = panel; ui.uiFont = regularFont; CreateText(panel.transform, "Title", "Крафт", 30, TextAnchor.UpperLeft, new Vector2(28f, -22f), new Vector2(280f, 42f), boldFont); ui.recipeListRoot = CreateVerticalList(panel.transform, "RecipeList", new Vector2(30f, -88f), new Vector2(320f, 470f)); ui.ingredientsText = CreateText(panel.transform, "Ingredients", "Ингредиенты", 18, TextAnchor.UpperLeft, new Vector2(390f, -90f), new Vector2(280f, 300f), regularFont); ui.resultText = CreateText(panel.transform, "Result", "Результат", 20, TextAnchor.UpperLeft, new Vector2(720f, -90f), new Vector2(280f, 120f), boldFont); ui.descriptionText = CreateText(panel.transform, "Description", string.Empty, 17, TextAnchor.UpperLeft, new Vector2(720f, -230f), new Vector2(280f, 180f), regularFont); ui.messageText = CreateText(panel.transform, "Message", string.Empty, 16, TextAnchor.LowerLeft, new Vector2(390f, 42f), new Vector2(360f, 42f), regularFont); ui.craftButton = CreateButton(panel.transform, "CraftButton", "Создать", new Vector2(822f, -555f), new Vector2(150f, 42f)); ui.closeButton = CreateButton(panel.transform, "CloseButton", "Закрыть", new Vector2(32f, -585f), new Vector2(120f, 38f)); return ui; } private static QuestLogUI CreateQuestLogPanel(Transform canvas, out GameObject panel) { panel = CreatePanel(canvas, "QuestLogPanel", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1050f, 650f), UITheme.Panel); QuestLogUI ui = panel.AddComponent(); ui.panelRoot = panel; ui.uiFont = regularFont; ui.titleText = CreateText(panel.transform, "Title", "Журнал квестов", 30, TextAnchor.UpperLeft, new Vector2(28f, -22f), new Vector2(360f, 42f), boldFont); CreateText(panel.transform, "ActiveHeader", "Активные", 20, TextAnchor.UpperLeft, new Vector2(32f, -78f), new Vector2(180f, 30f), boldFont); ui.activeListRoot = CreateVerticalList(panel.transform, "ActiveList", new Vector2(30f, -115f), new Vector2(340f, 230f)); CreateText(panel.transform, "CompletedHeader", "Завершённые", 20, TextAnchor.UpperLeft, new Vector2(32f, -365f), new Vector2(220f, 30f), boldFont); ui.completedListRoot = CreateVerticalList(panel.transform, "CompletedList", new Vector2(30f, -402f), new Vector2(340f, 160f)); ui.detailText = CreateText(panel.transform, "Details", "Нет выбранного квеста.", 17, TextAnchor.UpperLeft, new Vector2(420f, -92f), new Vector2(570f, 480f), regularFont); ui.closeButton = CreateButton(panel.transform, "CloseButton", "Закрыть", new Vector2(32f, -585f), new Vector2(120f, 38f)); return ui; } private static TalentTreeUI CreateTalentTreePanel(Transform canvas, out GameObject panel) { panel = CreatePanel(canvas, "TalentTreePanel", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(1150f, 720f), UITheme.Panel); TalentTreeUI ui = panel.AddComponent(); ui.panelRoot = panel; ui.uiFont = regularFont; ui.titleText = CreateText(panel.transform, "Title", "Дерево талантов", 30, TextAnchor.UpperLeft, new Vector2(28f, -22f), new Vector2(360f, 42f), boldFont); ui.pointsText = CreateText(panel.transform, "Points", "Очки талантов: 0", 20, TextAnchor.UpperRight, new Vector2(760f, -24f), new Vector2(340f, 36f), boldFont); GameObject lineRoot = new GameObject("LineRoot", typeof(RectTransform)); lineRoot.transform.SetParent(panel.transform, false); ui.lineRoot = lineRoot.GetComponent(); StretchRect(ui.lineRoot, new Vector2(40f, 80f), new Vector2(-340f, -110f)); GameObject nodesRoot = new GameObject("NodesRoot", typeof(RectTransform)); nodesRoot.transform.SetParent(panel.transform, false); ui.nodesRoot = nodesRoot.GetComponent(); StretchRect(ui.nodesRoot, new Vector2(40f, 80f), new Vector2(-340f, -110f)); ui.statsText = CreateText(panel.transform, "Stats", "Характеристики", 17, TextAnchor.UpperLeft, new Vector2(840f, -100f), new Vector2(270f, 360f), regularFont); ui.saveButton = CreateButton(panel.transform, "SaveButton", "Save", new Vector2(50f, -660f), new Vector2(110f, 38f)); ui.loadButton = CreateButton(panel.transform, "LoadButton", "Load", new Vector2(180f, -660f), new Vector2(110f, 38f)); ui.resetButton = CreateButton(panel.transform, "ResetButton", "Reset", new Vector2(310f, -660f), new Vector2(110f, 38f)); ui.closeButton = CreateButton(panel.transform, "CloseButton", "Закрыть", new Vector2(980f, -660f), new Vector2(120f, 38f)); GameObject tooltipRoot = CreatePanel(panel.transform, "TalentTooltip", new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(-28f, 34f), new Vector2(310f, 160f), new Color(0.03f, 0.04f, 0.055f, 0.96f)); Text tooltipText = CreateText(tooltipRoot.transform, "Text", string.Empty, 15, TextAnchor.UpperLeft, new Vector2(14f, -12f), new Vector2(282f, 136f), regularFont); TalentTooltipUI tooltip = tooltipRoot.AddComponent(); tooltip.panelRoot = tooltipRoot; tooltip.contentText = tooltipText; ui.tooltip = tooltip; tooltipRoot.SetActive(false); return ui; } private static DialogueUI CreateDialoguePanel(Transform canvas, out GameObject panel) { panel = CreatePanel(canvas, "DialoguePanel", new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 170f), new Vector2(1100f, 300f), new Color(0.04f, 0.05f, 0.07f, 0.94f)); DialogueUI ui = panel.AddComponent(); ui.panelRoot = panel; ui.uiFont = regularFont; ui.speakerText = CreateText(panel.transform, "Speaker", "NPC", 24, TextAnchor.UpperLeft, new Vector2(28f, -22f), new Vector2(360f, 36f), boldFont); ui.lineText = CreateText(panel.transform, "Line", string.Empty, 20, TextAnchor.UpperLeft, new Vector2(28f, -68f), new Vector2(680f, 150f), regularFont); ui.responseRoot = CreateVerticalList(panel.transform, "Responses", new Vector2(720f, -48f), new Vector2(340f, 210f)); ui.closeButton = CreateButton(panel.transform, "CloseButton", "Закрыть", new Vector2(962f, -250f), new Vector2(110f, 36f)); return ui; } private static GameObject CreatePausePanel(Transform canvas, out Button resume, out Button save, out Button load, out Button reset) { GameObject panel = CreatePanel(canvas, "PausePanel", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(420f, 340f), UITheme.Panel); CreateText(panel.transform, "Title", "Пауза", 30, TextAnchor.UpperCenter, new Vector2(0f, -24f), new Vector2(420f, 44f), boldFont).rectTransform.anchorMin = new Vector2(0f, 1f); resume = CreateButton(panel.transform, "Resume", "Продолжить", new Vector2(100f, -92f), new Vector2(220f, 42f)); save = CreateButton(panel.transform, "Save", "Сохранить", new Vector2(100f, -146f), new Vector2(220f, 42f)); load = CreateButton(panel.transform, "Load", "Загрузить", new Vector2(100f, -200f), new Vector2(220f, 42f)); reset = CreateButton(panel.transform, "Reset", "Сбросить сохранение", new Vector2(100f, -254f), new Vector2(220f, 42f)); return panel; } private static GameManager CreateGameManager(PlayerBundle player, QuestManager questManager, DialogueManager dialogueManager, CraftingSystem craftingSystem, UIManager uiManager, SaveSystem saveSystem) { GameObject root = new GameObject("GameManager"); GameManager manager = root.AddComponent(); manager.playerController = player.controller; manager.playerStats = player.stats; manager.inventory = player.inventory; manager.levelSystem = player.levelSystem; manager.weaponManager = player.weaponManager; manager.abilityManager = player.abilityManager; manager.talentManager = player.talentManager; manager.questManager = questManager; manager.dialogueManager = dialogueManager; manager.craftingSystem = craftingSystem; manager.uiManager = uiManager; manager.saveSystem = saveSystem; return manager; } private static void CreateTree(Vector3 position) { GameObject trunk = CreatePrimitive("Tree Trunk", PrimitiveType.Cylinder, position + Vector3.up * 0.8f, new Vector3(0.55f, 1.6f, 0.55f), materials["Wood"]); GameObject leaves = CreatePrimitive("Tree Leaves", PrimitiveType.Sphere, position + Vector3.up * 2.45f, new Vector3(2.1f, 1.7f, 2.1f), materials["Leaves"]); leaves.transform.SetParent(trunk.transform, true); } private static GameObject CreatePrimitive(string name, PrimitiveType primitive, Vector3 position, Vector3 scale, Material material) { GameObject obj = GameObject.CreatePrimitive(primitive); obj.name = name; obj.transform.position = position; obj.transform.localScale = scale; Renderer renderer = obj.GetComponent(); if (renderer != null) { renderer.sharedMaterial = material; } return obj; } private static void CreateNameplate(Transform parent, string text, Color color) { GameObject nameObject = new GameObject("Nameplate"); nameObject.transform.SetParent(parent, false); nameObject.transform.localPosition = new Vector3(0f, 1.55f, 0f); TextMesh mesh = nameObject.AddComponent(); mesh.text = text; mesh.anchor = TextAnchor.MiddleCenter; mesh.alignment = TextAlignment.Center; mesh.characterSize = 0.22f; mesh.fontSize = 42; mesh.color = color; if (regularFont != null) { mesh.font = regularFont; mesh.GetComponent().sharedMaterial = regularFont.material; } } private static void CreateSign(string text, Vector3 position) { GameObject sign = CreatePrimitive("Sign " + text, PrimitiveType.Cube, position + Vector3.up * 0.8f, new Vector3(1.8f, 0.9f, 0.12f), materials["Wood"]); CreateNameplate(sign.transform, text, Color.white); } private static GameObject CreatePanel(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 size, Color color) { GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); panel.transform.SetParent(parent, false); RectTransform rect = panel.GetComponent(); rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; panel.GetComponent().color = color; return panel; } private static Text CreateText(Transform parent, string name, string value, int fontSize, TextAnchor anchor, Vector2 anchoredPosition, Vector2 size, Font font) { GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(Text)); textObject.transform.SetParent(parent, false); RectTransform rect = textObject.GetComponent(); rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(0f, 1f); rect.pivot = new Vector2(0f, 1f); rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; Text text = textObject.GetComponent(); text.text = value; text.font = font != null ? font : regularFont != null ? regularFont : Resources.GetBuiltinResource("Arial.ttf"); text.fontSize = fontSize; text.alignment = anchor; text.color = UITheme.Text; text.supportRichText = true; return text; } private static Button CreateButton(Transform parent, string name, string label, Vector2 anchoredPosition, Vector2 size) { GameObject buttonObject = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button)); buttonObject.transform.SetParent(parent, false); RectTransform rect = buttonObject.GetComponent(); rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(0f, 1f); rect.pivot = new Vector2(0f, 1f); rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; buttonObject.GetComponent().color = new Color(0.13f, 0.18f, 0.24f, 0.96f); Text text = CreateText(buttonObject.transform, "Text", label, 16, TextAnchor.MiddleCenter, Vector2.zero, size, regularFont); text.rectTransform.anchorMin = Vector2.zero; text.rectTransform.anchorMax = Vector2.one; text.rectTransform.pivot = new Vector2(0.5f, 0.5f); text.rectTransform.anchoredPosition = Vector2.zero; text.rectTransform.offsetMin = Vector2.zero; text.rectTransform.offsetMax = Vector2.zero; return buttonObject.GetComponent