ZeroVR/ZeroPacientVR/Assets/CodeRespawn/DungeonArchitect/Scripts/Builders/Maze/MazeDungeonBuilder.cs

187 lines
6.6 KiB
C#

//$ Copyright 2015-22, Code Respawn Technologies Pvt Ltd - All Rights Reserved $//
using System.Collections.Generic;
using UnityEngine;
namespace DungeonArchitect.Builders.Maze
{
public static class MazeDungeonMarkerNames
{
public static readonly string GroundBlock = "GroundBlock";
public static readonly string WallBlock = "WallBlock";
}
public class MazeDungeonBuilder : DungeonBuilder {
MazeDungeonConfig MazeConfig;
MazeDungeonModel MazeModel;
new System.Random random;
/// <summary>
/// Builds the dungeon layout. In this method, you should build your dungeon layout and save it in your model file
/// No markers should be emitted here. (EmitMarkers function will be called later by the engine to do that)
/// </summary>
/// <param name="config">The builder configuration</param>
/// <param name="model">The dungeon model that the builder will populate</param>
public override void BuildDungeon(DungeonConfig config, DungeonModel model)
{
base.BuildDungeon(config, model);
random = new System.Random((int)config.Seed);
// We know that the dungeon prefab would have the appropriate config and models attached to it
// Cast and save it for future reference
MazeConfig = config as MazeDungeonConfig;
MazeModel = model as MazeDungeonModel;
MazeModel.Config = MazeConfig;
// Generate the city layout and save it in a model. No markers are emitted here.
GenerateLevelLayout();
}
/// <summary>
/// Override the builder's emit marker function to emit our own markers based on the layout that we built
/// You should emit your markers based on the layout you have saved in the model generated previously
/// When the user is designing the theme interactively, this function will be called whenever the graph state changes,
/// so the theme engine can populate the scene (BuildDungeon will not be called if there is no need to rebuild the layout again)
/// </summary>
public override void EmitMarkers()
{
base.EmitMarkers();
EmitLevelMarkers();
ProcessMarkerOverrideVolumes();
}
bool IsVisited(bool[,] visited, int x, int y)
{
if (x < 0 || y < 0 || x >= visited.GetLength(0) || y >= visited.GetLength(1)) return true;
return visited[x, y];
}
bool CanDigToPoint(IntVector2 point, bool[,] visited)
{
if (IsVisited(visited, point.x, point.y))
{
// Already visited
return false;
}
int neighborPathways = 0;
neighborPathways += IsVisited(visited, point.x - 1, point.y + 0) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x + 1, point.y + 0) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x + 0, point.y + 1) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x + 0, point.y - 1) ? 1 : 0;
/*
neighborPathways += IsVisited(visited, point.x - 1, point.y + 1) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x + 1, point.y + 1) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x - 1, point.y - 1) ? 1 : 0;
neighborPathways += IsVisited(visited, point.x + 1, point.y - 1) ? 1 : 0;
*/
return neighborPathways <= 1;
}
bool GetNextNeighbor(IntVector2 currentPoint, out IntVector2 nextPoint, bool[,] visited)
{
var offsets = new List<IntVector2>
{
new IntVector2(-1, 0),
new IntVector2(1, 0),
new IntVector2(0, -1),
new IntVector2(0, 1)
};
while (offsets.Count > 0)
{
int i = random.Next() % offsets.Count;
var offset = offsets[i];
offsets.RemoveAt(i);
if (CanDigToPoint(currentPoint + offset, visited))
{
nextPoint = currentPoint + offset;
return true;
}
}
nextPoint = currentPoint;
return false;
}
void GenerateLevelLayout()
{
MazeConfig.mazeWidth = Mathf.Max(MazeConfig.mazeWidth, 6);
MazeConfig.mazeHeight = Mathf.Max(MazeConfig.mazeHeight, 6);
int w = MazeConfig.mazeWidth;
int h = MazeConfig.mazeHeight;
var visited = new bool[MazeConfig.mazeWidth, MazeConfig.mazeHeight];
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
//bool boundary = (x == 0 || y == 0 || x == w - 1 || y == h - 1);
visited[x, y] = false;
}
}
var stack = new Stack<IntVector2>();
{
var startPoint = new IntVector2(0, 2 + random.Next() % (MazeConfig.mazeHeight - 4));
visited[startPoint.x, startPoint.y] = true;
stack.Push(startPoint);
}
while (stack.Count > 0)
{
var currentPoint = stack.Peek();
IntVector2 nextPoint;
if (GetNextNeighbor(currentPoint, out nextPoint, visited))
{
visited[nextPoint.x, nextPoint.y] = true;
stack.Push(nextPoint);
}
else
{
stack.Pop();
}
}
// Fill in the model
MazeModel.tileStates = new MazeTileState[w, h];
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
MazeModel.tileStates[x, y] = visited[x, y] ? MazeTileState.Empty : MazeTileState.Blocked;
}
}
}
void EmitLevelMarkers()
{
var gridSize = new Vector3(MazeConfig.gridSize.x, 0, MazeConfig.gridSize.y);
int w = MazeConfig.mazeWidth;
int h = MazeConfig.mazeHeight;
for (int x = 0; x < w; x++)
{
for (int y = 0; y < h; y++)
{
var position = Vector3.Scale(new Vector3(x + 0.5f, 0, y + 0.5f), gridSize);
var markerTransform = Matrix4x4.TRS(position, Quaternion.identity, Vector3.one);
var markerName = MazeModel.tileStates[x, y] == MazeTileState.Blocked
? MazeDungeonMarkerNames.WallBlock
: MazeDungeonMarkerNames.GroundBlock;
EmitMarker(markerName, markerTransform, new IntVector(x, 0, y), -1);
}
}
}
}
}