cirnogodot/Scripts/Controllers/RogueliteRoomManager.cs
2025-04-11 18:39:39 +02:00

162 lines
No EOL
4.8 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Cirno.Scripts.Enums;
using Cirno.Scripts.Resources.Roguelite;
using Godot;
using Godot.Collections;
namespace Cirno.Scripts.Controllers;
public partial class RogueliteRoomManager : Node2D
{
[Export] public Array<RogueliteRoomResource> Rooms { get; set; }
[Export] public Node2D SceneContainer { get; set; }
private Godot.Collections.Dictionary<Vector2I, RogueliteRoomResource> _grid = new();
[Export] public int DungeonWidth = 10;
[Export] public int DungeonHeight = 10;
[Export] public int MaxRooms = 12;
//[Export] public int Seed = -1;
[Export] public Vector2I TileSize { get; set; } = new Vector2I(16, 16);
[Export] public Vector2I RoomSizeInTiles { get; set; } = new Vector2I(20, 10);
public override void _Ready()
{
// Spawn first room
}
public void InitSpawning()
{
SpawnRoomsBinaryTree();
}
private void SpawnRoomsGrid()
{
//var firstRoom = Rooms.FirstOrDefault();
var origin = Vector2.Zero;
var tileSize = new Vector2(16, 16);
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
var roomIndex = GD.RandRange(0, Rooms.Count - 1);
var room = Rooms[roomIndex];
//SpawnRoom(room, origin + (room.Size * new Vector2(i, j) * tileSize));
}
}
//CallDeferred(MethodName.RebakeNavigationDeferred);
}
private void SpawnRoomsBinaryTree()
{
var directions = new List<Vector2I>
{
new Vector2I(0, -1), // North
new Vector2I(0, 1), // South
new Vector2I(1, 0), // East
new Vector2I(-1, 0), // West
};
var origin = Vector2I.Zero;
var tileSize = new Vector2(16, 16);
var gridRoomSizeInTiles = new Vector2(20, 10);
var starterRooms = Rooms.Where(x => x.Type is RoomType.Starter).ToList();
var regularRooms = Rooms.Where(x => x.Type == RoomType.Regular).ToList();
var starterRoom = starterRooms[GD.RandRange(0, starterRooms.Count - 1)];
var spawnedStartRoom = SpawnRoom(starterRoom, origin);
MarkRoom(origin, starterRoom.Size, starterRoom);
Queue<(Vector2I Position, RogueliteRoomResource Room)> queue = new();
queue.Enqueue((origin, starterRoom));
int spawnedRoomCount = 1;
while (spawnedRoomCount < MaxRooms && queue.Count > 0)
{
var (currentPos, currentRoom) = queue.Dequeue();
foreach (var dir in directions)
{
var offsetPos = currentPos + dir;
// Avoid placing rooms in occupied space
if (_grid.ContainsKey(offsetPos)) continue;
var nextRoom = regularRooms[GD.RandRange(0, regularRooms.Count - 1)];
var roomSize = nextRoom.Size;
// Multi-tile room placement validation
if (!CanPlaceRoom(offsetPos, roomSize)) continue;
SpawnRoom(nextRoom, offsetPos);
MarkRoom(offsetPos, roomSize, nextRoom);
queue.Enqueue((offsetPos, nextRoom));
spawnedRoomCount++;
if (spawnedRoomCount >= MaxRooms)
break;
}
}
//SpawnSpecialRooms(rng);
}
private void RebakeNavigationDeferred()
{
GameManager.Instance.RebakeNavigation();
}
private RogueliteRoom SpawnRoom(RogueliteRoomResource room, Vector2I gridPos)
{
var position = gridPos * TileSize * RoomSizeInTiles;
var roomScene = GD.Load<PackedScene>(room.ScenePath);
var spawnedScene = this.CreateChild<RogueliteRoom>(roomScene, position);
// for reference
//SpawnRoom(room, origin + (room.Size * new Vector2(i, j) * tileSize));
return spawnedScene.Spawn(CheckConnection);
}
private bool CheckConnection(Vector2I pos)
{
return _grid.ContainsKey(pos); // <- this is your dungeon grid dictionary
}
private bool CanPlaceRoom(Vector2I origin, Vector2I size)
{
for (int x = 0; x < size.X; x++)
{
for (int y = 0; y < size.Y; y++)
{
var pos = origin + new Vector2I(x, y);
if (_grid.ContainsKey(pos)) return false;
}
}
return true;
}
private void MarkRoom(Vector2I origin, Vector2I size, RogueliteRoomResource room)
{
for (int x = 0; x < size.X; x++)
{
for (int y = 0; y < size.Y; y++)
{
var pos = origin + new Vector2I(x, y);
_grid[pos] = room;
}
}
}
}