Kursach_StealthAction/Assets/Scripts/Editor/LaboratoryLevelGenerator.cs

934 lines
51 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.AI;
using Unity.AI.Navigation;
public static class LaboratoryLevelGenerator
{
private const float WALL_HEIGHT = 4.5f;
private const float WALL_THICKNESS = 0.35f;
// Outer Perimeter: 100m x 100m (-50 to +50 in X and Z)
private const float MIN_COORD = -50f;
private const float MAX_COORD = 50f;
[MenuItem("Tools/Generate Full Laboratory Location")]
public static void Generate()
{
Debug.Log("<color=cyan>=== GENERATING EXPANDED COMPLEX LABORATORY LOCATION (100x100m) ===</color>");
// 1. Remove previous Laboratory_Complex or old maze if present
var oldLab = GameObject.Find("Laboratory_Complex");
if (oldLab != null)
{
Undo.DestroyObjectImmediate(oldLab);
}
var oldMaze = GameObject.Find("Maze");
if (oldMaze != null)
{
oldMaze.SetActive(false);
}
// Disable or cleanup old planes
for (int i = 0; i <= 6; i++)
{
string pName = i == 0 ? "Plane" : $"Plane ({i})";
var pl = GameObject.Find(pName);
if (pl != null)
{
pl.SetActive(false);
}
}
// Load assets and materials
Material echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoCloudMat.mat");
if (echoMat == null)
{
echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoMat.mat");
}
GameObject tablePrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/table with drawers.prefab");
GameObject shelfShowcasePrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/shelf showcase.prefab");
GameObject shelf2Prefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/shelf (2).prefab");
GameObject rackPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/rack.prefab");
GameObject chairPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/chair.prefab");
GameObject pillarPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/pillar.prefab");
GameObject boxPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/Box.prefab");
GameObject beakerPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/Beaker.prefab");
GameObject flaskPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/Erlenmeyer_flask.prefab");
GameObject testTubePrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/Glass_Lab_test_tube.prefab");
GameObject instPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/analytical_lab_instruments.prefab");
GameObject tapPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/3D Laboratory Environment with Appratus/Prefabs/tap.prefab");
// Root object
GameObject labRoot = new GameObject("Laboratory_Complex");
Undo.RegisterCreatedObjectUndo(labRoot, "Create Laboratory_Complex");
GameObject floorsParent = new GameObject("Floors");
floorsParent.transform.SetParent(labRoot.transform);
GameObject wallsParent = new GameObject("Walls");
wallsParent.transform.SetParent(labRoot.transform);
GameObject roomsParent = new GameObject("Rooms_Props");
roomsParent.transform.SetParent(labRoot.transform);
GameObject lightsParent = new GameObject("Atmospheric_Lighting");
lightsParent.transform.SetParent(labRoot.transform);
GameObject pickupsParent = new GameObject("Throwable_Distractions");
pickupsParent.transform.SetParent(labRoot.transform);
// 2. Build Floor Grid (100m x 100m, from -50 to +50 in X and Z)
CreateFloorGrid(floorsParent.transform, echoMat, MIN_COORD, MAX_COORD, MIN_COORD, MAX_COORD);
// 3. Build Perimeter Exterior Walls (100m perimeter)
CreateWall(wallsParent.transform, echoMat, new Vector3(0, WALL_HEIGHT / 2f, MIN_COORD), new Vector3(100f, WALL_HEIGHT, WALL_THICKNESS), "Wall_Perimeter_South");
CreateWall(wallsParent.transform, echoMat, new Vector3(0, WALL_HEIGHT / 2f, MAX_COORD), new Vector3(100f, WALL_HEIGHT, WALL_THICKNESS), "Wall_Perimeter_North");
CreateWall(wallsParent.transform, echoMat, new Vector3(MIN_COORD, WALL_HEIGHT / 2f, 0), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 100f), "Wall_Perimeter_West");
CreateWall(wallsParent.transform, echoMat, new Vector3(MAX_COORD, WALL_HEIGHT / 2f, 0), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 100f), "Wall_Perimeter_East");
// 4. Build Major Facility Wings & Rooms:
// --- SECTOR A: Entrance & Decontamination Airlock (X: -8 to 8, Z: -50 to -38) ---
BuildRoomWalls(wallsParent.transform, echoMat, -8f, 8f, -50f, -38f,
southDoor: null,
northDoor: new Vector2(-2.2f, 2.2f), // Main airlock door to South Concourse
westDoor: null,
eastDoor: null,
roomName: "Decontamination_Airlock");
// --- SECTOR B: Cryogenics & Specimen Archive (Southwest, X: -48 to -26, Z: -48 to -26) ---
BuildRoomWalls(wallsParent.transform, echoMat, -48f, -26f, -48f, -26f,
southDoor: null,
northDoor: new Vector2(-39f, -35f), // to West Mid Lab
westDoor: null,
eastDoor: new Vector2(-39f, -35f), // to South Concourse
roomName: "Cryogenics_Vault");
// --- SECTOR C: High-Security Armory & CCTV (Southeast, X: 26 to 48, Z: -48 to -26) ---
BuildRoomWalls(wallsParent.transform, echoMat, 26f, 48f, -48f, -26f,
southDoor: null,
northDoor: new Vector2(35f, 39f), // to East Mid Medical
westDoor: new Vector2(-39f, -35f), // to South Concourse
eastDoor: null,
roomName: "Security_Armory");
// --- SECTOR D: Biochemistry Research Wing (West Mid, X: -48 to -24, Z: -22 to 2) ---
BuildRoomWalls(wallsParent.transform, echoMat, -48f, -24f, -22f, 2f,
southDoor: new Vector2(-39f, -35f), // to Cryo corridor
northDoor: new Vector2(-39f, -35f), // to Quarantine Wing
westDoor: null,
eastDoor: new Vector2(-12f, -8f), // to Maintenance Pipe Slalom
roomName: "Biochem_Research");
// --- SECTOR E: Surgical Theatre & ICU (East Mid, X: 24 to 48, Z: -22 to 2) ---
BuildRoomWalls(wallsParent.transform, echoMat, 24f, 48f, -22f, 2f,
southDoor: new Vector2(35f, 39f), // to Armory corridor
northDoor: new Vector2(35f, 39f), // to Genetics Lab
westDoor: new Vector2(-12f, -8f), // to East Slalom corridor
eastDoor: null,
roomName: "Surgical_ICU");
// --- SECTOR F: Quarantine Cellblock & Solitary Isolation (TIGHT / LABYRINTH, X: -48 to -22, Z: 6 to 26) ---
// Narrow cramped corridors with individual solitary confinement cells
BuildRoomWalls(wallsParent.transform, echoMat, -48f, -22f, 6f, 26f,
southDoor: new Vector2(-39f, -35f), // from Biochem
northDoor: new Vector2(-39f, -35f), // to Chemical Silo
westDoor: null,
eastDoor: new Vector2(14f, 18f), // to Grand Atrium West Entrance
roomName: "Quarantine_Wing");
// --- SECTOR G: Genetics & Cybernetics Laboratory (East North, X: 22 to 48, Z: 6 to 26) ---
BuildRoomWalls(wallsParent.transform, echoMat, 22f, 48f, 6f, 26f,
southDoor: new Vector2(35f, 39f), // from Surgical
northDoor: new Vector2(35f, 39f), // to Substation
westDoor: new Vector2(14f, 18f), // to Grand Atrium East Entrance
eastDoor: null,
roomName: "Genetics_Cybernetics");
// --- SECTOR H: Chemical Silos & Toxic Waste (Northwest, X: -48 to -26, Z: 28 to 48) ---
BuildRoomWalls(wallsParent.transform, echoMat, -48f, -26f, 28f, 48f,
southDoor: new Vector2(-39f, -35f), // from Quarantine
northDoor: new Vector2(-39f, -35f), // to North Maintenance Conduit
westDoor: null,
eastDoor: new Vector2(36f, 40f), // to Reactor Hall
roomName: "Chemical_Silos");
// --- SECTOR I: Auxiliary Power Substation & Battery Banks (Northeast, X: 26 to 48, Z: 28 to 48) ---
BuildRoomWalls(wallsParent.transform, echoMat, 26f, 48f, 28f, 48f,
southDoor: new Vector2(35f, 39f), // from Genetics
northDoor: new Vector2(35f, 39f), // to North Maintenance Conduit
westDoor: new Vector2(36f, 40f), // to Reactor Hall
eastDoor: null,
roomName: "Power_Substation");
// --- SECTOR J: GRAND CENTRAL ATRIUM & BIO-DOME (VERY SPACIOUS! X: -18 to 18, Z: -6 to 24) ---
// Huge 36m x 30m open amphitheatre / bio-dome
BuildRoomWalls(wallsParent.transform, echoMat, -18f, 18f, -6f, 24f,
southDoor: new Vector2(-4f, 4f), // Grand 8m South Portal to Concourse
northDoor: new Vector2(-4f, 4f), // Grand 8m North Portal to Reactor Core
westDoor: new Vector2(7f, 11f), // West Portal to Maintenance/Quarantine
eastDoor: new Vector2(7f, 11f), // East Portal to Medical/Genetics
roomName: "Grand_Atrium");
// --- SECTOR K: SUB-REACTOR & TURBINE POWER PLANT (VERY SPACIOUS! X: -22 to 22, Z: 28 to 48) ---
// Huge 44m x 20m industrial turbine hall
BuildRoomWalls(wallsParent.transform, echoMat, -22f, 22f, 28f, 48f,
southDoor: new Vector2(-4f, 4f), // Direct entrance from Atrium
northDoor: new Vector2(-2.5f, 2.5f),// Service door to North Maintenance Conduit
westDoor: new Vector2(36f, 40f), // to Chemical Silos
eastDoor: new Vector2(36f, 40f), // to Power Substation
roomName: "Reactor_Turbine_Hall");
// 5. Build Complex Corridors, Tight Crawlways, and Slalom Alley
BuildComplexCorridors(wallsParent.transform, echoMat);
// 6. Build Quarantine Cellblock Interior (Cramped cells with narrow 1.3m doors)
BuildQuarantineCells(wallsParent.transform, echoMat);
// 7. Populate Rooms & Halls with Furniture, Machinery, Obstacles and Distractions
PopulateAirlock(roomsParent.transform, pillarPrefab, rackPrefab, chairPrefab);
PopulateGrandAtrium(roomsParent.transform, pillarPrefab, tablePrefab, instPrefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateReactorHall(roomsParent.transform, pillarPrefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateSouthConcourse(roomsParent.transform, pillarPrefab, chairPrefab, boxPrefab, pickupsParent.transform);
PopulateBiochem(roomsParent.transform, tablePrefab, shelfShowcasePrefab, shelf2Prefab, tapPrefab, instPrefab, beakerPrefab, flaskPrefab, testTubePrefab, pickupsParent.transform);
PopulateSurgical(roomsParent.transform, tablePrefab, shelfShowcasePrefab, chairPrefab, beakerPrefab, pickupsParent.transform);
PopulateQuarantineProps(roomsParent.transform, boxPrefab, rackPrefab, chairPrefab, beakerPrefab, pickupsParent.transform);
PopulateGenetics(roomsParent.transform, tablePrefab, shelfShowcasePrefab, instPrefab, chairPrefab, beakerPrefab, pickupsParent.transform);
PopulateCryo(roomsParent.transform, rackPrefab, shelf2Prefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateArmory(roomsParent.transform, rackPrefab, tablePrefab, instPrefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateChemicalSilos(roomsParent.transform, pillarPrefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateSubstation(roomsParent.transform, pillarPrefab, boxPrefab, beakerPrefab, pickupsParent.transform);
PopulateSlalomAlleyProps(roomsParent.transform, boxPrefab, rackPrefab);
// 8. Atmospheric Lighting setup across all sectors
CreateLighting(lightsParent.transform);
// 9. Reconfigure Player Position in Starting Airlock
GameObject player = GameObject.Find("Player");
if (player == null) player = GameObject.FindWithTag("Player");
if (player == null) player = Object.FindAnyObjectByType<PlayerHealth>()?.gameObject;
if (player == null) player = GameObject.Find("Capsule");
if (player != null)
{
var cc = player.GetComponent<CharacterController>();
if (cc != null) cc.enabled = false;
player.transform.position = new Vector3(0f, 1.05f, -44f);
player.transform.rotation = Quaternion.Euler(0f, 0f, 0f);
if (cc != null) cc.enabled = true;
Debug.Log("<color=green>Positioned Player in Starting Airlock (0, 1.05, -44).</color>");
var cam = Camera.main;
if (cam != null)
{
cam.transform.position = new Vector3(0f, 2.5f, -48f);
cam.transform.rotation = Quaternion.Euler(12f, 0f, 0f);
}
}
// 10. Reconfigure Enemy (BlindMutantAI) & Comprehensive 32+ Point Patrol Route
GameObject enemy = GameObject.Find("enemy");
if (enemy == null) enemy = Object.FindAnyObjectByType<BlindMutantAI>()?.gameObject;
SetupPatrolWaypoints(labRoot.transform, enemy);
// Apply EchoCloudMat to all generated props and objects under labRoot
if (echoMat != null)
{
foreach (var r in labRoot.GetComponentsInChildren<Renderer>(true))
{
var mats = new Material[r.sharedMaterials.Length];
for (int i = 0; i < mats.Length; i++) mats[i] = echoMat;
r.sharedMaterials = mats;
}
}
// 11. Re-bake NavMesh Surface for entire 100x100m facility
var navMeshSurface = Object.FindAnyObjectByType<NavMeshSurface>();
if (navMeshSurface != null)
{
navMeshSurface.transform.position = Vector3.zero;
navMeshSurface.collectObjects = CollectObjects.All;
navMeshSurface.BuildNavMesh();
Debug.Log("<color=green>NavMesh successfully baked for the 100x100m complex facility!</color>");
}
// 12. Populate All Sectors and Rooms with Mutants (Roamers, Heavy Swarms, and Room Guards)
MutantPopulationSetup.Populate();
// Mark scene dirty
EditorUtility.SetDirty(labRoot);
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene());
Debug.Log("<color=green>=== EXPANDED LABORATORY COMPLEX GENERATION COMPLETE! ===</color>");
}
private static void CreateFloorGrid(Transform parent, Material mat, float minX, float maxX, float minZ, float maxZ)
{
// 16 large floor slabs (4x4 grid) for crisp rendering and solid physics
int divisions = 4;
float stepX = (maxX - minX) / divisions;
float stepZ = (maxZ - minZ) / divisions;
for (int ix = 0; ix < divisions; ix++)
{
for (int iz = 0; iz < divisions; iz++)
{
float x0 = minX + ix * stepX;
float x1 = x0 + stepX;
float z0 = minZ + iz * stepZ;
float z1 = z0 + stepZ;
var floorGo = GameObject.CreatePrimitive(PrimitiveType.Cube);
floorGo.name = $"Floor_Chunk_{ix}_{iz}";
floorGo.transform.SetParent(parent);
floorGo.transform.position = new Vector3((x0 + x1) / 2f, -0.25f, (z0 + z1) / 2f);
floorGo.transform.localScale = new Vector3(stepX, 0.5f, stepZ);
if (mat != null) floorGo.GetComponent<Renderer>().sharedMaterial = mat;
}
}
}
private static void BuildRoomWalls(Transform parent, Material mat, float minX, float maxX, float minZ, float maxZ,
Vector2? southDoor, Vector2? northDoor, Vector2? westDoor, Vector2? eastDoor, string roomName)
{
GameObject roomWalls = new GameObject($"Walls_{roomName}");
roomWalls.transform.SetParent(parent);
// South Wall (along X at Z = minZ)
CreateWallWithDoor(roomWalls.transform, mat, minX, maxX, minZ, true, southDoor, $"{roomName}_South");
// North Wall (along X at Z = maxZ)
CreateWallWithDoor(roomWalls.transform, mat, minX, maxX, maxZ, true, northDoor, $"{roomName}_North");
// West Wall (along Z at X = minX)
CreateWallWithDoor(roomWalls.transform, mat, minZ, maxZ, minX, false, westDoor, $"{roomName}_West");
// East Wall (along Z at X = maxX)
CreateWallWithDoor(roomWalls.transform, mat, minZ, maxZ, maxX, false, eastDoor, $"{roomName}_East");
}
private static void CreateWallWithDoor(Transform parent, Material mat, float startCoord, float endCoord, float fixedCoord,
bool isHorizontal, Vector2? doorRange, string wallName)
{
if (startCoord > endCoord)
{
float t = startCoord; startCoord = endCoord; endCoord = t;
}
if (!doorRange.HasValue)
{
float length = endCoord - startCoord;
float mid = (startCoord + endCoord) / 2f;
Vector3 pos = isHorizontal ? new Vector3(mid, WALL_HEIGHT / 2f, fixedCoord) : new Vector3(fixedCoord, WALL_HEIGHT / 2f, mid);
Vector3 scale = isHorizontal ? new Vector3(length, WALL_HEIGHT, WALL_THICKNESS) : new Vector3(WALL_THICKNESS, WALL_HEIGHT, length);
CreateWall(parent, mat, pos, scale, wallName);
return;
}
float dMin = Mathf.Clamp(doorRange.Value.x, startCoord, endCoord);
float dMax = Mathf.Clamp(doorRange.Value.y, startCoord, endCoord);
// Left / Start section
if (dMin > startCoord + 0.2f)
{
float len1 = dMin - startCoord;
float mid1 = (startCoord + dMin) / 2f;
Vector3 pos1 = isHorizontal ? new Vector3(mid1, WALL_HEIGHT / 2f, fixedCoord) : new Vector3(fixedCoord, WALL_HEIGHT / 2f, mid1);
Vector3 scale1 = isHorizontal ? new Vector3(len1, WALL_HEIGHT, WALL_THICKNESS) : new Vector3(WALL_THICKNESS, WALL_HEIGHT, len1);
CreateWall(parent, mat, pos1, scale1, $"{wallName}_PartA");
}
// Right / End section
if (endCoord > dMax + 0.2f)
{
float len2 = endCoord - dMax;
float mid2 = (dMax + endCoord) / 2f;
Vector3 pos2 = isHorizontal ? new Vector3(mid2, WALL_HEIGHT / 2f, fixedCoord) : new Vector3(fixedCoord, WALL_HEIGHT / 2f, mid2);
Vector3 scale2 = isHorizontal ? new Vector3(len2, WALL_HEIGHT, WALL_THICKNESS) : new Vector3(WALL_THICKNESS, WALL_HEIGHT, len2);
CreateWall(parent, mat, pos2, scale2, $"{wallName}_PartB");
}
// Over-door Lintel (bulkhead arch)
float doorLen = dMax - dMin;
float doorMid = (dMin + dMax) / 2f;
float lintelHeight = WALL_HEIGHT - 2.6f;
float lintelPosY = 2.6f + (lintelHeight / 2f);
Vector3 lintelPos = isHorizontal ? new Vector3(doorMid, lintelPosY, fixedCoord) : new Vector3(fixedCoord, lintelPosY, doorMid);
Vector3 lintelScale = isHorizontal ? new Vector3(doorLen, lintelHeight, WALL_THICKNESS) : new Vector3(WALL_THICKNESS, lintelHeight, doorLen);
CreateWall(parent, mat, lintelPos, lintelScale, $"{wallName}_Lintel");
}
private static GameObject CreateWall(Transform parent, Material mat, Vector3 pos, Vector3 scale, string name)
{
var wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.name = name;
wall.transform.SetParent(parent);
wall.transform.position = pos;
wall.transform.localScale = scale;
if (mat != null) wall.GetComponent<Renderer>().sharedMaterial = mat;
return wall;
}
private static void BuildComplexCorridors(Transform parent, Material mat)
{
GameObject corrGo = new GameObject("Corridors_Complex_Layout");
corrGo.transform.SetParent(parent);
// 1. GRAND SOUTH CONCOURSE (Z: -36 to -24, X: -26 to 26)
// High, wide passage with central architectural dividers
CreateWall(corrGo.transform, mat, new Vector3(-12f, WALL_HEIGHT / 2f, -30f), new Vector3(2.5f, WALL_HEIGHT, 4f), "Concourse_Pillar_West");
CreateWall(corrGo.transform, mat, new Vector3(12f, WALL_HEIGHT / 2f, -30f), new Vector3(2.5f, WALL_HEIGHT, 4f), "Concourse_Pillar_East");
// 2. WEST TIGHT MAINTENANCE SLALOM (CRAMPED / DIFFICULT CHOKEPOINT)
// Flanking the Grand Atrium between X: -24 and -18, Z: -22 to 24.
// Narrow 1.6m wide corridor with alternating zigzag baffles:
CreateWall(corrGo.transform, mat, new Vector3(-20f, WALL_HEIGHT / 2f, -18f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 8f), "Slalom_W_Wall1");
CreateWall(corrGo.transform, mat, new Vector3(-22.5f, WALL_HEIGHT / 2f, -14f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_W_Baffle1"); // forces jog east
CreateWall(corrGo.transform, mat, new Vector3(-19.5f, WALL_HEIGHT / 2f, -8f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_W_Baffle2"); // forces jog west
CreateWall(corrGo.transform, mat, new Vector3(-22.5f, WALL_HEIGHT / 2f, 0f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_W_Baffle3");
CreateWall(corrGo.transform, mat, new Vector3(-19.5f, WALL_HEIGHT / 2f, 8f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_W_Baffle4");
CreateWall(corrGo.transform, mat, new Vector3(-22.5f, WALL_HEIGHT / 2f, 16f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_W_Baffle5");
// 3. EAST TIGHT CABLE-TRUNK SLALOM (CRAMPED / DIFFICULT CHOKEPOINT)
// Flanking the Grand Atrium between X: 18 and 24, Z: -22 to 24.
CreateWall(corrGo.transform, mat, new Vector3(20f, WALL_HEIGHT / 2f, -18f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 8f), "Slalom_E_Wall1");
CreateWall(corrGo.transform, mat, new Vector3(22.5f, WALL_HEIGHT / 2f, -14f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_E_Baffle1");
CreateWall(corrGo.transform, mat, new Vector3(19.5f, WALL_HEIGHT / 2f, -8f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_E_Baffle2");
CreateWall(corrGo.transform, mat, new Vector3(22.5f, WALL_HEIGHT / 2f, 0f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_E_Baffle3");
CreateWall(corrGo.transform, mat, new Vector3(19.5f, WALL_HEIGHT / 2f, 8f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_E_Baffle4");
CreateWall(corrGo.transform, mat, new Vector3(22.5f, WALL_HEIGHT / 2f, 16f), new Vector3(3.2f, WALL_HEIGHT, WALL_THICKNESS), "Slalom_E_Baffle5");
// 4. NORTH MAINTENANCE BYPASS CONDUIT (LONG NARROW RUNWAY, Z: 46 to 49)
// Spans from X: -48 to +48 along the north perimeter, letting players bypass the loud reactor
CreateWallWithDoor(corrGo.transform, mat, -48f, -24f, 46f, true, new Vector2(-39f, -35f), "NorthBypass_WestGuide");
CreateWallWithDoor(corrGo.transform, mat, -20f, 20f, 46f, true, new Vector2(-2.5f, 2.5f), "NorthBypass_MidGuide");
CreateWallWithDoor(corrGo.transform, mat, 24f, 48f, 46f, true, new Vector2(35f, 39f), "NorthBypass_EastGuide");
// Inter-conduit chokepoint baffles
CreateWall(corrGo.transform, mat, new Vector3(-12f, WALL_HEIGHT / 2f, 47.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 2.5f), "Bypass_Baffle_W");
CreateWall(corrGo.transform, mat, new Vector3(12f, WALL_HEIGHT / 2f, 47.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 2.5f), "Bypass_Baffle_E");
}
private static void BuildQuarantineCells(Transform parent, Material mat)
{
// Quarantine Sector is located X: -48 to -22, Z: 6 to 26
// We build a central cell corridor along X = -35 (from Z = 6 to 26)
// With 6 individual high-security isolation cubicles
GameObject qGo = new GameObject("Quarantine_Cell_Partitions");
qGo.transform.SetParent(parent);
// West corridor wall (at X = -37) with doors into cells 1, 2, 3
CreateWallWithDoor(qGo.transform, mat, 6f, 12f, -37f, false, new Vector2(8f, 10f), "Cell_Door_1");
CreateWallWithDoor(qGo.transform, mat, 13f, 19f, -37f, false, new Vector2(15f, 17f), "Cell_Door_2");
CreateWallWithDoor(qGo.transform, mat, 20f, 26f, -37f, false, new Vector2(22f, 24f), "Cell_Door_3");
// Cell dividers on West side (Z = 12.5 and Z = 19.5, X from -48 to -37)
CreateWall(qGo.transform, mat, new Vector3(-42.5f, WALL_HEIGHT / 2f, 12.5f), new Vector3(11f, WALL_HEIGHT, WALL_THICKNESS), "Cell_Divider_W1");
CreateWall(qGo.transform, mat, new Vector3(-42.5f, WALL_HEIGHT / 2f, 19.5f), new Vector3(11f, WALL_HEIGHT, WALL_THICKNESS), "Cell_Divider_W2");
// East corridor wall (at X = -33) with doors into cells 4, 5, 6
CreateWallWithDoor(qGo.transform, mat, 6f, 12f, -33f, false, new Vector2(8f, 10f), "Cell_Door_4");
CreateWallWithDoor(qGo.transform, mat, 13f, 19f, -33f, false, new Vector2(15f, 17f), "Cell_Door_5");
CreateWallWithDoor(qGo.transform, mat, 20f, 26f, -33f, false, new Vector2(22f, 24f), "Cell_Door_6");
// Cell dividers on East side (Z = 12.5 and Z = 19.5, X from -33 to -22)
CreateWall(qGo.transform, mat, new Vector3(-27.5f, WALL_HEIGHT / 2f, 12.5f), new Vector3(11f, WALL_HEIGHT, WALL_THICKNESS), "Cell_Divider_E1");
CreateWall(qGo.transform, mat, new Vector3(-27.5f, WALL_HEIGHT / 2f, 19.5f), new Vector3(11f, WALL_HEIGHT, WALL_THICKNESS), "Cell_Divider_E2");
}
private static void PopulateAirlock(Transform parent, GameObject pillarPrefab, GameObject rackPrefab, GameObject chairPrefab)
{
GameObject room = new GameObject("Airlock_Props");
room.transform.SetParent(parent);
// Decon arch frames
SpawnProp(pillarPrefab, room.transform, new Vector3(-3f, 0f, -42f), Quaternion.identity);
SpawnProp(pillarPrefab, room.transform, new Vector3(3f, 0f, -42f), Quaternion.identity);
// Lockers and benches
SpawnProp(rackPrefab, room.transform, new Vector3(-6.5f, 0f, -46f), Quaternion.Euler(0f, 90f, 0f));
SpawnProp(rackPrefab, room.transform, new Vector3(-6.5f, 0f, -44f), Quaternion.Euler(0f, 90f, 0f));
SpawnProp(chairPrefab, room.transform, new Vector3(6.5f, 0f, -46f), Quaternion.Euler(0f, -90f, 0f));
SpawnProp(chairPrefab, room.transform, new Vector3(6.5f, 0f, -44f), Quaternion.Euler(0f, -90f, 0f));
}
private static void PopulateGrandAtrium(Transform parent, GameObject pillarPrefab, GameObject tablePrefab, GameObject instPrefab,
GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("GrandAtrium_Props");
room.transform.SetParent(parent);
// 8 Grand architectural colonnades ringing the open 36x30m atrium
Vector3[] pillarPos = new Vector3[]
{
new Vector3(-12f, 0f, 0f),
new Vector3(-12f, 0f, 18f),
new Vector3(12f, 0f, 0f),
new Vector3(12f, 0f, 18f),
new Vector3(-6f, 0f, -3f),
new Vector3(6f, 0f, -3f),
new Vector3(-6f, 0f, 21f),
new Vector3(6f, 0f, 21f)
};
foreach (var pos in pillarPos)
{
SpawnProp(pillarPrefab, room.transform, pos, Quaternion.identity);
}
// Massive Central Mutant Observation Specimen Vat (Radius 4.5m)
var coreCylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
coreCylinder.name = "Grand_Mutant_Cryo_Vat";
coreCylinder.transform.SetParent(room.transform);
coreCylinder.transform.position = new Vector3(0f, 2.25f, 9f);
coreCylinder.transform.localScale = new Vector3(7f, 2.25f, 7f);
// Observation Control Desks
SpawnProp(tablePrefab, room.transform, new Vector3(-5f, 0f, 4f), Quaternion.Euler(0f, 45f, 0f));
SpawnProp(instPrefab, room.transform, new Vector3(-5f, 1.07f, 4f), Quaternion.Euler(0f, 45f, 0f));
SpawnProp(tablePrefab, room.transform, new Vector3(5f, 0f, 4f), Quaternion.Euler(0f, -45f, 0f));
SpawnProp(instPrefab, room.transform, new Vector3(5f, 1.07f, 4f), Quaternion.Euler(0f, -45f, 0f));
// Cover Crates around perimeter
SpawnProp(boxPrefab, room.transform, new Vector3(-14f, 0f, 9f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(-14f, 1.08f, 9f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(14f, 0f, 9f), Quaternion.identity);
// Distraction Throwables in Grand Atrium
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-4.8f, 1.15f, 4.2f), "Throwable_Beaker_Atrium_W");
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(4.8f, 1.15f, 4.2f), "Throwable_Beaker_Atrium_E");
}
private static void PopulateReactorHall(Transform parent, GameObject pillarPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("ReactorHall_Props");
room.transform.SetParent(parent);
// Two massive industrial reactor turbine housings
var turbine1 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
turbine1.name = "Reactor_Turbine_Alpha";
turbine1.transform.SetParent(room.transform);
turbine1.transform.position = new Vector3(-10f, 2.5f, 38f);
turbine1.transform.localScale = new Vector3(6f, 2.5f, 6f);
var turbine2 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
turbine2.name = "Reactor_Turbine_Beta";
turbine2.transform.SetParent(room.transform);
turbine2.transform.position = new Vector3(10f, 2.5f, 38f);
turbine2.transform.localScale = new Vector3(6f, 2.5f, 6f);
// High Voltage Pillars
SpawnProp(pillarPrefab, room.transform, new Vector3(-18f, 0f, 32f), Quaternion.identity);
SpawnProp(pillarPrefab, room.transform, new Vector3(-18f, 0f, 44f), Quaternion.identity);
SpawnProp(pillarPrefab, room.transform, new Vector3(18f, 0f, 32f), Quaternion.identity);
SpawnProp(pillarPrefab, room.transform, new Vector3(18f, 0f, 44f), Quaternion.identity);
// Stacks of heavy shielded crates for stealth cover
SpawnProp(boxPrefab, room.transform, new Vector3(0f, 0f, 34f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(0f, 1.08f, 34f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(0f, 0f, 42f), Quaternion.identity);
// Throwable Beaker
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(0f, 1.15f, 34f), "Throwable_Beaker_Reactor_Core");
}
private static void PopulateSouthConcourse(Transform parent, GameObject pillarPrefab, GameObject chairPrefab, GameObject boxPrefab, Transform pickupsParent)
{
GameObject concourse = new GameObject("SouthConcourse_Props");
concourse.transform.SetParent(parent);
// Architectural rest benches
SpawnProp(chairPrefab, concourse.transform, new Vector3(-6f, 0f, -32f), Quaternion.identity);
SpawnProp(chairPrefab, concourse.transform, new Vector3(6f, 0f, -32f), Quaternion.identity);
SpawnProp(chairPrefab, concourse.transform, new Vector3(-18f, 0f, -32f), Quaternion.identity);
SpawnProp(chairPrefab, concourse.transform, new Vector3(18f, 0f, -32f), Quaternion.identity);
// Crates for cover
SpawnProp(boxPrefab, concourse.transform, new Vector3(-8f, 0f, -28f), Quaternion.identity);
SpawnProp(boxPrefab, concourse.transform, new Vector3(8f, 0f, -28f), Quaternion.identity);
}
private static void PopulateBiochem(Transform parent, GameObject tablePrefab, GameObject showcasePrefab, GameObject shelfPrefab,
GameObject tapPrefab, GameObject instPrefab, GameObject beakerPrefab, GameObject flaskPrefab, GameObject tubePrefab, Transform pickupsParent)
{
GameObject room = new GameObject("BiochemLab_Props");
room.transform.SetParent(parent);
// Lab Island 1
SpawnProp(tablePrefab, room.transform, new Vector3(-40f, 0f, -14f), Quaternion.identity);
SpawnProp(tablePrefab, room.transform, new Vector3(-36f, 0f, -14f), Quaternion.identity);
SpawnProp(instPrefab, room.transform, new Vector3(-40f, 1.07f, -14f), Quaternion.identity);
SpawnProp(tapPrefab, room.transform, new Vector3(-36f, 1.07f, -14f), Quaternion.identity);
// Lab Island 2
SpawnProp(tablePrefab, room.transform, new Vector3(-40f, 0f, -6f), Quaternion.identity);
SpawnProp(tablePrefab, room.transform, new Vector3(-36f, 0f, -6f), Quaternion.identity);
SpawnProp(tubePrefab, room.transform, new Vector3(-39.5f, 1.07f, -6f), Quaternion.identity);
SpawnProp(flaskPrefab, room.transform, new Vector3(-36.5f, 1.07f, -6f), Quaternion.identity);
// Shelves
SpawnProp(showcasePrefab, room.transform, new Vector3(-46f, 0f, -18f), Quaternion.Euler(0f, 90f, 0f));
SpawnProp(showcasePrefab, room.transform, new Vector3(-46f, 0f, -2f), Quaternion.Euler(0f, 90f, 0f));
// Throwables
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-35.5f, 1.15f, -14f), "Throwable_Beaker_Biochem1");
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-40.5f, 1.15f, -6f), "Throwable_Beaker_Biochem2");
}
private static void PopulateSurgical(Transform parent, GameObject tablePrefab, GameObject showcasePrefab, GameObject chairPrefab,
GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Surgical_Props");
room.transform.SetParent(parent);
// Operating Tables
SpawnProp(tablePrefab, room.transform, new Vector3(34f, 0f, -14f), Quaternion.identity);
SpawnProp(tablePrefab, room.transform, new Vector3(40f, 0f, -14f), Quaternion.identity);
SpawnProp(tablePrefab, room.transform, new Vector3(37f, 0f, -6f), Quaternion.identity);
// Chairs
SpawnProp(chairPrefab, room.transform, new Vector3(34f, 0f, -12.5f), Quaternion.Euler(0f, 180f, 0f));
SpawnProp(chairPrefab, room.transform, new Vector3(40f, 0f, -12.5f), Quaternion.Euler(0f, 180f, 0f));
// Medical Cabinets
SpawnProp(showcasePrefab, room.transform, new Vector3(46f, 0f, -10f), Quaternion.Euler(0f, -90f, 0f));
// Throwable
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(37f, 1.15f, -6f), "Throwable_Beaker_Surgical");
}
private static void PopulateQuarantineProps(Transform parent, GameObject boxPrefab, GameObject rackPrefab, GameObject chairPrefab,
GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Quarantine_Props");
room.transform.SetParent(parent);
// Clutter in cells (broken chairs, overturned cots, boxes)
SpawnProp(chairPrefab, room.transform, new Vector3(-43f, 0f, 9f), Quaternion.Euler(0f, 45f, 0f));
SpawnProp(boxPrefab, room.transform, new Vector3(-44f, 0f, 16f), Quaternion.Euler(0f, 25f, 0f));
SpawnProp(boxPrefab, room.transform, new Vector3(-27f, 0f, 9f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(-26f, 0f, 23f), Quaternion.Euler(0f, 90f, 0f));
// Distraction beaker hidden in isolation cell
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-43f, 0.5f, 23f), "Throwable_Beaker_Quarantine");
}
private static void PopulateGenetics(Transform parent, GameObject tablePrefab, GameObject showcasePrefab, GameObject instPrefab,
GameObject chairPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Genetics_Props");
room.transform.SetParent(parent);
SpawnProp(tablePrefab, room.transform, new Vector3(34f, 0f, 14f), Quaternion.identity);
SpawnProp(instPrefab, room.transform, new Vector3(34f, 1.07f, 14f), Quaternion.identity);
SpawnProp(tablePrefab, room.transform, new Vector3(40f, 0f, 14f), Quaternion.identity);
SpawnProp(instPrefab, room.transform, new Vector3(40f, 1.07f, 14f), Quaternion.identity);
SpawnProp(showcasePrefab, room.transform, new Vector3(46f, 0f, 18f), Quaternion.Euler(0f, -90f, 0f));
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(40f, 1.15f, 14f), "Throwable_Beaker_Genetics");
}
private static void PopulateCryo(Transform parent, GameObject rackPrefab, GameObject shelfPrefab, GameObject boxPrefab,
GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Cryo_Props");
room.transform.SetParent(parent);
// Aisles of cryogenic specimen racks
for (int i = 0; i < 3; i++)
{
float x = -44f + i * 6f;
SpawnProp(rackPrefab, room.transform, new Vector3(x, 0f, -44f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(x, 0f, -38f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(x, 0f, -32f), Quaternion.identity);
}
SpawnProp(boxPrefab, room.transform, new Vector3(-29f, 0f, -30f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(-29f, 1.08f, -30f), Quaternion.identity);
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-38f, 1.15f, -38f), "Throwable_Beaker_Cryo");
}
private static void PopulateArmory(Transform parent, GameObject rackPrefab, GameObject tablePrefab, GameObject instPrefab,
GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Armory_Props");
room.transform.SetParent(parent);
SpawnProp(tablePrefab, room.transform, new Vector3(36f, 0f, -35f), Quaternion.identity);
SpawnProp(instPrefab, room.transform, new Vector3(36f, 1.07f, -35f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(44f, 0f, -44f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(44f, 0f, -38f), Quaternion.identity);
SpawnProp(rackPrefab, room.transform, new Vector3(44f, 0f, -32f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(30f, 0f, -30f), Quaternion.identity);
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(36f, 1.15f, -35f), "Throwable_Beaker_Armory");
}
private static void PopulateChemicalSilos(Transform parent, GameObject pillarPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("ChemicalSilos_Props");
room.transform.SetParent(parent);
var silo1 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
silo1.name = "Toxic_Silo_Alpha";
silo1.transform.SetParent(room.transform);
silo1.transform.position = new Vector3(-37f, 2.5f, 38f);
silo1.transform.localScale = new Vector3(5.5f, 2.5f, 5.5f);
SpawnProp(boxPrefab, room.transform, new Vector3(-44f, 0f, 32f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(-44f, 1.08f, 32f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(-30f, 0f, 44f), Quaternion.identity);
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-44f, 1.15f, 32f), "Throwable_Beaker_Silo");
}
private static void PopulateSubstation(Transform parent, GameObject pillarPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent)
{
GameObject room = new GameObject("Substation_Props");
room.transform.SetParent(parent);
var cap1 = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cap1.name = "Substation_Transformer";
cap1.transform.SetParent(room.transform);
cap1.transform.position = new Vector3(37f, 2.5f, 38f);
cap1.transform.localScale = new Vector3(5.5f, 2.5f, 5.5f);
SpawnProp(boxPrefab, room.transform, new Vector3(44f, 0f, 32f), Quaternion.identity);
SpawnProp(boxPrefab, room.transform, new Vector3(30f, 0f, 44f), Quaternion.identity);
SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(44f, 1.15f, 32f), "Throwable_Beaker_Substation");
}
private static void PopulateSlalomAlleyProps(Transform parent, GameObject boxPrefab, GameObject rackPrefab)
{
GameObject props = new GameObject("Slalom_Chokepoint_Props");
props.transform.SetParent(parent);
// Obstacles placed right at the slalom bends to tighten passage
SpawnProp(boxPrefab, props.transform, new Vector3(-21f, 0f, -11f), Quaternion.Euler(0f, 45f, 0f));
SpawnProp(boxPrefab, props.transform, new Vector3(-21f, 0f, 5f), Quaternion.Euler(0f, 25f, 0f));
SpawnProp(rackPrefab, props.transform, new Vector3(21f, 0f, -11f), Quaternion.Euler(0f, 90f, 0f));
SpawnProp(rackPrefab, props.transform, new Vector3(21f, 0f, 5f), Quaternion.Euler(0f, 90f, 0f));
}
private static void SpawnProp(GameObject prefab, Transform parent, Vector3 pos, Quaternion rot)
{
if (prefab == null) return;
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
if (instance != null)
{
instance.transform.position = pos;
instance.transform.rotation = rot;
}
}
private static void SpawnThrowableBeaker(GameObject beakerPrefab, Transform parent, Vector3 pos, string name)
{
GameObject beaker;
if (beakerPrefab != null)
{
beaker = (GameObject)PrefabUtility.InstantiatePrefab(beakerPrefab, parent);
}
else
{
beaker = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
beaker.transform.localScale = new Vector3(0.3f, 0.5f, 0.3f);
}
beaker.name = name;
beaker.transform.position = pos;
var col = beaker.GetComponent<Collider>();
if (col == null)
{
col = beaker.AddComponent<CapsuleCollider>();
}
var rb = beaker.GetComponent<Rigidbody>();
if (rb == null)
{
rb = beaker.AddComponent<Rigidbody>();
}
rb.mass = 1.0f;
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
var wave = beaker.GetComponent<ImpactWave>();
if (wave == null)
{
wave = beaker.AddComponent<ImpactWave>();
}
wave.soundRadius = 22f;
wave.minImpactForce = 1.5f;
var pulsePrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/EchoPulse.prefab");
if (pulsePrefab != null)
{
wave.wavePrefab = pulsePrefab;
}
}
private static void CreateLighting(Transform parent)
{
// Entrance Airlock - Warning Red
CreatePointLight(parent, "Light_Decon_Red", new Vector3(0f, 3.8f, -44f), new Color(1f, 0.2f, 0.15f), 2.5f, 14f);
// Grand South Concourse - Warm Bronze
CreatePointLight(parent, "Light_Concourse_Mid", new Vector3(0f, 4f, -30f), new Color(1f, 0.75f, 0.4f), 2.2f, 20f);
CreatePointLight(parent, "Light_Concourse_West", new Vector3(-18f, 4f, -30f), new Color(1f, 0.75f, 0.4f), 1.8f, 18f);
CreatePointLight(parent, "Light_Concourse_East", new Vector3(18f, 4f, -30f), new Color(1f, 0.75f, 0.4f), 1.8f, 18f);
// Grand Central Atrium - Bio-Luminescent Emerald & Cyan
CreatePointLight(parent, "Light_Atrium_Core", new Vector3(0f, 4.2f, 9f), new Color(0.15f, 1f, 0.8f), 3.5f, 32f);
CreatePointLight(parent, "Light_Atrium_South", new Vector3(0f, 4.2f, 0f), new Color(0.2f, 0.9f, 0.8f), 2.0f, 22f);
CreatePointLight(parent, "Light_Atrium_North", new Vector3(0f, 4.2f, 18f), new Color(0.2f, 0.9f, 0.8f), 2.0f, 22f);
// Sub-Reactor Hall - Warning Amber & Industrial High-Voltage
CreatePointLight(parent, "Light_Reactor_Turbine_W", new Vector3(-10f, 4.2f, 38f), new Color(1f, 0.55f, 0.1f), 3.0f, 26f);
CreatePointLight(parent, "Light_Reactor_Turbine_E", new Vector3(10f, 4.2f, 38f), new Color(1f, 0.55f, 0.1f), 3.0f, 26f);
CreatePointLight(parent, "Light_Reactor_Core", new Vector3(0f, 4.2f, 38f), new Color(0.3f, 0.85f, 1f), 2.5f, 24f);
// Quarantine Wing - Claustrophobic Flickering Crimson
CreatePointLight(parent, "Light_Quarantine_1", new Vector3(-35f, 3.8f, 9f), new Color(1f, 0.1f, 0.1f), 2.0f, 16f);
CreatePointLight(parent, "Light_Quarantine_2", new Vector3(-35f, 3.8f, 23f), new Color(1f, 0.1f, 0.1f), 2.0f, 16f);
// Biochem Lab - Toxic Mint Green
CreatePointLight(parent, "Light_Biochem", new Vector3(-37f, 3.8f, -10f), new Color(0.3f, 0.95f, 0.5f), 2.2f, 22f);
// Surgical ICU - Cold Clinical White
CreatePointLight(parent, "Light_Surgical", new Vector3(37f, 3.8f, -10f), new Color(0.85f, 0.95f, 1f), 2.2f, 22f);
// Genetics Lab - Electric Violet
CreatePointLight(parent, "Light_Genetics", new Vector3(37f, 3.8f, 16f), new Color(0.6f, 0.35f, 1f), 2.0f, 22f);
// Cryo Vault - Deep Glacier Cyan
CreatePointLight(parent, "Light_Cryo", new Vector3(-37f, 3.8f, -37f), new Color(0.2f, 0.65f, 1f), 2.0f, 22f);
// Armory - Surveillance Amber
CreatePointLight(parent, "Light_Armory", new Vector3(37f, 3.8f, -37f), new Color(1f, 0.65f, 0.2f), 2.0f, 22f);
// Silos & Substation
CreatePointLight(parent, "Light_Silos", new Vector3(-37f, 3.8f, 38f), new Color(0.8f, 0.9f, 0.2f), 2.0f, 22f);
CreatePointLight(parent, "Light_Substation", new Vector3(37f, 3.8f, 38f), new Color(0.4f, 0.7f, 1f), 2.0f, 22f);
// North Bypass Runways
CreatePointLight(parent, "Light_Bypass_W", new Vector3(-25f, 3.2f, 47.5f), new Color(1f, 0.7f, 0.3f), 1.4f, 14f);
CreatePointLight(parent, "Light_Bypass_Mid", new Vector3(0f, 3.2f, 47.5f), new Color(1f, 0.7f, 0.3f), 1.4f, 14f);
CreatePointLight(parent, "Light_Bypass_E", new Vector3(25f, 3.2f, 47.5f), new Color(1f, 0.7f, 0.3f), 1.4f, 14f);
}
private static void CreatePointLight(Transform parent, string name, Vector3 pos, Color color, float intensity, float range)
{
GameObject lightGo = new GameObject(name);
lightGo.transform.SetParent(parent);
lightGo.transform.position = pos;
var light = lightGo.AddComponent<Light>();
light.type = LightType.Point;
light.color = color;
light.intensity = intensity;
light.range = range;
light.shadows = LightShadows.Soft;
}
private static void SetupPatrolWaypoints(Transform parent, GameObject enemy)
{
var oldPatrol = GameObject.Find("PatrolWaypoints");
if (oldPatrol != null)
{
Undo.DestroyObjectImmediate(oldPatrol);
}
// Clean any old 1..15 in scene
for (int i = 0; i <= 20; i++)
{
string oldName = i == 0 ? "1" : $"1 ({i})";
var oldGo = GameObject.Find(oldName);
if (oldGo != null)
{
oldGo.SetActive(false);
}
}
GameObject patrolParent = new GameObject("PatrolWaypoints");
patrolParent.transform.SetParent(parent);
// 32 Waypoints looping smoothly through ALL wings and sectors of the 100x100m facility!
Vector3[] waypoints = new Vector3[]
{
// South Concourse & Cryo
new Vector3(0f, 0f, -30f), // WP0: Concourse Center
new Vector3(-18f, 0f, -30f), // WP1: Concourse West
new Vector3(-37f, 0f, -37f), // WP2: Cryogenics Vault
new Vector3(-37f, 0f, -24f), // WP3: Cryo North Gate
// Biochem & West Slalom
new Vector3(-37f, 0f, -14f), // WP4: Biochem Lab South
new Vector3(-37f, 0f, -4f), // WP5: Biochem Lab North
new Vector3(-21f, 0f, -4f), // WP6: West Slalom Entry
new Vector3(-21f, 0f, 6f), // WP7: West Slalom Mid
// Quarantine Cellblock (Labyrinth)
new Vector3(-35f, 0f, 8f), // WP8: Quarantine Cell Corridor South
new Vector3(-43f, 0f, 9f), // WP9: Isolation Cell 1
new Vector3(-35f, 0f, 16f), // WP10: Quarantine Cell Corridor Mid
new Vector3(-27f, 0f, 16f), // WP11: Isolation Cell 5
new Vector3(-35f, 0f, 23f), // WP12: Quarantine Cell Corridor North
// Chemical Silos & North Bypass
new Vector3(-37f, 0f, 38f), // WP13: Chemical Silos
new Vector3(-37f, 0f, 47.5f), // WP14: North Bypass West
new Vector3(-15f, 0f, 47.5f), // WP15: North Bypass Mid-West
// Reactor Turbine Hall (Very Spacious)
new Vector3(-10f, 0f, 38f), // WP16: Reactor Turbine Alpha
new Vector3(0f, 0f, 38f), // WP17: Reactor Core Platform
new Vector3(10f, 0f, 38f), // WP18: Reactor Turbine Beta
// Substation & East North Bypass
new Vector3(25f, 0f, 47.5f), // WP19: North Bypass East
new Vector3(37f, 0f, 38f), // WP20: Power Substation
// Genetics & Cybernetics
new Vector3(37f, 0f, 22f), // WP21: Genetics Lab North
new Vector3(37f, 0f, 10f), // WP22: Genetics Lab South
// Grand Atrium (Very Spacious)
new Vector3(12f, 0f, 9f), // WP23: Grand Atrium East Colonnade
new Vector3(0f, 0f, 18f), // WP24: Grand Atrium North Observation
new Vector3(-12f, 0f, 9f), // WP25: Grand Atrium West Colonnade
new Vector3(0f, 0f, 0f), // WP26: Grand Atrium South Portal
// East Slalom & Surgical ICU
new Vector3(21f, 0f, 0f), // WP27: East Slalom Corridor
new Vector3(37f, 0f, -4f), // WP28: Surgical ICU North
new Vector3(37f, 0f, -14f), // WP29: Surgical ICU South
// Security Armory & Return to Concourse
new Vector3(37f, 0f, -37f), // WP30: Security Armory
new Vector3(18f, 0f, -30f) // WP31: Concourse East
};
Transform[] transforms = new Transform[waypoints.Length];
for (int i = 0; i < waypoints.Length; i++)
{
var wpGo = new GameObject($"Waypoint_{i}");
wpGo.transform.SetParent(patrolParent.transform);
wpGo.transform.position = waypoints[i];
transforms[i] = wpGo.transform;
}
if (enemy != null)
{
// Position enemy in South Concourse right outside the starting airlock
enemy.transform.position = new Vector3(0f, 0.08f, -28f);
enemy.transform.rotation = Quaternion.Euler(0f, 90f, 0f);
var ai = enemy.GetComponent<BlindMutantAI>();
if (ai != null)
{
Undo.RecordObject(ai, "Setup Patrol Points");
ai.patrolPoints = transforms;
EditorUtility.SetDirty(ai);
EditorUtility.SetDirty(enemy);
Debug.Log($"<color=green>Configured {transforms.Length} patrol points for SkinnyOrc BlindMutantAI in South Concourse (0, 0.08, -28)!</color>");
}
}
}
}