1154 lines
50 KiB
C#
1154 lines
50 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
|
|
public static class Lab7SceneBuilder
|
|
{
|
|
private const string ScenePath = "Assets/_Scenes/Lab07_TalentSystem.unity";
|
|
private const string NodePrefabPath = "Assets/_Prefabs/UI/TalentNodeUI.prefab";
|
|
private const string TalentIconFolder = "Assets/_Data/Talents/Icons";
|
|
|
|
[MenuItem("Tools/Technical Game Design/Lab7/Create Talent System Scene")]
|
|
public static void CreateTalentSystemScene()
|
|
{
|
|
EnsureProjectFolders();
|
|
|
|
Font regularFont = InstallUbuntuFont(
|
|
"Ubuntu-Regular.ttf",
|
|
new[]
|
|
{
|
|
"/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf",
|
|
"/usr/share/fonts/TTF/Ubuntu-R.ttf",
|
|
"/usr/share/fonts/Ubuntu-R.ttf"
|
|
},
|
|
"regular");
|
|
|
|
Font boldFont = InstallUbuntuFont(
|
|
"Ubuntu-Bold.ttf",
|
|
new[]
|
|
{
|
|
"/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf",
|
|
"/usr/share/fonts/TTF/Ubuntu-B.ttf",
|
|
"/usr/share/fonts/Ubuntu-B.ttf"
|
|
},
|
|
"bold");
|
|
|
|
if (boldFont == null)
|
|
{
|
|
boldFont = regularFont;
|
|
}
|
|
|
|
Material arenaMaterial = CreateMaterial("Assets/_Materials/Arena_Ground.mat", new Color(0.22f, 0.27f, 0.23f, 1f));
|
|
Material playerMaterial = CreateMaterial("Assets/_Materials/Player_Gold.mat", new Color(0.92f, 0.62f, 0.18f, 1f));
|
|
Material mannequinMaterial = CreateMaterial("Assets/_Materials/Training_Dummy.mat", new Color(0.48f, 0.34f, 0.22f, 1f));
|
|
Material crystalMaterial = CreateMaterial("Assets/_Materials/Experience_Crystal.mat", new Color(0.16f, 0.78f, 0.96f, 1f), true);
|
|
Material decorMaterial = CreateMaterial("Assets/_Materials/Decor_Stone.mat", new Color(0.34f, 0.36f, 0.38f, 1f));
|
|
|
|
Dictionary<string, TalentData> talents = CreateTalentAssets();
|
|
TalentNodeUI nodePrefab = CreateTalentNodePrefab(regularFont, boldFont);
|
|
|
|
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
scene.name = "Lab07_TalentSystem";
|
|
|
|
ConfigureSceneLighting();
|
|
Camera mainCamera = CreateCamera();
|
|
|
|
PlayerStats playerStats = CreatePlayer(playerMaterial, mainCamera);
|
|
GameObject systemsObject = new GameObject("GameSystems");
|
|
TalentManager talentManager = systemsObject.AddComponent<TalentManager>();
|
|
talentManager.playerStats = playerStats;
|
|
talentManager.startingTalentPoints = 1;
|
|
talentManager.availableTalentPoints = 1;
|
|
talentManager.allTalents = new List<TalentData>(talents.Values);
|
|
|
|
LevelSystem levelSystem = systemsObject.AddComponent<LevelSystem>();
|
|
levelSystem.talentManager = talentManager;
|
|
|
|
CreateArena(arenaMaterial);
|
|
CreateTrainingDummies(mannequinMaterial);
|
|
CreateDecorations(decorMaterial);
|
|
|
|
Canvas canvas = CreateCanvas();
|
|
EnsureEventSystem();
|
|
|
|
NotificationUI notificationUI = CreateNotificationUI(canvas.transform, regularFont);
|
|
talentManager.notificationUI = notificationUI;
|
|
levelSystem.notificationUI = notificationUI;
|
|
|
|
CreateLevelHud(canvas.transform, regularFont, boldFont, levelSystem);
|
|
CreateStatsHud(canvas.transform, regularFont, boldFont, playerStats);
|
|
CreateTalentTree(canvas.transform, regularFont, boldFont, nodePrefab, talentManager, playerStats);
|
|
CreateExperiencePickups(crystalMaterial, levelSystem, notificationUI);
|
|
|
|
EditorUtility.SetDirty(talentManager);
|
|
EditorUtility.SetDirty(levelSystem);
|
|
|
|
EditorSceneManager.SaveScene(scene, ScenePath);
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Debug.Log($"Lab 7 talent system scene created: {ScenePath}");
|
|
}
|
|
|
|
[MenuItem("Tools/Technical Game Design/Lab7/Validate Generated Scene")]
|
|
public static void ValidateGeneratedScene()
|
|
{
|
|
if (!File.Exists(ToSystemPath(ScenePath)))
|
|
{
|
|
Debug.LogError($"Scene does not exist: {ScenePath}");
|
|
return;
|
|
}
|
|
|
|
Scene scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
|
|
int errors = 0;
|
|
|
|
PlayerStats playerStats = UnityEngine.Object.FindObjectOfType<PlayerStats>(true);
|
|
TalentManager talentManager = UnityEngine.Object.FindObjectOfType<TalentManager>(true);
|
|
LevelSystem levelSystem = UnityEngine.Object.FindObjectOfType<LevelSystem>(true);
|
|
TalentTreeUI talentTreeUI = UnityEngine.Object.FindObjectOfType<TalentTreeUI>(true);
|
|
NotificationUI notificationUI = UnityEngine.Object.FindObjectOfType<NotificationUI>(true);
|
|
|
|
errors += RequireObject(playerStats, "PlayerStats");
|
|
errors += RequireObject(talentManager, "TalentManager");
|
|
errors += RequireObject(levelSystem, "LevelSystem");
|
|
errors += RequireObject(talentTreeUI, "TalentTreeUI");
|
|
errors += RequireObject(notificationUI, "NotificationUI");
|
|
|
|
if (talentManager != null)
|
|
{
|
|
errors += RequireObject(talentManager.playerStats, "TalentManager.playerStats");
|
|
errors += RequireObject(talentManager.notificationUI, "TalentManager.notificationUI");
|
|
errors += RequireCondition(talentManager.allTalents != null && talentManager.allTalents.Count == 10, "TalentManager must reference exactly 10 talents.");
|
|
errors += ValidateTalentPrerequisites(talentManager);
|
|
}
|
|
|
|
if (levelSystem != null)
|
|
{
|
|
errors += RequireObject(levelSystem.talentManager, "LevelSystem.talentManager");
|
|
errors += RequireObject(levelSystem.notificationUI, "LevelSystem.notificationUI");
|
|
errors += RequireObject(levelSystem.levelText, "LevelSystem.levelText");
|
|
errors += RequireObject(levelSystem.expText, "LevelSystem.expText");
|
|
errors += RequireObject(levelSystem.expFillImage, "LevelSystem.expFillImage");
|
|
}
|
|
|
|
if (talentTreeUI != null)
|
|
{
|
|
errors += RequireObject(talentTreeUI.panelRoot, "TalentTreeUI.panelRoot");
|
|
errors += RequireObject(talentTreeUI.lineRoot, "TalentTreeUI.lineRoot");
|
|
errors += RequireObject(talentTreeUI.nodesRoot, "TalentTreeUI.nodesRoot");
|
|
errors += RequireObject(talentTreeUI.nodePrefab, "TalentTreeUI.nodePrefab");
|
|
errors += RequireObject(talentTreeUI.tooltip, "TalentTreeUI.tooltip");
|
|
errors += RequireObject(talentTreeUI.statsPanel, "TalentTreeUI.statsPanel");
|
|
errors += RequireObject(talentTreeUI.saveButton, "TalentTreeUI.saveButton");
|
|
errors += RequireObject(talentTreeUI.loadButton, "TalentTreeUI.loadButton");
|
|
errors += RequireObject(talentTreeUI.resetButton, "TalentTreeUI.resetButton");
|
|
}
|
|
|
|
errors += ValidateMissingScripts(scene);
|
|
errors += ValidateUbuntuFonts(scene);
|
|
|
|
if (errors == 0)
|
|
{
|
|
Debug.Log("Lab 7 validation passed.");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Lab 7 validation failed. Errors: {errors}");
|
|
}
|
|
}
|
|
|
|
private static void EnsureProjectFolders()
|
|
{
|
|
string[] folders =
|
|
{
|
|
"Assets/_Scenes",
|
|
"Assets/_Scripts",
|
|
"Assets/_Scripts/Core",
|
|
"Assets/_Scripts/Stats",
|
|
"Assets/_Scripts/Talents",
|
|
"Assets/_Scripts/UI",
|
|
"Assets/_Scripts/Gameplay",
|
|
"Assets/_Scripts/Save",
|
|
"Assets/_Data",
|
|
"Assets/_Data/Talents",
|
|
TalentIconFolder,
|
|
"Assets/_Prefabs",
|
|
"Assets/_Prefabs/UI",
|
|
"Assets/_Materials",
|
|
"Assets/_Fonts",
|
|
"Assets/Editor"
|
|
};
|
|
|
|
for (int i = 0; i < folders.Length; i++)
|
|
{
|
|
if (!Directory.Exists(folders[i]))
|
|
{
|
|
Directory.CreateDirectory(folders[i]);
|
|
}
|
|
}
|
|
|
|
AssetDatabase.Refresh();
|
|
}
|
|
|
|
private static Font InstallUbuntuFont(string destinationFileName, string[] preferredPaths, string label)
|
|
{
|
|
string destinationPath = "Assets/_Fonts/" + destinationFileName;
|
|
string sourcePath = FindExistingFile(preferredPaths);
|
|
|
|
if (string.IsNullOrEmpty(sourcePath))
|
|
{
|
|
sourcePath = FindFontRecursive(destinationFileName.Contains("Bold") ? "Ubuntu-B.ttf" : "Ubuntu-R.ttf");
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(sourcePath))
|
|
{
|
|
File.Copy(sourcePath, destinationPath, true);
|
|
AssetDatabase.ImportAsset(destinationPath, ImportAssetOptions.ForceUpdate);
|
|
Font font = AssetDatabase.LoadAssetAtPath<Font>(destinationPath);
|
|
if (font != null)
|
|
{
|
|
return font;
|
|
}
|
|
}
|
|
|
|
Debug.LogWarning($"Ubuntu {label} font was not found in system font folders. Arial fallback will be used.");
|
|
return Resources.GetBuiltinResource<Font>("Arial.ttf");
|
|
}
|
|
|
|
private static string FindExistingFile(string[] paths)
|
|
{
|
|
for (int i = 0; i < paths.Length; i++)
|
|
{
|
|
if (File.Exists(paths[i]))
|
|
{
|
|
return paths[i];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string FindFontRecursive(string fileName)
|
|
{
|
|
string[] roots = { "/usr/share/fonts", "/usr/share/fonts/truetype/ubuntu", "/usr/share/fonts/TTF" };
|
|
for (int i = 0; i < roots.Length; i++)
|
|
{
|
|
if (!Directory.Exists(roots[i]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
string[] matches = Directory.GetFiles(roots[i], fileName, SearchOption.AllDirectories);
|
|
if (matches.Length > 0)
|
|
{
|
|
return matches[0];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static Material CreateMaterial(string path, Color color, bool emission = false)
|
|
{
|
|
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
if (material == null)
|
|
{
|
|
material = new Material(Shader.Find("Standard"));
|
|
AssetDatabase.CreateAsset(material, path);
|
|
}
|
|
|
|
material.color = color;
|
|
if (emission)
|
|
{
|
|
material.EnableKeyword("_EMISSION");
|
|
material.SetColor("_EmissionColor", color * 1.8f);
|
|
}
|
|
else
|
|
{
|
|
material.DisableKeyword("_EMISSION");
|
|
}
|
|
|
|
EditorUtility.SetDirty(material);
|
|
return material;
|
|
}
|
|
|
|
private static Dictionary<string, TalentData> CreateTalentAssets()
|
|
{
|
|
Color toughnessColor = new Color(0.27f, 0.78f, 0.42f, 1f);
|
|
Color strengthColor = new Color(0.92f, 0.46f, 0.24f, 1f);
|
|
Color agilityColor = new Color(0.24f, 0.76f, 0.86f, 1f);
|
|
Color manaColor = new Color(0.42f, 0.52f, 0.96f, 1f);
|
|
Color masteryColor = new Color(1.00f, 0.72f, 0.24f, 1f);
|
|
|
|
TalentSpec[] specs =
|
|
{
|
|
new TalentSpec(
|
|
"Toughness",
|
|
"Выносливость",
|
|
"Увеличивает максимальное здоровье персонажа.",
|
|
1,
|
|
new Vector2(-340f, 170f),
|
|
toughnessColor,
|
|
new StatModifier(StatType.MaxHealth, ModifierType.FlatAdd, 20f),
|
|
Array.Empty<string>()),
|
|
new TalentSpec(
|
|
"Strength",
|
|
"Сила",
|
|
"Увеличивает базовый урон персонажа.",
|
|
1,
|
|
new Vector2(-90f, 170f),
|
|
strengthColor,
|
|
new StatModifier(StatType.Damage, ModifierType.FlatAdd, 5f),
|
|
Array.Empty<string>()),
|
|
new TalentSpec(
|
|
"Agility",
|
|
"Ловкость",
|
|
"Увеличивает скорость передвижения.",
|
|
1,
|
|
new Vector2(160f, 170f),
|
|
agilityColor,
|
|
new StatModifier(StatType.MoveSpeed, ModifierType.FlatAdd, 1f),
|
|
Array.Empty<string>()),
|
|
new TalentSpec(
|
|
"ManaFlow",
|
|
"Поток маны",
|
|
"Увеличивает максимальный запас маны.",
|
|
1,
|
|
new Vector2(410f, 170f),
|
|
manaColor,
|
|
new StatModifier(StatType.MaxMana, ModifierType.FlatAdd, 25f),
|
|
Array.Empty<string>()),
|
|
new TalentSpec(
|
|
"AdvancedToughness",
|
|
"Улучшенная выносливость",
|
|
"Дополнительно усиливает запас здоровья.",
|
|
1,
|
|
new Vector2(-340f, 35f),
|
|
toughnessColor,
|
|
new StatModifier(StatType.MaxHealth, ModifierType.FlatAdd, 30f),
|
|
new[] { "Toughness" }),
|
|
new TalentSpec(
|
|
"BattleTraining",
|
|
"Боевая подготовка",
|
|
"Повышает весь наносимый урон на 10%.",
|
|
1,
|
|
new Vector2(-90f, 35f),
|
|
strengthColor,
|
|
new StatModifier(StatType.Damage, ModifierType.PercentAdd, 0.10f),
|
|
new[] { "Strength" }),
|
|
new TalentSpec(
|
|
"CriticalEye",
|
|
"Меткий взгляд",
|
|
"Увеличивает шанс критического удара.",
|
|
1,
|
|
new Vector2(35f, -100f),
|
|
strengthColor,
|
|
new StatModifier(StatType.CriticalChance, ModifierType.FlatAdd, 5f),
|
|
new[] { "Strength" }),
|
|
new TalentSpec(
|
|
"SwiftStep",
|
|
"Быстрый шаг",
|
|
"Повышает скорость передвижения на 10%.",
|
|
1,
|
|
new Vector2(160f, 35f),
|
|
agilityColor,
|
|
new StatModifier(StatType.MoveSpeed, ModifierType.PercentAdd, 0.10f),
|
|
new[] { "Agility" }),
|
|
new TalentSpec(
|
|
"ArcaneFocus",
|
|
"Магическая концентрация",
|
|
"Сокращает время восстановления способностей на 10%.",
|
|
1,
|
|
new Vector2(410f, 35f),
|
|
manaColor,
|
|
new StatModifier(StatType.AbilityCooldownReduction, ModifierType.FlatAdd, 10f),
|
|
new[] { "ManaFlow" }),
|
|
new TalentSpec(
|
|
"MasterWarrior",
|
|
"Мастер-воин",
|
|
"Финальный боевой талант: больше урона и здоровья.",
|
|
2,
|
|
new Vector2(-220f, -185f),
|
|
masteryColor,
|
|
new[]
|
|
{
|
|
new StatModifier(StatType.Damage, ModifierType.PercentAdd, 0.15f),
|
|
new StatModifier(StatType.MaxHealth, ModifierType.FlatAdd, 10f)
|
|
},
|
|
new[] { "BattleTraining", "AdvancedToughness" })
|
|
};
|
|
|
|
Dictionary<string, TalentData> assets = new Dictionary<string, TalentData>();
|
|
for (int i = 0; i < specs.Length; i++)
|
|
{
|
|
TalentSpec spec = specs[i];
|
|
TalentData talent = LoadOrCreateTalent(spec.id);
|
|
talent.talentId = spec.id;
|
|
talent.displayName = spec.displayName;
|
|
talent.description = spec.description;
|
|
talent.cost = spec.cost;
|
|
talent.treePosition = spec.treePosition;
|
|
talent.branchColor = spec.branchColor;
|
|
talent.modifiers = spec.modifiers;
|
|
talent.unlockedByDefault = false;
|
|
talent.icon = CreateTalentIcon(spec.id, spec.branchColor);
|
|
assets[spec.id] = talent;
|
|
}
|
|
|
|
for (int i = 0; i < specs.Length; i++)
|
|
{
|
|
TalentSpec spec = specs[i];
|
|
TalentData talent = assets[spec.id];
|
|
TalentData[] prerequisites = new TalentData[spec.prerequisiteIds.Length];
|
|
for (int prerequisiteIndex = 0; prerequisiteIndex < spec.prerequisiteIds.Length; prerequisiteIndex++)
|
|
{
|
|
prerequisites[prerequisiteIndex] = assets[spec.prerequisiteIds[prerequisiteIndex]];
|
|
}
|
|
|
|
talent.prerequisites = prerequisites;
|
|
EditorUtility.SetDirty(talent);
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
return assets;
|
|
}
|
|
|
|
private static TalentData LoadOrCreateTalent(string id)
|
|
{
|
|
string path = $"Assets/_Data/Talents/{id}.asset";
|
|
TalentData talent = AssetDatabase.LoadAssetAtPath<TalentData>(path);
|
|
if (talent == null)
|
|
{
|
|
talent = ScriptableObject.CreateInstance<TalentData>();
|
|
AssetDatabase.CreateAsset(talent, path);
|
|
}
|
|
|
|
return talent;
|
|
}
|
|
|
|
private static Sprite CreateTalentIcon(string id, Color color)
|
|
{
|
|
string iconPath = $"{TalentIconFolder}/{id}_Icon.png";
|
|
Texture2D texture = new Texture2D(64, 64, TextureFormat.RGBA32, false);
|
|
Color clear = new Color(0f, 0f, 0f, 0f);
|
|
Vector2 center = new Vector2(31.5f, 31.5f);
|
|
|
|
for (int y = 0; y < 64; y++)
|
|
{
|
|
for (int x = 0; x < 64; x++)
|
|
{
|
|
float distance = Vector2.Distance(new Vector2(x, y), center);
|
|
if (distance > 29f)
|
|
{
|
|
texture.SetPixel(x, y, clear);
|
|
}
|
|
else if (distance > 25f)
|
|
{
|
|
texture.SetPixel(x, y, UITheme.Gold);
|
|
}
|
|
else if (distance < 8f)
|
|
{
|
|
texture.SetPixel(x, y, Color.white);
|
|
}
|
|
else
|
|
{
|
|
texture.SetPixel(x, y, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
texture.Apply();
|
|
File.WriteAllBytes(ToSystemPath(iconPath), texture.EncodeToPNG());
|
|
UnityEngine.Object.DestroyImmediate(texture);
|
|
AssetDatabase.ImportAsset(iconPath, ImportAssetOptions.ForceUpdate);
|
|
|
|
TextureImporter importer = AssetImporter.GetAtPath(iconPath) as TextureImporter;
|
|
if (importer != null)
|
|
{
|
|
importer.textureType = TextureImporterType.Sprite;
|
|
importer.spriteImportMode = SpriteImportMode.Single;
|
|
importer.alphaIsTransparency = true;
|
|
importer.mipmapEnabled = false;
|
|
importer.SaveAndReimport();
|
|
}
|
|
|
|
return AssetDatabase.LoadAssetAtPath<Sprite>(iconPath);
|
|
}
|
|
|
|
private static TalentNodeUI CreateTalentNodePrefab(Font regularFont, Font boldFont)
|
|
{
|
|
GameObject root = new GameObject("TalentNodeUI", typeof(RectTransform), typeof(Image), typeof(Button), typeof(Outline), typeof(TalentNodeUI));
|
|
RectTransform rootRect = root.GetComponent<RectTransform>();
|
|
rootRect.sizeDelta = new Vector2(166f, 94f);
|
|
|
|
Image background = root.GetComponent<Image>();
|
|
background.color = UITheme.LockedNode;
|
|
background.raycastTarget = true;
|
|
|
|
Button button = root.GetComponent<Button>();
|
|
button.targetGraphic = background;
|
|
ColorBlock colors = button.colors;
|
|
colors.normalColor = Color.white;
|
|
colors.highlightedColor = new Color(1f, 1f, 1f, 0.92f);
|
|
colors.pressedColor = new Color(0.84f, 0.84f, 0.84f, 1f);
|
|
colors.disabledColor = new Color(0.55f, 0.55f, 0.55f, 1f);
|
|
button.colors = colors;
|
|
|
|
Outline outline = root.GetComponent<Outline>();
|
|
outline.effectColor = UITheme.LockedNode;
|
|
outline.effectDistance = new Vector2(2f, -2f);
|
|
|
|
Image icon = CreateUIImage(root.transform, "Icon", Color.white);
|
|
SetRect(icon.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(10f, -10f), new Vector2(36f, 36f));
|
|
|
|
Text nameText = CreateText(root.transform, "Name", boldFont, "Talent", 13, UITheme.Text, TextAnchor.UpperLeft);
|
|
nameText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
nameText.verticalOverflow = VerticalWrapMode.Truncate;
|
|
SetRect(nameText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(52f, -9f), new Vector2(-60f, 34f));
|
|
|
|
Text costText = CreateText(root.transform, "Cost", boldFont, "1 очк.", 12, UITheme.Gold, TextAnchor.UpperRight);
|
|
SetRect(costText.rectTransform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-9f, -9f), new Vector2(54f, 24f));
|
|
|
|
Text stateText = CreateText(root.transform, "State", regularFont, "Заблокировано", 12, UITheme.Text, TextAnchor.LowerCenter);
|
|
stateText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
SetRect(stateText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 10f), new Vector2(-18f, 30f));
|
|
|
|
TalentNodeUI node = root.GetComponent<TalentNodeUI>();
|
|
node.backgroundImage = background;
|
|
node.iconImage = icon;
|
|
node.nameText = nameText;
|
|
node.costText = costText;
|
|
node.stateText = stateText;
|
|
node.learnButton = button;
|
|
node.borderOutline = outline;
|
|
|
|
PrefabUtility.SaveAsPrefabAsset(root, NodePrefabPath);
|
|
UnityEngine.Object.DestroyImmediate(root);
|
|
AssetDatabase.SaveAssets();
|
|
return AssetDatabase.LoadAssetAtPath<TalentNodeUI>(NodePrefabPath);
|
|
}
|
|
|
|
private static void ConfigureSceneLighting()
|
|
{
|
|
RenderSettings.ambientLight = new Color(0.34f, 0.37f, 0.42f, 1f);
|
|
RenderSettings.skybox = null;
|
|
|
|
GameObject lightObject = new GameObject("Directional Light");
|
|
Light light = lightObject.AddComponent<Light>();
|
|
light.type = LightType.Directional;
|
|
light.intensity = 1.15f;
|
|
light.shadows = LightShadows.Soft;
|
|
lightObject.transform.rotation = Quaternion.Euler(50f, -35f, 0f);
|
|
}
|
|
|
|
private static Camera CreateCamera()
|
|
{
|
|
GameObject cameraObject = new GameObject("Main Camera");
|
|
cameraObject.tag = "MainCamera";
|
|
Camera camera = cameraObject.AddComponent<Camera>();
|
|
camera.clearFlags = CameraClearFlags.SolidColor;
|
|
camera.backgroundColor = new Color(0.12f, 0.14f, 0.17f, 1f);
|
|
camera.fieldOfView = 52f;
|
|
cameraObject.AddComponent<AudioListener>();
|
|
cameraObject.transform.position = new Vector3(-6f, 8f, -8f);
|
|
cameraObject.transform.LookAt(Vector3.up);
|
|
return camera;
|
|
}
|
|
|
|
private static PlayerStats CreatePlayer(Material playerMaterial, Camera mainCamera)
|
|
{
|
|
GameObject player = GameObject.CreatePrimitive(PrimitiveType.Capsule);
|
|
player.name = "Player";
|
|
player.tag = "Player";
|
|
player.transform.position = new Vector3(0f, 1f, -5f);
|
|
player.GetComponent<Renderer>().sharedMaterial = playerMaterial;
|
|
|
|
CapsuleCollider capsuleCollider = player.GetComponent<CapsuleCollider>();
|
|
if (capsuleCollider != null)
|
|
{
|
|
UnityEngine.Object.DestroyImmediate(capsuleCollider);
|
|
}
|
|
|
|
CharacterController characterController = player.AddComponent<CharacterController>();
|
|
characterController.height = 2f;
|
|
characterController.radius = 0.45f;
|
|
characterController.center = Vector3.zero;
|
|
characterController.stepOffset = 0.35f;
|
|
|
|
PlayerStats playerStats = player.AddComponent<PlayerStats>();
|
|
SimplePlayerController controller = player.AddComponent<SimplePlayerController>();
|
|
controller.playerStats = playerStats;
|
|
controller.characterController = characterController;
|
|
controller.cameraTransform = mainCamera.transform;
|
|
controller.cameraYaw = 42f;
|
|
controller.cameraOffset = new Vector3(0f, 9f, -8.5f);
|
|
|
|
return playerStats;
|
|
}
|
|
|
|
private static void CreateArena(Material arenaMaterial)
|
|
{
|
|
GameObject arena = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
|
arena.name = "Arena";
|
|
arena.transform.localScale = new Vector3(3f, 1f, 3f);
|
|
arena.GetComponent<Renderer>().sharedMaterial = arenaMaterial;
|
|
|
|
CreateArenaMarker(new Vector3(0f, 0.03f, 0f), new Vector3(9f, 0.04f, 9f), new Color(0.16f, 0.18f, 0.17f, 1f));
|
|
CreateArenaMarker(new Vector3(0f, 0.04f, 0f), new Vector3(4f, 0.05f, 4f), new Color(0.25f, 0.22f, 0.14f, 1f));
|
|
}
|
|
|
|
private static void CreateArenaMarker(Vector3 position, Vector3 scale, Color color)
|
|
{
|
|
GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
marker.name = "Arena Marker";
|
|
marker.transform.position = position;
|
|
marker.transform.localScale = scale;
|
|
|
|
Material material = new Material(Shader.Find("Standard"));
|
|
material.color = color;
|
|
marker.GetComponent<Renderer>().sharedMaterial = material;
|
|
UnityEngine.Object.DestroyImmediate(marker.GetComponent<BoxCollider>());
|
|
}
|
|
|
|
private static void CreateTrainingDummies(Material mannequinMaterial)
|
|
{
|
|
Vector3[] positions =
|
|
{
|
|
new Vector3(-5f, 0f, 3.5f),
|
|
new Vector3(0f, 0f, 5f),
|
|
new Vector3(5f, 0f, 3.5f)
|
|
};
|
|
|
|
for (int i = 0; i < positions.Length; i++)
|
|
{
|
|
GameObject root = new GameObject($"Training Dummy {i + 1}");
|
|
root.transform.position = positions[i];
|
|
|
|
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
|
body.name = "Body";
|
|
body.transform.SetParent(root.transform, false);
|
|
body.transform.localPosition = new Vector3(0f, 0.9f, 0f);
|
|
body.transform.localScale = new Vector3(0.55f, 0.8f, 0.55f);
|
|
body.GetComponent<Renderer>().sharedMaterial = mannequinMaterial;
|
|
|
|
GameObject head = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
|
head.name = "Head";
|
|
head.transform.SetParent(root.transform, false);
|
|
head.transform.localPosition = new Vector3(0f, 1.85f, 0f);
|
|
head.transform.localScale = Vector3.one * 0.55f;
|
|
head.GetComponent<Renderer>().sharedMaterial = mannequinMaterial;
|
|
|
|
GameObject baseObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
baseObject.name = "Base";
|
|
baseObject.transform.SetParent(root.transform, false);
|
|
baseObject.transform.localPosition = new Vector3(0f, 0.1f, 0f);
|
|
baseObject.transform.localScale = new Vector3(1.2f, 0.2f, 1.2f);
|
|
baseObject.GetComponent<Renderer>().sharedMaterial = mannequinMaterial;
|
|
}
|
|
}
|
|
|
|
private static void CreateDecorations(Material decorMaterial)
|
|
{
|
|
Vector3[] positions =
|
|
{
|
|
new Vector3(-9f, 0.5f, -7f),
|
|
new Vector3(8f, 0.5f, -7f),
|
|
new Vector3(-8f, 0.5f, 8f),
|
|
new Vector3(8.5f, 0.5f, 7.5f),
|
|
new Vector3(-2.5f, 0.5f, 8.5f)
|
|
};
|
|
|
|
for (int i = 0; i < positions.Length; i++)
|
|
{
|
|
GameObject decor = GameObject.CreatePrimitive(i % 2 == 0 ? PrimitiveType.Cube : PrimitiveType.Sphere);
|
|
decor.name = $"Arena Decor {i + 1}";
|
|
decor.transform.position = positions[i];
|
|
decor.transform.localScale = i % 2 == 0 ? new Vector3(1.2f, 1f, 1.2f) : Vector3.one * 1.15f;
|
|
decor.GetComponent<Renderer>().sharedMaterial = decorMaterial;
|
|
}
|
|
}
|
|
|
|
private static void CreateExperiencePickups(Material crystalMaterial, LevelSystem levelSystem, NotificationUI notificationUI)
|
|
{
|
|
GameObject root = new GameObject("Experience Pickups");
|
|
Vector3[] positions =
|
|
{
|
|
new Vector3(-6f, 0.55f, -2f),
|
|
new Vector3(-3f, 0.55f, 1.5f),
|
|
new Vector3(2.5f, 0.55f, -1.5f),
|
|
new Vector3(5.5f, 0.55f, 1.8f),
|
|
new Vector3(0f, 0.55f, 8f)
|
|
};
|
|
|
|
for (int i = 0; i < positions.Length; i++)
|
|
{
|
|
GameObject crystal = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
|
crystal.name = $"Experience Crystal {i + 1}";
|
|
crystal.transform.SetParent(root.transform, true);
|
|
crystal.transform.position = positions[i];
|
|
crystal.transform.localScale = new Vector3(0.55f, 0.75f, 0.55f);
|
|
crystal.GetComponent<Renderer>().sharedMaterial = crystalMaterial;
|
|
|
|
Collider collider = crystal.GetComponent<Collider>();
|
|
if (collider != null)
|
|
{
|
|
collider.isTrigger = true;
|
|
}
|
|
|
|
ExperiencePickup pickup = crystal.AddComponent<ExperiencePickup>();
|
|
pickup.experienceAmount = 50;
|
|
pickup.destroyOnCollect = true;
|
|
pickup.levelSystem = levelSystem;
|
|
pickup.notificationUI = notificationUI;
|
|
|
|
Light light = crystal.AddComponent<Light>();
|
|
light.type = LightType.Point;
|
|
light.color = new Color(0.16f, 0.78f, 0.96f, 1f);
|
|
light.range = 3.2f;
|
|
light.intensity = 1.5f;
|
|
}
|
|
}
|
|
|
|
private static Canvas CreateCanvas()
|
|
{
|
|
GameObject canvasObject = new GameObject("Canvas", typeof(RectTransform), 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.matchWidthOrHeight = 0.5f;
|
|
|
|
return canvas;
|
|
}
|
|
|
|
private static void EnsureEventSystem()
|
|
{
|
|
if (UnityEngine.Object.FindObjectOfType<EventSystem>() != null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
|
|
}
|
|
|
|
private static NotificationUI CreateNotificationUI(Transform parent, Font font)
|
|
{
|
|
Image panel = CreateUIImage(parent, "Notification", new Color(0.07f, 0.08f, 0.10f, 0.92f));
|
|
SetRect(panel.rectTransform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -18f), new Vector2(560f, 48f));
|
|
CanvasGroup canvasGroup = panel.gameObject.AddComponent<CanvasGroup>();
|
|
|
|
Text messageText = CreateText(panel.transform, "Message", font, "", 18, UITheme.Text, TextAnchor.MiddleCenter);
|
|
Stretch(messageText.rectTransform, Vector2.zero, Vector2.one, new Vector2(14f, 6f), new Vector2(-14f, -6f));
|
|
|
|
NotificationUI notification = panel.gameObject.AddComponent<NotificationUI>();
|
|
notification.messageText = messageText;
|
|
notification.canvasGroup = canvasGroup;
|
|
return notification;
|
|
}
|
|
|
|
private static void CreateLevelHud(Transform parent, Font regularFont, Font boldFont, LevelSystem levelSystem)
|
|
{
|
|
Image panel = CreateUIImage(parent, "Level HUD", UITheme.PanelSoft);
|
|
SetRect(panel.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(16f, -16f), new Vector2(300f, 98f));
|
|
|
|
Text levelText = CreateText(panel.transform, "Level Text", boldFont, "Уровень 1", 20, UITheme.Gold, TextAnchor.UpperLeft);
|
|
SetRect(levelText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(16f, -12f), new Vector2(-32f, 28f));
|
|
|
|
Image expBackground = CreateUIImage(panel.transform, "Exp Bar Background", new Color(0.03f, 0.035f, 0.04f, 0.95f));
|
|
SetRect(expBackground.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 18f), new Vector2(-32f, 18f));
|
|
|
|
Image expFill = CreateUIImage(expBackground.transform, "Exp Bar Fill", new Color(0.20f, 0.78f, 0.96f, 1f));
|
|
Stretch(expFill.rectTransform, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
|
|
expFill.type = Image.Type.Filled;
|
|
expFill.fillMethod = Image.FillMethod.Horizontal;
|
|
expFill.fillOrigin = 0;
|
|
|
|
Text expText = CreateText(panel.transform, "Exp Text", regularFont, "0 / 100 XP", 14, UITheme.Text, TextAnchor.MiddleCenter);
|
|
SetRect(expText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 17f), new Vector2(-32f, 20f));
|
|
|
|
levelSystem.levelText = levelText;
|
|
levelSystem.expText = expText;
|
|
levelSystem.expFillImage = expFill;
|
|
}
|
|
|
|
private static StatsPanelUI CreateStatsHud(Transform parent, Font regularFont, Font boldFont, PlayerStats playerStats)
|
|
{
|
|
return CreateStatsPanel(parent, "Stats HUD", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-16f, -16f), new Vector2(300f, 230f), regularFont, boldFont, playerStats);
|
|
}
|
|
|
|
private static TalentTreeUI CreateTalentTree(Transform parent, Font regularFont, Font boldFont, TalentNodeUI nodePrefab, TalentManager manager, PlayerStats playerStats)
|
|
{
|
|
TalentTreeUI treeUI = parent.gameObject.AddComponent<TalentTreeUI>();
|
|
treeUI.manager = manager;
|
|
treeUI.playerStats = playerStats;
|
|
treeUI.nodePrefab = nodePrefab;
|
|
treeUI.openOnStart = false;
|
|
|
|
Image overlay = CreateUIImage(parent, "Talent Tree Panel", UITheme.Panel);
|
|
Stretch(overlay.rectTransform, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
|
|
treeUI.panelRoot = overlay.gameObject;
|
|
|
|
Text title = CreateText(overlay.transform, "Title", boldFont, "Дерево талантов", 30, UITheme.Gold, TextAnchor.MiddleLeft);
|
|
SetRect(title.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -18f), new Vector2(420f, 46f));
|
|
treeUI.titleText = title;
|
|
|
|
Text points = CreateText(overlay.transform, "Talent Points", boldFont, "Очки талантов: 1", 22, UITheme.Text, TextAnchor.MiddleLeft);
|
|
SetRect(points.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(470f, -22f), new Vector2(300f, 40f));
|
|
treeUI.pointsText = points;
|
|
|
|
Button saveButton = CreateButton(overlay.transform, "Save Button", regularFont, "Save", new Color(0.18f, 0.21f, 0.25f, 1f));
|
|
SetRect(saveButton.GetComponent<RectTransform>(), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-292f, -20f), new Vector2(82f, 36f));
|
|
treeUI.saveButton = saveButton;
|
|
|
|
Button loadButton = CreateButton(overlay.transform, "Load Button", regularFont, "Load", new Color(0.18f, 0.21f, 0.25f, 1f));
|
|
SetRect(loadButton.GetComponent<RectTransform>(), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-198f, -20f), new Vector2(82f, 36f));
|
|
treeUI.loadButton = loadButton;
|
|
|
|
Button resetButton = CreateButton(overlay.transform, "Reset Button", regularFont, "Reset", new Color(0.30f, 0.17f, 0.17f, 1f));
|
|
SetRect(resetButton.GetComponent<RectTransform>(), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-104f, -20f), new Vector2(82f, 36f));
|
|
treeUI.resetButton = resetButton;
|
|
|
|
Image treePanel = CreateUIImage(overlay.transform, "Talent Tree Area", UITheme.PanelSoft);
|
|
Stretch(treePanel.rectTransform, Vector2.zero, Vector2.one, new Vector2(24f, 78f), new Vector2(-360f, -78f));
|
|
|
|
RectTransform lineRoot = CreateUIRoot(treePanel.transform, "Lines");
|
|
Stretch(lineRoot, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
|
|
treeUI.lineRoot = lineRoot;
|
|
|
|
RectTransform nodesRoot = CreateUIRoot(treePanel.transform, "Nodes");
|
|
Stretch(nodesRoot, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
|
|
treeUI.nodesRoot = nodesRoot;
|
|
|
|
StatsPanelUI treeStats = CreateStatsPanel(overlay.transform, "Tree Stats Panel", new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -90f), new Vector2(320f, 260f), regularFont, boldFont, playerStats);
|
|
treeUI.statsPanel = treeStats;
|
|
|
|
Text controls = CreateText(overlay.transform, "Controls", regularFont, "", 17, UITheme.MutedText, TextAnchor.MiddleLeft);
|
|
SetRect(controls.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(28f, 20f), new Vector2(-56f, 34f));
|
|
treeUI.controlsText = controls;
|
|
|
|
TalentTooltipUI tooltip = CreateTooltip(overlay.transform, regularFont, boldFont);
|
|
treeUI.tooltip = tooltip;
|
|
|
|
return treeUI;
|
|
}
|
|
|
|
private static TalentTooltipUI CreateTooltip(Transform parent, Font regularFont, Font boldFont)
|
|
{
|
|
Image panel = CreateUIImage(parent, "Talent Tooltip", new Color(0.05f, 0.055f, 0.07f, 0.98f));
|
|
SetRect(panel.rectTransform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -120f), new Vector2(330f, 260f));
|
|
CanvasGroup canvasGroup = panel.gameObject.AddComponent<CanvasGroup>();
|
|
|
|
TalentTooltipUI tooltip = panel.gameObject.AddComponent<TalentTooltipUI>();
|
|
tooltip.panelRoot = panel.rectTransform;
|
|
tooltip.canvasGroup = canvasGroup;
|
|
|
|
tooltip.titleText = CreateText(panel.transform, "Tooltip Title", boldFont, "", 20, UITheme.Gold, TextAnchor.UpperLeft);
|
|
SetRect(tooltip.titleText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(14f, -12f), new Vector2(-28f, 30f));
|
|
|
|
tooltip.descriptionText = CreateText(panel.transform, "Tooltip Description", regularFont, "", 14, UITheme.Text, TextAnchor.UpperLeft);
|
|
tooltip.descriptionText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
SetRect(tooltip.descriptionText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(14f, -46f), new Vector2(-28f, 46f));
|
|
|
|
tooltip.costText = CreateText(panel.transform, "Tooltip Cost", regularFont, "", 14, UITheme.Text, TextAnchor.UpperLeft);
|
|
SetRect(tooltip.costText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(14f, -92f), new Vector2(-28f, 22f));
|
|
|
|
tooltip.modifiersText = CreateText(panel.transform, "Tooltip Modifiers", regularFont, "", 14, UITheme.Text, TextAnchor.UpperLeft);
|
|
tooltip.modifiersText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
tooltip.modifiersText.verticalOverflow = VerticalWrapMode.Overflow;
|
|
SetRect(tooltip.modifiersText.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(14f, -118f), new Vector2(-28f, 70f));
|
|
|
|
tooltip.requirementsText = CreateText(panel.transform, "Tooltip Requirements", regularFont, "", 14, UITheme.MutedText, TextAnchor.UpperLeft);
|
|
tooltip.requirementsText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
SetRect(tooltip.requirementsText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(14f, 54f), new Vector2(-28f, 32f));
|
|
|
|
tooltip.statusText = CreateText(panel.transform, "Tooltip Status", boldFont, "", 14, UITheme.Gold, TextAnchor.UpperLeft);
|
|
SetRect(tooltip.statusText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(14f, 30f), new Vector2(-28f, 24f));
|
|
|
|
tooltip.reasonText = CreateText(panel.transform, "Tooltip Reason", regularFont, "", 13, UITheme.MutedText, TextAnchor.UpperLeft);
|
|
tooltip.reasonText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
SetRect(tooltip.reasonText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(0f, 0f), new Vector2(14f, 8f), new Vector2(-28f, 26f));
|
|
|
|
return tooltip;
|
|
}
|
|
|
|
private static StatsPanelUI CreateStatsPanel(Transform parent, string name, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 position, Vector2 size, Font regularFont, Font boldFont, PlayerStats playerStats)
|
|
{
|
|
Image panel = CreateUIImage(parent, name, UITheme.PanelSoft);
|
|
SetRect(panel.rectTransform, anchorMin, anchorMax, pivot, position, size);
|
|
|
|
StatsPanelUI statsPanel = panel.gameObject.AddComponent<StatsPanelUI>();
|
|
statsPanel.playerStats = playerStats;
|
|
|
|
Text title = CreateText(panel.transform, "Title", boldFont, "Характеристики", 18, UITheme.Gold, TextAnchor.UpperLeft);
|
|
SetRect(title.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(16f, -14f), new Vector2(-32f, 28f));
|
|
statsPanel.titleText = title;
|
|
|
|
statsPanel.maxHealthText = CreateStatLine(panel.transform, regularFont, "Max Health: 100", 50f);
|
|
statsPanel.maxManaText = CreateStatLine(panel.transform, regularFont, "Max Mana: 100", 78f);
|
|
statsPanel.damageText = CreateStatLine(panel.transform, regularFont, "Damage: 10", 106f);
|
|
statsPanel.moveSpeedText = CreateStatLine(panel.transform, regularFont, "Move Speed: 5", 134f);
|
|
statsPanel.cooldownReductionText = CreateStatLine(panel.transform, regularFont, "Cooldown Reduction: 0%", 162f);
|
|
statsPanel.criticalChanceText = CreateStatLine(panel.transform, regularFont, "Critical Chance: 0%", 190f);
|
|
statsPanel.Refresh();
|
|
return statsPanel;
|
|
}
|
|
|
|
private static Text CreateStatLine(Transform parent, Font font, string text, float topOffset)
|
|
{
|
|
Text line = CreateText(parent, text.Replace(":", string.Empty), font, text, 15, UITheme.Text, TextAnchor.UpperLeft);
|
|
line.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
SetRect(line.rectTransform, new Vector2(0f, 1f), new Vector2(1f, 1f), new Vector2(0f, 1f), new Vector2(16f, -topOffset), new Vector2(-32f, 24f));
|
|
return line;
|
|
}
|
|
|
|
private static Image CreateUIImage(Transform parent, string name, Color color)
|
|
{
|
|
GameObject gameObject = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
|
gameObject.transform.SetParent(parent, false);
|
|
Image image = gameObject.GetComponent<Image>();
|
|
image.color = color;
|
|
return image;
|
|
}
|
|
|
|
private static RectTransform CreateUIRoot(Transform parent, string name)
|
|
{
|
|
GameObject gameObject = new GameObject(name, typeof(RectTransform));
|
|
gameObject.transform.SetParent(parent, false);
|
|
return gameObject.GetComponent<RectTransform>();
|
|
}
|
|
|
|
private static Text CreateText(Transform parent, string name, Font font, string text, int size, Color color, TextAnchor alignment)
|
|
{
|
|
GameObject gameObject = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
|
gameObject.transform.SetParent(parent, false);
|
|
Text label = gameObject.GetComponent<Text>();
|
|
label.font = font != null ? font : Resources.GetBuiltinResource<Font>("Arial.ttf");
|
|
label.text = text;
|
|
label.fontSize = size;
|
|
label.color = color;
|
|
label.alignment = alignment;
|
|
label.raycastTarget = false;
|
|
label.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
label.verticalOverflow = VerticalWrapMode.Truncate;
|
|
return label;
|
|
}
|
|
|
|
private static Button CreateButton(Transform parent, string name, Font font, string text, Color backgroundColor)
|
|
{
|
|
Image background = CreateUIImage(parent, name, backgroundColor);
|
|
Button button = background.gameObject.AddComponent<Button>();
|
|
button.targetGraphic = background;
|
|
|
|
Text label = CreateText(background.transform, "Label", font, text, 16, UITheme.Text, TextAnchor.MiddleCenter);
|
|
Stretch(label.rectTransform, Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero);
|
|
|
|
ColorBlock colors = button.colors;
|
|
colors.normalColor = Color.white;
|
|
colors.highlightedColor = new Color(1f, 0.92f, 0.72f, 1f);
|
|
colors.pressedColor = new Color(0.82f, 0.70f, 0.46f, 1f);
|
|
colors.disabledColor = new Color(0.46f, 0.46f, 0.46f, 1f);
|
|
button.colors = colors;
|
|
|
|
return button;
|
|
}
|
|
|
|
private static void SetRect(RectTransform rectTransform, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Vector2 anchoredPosition, Vector2 sizeDelta)
|
|
{
|
|
rectTransform.anchorMin = anchorMin;
|
|
rectTransform.anchorMax = anchorMax;
|
|
rectTransform.pivot = pivot;
|
|
rectTransform.anchoredPosition = anchoredPosition;
|
|
rectTransform.sizeDelta = sizeDelta;
|
|
}
|
|
|
|
private static void Stretch(RectTransform rectTransform, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax)
|
|
{
|
|
rectTransform.anchorMin = anchorMin;
|
|
rectTransform.anchorMax = anchorMax;
|
|
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
|
rectTransform.offsetMin = offsetMin;
|
|
rectTransform.offsetMax = offsetMax;
|
|
}
|
|
|
|
private static string ToSystemPath(string assetPath)
|
|
{
|
|
return Path.Combine(Directory.GetCurrentDirectory(), assetPath);
|
|
}
|
|
|
|
private static int ValidateTalentPrerequisites(TalentManager talentManager)
|
|
{
|
|
int errors = 0;
|
|
Dictionary<string, TalentData> byId = new Dictionary<string, TalentData>();
|
|
for (int i = 0; i < talentManager.allTalents.Count; i++)
|
|
{
|
|
TalentData talent = talentManager.allTalents[i];
|
|
if (talent == null)
|
|
{
|
|
errors += RequireCondition(false, "TalentManager contains a null talent reference.");
|
|
continue;
|
|
}
|
|
|
|
if (byId.ContainsKey(talent.talentId))
|
|
{
|
|
errors += RequireCondition(false, $"Duplicate talent id: {talent.talentId}");
|
|
continue;
|
|
}
|
|
|
|
byId.Add(talent.talentId, talent);
|
|
}
|
|
|
|
string[] requiredIds =
|
|
{
|
|
"Toughness",
|
|
"Strength",
|
|
"Agility",
|
|
"AdvancedToughness",
|
|
"BattleTraining",
|
|
"SwiftStep",
|
|
"ManaFlow",
|
|
"ArcaneFocus",
|
|
"CriticalEye",
|
|
"MasterWarrior"
|
|
};
|
|
|
|
for (int i = 0; i < requiredIds.Length; i++)
|
|
{
|
|
errors += RequireCondition(byId.ContainsKey(requiredIds[i]), $"Missing talent id: {requiredIds[i]}");
|
|
}
|
|
|
|
errors += RequirePrerequisites(byId, "AdvancedToughness", "Toughness");
|
|
errors += RequirePrerequisites(byId, "BattleTraining", "Strength");
|
|
errors += RequirePrerequisites(byId, "SwiftStep", "Agility");
|
|
errors += RequirePrerequisites(byId, "ArcaneFocus", "ManaFlow");
|
|
errors += RequirePrerequisites(byId, "CriticalEye", "Strength");
|
|
errors += RequirePrerequisites(byId, "MasterWarrior", "BattleTraining", "AdvancedToughness");
|
|
return errors;
|
|
}
|
|
|
|
private static int RequirePrerequisites(Dictionary<string, TalentData> byId, string talentId, params string[] prerequisiteIds)
|
|
{
|
|
if (!byId.ContainsKey(talentId))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
TalentData talent = byId[talentId];
|
|
for (int i = 0; i < prerequisiteIds.Length; i++)
|
|
{
|
|
if (!byId.ContainsKey(prerequisiteIds[i]) || talent.prerequisites == null || Array.IndexOf(talent.prerequisites, byId[prerequisiteIds[i]]) < 0)
|
|
{
|
|
Debug.LogError($"{talentId} is missing prerequisite {prerequisiteIds[i]}.");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static int ValidateMissingScripts(Scene scene)
|
|
{
|
|
int errors = 0;
|
|
GameObject[] roots = scene.GetRootGameObjects();
|
|
for (int i = 0; i < roots.Length; i++)
|
|
{
|
|
Transform[] transforms = roots[i].GetComponentsInChildren<Transform>(true);
|
|
for (int transformIndex = 0; transformIndex < transforms.Length; transformIndex++)
|
|
{
|
|
GameObject gameObject = transforms[transformIndex].gameObject;
|
|
int missingCount = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject);
|
|
if (missingCount > 0)
|
|
{
|
|
Debug.LogError($"{gameObject.name} has missing scripts: {missingCount}");
|
|
errors += missingCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private static int ValidateUbuntuFonts(Scene scene)
|
|
{
|
|
Font regular = AssetDatabase.LoadAssetAtPath<Font>("Assets/_Fonts/Ubuntu-Regular.ttf");
|
|
Font bold = AssetDatabase.LoadAssetAtPath<Font>("Assets/_Fonts/Ubuntu-Bold.ttf");
|
|
int errors = 0;
|
|
|
|
Text[] texts = UnityEngine.Object.FindObjectsOfType<Text>(true);
|
|
for (int i = 0; i < texts.Length; i++)
|
|
{
|
|
Text text = texts[i];
|
|
if (text.gameObject.scene != scene)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (text.font != regular && text.font != bold)
|
|
{
|
|
Debug.LogError($"Text object does not use local Ubuntu font: {text.name}");
|
|
errors++;
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
private static int RequireObject(UnityEngine.Object value, string label)
|
|
{
|
|
if (value != null)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Debug.LogError($"Missing required reference: {label}");
|
|
return 1;
|
|
}
|
|
|
|
private static int RequireCondition(bool condition, string message)
|
|
{
|
|
if (condition)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
Debug.LogError(message);
|
|
return 1;
|
|
}
|
|
|
|
private sealed class TalentSpec
|
|
{
|
|
public readonly string id;
|
|
public readonly string displayName;
|
|
public readonly string description;
|
|
public readonly int cost;
|
|
public readonly Vector2 treePosition;
|
|
public readonly Color branchColor;
|
|
public readonly StatModifier[] modifiers;
|
|
public readonly string[] prerequisiteIds;
|
|
|
|
public TalentSpec(string id, string displayName, string description, int cost, Vector2 treePosition, Color branchColor, StatModifier modifier, string[] prerequisiteIds)
|
|
: this(id, displayName, description, cost, treePosition, branchColor, new[] { modifier }, prerequisiteIds)
|
|
{
|
|
}
|
|
|
|
public TalentSpec(string id, string displayName, string description, int cost, Vector2 treePosition, Color branchColor, StatModifier[] modifiers, string[] prerequisiteIds)
|
|
{
|
|
this.id = id;
|
|
this.displayName = displayName;
|
|
this.description = description;
|
|
this.cost = cost;
|
|
this.treePosition = treePosition;
|
|
this.branchColor = branchColor;
|
|
this.modifiers = modifiers;
|
|
this.prerequisiteIds = prerequisiteIds;
|
|
}
|
|
}
|
|
}
|