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.25f; private const float WALL_THICKNESS = 0.35f; [MenuItem("Tools/Generate Full Laboratory Location")] public static void Generate() { Debug.Log("=== GENERATING LABORATORY LOCATION ==="); // 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 <= 4; 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("Assets/EchoCloudMat.mat"); if (echoMat == null) { echoMat = AssetDatabase.LoadAssetAtPath("Assets/EchoMat.mat"); } Material woodMat = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Materials/Laboratory03_Wood_BaseMap.mat"); Material otherMat = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Materials/Laboratory03_Others_BaseMap.mat"); Material glassMat = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Materials/Laboratory03_Glass_BaseMap 1.mat"); GameObject tablePrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/table with drawers.prefab"); GameObject shelfShowcasePrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/shelf showcase.prefab"); GameObject shelf2Prefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/shelf (2).prefab"); GameObject rackPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/rack.prefab"); GameObject chairPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/chair.prefab"); GameObject pillarPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/pillar.prefab"); GameObject boxPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/Box.prefab"); GameObject beakerPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/Beaker.prefab"); GameObject flaskPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/Erlenmeyer_flask.prefab"); GameObject testTubePrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/Glass_Lab_test_tube.prefab"); GameObject instPrefab = AssetDatabase.LoadAssetAtPath("Assets/3D Laboratory Environment with Appratus/Prefabs/analytical_lab_instruments.prefab"); GameObject tapPrefab = AssetDatabase.LoadAssetAtPath("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 (64m x 64m, from -32 to +32 in X and Z) CreateFloorGrid(floorsParent.transform, echoMat, -32f, 32f, -32f, 32f); // 3. Build Perimeter Exterior Walls CreateWall(wallsParent.transform, echoMat, new Vector3(0, WALL_HEIGHT / 2f, -32f), new Vector3(64f, WALL_HEIGHT, WALL_THICKNESS), "Wall_Perimeter_South"); CreateWall(wallsParent.transform, echoMat, new Vector3(0, WALL_HEIGHT / 2f, 32f), new Vector3(64f, WALL_HEIGHT, WALL_THICKNESS), "Wall_Perimeter_North"); CreateWall(wallsParent.transform, echoMat, new Vector3(-32f, WALL_HEIGHT / 2f, 0), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 64f), "Wall_Perimeter_West"); CreateWall(wallsParent.transform, echoMat, new Vector3(32f, WALL_HEIGHT / 2f, 0), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 64f), "Wall_Perimeter_East"); // 4. Build Rooms Walls with Doorways // We define rooms as bounding boxes [minX, maxX, minZ, maxZ] and doors on specific sides with cutout [doorMin, doorMax] // Room 1: Entrance / Decontamination (X: -6 to +6, Z: -32 to -22) BuildRoomWalls(wallsParent.transform, echoMat, -6f, 6f, -32f, -22f, southDoor: null, // sealed northDoor: new Vector2(-1.8f, 1.8f), westDoor: null, eastDoor: null, roomName: "Decontamination"); // Room 4: Cryo Archive (X: -30 to -14, Z: -32 to -20) BuildRoomWalls(wallsParent.transform, echoMat, -30f, -14f, -32f, -20f, southDoor: null, northDoor: new Vector2(-24f, -20f), westDoor: null, eastDoor: new Vector2(-27f, -23f), roomName: "CryoArchive"); // Room 8: Security & Surveillance (X: 14 to 30, Z: -32 to -20) BuildRoomWalls(wallsParent.transform, echoMat, 14f, 30f, -32f, -20f, southDoor: null, northDoor: new Vector2(20f, 24f), westDoor: new Vector2(-27f, -23f), eastDoor: null, roomName: "SecurityOffice"); // Room 2: Biochemistry Research Lab (X: -30 to -14, Z: -14 to 2) BuildRoomWalls(wallsParent.transform, echoMat, -30f, -14f, -14f, 2f, southDoor: new Vector2(-24f, -20f), northDoor: new Vector2(-24f, -20f), westDoor: null, eastDoor: new Vector2(-8f, -4f), roomName: "BiochemLab"); // Room 6: Surgical Theatre & Medical Bay (X: 14 to 30, Z: -14 to 2) BuildRoomWalls(wallsParent.transform, echoMat, 14f, 30f, -14f, 2f, southDoor: new Vector2(20f, 24f), northDoor: new Vector2(20f, 24f), westDoor: new Vector2(-8f, -4f), eastDoor: null, roomName: "SurgicalBay"); // Room 3: Bio-Hazard Containment Vault (X: -30 to -12, Z: 8 to 26) BuildRoomWalls(wallsParent.transform, echoMat, -30f, -12f, 8f, 26f, southDoor: new Vector2(-23f, -19f), northDoor: new Vector2(-23f, -19f), // leads to North Maintenance bypass westDoor: null, eastDoor: new Vector2(15f, 19f), roomName: "BioContainment"); // Room 5: Central Bio-Dome & Core Reactor (X: -7 to 7, Z: 8 to 26) BuildRoomWalls(wallsParent.transform, echoMat, -7f, 7f, 8f, 26f, southDoor: new Vector2(-2f, 2f), northDoor: new Vector2(-2f, 2f), // leads to North Maintenance bypass westDoor: new Vector2(15f, 19f), eastDoor: new Vector2(15f, 19f), roomName: "CentralCore"); // Room 7: Genetics Sequencing Lab (X: 12 to 30, Z: 6 to 18) BuildRoomWalls(wallsParent.transform, echoMat, 12f, 30f, 6f, 18f, southDoor: new Vector2(19f, 23f), northDoor: new Vector2(19f, 23f), westDoor: new Vector2(10f, 14f), eastDoor: null, roomName: "GeneticsLab"); // Room 9: Generator Substation (X: 12 to 30, Z: 20 to 30) BuildRoomWalls(wallsParent.transform, echoMat, 12f, 30f, 20f, 30f, southDoor: new Vector2(19f, 23f), northDoor: null, westDoor: new Vector2(23f, 27f), // leads to North Maintenance bypass eastDoor: null, roomName: "GeneratorRoom"); // 5. Build Tangled Corridor Internal Walls & Partitions (Maze structure) BuildCorridorMaze(wallsParent.transform, echoMat); // 6. Populate Rooms with Furniture & Props PopulateDecontamination(roomsParent.transform, pillarPrefab, rackPrefab, chairPrefab, echoMat); PopulateBiochemLab(roomsParent.transform, tablePrefab, shelfShowcasePrefab, shelf2Prefab, tapPrefab, instPrefab, beakerPrefab, flaskPrefab, testTubePrefab, pickupsParent.transform); PopulateBioContainment(roomsParent.transform, pillarPrefab, tablePrefab, boxPrefab, beakerPrefab, instPrefab, pickupsParent.transform); PopulateCryoArchive(roomsParent.transform, rackPrefab, shelf2Prefab, boxPrefab, beakerPrefab, pickupsParent.transform); PopulateCentralCore(roomsParent.transform, pillarPrefab, tablePrefab, instPrefab, boxPrefab, beakerPrefab, pickupsParent.transform); PopulateSurgicalBay(roomsParent.transform, tablePrefab, shelfShowcasePrefab, chairPrefab, beakerPrefab, pickupsParent.transform); PopulateGeneticsLab(roomsParent.transform, tablePrefab, shelfShowcasePrefab, instPrefab, chairPrefab, beakerPrefab, pickupsParent.transform); PopulateGeneratorRoom(roomsParent.transform, pillarPrefab, boxPrefab, beakerPrefab, pickupsParent.transform); PopulateCorridorNiches(roomsParent.transform, pillarPrefab, boxPrefab, rackPrefab); // 7. Atmospheric Lighting setup CreateLighting(lightsParent.transform); // 8. Reconfigure Player Position var player = GameObject.Find("Capsule"); if (player != null) { player.transform.position = new Vector3(0f, 1.05f, -28f); player.transform.rotation = Quaternion.Euler(0f, 0f, 0f); Debug.Log("Moved Player to Entrance Airlock (0, 1.05, -28)"); } // 9. Reconfigure Enemy (BlindMutantAI) & Patrol Waypoints var enemy = GameObject.Find("enemy"); SetupPatrolWaypoints(labRoot.transform, enemy); // 10. Re-bake NavMesh Surface var navMeshSurface = Object.FindAnyObjectByType(); if (navMeshSurface != null) { navMeshSurface.transform.position = Vector3.zero; navMeshSurface.collectObjects = CollectObjects.All; navMeshSurface.BuildNavMesh(); Debug.Log("NavMesh successfully baked for the entire laboratory complex!"); } // Mark scene dirty EditorUtility.SetDirty(labRoot); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()); Debug.Log("=== LABORATORY COMPLEX GENERATION COMPLETE! ==="); } private static void CreateFloorGrid(Transform parent, Material mat, float minX, float maxX, float minZ, float maxZ) { // 4 large floor quadrants for clean rendering and collision float midX = (minX + maxX) / 2f; float midZ = (minZ + maxZ) / 2f; float sizeX = (maxX - minX) / 2f; float sizeZ = (maxZ - minZ) / 2f; Vector2[] quads = new Vector2[] { new Vector2((minX + midX) / 2f, (minZ + midZ) / 2f), new Vector2((midX + maxX) / 2f, (minZ + midZ) / 2f), new Vector2((minX + midX) / 2f, (midZ + maxZ) / 2f), new Vector2((midX + maxX) / 2f, (midZ + maxZ) / 2f) }; for (int i = 0; i < quads.Length; i++) { var floorGo = GameObject.CreatePrimitive(PrimitiveType.Cube); floorGo.name = $"Floor_Quadrant_{i + 1}"; floorGo.transform.SetParent(parent); floorGo.transform.position = new Vector3(quads[i].x, -0.25f, quads[i].y); floorGo.transform.localScale = new Vector3(sizeX, 0.5f, sizeZ); if (mat != null) floorGo.GetComponent().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) { // Full solid wall 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.5f; float lintelPosY = 2.5f + (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().sharedMaterial = mat; return wall; } private static void BuildCorridorMaze(Transform parent, Material mat) { GameObject mazeGo = new GameObject("Tangled_Corridors_Maze"); mazeGo.transform.SetParent(parent); // 1. Central Core Elevator Shaft / Ventilation Pillar divider (splits Central Hallway into two paths) // Position: X: -3 to +3, Z: -6 to 2 CreateWall(mazeGo.transform, mat, new Vector3(0f, WALL_HEIGHT / 2f, -2f), new Vector3(5f, WALL_HEIGHT, 6f), "Central_Ventilation_Core"); // 2. West Serpentine Hallway Dividers (creating zig-zags between X: -14 and -6) // Horizontal baffle at Z = -17 (X from -14 to -9) CreateWall(mazeGo.transform, mat, new Vector3(-11.5f, WALL_HEIGHT / 2f, -17f), new Vector3(5f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_West_1"); // Vertical wall at X = -9 (Z from -17 to -12) with opening CreateWall(mazeGo.transform, mat, new Vector3(-9f, WALL_HEIGHT / 2f, -14.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 5f), "Baffle_West_2"); // Horizontal baffle at Z = -11 (X from -11 to -7) CreateWall(mazeGo.transform, mat, new Vector3(-9f, WALL_HEIGHT / 2f, -11f), new Vector3(4f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_West_3"); // Vertical wall at X = -10 (Z from -4 to 3) CreateWall(mazeGo.transform, mat, new Vector3(-10f, WALL_HEIGHT / 2f, -0.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 7f), "Baffle_West_4"); // Alcove cover at X = -7 (Z = 5) CreateWall(mazeGo.transform, mat, new Vector3(-8.5f, WALL_HEIGHT / 2f, 5f), new Vector3(3f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_West_Alcove"); // 3. East Serpentine Hallway Dividers (creating zig-zags between X: +6 and +14) // Horizontal baffle at Z = -17 (X from 9 to 14) CreateWall(mazeGo.transform, mat, new Vector3(11.5f, WALL_HEIGHT / 2f, -17f), new Vector3(5f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_East_1"); // Vertical wall at X = 9 (Z from -17 to -12) CreateWall(mazeGo.transform, mat, new Vector3(9f, WALL_HEIGHT / 2f, -14.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 5f), "Baffle_East_2"); // Horizontal baffle at Z = -11 (X from 7 to 11) CreateWall(mazeGo.transform, mat, new Vector3(9f, WALL_HEIGHT / 2f, -11f), new Vector3(4f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_East_3"); // Vertical wall at X = 10 (Z from -4 to 3) CreateWall(mazeGo.transform, mat, new Vector3(10f, WALL_HEIGHT / 2f, -0.5f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 7f), "Baffle_East_4"); // Alcove cover at X = 7 (Z = 5) CreateWall(mazeGo.transform, mat, new Vector3(8.5f, WALL_HEIGHT / 2f, 5f), new Vector3(3f, WALL_HEIGHT, WALL_THICKNESS), "Baffle_East_Alcove"); // 4. North Maintenance Crawlway / Bypass (Long narrow bypass at Z: 28 to 31, spanning X: -27 to +27) // Southern guide wall of the crawlway: // Section A (X: -30 to -9 at Z = 28.5) with door into BioContainment at X: -21 CreateWallWithDoor(mazeGo.transform, mat, -30f, -9f, 28.5f, true, new Vector2(-23f, -19f), "NorthCrawl_WestGuide"); // Section B (X: -5 to +5 at Z = 28.5) behind Central Core with center access door CreateWallWithDoor(mazeGo.transform, mat, -7f, 7f, 28.5f, true, new Vector2(-2f, 2f), "NorthCrawl_MidGuide"); // Section C (X: 9 to 30 at Z = 28.5) leading to Generator room with door at X: 21 CreateWallWithDoor(mazeGo.transform, mat, 9f, 30f, 28.5f, true, new Vector2(19f, 23f), "NorthCrawl_EastGuide"); // 5. South Crossroads Corridor Dividers (between Decontamination and Wings) // Partition at X = -6, Z = -18 (creating blind corner from Decon to Cryo) CreateWall(mazeGo.transform, mat, new Vector3(-6f, WALL_HEIGHT / 2f, -18f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 4f), "Crossroad_West_Corner"); // Partition at X = +6, Z = -18 (creating blind corner from Decon to Security) CreateWall(mazeGo.transform, mat, new Vector3(6f, WALL_HEIGHT / 2f, -18f), new Vector3(WALL_THICKNESS, WALL_HEIGHT, 4f), "Crossroad_East_Corner"); } private static void PopulateDecontamination(Transform parent, GameObject pillarPrefab, GameObject rackPrefab, GameObject chairPrefab, Material echoMat) { GameObject room = new GameObject("Decontamination_Props"); room.transform.SetParent(parent); // Decontamination Frame (two pillars with lintel) SpawnProp(pillarPrefab, room.transform, new Vector3(-2f, 0f, -25f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(2f, 0f, -25f), Quaternion.identity); // Lockers / Racks along West wall SpawnProp(rackPrefab, room.transform, new Vector3(-4.8f, 0f, -29f), Quaternion.Euler(0f, 90f, 0f)); SpawnProp(rackPrefab, room.transform, new Vector3(-4.8f, 0f, -27f), Quaternion.Euler(0f, 90f, 0f)); // Benches along East wall SpawnProp(chairPrefab, room.transform, new Vector3(4.5f, 0f, -29f), Quaternion.Euler(0f, -90f, 0f)); SpawnProp(chairPrefab, room.transform, new Vector3(4.5f, 0f, -27f), Quaternion.Euler(0f, -90f, 0f)); } private static void PopulateBiochemLab(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); // Central Lab Island 1 SpawnProp(tablePrefab, room.transform, new Vector3(-24f, 0f, -8f), Quaternion.identity); SpawnProp(tablePrefab, room.transform, new Vector3(-20f, 0f, -8f), Quaternion.identity); SpawnProp(instPrefab, room.transform, new Vector3(-24f, 1.07f, -8f), Quaternion.identity); SpawnProp(tapPrefab, room.transform, new Vector3(-20f, 1.07f, -8f), Quaternion.identity); // Central Lab Island 2 SpawnProp(tablePrefab, room.transform, new Vector3(-24f, 0f, -4f), Quaternion.identity); SpawnProp(tablePrefab, room.transform, new Vector3(-20f, 0f, -4f), Quaternion.identity); SpawnProp(tubePrefab, room.transform, new Vector3(-23.5f, 1.07f, -4f), Quaternion.identity); SpawnProp(flaskPrefab, room.transform, new Vector3(-20.5f, 1.07f, -4f), Quaternion.identity); // Shelves along West Wall SpawnProp(showcasePrefab, room.transform, new Vector3(-29f, 0f, -10f), Quaternion.Euler(0f, 90f, 0f)); SpawnProp(showcasePrefab, room.transform, new Vector3(-29f, 0f, -2f), Quaternion.Euler(0f, 90f, 0f)); SpawnProp(shelfPrefab, room.transform, new Vector3(-29f, 0f, -6f), Quaternion.Euler(0f, 90f, 0f)); // Throwable Beaker distraction on bench SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-19.5f, 1.15f, -8f), "Throwable_Beaker_Biochem1"); SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-24.5f, 1.15f, -4f), "Throwable_Beaker_Biochem2"); } private static void PopulateBioContainment(Transform parent, GameObject pillarPrefab, GameObject tablePrefab, GameObject boxPrefab, GameObject beakerPrefab, GameObject instPrefab, Transform pickupsParent) { GameObject room = new GameObject("BioContainment_Props"); room.transform.SetParent(parent); // 4 massive containment corner pillars SpawnProp(pillarPrefab, room.transform, new Vector3(-26f, 0f, 12f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(-15f, 0f, 12f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(-26f, 0f, 22f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(-15f, 0f, 22f), Quaternion.identity); // Broken containment center vat/chamber var vatBase = GameObject.CreatePrimitive(PrimitiveType.Cylinder); vatBase.name = "Containment_Vat_Broken"; vatBase.transform.SetParent(room.transform); vatBase.transform.position = new Vector3(-20.5f, 1.5f, 17f); vatBase.transform.localScale = new Vector3(3.5f, 1.5f, 3.5f); // Observation console desk SpawnProp(tablePrefab, room.transform, new Vector3(-20.5f, 0f, 10.5f), Quaternion.identity); SpawnProp(instPrefab, room.transform, new Vector3(-20.5f, 1.07f, 10.5f), Quaternion.identity); // Overturned boxes and cover SpawnProp(boxPrefab, room.transform, new Vector3(-24f, 0f, 15f), Quaternion.Euler(0f, 35f, 0f)); SpawnProp(boxPrefab, room.transform, new Vector3(-24f, 0f, 16.5f), Quaternion.Euler(0f, 15f, 0f)); SpawnProp(boxPrefab, room.transform, new Vector3(-16f, 0f, 20f), Quaternion.Euler(0f, 60f, 0f)); // Throwable distraction beaker SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-20f, 1.15f, 10.5f), "Throwable_Beaker_Containment"); } private static void PopulateCryoArchive(Transform parent, GameObject rackPrefab, GameObject shelfPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent) { GameObject room = new GameObject("CryoArchive_Props"); room.transform.SetParent(parent); // Rows of storage racks creating narrow aisles SpawnProp(rackPrefab, room.transform, new Vector3(-26f, 0f, -28f), Quaternion.identity); SpawnProp(rackPrefab, room.transform, new Vector3(-26f, 0f, -24f), Quaternion.identity); SpawnProp(rackPrefab, room.transform, new Vector3(-21f, 0f, -28f), Quaternion.identity); SpawnProp(rackPrefab, room.transform, new Vector3(-21f, 0f, -24f), Quaternion.identity); SpawnProp(rackPrefab, room.transform, new Vector3(-16f, 0f, -28f), Quaternion.identity); // Storage boxes stacked for cover SpawnProp(boxPrefab, room.transform, new Vector3(-28.5f, 0f, -22f), Quaternion.identity); SpawnProp(boxPrefab, room.transform, new Vector3(-28.5f, 1.08f, -22f), Quaternion.identity); SpawnProp(boxPrefab, room.transform, new Vector3(-17f, 0f, -22f), Quaternion.identity); // Shelves along back wall SpawnProp(shelfPrefab, room.transform, new Vector3(-23f, 0f, -31f), Quaternion.identity); // Throwable distraction beaker in storage SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-26f, 1.15f, -28f), "Throwable_Beaker_Cryo"); } private static void PopulateCentralCore(Transform parent, GameObject pillarPrefab, GameObject tablePrefab, GameObject instPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent) { GameObject room = new GameObject("CentralCore_Props"); room.transform.SetParent(parent); // 4 massive observation pillars SpawnProp(pillarPrefab, room.transform, new Vector3(-5.5f, 0f, 10.5f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(5.5f, 0f, 10.5f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(-5.5f, 0f, 23.5f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(5.5f, 0f, 23.5f), Quaternion.identity); // Central Specimen Reactor Vat var coreCylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder); coreCylinder.name = "Central_Mutant_Reactor_Core"; coreCylinder.transform.SetParent(room.transform); coreCylinder.transform.position = new Vector3(0f, 2.0f, 17f); coreCylinder.transform.localScale = new Vector3(4.5f, 2.0f, 4.5f); // Observation console desks SpawnProp(tablePrefab, room.transform, new Vector3(-3f, 0f, 13f), Quaternion.Euler(0f, 45f, 0f)); SpawnProp(instPrefab, room.transform, new Vector3(-3f, 1.07f, 13f), Quaternion.Euler(0f, 45f, 0f)); SpawnProp(tablePrefab, room.transform, new Vector3(3f, 0f, 13f), Quaternion.Euler(0f, -45f, 0f)); SpawnProp(instPrefab, room.transform, new Vector3(3f, 1.07f, 13f), Quaternion.Euler(0f, -45f, 0f)); // Cover crates in corners SpawnProp(boxPrefab, room.transform, new Vector3(-5.5f, 0f, 17f), Quaternion.identity); SpawnProp(boxPrefab, room.transform, new Vector3(5.5f, 0f, 17f), Quaternion.identity); // Throwable distraction beaker on console SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(-2.8f, 1.15f, 13.2f), "Throwable_Beaker_Core"); } private static void PopulateSurgicalBay(Transform parent, GameObject tablePrefab, GameObject showcasePrefab, GameObject chairPrefab, GameObject beakerPrefab, Transform pickupsParent) { GameObject room = new GameObject("SurgicalBay_Props"); room.transform.SetParent(parent); // Dual operating / dissection tables SpawnProp(tablePrefab, room.transform, new Vector3(18f, 0f, -6f), Quaternion.identity); SpawnProp(tablePrefab, room.transform, new Vector3(24f, 0f, -6f), Quaternion.identity); // Surgeon stools SpawnProp(chairPrefab, room.transform, new Vector3(18f, 0f, -4.5f), Quaternion.Euler(0f, 180f, 0f)); SpawnProp(chairPrefab, room.transform, new Vector3(24f, 0f, -4.5f), Quaternion.Euler(0f, 180f, 0f)); // Medical supply showcases along East Wall SpawnProp(showcasePrefab, room.transform, new Vector3(29f, 0f, -10f), Quaternion.Euler(0f, -90f, 0f)); SpawnProp(showcasePrefab, room.transform, new Vector3(29f, 0f, -2f), Quaternion.Euler(0f, -90f, 0f)); // Throwable beaker SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(23.5f, 1.15f, -6f), "Throwable_Beaker_Surgery"); } private static void PopulateGeneticsLab(Transform parent, GameObject tablePrefab, GameObject showcasePrefab, GameObject instPrefab, GameObject chairPrefab, GameObject beakerPrefab, Transform pickupsParent) { GameObject room = new GameObject("GeneticsLab_Props"); room.transform.SetParent(parent); // Genetic sequencer workstations SpawnProp(tablePrefab, room.transform, new Vector3(18f, 0f, 12f), Quaternion.identity); SpawnProp(instPrefab, room.transform, new Vector3(18f, 1.07f, 12f), Quaternion.identity); SpawnProp(chairPrefab, room.transform, new Vector3(18f, 0f, 10.5f), Quaternion.identity); SpawnProp(tablePrefab, room.transform, new Vector3(24f, 0f, 12f), Quaternion.identity); SpawnProp(instPrefab, room.transform, new Vector3(24f, 1.07f, 12f), Quaternion.identity); SpawnProp(chairPrefab, room.transform, new Vector3(24f, 0f, 10.5f), Quaternion.identity); // Showcases along East Wall SpawnProp(showcasePrefab, room.transform, new Vector3(29f, 0f, 12f), Quaternion.Euler(0f, -90f, 0f)); // Throwable beaker SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(24.5f, 1.15f, 12f), "Throwable_Beaker_Genetics"); } private static void PopulateGeneratorRoom(Transform parent, GameObject pillarPrefab, GameObject boxPrefab, GameObject beakerPrefab, Transform pickupsParent) { GameObject room = new GameObject("GeneratorRoom_Props"); room.transform.SetParent(parent); // 2 Massive Transformer Generator Blocks var gen1 = GameObject.CreatePrimitive(PrimitiveType.Cube); gen1.name = "Generator_Core_1"; gen1.transform.SetParent(room.transform); gen1.transform.position = new Vector3(18f, 2.0f, 25f); gen1.transform.localScale = new Vector3(3f, 4f, 4f); var gen2 = GameObject.CreatePrimitive(PrimitiveType.Cube); gen2.name = "Generator_Core_2"; gen2.transform.SetParent(room.transform); gen2.transform.position = new Vector3(25f, 2.0f, 25f); gen2.transform.localScale = new Vector3(3f, 4f, 4f); // Support pillars SpawnProp(pillarPrefab, room.transform, new Vector3(14f, 0f, 28f), Quaternion.identity); SpawnProp(pillarPrefab, room.transform, new Vector3(28f, 0f, 28f), Quaternion.identity); // Power supply crates SpawnProp(boxPrefab, room.transform, new Vector3(21.5f, 0f, 27.5f), Quaternion.identity); SpawnProp(boxPrefab, room.transform, new Vector3(21.5f, 1.08f, 27.5f), Quaternion.identity); // Throwable distraction SpawnThrowableBeaker(beakerPrefab, pickupsParent, new Vector3(21.5f, 1.15f, 22f), "Throwable_Beaker_Generator"); } private static void PopulateCorridorNiches(Transform parent, GameObject pillarPrefab, GameObject boxPrefab, GameObject rackPrefab) { GameObject niches = new GameObject("Corridor_Niches_Cover"); niches.transform.SetParent(parent); // South Concourse pillars & crates SpawnProp(pillarPrefab, niches.transform, new Vector3(-3f, 0f, -18f), Quaternion.identity); SpawnProp(pillarPrefab, niches.transform, new Vector3(3f, 0f, -18f), Quaternion.identity); SpawnProp(boxPrefab, niches.transform, new Vector3(0f, 0f, -17f), Quaternion.identity); // West corridor alcove hiding spot SpawnProp(boxPrefab, niches.transform, new Vector3(-8.5f, 0f, 6.5f), Quaternion.identity); SpawnProp(rackPrefab, niches.transform, new Vector3(-8.5f, 0f, -12.5f), Quaternion.Euler(0f, 90f, 0f)); // East corridor alcove hiding spot SpawnProp(boxPrefab, niches.transform, new Vector3(8.5f, 0f, 6.5f), Quaternion.identity); SpawnProp(rackPrefab, niches.transform, new Vector3(8.5f, 0f, -12.5f), Quaternion.Euler(0f, -90f, 0f)); // North Crawlway mid-point cover SpawnProp(boxPrefab, niches.transform, new Vector3(-8f, 0f, 30f), Quaternion.identity); SpawnProp(boxPrefab, niches.transform, new Vector3(8f, 0f, 30f), Quaternion.identity); } 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; // Ensure collider var col = beaker.GetComponent(); if (col == null) { col = beaker.AddComponent(); } // Rigidbody for throwing physics var rb = beaker.GetComponent(); if (rb == null) { rb = beaker.AddComponent(); } rb.mass = 1.0f; rb.collisionDetectionMode = CollisionDetectionMode.Continuous; // ImpactWave component so it triggers echolocation wave and mutant alert when landing! var wave = beaker.GetComponent(); if (wave == null) { wave = beaker.AddComponent(); } wave.soundRadius = 18f; wave.minImpactForce = 1.5f; // Load EchoPulse prefab if available var pulsePrefab = AssetDatabase.LoadAssetAtPath("Assets/EchoPulse.prefab"); if (pulsePrefab != null) { wave.wavePrefab = pulsePrefab; } } private static void CreateLighting(Transform parent) { // Entrance Airlock - Warning Red emergency light CreatePointLight(parent, "Light_Decon_Red", new Vector3(0f, 3.8f, -27f), new Color(1f, 0.2f, 0.15f), 2.5f, 12f); // South Concourse - Dim Amber light CreatePointLight(parent, "Light_SouthConcourse", new Vector3(0f, 3.8f, -18f), new Color(1f, 0.75f, 0.4f), 1.8f, 14f); // Cryo Archive - Cold Cyan/Blue CreatePointLight(parent, "Light_Cryo_Cyan", new Vector3(-20f, 3.8f, -26f), new Color(0.2f, 0.7f, 1f), 1.6f, 16f); // Security Office - Amber Terminal light CreatePointLight(parent, "Light_Security_Amber", new Vector3(20f, 3.8f, -26f), new Color(1f, 0.65f, 0.2f), 1.8f, 16f); // Biochem Lab - Toxic Pale Green CreatePointLight(parent, "Light_Biochem_Green", new Vector3(-20f, 3.8f, -6f), new Color(0.35f, 0.95f, 0.5f), 2.0f, 18f); // Surgical Theatre - Clinical Cold White CreatePointLight(parent, "Light_Surgery_White", new Vector3(20f, 3.8f, -6f), new Color(0.85f, 0.92f, 1f), 2.2f, 18f); // Bio-Containment Vault - Deep Pulsing Red CreatePointLight(parent, "Light_BioContainment_Alarm", new Vector3(-20f, 3.8f, 17f), new Color(1f, 0.1f, 0.1f), 2.5f, 20f); // Central Core Bio-Reactor - Eerie Glowing Cyan/Emerald Spotlight from above CreatePointLight(parent, "Light_CentralReactor_Glow", new Vector3(0f, 3.8f, 17f), new Color(0.2f, 1f, 0.85f), 3.0f, 22f); // Genetics Lab - Blue / Violet CreatePointLight(parent, "Light_Genetics_Violet", new Vector3(20f, 3.8f, 12f), new Color(0.6f, 0.4f, 1f), 1.8f, 16f); // Generator Room - Warning Amber CreatePointLight(parent, "Light_Generator_Amber", new Vector3(20f, 3.8f, 25f), new Color(1f, 0.5f, 0.1f), 2.0f, 16f); // North Crawlway Corridor lights CreatePointLight(parent, "Light_NorthCrawl_West", new Vector3(-15f, 3.2f, 29.5f), new Color(1f, 0.7f, 0.3f), 1.2f, 10f); CreatePointLight(parent, "Light_NorthCrawl_Mid", new Vector3(0f, 3.2f, 29.5f), new Color(1f, 0.7f, 0.3f), 1.2f, 10f); CreatePointLight(parent, "Light_NorthCrawl_East", new Vector3(15f, 3.2f, 29.5f), new Color(1f, 0.7f, 0.3f), 1.2f, 10f); // Serpentine Corridor Waypoint lights CreatePointLight(parent, "Light_WestSerpentine", new Vector3(-10f, 3.5f, -8f), new Color(0.5f, 0.6f, 0.7f), 1.0f, 10f); CreatePointLight(parent, "Light_EastSerpentine", new Vector3(10f, 3.5f, -8f), new Color(0.5f, 0.6f, 0.7f), 1.0f, 10f); } 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.type = LightType.Point; light.color = color; light.intensity = intensity; light.range = range; light.shadows = LightShadows.Soft; } private static void SetupPatrolWaypoints(Transform parent, GameObject enemy) { // Clean up any old patrol points var oldPatrol = GameObject.Find("PatrolWaypoints"); if (oldPatrol != null) { Undo.DestroyObjectImmediate(oldPatrol); } // Also disable old 1 .. 1 (13) if present in scene root for (int i = 0; i <= 15; 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); // 20 Waypoints looping smoothly through all sections of the lab! Vector3[] waypoints = new Vector3[] { new Vector3(0f, 0f, -20f), // WP0: South Concourse new Vector3(-10f, 0f, -20f), // WP1: West Concourse new Vector3(-20f, 0f, -26f), // WP2: Cryo Archive new Vector3(-10f, 0f, -14f), // WP3: West Serpentine South new Vector3(-20f, 0f, -10f), // WP4: Biochem Lab South new Vector3(-20f, 0f, -2f), // WP5: Biochem Lab North new Vector3(-10f, 0f, 4f), // WP6: West Serpentine North new Vector3(-20f, 0f, 12f), // WP7: Bio-Containment South new Vector3(-20f, 0f, 22f), // WP8: Bio-Containment North new Vector3(-15f, 0f, 29.5f), // WP9: North Crawlway West new Vector3(0f, 0f, 29.5f), // WP10: North Crawlway Center new Vector3(15f, 0f, 29.5f), // WP11: North Crawlway East new Vector3(20f, 0f, 25f), // WP12: Generator Room new Vector3(20f, 0f, 12f), // WP13: Genetics Lab new Vector3(0f, 0f, 21f), // WP14: Central Core North new Vector3(0f, 0f, 13f), // WP15: Central Core South new Vector3(10f, 0f, 4f), // WP16: East Serpentine North new Vector3(20f, 0f, -6f), // WP17: Surgical Theatre new Vector3(10f, 0f, -14f), // WP18: East Serpentine South new Vector3(20f, 0f, -26f) // WP19: Security Office }; 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) { enemy.transform.position = new Vector3(0f, 0.5f, 17f); // Start mutant in Central Core var ai = enemy.GetComponent(); if (ai != null) { ai.patrolPoints = transforms; Debug.Log($"Configured {transforms.Length} patrol points on BlindMutantAI."); } } } }