Files
OTG_8/Assets/Editor/Lab8SceneBuilder.cs
T

1390 lines
70 KiB
C#
Raw Normal View History

2026-06-04 23:22:13 +03:00
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<string, Material> 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<GameManager>() != null, "GameManager missing");
Require(Object.FindObjectOfType<PlayerController>() != null, "PlayerController missing");
Require(Object.FindObjectOfType<PlayerInventory>() != null, "Inventory missing");
Require(Object.FindObjectOfType<PlayerStats>() != null, "PlayerStats missing");
Require(Object.FindObjectOfType<QuestManager>() != null, "QuestManager missing");
Require(Object.FindObjectOfType<DialogueManager>() != null, "DialogueManager missing");
Require(Object.FindObjectOfType<UIManager>() != null, "UIManager missing");
Require(Object.FindObjectOfType<SaveSystem>() != null, "SaveSystem missing");
Require(Object.FindObjectsOfType<Enemy>().Length >= 5, "Goblin enemies missing");
Require(Object.FindObjectsOfType<ResourceNode>().Length >= 10, "Resource nodes missing");
Require(Object.FindObjectOfType<EventSystem>() != null, "EventSystem missing");
Font expectedFont = AssetDatabase.LoadAssetAtPath<Font>("Assets/_Fonts/Ubuntu-Regular.ttf");
if (expectedFont != null)
{
foreach (Text text in Resources.FindObjectsOfTypeAll<Text>())
{
if (text.gameObject.scene == scene)
{
Require(text.font == expectedFont || text.font == AssetDatabase.LoadAssetAtPath<Font>("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<ItemData> allItems = new List<ItemData>();
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<Font>("Arial.ttf");
}
File.Copy(source, destination, true);
AssetDatabase.ImportAsset(destination, ImportAssetOptions.ForceUpdate);
Font font = AssetDatabase.LoadAssetAtPath<Font>(destination);
return font != null ? font : Resources.GetBuiltinResource<Font>("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<string, Material> CreateMaterials()
{
return new Dictionary<string, Material>
{
["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<Material>(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<QuestRewardItem> { 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<QuestRewardItem> { 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<ItemData>($"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<WeaponData>($"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<RecipeData>($"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<QuestData>($"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<QuestGoal> { goal };
quest.rewardItems = quest.rewardItems ?? new List<QuestRewardItem>();
EditorUtility.SetDirty(quest);
return quest;
}
private static DialogueNode CreateDialogue(string id, string speaker, string line)
{
DialogueNode node = LoadOrCreate<DialogueNode>($"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<AbilityData>($"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<TalentData>($"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<T>(string path) where T : ScriptableObject
{
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset == null)
{
asset = ScriptableObject.CreateInstance<T>();
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<Sprite>(path);
}
private static GameObject CreateFireballPrefab()
{
GameObject root = new GameObject("PF_FireballEffect");
SphereCollider collider = root.AddComponent<SphereCollider>();
collider.radius = 0.35f;
collider.isTrigger = true;
root.AddComponent<FireballEffect>();
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<Collider>());
visual.GetComponent<Renderer>().sharedMaterial = materials["Fire"];
return SavePrefab(root, "Assets/_Prefabs/Effects/PF_FireballEffect.prefab");
}
private static GameObject CreateHealPrefab()
{
GameObject root = new GameObject("PF_HealEffect");
root.AddComponent<HealEffect>();
return SavePrefab(root, "Assets/_Prefabs/Effects/PF_HealEffect.prefab");
}
private static GameObject CreateBlinkPrefab()
{
GameObject root = new GameObject("PF_BlinkEffect");
root.AddComponent<BlinkEffect>();
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>();
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<Renderer>().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<Renderer>().sharedMaterial = material;
Object.DestroyImmediate(marker.GetComponent<Collider>());
}
private static Camera CreateCamera()
{
GameObject cameraObject = new GameObject("Main Camera");
Camera camera = cameraObject.AddComponent<Camera>();
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<Renderer>().sharedMaterial = materials["Player"];
CharacterController characterController = player.AddComponent<CharacterController>();
characterController.height = 2f;
characterController.radius = 0.42f;
characterController.center = Vector3.zero;
Object.DestroyImmediate(player.GetComponent<CapsuleCollider>());
bundle.root = player;
bundle.stats = player.AddComponent<PlayerStats>();
bundle.inventory = player.AddComponent<PlayerInventory>();
bundle.levelSystem = player.AddComponent<LevelSystem>();
bundle.weaponController = null;
WeaponController weaponController = player.AddComponent<WeaponController>();
bundle.weaponManager = player.AddComponent<WeaponManager>();
bundle.abilityManager = player.AddComponent<AbilityManager>();
bundle.talentManager = player.AddComponent<TalentManager>();
bundle.controller = player.AddComponent<PlayerController>();
bundle.interaction = player.AddComponent<PlayerInteraction>();
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<AbilityData> { 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<WeaponData> { data.basicSwordWeapon, data.ironAxeWeapon };
bundle.weaponManager.itemUnlocks = new List<WeaponUnlock>
{
new WeaponUnlock { item = data.ironAxeItem, weapon = data.ironAxeWeapon }
};
bundle.talentManager.allTalents = new List<TalentData>
{
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<Renderer>().sharedMaterial = materials["Workbench"];
CraftingSystem crafting = workbench.AddComponent<CraftingSystem>();
crafting.availableRecipes = new List<RecipeData> { 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<Renderer>().sharedMaterial = material;
NPCInteract interact = npc.AddComponent<NPCInteract>();
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<Renderer>().sharedMaterial = materials["Village"];
}
private static QuestManager CreateQuestManager(DataSet data)
{
GameObject root = new GameObject("QuestManager");
QuestManager manager = root.AddComponent<QuestManager>();
manager.allQuests = new List<QuestData> { data.questGatherWood, data.questKillGoblins, data.questCraftAxe };
return manager;
}
private static DialogueManager CreateDialogueManager()
{
GameObject root = new GameObject("DialogueManager");
return root.AddComponent<DialogueManager>();
}
private static SaveSystem CreateSaveSystem(DataSet data)
{
GameObject root = new GameObject("SaveSystem");
SaveSystem saveSystem = root.AddComponent<SaveSystem>();
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<Renderer>().sharedMaterial = material;
ResourceNode node = resource.AddComponent<ResourceNode>();
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<Renderer>().sharedMaterial = materials["Goblin"];
Enemy enemy = goblin.AddComponent<Enemy>();
enemy.enemyId = "Goblin";
enemy.displayName = "Goblin";
enemy.maxHealth = 50f;
enemy.damage = 5f;
enemy.expReward = 20;
enemy.dropItems = new List<EnemyDrop> { 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<Renderer>().sharedMaterial = materials["Mana"];
Object.DestroyImmediate(circle.GetComponent<Collider>());
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<Renderer>().sharedMaterial = materials["Wood"];
Damageable damageable = dummy.AddComponent<Damageable>();
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>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
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<UIManager>();
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<HUDController>();
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<RectTransform>();
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<Image>().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<RectTransform>();
fillRect.anchorMin = Vector2.zero;
fillRect.anchorMax = Vector2.one;
fillRect.offsetMin = Vector2.zero;
fillRect.offsetMax = Vector2.zero;
Image fill = fillObject.GetComponent<Image>();
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<Image>();
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<RectTransform>();
overlayRect.anchorMin = Vector2.zero;
overlayRect.anchorMax = Vector2.one;
overlayRect.offsetMin = Vector2.zero;
overlayRect.offsetMax = Vector2.zero;
slot.cooldownOverlay = overlayObject.GetComponent<Image>();
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<RectTransform>();
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<VerticalLayoutGroup>();
layout.spacing = 8f;
layout.childAlignment = TextAnchor.UpperCenter;
layout.childControlWidth = false;
layout.childControlHeight = false;
NotificationUI notificationUI = root.AddComponent<NotificationUI>();
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<InteractionPromptUI>();
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<InventoryUI>();
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<RectTransform>();
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<GridLayoutGroup>();
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<CraftingUI>();
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<QuestLogUI>();
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<TalentTreeUI>();
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<RectTransform>();
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<RectTransform>();
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<TalentTooltipUI>();
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<DialogueUI>();
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<GameManager>();
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<Renderer>();
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<TextMesh>();
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<MeshRenderer>().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<RectTransform>();
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.pivot = pivot;
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = size;
panel.GetComponent<Image>().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<RectTransform>();
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.text = value;
text.font = font != null ? font : regularFont != null ? regularFont : Resources.GetBuiltinResource<Font>("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<RectTransform>();
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<Image>().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<Button>();
}
private static RectTransform CreateVerticalList(Transform parent, string name, Vector2 anchoredPosition, Vector2 size)
{
GameObject list = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup));
list.transform.SetParent(parent, false);
RectTransform rect = list.GetComponent<RectTransform>();
rect.anchorMin = new Vector2(0f, 1f);
rect.anchorMax = new Vector2(0f, 1f);
rect.pivot = new Vector2(0f, 1f);
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = size;
VerticalLayoutGroup layout = list.GetComponent<VerticalLayoutGroup>();
layout.spacing = 8f;
layout.childAlignment = TextAnchor.UpperLeft;
layout.childControlWidth = false;
layout.childControlHeight = false;
return rect;
}
private static void StretchRect(RectTransform rect, Vector2 offsetMin, Vector2 offsetMax)
{
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = offsetMin;
rect.offsetMax = offsetMax;
}
private static void EnsureEventSystem()
{
if (Object.FindObjectOfType<EventSystem>() != null)
{
return;
}
GameObject eventSystem = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
}
private static void AddSceneToBuildSettings(string path)
{
List<EditorBuildSettingsScene> scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
for (int i = 0; i < scenes.Count; i++)
{
if (scenes[i].path == path)
{
scenes[i] = new EditorBuildSettingsScene(path, true);
EditorBuildSettings.scenes = scenes.ToArray();
return;
}
}
scenes.Add(new EditorBuildSettingsScene(path, true));
EditorBuildSettings.scenes = scenes.ToArray();
}
private static int CountMissingScripts(Scene scene)
{
int total = 0;
GameObject[] roots = scene.GetRootGameObjects();
for (int i = 0; i < roots.Length; i++)
{
total += CountMissingScriptsRecursive(roots[i]);
}
return total;
}
private static int CountMissingScriptsRecursive(GameObject obj)
{
int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(obj);
foreach (Transform child in obj.transform)
{
count += CountMissingScriptsRecursive(child.gameObject);
}
return count;
}
private static string ToSystemPath(string assetPath)
{
return Path.GetFullPath(assetPath);
}
private static void Require(bool condition, string message)
{
if (!condition)
{
throw new System.Exception(message);
}
}
}