diff options
Diffstat (limited to 'scripts/map/DungeonGenerator.cs')
| -rw-r--r-- | scripts/map/DungeonGenerator.cs | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/scripts/map/DungeonGenerator.cs b/scripts/map/DungeonGenerator.cs index ebb7a3d..1da7e16 100644 --- a/scripts/map/DungeonGenerator.cs +++ b/scripts/map/DungeonGenerator.cs @@ -1,26 +1,51 @@ using Godot; +/// <summary> +/// A classe dungeonGenerator cria exatamente um andar da masmorra. +/// Ela é chamada quando necessário. +/// </summary> public partial class DungeonGenerator : Node { + /// <summary> + /// Coleção de todos os inimigos que o gerador tem acesso. + /// </summary> private static readonly Godot.Collections.Array<EnemyDefinition> enemies = [ GD.Load<EnemyDefinition>("res://assets/definitions/actor/Skeleton.tres") ]; + /// <summary> + /// Dimensões do mapa a ser criado. + /// </summary> [ExportCategory("Dimension")] [Export] private int width = 80; [Export] private int height = 60; + /// <summary> + /// Gerador de números aleatórios + /// </summary> [ExportCategory("RNG")] private RandomNumberGenerator rng = new(); + /// <summary> + /// Qual seed utilizar. + /// </summary> [Export] private ulong seed; + /// <summary> + /// Se será utilizada a nossa seed ou a seed padrão da classe RandomNumberGenerator. + /// </summary> [Export] private bool useSeed = true; + /// <summary> + /// Quantas iterações do algoritmo chamar. + /// </summary> [Export] private int iterations = 3; + /// <summary> + /// Quantidade máxima de inimigos por sala. + /// </summary> [ExportCategory("Monster RNG")] [Export] private int maxMonsterPerRoom = 2; @@ -33,6 +58,11 @@ public partial class DungeonGenerator : Node } } + /// <summary> + /// Transforma o tile da posição especificada em chão. + /// </summary> + /// <param name="data">o mapa</param> + /// <param name="pos">posição para colocar o chão.</param> private static void CarveTile(MapData data, Vector2I pos) { Tile tile = data.GetTile(pos); @@ -41,6 +71,11 @@ public partial class DungeonGenerator : Node tile.SetDefinition(MapData.floorDefinition); } + /// <summary> + /// Preenche uma área retangular com chão. + /// </summary> + /// <param name="data">O mapa</param> + /// <param name="room">Área para preencher com chão</param> private static void CarveRoom(MapData data, Rect2I room) { for (int y = room.Position.Y; y < room.End.Y; y++) @@ -52,25 +87,34 @@ public partial class DungeonGenerator : Node } } + /// <summary> + /// Gera um andar da masmorra. + /// Inimigos são colocados conforme configurações. + /// O jogador é colocado na primeira sala gerada. + /// </summary> + /// <param name="player">Jogador.</param> + /// <returns>O mapa gerado.</returns> public MapData GenerateDungeon(Player player) { MapData data = new MapData(width, height, player); - data.InsertActor(player); - player.Map_Data = data; - + // Divisão mestre que engloba o mapa inteiro. MapDivision root = new MapDivision(0, 0, width, height); + // Chama o algoritmo para dividir o mapa. root.Split(iterations, rng); bool first = true; + // Coloca os corredores. TunnelDivisions(data, root); + // Cria as salas com base nas divisões geradas. foreach(MapDivision division in root.GetLeaves()) { Rect2I room = new(division.Position, division.Size); + // A sala não pode oculpar a divisão inteira, senão não haveriam paredes. room = room.GrowIndividual( -rng.RandiRange(1, 2), -rng.RandiRange(1, 2), @@ -78,28 +122,40 @@ public partial class DungeonGenerator : Node -rng.RandiRange(1, 2) ); + // De fato cria a sala. CarveRoom(data, room); + // Colocamos o jogador na primeira sala. if (first) { first = false; player.GridPosition = room.GetCenter(); } + // Colocamos os inimigos na sala. PlaceEntities(data, room); } + // Feito o mapa, inicializamos o algoritmo de pathfinding. data.SetupPathfinding(); return data; } + /// <summary> + /// Popula uma sala com inimigos. + /// </summary> + /// <param name="data">O mapa</param> + /// <param name="room">A sala.</param> private void PlaceEntities(MapData data, Rect2I room) { + // Define quantos monstros serão colocados na sala int monsterAmount = rng.RandiRange(0, maxMonsterPerRoom); for (int i = 0; i < monsterAmount; i++) { + // Escolhe um lugar aleatório na sala. Vector2I position = new( rng.RandiRange(room.Position.X, room.End.X - 1), rng.RandiRange(room.Position.Y, room.End.Y - 1) ); + // Só podemos colocar um ator por ponto no espaço. bool canPlace = true; foreach (Actor actor in data.Actors) { if (actor.GridPosition == position) { @@ -108,6 +164,7 @@ public partial class DungeonGenerator : Node } } + // Se possível, criamos um inimigo aleatório na posição escolhida. if (canPlace) { EnemyDefinition definition = enemies.PickRandom(); Enemy enemy = new Enemy(position, data, definition); @@ -116,6 +173,13 @@ public partial class DungeonGenerator : Node } } + /// <summary> + /// Preenche uma linha horizontal com chão. + /// </summary> + /// <param name="data">O mapa</param> + /// <param name="y">Eixo y do corredor.</param> + /// <param name="xBegin">Início do corredor</param> + /// <param name="xEnd">Final do corredor.</param> private static void HorizontalCorridor(MapData data, int y, int xBegin, int xEnd) { int begin = (xBegin < xEnd) ? xBegin : xEnd; int end = (xEnd > xBegin) ? xEnd : xBegin; @@ -124,6 +188,13 @@ public partial class DungeonGenerator : Node } } + /// <summary> + /// Preenche uma linha vertical com chão. + /// </summary> + /// <param name="data">O mapa.</param> + /// <param name="x">Eixo x do corredor.</param> + /// <param name="yBegin">Início do corredor</param> + /// <param name="yEnd">Final do corredor.</param> private static void VerticalCorridor(MapData data, int x, int yBegin, int yEnd) { int begin = (yBegin < yEnd) ? yBegin : yEnd; int end = (yEnd > yBegin) ? yEnd : yBegin; @@ -132,11 +203,22 @@ public partial class DungeonGenerator : Node } } + /// <summary> + /// Cria corredores vertical e horizontal para unir dois pontos no mapa. + /// </summary> + /// <param name="data">O mapa</param> + /// <param name="start">Ponto inicial</param> + /// <param name="end">Ponto final.</param> private void TunnelBetween(MapData data, Vector2I start, Vector2I end) { HorizontalCorridor(data, start.Y, start.X, end.X); VerticalCorridor(data, end.X, start.Y, end.Y); } + /// <summary> + /// Cria recursivamente corredores entre o centro de cada divisão do mapa. + /// </summary> + /// <param name="data">O mapa</param> + /// <param name="root">Divisão mestre.</param> private void TunnelDivisions(MapData data, MapDivision root) { if (root.IsLeaf) { return; |
