PuzzleGame/PuzzleGameProject/Assets/Scripts/DungeonGenerator/DungeonGenerator.cs

257 lines
No EOL
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Unity.VisualScripting;
using UnityEngine;
using Random = System.Random;
namespace DungeonGenerator
{
public class DungeonGenerator
{
private const int SIDE_LENGTH_OF_MONSTER = 4;
private const int SIDE_LENGTH_OF_NORMAL = 2;
private const int WIDTH_OF_BOSS = 10;
private const int HEIGHT_OF_BOSS = 6;
private int _xLength = 40;
private int _yLength = 28;
private List<Room> _monsterRooms = new List<Room>();
private List<Room> _entranceRooms;
private Room _bossRoom;
public void GenerateDungeon(int length, float monsterRoomRatio)
{
_xLength = 40;
_yLength = 28;
Random random = new Random();
List<Point> pointsToExclude = new List<Point>();
_entranceRooms = GenerateEntranceRooms(_xLength, _yLength, random.Next(1,4));
pointsToExclude.AddRange(_entranceRooms.SelectMany(room => room.GetPointsInRoom()).ToList());
_bossRoom = GenerateOnlyBossRoom(_xLength, _yLength, WIDTH_OF_BOSS, HEIGHT_OF_BOSS);
pointsToExclude.AddRange(_bossRoom.GetPointsInRoom());
EvenDisperser disperser = new EvenDisperser(_xLength, _yLength, pointsToExclude); //TODO calculate L and W from length
int numberOfMonsterRooms = 7; // TODO: Calculate from ratio
for (var i = 0; i < numberOfMonsterRooms; i ++)
{
_monsterRooms.Add(disperser.GenerateAndPlaceRoom(
SIDE_LENGTH_OF_MONSTER,
SIDE_LENGTH_OF_MONSTER,
RoomType.Monster));
}
}
public void PrintMap()
{
List<Point> monsterRoomPoints = _monsterRooms.SelectMany(room => room.GetPointsInRoom()).ToList();
List<Point> entranceRoomPoints = _entranceRooms.SelectMany(room => room.GetPointsInRoom()).ToList();
char[,] mapMatrix = new Char[_xLength, _yLength];
for (int x = 0; x < _xLength; x++)
{
for (int y = 0; y < _yLength; y++)
{
if (entranceRoomPoints.Contains(new Point(x, y)))
{
mapMatrix[x, y] = '*';
}
else if (monsterRoomPoints.Contains(new Point(x,y)))
{
mapMatrix[x, y] = 'X';
}
else if (_bossRoom.GetPointsInRoom().Contains(new Point(x, y)))
{
mapMatrix[x, y] = 'B';
}
else
{
mapMatrix[x, y] = '-';
}
}
}
StringBuilder sb = new StringBuilder();
for (int j = 0; j < mapMatrix.GetLength(1); j++)
{
for (int i = 0; i < mapMatrix.GetLength(0); i++)
{
sb.Append(mapMatrix[i, j] + " ");
}
sb.Append(Environment.NewLine);
}
Debug.Log($"{DateTime.Now}{Environment.NewLine}" +
$"X Length: {_xLength}{Environment.NewLine}" +
$"Y Length: {_yLength}{Environment.NewLine}" +
$"Monster Rooms: {_monsterRooms.Count}{Environment.NewLine}" +
sb.ToString());
}
private Room GenerateOnlyBossRoom(int xLengthOfDungeon, int yLengthOfDungeon, int width, int heigth)
{
// Place monster room in the middle third
int middleAreaX = xLengthOfDungeon / 3;
int middleAreaY = yLengthOfDungeon / 3;
Random random = new Random();
int bossX = middleAreaX + random.Next(0, middleAreaX - width);
int bossY = middleAreaY + random.Next(0, middleAreaY - heigth);
return new Room(RoomType.Boss, width, heigth, new Point(bossX, bossY));
}
private enum Side
{
Top,
Right,
Bottom,
Left
}
private List<Room> GenerateEntranceRooms(int xLengthOfDungeon, int yLengthOfDungeon, int numberOfEntranceLines)
{
if (numberOfEntranceLines > 4)
{
throw new Exception("Number of entrance lines cannot be greater than 4");
}
const int numEntranceRooms = 12;
List<Room> entranceRooms = new List<Room>();
Random random = new Random();
Dictionary<Side, int> sidesToEnterOn = new()
{
{ Side.Top, 0},
{ Side.Right, 0},
{ Side.Bottom, 0},
{ Side.Left, 0}
};
// Randomly assign starting lines to sides of dungeon
for (var i = 0; i < numberOfEntranceLines; i++)
{
while (true)
{
Side side = (Side)random.Next(0, 4);
if (sidesToEnterOn[side] < 2)
{
sidesToEnterOn[side] += 1;
break;
}
}
}
int roomsPerLine = numEntranceRooms / numberOfEntranceLines;
int bottom = yLengthOfDungeon - SIDE_LENGTH_OF_NORMAL;
int oneRoomFromBottom = bottom - SIDE_LENGTH_OF_NORMAL;
int right = xLengthOfDungeon - SIDE_LENGTH_OF_NORMAL;
// Generate entrance lines on each side
switch (sidesToEnterOn[Side.Top])
{
case 2:
// Add half of points for this entrance line to the top left side
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(i,0)));
}
// Add the rest of the points for this entrance line to the top right side.
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(right - i,0)));
}
break;
case 1:
int startOfLine = GetStartOfCenteredLine(xLengthOfDungeon, roomsPerLine, SIDE_LENGTH_OF_NORMAL);
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL,new Point(startOfLine + i, 0)));
}
break;
}
switch (sidesToEnterOn[Side.Right])
{
case 2:
// Add points for this entrance line to the top right side
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(right, i + SIDE_LENGTH_OF_NORMAL)));
}
// Add the rest of the points for this entrance line to the bottom right side.
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(right, oneRoomFromBottom - i)));
}
break;
case 1:
int startOfLine = GetStartOfCenteredLine(yLengthOfDungeon, roomsPerLine, SIDE_LENGTH_OF_NORMAL);
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(right, startOfLine + i)));
}
break;
}
switch (sidesToEnterOn[Side.Bottom])
{
case 2:
// Add half of points for this entrance line to the bottom left side
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(i, bottom)));
}
// Add the rest of the points for this entrance line to the bottom right side.
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point((xLengthOfDungeon - 1) - i, bottom)));
}
break;
case 1:
int startOfLine = GetStartOfCenteredLine(xLengthOfDungeon, roomsPerLine, SIDE_LENGTH_OF_NORMAL);
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(startOfLine + i, bottom)));
}
break;
}
switch (sidesToEnterOn[Side.Left])
{
case 2:
// Add half of points for this entrance line to the top left side
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(0, i + SIDE_LENGTH_OF_NORMAL)));
}
// Add the rest of the points for this entrance line to the bottom left side.
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(0, oneRoomFromBottom - i)));
}
break;
case 1:
int startOfLine = GetStartOfCenteredLine(yLengthOfDungeon, roomsPerLine, SIDE_LENGTH_OF_NORMAL);
for (var i = 0; i < roomsPerLine * SIDE_LENGTH_OF_NORMAL; i += SIDE_LENGTH_OF_NORMAL)
{
entranceRooms.Add(new Room(RoomType.Entrance, SIDE_LENGTH_OF_NORMAL, SIDE_LENGTH_OF_NORMAL, new Point(0, startOfLine + i)));
}
break;
}
return entranceRooms;
}
private int GetStartOfCenteredLine(int length, int numberOfRooms, int sizeOfRoom)
{
int midpoint = length / 2;
return midpoint - (numberOfRooms * sizeOfRoom / 2);
}
}
}