using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using UnityEditor.SceneManagement;
public static class LevelStoryBuilder
{
private static Font defaultUiFont;
private static Font handwrittenFont;
private static Material echoMat;
[MenuItem("Tools/Build Level Story System (Elevator + Notes)")]
public static void Build()
{
Debug.Log("=== СБОРКА СЮЖЕТНОЙ СИСТЕМЫ: ЛИФТ И 4 ЗАПИСКИ С КОДОМ ===");
if (EditorApplication.isPlaying)
{
Debug.LogError("Нельзя запускать сборку во время Play Mode! Остановите игру.");
return;
}
defaultUiFont = Resources.GetBuiltinResource("LegacyRuntime.ttf");
if (defaultUiFont == null) defaultUiFont = Resources.GetBuiltinResource("Arial.ttf");
handwrittenFont = AssetDatabase.LoadAssetAtPath("Assets/Fonts/SegoeScriptBold.ttf");
if (handwrittenFont == null) handwrittenFont = AssetDatabase.LoadAssetAtPath("Assets/Fonts/Inkfree.ttf");
if (handwrittenFont == null) handwrittenFont = defaultUiFont;
echoMat = AssetDatabase.LoadAssetAtPath("Assets/EchoCloudMat.mat");
if (echoMat == null) echoMat = AssetDatabase.LoadAssetAtPath("Assets/EchoMat.mat");
// Убеждаемся, что у игрока стоит тег Player
var player = GameObject.Find("Player");
if (player != null)
{
player.tag = "Player";
EditorUtility.SetDirty(player);
}
// 1. Создаем или находим StoryManager (LevelCodeManager)
SetupStoryManager();
// 2. Создаем 3D-зону лифта и терминал
SetupElevatorPlatformAndTerminal();
// 3. Создаем 4 записки в разных комнатах лаборатории
SetupFourStoryNotes();
// 4. Добавляем в HUD_Canvas весь UI
SetupStoryUIInHUDCanvas();
// Сохраняем сцену
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
EditorSceneManager.SaveOpenScenes();
Debug.Log("=== СЮЖЕТНАЯ СИСТЕМА УСПЕШНО СОБРАНА И СОХРАНЕНА! ===");
}
private static void SetupStoryManager()
{
var existing = Object.FindAnyObjectByType();
if (existing == null)
{
var go = new GameObject("StoryManager");
Undo.RegisterCreatedObjectUndo(go, "Create StoryManager");
go.AddComponent();
Debug.Log("[LevelStoryBuilder] Создан объект StoryManager с LevelCodeManager.");
}
}
private static void SetupElevatorPlatformAndTerminal()
{
var oldElevator = GameObject.Find("Elevator_Level2_Zone");
if (oldElevator != null)
{
Undo.DestroyObjectImmediate(oldElevator);
}
GameObject elevatorRoot = new GameObject("Elevator_Level2_Zone");
Undo.RegisterCreatedObjectUndo(elevatorRoot, "Create Elevator_Level2_Zone");
elevatorRoot.transform.position = new Vector3(0f, 0f, 45f);
// 1. Платформа лифта (6x6м на полу)
GameObject platform = GameObject.CreatePrimitive(PrimitiveType.Cube);
platform.name = "Elevator_Platform";
platform.transform.SetParent(elevatorRoot.transform);
platform.transform.localPosition = new Vector3(0f, 0.05f, 0f);
platform.transform.localScale = new Vector3(6f, 0.1f, 6f);
if (echoMat != null) platform.GetComponent().sharedMaterial = echoMat;
CreateElevatorFrame(elevatorRoot.transform);
// 2. Стойка терминала управления лифтом
GameObject console = new GameObject("Elevator_Terminal_Console");
console.transform.SetParent(elevatorRoot.transform);
console.transform.localPosition = new Vector3(1.8f, 0f, -2.2f);
console.transform.localRotation = Quaternion.Euler(0f, -35f, 0f);
GameObject stand = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
stand.name = "Stand";
stand.transform.SetParent(console.transform);
stand.transform.localPosition = new Vector3(0f, 0.55f, 0f);
stand.transform.localScale = new Vector3(0.2f, 0.55f, 0.2f);
if (echoMat != null) stand.GetComponent().sharedMaterial = echoMat;
GameObject screenBox = GameObject.CreatePrimitive(PrimitiveType.Cube);
screenBox.name = "ScreenBox";
screenBox.transform.SetParent(console.transform);
screenBox.transform.localPosition = new Vector3(0f, 1.15f, 0f);
screenBox.transform.localRotation = Quaternion.Euler(30f, 0f, 0f);
screenBox.transform.localScale = new Vector3(0.7f, 0.45f, 0.12f);
if (echoMat != null) screenBox.GetComponent().sharedMaterial = echoMat;
GameObject lightGo = new GameObject("Terminal_StatusLight");
lightGo.transform.SetParent(console.transform);
lightGo.transform.localPosition = new Vector3(0f, 1.45f, 0f);
Light statusLight = lightGo.AddComponent();
statusLight.type = LightType.Point;
statusLight.color = new Color(1f, 0.85f, 0.1f);
statusLight.intensity = 2.5f;
statusLight.range = 5.0f;
var terminalComp = console.AddComponent();
terminalComp.interactionDistance = 3.8f;
terminalComp.statusLight = statusLight;
terminalComp.screenRenderer = screenBox.GetComponent();
CreateWorldText(console.transform, new Vector3(0f, 1.7f, 0f), "ТЕРМИНАЛ ЛИФТА\n[ВВЕДИТЕ 4 ЦИФРЫ]", new Color(1f, 0.85f, 0.1f), 0.035f);
}
private static void CreateElevatorFrame(Transform parent)
{
Vector3[] corners = new Vector3[] {
new Vector3(-2.9f, 2.25f, -2.9f),
new Vector3(2.9f, 2.25f, -2.9f),
new Vector3(-2.9f, 2.25f, 2.9f),
new Vector3(2.9f, 2.25f, 2.9f)
};
for (int i = 0; i < corners.Length; i++)
{
GameObject pillar = GameObject.CreatePrimitive(PrimitiveType.Cube);
pillar.name = $"Pillar_{i}";
pillar.transform.SetParent(parent);
pillar.transform.localPosition = corners[i];
pillar.transform.localScale = new Vector3(0.25f, 4.5f, 0.25f);
if (echoMat != null) pillar.GetComponent().sharedMaterial = echoMat;
}
GameObject beam = GameObject.CreatePrimitive(PrimitiveType.Cube);
beam.name = "Elevator_TopBeam";
beam.transform.SetParent(parent);
beam.transform.localPosition = new Vector3(0f, 4.3f, -2.9f);
beam.transform.localScale = new Vector3(6f, 0.4f, 0.3f);
if (echoMat != null) beam.GetComponent().sharedMaterial = echoMat;
CreateWorldText(parent, new Vector3(0f, 4.3f, -3.1f), "ВЫХОД: ЭВАКУАЦИОННЫЙ ЛИФТ (УРОВЕНЬ 2)", new Color(1f, 0.85f, 0.1f), 0.05f);
}
private static void SetupFourStoryNotes()
{
var oldNotes = GameObject.Find("StoryNotes_Container");
if (oldNotes != null)
{
Undo.DestroyObjectImmediate(oldNotes);
}
GameObject notesRoot = new GameObject("StoryNotes_Container");
Undo.RegisterCreatedObjectUndo(notesRoot, "Create StoryNotes_Container");
var noteDefs = new[]
{
new {
pos = 0,
coords = new Vector3(-36f, 0.85f, -36f),
title = "ЗАПИСКА ДЕЖУРНОГО ТЕХНИКА #1",
sector = "СЕКТОР B: КРИОГЕНИКА",
text = "Срочное сообщение: система заблокировала шлюз из-за проникновения мутантов. Первую цифру кода аварийного лифта пришлось изменить, чтобы изолировать отсек. Запомните её, если хотите выбраться отсюда живым! Слепые твари не видят вас в темноте, пока вы стоите неподвижно."
},
new {
pos = 1,
coords = new Vector3(36f, 0.85f, -36f),
title = "РАПОРТ СЛУЖБЫ БЕЗОПАСНОСТИ #2",
sector = "СЕКТОР C: АРХИВ ОРУЖЕЙНОЙ",
text = "Внимание личному составу: мутанты ориентируются исключительно на эхо шагов и грохот выстрелов! Вторая цифра защитного протокола лифта сохранена в этой директиве. Берегите патроны и используйте скрытное перемещение."
},
new {
pos = 2,
coords = new Vector3(-34f, 0.85f, -10f),
title = "ДНЕВНИК ГЛАВНОГО БИОХИМИКА #3",
sector = "СЕКТОР D: БИОХИМИЯ",
text = "Эксперимент 'Эхо-11' полностью вышел из-под контроля. Все отсеки запечатаны. Третья цифра для активации транспортного лифта зафиксирована здесь. Если вы читаете это — немедленно прорывайтесь к реакторному залу на севере!"
},
new {
pos = 3,
coords = new Vector3(36f, 0.85f, 16f),
title = "СЕРВЕРНЫЙ ЛОГ ЭНЕРГОСИСТЕМЫ #4",
sector = "СЕКТОР G: ГЕНЕТИКА И КИБЕРНЕТИКА",
text = "Автоматический протокол эвакуации: питание гидравлической шахты переведено на аварийный генератор. Финальная, четвертая цифра защитного кода записана в этот модуль. Транспортный лифт ожидает в северном крыле комплекса."
}
};
foreach (var def in noteDefs)
{
CreateNoteObject(notesRoot.transform, def.pos, def.coords, def.title, def.sector, def.text);
}
}
private static void CreateNoteObject(Transform parent, int digitPos, Vector3 position, string title, string sector, string text)
{
GameObject noteGo = new GameObject($"Note_Digit_{digitPos + 1}");
noteGo.transform.SetParent(parent);
noteGo.transform.position = position;
GameObject folder = GameObject.CreatePrimitive(PrimitiveType.Cube);
folder.name = "FolderVisual";
folder.transform.SetParent(noteGo.transform);
folder.transform.localPosition = Vector3.zero;
folder.transform.localRotation = Quaternion.Euler(0f, Random.Range(-30f, 30f), 0f);
folder.transform.localScale = new Vector3(0.4f, 0.04f, 0.55f);
if (echoMat != null) folder.GetComponent().sharedMaterial = echoMat;
GameObject paper = GameObject.CreatePrimitive(PrimitiveType.Cube);
paper.name = "PaperSheet";
paper.transform.SetParent(folder.transform);
paper.transform.localPosition = new Vector3(0f, 0.55f, 0f);
paper.transform.localScale = new Vector3(0.85f, 0.1f, 0.85f);
if (echoMat != null) paper.GetComponent().sharedMaterial = echoMat;
GameObject markerLight = new GameObject("NoteLight");
markerLight.transform.SetParent(noteGo.transform);
markerLight.transform.localPosition = new Vector3(0f, 0.4f, 0f);
Light lightComp = markerLight.AddComponent();
lightComp.type = LightType.Point;
lightComp.color = new Color(1.0f, 0.85f, 0.1f);
lightComp.intensity = 2.0f;
lightComp.range = 3.5f;
NotePickup pickup = noteGo.AddComponent();
pickup.digitPosition = digitPos;
pickup.noteTitle = title;
pickup.sectorName = sector;
pickup.storyText = text;
pickup.interactionDistance = 3.6f;
// 3D текстовая подсказка отключена по запросу пользователя (убрана желтая надпись НАЖМИТЕ [E])
// CreateWorldText(noteGo.transform, new Vector3(0f, 0.7f, 0f), $"[ЗАПИСКА #{digitPos + 1}]\nНАЖМИТЕ [E]", new Color(1f, 0.85f, 0.1f), 0.025f);
}
private static void CreateWorldText(Transform parent, Vector3 localPos, string text, Color color, float characterSize)
{
GameObject textGo = new GameObject("FloatingLabel");
textGo.transform.SetParent(parent);
textGo.transform.localPosition = localPos;
TextMesh tm = textGo.AddComponent();
tm.text = text;
tm.fontSize = 42;
tm.characterSize = characterSize;
tm.color = color;
tm.alignment = TextAlignment.Center;
tm.anchor = TextAnchor.MiddleCenter;
tm.fontStyle = FontStyle.Bold;
if (defaultUiFont != null) tm.font = defaultUiFont;
}
private static void SetupStoryUIInHUDCanvas()
{
GameObject hudCanvasGo = GameObject.Find("HUD_Canvas");
if (hudCanvasGo == null)
{
hudCanvasGo = new GameObject("HUD_Canvas");
Canvas c = hudCanvasGo.AddComponent