Kursach_StealthAction/Assets/Scripts/Editor/Level2SceneBuilder.cs

759 lines
38 KiB
C#
Raw 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 UnityEditor.SceneManagement;
using UnityEngine.UI;
using Unity.AI.Navigation;
using UnityEngine.AI;
public static class Level2SceneBuilder
{
[MenuItem("Tools/Synthesia/Build Level 2 (SubLevel Complex)")]
public static void BuildLevel2Scene()
{
Debug.Log("<color=cyan>[Level 2] Начало сборки 2-го уровня...</color>");
// 1. Открываем SampleScene как шаблон
string samplePath = "Assets/Scenes/SampleScene.unity";
string level2Path = "Assets/Scenes/Level2.unity";
var scene = EditorSceneManager.OpenScene(samplePath, OpenSceneMode.Single);
// 2. Сохраняем сцену под новым именем Level2.unity
bool savedAs = EditorSceneManager.SaveScene(scene, level2Path);
if (!savedAs)
{
Debug.LogError("[Level 2] Не удалось сохранить сцену как Level2.unity!");
return;
}
scene = EditorSceneManager.OpenScene(level2Path, OpenSceneMode.Single);
// 3. Очищаем объекты 1-го уровня
string[] toRemove = new string[] {
"Laboratory_Complex",
"Cosmic_Retro_Laboratory",
"Elevator_Level2_Zone",
"StoryNotes_Container",
"Enemies",
"StoryManager",
"SubLevel2_Complex",
"Enemies_Level2"
};
foreach (var name in toRemove)
{
var go = GameObject.Find(name);
if (go != null) Undo.DestroyObjectImmediate(go);
}
// Удаляем старые тестовые коробки патронов
var allBoxes = Object.FindObjectsByType<AmmoPickup>(FindObjectsInactive.Include, FindObjectsSortMode.None);
foreach (var b in allBoxes)
{
Undo.DestroyObjectImmediate(b.gameObject);
}
// 4. Загрузка материалов и префабов
Material echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoCloudMat.mat");
if (echoMat == null) echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoMat.mat");
GameObject mutantPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/Mutant.prefab");
Font defaultFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
// 5. Создаем контейнер SubLevel2_Complex
GameObject level2Root = new GameObject("SubLevel2_Complex");
level2Root.transform.position = Vector3.zero;
// Позиционируем игрока в стартовом лифте прибытия (0, 0.1, -22)
GameObject playerGo = GameObject.FindWithTag("Player");
if (playerGo == null) playerGo = GameObject.Find("Player");
if (playerGo != null)
{
playerGo.transform.position = new Vector3(0f, 0.15f, -22f);
playerGo.transform.rotation = Quaternion.identity;
}
// --- ГЕОМЕТРИЯ 2-ГО УРОВНЯ ---
// А. Шахта лифта прибытия (Arrival Chamber): (0, 0, -22), размер 6x6
BuildRoom(level2Root.transform, "Room_ArrivalElevator", new Vector3(0f, 0f, -22f), new Vector3(6f, 0.2f, 6f), 3.8f, echoMat,
hasNorthDoor: true, hasSouthDoor: false, hasWestDoor: false, hasEastDoor: false);
// Рама лифта прибытия
BuildElevatorShaft(level2Root.transform, new Vector3(0f, 0f, -22.5f), echoMat);
// Б. Южный шлюзовой коридор: (0, 0, -12.5), размер 4x13
BuildCorridor(level2Root.transform, "Corridor_South", new Vector3(0f, 0f, -12.5f), new Vector3(4f, 0.2f, 13f), 3.8f, echoMat, isNS: true);
// В. Центральный узел (Central Hub): (0, 0, 0), размер 12x12
BuildRoom(level2Root.transform, "Room_CentralHub", new Vector3(0f, 0f, 0f), new Vector3(12f, 0.2f, 12f), 3.8f, echoMat,
hasNorthDoor: true, hasSouthDoor: true, hasWestDoor: true, hasEastDoor: true);
// Колонны в Центральном узле
BuildPillar(level2Root.transform, new Vector3(-3.5f, 1.9f, -3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
BuildPillar(level2Root.transform, new Vector3(3.5f, 1.9f, -3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
BuildPillar(level2Root.transform, new Vector3(-3.5f, 1.9f, 3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
BuildPillar(level2Root.transform, new Vector3(3.5f, 1.9f, 3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
// Г. Западный сектор: Крио-изолятор и виварий (-17, 0, 0), размер 14x14
BuildCorridor(level2Root.transform, "Corridor_West", new Vector3(-8f, 0f, 0f), new Vector3(4f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
BuildRoom(level2Root.transform, "Room_CryoIsolation", new Vector3(-17f, 0f, 0f), new Vector3(14f, 0.2f, 14f), 3.8f, echoMat,
hasNorthDoor: false, hasSouthDoor: false, hasWestDoor: false, hasEastDoor: true);
// Д. Восточный сектор: Био-синтез и химические реакторы (17, 0, 0), размер 14x14
BuildCorridor(level2Root.transform, "Corridor_East", new Vector3(8f, 0f, 0f), new Vector3(4f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
BuildRoom(level2Root.transform, "Room_BioSynthesis", new Vector3(17f, 0f, 0f), new Vector3(14f, 0.2f, 14f), 3.8f, echoMat,
hasNorthDoor: false, hasSouthDoor: false, hasWestDoor: true, hasEastDoor: false);
// Е. Северный переход к архиву и реактору (0, 0, 9), размер 4x6
BuildCorridor(level2Root.transform, "Corridor_North", new Vector3(0f, 0f, 9f), new Vector3(4f, 0.2f, 6f), 3.8f, echoMat, isNS: true);
// Ж. Сектор архивов и серверной (-11, 0, 15), размер 10x10
BuildCorridor(level2Root.transform, "Corridor_ArchivesBranch", new Vector3(-4.5f, 0f, 9f), new Vector3(5f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
BuildRoom(level2Root.transform, "Room_SecurityArchives", new Vector3(-11f, 0f, 15f), new Vector3(10f, 0.2f, 10f), 3.8f, echoMat,
hasNorthDoor: false, hasSouthDoor: true, hasWestDoor: false, hasEastDoor: false);
// З. Главный реакторный отсек и эвакуационный шлюз (0, 0, 26), просторная арена 26x26 м
BuildRoom(level2Root.transform, "Room_ReactorAirlock", new Vector3(0f, 0f, 26f), new Vector3(26f, 0.2f, 26f), 4.5f, echoMat,
hasNorthDoor: false, hasSouthDoor: true, hasWestDoor: false, hasEastDoor: false);
// Массивные реакторные колонны-укрытия на арене босса
BuildPillar(level2Root.transform, new Vector3(-6.5f, 2.25f, 20f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
BuildPillar(level2Root.transform, new Vector3(6.5f, 2.25f, 20f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
BuildPillar(level2Root.transform, new Vector3(-6.5f, 2.25f, 31f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
BuildPillar(level2Root.transform, new Vector3(6.5f, 2.25f, 31f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
// Финальный терминал эвакуации и шлюз на севере арены (0, 0, 38.2)
BuildFinalEscapeAirlock(level2Root.transform, new Vector3(0f, 0f, 38.2f), echoMat);
// Атмосферное аварийное освещение (тусклые маяки)
BuildBeacon(level2Root.transform, new Vector3(0f, 2.5f, -22f), new Color(0.2f, 0.6f, 0.9f), 1.8f, 8f);
BuildBeacon(level2Root.transform, new Vector3(0f, 2.8f, 0f), new Color(1.0f, 0.75f, 0.2f), 2.2f, 12f);
BuildBeacon(level2Root.transform, new Vector3(-17f, 2.8f, 0f), new Color(0.1f, 0.8f, 0.9f), 1.8f, 10f);
BuildBeacon(level2Root.transform, new Vector3(17f, 2.8f, 0f), new Color(1.0f, 0.45f, 0.1f), 1.8f, 10f);
BuildBeacon(level2Root.transform, new Vector3(-11f, 2.5f, 15f), new Color(0.3f, 0.8f, 0.4f), 1.5f, 8f);
BuildBeacon(level2Root.transform, new Vector3(-8f, 3.2f, 22f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
BuildBeacon(level2Root.transform, new Vector3(8f, 3.2f, 22f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
BuildBeacon(level2Root.transform, new Vector3(-8f, 3.2f, 31f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
BuildBeacon(level2Root.transform, new Vector3(8f, 3.2f, 31f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
// Патроны на уровне 2
CreateAmmoBox(new Vector3(-18f, 0.3f, 4f), 14);
CreateAmmoBox(new Vector3(18f, 0.3f, -4f), 21);
CreateAmmoBox(new Vector3(-12f, 0.3f, 16f), 14);
// Физические предметы для бросков (отвлечение мутантов)
CreateThrowableProp(new Vector3(-2f, 0.5f, -2f), "Distraction_Flask_1");
CreateThrowableProp(new Vector3(2f, 0.5f, -1f), "Distraction_Flask_2");
CreateThrowableProp(new Vector3(-16f, 0.5f, 2f), "Distraction_Flask_3");
CreateThrowableProp(new Vector3(16f, 0.5f, 2f), "Distraction_Flask_4");
// 6. Противники и Босс Уровня 2
GameObject enemiesContainer = new GameObject("Enemies_Level2");
if (mutantPrefab != null)
{
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(0f, 0.1f, 3f), "Mutant_Central_Hub");
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(-17f, 0.1f, 0f), "Mutant_CryoVault");
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(17f, 0.1f, 0f), "Mutant_BioCore");
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(-11f, 0.1f, 15f), "Mutant_Archives");
// Уникальный Босс 2-го уровня в центре расширенной реакторной арены
GameObject bossObj = SpawnTitanBoss(mutantPrefab, enemiesContainer.transform, new Vector3(0f, 0.15f, 28f), "Titan_Boss_Alpha");
BuildBossArenaLockdown(level2Root.transform, new Vector3(0f, 0f, 13f), echoMat, bossObj);
// Боеприпасы и тактические колбы на арене босса
CreateAmmoBox(new Vector3(-9.5f, 0.3f, 18f), 21);
CreateAmmoBox(new Vector3(9.5f, 0.3f, 18f), 21);
CreateAmmoBox(new Vector3(-9.5f, 0.3f, 32f), 21);
CreateAmmoBox(new Vector3(9.5f, 0.3f, 32f), 14);
CreateThrowableProp(new Vector3(-6.5f, 0.5f, 25f), "Arena_Distraction_Flask_1");
CreateThrowableProp(new Vector3(6.5f, 0.5f, 25f), "Arena_Distraction_Flask_2");
CreateThrowableProp(new Vector3(0f, 0.5f, 34f), "Arena_Distraction_Flask_3");
}
// 7. Настройка UI (Финальная победа, полоса здоровья босса, оверлей ослепления)
SetupFinalVictoryUI();
// 8. Настраиваем и запекаем NavMesh для Уровня 2
BakeLevel2NavMesh();
// 9. Регистрация сцены в Build Settings
RegisterSceneInBuildSettings(level2Path);
// 10. Сохранение сцены
EditorSceneManager.MarkSceneDirty(scene);
EditorSceneManager.SaveScene(scene);
Debug.Log("<color=green>[Level 2] Сборка 2-го уровня полностью завершена!</color>");
}
private static void BuildRoom(Transform parent, string name, Vector3 center, Vector3 floorSize, float height, Material mat,
bool hasNorthDoor, bool hasSouthDoor, bool hasWestDoor, bool hasEastDoor)
{
GameObject room = new GameObject(name);
room.transform.SetParent(parent);
room.transform.position = center;
// Пол
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
floor.name = "Floor";
floor.transform.SetParent(room.transform);
floor.transform.localPosition = Vector3.zero;
floor.transform.localScale = floorSize;
if (mat != null) floor.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Потолок
GameObject roof = GameObject.CreatePrimitive(PrimitiveType.Cube);
roof.name = "Ceiling";
roof.transform.SetParent(room.transform);
roof.transform.localPosition = new Vector3(0f, height, 0f);
roof.transform.localScale = floorSize;
if (mat != null) roof.GetComponent<MeshRenderer>().sharedMaterial = mat;
float halfX = floorSize.x * 0.5f;
float halfZ = floorSize.z * 0.5f;
float wallThick = 0.35f;
// Северная стена
BuildWallWithDoor(room.transform, new Vector3(0f, height * 0.5f, halfZ), floorSize.x, height, wallThick, hasNorthDoor, mat, false);
// Южная стена
BuildWallWithDoor(room.transform, new Vector3(0f, height * 0.5f, -halfZ), floorSize.x, height, wallThick, hasSouthDoor, mat, false);
// Западная стена
BuildWallWithDoor(room.transform, new Vector3(-halfX, height * 0.5f, 0f), floorSize.z, height, wallThick, hasWestDoor, mat, true);
// Восточная стена
BuildWallWithDoor(room.transform, new Vector3(halfX, height * 0.5f, 0f), floorSize.z, height, wallThick, hasEastDoor, mat, true);
}
private static void BuildWallWithDoor(Transform parent, Vector3 pos, float length, float height, float thick, bool hasDoor, Material mat, bool rotate90)
{
Quaternion rot = rotate90 ? Quaternion.Euler(0f, 90f, 0f) : Quaternion.identity;
if (!hasDoor)
{
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.name = "SolidWall";
wall.transform.SetParent(parent);
wall.transform.localPosition = pos;
wall.transform.localRotation = rot;
wall.transform.localScale = new Vector3(length, height, thick);
if (mat != null) wall.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
else
{
float doorWidth = 3.6f;
float sideWidth = Mathf.Max((length - doorWidth) * 0.5f, 0.5f);
// Левый сегмент
GameObject leftPart = GameObject.CreatePrimitive(PrimitiveType.Cube);
leftPart.name = "Wall_PartL";
leftPart.transform.SetParent(parent);
Vector3 offsetL = rotate90 ? new Vector3(0f, 0f, -(doorWidth + sideWidth) * 0.5f) : new Vector3(-(doorWidth + sideWidth) * 0.5f, 0f, 0f);
leftPart.transform.localPosition = pos + offsetL;
leftPart.transform.localRotation = rot;
leftPart.transform.localScale = new Vector3(sideWidth, height, thick);
if (mat != null) leftPart.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Правый сегмент
GameObject rightPart = GameObject.CreatePrimitive(PrimitiveType.Cube);
rightPart.name = "Wall_PartR";
rightPart.transform.SetParent(parent);
Vector3 offsetR = rotate90 ? new Vector3(0f, 0f, (doorWidth + sideWidth) * 0.5f) : new Vector3((doorWidth + sideWidth) * 0.5f, 0f, 0f);
rightPart.transform.localPosition = pos + offsetR;
rightPart.transform.localRotation = rot;
rightPart.transform.localScale = new Vector3(sideWidth, height, thick);
if (mat != null) rightPart.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Верхняя перемычка
GameObject topLintel = GameObject.CreatePrimitive(PrimitiveType.Cube);
topLintel.name = "Wall_TopLintel";
topLintel.transform.SetParent(parent);
Vector3 offsetTop = new Vector3(0f, height * 0.38f, 0f);
topLintel.transform.localPosition = pos + offsetTop;
topLintel.transform.localRotation = rot;
topLintel.transform.localScale = new Vector3(doorWidth, height * 0.25f, thick);
if (mat != null) topLintel.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
}
private static void BuildCorridor(Transform parent, string name, Vector3 pos, Vector3 floorSize, float height, Material mat, bool isNS)
{
GameObject corr = new GameObject(name);
corr.transform.SetParent(parent);
corr.transform.position = pos;
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
floor.name = "Floor";
floor.transform.SetParent(corr.transform);
floor.transform.localPosition = Vector3.zero;
floor.transform.localScale = floorSize;
if (mat != null) floor.GetComponent<MeshRenderer>().sharedMaterial = mat;
GameObject roof = GameObject.CreatePrimitive(PrimitiveType.Cube);
roof.name = "Ceiling";
roof.transform.SetParent(corr.transform);
roof.transform.localPosition = new Vector3(0f, height, 0f);
roof.transform.localScale = floorSize;
if (mat != null) roof.GetComponent<MeshRenderer>().sharedMaterial = mat;
float thick = 0.35f;
if (isNS)
{
// Стены слева и справа
float halfW = floorSize.x * 0.5f;
GameObject wL = GameObject.CreatePrimitive(PrimitiveType.Cube);
wL.name = "Wall_L";
wL.transform.SetParent(corr.transform);
wL.transform.localPosition = new Vector3(-halfW, height * 0.5f, 0f);
wL.transform.localScale = new Vector3(thick, height, floorSize.z);
if (mat != null) wL.GetComponent<MeshRenderer>().sharedMaterial = mat;
GameObject wR = GameObject.CreatePrimitive(PrimitiveType.Cube);
wR.name = "Wall_R";
wR.transform.SetParent(corr.transform);
wR.transform.localPosition = new Vector3(halfW, height * 0.5f, 0f);
wR.transform.localScale = new Vector3(thick, height, floorSize.z);
if (mat != null) wR.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
else
{
// Стены спереди и сзади
float halfL = floorSize.z * 0.5f;
GameObject wN = GameObject.CreatePrimitive(PrimitiveType.Cube);
wN.name = "Wall_N";
wN.transform.SetParent(corr.transform);
wN.transform.localPosition = new Vector3(0f, height * 0.5f, halfL);
wN.transform.localScale = new Vector3(floorSize.x, height, thick);
if (mat != null) wN.GetComponent<MeshRenderer>().sharedMaterial = mat;
GameObject wS = GameObject.CreatePrimitive(PrimitiveType.Cube);
wS.name = "Wall_S";
wS.transform.SetParent(corr.transform);
wS.transform.localPosition = new Vector3(0f, height * 0.5f, -halfL);
wS.transform.localScale = new Vector3(floorSize.x, height, thick);
if (mat != null) wS.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
}
private static void BuildPillar(Transform parent, Vector3 pos, Vector3 size, Material mat)
{
GameObject p = GameObject.CreatePrimitive(PrimitiveType.Cube);
p.name = "Structural_Pillar";
p.transform.SetParent(parent);
p.transform.localPosition = pos;
p.transform.localScale = size;
if (mat != null) p.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
private static void BuildElevatorShaft(Transform parent, Vector3 pos, Material mat)
{
GameObject shaft = new GameObject("Arrival_Elevator_Shaft");
shaft.transform.SetParent(parent);
shaft.transform.position = pos;
Vector3[] corners = new Vector3[] {
new Vector3(-2.2f, 1.9f, -2.2f),
new Vector3(2.2f, 1.9f, -2.2f),
new Vector3(-2.2f, 1.9f, 2.2f),
new Vector3(2.2f, 1.9f, 2.2f)
};
for (int i = 0; i < corners.Length; i++)
{
GameObject col = GameObject.CreatePrimitive(PrimitiveType.Cube);
col.name = $"Shaft_Pillar_{i}";
col.transform.SetParent(shaft.transform);
col.transform.localPosition = corners[i];
col.transform.localScale = new Vector3(0.25f, 3.8f, 0.25f);
if (mat != null) col.GetComponent<MeshRenderer>().sharedMaterial = mat;
}
}
private static void BuildFinalEscapeAirlock(Transform parent, Vector3 pos, Material mat)
{
GameObject airlock = new GameObject("Final_Escape_Airlock_Zone");
airlock.transform.SetParent(parent);
airlock.transform.position = pos;
// Массивная стальная рама шлюза
GameObject hatch = GameObject.CreatePrimitive(PrimitiveType.Cube);
hatch.name = "Airlock_BlastDoor";
hatch.transform.SetParent(airlock.transform);
hatch.transform.localPosition = new Vector3(0f, 2.1f, 0f);
hatch.transform.localScale = new Vector3(5f, 4.2f, 0.8f);
if (mat != null) hatch.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Консоль управления шлюзом (терминал)
GameObject console = GameObject.CreatePrimitive(PrimitiveType.Cube);
console.name = "FinalExit_Terminal_Console";
console.transform.SetParent(airlock.transform);
console.transform.localPosition = new Vector3(0f, 0.6f, -2.5f);
console.transform.localScale = new Vector3(0.9f, 1.2f, 0.5f);
if (mat != null) console.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Экран терминала
GameObject screen = GameObject.CreatePrimitive(PrimitiveType.Cube);
screen.name = "Terminal_Screen";
screen.transform.SetParent(console.transform);
screen.transform.localPosition = new Vector3(0f, 0.35f, 0.05f);
screen.transform.localRotation = Quaternion.Euler(25f, 0f, 0f);
screen.transform.localScale = new Vector3(0.7f, 0.4f, 0.15f);
if (mat != null) screen.GetComponent<MeshRenderer>().sharedMaterial = mat;
// Световой маяк
GameObject lightObj = new GameObject("Airlock_StatusLight");
lightObj.transform.SetParent(console.transform);
lightObj.transform.localPosition = new Vector3(0f, 0.75f, 0f);
Light sl = lightObj.AddComponent<Light>();
sl.type = LightType.Point;
sl.color = new Color(1f, 0.35f, 0.1f);
sl.intensity = 2.5f;
sl.range = 6f;
// Скрипт финала
var terminalComp = console.AddComponent<FinalExitTerminal>();
terminalComp.statusLight = sl;
terminalComp.screenRenderer = screen.GetComponent<MeshRenderer>();
terminalComp.interactionDistance = 3.5f;
}
private static void BuildBeacon(Transform parent, Vector3 pos, Color color, float intensity, float range)
{
GameObject beacon = new GameObject("Emergency_Beacon");
beacon.transform.SetParent(parent);
beacon.transform.position = pos;
Light l = beacon.AddComponent<Light>();
l.type = LightType.Point;
l.color = color;
l.intensity = intensity;
l.range = range;
}
private static void CreateAmmoBox(Vector3 pos, int count)
{
GameObject box = GameObject.CreatePrimitive(PrimitiveType.Cube);
box.name = $"AmmoBox_({count})";
box.transform.position = pos;
box.transform.localScale = new Vector3(0.6f, 0.35f, 0.45f);
var pickup = box.AddComponent<AmmoPickup>();
pickup.ammoAmount = count;
pickup.pickupOnTouch = true;
}
private static void CreateThrowableProp(Vector3 pos, string name)
{
GameObject prop = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
prop.name = name;
prop.transform.position = pos;
prop.transform.localScale = new Vector3(0.3f, 0.35f, 0.3f);
var rb = prop.AddComponent<Rigidbody>();
rb.mass = 2.5f;
prop.layer = LayerMask.NameToLayer("Default");
}
private static void SpawnMutant(GameObject prefab, Transform parent, Vector3 pos, string name)
{
GameObject mutant = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
if (mutant == null) mutant = Object.Instantiate(prefab);
mutant.name = name;
mutant.transform.SetParent(parent);
mutant.transform.position = pos;
}
private static GameObject SpawnTitanBoss(GameObject prefab, Transform parent, Vector3 pos, string name)
{
GameObject boss = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
if (boss == null) boss = Object.Instantiate(prefab);
boss.name = name;
boss.transform.SetParent(parent);
boss.transform.position = pos;
boss.transform.localScale = new Vector3(1.75f, 1.75f, 1.75f); // Массивный титан
// Удаляем обычный BlindMutantAI, чтобы боссом управлял специализированный TitanBossAI
var standardAI = boss.GetComponent<BlindMutantAI>();
if (standardAI != null) Object.DestroyImmediate(standardAI);
// Настраиваем здоровье босса на 200 HP
var health = boss.GetComponent<MutantHealth>();
if (health == null) health = boss.AddComponent<MutantHealth>();
health.maxHealth = 200f;
// Добавляем специализированный интеллект босса
var titanAI = boss.GetComponent<TitanBossAI>();
if (titanAI == null) titanAI = boss.AddComponent<TitanBossAI>();
titanAI.walkSpeed = 2.8f;
titanAI.runSpeed = 4.6f;
titanAI.meleeDamage = 18f;
titanAI.meleeCooldown = 2.2f;
titanAI.spitDamage = 14f;
titanAI.spitCooldown = 8.5f;
// Настраиваем NavMeshAgent под габариты босса
var agent = boss.GetComponent<UnityEngine.AI.NavMeshAgent>();
if (agent != null)
{
agent.speed = 2.8f;
agent.radius = 0.85f;
agent.height = 3.0f;
agent.stoppingDistance = 2.4f;
agent.acceleration = 10f;
}
// Подсветка биолюминесценции босса (багровое свечение)
GameObject bossGlow = new GameObject("Titan_Bioluminescence");
bossGlow.transform.SetParent(boss.transform);
bossGlow.transform.localPosition = new Vector3(0f, 1.8f, 0f);
Light glowLight = bossGlow.AddComponent<Light>();
glowLight.type = LightType.Point;
glowLight.color = new Color(0.95f, 0.15f, 0.2f);
glowLight.range = 7.5f;
glowLight.intensity = 2.4f;
return boss;
}
private static void BuildBossArenaLockdown(Transform parent, Vector3 entrancePos, Material echoMat, GameObject bossObj)
{
GameObject arenaRoot = new GameObject("BossArena_LockdownManager");
arenaRoot.transform.SetParent(parent);
arenaRoot.transform.position = entrancePos;
// 1. Гермоворота (Blast Door) на входе в реакторную комнату (pos = 0, 0, 13)
// Вход шириной 3.6м, высотой 4.2м
GameObject doorObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
doorObj.name = "BlastDoor_Quarantine";
doorObj.transform.SetParent(arenaRoot.transform);
doorObj.transform.position = entrancePos + new Vector3(0f, 2.1f, 0f);
doorObj.transform.localScale = new Vector3(3.8f, 4.2f, 0.45f);
if (echoMat != null) doorObj.GetComponent<MeshRenderer>().sharedMaterial = echoMat;
// Добавляем NavMeshObstacle для предотвращения прохода через закрытую дверь
var obstacle = doorObj.AddComponent<UnityEngine.AI.NavMeshObstacle>();
obstacle.carving = true;
obstacle.size = new Vector3(3.8f, 4.2f, 0.45f);
// 2. Сигнальная лампа карантина над дверью
GameObject lampObj = new GameObject("Quarantine_EmergencyLight");
lampObj.transform.SetParent(arenaRoot.transform);
lampObj.transform.position = entrancePos + new Vector3(0f, 3.8f, 0.8f);
Light alertLight = lampObj.AddComponent<Light>();
alertLight.type = LightType.Point;
alertLight.color = new Color(0.2f, 0.85f, 0.35f); // До боя дежурный зеленоватый свет
alertLight.range = 10f;
alertLight.intensity = 1.8f;
// 3. Зона триггера блокировки (сразу за дверью внутри комнаты)
GameObject triggerObj = new GameObject("Trigger_LockdownArea");
triggerObj.transform.SetParent(arenaRoot.transform);
triggerObj.transform.position = entrancePos + new Vector3(0f, 1.5f, 2.8f);
BoxCollider col = triggerObj.AddComponent<BoxCollider>();
col.isTrigger = true;
col.size = new Vector3(5.5f, 3.5f, 3.5f);
var rb = triggerObj.AddComponent<Rigidbody>();
rb.isKinematic = true;
// Навешиваем BossArenaController прямо на триггер
var arenaCtrl = triggerObj.AddComponent<BossArenaController>();
arenaCtrl.blastDoorRoot = doorObj;
arenaCtrl.blastDoorCollider = doorObj.GetComponent<Collider>();
arenaCtrl.emergencyLight = alertLight;
if (bossObj != null)
{
var titanAI = bossObj.GetComponent<TitanBossAI>();
arenaCtrl.bossAI = titanAI;
if (titanAI != null)
{
titanAI.arenaController = arenaCtrl;
}
}
}
private static void SetupFinalVictoryUI()
{
GameObject hudCanvas = GameObject.Find("HUD_Canvas");
if (hudCanvas == null) return;
// Создаем полосу здоровья босса (скрыта по умолчанию)
BossHealthBarUI.Create(hudCanvas.transform);
// Создаем оверлей акустического ослепления босса (скрыт по умолчанию)
AcousticJammerOverlay.Create(hudCanvas.transform);
// Проверяем, существует ли уже панель победы
var old = hudCanvas.transform.Find("FinalVictoryPanel");
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
// Корневая панель
GameObject root = new GameObject("FinalVictoryPanel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
root.transform.SetParent(hudCanvas.transform, false);
RectTransform rt = root.GetComponent<RectTransform>();
rt.anchorMin = Vector2.zero;
rt.anchorMax = Vector2.one;
rt.sizeDelta = Vector2.zero;
Image bg = root.GetComponent<Image>();
bg.color = new Color(0.02f, 0.02f, 0.03f, 0.95f);
// Карточка
GameObject card = new GameObject("VictoryCard", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Outline));
card.transform.SetParent(root.transform, false);
RectTransform cardRt = card.GetComponent<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(680f, 440f);
Image cardImg = card.GetComponent<Image>();
cardImg.color = new Color(0.06f, 0.06f, 0.07f, 1f);
Outline cardOutline = card.GetComponent<Outline>();
cardOutline.effectColor = new Color(0.2f, 0.9f, 0.5f, 0.8f);
cardOutline.effectDistance = new Vector2(2f, -2f);
// Заголовок
GameObject titleObj = new GameObject("Title", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
titleObj.transform.SetParent(card.transform, false);
RectTransform titleRt = titleObj.GetComponent<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, -25f);
titleRt.sizeDelta = new Vector2(620f, 45f);
Text titleTxt = titleObj.GetComponent<Text>();
titleTxt.text = "МИССИЯ ВЫПОЛНЕНА // ЭВАКУАЦИЯ УСПЕШНА!";
titleTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
titleTxt.fontSize = 22;
titleTxt.fontStyle = FontStyle.Bold;
titleTxt.alignment = TextAnchor.MiddleCenter;
titleTxt.color = new Color(0.2f, 1.0f, 0.5f, 1.0f);
// Описание
GameObject descObj = new GameObject("Description", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
descObj.transform.SetParent(card.transform, false);
RectTransform descRt = descObj.GetComponent<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, 15f);
descRt.sizeDelta = new Vector2(580f, 140f);
Text descTxt = descObj.GetComponent<Text>();
descTxt.text = "Вы успешно преодолели Сектор глубоких исследований, обошли слепых мутантов и активировали аварийный гермошлюз.\n\nКомплекс 'Synthesia' запечатан. Вы выбрались на поверхность живым!";
descTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
descTxt.fontSize = 16;
descTxt.lineSpacing = 1.3f;
descTxt.alignment = TextAnchor.MiddleCenter;
descTxt.color = new Color(0.9f, 0.9f, 0.9f, 0.95f);
// Кнопка В главное меню
GameObject btnMenuObj = new GameObject("MainMenuButton", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
btnMenuObj.transform.SetParent(card.transform, false);
RectTransform btnMenuRt = btnMenuObj.GetComponent<RectTransform>();
btnMenuRt.anchorMin = new Vector2(0.5f, 0f);
btnMenuRt.anchorMax = new Vector2(0.5f, 0f);
btnMenuRt.pivot = new Vector2(0.5f, 0f);
btnMenuRt.anchoredPosition = new Vector2(-150f, 30f);
btnMenuRt.sizeDelta = new Vector2(260f, 48f);
Image btnMenuImg = btnMenuObj.GetComponent<Image>();
btnMenuImg.color = new Color(0.18f, 0.18f, 0.20f, 1f);
Button btnMenu = btnMenuObj.GetComponent<Button>();
GameObject menuTxtObj = new GameObject("Text", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
menuTxtObj.transform.SetParent(btnMenuObj.transform, false);
RectTransform menuTxtRt = menuTxtObj.GetComponent<RectTransform>();
menuTxtRt.anchorMin = Vector2.zero;
menuTxtRt.anchorMax = Vector2.one;
menuTxtRt.sizeDelta = Vector2.zero;
Text mTxt = menuTxtObj.GetComponent<Text>();
mTxt.text = "В ГЛАВНОЕ МЕНЮ";
mTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
mTxt.fontSize = 15;
mTxt.fontStyle = FontStyle.Bold;
mTxt.alignment = TextAnchor.MiddleCenter;
mTxt.color = Color.white;
// Кнопка Начать заново
GameObject btnRestartObj = new GameObject("RestartButton", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
btnRestartObj.transform.SetParent(card.transform, false);
RectTransform btnRestartRt = btnRestartObj.GetComponent<RectTransform>();
btnRestartRt.anchorMin = new Vector2(0.5f, 0f);
btnRestartRt.anchorMax = new Vector2(0.5f, 0f);
btnRestartRt.pivot = new Vector2(0.5f, 0f);
btnRestartRt.anchoredPosition = new Vector2(150f, 30f);
btnRestartRt.sizeDelta = new Vector2(260f, 48f);
Image btnRestartImg = btnRestartObj.GetComponent<Image>();
btnRestartImg.color = new Color(0.2f, 0.85f, 0.45f, 1f);
Button btnRestart = btnRestartObj.GetComponent<Button>();
GameObject resTxtObj = new GameObject("Text", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
resTxtObj.transform.SetParent(btnRestartObj.transform, false);
RectTransform resTxtRt = resTxtObj.GetComponent<RectTransform>();
resTxtRt.anchorMin = Vector2.zero;
resTxtRt.anchorMax = Vector2.one;
resTxtRt.sizeDelta = Vector2.zero;
Text rTxt = resTxtObj.GetComponent<Text>();
rTxt.text = "ИГРАТЬ ЗАНОВО";
rTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
rTxt.fontSize = 15;
rTxt.fontStyle = FontStyle.Bold;
rTxt.alignment = TextAnchor.MiddleCenter;
rTxt.color = new Color(0.05f, 0.05f, 0.05f, 1f);
// Компонент FinalVictoryUI
var victoryComp = hudCanvas.GetComponent<FinalVictoryUI>();
if (victoryComp == null) victoryComp = hudCanvas.AddComponent<FinalVictoryUI>();
var flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
victoryComp.GetType().GetField("victoryPanelRoot", flags)?.SetValue(victoryComp, root);
victoryComp.GetType().GetField("mainMenuButton", flags)?.SetValue(victoryComp, btnMenu);
victoryComp.GetType().GetField("restartButton", flags)?.SetValue(victoryComp, btnRestart);
UnityEditor.Events.UnityEventTools.AddPersistentListener(btnMenu.onClick, victoryComp.OnMainMenuClicked);
UnityEditor.Events.UnityEventTools.AddPersistentListener(btnRestart.onClick, victoryComp.OnRestartGameClicked);
root.SetActive(false);
}
private static void BakeLevel2NavMesh()
{
var surface = Object.FindAnyObjectByType<NavMeshSurface>();
if (surface == null)
{
GameObject sGo = new GameObject("NavMesh_Surface_Level2");
surface = sGo.AddComponent<NavMeshSurface>();
}
surface.collectObjects = CollectObjects.All;
surface.useGeometry = NavMeshCollectGeometry.PhysicsColliders;
surface.BuildNavMesh();
Debug.Log("<color=green>[Level 2] NavMesh успешно запечен для Уровня 2!</color>");
}
private static void RegisterSceneInBuildSettings(string scenePath)
{
var currentScenes = EditorBuildSettings.scenes;
foreach (var s in currentScenes)
{
if (s.path == scenePath) return; // уже добавлена
}
var newScenes = new EditorBuildSettingsScene[currentScenes.Length + 1];
for (int i = 0; i < currentScenes.Length; i++)
{
newScenes[i] = currentScenes[i];
}
newScenes[newScenes.Length - 1] = new EditorBuildSettingsScene(scenePath, true);
EditorBuildSettings.scenes = newScenes;
Debug.Log($"<color=cyan>[BuildSettings] Сцена {scenePath} зарегистрирована в Build Settings!</color>");
}
}