Use queues for room generation

This commit is contained in:
Marco 2025-04-30 10:30:43 +02:00
commit 7482cfa496
3 changed files with 128 additions and 66 deletions

View file

@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.4.0">
<Project Sdk="Godot.NET.Sdk/4.4.1">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>

View file

@ -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")

View file

@ -15,7 +15,7 @@ namespace Cirno.Scripts.Controllers;
public partial class RogueliteRoomManager : Node2D
{
//[Export] public Array<RogueliteRoomResource> Rooms { get; set; }
[Export] public RogueliteMapTheme MapTheme { get; set; }
//private Godot.Collections.Dictionary<Vector2I, RogueliteRoomResource> _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<RogueliteRoomResource>().EnqueueRange(randomRoomsList);
var randomOffshootRoomsList = OffshootRooms.Shuffle().ToList();
var offshootRoomsQueue = new Queue<RogueliteRoomResource>().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<RoomType>();
// offshootsQueue.EnqueueRange(shuffledOffshoots);
var offshootsQueue = new Queue<RoomType>();
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<Direction>();
@ -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<RogueliteRoomResource>().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<RogueliteRoomResource>().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<RogueliteRoomResource> roomsList, RogueliteRoom lastRoom,
private RogueliteRoom TrySpawnRoom(Queue<RogueliteRoomResource> 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<RogueliteRoomResource> randomOffshootRoomsList)
int roomsInOffshot, Queue<RogueliteRoomResource> randomOffshootRoomsList, Queue<RogueliteRoomResource> 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)