using System.Collections; using System.Collections.Generic; using Hallucinate.GameSetup.Maze.Extensions; using Hallucinate.GameSetup.Maze.Native; using UnityEngine; namespace Hallucinate.GameSetup.Maze { /// /// Advanced generator that combines C++ Native Noise with a Recursive Backtracking algorithm. /// Creates a hybrid layout of large rooms and chaotic corridors. /// public class NoiseRecursiveGenerator : IMazeAlgorithm { private readonly List _directions = MapLocation.Directions; private float[] _noiseMap; private int _seed = 1337; // Thresholds private const float RoomThreshold = 0.5f; private const float CorridorThreshold = -0.3f; private const int DeadEndNeighbourThreshold = 2; public void Generate(MazeGrid grid) { InitializeNoise(grid); // Step 1: Pre-place rooms based on noise peaks for (int z = 0; z < grid.Depth; z++) { for (int x = 0; x < grid.Width; x++) { float noise = GetNoiseAt(x, z, grid.Width); if (noise > RoomThreshold) { grid.SetCell(x, z, MazeCellType.Corridor); } } } // Step 2: Run recursive carving in the "connectable" zones // We start from a few points to ensure coverage for (int i = 0; i < 5; i++) { int startX = Random.Range(1, grid.Width - 1); int startZ = Random.Range(1, grid.Depth - 1); GenerateRecursive(grid, startX, startZ); } } public IEnumerator GenerateStepByStep(MazeGrid grid, float interval) { InitializeNoise(grid); // Visual feedback for noise pre-placement for (int z = 0; z < grid.Depth; z++) { for (int x = 0; x < grid.Width; x++) { float noise = GetNoiseAt(x, z, grid.Width); if (noise > RoomThreshold) { grid.SetCell(x, z, MazeCellType.Processing); } } if (z % 5 == 0) yield return null; } for (int z = 0; z < grid.Depth; z++) { for (int x = 0; x < grid.Width; x++) { if (grid.GetCell(x, z) == MazeCellType.Processing) grid.SetCell(x, z, MazeCellType.Corridor); } } yield return GenerateRecursiveStepByStep(grid, 5, 5, interval); } private void InitializeNoise(MazeGrid grid) { _noiseMap = new float[grid.Width * grid.Depth]; using (var provider = new NativeNoiseProvider(_seed, 0.05f)) { if (provider.IsInitialized) { provider.FillBuffer(0, 0, grid.Width, grid.Depth, _noiseMap); } else { // Fallback to Unity Perlin for (int z = 0; z < grid.Depth; z++) { for (int x = 0; x < grid.Width; x++) { _noiseMap[z * grid.Width + x] = Mathf.PerlinNoise(x * 0.1f, z * 0.1f) * 2f - 1f; } } } } } private float GetNoiseAt(int x, int z, int width) { if (_noiseMap == null) return 0f; return _noiseMap[z * width + x]; } private void GenerateRecursive(MazeGrid grid, int x, int z) { // Boundary and Noise check if (!grid.IsInBounds(x, z)) return; if (GetNoiseAt(x, z, grid.Width) < CorridorThreshold) return; if (grid.GetCell(x, z) == MazeCellType.Corridor) return; if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) return; grid.SetCell(x, z, MazeCellType.Corridor); List shuffledDirs = new List(_directions); shuffledDirs.Shuffle(); foreach (var dir in shuffledDirs) { GenerateRecursive(grid, x + dir.x, z + dir.z); } } private IEnumerator GenerateRecursiveStepByStep(MazeGrid grid, int x, int z, float interval) { if (!grid.IsInBounds(x, z)) yield break; if (GetNoiseAt(x, z, grid.Width) < CorridorThreshold) yield break; if (grid.GetCell(x, z) == MazeCellType.Corridor) yield break; if (grid.CountSquareNeighbours(x, z, MazeCellType.Corridor) >= DeadEndNeighbourThreshold) yield break; grid.SetCell(x, z, MazeCellType.Processing); if (interval > 0) yield return new WaitForSeconds(interval); grid.SetCell(x, z, MazeCellType.Corridor); List shuffledDirs = new List(_directions); shuffledDirs.Shuffle(); foreach (var dir in shuffledDirs) { yield return GenerateRecursiveStepByStep(grid, x + dir.x, z + dir.z, interval); } } } }