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 Rooms { get; set; } [Export] public Node2D SceneContainer { get; set; } private Godot.Collections.Dictionary _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 { 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(room.ScenePath); var spawnedScene = this.CreateChild(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; } } } }