A dungeon delver roguelike using Pathfinder 2nd edition rules
1class_name Dungeon
2
3extends Node
4
5@export var grid: PackedByteArray = []
6@export var width: int = 6
7@export var height: int = 6
8@export var entrance: int = -1
9@export var init: bool = false
10@export var currentRoom: int = -1
11
12const BitUsedRoom: int = 0x01
13const BitEntrance: int = 0x02
14const BitDoorNorth: int = 0x04
15const BitDoorEast: int = 0x08
16const BitDoorSouth: int = 0x10
17const BitDoorWest: int = 0x20
18const BitStairBelow: int = 0x40
19const BitStairUp: int = 0x80
20
21const Neighbors: int = BitDoorNorth | BitDoorEast | BitDoorSouth | BitDoorWest
22
23var rng: RandomNumberGenerator = RandomNumberGenerator.new()
24
25func _ready() -> void:
26 if init:
27 generate()
28 currentRoom = entrance
29 var nextScene: Node3D = preload("res://dungeonRoom/dungeonRoom.tscn").instantiate()
30 get_node("/root/Main").call_deferred("add_child", nextScene)
31
32func fillArray(r: Array, size: int):
33 for i in range(size):
34 r.append(0)
35
36func fillByteArray(r: PackedByteArray, size: int):
37 for i in range(size):
38 r.append(0)
39
40func generate():
41 var dungeonArea: int = width * height
42 fillByteArray(grid, dungeonArea)
43 var generatedCellsNum: int = 0
44 var generatedCells: Array[int]
45 fillArray(generatedCells, dungeonArea)
46 var i = 0
47
48 while ((generatedCellsNum < dungeonArea) and ((i == 0) or (i < generatedCellsNum))):
49 if (i == 0) and (generatedCellsNum == 0):
50 entrance = rng.randi_range(0, dungeonArea - 1)
51 generatedCells[0] = entrance
52 grid[entrance] = BitEntrance | BitUsedRoom
53 generatedCellsNum = 1
54
55 var generatedCellsRef: Array[int] = [generatedCellsNum]
56 generateRoom(i, generatedCells, generatedCellsRef)
57 generatedCellsNum = generatedCellsRef[0]
58
59 if !(grid[generatedCells[i]] & BitUsedRoom):
60 grid[generatedCells[i]] |= BitUsedRoom
61
62 if (i == (generatedCellsNum - 1)) and (generatedCellsNum < (dungeonArea * 0.75)):
63 i = -1
64
65 i += 1
66
67func generateRoom(cellIndexQueue: int, cellsQueue: Array[int], queueSize: Array[int]) -> void:
68 var potentialDoors: int = rng.randi_range(0, Neighbors - 1)
69 var cellIndex: int = cellsQueue[cellIndexQueue]
70
71 var door: int = 1
72 var oppositeDoor: int
73
74 while door <= Neighbors:
75 if ((door & Neighbors) != door) or (grid[cellIndex] & door):
76 door <<= 1
77 continue
78
79 var neighborRoom: int = getNeighborRoomIndex(cellIndex, door)
80
81 if (!~neighborRoom) or (grid[neighborRoom] & BitUsedRoom):
82 door <<= 1
83 continue
84
85 oppositeDoor = getOppositeDirectionBit(door)
86
87 if (door & potentialDoors) == door:
88 grid[cellIndex] |= door
89 grid[neighborRoom] |= oppositeDoor
90
91 if grid[neighborRoom] == oppositeDoor:
92 cellsQueue[queueSize[0]] = neighborRoom
93 queueSize[0] += 1
94
95 door <<= 1
96
97func getNeighborRoomIndex(currRoom: int, direction: int) -> int:
98 var neighborRoom: int
99
100 if direction == BitDoorNorth:
101 neighborRoom = currRoom - width
102 elif direction == BitDoorEast:
103 neighborRoom = currRoom + 1
104 elif direction == BitDoorSouth:
105 neighborRoom = currRoom + width
106 elif direction == BitDoorWest:
107 neighborRoom = currRoom - 1
108
109 if ((direction == BitDoorNorth) and (neighborRoom >= 0)) or ((direction == BitDoorSouth) and (neighborRoom < (width * height))) or ((direction == BitDoorEast) and ((neighborRoom % width) > 0)) or ((direction == BitDoorWest) and ((neighborRoom % width) < (width - 1))):
110 return neighborRoom
111
112 return -1
113
114func getOppositeDirectionBit(direction: int) -> int:
115 var oppositeDirection: int = -1
116
117 if direction == BitDoorNorth:
118 oppositeDirection = BitDoorSouth
119 elif direction == BitDoorEast:
120 oppositeDirection = BitDoorWest
121 elif direction == BitDoorSouth:
122 oppositeDirection = BitDoorNorth
123 elif direction == BitDoorWest:
124 oppositeDirection = BitDoorEast
125
126 return oppositeDirection