mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-04 09:55:55 +00:00
room positioning and door generator
This commit is contained in:
parent
59f98ebf0e
commit
5bfffc22ad
19 changed files with 571 additions and 17 deletions
38
Scripts/Controllers/DoorMarker.cs
Normal file
38
Scripts/Controllers/DoorMarker.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Controllers;
|
||||
|
||||
// [Tool]
|
||||
public partial class DoorMarker : Marker2D
|
||||
{
|
||||
[Export] public RoomDirection Direction { get; set; } = RoomDirection.North;
|
||||
[Export] public int WallIndex { get; set; } = 0; // Useful if there are multiple along a wall
|
||||
|
||||
public Vector2I GetWorldDirection()
|
||||
{
|
||||
return Direction switch
|
||||
{
|
||||
RoomDirection.North => new Vector2I(0, -1),
|
||||
RoomDirection.South => new Vector2I(0, 1),
|
||||
RoomDirection.East => new Vector2I(1, 0),
|
||||
RoomDirection.West => new Vector2I(-1, 0),
|
||||
_ => Vector2I.Zero
|
||||
};
|
||||
}
|
||||
|
||||
// public override void _Draw()
|
||||
// {
|
||||
// if (!Engine.IsEditorHint()) return;
|
||||
//
|
||||
// DrawLine(Vector2.Zero, Vector2.Up * 16, Colors.Orange, 2);
|
||||
// DrawString(GetFont("font", "Label"), new Vector2(10, -10), Direction.ToString(), HAlign.Left);
|
||||
// }
|
||||
}
|
||||
|
||||
public enum RoomDirection
|
||||
{
|
||||
North,
|
||||
South,
|
||||
East,
|
||||
West
|
||||
}
|
||||
1
Scripts/Controllers/DoorMarker.cs.uid
Normal file
1
Scripts/Controllers/DoorMarker.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ddry5kjj3fr6c
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Cirno.Scripts.Resources;
|
||||
using Cirno.Scripts.Resources.Roguelite;
|
||||
using Godot;
|
||||
|
|
@ -9,11 +10,74 @@ namespace Cirno.Scripts.Controllers;
|
|||
public partial class RogueliteRoom : Node2D
|
||||
{
|
||||
[Export] public RogueliteRoomResource RoomResource { get; set; }
|
||||
|
||||
public Vector2I GridPosition { get; set; } // Set by dungeon manager
|
||||
|
||||
[Export] public PackedScene DoorPrefab { get; private set; }
|
||||
|
||||
private static readonly Dictionary<string, Vector2I> DirectionMap = new()
|
||||
{
|
||||
{ "North", new Vector2I(0, -1) },
|
||||
{ "South", new Vector2I(0, 1) },
|
||||
{ "East", new Vector2I(1, 0) },
|
||||
{ "West", new Vector2I(-1, 0) },
|
||||
};
|
||||
|
||||
private Array<EnemyResource> SpawnableEnemies => RoomResource.SpawnableEnemies;
|
||||
|
||||
public void Spawn()
|
||||
public RogueliteRoom Spawn(Func<Vector2I, bool> connectionChecker)
|
||||
{
|
||||
SpawnEnemies();
|
||||
HandleDoors(connectionChecker);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void HandleDoors(Func<Vector2I, bool> connectionChecker)
|
||||
{
|
||||
if (!HasNode("Doors")) return;
|
||||
var doorsNode = GetNode("Doors");
|
||||
|
||||
foreach (Node child in doorsNode.GetChildren())
|
||||
{
|
||||
if (child is not DoorMarker marker) continue;
|
||||
|
||||
var baseDir = marker.GetWorldDirection();
|
||||
|
||||
// WallIndex determines the offset *along* the edge of the room
|
||||
Vector2I offset = marker.Direction switch
|
||||
{
|
||||
RoomDirection.North or RoomDirection.South => new Vector2I(marker.WallIndex, 0),
|
||||
RoomDirection.East or RoomDirection.West => new Vector2I(0, marker.WallIndex),
|
||||
_ => Vector2I.Zero
|
||||
};
|
||||
|
||||
// Combine GridPosition + offset to locate where this door aligns
|
||||
Vector2I doorEdge = GridPosition + offset;
|
||||
Vector2I neighborPos = doorEdge + baseDir;
|
||||
|
||||
bool connected = connectionChecker.Invoke(neighborPos);
|
||||
|
||||
if (!connected) continue;
|
||||
|
||||
var door = this.CreateChild<Door>(DoorPrefab, marker.GlobalPosition);
|
||||
|
||||
door.State = DoorState.Open;
|
||||
|
||||
// PackedScene prefab = hasConnection
|
||||
// ? GD.Load<PackedScene>("res://Prefabs/Door.tscn")
|
||||
// : GD.Load<PackedScene>("res://Prefabs/Wall.tscn");
|
||||
//
|
||||
// var instance = prefab.Instantiate<Node2D>();
|
||||
// AddChild(instance);
|
||||
// instance.GlobalPosition = ((Node2D)child).GlobalPosition;
|
||||
// instance.Rotation = ((Node2D)child).GlobalRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnEnemies()
|
||||
{
|
||||
if (SpawnableEnemies is null || !SpawnableEnemies.Any()) return;
|
||||
|
||||
var enemySpawners = this.GetNode("EnemySpawners").GetChildren().Cast<Marker2D>();
|
||||
|
||||
foreach (var spawner in enemySpawners)
|
||||
|
|
@ -26,6 +90,5 @@ public partial class RogueliteRoom : Node2D
|
|||
var enemyScene = GD.Load<PackedScene>(e.PrefabPath);
|
||||
var spawnedEnemy = spawner.CreateChild<Node2D>(enemyScene);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cirno.Scripts.Enums;
|
||||
using Cirno.Scripts.Resources.Roguelite;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
|
@ -10,13 +12,27 @@ 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();
|
||||
|
|
@ -30,12 +46,70 @@ public partial class RogueliteRoomManager : Node2D
|
|||
{
|
||||
var roomIndex = GD.RandRange(0, Rooms.Count - 1);
|
||||
var room = Rooms[roomIndex];
|
||||
SpawnRoom(room, origin + (room.Size * new Vector2(i, j) * tileSize));
|
||||
//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()
|
||||
|
|
@ -43,12 +117,46 @@ public partial class RogueliteRoomManager : Node2D
|
|||
GameManager.Instance.RebakeNavigation();
|
||||
}
|
||||
|
||||
private void SpawnRoom(RogueliteRoomResource room, Vector2 position)
|
||||
private RogueliteRoom SpawnRoom(RogueliteRoomResource room, Vector2I gridPos)
|
||||
{
|
||||
var roomScene = GD.Load<PackedScene>(room.ScenePath);
|
||||
var position = gridPos * TileSize * RoomSizeInTiles;
|
||||
|
||||
var roomScene = GD.Load<PackedScene>(room.ScenePath);
|
||||
var spawnedScene = this.CreateChild<RogueliteRoom>(roomScene, position);
|
||||
|
||||
spawnedScene.Spawn();
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue