From 7482cfa4968145cbb21ea17199eb793e3d027831 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 30 Apr 2025 10:30:43 +0200 Subject: [PATCH] Use queues for room generation --- Cirno.csproj | 2 +- Resources/Enemies/Boss_1.tres | 2 +- Scripts/Controllers/RogueliteRoomManager.cs | 190 +++++++++++++------- 3 files changed, 128 insertions(+), 66 deletions(-) diff --git a/Cirno.csproj b/Cirno.csproj index ff0cdeff..afde3cb6 100644 --- a/Cirno.csproj +++ b/Cirno.csproj @@ -1,4 +1,4 @@ - + net8.0 true diff --git a/Resources/Enemies/Boss_1.tres b/Resources/Enemies/Boss_1.tres index 8f23366a..dea8d94d 100644 --- a/Resources/Enemies/Boss_1.tres +++ b/Resources/Enemies/Boss_1.tres @@ -13,7 +13,7 @@ region = Rect2(0, 0, 64, 64) script = ExtResource("9_vykx5") EnemyName = &"Boss 1" EnemyKey = &"BOSS_1" -PrefabPath = &"uid://dt7i3x3g5ktbl" +PrefabPath = &"uid://clyrne78j3f5a" MaxHealth = 100.0 MovementSpeed = 40.0 Weapon = ExtResource("8_w06jt") diff --git a/Scripts/Controllers/RogueliteRoomManager.cs b/Scripts/Controllers/RogueliteRoomManager.cs index 5a03bb22..877e023e 100644 --- a/Scripts/Controllers/RogueliteRoomManager.cs +++ b/Scripts/Controllers/RogueliteRoomManager.cs @@ -15,7 +15,7 @@ namespace Cirno.Scripts.Controllers; public partial class RogueliteRoomManager : Node2D { //[Export] public Array Rooms { get; set; } - + [Export] public RogueliteMapTheme MapTheme { get; set; } //private Godot.Collections.Dictionary _grid = new(); @@ -44,8 +44,8 @@ public partial class RogueliteRoomManager : Node2D [Export] public int MaxShops = 1; [Export] public string ManualSeed { get; private set; } - private ulong _seed; - + private ulong _seed; + [Export] public Vector2I TileSize { get; set; } = new Vector2I(16, 16); [Export] public Vector2I RoomSizeInTiles { get; set; } = new Vector2I(20, 10); @@ -108,13 +108,14 @@ public partial class RogueliteRoomManager : Node2D _seed = rng.GetSeed(); rng.Dispose(); } + GD.Print($"Seed: {_seed}"); } - + private void GenerateStraightLineDungeon() { SetSeed(); - + MapTheme.MakeChestLootQueue(); MapTheme.TeleportersList = []; @@ -122,9 +123,13 @@ public partial class RogueliteRoomManager : Node2D var starterRoom = MapTheme.Rooms.Where(r => r.Type == RoomType.Starter).PickRandom(); SpawnRoom(starterRoom, origin, out var spawnedBeginRoom); - var randomRoomsList = RegularRooms.Shuffle(MaxRooms * 4).ToList(); + var randomRoomsList = RegularRooms.Shuffle().ToList(); - var randomOffshootRoomsList = OffshootRooms.Shuffle(DungeonLength * 4).ToList(); + var regularRoomsQueue = new Queue().EnqueueRange(randomRoomsList); + + var randomOffshootRoomsList = OffshootRooms.Shuffle().ToList(); + + var offshootRoomsQueue = new Queue().EnqueueRange(randomOffshootRoomsList); var currentPos = spawnedBeginRoom.RandomBottomExit(); _connections.Add(new RoomConnection(origin, currentPos + new Vector2I(0, 1))); @@ -139,29 +144,40 @@ public partial class RogueliteRoomManager : Node2D var shuffledOffshoots = offshoots.Shuffle().ToList(); - - - // var offshootsQueue = new Queue(); - // offshootsQueue.EnqueueRange(shuffledOffshoots); - + var offshootsQueue = new Queue(); + offshootsQueue.EnqueueRange(shuffledOffshoots); + int currentOffshoot = 0; RogueliteRoom lastRoom = spawnedBeginRoom; bool lockNext = false; - + for (int i = 0; i < DungeonLength; i++) { GD.Print($"Dungeon room {i}"); - var randRoomStartIndex = SpawnedRooms.Count(x => x.RoomResource.Type is RoomType.Regular && x.RoomResource.HasDoors(DoorDirections.North | DoorDirections.South)); + // if (!regularRoomsQueue.TryDequeue(out var roomToSpawn)) + // { + // GD.Print("Ran out of regular rooms, add more"); + // return; + // } - var spawnedRoom = TrySpawnRoom(randomRoomsList.Take(new Range(randRoomStartIndex, randomRoomsList.Count - 1)).ToList(), + // var randRoomStartIndex = SpawnedRooms.Count(x => + // x.RoomResource.Type is RoomType.Regular && + // x.RoomResource.HasDoors(DoorDirections.North | DoorDirections.South)); + + // var spawnedRoom = TrySpawnRoom( + // randomRoomsList.Take(new Range(randRoomStartIndex, randomRoomsList.Count - 1)).ToList(), + // lastRoom, Direction.Down); + + var spawnedRoom = TrySpawnRoom(regularRoomsQueue, lastRoom, Direction.Down); if (spawnedRoom is null) { - continue; + GD.Print("Abort creation"); + return; } if (lockNext) @@ -171,9 +187,9 @@ public partial class RogueliteRoomManager : Node2D } lastRoom = spawnedRoom; - + // Spawn offshoot here - + // Roll whether to go left or right, if direction is full go the other, if both are full do not spawn var directions = new List(); @@ -182,27 +198,40 @@ public partial class RogueliteRoomManager : Node2D if (lastRoom.RoomResource.DoorDirections.HasFlag(DoorDirections.West)) directions.Add(Direction.Left); directions = directions.Shuffle().ToList(); - + foreach (var direction in directions) { - var randOffshootStartIndex = SpawnedRooms.Count(x => x.RoomResource.Type is RoomType.Regular && x.RoomResource.HasDoors(DoorDirections.West | DoorDirections.East)); - - var offshootTypeToSpawn = shuffledOffshoots[currentOffshoot % shuffledOffshoots.Count()]; - + // var randOffshootStartIndex = SpawnedRooms.Count(x => + // x.RoomResource.Type is RoomType.Regular && + // x.RoomResource.HasDoors(DoorDirections.West | DoorDirections.East)); + + //var offshootTypeToSpawn = shuffledOffshoots[currentOffshoot % shuffledOffshoots.Count()]; + + if (!offshootsQueue.TryDequeue(out var offshootTypeToSpawn)) + { + GD.Print("Ran out of offshoot types, add more"); + break; + } + int roomsInOffshot = offshootTypeToSpawn is RoomType.Secret or RoomType.Shop ? 0 : GD.RandRange(0, MaxBranchLength); - var roomsForOffshoot = randomOffshootRoomsList - .Take(new Range(randOffshootStartIndex, randomOffshootRoomsList.Count - 1)).ToList(); + // var roomsForOffshoot = randomOffshootRoomsList + // .Take(new Range(randOffshootStartIndex, randomOffshootRoomsList.Count - 1)).ToList(); + + // This one reshuffles improperly, good for now + var endRooms = + new Queue().EnqueueRange(MapTheme.Rooms + .Where(x => x.Type == offshootTypeToSpawn).ToList()); - var res = SpawnOffshoot(lastRoom, direction, offshootTypeToSpawn, roomsInOffshot, roomsForOffshoot); + var res = SpawnOffshoot(lastRoom, direction, offshootTypeToSpawn, roomsInOffshot, offshootRoomsQueue, endRooms); if (res) { currentOffshoot++; // Try to spawn one the other direction too - + // If key, lock the last connection if (offshootTypeToSpawn is RoomType.Key) { @@ -210,8 +239,9 @@ public partial class RogueliteRoomManager : Node2D // Need to lock the next connection lockNext = true; } - - if (offshootTypeToSpawn is RoomType.Key && shuffledOffshoots[currentOffshoot % shuffledOffshoots.Count()] is RoomType.Key) + + if (offshootTypeToSpawn is RoomType.Key && + offshootsQueue.Peek() is RoomType.Key) { // Stop if next room is a key break; @@ -230,13 +260,15 @@ public partial class RogueliteRoomManager : Node2D var bossRoom = BossRooms.PickRandom(); - var spawnedBossRoom = TrySpawnRoom(BossRooms.ToList(), lastRoom, Direction.Down); + var bossQueue = new Queue().EnqueueRange(BossRooms.Shuffle()); + + var spawnedBossRoom = TrySpawnRoom(bossQueue, lastRoom, Direction.Down); if (spawnedBossRoom is null) { GD.PrintErr($"Could not spawn boss room {bossRoom}"); } - + if (lockNext) { _connections.Last().IsLocked = true; @@ -253,7 +285,7 @@ public partial class RogueliteRoomManager : Node2D //return _roomGrid.ContainsKey(neighborPos); }); } - + // Debug teleporter matching MatchBossTeleporter(); @@ -265,13 +297,13 @@ public partial class RogueliteRoomManager : Node2D private void MatchBossTeleporter() { var bossTeleporters = MapTheme.TeleportersList.Where(x => x.Type is TeleporterMarkerType.Boss); - + foreach (var teleporterMarker in bossTeleporters) { var bossRoom = SpawnedRooms.FirstOrDefault(x => x.RoomResource.Type is RoomType.Boss); if (bossRoom is null) return; - + var teleporter = bossRoom.Teleporters.FirstOrDefault(x => x.Type is TeleporterMarkerType.Receiver or TeleporterMarkerType.InvisibleReceiver); @@ -281,7 +313,7 @@ public partial class RogueliteRoomManager : Node2D } } - private RogueliteRoom TrySpawnRoom(List roomsList, RogueliteRoom lastRoom, + private RogueliteRoom TrySpawnRoom(Queue roomsList, RogueliteRoom lastRoom, Direction direction) { //var nextPos = originPos + new Vector2I(0, 1); @@ -297,16 +329,22 @@ public partial class RogueliteRoomManager : Node2D _ => exitPosition }; - var rooms = roomsList; //.Shuffle().ToList(); - - for (int i = 0; i < rooms.Count(); i++) + //var rooms = roomsList; //.Shuffle().ToList(); + int tries = 0; + while (roomsList.Count > 0 && tries < 10) { - var roomToSpawn = rooms[i % rooms.Count()]; - + if (!roomsList.TryDequeue(out var roomToSpawn)) + { + GD.Print("Ran out of regular rooms, add more"); + return null; + } + var spawnedRoom = TrySpawnRoom(roomToSpawn, nextPos, direction); if (spawnedRoom is null) { GD.PrintErr($"Could not spawn room {roomToSpawn} at {nextPos}"); + roomsList.Enqueue(roomToSpawn); + tries++; continue; } @@ -384,41 +422,65 @@ public partial class RogueliteRoomManager : Node2D } private bool SpawnOffshoot(RogueliteRoom lastRoom, Direction direction, RoomType offshootTypeToSpawn, - int roomsInOffshot, List randomOffshootRoomsList) + int roomsInOffshot, Queue randomOffshootRoomsList, Queue endRoomsQueue) { RogueliteRoom lastSpawnedOffshootRoom = lastRoom; - for (int j = 0; j < roomsInOffshot; j++) + + int tries = 0; + int spawned = 0; + while (randomOffshootRoomsList.Count > 0 && tries < 10 && spawned < roomsInOffshot) { - var shuffledOffshootRoomsList = randomOffshootRoomsList;//.Shuffle().ToList(); - - foreach (var shuffledOffshoot in shuffledOffshootRoomsList) + var spawnedRoom = TrySpawnRoom(randomOffshootRoomsList, lastSpawnedOffshootRoom, direction); + + if (spawnedRoom is null) { - var spawnedRoom = TrySpawnRoom(shuffledOffshootRoomsList, lastSpawnedOffshootRoom, direction); - - if (spawnedRoom is null) - { - GD.Print($"Could not place offshoot {shuffledOffshoot}"); - // Try next in list - continue; - } - - lastSpawnedOffshootRoom = spawnedRoom; - break; - } - - // Nope no offshoot - if (lastSpawnedOffshootRoom is null) - { - return false; + // GD.Print($"Could not place offshoot {shuffledOffshoot}"); + // Try next in list + tries++; + continue; } + lastSpawnedOffshootRoom = spawnedRoom; + //roomsInOffshot++; + spawned++; } + + if (lastSpawnedOffshootRoom is null) + { + return false; + } + + // for (int j = 0; j < roomsInOffshot; j++) + // { + // var shuffledOffshootRoomsList = randomOffshootRoomsList; //.Shuffle().ToList(); + // + // foreach (var shuffledOffshoot in shuffledOffshootRoomsList) + // { + // var spawnedRoom = TrySpawnRoom(shuffledOffshootRoomsList, lastSpawnedOffshootRoom, direction); + // + // if (spawnedRoom is null) + // { + // GD.Print($"Could not place offshoot {shuffledOffshoot}"); + // // Try next in list + // continue; + // } + // + // lastSpawnedOffshootRoom = spawnedRoom; + // break; + // } + // + // // Nope no offshoot + // if (lastSpawnedOffshootRoom is null) + // { + // return false; + // } + // } // Offshoot over // Spawn final room // var finalRoomToSpawn = Rooms.Where(x => x.Type == offshootTypeToSpawn).PickRandom(); - - var spawnedFinalRoom = TrySpawnRoom(MapTheme.Rooms.Where(x => x.Type == offshootTypeToSpawn).ToList(), + + var spawnedFinalRoom = TrySpawnRoom(endRoomsQueue, lastSpawnedOffshootRoom, direction); if (spawnedFinalRoom is null)