Kursach_StealthAction/Assets/Scripts/Editor/LevelStoryBuilder.cs
2026-10-23 15:44:09 +03:00

975 lines
45 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("<color=cyan>=== СБОРКА СЮЖЕТНОЙ СИСТЕМЫ: ЛИФТ И 4 ЗАПИСКИ С КОДОМ ===</color>");
if (EditorApplication.isPlaying)
{
Debug.LogError("Нельзя запускать сборку во время Play Mode! Остановите игру.");
return;
}
defaultUiFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
if (defaultUiFont == null) defaultUiFont = Resources.GetBuiltinResource<Font>("Arial.ttf");
handwrittenFont = AssetDatabase.LoadAssetAtPath<Font>("Assets/Fonts/SegoeScriptBold.ttf");
if (handwrittenFont == null) handwrittenFont = AssetDatabase.LoadAssetAtPath<Font>("Assets/Fonts/Inkfree.ttf");
if (handwrittenFont == null) handwrittenFont = defaultUiFont;
echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoCloudMat.mat");
if (echoMat == null) echoMat = AssetDatabase.LoadAssetAtPath<Material>("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("<color=green>=== СЮЖЕТНАЯ СИСТЕМА УСПЕШНО СОБРАНА И СОХРАНЕНА! ===</color>");
}
private static void SetupStoryManager()
{
var existing = Object.FindAnyObjectByType<LevelCodeManager>();
if (existing == null)
{
var go = new GameObject("StoryManager");
Undo.RegisterCreatedObjectUndo(go, "Create StoryManager");
go.AddComponent<LevelCodeManager>();
Debug.Log("<color=yellow>[LevelStoryBuilder] Создан объект StoryManager с LevelCodeManager.</color>");
}
}
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<MeshRenderer>().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<MeshRenderer>().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<MeshRenderer>().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<Light>();
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<ElevatorTerminal>();
terminalComp.interactionDistance = 3.8f;
terminalComp.statusLight = statusLight;
terminalComp.screenRenderer = screenBox.GetComponent<MeshRenderer>();
// 3D текстовая подсказка отключена по запросу пользователя (убрана желтая надпись ТЕРМИНАЛ ЛИФТА)
// 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<MeshRenderer>().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<MeshRenderer>().sharedMaterial = echoMat;
// 3D текстовая подсказка отключена по запросу пользователя (убрана желтая надпись ВЫХОД: ЭВАКУАЦИОННЫЙ ЛИФТ)
// 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<MeshRenderer>().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<MeshRenderer>().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<Light>();
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<NotePickup>();
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<TextMesh>();
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<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
hudCanvasGo.AddComponent<CanvasScaler>();
hudCanvasGo.AddComponent<GraphicRaycaster>();
}
Transform hudRoot = hudCanvasGo.transform;
SetupInteractionPrompt(hudRoot);
SetupCodeHUDTracker(hudRoot);
SetupNoteViewerPanel(hudRoot);
SetupElevatorKeypadPanel(hudRoot);
SetupLevelSuccessPanel(hudRoot);
}
private static void SetupInteractionPrompt(Transform hudRoot)
{
var old = hudRoot.Find("InteractionPromptPanel");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
}
private static void SetupCodeHUDTracker(Transform hudRoot)
{
var old = hudRoot.Find("CodeTrackerHUD");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
GameObject trackerObj = new GameObject("CodeTrackerHUD");
trackerObj.transform.SetParent(hudRoot, false);
RectTransform rt = trackerObj.AddComponent<RectTransform>();
rt.anchorMin = new Vector2(0f, 1.0f);
rt.anchorMax = new Vector2(0f, 1.0f);
rt.pivot = new Vector2(0f, 1.0f);
rt.anchoredPosition = new Vector2(35f, -35f);
rt.sizeDelta = new Vector2(210f, 50f);
Text[] slotTexts = new Text[4];
Image[] slotBorders = new Image[4];
float slotSize = 44f;
float spacing = 52f;
float startX = slotSize * 0.5f;
for (int i = 0; i < 4; i++)
{
GameObject slotObj = new GameObject($"Slot_{i}");
slotObj.transform.SetParent(trackerObj.transform, false);
RectTransform slotRt = slotObj.AddComponent<RectTransform>();
slotRt.anchorMin = new Vector2(0f, 0.5f);
slotRt.anchorMax = new Vector2(0f, 0.5f);
slotRt.pivot = new Vector2(0.5f, 0.5f);
slotRt.anchoredPosition = new Vector2(startX + i * spacing, 0f);
slotRt.sizeDelta = new Vector2(slotSize, slotSize);
Image slotBg = slotObj.AddComponent<Image>();
slotBg.color = new Color(0.08f, 0.08f, 0.08f, 0.95f);
slotBorders[i] = slotBg;
Outline slotOutline = slotObj.AddComponent<Outline>();
slotOutline.effectColor = new Color(0.3f, 0.3f, 0.3f, 0.5f);
slotOutline.effectDistance = new Vector2(1f, -1f);
GameObject txtObj = new GameObject("Text");
txtObj.transform.SetParent(slotObj.transform, false);
RectTransform txtRt = txtObj.AddComponent<RectTransform>();
txtRt.anchorMin = Vector2.zero;
txtRt.anchorMax = Vector2.one;
txtRt.sizeDelta = Vector2.zero;
Text st = txtObj.AddComponent<Text>();
st.font = handwrittenFont;
st.fontSize = 22;
st.fontStyle = FontStyle.Bold;
st.alignment = TextAnchor.MiddleCenter;
st.color = new Color(0.45f, 0.45f, 0.45f, 0.7f);
st.text = "?";
slotTexts[i] = st;
}
var trackerComp = trackerObj.AddComponent<CodeHUDTracker>();
SetField(trackerComp, "digitSlotTexts", slotTexts);
SetField(trackerComp, "digitSlotBorders", slotBorders);
}
private static void SetupNoteViewerPanel(Transform hudRoot)
{
var old = hudRoot.Find("NoteViewerPanel");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
var tornPaperSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/Textures/TornPaper_Sprite.png");
var inkFreeFont = AssetDatabase.LoadAssetAtPath<Font>("Assets/Fonts/Inkfree.ttf");
var courierFont = Font.CreateDynamicFontFromOSFont("Courier New", 18);
if (courierFont == null) courierFont = Font.CreateDynamicFontFromOSFont("Consolas", 18);
if (courierFont == null) courierFont = defaultUiFont;
GameObject rootPanel = new GameObject("NoteViewerPanel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
rootPanel.transform.SetParent(hudRoot, false);
RectTransform rootRt = rootPanel.GetComponent<RectTransform>();
rootRt.anchorMin = Vector2.zero;
rootRt.anchorMax = Vector2.one;
rootRt.sizeDelta = Vector2.zero;
Image overlay = rootPanel.GetComponent<Image>();
overlay.color = new Color(0.02f, 0.02f, 0.03f, 0.82f);
Button overlayBtn = rootPanel.GetComponent<Button>();
overlayBtn.transition = Selectable.Transition.None;
GameObject shadowObj = new GameObject("PaperShadow", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
shadowObj.transform.SetParent(rootPanel.transform, false);
RectTransform shadowRt = shadowObj.GetComponent<RectTransform>();
shadowRt.anchorMin = new Vector2(0.5f, 0.5f);
shadowRt.anchorMax = new Vector2(0.5f, 0.5f);
shadowRt.pivot = new Vector2(0.5f, 0.5f);
shadowRt.anchoredPosition = new Vector2(8f, 12f);
shadowRt.sizeDelta = new Vector2(635f, 635f);
Image shadowImg = shadowObj.GetComponent<Image>();
if (tornPaperSprite != null) shadowImg.sprite = tornPaperSprite;
shadowImg.color = new Color(0f, 0f, 0f, 0.45f);
GameObject paperObj = new GameObject("NoteDocumentCard", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
paperObj.transform.SetParent(rootPanel.transform, false);
RectTransform paperRt = paperObj.GetComponent<RectTransform>();
paperRt.anchorMin = new Vector2(0.5f, 0.5f);
paperRt.anchorMax = new Vector2(0.5f, 0.5f);
paperRt.pivot = new Vector2(0.5f, 0.5f);
paperRt.anchoredPosition = new Vector2(0f, 20f);
paperRt.sizeDelta = new Vector2(620f, 620f);
Image paperImg = paperObj.GetComponent<Image>();
if (tornPaperSprite != null) paperImg.sprite = tornPaperSprite;
paperImg.color = Color.white;
paperObj.transform.localRotation = Quaternion.Euler(0f, 0f, -1.2f);
shadowObj.transform.localRotation = paperObj.transform.localRotation;
GameObject headerObj = new GameObject("DocumentHeader", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
headerObj.transform.SetParent(paperObj.transform, false);
RectTransform headerRt = headerObj.GetComponent<RectTransform>();
headerRt.anchorMin = new Vector2(0.5f, 1f);
headerRt.anchorMax = new Vector2(0.5f, 1f);
headerRt.pivot = new Vector2(0.5f, 1f);
headerRt.anchoredPosition = new Vector2(0f, -65f);
headerRt.sizeDelta = new Vector2(450f, 32f);
Text headerTxt = headerObj.GetComponent<Text>();
headerTxt.font = courierFont;
headerTxt.fontSize = 24;
headerTxt.fontStyle = FontStyle.Bold;
headerTxt.alignment = TextAnchor.MiddleCenter;
headerTxt.color = new Color(0.12f, 0.12f, 0.14f, 1.0f);
headerTxt.text = "Секретный протокол";
GameObject ellipsisObj = new GameObject("EllipsisText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
ellipsisObj.transform.SetParent(paperObj.transform, false);
RectTransform ellipsisRt = ellipsisObj.GetComponent<RectTransform>();
ellipsisRt.anchorMin = new Vector2(0.5f, 1f);
ellipsisRt.anchorMax = new Vector2(0.5f, 1f);
ellipsisRt.pivot = new Vector2(0.5f, 1f);
ellipsisRt.anchoredPosition = new Vector2(0f, -94f);
ellipsisRt.sizeDelta = new Vector2(450f, 22f);
Text ellipsisTxt = ellipsisObj.GetComponent<Text>();
ellipsisTxt.font = courierFont;
ellipsisTxt.fontSize = 18;
ellipsisTxt.alignment = TextAnchor.MiddleCenter;
ellipsisTxt.color = new Color(0.35f, 0.35f, 0.38f, 0.85f);
ellipsisTxt.text = ". . .";
GameObject sectorObj = new GameObject("SectorText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
sectorObj.transform.SetParent(paperObj.transform, false);
RectTransform sectorRt = sectorObj.GetComponent<RectTransform>();
sectorRt.anchorMin = new Vector2(0.5f, 1f);
sectorRt.anchorMax = new Vector2(0.5f, 1f);
sectorRt.pivot = new Vector2(0.5f, 1f);
sectorRt.anchoredPosition = new Vector2(0f, -120f);
sectorRt.sizeDelta = new Vector2(440f, 22f);
Text sectorTxt = sectorObj.GetComponent<Text>();
sectorTxt.font = courierFont;
sectorTxt.fontSize = 12;
sectorTxt.fontStyle = FontStyle.Bold;
sectorTxt.alignment = TextAnchor.MiddleCenter;
sectorTxt.color = new Color(0.38f, 0.38f, 0.42f, 0.9f);
sectorTxt.text = "ОТДЕЛ: СЕКТОР B // КРИОГЕНИКА";
GameObject storyObj = new GameObject("StoryText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
storyObj.transform.SetParent(paperObj.transform, false);
RectTransform storyRt = storyObj.GetComponent<RectTransform>();
storyRt.anchorMin = new Vector2(0.5f, 1f);
storyRt.anchorMax = new Vector2(0.5f, 1f);
storyRt.pivot = new Vector2(0.5f, 1f);
storyRt.anchoredPosition = new Vector2(0f, -148f);
storyRt.sizeDelta = new Vector2(430f, 160f);
Text storyTxt = storyObj.GetComponent<Text>();
storyTxt.font = courierFont;
storyTxt.fontSize = 13;
storyTxt.alignment = TextAnchor.UpperLeft;
storyTxt.lineSpacing = 1.18f;
storyTxt.color = new Color(0.18f, 0.18f, 0.22f, 1.0f);
storyTxt.text = "Срочное сообщение: система заблокировала шлюз из-за проникновения мутантов. Первую цифру кода аварийного лифта пришлось изменить, чтобы изолировать отсек. Запомните её, если хотите выбраться отсюда живым!";
GameObject codeArea = new GameObject("PaperCodeArea", typeof(RectTransform));
codeArea.transform.SetParent(paperObj.transform, false);
RectTransform codeRt = codeArea.GetComponent<RectTransform>();
codeRt.anchorMin = new Vector2(0.5f, 1f);
codeRt.anchorMax = new Vector2(0.5f, 1f);
codeRt.pivot = new Vector2(0.5f, 1f);
codeRt.anchoredPosition = new Vector2(0f, -320f);
codeRt.sizeDelta = new Vector2(440f, 150f);
GameObject posObj = new GameObject("PositionText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
posObj.transform.SetParent(codeArea.transform, false);
RectTransform posRt = posObj.GetComponent<RectTransform>();
posRt.anchorMin = new Vector2(0.5f, 1f);
posRt.anchorMax = new Vector2(0.5f, 1f);
posRt.pivot = new Vector2(0.5f, 1f);
posRt.anchoredPosition = new Vector2(0f, 0f);
posRt.sizeDelta = new Vector2(440f, 26f);
Text posTxt = posObj.GetComponent<Text>();
posTxt.font = courierFont;
posTxt.fontSize = 13;
posTxt.fontStyle = FontStyle.Bold;
posTxt.alignment = TextAnchor.MiddleCenter;
posTxt.color = new Color(0.25f, 0.25f, 0.30f, 1.0f);
posTxt.text = "ПОЗИЦИЯ В КОДЕ ЛИФТА: 1-Я ЦИФРА [ _ * * * ]";
GameObject digitObj = new GameObject("BigDigitText", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
digitObj.transform.SetParent(codeArea.transform, false);
RectTransform digitRt = digitObj.GetComponent<RectTransform>();
digitRt.anchorMin = new Vector2(0.5f, 0.5f);
digitRt.anchorMax = new Vector2(0.5f, 0.5f);
digitRt.pivot = new Vector2(0.5f, 0.5f);
digitRt.anchoredPosition = new Vector2(0f, -25f);
digitRt.sizeDelta = new Vector2(160f, 110f);
Text digitTxt = digitObj.GetComponent<Text>();
digitTxt.font = inkFreeFont != null ? inkFreeFont : courierFont;
digitTxt.fontSize = 82;
digitTxt.fontStyle = FontStyle.Bold;
digitTxt.alignment = TextAnchor.MiddleCenter;
digitTxt.color = new Color(0.06f, 0.16f, 0.52f, 1.0f);
digitTxt.text = "7";
GameObject hintObj = new GameObject("CloseHint", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
hintObj.transform.SetParent(rootPanel.transform, false);
RectTransform hintRt = hintObj.GetComponent<RectTransform>();
hintRt.anchorMin = new Vector2(0.5f, 0.5f);
hintRt.anchorMax = new Vector2(0.5f, 0.5f);
hintRt.pivot = new Vector2(0.5f, 0.5f);
hintRt.anchoredPosition = new Vector2(0f, -325f);
hintRt.sizeDelta = new Vector2(600f, 32f);
Text hintTxt = hintObj.GetComponent<Text>();
hintTxt.font = courierFont;
hintTxt.fontSize = 13;
hintTxt.alignment = TextAnchor.MiddleCenter;
hintTxt.color = new Color(0.85f, 0.85f, 0.85f, 0.70f);
hintTxt.text = "[ НАЖМИТЕ E, ESC ИЛИ КЛИКНИТЕ, ЧТОБЫ ЗАКРЫТЬ ]";
var noteViewerComp = rootPanel.AddComponent<NoteViewerUI>();
SetField(noteViewerComp, "notePanelRoot", rootPanel);
SetField(noteViewerComp, "documentHeader", headerTxt);
SetField(noteViewerComp, "sectorText", sectorTxt);
SetField(noteViewerComp, "positionNoticeText", posTxt);
SetField(noteViewerComp, "bigDigitText", digitTxt);
SetField(noteViewerComp, "flavorBodyText", storyTxt);
SetField(noteViewerComp, "closeButton", overlayBtn);
overlayBtn.onClick.RemoveAllListeners();
overlayBtn.onClick.AddListener(noteViewerComp.CloseNote);
rootPanel.SetActive(false);
}
private static void SetupElevatorKeypadPanel(Transform hudRoot)
{
var old = hudRoot.Find("ElevatorKeypadPanel");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
GameObject rootPanel = new GameObject("ElevatorKeypadPanel");
rootPanel.transform.SetParent(hudRoot, false);
RectTransform rootRt = rootPanel.AddComponent<RectTransform>();
rootRt.anchorMin = Vector2.zero;
rootRt.anchorMax = Vector2.one;
rootRt.sizeDelta = Vector2.zero;
Image overlay = rootPanel.AddComponent<Image>();
overlay.color = new Color(0f, 0f, 0f, 0.9f);
GameObject card = new GameObject("TerminalCard");
card.transform.SetParent(rootPanel.transform, false);
RectTransform cardRt = card.AddComponent<RectTransform>();
cardRt.anchorMin = new Vector2(0.5f, 0.5f);
cardRt.anchorMax = new Vector2(0.5f, 0.5f);
cardRt.pivot = new Vector2(0.5f, 0.5f);
cardRt.sizeDelta = new Vector2(560f, 660f);
Image cardBg = card.AddComponent<Image>();
cardBg.color = new Color(0.04f, 0.04f, 0.04f, 0.98f);
Outline cardOutline = card.AddComponent<Outline>();
cardOutline.effectColor = new Color(1.0f, 0.85f, 0.10f, 0.8f);
cardOutline.effectDistance = new Vector2(2f, -2f);
GameObject headerObj = new GameObject("Title");
headerObj.transform.SetParent(card.transform, false);
RectTransform headerRt = headerObj.AddComponent<RectTransform>();
headerRt.anchorMin = new Vector2(0f, 1f);
headerRt.anchorMax = new Vector2(1f, 1f);
headerRt.pivot = new Vector2(0.5f, 1f);
headerRt.anchoredPosition = new Vector2(0f, -20f);
headerRt.sizeDelta = new Vector2(520f, 36f);
Text headerTxt = headerObj.AddComponent<Text>();
headerTxt.font = defaultUiFont;
headerTxt.fontSize = 18;
headerTxt.fontStyle = FontStyle.Bold;
headerTxt.alignment = TextAnchor.MiddleCenter;
headerTxt.color = new Color(1.0f, 0.85f, 0.1f, 1.0f);
headerTxt.text = "ТЕРМИНАЛ ЛИФТА // ВЫХОД НА УРОВЕНЬ 2";
GameObject displayBox = new GameObject("DisplayBox");
displayBox.transform.SetParent(card.transform, false);
RectTransform displayRt = displayBox.AddComponent<RectTransform>();
displayRt.anchorMin = new Vector2(0.5f, 1f);
displayRt.anchorMax = new Vector2(0.5f, 1f);
displayRt.pivot = new Vector2(0.5f, 1f);
displayRt.anchoredPosition = new Vector2(0f, -70f);
displayRt.sizeDelta = new Vector2(460f, 85f);
Image dispBg = displayBox.AddComponent<Image>();
dispBg.color = new Color(0.01f, 0.01f, 0.01f, 1.0f);
Outline dispOutline = displayBox.AddComponent<Outline>();
dispOutline.effectColor = new Color(1.0f, 0.85f, 0.10f, 0.4f);
dispOutline.effectDistance = new Vector2(1.5f, -1.5f);
Text[] digitSlots = new Text[4];
Image[] digitSlotBorders = new Image[4];
float slotStartX = -150f;
float slotSpacing = 100f;
for (int i = 0; i < 4; i++)
{
GameObject sObj = new GameObject($"DigitSlot_{i}");
sObj.transform.SetParent(displayBox.transform, false);
RectTransform sRt = sObj.AddComponent<RectTransform>();
sRt.anchorMin = new Vector2(0.5f, 0.5f);
sRt.anchorMax = new Vector2(0.5f, 0.5f);
sRt.pivot = new Vector2(0.5f, 0.5f);
sRt.anchoredPosition = new Vector2(slotStartX + i * slotSpacing, 0f);
sRt.sizeDelta = new Vector2(75f, 65f);
Image sBg = sObj.AddComponent<Image>();
sBg.color = new Color(0.06f, 0.06f, 0.06f, 1.0f);
digitSlotBorders[i] = sBg;
Outline sOutline = sObj.AddComponent<Outline>();
sOutline.effectColor = new Color(1.0f, 0.85f, 0.10f, 0.3f);
sOutline.effectDistance = new Vector2(1f, -1f);
GameObject txtObj = new GameObject("Text");
txtObj.transform.SetParent(sObj.transform, false);
RectTransform txtRt = txtObj.AddComponent<RectTransform>();
txtRt.anchorMin = Vector2.zero;
txtRt.anchorMax = Vector2.one;
txtRt.sizeDelta = Vector2.zero;
Text st = txtObj.AddComponent<Text>();
st.font = defaultUiFont;
st.fontSize = 42;
st.fontStyle = FontStyle.Bold;
st.alignment = TextAnchor.MiddleCenter;
st.color = new Color(1.0f, 0.85f, 0.1f, 1.0f);
st.text = "_";
digitSlots[i] = st;
}
GameObject statusObj = new GameObject("StatusText");
statusObj.transform.SetParent(card.transform, false);
RectTransform statusRt = statusObj.AddComponent<RectTransform>();
statusRt.anchorMin = new Vector2(0.5f, 1f);
statusRt.anchorMax = new Vector2(0.5f, 1f);
statusRt.pivot = new Vector2(0.5f, 1f);
statusRt.anchoredPosition = new Vector2(0f, -170f);
statusRt.sizeDelta = new Vector2(500f, 32f);
Text statusTxt = statusObj.AddComponent<Text>();
statusTxt.font = defaultUiFont;
statusTxt.fontSize = 15;
statusTxt.fontStyle = FontStyle.Bold;
statusTxt.alignment = TextAnchor.MiddleCenter;
statusTxt.color = new Color(1.0f, 0.85f, 0.1f, 1.0f);
statusTxt.text = "ВВЕДИТЕ 4-ЗНАЧНЫЙ КОД С ЗАПИСОК";
GameObject keypadGrid = new GameObject("KeypadGrid");
keypadGrid.transform.SetParent(card.transform, false);
RectTransform gridRt = keypadGrid.AddComponent<RectTransform>();
gridRt.anchorMin = new Vector2(0.5f, 0f);
gridRt.anchorMax = new Vector2(0.5f, 0f);
gridRt.pivot = new Vector2(0.5f, 0f);
gridRt.anchoredPosition = new Vector2(0f, 90f);
gridRt.sizeDelta = new Vector2(390f, 340f);
Button[] numberButtons = new Button[10];
Button clearButton = null;
Button enterButton = null;
float btnW = 115f;
float btnH = 68f;
float[] xs = new float[] { -130f, 0f, 130f };
int[][] layout = new int[][]
{
new int[] { 1, 2, 3 },
new int[] { 4, 5, 6 },
new int[] { 7, 8, 9 }
};
for (int r = 0; r < 3; r++)
{
float yPos = 255f - r * 85f;
for (int c = 0; c < 3; c++)
{
int val = layout[r][c];
numberButtons[val] = CreateKeypadButton(keypadGrid.transform, xs[c], yPos, btnW, btnH, val.ToString(),
new Color(0.12f, 0.12f, 0.12f, 1f), new Color(1f, 0.85f, 0.1f, 1f));
}
}
clearButton = CreateKeypadButton(keypadGrid.transform, xs[0], 0f, btnW, btnH, "СБРОС",
new Color(0.35f, 0.08f, 0.08f, 1f), new Color(1f, 0.4f, 0.4f, 1f));
numberButtons[0] = CreateKeypadButton(keypadGrid.transform, xs[1], 0f, btnW, btnH, "0",
new Color(0.12f, 0.12f, 0.12f, 1f), new Color(1f, 0.85f, 0.1f, 1f));
enterButton = CreateKeypadButton(keypadGrid.transform, xs[2], 0f, btnW, btnH, "ВВОД",
new Color(1.0f, 0.85f, 0.1f, 1f), new Color(0.05f, 0.05f, 0.05f, 1f));
GameObject exitObj = new GameObject("ExitButton");
exitObj.transform.SetParent(card.transform, false);
RectTransform exitRt = exitObj.AddComponent<RectTransform>();
exitRt.anchorMin = new Vector2(0.5f, 0f);
exitRt.anchorMax = new Vector2(0.5f, 0f);
exitRt.pivot = new Vector2(0.5f, 0f);
exitRt.anchoredPosition = new Vector2(0f, 25f);
exitRt.sizeDelta = new Vector2(390f, 44f);
Image exitImg = exitObj.AddComponent<Image>();
exitImg.color = new Color(0.12f, 0.12f, 0.12f, 1f);
Outline exitOutline = exitObj.AddComponent<Outline>();
exitOutline.effectColor = new Color(0.5f, 0.5f, 0.5f, 0.6f);
exitOutline.effectDistance = new Vector2(1.5f, -1.5f);
Button exitBtn = exitObj.AddComponent<Button>();
GameObject exitTxtObj = new GameObject("Text");
exitTxtObj.transform.SetParent(exitObj.transform, false);
RectTransform exitTxtRt = exitTxtObj.AddComponent<RectTransform>();
exitTxtRt.anchorMin = Vector2.zero;
exitTxtRt.anchorMax = Vector2.one;
exitTxtRt.sizeDelta = Vector2.zero;
Text etxt = exitTxtObj.AddComponent<Text>();
etxt.font = defaultUiFont;
etxt.fontSize = 15;
etxt.fontStyle = FontStyle.Bold;
etxt.alignment = TextAnchor.MiddleCenter;
etxt.color = new Color(0.85f, 0.85f, 0.85f, 1.0f);
etxt.text = "ОТОЙТИ ОТ ТЕРМИНАЛА [ESC]";
var keypadComp = rootPanel.AddComponent<ElevatorKeypadUI>();
SetField(keypadComp, "keypadPanelRoot", rootPanel);
SetField(keypadComp, "digitSlots", digitSlots);
SetField(keypadComp, "digitSlotBorders", digitSlotBorders);
SetField(keypadComp, "statusText", statusTxt);
SetField(keypadComp, "numberButtons", numberButtons);
SetField(keypadComp, "clearButton", clearButton);
SetField(keypadComp, "enterButton", enterButton);
SetField(keypadComp, "exitButton", exitBtn);
rootPanel.SetActive(false);
}
private static Button CreateKeypadButton(Transform parent, float x, float y, float w, float h, string text, Color bgColor, Color textColor)
{
GameObject btnObj = new GameObject($"Btn_{text}");
btnObj.transform.SetParent(parent, false);
RectTransform rt = btnObj.AddComponent<RectTransform>();
rt.anchorMin = new Vector2(0.5f, 0f);
rt.anchorMax = new Vector2(0.5f, 0f);
rt.pivot = new Vector2(0.5f, 0f);
rt.anchoredPosition = new Vector2(x, y);
rt.sizeDelta = new Vector2(w, h);
Image bg = btnObj.AddComponent<Image>();
bg.color = bgColor;
Outline outline = btnObj.AddComponent<Outline>();
outline.effectColor = new Color(1.0f, 0.85f, 0.10f, 0.45f);
outline.effectDistance = new Vector2(1.5f, -1.5f);
Button btn = btnObj.AddComponent<Button>();
ColorBlock cb = btn.colors;
cb.normalColor = bgColor;
cb.highlightedColor = bgColor + new Color(0.15f, 0.15f, 0.15f, 0f);
cb.pressedColor = bgColor - new Color(0.1f, 0.1f, 0.1f, 0f);
btn.colors = cb;
GameObject txtObj = new GameObject("Text");
txtObj.transform.SetParent(btnObj.transform, false);
RectTransform txtRt = txtObj.AddComponent<RectTransform>();
txtRt.anchorMin = Vector2.zero;
txtRt.anchorMax = Vector2.one;
txtRt.sizeDelta = Vector2.zero;
Text txt = txtObj.AddComponent<Text>();
txt.font = defaultUiFont;
txt.fontSize = (text.Length > 2) ? 17 : 26;
txt.fontStyle = FontStyle.Bold;
txt.alignment = TextAnchor.MiddleCenter;
txt.color = textColor;
txt.text = text;
return btn;
}
private static void SetupLevelSuccessPanel(Transform hudRoot)
{
var old = hudRoot.Find("LevelSuccessPanel");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
GameObject rootPanel = new GameObject("LevelSuccessPanel");
rootPanel.transform.SetParent(hudRoot, false);
RectTransform rootRt = rootPanel.AddComponent<RectTransform>();
rootRt.anchorMin = Vector2.zero;
rootRt.anchorMax = Vector2.one;
rootRt.sizeDelta = Vector2.zero;
Image overlay = rootPanel.AddComponent<Image>();
overlay.color = new Color(0f, 0f, 0f, 0.92f);
GameObject card = new GameObject("SuccessCard");
card.transform.SetParent(rootPanel.transform, false);
RectTransform cardRt = card.AddComponent<RectTransform>();
cardRt.anchorMin = new Vector2(0.5f, 0.5f);
cardRt.anchorMax = new Vector2(0.5f, 0.5f);
cardRt.pivot = new Vector2(0.5f, 0.5f);
cardRt.sizeDelta = new Vector2(650f, 440f);
Image cardBg = card.AddComponent<Image>();
cardBg.color = new Color(0.04f, 0.04f, 0.04f, 0.98f);
Outline cardOutline = card.AddComponent<Outline>();
cardOutline.effectColor = new Color(1.0f, 0.85f, 0.10f, 0.9f);
cardOutline.effectDistance = new Vector2(2f, -2f);
GameObject titleObj = new GameObject("Title");
titleObj.transform.SetParent(card.transform, false);
RectTransform titleRt = titleObj.AddComponent<RectTransform>();
titleRt.anchorMin = new Vector2(0f, 1f);
titleRt.anchorMax = new Vector2(1f, 1f);
titleRt.pivot = new Vector2(0.5f, 1f);
titleRt.anchoredPosition = new Vector2(0f, -30f);
titleRt.sizeDelta = new Vector2(600f, 45f);
Text titleTxt = titleObj.AddComponent<Text>();
titleTxt.font = defaultUiFont;
titleTxt.fontSize = 32;
titleTxt.fontStyle = FontStyle.Bold;
titleTxt.alignment = TextAnchor.MiddleCenter;
titleTxt.color = new Color(1.0f, 0.85f, 0.1f, 1.0f);
titleTxt.text = "УРОВЕНЬ 1 ПРОЙДЕН!";
GameObject subObj = new GameObject("Subtitle");
subObj.transform.SetParent(card.transform, false);
RectTransform subRt = subObj.AddComponent<RectTransform>();
subRt.anchorMin = new Vector2(0f, 1f);
subRt.anchorMax = new Vector2(1f, 1f);
subRt.pivot = new Vector2(0.5f, 1f);
subRt.anchoredPosition = new Vector2(0f, -85f);
subRt.sizeDelta = new Vector2(600f, 30f);
Text subTxt = subObj.AddComponent<Text>();
subTxt.font = defaultUiFont;
subTxt.fontSize = 16;
subTxt.fontStyle = FontStyle.Bold;
subTxt.alignment = TextAnchor.MiddleCenter;
subTxt.color = new Color(0.2f, 1.0f, 0.4f, 1.0f);
subTxt.text = "ЛИФТ УСПЕШНО АКТИВИРОВАН // СПУСК НА УРОВЕНЬ 2";
GameObject descObj = new GameObject("Description");
descObj.transform.SetParent(card.transform, false);
RectTransform descRt = descObj.AddComponent<RectTransform>();
descRt.anchorMin = new Vector2(0.5f, 0.5f);
descRt.anchorMax = new Vector2(0.5f, 0.5f);
descRt.pivot = new Vector2(0.5f, 0.5f);
descRt.anchoredPosition = new Vector2(0f, 0f);
descRt.sizeDelta = new Vector2(560f, 110f);
Text descTxt = descObj.AddComponent<Text>();
descTxt.font = defaultUiFont;
descTxt.fontSize = 16;
descTxt.lineSpacing = 1.3f;
descTxt.alignment = TextAnchor.MiddleCenter;
descTxt.color = new Color(0.85f, 0.85f, 0.85f, 1.0f);
descTxt.text = "Вы успешно собрали все 4 цифры защитного кода с записок персонала и запустили гидравлический лифт.\n\nВторой (более сложный) уровень локации находится в разработке!";
GameObject btn1 = new GameObject("NextLevelButton");
btn1.transform.SetParent(card.transform, false);
RectTransform btn1Rt = btn1.AddComponent<RectTransform>();
btn1Rt.anchorMin = new Vector2(0.5f, 0f);
btn1Rt.anchorMax = new Vector2(0.5f, 0f);
btn1Rt.pivot = new Vector2(0.5f, 0f);
btn1Rt.anchoredPosition = new Vector2(0f, 95f);
btn1Rt.sizeDelta = new Vector2(420f, 50f);
Image btn1Img = btn1.AddComponent<Image>();
btn1Img.color = new Color(1.0f, 0.85f, 0.1f, 1.0f);
Button nextBtn = btn1.AddComponent<Button>();
GameObject b1Txt = new GameObject("Text");
b1Txt.transform.SetParent(btn1.transform, false);
RectTransform b1TxtRt = b1Txt.AddComponent<RectTransform>();
b1TxtRt.anchorMin = Vector2.zero;
b1TxtRt.anchorMax = Vector2.one;
b1TxtRt.sizeDelta = Vector2.zero;
Text t1 = b1Txt.AddComponent<Text>();
t1.font = defaultUiFont;
t1.fontSize = 16;
t1.fontStyle = FontStyle.Bold;
t1.alignment = TextAnchor.MiddleCenter;
t1.color = new Color(0.05f, 0.05f, 0.05f, 1.0f);
t1.text = "СЫГРАТЬ СНОВА (НОВЫЙ СЛУЧАЙНЫЙ КОД)";
GameObject btn2 = new GameObject("MainMenuButton");
btn2.transform.SetParent(card.transform, false);
RectTransform btn2Rt = btn2.AddComponent<RectTransform>();
btn2Rt.anchorMin = new Vector2(0.5f, 0f);
btn2Rt.anchorMax = new Vector2(0.5f, 0f);
btn2Rt.pivot = new Vector2(0.5f, 0f);
btn2Rt.anchoredPosition = new Vector2(0f, 35f);
btn2Rt.sizeDelta = new Vector2(420f, 44f);
Image btn2Img = btn2.AddComponent<Image>();
btn2Img.color = new Color(0.12f, 0.12f, 0.12f, 1.0f);
Outline b2Outline = btn2.AddComponent<Outline>();
b2Outline.effectColor = new Color(0.5f, 0.5f, 0.5f, 0.6f);
b2Outline.effectDistance = new Vector2(1.5f, -1.5f);
Button menuBtn = btn2.AddComponent<Button>();
GameObject b2Txt = new GameObject("Text");
b2Txt.transform.SetParent(btn2.transform, false);
RectTransform b2TxtRt = b2Txt.AddComponent<RectTransform>();
b2TxtRt.anchorMin = Vector2.zero;
b2TxtRt.anchorMax = Vector2.one;
b2TxtRt.sizeDelta = Vector2.zero;
Text t2 = b2Txt.AddComponent<Text>();
t2.font = defaultUiFont;
t2.fontSize = 15;
t2.fontStyle = FontStyle.Bold;
t2.alignment = TextAnchor.MiddleCenter;
t2.color = new Color(0.85f, 0.85f, 0.85f, 1.0f);
t2.text = "В ГЛАВНОЕ МЕНЮ";
var successComp = rootPanel.AddComponent<LevelSuccessUI>();
SetField(successComp, "successPanelRoot", rootPanel);
SetField(successComp, "nextLevelButton", nextBtn);
SetField(successComp, "mainMenuButton", menuBtn);
rootPanel.SetActive(false);
}
private static void SetField(object target, string fieldName, object value)
{
if (target == null) return;
var field = target.GetType().GetField(fieldName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (field != null)
{
field.SetValue(target, value);
}
}
}