From a70f7fe2b97e9d9aca8f6d736ee997468492c872 Mon Sep 17 00:00:00 2001 From: Dmitry Evdokimov Date: Thu, 4 Jun 2026 20:14:47 +0300 Subject: [PATCH] first commit --- .gitignore | 74 + .vscode/extensions.json | 5 + .vscode/launch.json | 10 + .vscode/settings.json | 55 + Assets/Editor.meta | 8 + Assets/Editor/Lab5SceneBuilder.cs | 1117 ++ Assets/Editor/Lab5SceneBuilder.cs.meta | 11 + Assets/Scenes.meta | 8 + Assets/Scenes/SampleScene.unity | 267 + Assets/Scenes/SampleScene.unity.meta | 7 + Assets/_Data.meta | 8 + Assets/_Data/Enemies.meta | 8 + Assets/_Data/Items.meta | 8 + Assets/_Data/Items/GoldOre.asset | 20 + Assets/_Data/Items/GoldOre.asset.meta | 8 + Assets/_Data/Items/Stone.asset | 19 + Assets/_Data/Items/Stone.asset.meta | 8 + Assets/_Data/Items/Wood.asset | 20 + Assets/_Data/Items/Wood.asset.meta | 8 + Assets/_Data/Quests.meta | 8 + Assets/_Data/Quests/quest_clear_camp.asset | 25 + .../_Data/Quests/quest_clear_camp.asset.meta | 8 + .../_Data/Quests/quest_help_lumberjack.asset | 25 + .../Quests/quest_help_lumberjack.asset.meta | 8 + Assets/_Data/VillageElderDialogue.asset | 26 + Assets/_Data/VillageElderDialogue.asset.meta | 8 + Assets/_Fonts.meta | 8 + Assets/_Fonts/Ubuntu-R.ttf | Bin 0 -> 353824 bytes Assets/_Fonts/Ubuntu-R.ttf.meta | 21 + Assets/_Materials.meta | 8 + Assets/_Materials/M_Central_Square.mat | 83 + Assets/_Materials/M_Central_Square.mat.meta | 8 + Assets/_Materials/M_Elder.mat | 83 + Assets/_Materials/M_Elder.mat.meta | 8 + Assets/_Materials/M_Forest_Dark.mat | 83 + Assets/_Materials/M_Forest_Dark.mat.meta | 8 + Assets/_Materials/M_Goblin.mat | 83 + Assets/_Materials/M_Goblin.mat.meta | 8 + Assets/_Materials/M_Goblin_Camp.mat | 83 + Assets/_Materials/M_Goblin_Camp.mat.meta | 8 + Assets/_Materials/M_GoldOre.mat | 83 + Assets/_Materials/M_GoldOre.mat.meta | 8 + Assets/_Materials/M_Gold_Accent.mat | 83 + Assets/_Materials/M_Gold_Accent.mat.meta | 8 + Assets/_Materials/M_Ground_Grass.mat | 83 + Assets/_Materials/M_Ground_Grass.mat.meta | 8 + Assets/_Materials/M_Leaves.mat | 83 + Assets/_Materials/M_Leaves.mat.meta | 8 + Assets/_Materials/M_Player.mat | 83 + Assets/_Materials/M_Player.mat.meta | 8 + Assets/_Materials/M_Quarry_Stone.mat | 83 + Assets/_Materials/M_Quarry_Stone.mat.meta | 8 + Assets/_Materials/M_Stone.mat | 83 + Assets/_Materials/M_Stone.mat.meta | 8 + Assets/_Materials/M_Tent_Red.mat | 83 + Assets/_Materials/M_Tent_Red.mat.meta | 8 + Assets/_Materials/M_Wood.mat | 83 + Assets/_Materials/M_Wood.mat.meta | 8 + Assets/_Prefabs.meta | 8 + Assets/_Prefabs/Goblin.prefab | 230 + Assets/_Prefabs/Goblin.prefab.meta | 7 + Assets/_Prefabs/GoldOre_Resource.prefab | 124 + Assets/_Prefabs/GoldOre_Resource.prefab.meta | 7 + Assets/_Prefabs/NPC_Starosta.prefab | 232 + Assets/_Prefabs/NPC_Starosta.prefab.meta | 7 + Assets/_Prefabs/Player.prefab | 184 + Assets/_Prefabs/Player.prefab.meta | 7 + Assets/_Prefabs/Stone_Resource.prefab | 124 + Assets/_Prefabs/Stone_Resource.prefab.meta | 7 + Assets/_Prefabs/Wood_Resource.prefab | 126 + Assets/_Prefabs/Wood_Resource.prefab.meta | 7 + Assets/_Scenes.meta | 8 + Assets/_Scenes/Lab05_QuestSystem.unity | 11064 ++++++++++++++++ Assets/_Scenes/Lab05_QuestSystem.unity.meta | 7 + Assets/_Scripts.meta | 8 + Assets/_Scripts/Dialogue.meta | 8 + Assets/_Scripts/Dialogue/DialogueData.cs | 18 + Assets/_Scripts/Dialogue/DialogueData.cs.meta | 11 + Assets/_Scripts/Dialogue/NPCDialogue.cs | 81 + Assets/_Scripts/Dialogue/NPCDialogue.cs.meta | 11 + Assets/_Scripts/Gameplay.meta | 8 + Assets/_Scripts/Gameplay/Enemy.cs | 48 + Assets/_Scripts/Gameplay/Enemy.cs.meta | 11 + Assets/_Scripts/Gameplay/PlayerCombat.cs | 56 + Assets/_Scripts/Gameplay/PlayerCombat.cs.meta | 11 + Assets/_Scripts/Gameplay/ResourceNode.cs | 62 + Assets/_Scripts/Gameplay/ResourceNode.cs.meta | 11 + .../Gameplay/SimplePlayerController.cs | 135 + .../Gameplay/SimplePlayerController.cs.meta | 11 + Assets/_Scripts/Inventory.meta | 8 + Assets/_Scripts/Inventory/Inventory.cs | 98 + Assets/_Scripts/Inventory/Inventory.cs.meta | 11 + Assets/_Scripts/Inventory/ItemData.cs | 11 + Assets/_Scripts/Inventory/ItemData.cs.meta | 11 + Assets/_Scripts/QuestSystem.meta | 8 + Assets/_Scripts/QuestSystem/QuestData.cs | 13 + Assets/_Scripts/QuestSystem/QuestData.cs.meta | 11 + Assets/_Scripts/QuestSystem/QuestGoal.cs | 49 + Assets/_Scripts/QuestSystem/QuestGoal.cs.meta | 11 + Assets/_Scripts/QuestSystem/QuestManager.cs | 276 + .../_Scripts/QuestSystem/QuestManager.cs.meta | 11 + Assets/_Scripts/QuestSystem/QuestState.cs | 76 + .../_Scripts/QuestSystem/QuestState.cs.meta | 11 + Assets/_Scripts/QuestSystem/QuestStatus.cs | 6 + .../_Scripts/QuestSystem/QuestStatus.cs.meta | 11 + Assets/_Scripts/UI.meta | 8 + Assets/_Scripts/UI/DialogueUI.cs | 108 + Assets/_Scripts/UI/DialogueUI.cs.meta | 11 + Assets/_Scripts/UI/InteractionPromptUI.cs | 41 + .../_Scripts/UI/InteractionPromptUI.cs.meta | 11 + Assets/_Scripts/UI/InventoryUI.cs | 158 + Assets/_Scripts/UI/InventoryUI.cs.meta | 11 + Assets/_Scripts/UI/NotificationUI.cs | 67 + Assets/_Scripts/UI/NotificationUI.cs.meta | 11 + Assets/_Scripts/UI/QuestLogUI.cs | 208 + Assets/_Scripts/UI/QuestLogUI.cs.meta | 11 + Assets/_Scripts/UI/QuestTrackerUI.cs | 181 + Assets/_Scripts/UI/QuestTrackerUI.cs.meta | 11 + Packages/manifest.json | 41 + Packages/packages-lock.json | 382 + ProjectSettings/AudioManager.asset | 19 + ProjectSettings/ClusterInputManager.asset | 6 + ProjectSettings/DynamicsManager.asset | 34 + ProjectSettings/EditorBuildSettings.asset | 11 + ProjectSettings/EditorSettings.asset | 30 + ProjectSettings/GraphicsSettings.asset | 63 + ProjectSettings/InputManager.asset | 295 + ProjectSettings/MemorySettings.asset | 35 + ProjectSettings/NavMeshAreas.asset | 91 + ProjectSettings/PackageManagerSettings.asset | 35 + .../Settings.json | 5 + ProjectSettings/Physics2DSettings.asset | 56 + ProjectSettings/PresetManager.asset | 7 + ProjectSettings/ProjectSettings.asset | 772 ++ ProjectSettings/ProjectVersion.txt | 2 + ProjectSettings/QualitySettings.asset | 234 + ProjectSettings/SceneTemplateSettings.json | 121 + ProjectSettings/TagManager.asset | 43 + ProjectSettings/TimeManager.asset | 9 + ProjectSettings/UnityConnectSettings.asset | 36 + ProjectSettings/VFXManager.asset | 12 + ProjectSettings/VersionControlSettings.asset | 8 + ProjectSettings/XRSettings.asset | 10 + 143 files changed, 19543 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 Assets/Editor.meta create mode 100644 Assets/Editor/Lab5SceneBuilder.cs create mode 100644 Assets/Editor/Lab5SceneBuilder.cs.meta create mode 100644 Assets/Scenes.meta create mode 100644 Assets/Scenes/SampleScene.unity create mode 100644 Assets/Scenes/SampleScene.unity.meta create mode 100644 Assets/_Data.meta create mode 100644 Assets/_Data/Enemies.meta create mode 100644 Assets/_Data/Items.meta create mode 100644 Assets/_Data/Items/GoldOre.asset create mode 100644 Assets/_Data/Items/GoldOre.asset.meta create mode 100644 Assets/_Data/Items/Stone.asset create mode 100644 Assets/_Data/Items/Stone.asset.meta create mode 100644 Assets/_Data/Items/Wood.asset create mode 100644 Assets/_Data/Items/Wood.asset.meta create mode 100644 Assets/_Data/Quests.meta create mode 100644 Assets/_Data/Quests/quest_clear_camp.asset create mode 100644 Assets/_Data/Quests/quest_clear_camp.asset.meta create mode 100644 Assets/_Data/Quests/quest_help_lumberjack.asset create mode 100644 Assets/_Data/Quests/quest_help_lumberjack.asset.meta create mode 100644 Assets/_Data/VillageElderDialogue.asset create mode 100644 Assets/_Data/VillageElderDialogue.asset.meta create mode 100644 Assets/_Fonts.meta create mode 100644 Assets/_Fonts/Ubuntu-R.ttf create mode 100644 Assets/_Fonts/Ubuntu-R.ttf.meta create mode 100644 Assets/_Materials.meta create mode 100644 Assets/_Materials/M_Central_Square.mat create mode 100644 Assets/_Materials/M_Central_Square.mat.meta create mode 100644 Assets/_Materials/M_Elder.mat create mode 100644 Assets/_Materials/M_Elder.mat.meta create mode 100644 Assets/_Materials/M_Forest_Dark.mat create mode 100644 Assets/_Materials/M_Forest_Dark.mat.meta create mode 100644 Assets/_Materials/M_Goblin.mat create mode 100644 Assets/_Materials/M_Goblin.mat.meta create mode 100644 Assets/_Materials/M_Goblin_Camp.mat create mode 100644 Assets/_Materials/M_Goblin_Camp.mat.meta create mode 100644 Assets/_Materials/M_GoldOre.mat create mode 100644 Assets/_Materials/M_GoldOre.mat.meta create mode 100644 Assets/_Materials/M_Gold_Accent.mat create mode 100644 Assets/_Materials/M_Gold_Accent.mat.meta create mode 100644 Assets/_Materials/M_Ground_Grass.mat create mode 100644 Assets/_Materials/M_Ground_Grass.mat.meta create mode 100644 Assets/_Materials/M_Leaves.mat create mode 100644 Assets/_Materials/M_Leaves.mat.meta create mode 100644 Assets/_Materials/M_Player.mat create mode 100644 Assets/_Materials/M_Player.mat.meta create mode 100644 Assets/_Materials/M_Quarry_Stone.mat create mode 100644 Assets/_Materials/M_Quarry_Stone.mat.meta create mode 100644 Assets/_Materials/M_Stone.mat create mode 100644 Assets/_Materials/M_Stone.mat.meta create mode 100644 Assets/_Materials/M_Tent_Red.mat create mode 100644 Assets/_Materials/M_Tent_Red.mat.meta create mode 100644 Assets/_Materials/M_Wood.mat create mode 100644 Assets/_Materials/M_Wood.mat.meta create mode 100644 Assets/_Prefabs.meta create mode 100644 Assets/_Prefabs/Goblin.prefab create mode 100644 Assets/_Prefabs/Goblin.prefab.meta create mode 100644 Assets/_Prefabs/GoldOre_Resource.prefab create mode 100644 Assets/_Prefabs/GoldOre_Resource.prefab.meta create mode 100644 Assets/_Prefabs/NPC_Starosta.prefab create mode 100644 Assets/_Prefabs/NPC_Starosta.prefab.meta create mode 100644 Assets/_Prefabs/Player.prefab create mode 100644 Assets/_Prefabs/Player.prefab.meta create mode 100644 Assets/_Prefabs/Stone_Resource.prefab create mode 100644 Assets/_Prefabs/Stone_Resource.prefab.meta create mode 100644 Assets/_Prefabs/Wood_Resource.prefab create mode 100644 Assets/_Prefabs/Wood_Resource.prefab.meta create mode 100644 Assets/_Scenes.meta create mode 100644 Assets/_Scenes/Lab05_QuestSystem.unity create mode 100644 Assets/_Scenes/Lab05_QuestSystem.unity.meta create mode 100644 Assets/_Scripts.meta create mode 100644 Assets/_Scripts/Dialogue.meta create mode 100644 Assets/_Scripts/Dialogue/DialogueData.cs create mode 100644 Assets/_Scripts/Dialogue/DialogueData.cs.meta create mode 100644 Assets/_Scripts/Dialogue/NPCDialogue.cs create mode 100644 Assets/_Scripts/Dialogue/NPCDialogue.cs.meta create mode 100644 Assets/_Scripts/Gameplay.meta create mode 100644 Assets/_Scripts/Gameplay/Enemy.cs create mode 100644 Assets/_Scripts/Gameplay/Enemy.cs.meta create mode 100644 Assets/_Scripts/Gameplay/PlayerCombat.cs create mode 100644 Assets/_Scripts/Gameplay/PlayerCombat.cs.meta create mode 100644 Assets/_Scripts/Gameplay/ResourceNode.cs create mode 100644 Assets/_Scripts/Gameplay/ResourceNode.cs.meta create mode 100644 Assets/_Scripts/Gameplay/SimplePlayerController.cs create mode 100644 Assets/_Scripts/Gameplay/SimplePlayerController.cs.meta create mode 100644 Assets/_Scripts/Inventory.meta create mode 100644 Assets/_Scripts/Inventory/Inventory.cs create mode 100644 Assets/_Scripts/Inventory/Inventory.cs.meta create mode 100644 Assets/_Scripts/Inventory/ItemData.cs create mode 100644 Assets/_Scripts/Inventory/ItemData.cs.meta create mode 100644 Assets/_Scripts/QuestSystem.meta create mode 100644 Assets/_Scripts/QuestSystem/QuestData.cs create mode 100644 Assets/_Scripts/QuestSystem/QuestData.cs.meta create mode 100644 Assets/_Scripts/QuestSystem/QuestGoal.cs create mode 100644 Assets/_Scripts/QuestSystem/QuestGoal.cs.meta create mode 100644 Assets/_Scripts/QuestSystem/QuestManager.cs create mode 100644 Assets/_Scripts/QuestSystem/QuestManager.cs.meta create mode 100644 Assets/_Scripts/QuestSystem/QuestState.cs create mode 100644 Assets/_Scripts/QuestSystem/QuestState.cs.meta create mode 100644 Assets/_Scripts/QuestSystem/QuestStatus.cs create mode 100644 Assets/_Scripts/QuestSystem/QuestStatus.cs.meta create mode 100644 Assets/_Scripts/UI.meta create mode 100644 Assets/_Scripts/UI/DialogueUI.cs create mode 100644 Assets/_Scripts/UI/DialogueUI.cs.meta create mode 100644 Assets/_Scripts/UI/InteractionPromptUI.cs create mode 100644 Assets/_Scripts/UI/InteractionPromptUI.cs.meta create mode 100644 Assets/_Scripts/UI/InventoryUI.cs create mode 100644 Assets/_Scripts/UI/InventoryUI.cs.meta create mode 100644 Assets/_Scripts/UI/NotificationUI.cs create mode 100644 Assets/_Scripts/UI/NotificationUI.cs.meta create mode 100644 Assets/_Scripts/UI/QuestLogUI.cs create mode 100644 Assets/_Scripts/UI/QuestLogUI.cs.meta create mode 100644 Assets/_Scripts/UI/QuestTrackerUI.cs create mode 100644 Assets/_Scripts/UI/QuestTrackerUI.cs.meta create mode 100644 Packages/manifest.json create mode 100644 Packages/packages-lock.json create mode 100644 ProjectSettings/AudioManager.asset create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/DynamicsManager.asset create mode 100644 ProjectSettings/EditorBuildSettings.asset create mode 100644 ProjectSettings/EditorSettings.asset create mode 100644 ProjectSettings/GraphicsSettings.asset create mode 100644 ProjectSettings/InputManager.asset create mode 100644 ProjectSettings/MemorySettings.asset create mode 100644 ProjectSettings/NavMeshAreas.asset create mode 100644 ProjectSettings/PackageManagerSettings.asset create mode 100644 ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json create mode 100644 ProjectSettings/Physics2DSettings.asset create mode 100644 ProjectSettings/PresetManager.asset create mode 100644 ProjectSettings/ProjectSettings.asset create mode 100644 ProjectSettings/ProjectVersion.txt create mode 100644 ProjectSettings/QualitySettings.asset create mode 100644 ProjectSettings/SceneTemplateSettings.json create mode 100644 ProjectSettings/TagManager.asset create mode 100644 ProjectSettings/TimeManager.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset create mode 100644 ProjectSettings/VFXManager.asset create mode 100644 ProjectSettings/VersionControlSettings.asset create mode 100644 ProjectSettings/XRSettings.asset diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..302564c --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# Unity generated folders +/Library/ +/Temp/ +/Obj/ +/Build/ +/Builds/ +/Logs/ +/UserSettings/ +/MemoryCaptures/ +/Recordings/ + +# Unity generated files +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# Unity crash and profiler artifacts +sysinfo.txt +mono_crash.mem.*.blob +*.stackdump + +# Unity asset cache +*.pidb +*.booproj +.consulo/ + +# Visual Studio / Rider / VS Code local settings +.vs/ +.idea/ +*.csproj.DotSettings.user +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json +!.vscode/launch.json +!.vscode/tasks.json + +# OS files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +Thumbs.db +ehthumbs.db +Desktop.ini + +# Archives and build outputs +*.apk +*.aab +*.unitypackage +*.app +*.exe +*.x86 +*.x86_64 +*.so + +# Documents should not be committed for this lab +*.doc +*.docx +~$*.docx diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ddb6ff8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "visualstudiotoolsforunity.vstuc" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..da60e25 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Unity", + "type": "vstuc", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ed4f000 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,55 @@ +{ + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.gitmodules": true, + "**/*.booproj": true, + "**/*.pidb": true, + "**/*.suo": true, + "**/*.user": true, + "**/*.userprefs": true, + "**/*.unityproj": true, + "**/*.dll": true, + "**/*.exe": true, + "**/*.pdf": true, + "**/*.mid": true, + "**/*.midi": true, + "**/*.wav": true, + "**/*.gif": true, + "**/*.ico": true, + "**/*.jpg": true, + "**/*.jpeg": true, + "**/*.png": true, + "**/*.psd": true, + "**/*.tga": true, + "**/*.tif": true, + "**/*.tiff": true, + "**/*.3ds": true, + "**/*.3DS": true, + "**/*.fbx": true, + "**/*.FBX": true, + "**/*.lxo": true, + "**/*.LXO": true, + "**/*.ma": true, + "**/*.MA": true, + "**/*.obj": true, + "**/*.OBJ": true, + "**/*.asset": true, + "**/*.cubemap": true, + "**/*.flare": true, + "**/*.mat": true, + "**/*.meta": true, + "**/*.prefab": true, + "**/*.unity": true, + "build/": true, + "Build/": true, + "Library/": true, + "library/": true, + "obj/": true, + "Obj/": true, + "ProjectSettings/": true, + "temp/": true, + "Temp/": true + }, + "dotnet.defaultSolution": "OTG_Lab05_QuestSystem.sln" +} \ No newline at end of file diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 0000000..57fefe3 --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67c703375e86181f69d7032805b2b660 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/Lab5SceneBuilder.cs b/Assets/Editor/Lab5SceneBuilder.cs new file mode 100644 index 0000000..53bdbb8 --- /dev/null +++ b/Assets/Editor/Lab5SceneBuilder.cs @@ -0,0 +1,1117 @@ +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 Lab5SceneBuilder +{ + private const string ScenePath = "Assets/_Scenes/Lab05_QuestSystem.unity"; + private static readonly Color DarkPanel = new Color(0.04f, 0.045f, 0.055f, 0.82f); + private static readonly Color Gold = new Color(1f, 0.73f, 0.24f, 1f); + private static readonly Color LightText = new Color(0.93f, 0.91f, 0.84f, 1f); + + [MenuItem("Tools/Technical Game Design/Lab5/Create Quest System Scene")] + public static void CreateQuestSystemScene() + { + EnsureFolders(); + Font uiFont = ImportUbuntuFont(); + Dictionary materials = CreateMaterials(); + + ItemData wood = CreateItem("Wood", "Древесина", "Пучок сухих веток и брёвен для лесоруба.", 20); + ItemData stone = CreateItem("Stone", "Stone", "Камень из каменоломни.", 20); + ItemData goldOre = CreateItem("GoldOre", "GoldOre", "Редкая золотая руда.", 10); + + QuestData woodQuest = CreateQuest( + "quest_help_lumberjack", + "Помощь лесорубу", + "Соберите 5 древесины.", + new QuestGoal + { + goalType = QuestGoalType.CollectItem, + targetItem = wood, + requiredAmount = 5 + }, + goldOre, + 3); + + QuestData goblinQuest = CreateQuest( + "quest_clear_camp", + "Очистка лагеря", + "Уничтожьте 3 гоблинов.", + new QuestGoal + { + goalType = QuestGoalType.KillEnemy, + enemyType = "Goblin", + requiredAmount = 3 + }, + stone, + 5); + + DialogueData elderDialogue = CreateElderDialogue(woodQuest, goblinQuest); + + Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + scene.name = "Lab05_QuestSystem"; + + CreateLighting(); + CreateGround(materials); + CreateZones(materials, uiFont); + + Camera camera = CreateCamera(); + GameObject playerPrefab = CreatePlayerPrefab(materials); + GameObject elderPrefab = CreateNpcPrefab(materials, elderDialogue); + GameObject goblinPrefab = CreateGoblinPrefab(materials); + GameObject woodPrefab = CreateResourcePrefab("Wood Resource", PrimitiveType.Cylinder, materials["Wood"], wood); + GameObject stonePrefab = CreateResourcePrefab("Stone Resource", PrimitiveType.Sphere, materials["Stone"], stone); + GameObject goldPrefab = CreateResourcePrefab("GoldOre Resource", PrimitiveType.Sphere, materials["Gold"], goldOre); + + InstantiatePrefab(playerPrefab, new Vector3(0f, 1f, -5f), Quaternion.identity, "Player"); + InstantiatePrefab(elderPrefab, new Vector3(0f, 1f, 3f), Quaternion.Euler(0f, 180f, 0f), "NPC Староста"); + PlaceResources(woodPrefab, stonePrefab, goldPrefab); + PlaceGoblins(goblinPrefab); + CreateEnvironmentProps(materials); + CreateSystems(new[] { woodQuest, goblinQuest }); + CreateCanvas(uiFont); + + EditorSceneManager.MarkSceneDirty(scene); + EditorSceneManager.SaveScene(scene, ScenePath); + AddSceneToBuildSettings(ScenePath); + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + Debug.Log($"Lab05 quest system scene created: {ScenePath}"); + } + + [MenuItem("Tools/Technical Game Design/Lab5/Validate Quest System Scene")] + public static void ValidateGeneratedScene() + { + if (!File.Exists(ScenePath)) + { + throw new System.Exception($"Scene does not exist: {ScenePath}"); + } + + Scene scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); + int missingScripts = CountMissingScripts(scene); + if (missingScripts > 0) + { + throw new System.Exception($"Missing scripts found: {missingScripts}"); + } + + Require(Object.FindObjectOfType() != null, "QuestManager is missing"); + Require(Object.FindObjectOfType() != null, "Inventory is missing"); + Require(Object.FindObjectOfType() != null, "QuestLogUI is missing"); + Require(Object.FindObjectOfType() != null, "QuestTrackerUI is missing"); + Require(Object.FindObjectOfType() != null, "InventoryUI is missing"); + Require(Object.FindObjectOfType() != null, "NotificationUI is missing"); + Require(Object.FindObjectOfType() != null, "InteractionPromptUI is missing"); + Require(Object.FindObjectOfType() != null, "DialogueUI is missing"); + Require(Object.FindObjectOfType() != null, "EventSystem is missing"); + Require(Object.FindObjectOfType() != null, "StandaloneInputModule is missing"); + Require(Object.FindObjectOfType() != null, "NPC dialogue is missing"); + Require(GameObject.FindGameObjectWithTag("Player") != null, "Player tag object is missing"); + + Require(Object.FindObjectsOfType().Length >= 5, "At least 5 goblins are required"); + Require(CountResources("Wood") >= 5, "Wood resources are missing"); + Require(CountResources("Stone") >= 1, "Stone resources are missing"); + Require(CountResources("GoldOre") >= 1, "GoldOre resources are missing"); + + Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/Wood.asset") != null, "Wood ItemData is missing"); + Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/Stone.asset") != null, "Stone ItemData is missing"); + Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/GoldOre.asset") != null, "GoldOre ItemData is missing"); + Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_help_lumberjack.asset") != null, "Quest 1 asset is missing"); + Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_clear_camp.asset") != null, "Quest 2 asset is missing"); + Font ubuntuFont = AssetDatabase.LoadAssetAtPath("Assets/_Fonts/Ubuntu-R.ttf"); + Require(ubuntuFont != null, "Ubuntu font asset is missing"); + ValidateSceneFonts(scene, ubuntuFont); + + ValidateQuest(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_help_lumberjack.asset"), "Помощь лесорубу"); + ValidateQuest(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_clear_camp.asset"), "Очистка лагеря"); + + Debug.Log("Lab05 validation passed: scene, UI, quests, rewards, resources, goblins and Ubuntu font are present."); + } + + private static void Require(bool condition, string message) + { + if (!condition) + { + throw new System.Exception(message); + } + } + + private static int CountMissingScripts(Scene scene) + { + int count = 0; + foreach (GameObject root in scene.GetRootGameObjects()) + { + count += CountMissingScriptsRecursive(root); + } + + return count; + } + + private static int CountMissingScriptsRecursive(GameObject gameObject) + { + int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject); + foreach (Transform child in gameObject.transform) + { + count += CountMissingScriptsRecursive(child.gameObject); + } + + return count; + } + + private static int CountResources(string itemId) + { + int count = 0; + foreach (ResourceNode node in Object.FindObjectsOfType()) + { + if (node.itemData != null && node.itemData.itemId == itemId) + { + count++; + } + } + + return count; + } + + private static void ValidateQuest(QuestData quest, string expectedName) + { + Require(quest != null, $"{expectedName} is missing"); + Require(quest.questName == expectedName, $"{expectedName} has wrong name"); + Require(quest.goals != null && quest.goals.Count > 0, $"{expectedName} has no goals"); + Require(quest.rewardItem != null && quest.rewardAmount > 0, $"{expectedName} reward is not configured"); + } + + private static void ValidateSceneFonts(Scene scene, Font expectedFont) + { + foreach (Text text in Resources.FindObjectsOfTypeAll()) + { + if (text.gameObject.scene == scene) + { + Require(text.font == expectedFont, $"Text object does not use Ubuntu Font: {text.name}"); + } + } + + foreach (TextMesh textMesh in Resources.FindObjectsOfTypeAll()) + { + if (textMesh.gameObject.scene == scene) + { + Require(textMesh.font == expectedFont, $"TextMesh object does not use Ubuntu Font: {textMesh.name}"); + } + } + + foreach (InventoryUI inventoryUI in Resources.FindObjectsOfTypeAll()) + { + if (inventoryUI.gameObject.scene == scene) + { + Require(inventoryUI.uiFont == expectedFont, "InventoryUI does not use Ubuntu Font"); + } + } + + foreach (QuestTrackerUI trackerUI in Resources.FindObjectsOfTypeAll()) + { + if (trackerUI.gameObject.scene == scene) + { + Require(trackerUI.uiFont == expectedFont, "QuestTrackerUI does not use Ubuntu Font"); + } + } + + foreach (QuestLogUI questLogUI in Resources.FindObjectsOfTypeAll()) + { + if (questLogUI.gameObject.scene == scene) + { + Require(questLogUI.uiFont == expectedFont, "QuestLogUI does not use Ubuntu Font"); + } + } + } + + private static void EnsureFolders() + { + string[] folders = + { + "Assets/_Scenes", + "Assets/_Scripts", + "Assets/_Scripts/QuestSystem", + "Assets/_Scripts/Dialogue", + "Assets/_Scripts/Inventory", + "Assets/_Scripts/UI", + "Assets/_Scripts/Gameplay", + "Assets/_Data", + "Assets/_Data/Quests", + "Assets/_Data/Items", + "Assets/_Data/Enemies", + "Assets/_Prefabs", + "Assets/_Materials", + "Assets/_Fonts", + "Assets/Editor" + }; + + foreach (string folder in folders) + { + CreateFolderRecursive(folder); + } + } + + private static void CreateFolderRecursive(string folder) + { + if (AssetDatabase.IsValidFolder(folder)) + { + return; + } + + string parent = Path.GetDirectoryName(folder)?.Replace("\\", "/"); + string name = Path.GetFileName(folder); + if (!string.IsNullOrEmpty(parent)) + { + CreateFolderRecursive(parent); + AssetDatabase.CreateFolder(parent, name); + } + } + + private static Font ImportUbuntuFont() + { + string destination = "Assets/_Fonts/Ubuntu-R.ttf"; + string source = FindUbuntuFont(); + + if (!string.IsNullOrEmpty(source)) + { + if (!File.Exists(destination)) + { + FileUtil.CopyFileOrDirectory(source, destination); + } + + AssetDatabase.ImportAsset(destination, ImportAssetOptions.ForceUpdate); + Font ubuntu = AssetDatabase.LoadAssetAtPath(destination); + if (ubuntu != null) + { + return ubuntu; + } + } + + return Resources.GetBuiltinResource("Arial.ttf"); + } + + private static string FindUbuntuFont() + { + string[] roots = + { + "/usr/share/fonts/truetype/ubuntu", + "/usr/share/fonts/TTF", + "/usr/share/fonts" + }; + + foreach (string root in roots) + { + if (!Directory.Exists(root)) + { + continue; + } + + string[] regular = Directory.GetFiles(root, "Ubuntu-R.ttf", SearchOption.AllDirectories); + if (regular.Length > 0) + { + return regular[0]; + } + + string[] anyUbuntu = Directory.GetFiles(root, "*Ubuntu*.ttf", SearchOption.AllDirectories); + if (anyUbuntu.Length > 0) + { + return anyUbuntu[0]; + } + } + + return string.Empty; + } + + private static Dictionary CreateMaterials() + { + return new Dictionary + { + ["Ground"] = CreateMaterial("M_Ground_Grass", new Color(0.22f, 0.45f, 0.22f)), + ["Square"] = CreateMaterial("M_Central_Square", new Color(0.42f, 0.38f, 0.32f)), + ["Forest"] = CreateMaterial("M_Forest_Dark", new Color(0.08f, 0.3f, 0.12f)), + ["Quarry"] = CreateMaterial("M_Quarry_Stone", new Color(0.45f, 0.43f, 0.4f)), + ["Camp"] = CreateMaterial("M_Goblin_Camp", new Color(0.32f, 0.2f, 0.14f)), + ["Wood"] = CreateMaterial("M_Wood", new Color(0.47f, 0.27f, 0.13f)), + ["Leaves"] = CreateMaterial("M_Leaves", new Color(0.09f, 0.42f, 0.16f)), + ["Stone"] = CreateMaterial("M_Stone", new Color(0.55f, 0.55f, 0.58f)), + ["Gold"] = CreateMaterial("M_GoldOre", new Color(1f, 0.72f, 0.22f)), + ["Player"] = CreateMaterial("M_Player", new Color(0.18f, 0.36f, 0.8f)), + ["Npc"] = CreateMaterial("M_Elder", new Color(0.72f, 0.64f, 0.52f)), + ["Goblin"] = CreateMaterial("M_Goblin", new Color(0.24f, 0.62f, 0.18f)), + ["Tent"] = CreateMaterial("M_Tent_Red", new Color(0.5f, 0.12f, 0.08f)), + ["Accent"] = CreateMaterial("M_Gold_Accent", Gold) + }; + } + + private static Material CreateMaterial(string name, Color color) + { + string path = $"Assets/_Materials/{name}.mat"; + Material material = AssetDatabase.LoadAssetAtPath(path); + if (material == null) + { + material = new Material(Shader.Find("Standard")); + AssetDatabase.CreateAsset(material, path); + } + + material.color = color; + EditorUtility.SetDirty(material); + return material; + } + + private static ItemData CreateItem(string id, string displayName, string description, int maxStack) + { + string path = $"Assets/_Data/Items/{id}.asset"; + ItemData item = AssetDatabase.LoadAssetAtPath(path); + if (item == null) + { + item = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(item, path); + } + + item.itemId = id; + item.itemName = displayName; + item.description = description; + item.maxStack = maxStack; + EditorUtility.SetDirty(item); + return item; + } + + private static QuestData CreateQuest(string id, string name, string description, QuestGoal goal, ItemData rewardItem, int rewardAmount) + { + string path = $"Assets/_Data/Quests/{id}.asset"; + QuestData quest = AssetDatabase.LoadAssetAtPath(path); + if (quest == null) + { + quest = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(quest, path); + } + + quest.questId = id; + quest.questName = name; + quest.description = description; + quest.goals.Clear(); + quest.goals.Add(goal); + quest.rewardItem = rewardItem; + quest.rewardAmount = rewardAmount; + EditorUtility.SetDirty(quest); + return quest; + } + + private static DialogueData CreateElderDialogue(QuestData woodQuest, QuestData goblinQuest) + { + string path = "Assets/_Data/VillageElderDialogue.asset"; + DialogueData dialogue = AssetDatabase.LoadAssetAtPath(path); + if (dialogue == null) + { + dialogue = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(dialogue, path); + } + + dialogue.npcName = "Староста"; + dialogue.greeting = "Добро пожаловать на площадь. Работы много, выбирайте задание."; + dialogue.responses.Clear(); + dialogue.responses.Add(new DialogueResponse { text = "Нужна работа.", questToStart = woodQuest }); + dialogue.responses.Add(new DialogueResponse { text = "Есть ещё задания?", questToStart = goblinQuest }); + dialogue.responses.Add(new DialogueResponse { text = "До свидания.", questToStart = null }); + EditorUtility.SetDirty(dialogue); + return dialogue; + } + + private static void CreateLighting() + { + GameObject lightObject = new GameObject("Directional Light"); + Light light = lightObject.AddComponent(); + light.type = LightType.Directional; + light.intensity = 1.1f; + lightObject.transform.rotation = Quaternion.Euler(50f, -35f, 0f); + + RenderSettings.ambientLight = new Color(0.45f, 0.48f, 0.52f); + RenderSettings.fog = true; + RenderSettings.fogColor = new Color(0.55f, 0.62f, 0.58f); + RenderSettings.fogDensity = 0.012f; + } + + private static void CreateGround(Dictionary materials) + { + GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); + ground.name = "RPG Polygon Ground"; + ground.transform.localScale = new Vector3(5.4f, 1f, 5.4f); + AssignMaterial(ground, materials["Ground"]); + + CreateFlatZone("Центральная площадь", new Vector3(0f, 0.03f, 0f), new Vector3(12f, 0.08f, 12f), materials["Square"]); + CreateFlatZone("Лес", new Vector3(-17f, 0.04f, 2f), new Vector3(15f, 0.08f, 17f), materials["Forest"]); + CreateFlatZone("Каменоломня", new Vector3(17f, 0.04f, 1f), new Vector3(15f, 0.08f, 17f), materials["Quarry"]); + CreateFlatZone("Лагерь гоблинов", new Vector3(0f, 0.04f, 20f), new Vector3(18f, 0.08f, 14f), materials["Camp"]); + } + + private static void CreateFlatZone(string name, Vector3 position, Vector3 scale, Material material) + { + GameObject zone = GameObject.CreatePrimitive(PrimitiveType.Cube); + zone.name = name; + zone.transform.position = position; + zone.transform.localScale = scale; + AssignMaterial(zone, material); + } + + private static void CreateZones(Dictionary materials, Font font) + { + CreateWorldLabel("Центральная площадь", new Vector3(-4f, 0.2f, -4f), font); + CreateWorldLabel("Лес", new Vector3(-18f, 0.2f, -7f), font); + CreateWorldLabel("Каменоломня", new Vector3(14f, 0.2f, -7f), font); + CreateWorldLabel("Лагерь гоблинов", new Vector3(-5f, 0.2f, 14f), font); + + for (int i = 0; i < 18; i++) + { + float x = -23f + (i % 6) * 3.2f; + float z = -6f + (i / 6) * 5.1f; + CreateTree(new Vector3(x, 0f, z), materials["Wood"], materials["Leaves"]); + } + + for (int i = 0; i < 14; i++) + { + float x = 13f + (i % 5) * 2.2f; + float z = -7f + (i / 5) * 4.2f; + CreateRock(new Vector3(x, 0.45f, z), materials["Stone"], 0.8f + i % 3 * 0.25f); + } + } + + private static void CreateWorldLabel(string text, Vector3 position, Font font) + { + GameObject label = new GameObject($"Label {text}", typeof(TextMesh)); + label.transform.position = position; + label.transform.rotation = Quaternion.Euler(75f, 0f, 0f); + + TextMesh mesh = label.GetComponent(); + mesh.text = text; + mesh.font = font; + mesh.fontSize = 32; + mesh.characterSize = 0.18f; + mesh.anchor = TextAnchor.MiddleCenter; + mesh.color = Gold; + + MeshRenderer renderer = label.GetComponent(); + if (font != null) + { + renderer.sharedMaterial = font.material; + } + } + + private static void CreateTree(Vector3 position, Material wood, Material leaves) + { + GameObject trunk = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + trunk.name = "Tree Trunk"; + trunk.transform.position = position + Vector3.up * 1f; + trunk.transform.localScale = new Vector3(0.35f, 1f, 0.35f); + AssignMaterial(trunk, wood); + + GameObject crown = GameObject.CreatePrimitive(PrimitiveType.Sphere); + crown.name = "Tree Crown"; + crown.transform.position = position + Vector3.up * 2.35f; + crown.transform.localScale = new Vector3(1.7f, 1.35f, 1.7f); + AssignMaterial(crown, leaves); + } + + private static void CreateRock(Vector3 position, Material material, float scale) + { + GameObject rock = GameObject.CreatePrimitive(PrimitiveType.Sphere); + rock.name = "Quarry Rock"; + rock.transform.position = position; + rock.transform.localScale = new Vector3(scale * 1.25f, scale * 0.65f, scale); + AssignMaterial(rock, material); + } + + private static Camera CreateCamera() + { + GameObject cameraObject = new GameObject("Main Camera", typeof(Camera), typeof(AudioListener)); + cameraObject.tag = "MainCamera"; + Camera camera = cameraObject.GetComponent(); + camera.fieldOfView = 62f; + camera.nearClipPlane = 0.08f; + camera.transform.position = new Vector3(0f, 5f, -10f); + camera.transform.rotation = Quaternion.Euler(20f, 0f, 0f); + return camera; + } + + private static GameObject CreatePlayerPrefab(Dictionary materials) + { + GameObject player = GameObject.CreatePrimitive(PrimitiveType.Capsule); + player.name = "Player"; + player.tag = "Player"; + player.transform.position = new Vector3(0f, 1f, -5f); + AssignMaterial(player, materials["Player"]); + Object.DestroyImmediate(player.GetComponent()); + + CharacterController controller = player.AddComponent(); + controller.height = 2f; + controller.radius = 0.45f; + controller.center = Vector3.zero; + + SimplePlayerController movement = player.AddComponent(); + + GameObject pivot = new GameObject("Camera Pivot"); + pivot.transform.SetParent(player.transform); + pivot.transform.localPosition = new Vector3(0f, 1.35f, 0f); + movement.cameraPivot = pivot.transform; + player.AddComponent(); + + GameObject prefab = SavePrefab(player, "Assets/_Prefabs/Player.prefab"); + Object.DestroyImmediate(player); + return prefab; + } + + private static GameObject CreateNpcPrefab(Dictionary materials, DialogueData dialogue) + { + GameObject npc = GameObject.CreatePrimitive(PrimitiveType.Capsule); + npc.name = "NPC Староста"; + npc.transform.position = new Vector3(0f, 1f, 3f); + AssignMaterial(npc, materials["Npc"]); + + NPCDialogue npcDialogue = npc.AddComponent(); + npcDialogue.npcId = "VillageElder"; + npcDialogue.dialogueData = dialogue; + + GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere); + marker.name = "Quest Marker"; + marker.transform.SetParent(npc.transform); + marker.transform.localPosition = new Vector3(0f, 1.45f, 0f); + marker.transform.localScale = Vector3.one * 0.28f; + AssignMaterial(marker, materials["Accent"]); + + GameObject prefab = SavePrefab(npc, "Assets/_Prefabs/NPC_Starosta.prefab"); + Object.DestroyImmediate(npc); + return prefab; + } + + private static GameObject CreateGoblinPrefab(Dictionary materials) + { + GameObject goblin = GameObject.CreatePrimitive(PrimitiveType.Capsule); + goblin.name = "Goblin"; + goblin.transform.position = Vector3.up; + goblin.transform.localScale = new Vector3(0.85f, 0.85f, 0.85f); + AssignMaterial(goblin, materials["Goblin"]); + + Enemy enemy = goblin.AddComponent(); + enemy.enemyType = "Goblin"; + enemy.health = 100; + + GameObject head = GameObject.CreatePrimitive(PrimitiveType.Sphere); + head.name = "Goblin Head"; + head.transform.SetParent(goblin.transform); + head.transform.localPosition = new Vector3(0f, 0.75f, 0f); + head.transform.localScale = Vector3.one * 0.55f; + AssignMaterial(head, materials["Goblin"]); + + GameObject prefab = SavePrefab(goblin, "Assets/_Prefabs/Goblin.prefab"); + Object.DestroyImmediate(goblin); + return prefab; + } + + private static GameObject CreateResourcePrefab(string name, PrimitiveType primitive, Material material, ItemData item) + { + GameObject resource = GameObject.CreatePrimitive(primitive); + resource.name = name; + resource.transform.position = Vector3.up * 0.5f; + resource.transform.localScale = name.StartsWith("Wood") ? new Vector3(0.45f, 1.1f, 0.45f) : Vector3.one * 0.8f; + AssignMaterial(resource, material); + + ResourceNode node = resource.AddComponent(); + node.itemData = item; + node.amount = 1; + + GameObject prefab = SavePrefab(resource, $"Assets/_Prefabs/{name.Replace(" ", "_")}.prefab"); + Object.DestroyImmediate(resource); + return prefab; + } + + private static void PlaceResources(GameObject woodPrefab, GameObject stonePrefab, GameObject goldPrefab) + { + Vector3[] woodPositions = + { + new Vector3(-14f, 0.55f, -1f), new Vector3(-17f, 0.55f, 3f), new Vector3(-20f, 0.55f, 6f), + new Vector3(-12f, 0.55f, 6f), new Vector3(-18f, 0.55f, 10f), new Vector3(-22f, 0.55f, 0f) + }; + + Vector3[] stonePositions = + { + new Vector3(14f, 0.6f, -2f), new Vector3(18f, 0.6f, 1f), new Vector3(21f, 0.6f, 5f), + new Vector3(15f, 0.6f, 7f), new Vector3(22f, 0.6f, -5f) + }; + + Vector3[] goldPositions = + { + new Vector3(18f, 0.6f, 9f), new Vector3(23f, 0.6f, 2f), new Vector3(12f, 0.6f, 4f) + }; + + foreach (Vector3 position in woodPositions) + { + InstantiatePrefab(woodPrefab, position, Quaternion.identity, "Wood"); + } + + foreach (Vector3 position in stonePositions) + { + InstantiatePrefab(stonePrefab, position, Quaternion.identity, "Stone"); + } + + foreach (Vector3 position in goldPositions) + { + InstantiatePrefab(goldPrefab, position, Quaternion.identity, "GoldOre"); + } + } + + private static void PlaceGoblins(GameObject goblinPrefab) + { + Vector3[] positions = + { + new Vector3(-5f, 0.9f, 18f), new Vector3(0f, 0.9f, 20f), new Vector3(5f, 0.9f, 18f), + new Vector3(-3f, 0.9f, 24f), new Vector3(4f, 0.9f, 25f) + }; + + foreach (Vector3 position in positions) + { + InstantiatePrefab(goblinPrefab, position, Quaternion.identity, "Goblin"); + } + } + + private static void CreateEnvironmentProps(Dictionary materials) + { + for (int i = 0; i < 3; i++) + { + GameObject tent = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + tent.name = "Goblin Tent"; + tent.transform.position = new Vector3(-6f + i * 6f, 0.65f, 26f); + tent.transform.localScale = new Vector3(1.7f, 0.65f, 1.7f); + AssignMaterial(tent, materials["Tent"]); + } + + GameObject fire = GameObject.CreatePrimitive(PrimitiveType.Cylinder); + fire.name = "Campfire"; + fire.transform.position = new Vector3(0f, 0.35f, 18f); + fire.transform.localScale = new Vector3(0.75f, 0.35f, 0.75f); + AssignMaterial(fire, materials["Accent"]); + + PointLight("Campfire Light", new Vector3(0f, 1.7f, 18f), new Color(1f, 0.45f, 0.14f), 2.5f, 8f); + PointLight("Square Lantern", new Vector3(2f, 3.2f, 2f), Gold, 1.2f, 10f); + } + + private static void PointLight(string name, Vector3 position, Color color, float intensity, float range) + { + GameObject lightObject = new GameObject(name); + lightObject.transform.position = position; + Light light = lightObject.AddComponent(); + light.type = LightType.Point; + light.color = color; + light.intensity = intensity; + light.range = range; + } + + private static void CreateSystems(IEnumerable quests) + { + GameObject systems = new GameObject("Systems"); + QuestManager questManager = systems.AddComponent(); + questManager.SetAvailableQuests(quests); + systems.AddComponent(); + EditorUtility.SetDirty(questManager); + } + + private static void CreateCanvas(Font font) + { + CreateEventSystem(); + + GameObject canvasObject = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); + Canvas canvas = canvasObject.GetComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + + CanvasScaler scaler = canvasObject.GetComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1920f, 1080f); + scaler.matchWidthOrHeight = 0.5f; + + CreateQuestTracker(canvasObject.transform, font); + CreateInventoryPanel(canvasObject.transform, font); + CreateNotificationPanel(canvasObject.transform, font); + CreateInteractionPrompt(canvasObject.transform, font); + CreateQuestLog(canvasObject.transform, font); + CreateDialoguePanel(canvasObject.transform, font); + } + + private static void CreateEventSystem() + { + GameObject eventSystemObject = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule)); + EventSystem eventSystem = eventSystemObject.GetComponent(); + eventSystem.sendNavigationEvents = true; + + StandaloneInputModule inputModule = eventSystemObject.GetComponent(); + inputModule.horizontalAxis = "Horizontal"; + inputModule.verticalAxis = "Vertical"; + inputModule.submitButton = "Submit"; + inputModule.cancelButton = "Cancel"; + } + + private static void CreateQuestTracker(Transform parent, Font font) + { + GameObject panel = CreatePanel("Active Quests Panel", parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(18f, -18f), new Vector2(380f, 220f), new Vector2(0f, 1f)); + CreateText("Активные квесты", panel.transform, font, 20, Gold, FontStyle.Bold, new Vector2(14f, -12f), new Vector2(350f, 30f), new Vector2(0f, 1f)); + Text output = CreateText("Нет активных квестов", panel.transform, font, 16, LightText, FontStyle.Normal, new Vector2(14f, -54f), new Vector2(350f, 142f), new Vector2(0f, 1f)); + output.lineSpacing = 1.12f; + output.alignment = TextAnchor.UpperLeft; + RectTransform content = CreateVerticalContent("Active Quest Content Legacy", panel.transform, new Vector2(14f, -50f), new Vector2(350f, 150f), new Vector2(0f, 1f)); + content.gameObject.SetActive(false); + + QuestTrackerUI tracker = panel.AddComponent(); + tracker.content = content; + tracker.outputText = output; + tracker.uiFont = font; + } + + private static void CreateInventoryPanel(Transform parent, Font font) + { + GameObject panel = CreatePanel("Inventory Panel", parent, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-18f, -18f), new Vector2(330f, 220f), new Vector2(1f, 1f)); + CreateText("Инвентарь", panel.transform, font, 20, Gold, FontStyle.Bold, new Vector2(-14f, -12f), new Vector2(300f, 30f), new Vector2(1f, 1f)); + Text output = CreateText("Пусто", panel.transform, font, 16, LightText, FontStyle.Normal, new Vector2(-14f, -54f), new Vector2(300f, 142f), new Vector2(1f, 1f)); + output.lineSpacing = 1.12f; + output.alignment = TextAnchor.UpperLeft; + RectTransform content = CreateVerticalContent("Inventory Content Legacy", panel.transform, new Vector2(-14f, -50f), new Vector2(300f, 150f), new Vector2(1f, 1f)); + content.gameObject.SetActive(false); + + InventoryUI inventoryUI = panel.AddComponent(); + inventoryUI.content = content; + inventoryUI.outputText = output; + inventoryUI.uiFont = font; + } + + private static void CreateNotificationPanel(Transform parent, Font font) + { + GameObject panel = CreatePanel("Notification Panel", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 42f), new Vector2(620f, 96f), new Vector2(0.5f, 0f)); + CanvasGroup group = panel.AddComponent(); + Text message = CreateText(string.Empty, panel.transform, font, 22, LightText, FontStyle.Bold, Vector2.zero, new Vector2(570f, 74f), new Vector2(0.5f, 0.5f)); + message.alignment = TextAnchor.MiddleCenter; + + NotificationUI notification = panel.AddComponent(); + notification.messageText = message; + notification.canvasGroup = group; + } + + private static void CreateInteractionPrompt(Transform parent, Font font) + { + GameObject root = CreatePanel("Interaction Prompt", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 172f), new Vector2(360f, 46f), new Vector2(0.5f, 0f)); + root.GetComponent().color = new Color(0.035f, 0.04f, 0.045f, 0.9f); + + Outline outline = root.AddComponent(); + outline.effectColor = new Color(1f, 0.73f, 0.24f, 0.55f); + outline.effectDistance = new Vector2(1f, -1f); + + Text text = CreateText("E Поговорить", root.transform, font, 18, LightText, FontStyle.Bold, Vector2.zero, new Vector2(320f, 34f), new Vector2(0.5f, 0.5f)); + text.alignment = TextAnchor.MiddleCenter; + + InteractionPromptUI prompt = parent.gameObject.AddComponent(); + prompt.root = root; + prompt.promptText = text; + root.SetActive(false); + } + + private static void CreateQuestLog(Transform parent, Font font) + { + GameObject root = CreatePanel("Quest Log Panel", parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(760f, 650f), new Vector2(0.5f, 0.5f)); + CreateText("Журнал квестов", root.transform, font, 28, Gold, FontStyle.Bold, new Vector2(0f, -24f), new Vector2(700f, 42f), new Vector2(0.5f, 1f)); + + GameObject scrollObject = new GameObject("Scroll View", typeof(RectTransform), typeof(ScrollRect), typeof(Image)); + scrollObject.transform.SetParent(root.transform, false); + RectTransform scrollRectTransform = scrollObject.GetComponent(); + scrollRectTransform.anchorMin = new Vector2(0.5f, 0.5f); + scrollRectTransform.anchorMax = new Vector2(0.5f, 0.5f); + scrollRectTransform.pivot = new Vector2(0.5f, 0.5f); + scrollRectTransform.anchoredPosition = new Vector2(0f, -34f); + scrollRectTransform.sizeDelta = new Vector2(700f, 540f); + scrollObject.GetComponent().color = new Color(0f, 0f, 0f, 0.18f); + + GameObject viewport = new GameObject("Viewport", typeof(RectTransform), typeof(Image), typeof(Mask)); + viewport.transform.SetParent(scrollObject.transform, false); + RectTransform viewportRect = Stretch(viewport); + viewport.GetComponent().color = new Color(0f, 0f, 0f, 0.08f); + viewport.GetComponent().showMaskGraphic = false; + + GameObject contentObject = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter)); + contentObject.transform.SetParent(viewport.transform, false); + RectTransform content = contentObject.GetComponent(); + content.anchorMin = new Vector2(0f, 1f); + content.anchorMax = new Vector2(1f, 1f); + content.pivot = new Vector2(0.5f, 1f); + content.anchoredPosition = Vector2.zero; + content.sizeDelta = new Vector2(0f, 0f); + VerticalLayoutGroup layout = contentObject.GetComponent(); + layout.padding = new RectOffset(18, 18, 18, 18); + layout.spacing = 3f; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = false; + ContentSizeFitter fitter = contentObject.GetComponent(); + fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; + + ScrollRect scroll = scrollObject.GetComponent(); + scroll.viewport = viewportRect; + scroll.content = content; + scroll.horizontal = false; + + QuestLogUI questLog = parent.gameObject.AddComponent(); + questLog.root = root; + questLog.content = content; + questLog.uiFont = font; + root.SetActive(false); + } + + private static void CreateDialoguePanel(Transform parent, Font font) + { + GameObject root = CreatePanel("Dialogue Panel", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 38f), new Vector2(980f, 250f), new Vector2(0.5f, 0f)); + root.GetComponent().color = new Color(0.04f, 0.04f, 0.04f, 0.93f); + + Outline rootOutline = root.AddComponent(); + rootOutline.effectColor = new Color(1f, 0.73f, 0.24f, 0.24f); + rootOutline.effectDistance = new Vector2(1f, -1f); + + GameObject npcColumn = CreateAnchoredPanel("NPC Dialogue Copy", root.transform, new Vector2(0f, 1f), new Vector2(24f, -24f), new Vector2(590f, 198f), new Vector2(0f, 1f), new Color(0f, 0f, 0f, 0.16f)); + Text nameText = CreateText("Староста", npcColumn.transform, font, 25, Gold, FontStyle.Bold, new Vector2(18f, -16f), new Vector2(540f, 36f), new Vector2(0f, 1f)); + Text bodyText = CreateText(string.Empty, npcColumn.transform, font, 18, LightText, FontStyle.Normal, new Vector2(18f, -64f), new Vector2(540f, 100f), new Vector2(0f, 1f)); + bodyText.lineSpacing = 1.08f; + + GameObject responseArea = CreateAnchoredPanel("Dialogue Responses", root.transform, new Vector2(1f, 1f), new Vector2(-24f, -24f), new Vector2(340f, 198f), new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.2f)); + CreateText("Ответы", responseArea.transform, font, 15, Gold, FontStyle.Bold, new Vector2(16f, -10f), new Vector2(300f, 24f), new Vector2(0f, 1f)); + + GameObject responseViewport = CreateAnchoredPanel("Response Button Stack", responseArea.transform, new Vector2(0f, 1f), new Vector2(14f, -44f), new Vector2(312f, 142f), new Vector2(0f, 1f), new Color(0f, 0f, 0f, 0f)); + RectTransform responseContainer = CreateStretchVerticalContent("Responses", responseViewport.transform, new RectOffset(0, 0, 0, 0)); + Button template = CreateResponseButton("Response Button Template", responseContainer, font); + template.gameObject.SetActive(false); + + DialogueUI dialogueUI = parent.gameObject.AddComponent(); + dialogueUI.root = root; + dialogueUI.nameText = nameText; + dialogueUI.bodyText = bodyText; + dialogueUI.responseContainer = responseContainer; + dialogueUI.responseButtonTemplate = template; + root.SetActive(false); + } + + private static GameObject CreatePanel(string name, Transform parent, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 size, Vector2 pivot) + { + GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); + panel.transform.SetParent(parent, false); + RectTransform rect = panel.GetComponent(); + rect.anchorMin = anchorMin; + rect.anchorMax = anchorMax; + rect.pivot = pivot; + rect.anchoredPosition = anchoredPosition; + rect.sizeDelta = size; + Image image = panel.GetComponent(); + image.color = DarkPanel; + return panel; + } + + private static GameObject CreateInsetPanel(string name, Transform parent, Vector2 offsetMin, Vector2 offsetMax, Color color) + { + GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); + panel.transform.SetParent(parent, false); + RectTransform rect = panel.GetComponent(); + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + rect.offsetMin = offsetMin; + rect.offsetMax = offsetMax; + + Image image = panel.GetComponent(); + image.color = color; + return panel; + } + + private static GameObject CreateAnchoredPanel(string name, Transform parent, Vector2 anchor, Vector2 anchoredPosition, Vector2 size, Vector2 pivot, Color color) + { + GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); + panel.transform.SetParent(parent, false); + RectTransform rect = panel.GetComponent(); + rect.anchorMin = anchor; + rect.anchorMax = anchor; + rect.pivot = pivot; + rect.anchoredPosition = anchoredPosition; + rect.sizeDelta = size; + + Image image = panel.GetComponent(); + image.color = color; + return panel; + } + + private static Text CreateText(string value, Transform parent, Font font, int size, Color color, FontStyle style, Vector2 anchoredPosition, Vector2 rectSize, Vector2 pivot) + { + GameObject textObject = new GameObject("Text", typeof(RectTransform), typeof(Text)); + textObject.transform.SetParent(parent, false); + RectTransform rect = textObject.GetComponent(); + rect.anchorMin = pivot; + rect.anchorMax = pivot; + rect.pivot = pivot; + rect.anchoredPosition = anchoredPosition; + rect.sizeDelta = rectSize; + + Text text = textObject.GetComponent(); + text.font = font != null ? font : Resources.GetBuiltinResource("Arial.ttf"); + text.text = value; + text.fontSize = size; + text.fontStyle = style; + text.color = color; + text.horizontalOverflow = HorizontalWrapMode.Wrap; + text.verticalOverflow = VerticalWrapMode.Overflow; + return text; + } + + private static RectTransform CreateVerticalContent(string name, Transform parent, Vector2 anchoredPosition, Vector2 size, Vector2 pivot) + { + GameObject contentObject = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup)); + contentObject.transform.SetParent(parent, false); + RectTransform rect = contentObject.GetComponent(); + rect.anchorMin = pivot; + rect.anchorMax = pivot; + rect.pivot = pivot; + rect.anchoredPosition = anchoredPosition; + rect.sizeDelta = size; + + VerticalLayoutGroup layout = contentObject.GetComponent(); + layout.spacing = 2f; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = false; + return rect; + } + + private static RectTransform CreateStretchVerticalContent(string name, Transform parent, RectOffset padding) + { + GameObject contentObject = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup)); + contentObject.transform.SetParent(parent, false); + RectTransform rect = contentObject.GetComponent(); + rect.anchorMin = Vector2.zero; + rect.anchorMax = Vector2.one; + rect.offsetMin = Vector2.zero; + rect.offsetMax = Vector2.zero; + + VerticalLayoutGroup layout = contentObject.GetComponent(); + layout.padding = padding; + layout.spacing = 8f; + layout.childAlignment = TextAnchor.UpperLeft; + layout.childControlWidth = true; + layout.childControlHeight = true; + layout.childForceExpandWidth = true; + layout.childForceExpandHeight = false; + return rect; + } + + private static Button CreateResponseButton(string name, Transform parent, Font font) + { + GameObject buttonObject = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button), typeof(LayoutElement), typeof(Outline)); + buttonObject.transform.SetParent(parent, false); + RectTransform rect = buttonObject.GetComponent(); + rect.anchorMin = new Vector2(0f, 1f); + rect.anchorMax = new Vector2(1f, 1f); + rect.pivot = new Vector2(0.5f, 1f); + rect.sizeDelta = new Vector2(0f, 40f); + + LayoutElement layoutElement = buttonObject.GetComponent(); + layoutElement.minHeight = 40f; + layoutElement.preferredHeight = 40f; + layoutElement.flexibleWidth = 1f; + + Image image = buttonObject.GetComponent(); + image.color = new Color(0.12f, 0.1f, 0.075f, 0.98f); + + Outline outline = buttonObject.GetComponent(); + outline.effectColor = new Color(1f, 0.73f, 0.24f, 0.45f); + outline.effectDistance = new Vector2(1f, -1f); + + Button button = buttonObject.GetComponent