7DRL 2025 progress
This commit is contained in:
parent
e928dda4b3
commit
6be474da08
16 changed files with 856 additions and 80 deletions
|
|
@ -25,6 +25,10 @@ class BinaryRoomNode:
|
|||
def __repr__(self):
|
||||
return f"<RoomNode {self.data}>"
|
||||
|
||||
def center(self):
|
||||
x, y, w, h = self.data
|
||||
return (x + w // 2, y + h // 2)
|
||||
|
||||
def split(self):
|
||||
new_data = binary_space_partition(*self.data)
|
||||
self.left = BinaryRoomNode(new_data[0])
|
||||
|
|
@ -35,6 +39,11 @@ class BinaryRoomNode:
|
|||
return self.left.walk() + self.right.walk()
|
||||
return [self]
|
||||
|
||||
def contains(self, pt):
|
||||
x, y, w, h = self.data
|
||||
tx, ty = pt
|
||||
return x <= tx <= x + w and y <= ty <= y + h
|
||||
|
||||
class RoomGraph:
|
||||
def __init__(self, xywh):
|
||||
self.root = BinaryRoomNode(xywh)
|
||||
|
|
@ -49,6 +58,7 @@ class RoomGraph:
|
|||
|
||||
def room_coord(room, margin=0):
|
||||
x, y, w, h = room.data
|
||||
#print(x,y,w,h, f'{margin=}', end=';')
|
||||
w -= 1
|
||||
h -= 1
|
||||
margin += 1
|
||||
|
|
@ -58,10 +68,37 @@ def room_coord(room, margin=0):
|
|||
h -= margin
|
||||
if w < 0: w = 0
|
||||
if h < 0: h = 0
|
||||
#print(x,y,w,h, end=' -> ')
|
||||
tx = x if w==0 else random.randint(x, x+w)
|
||||
ty = y if h==0 else random.randint(y, y+h)
|
||||
#print((tx, ty))
|
||||
return (tx, ty)
|
||||
|
||||
def adjacent_rooms(r, rooms):
|
||||
x, y, w, h = r.data
|
||||
adjacents = {}
|
||||
|
||||
for i, other_r in enumerate(rooms):
|
||||
rx, ry, rw, rh = other_r.data
|
||||
if (rx, ry, rw, rh) == r:
|
||||
continue # Skip self
|
||||
|
||||
# Check vertical adjacency (above or below)
|
||||
if rx < x + w and x < rx + rw: # Overlapping width
|
||||
if ry + rh == y: # Above
|
||||
adjacents[i] = (x + w // 2, y - 1)
|
||||
elif y + h == ry: # Below
|
||||
adjacents[i] = (x + w // 2, y + h + 1)
|
||||
|
||||
# Check horizontal adjacency (left or right)
|
||||
if ry < y + h and y < ry + rh: # Overlapping height
|
||||
if rx + rw == x: # Left
|
||||
adjacents[i] = (x - 1, y + h // 2)
|
||||
elif x + w == rx: # Right
|
||||
adjacents[i] = (x + w + 1, y + h // 2)
|
||||
|
||||
return adjacents
|
||||
|
||||
class Level:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
|
|
@ -70,6 +107,7 @@ class Level:
|
|||
self.graph = RoomGraph( (0, 0, width, height) )
|
||||
self.grid = mcrfpy.Grid(width, height, t, (10, 10), (1014, 758))
|
||||
self.highlighted = -1 #debug view feature
|
||||
self.walled_rooms = [] # for tracking "hallway rooms" vs "walled rooms"
|
||||
|
||||
def fill(self, xywh, highlight = False):
|
||||
if highlight:
|
||||
|
|
@ -84,7 +122,7 @@ class Level:
|
|||
def highlight(self, delta):
|
||||
rooms = self.graph.walk()
|
||||
if self.highlighted < len(rooms):
|
||||
print(f"reset {self.highlighted}")
|
||||
#print(f"reset {self.highlighted}")
|
||||
self.fill(rooms[self.highlighted].data) # reset
|
||||
self.highlighted += delta
|
||||
print(f"highlight {self.highlighted}")
|
||||
|
|
@ -110,7 +148,7 @@ class Level:
|
|||
|
||||
def fill_rooms(self, features=None):
|
||||
rooms = self.graph.walk()
|
||||
print(f"rooms: {len(rooms)}")
|
||||
#print(f"rooms: {len(rooms)}")
|
||||
for i, g in enumerate(rooms):
|
||||
X, Y, W, H = g.data
|
||||
#c = [random.randint(0, 255) for _ in range(3)]
|
||||
|
|
@ -120,9 +158,14 @@ class Level:
|
|||
self.grid.at((x, y)).tilesprite = ts
|
||||
|
||||
def wall_rooms(self):
|
||||
self.walled_rooms = []
|
||||
rooms = self.graph.walk()
|
||||
for g in rooms:
|
||||
#if random.random() > 0.66: continue
|
||||
for i, g in enumerate(rooms):
|
||||
# unwalled / hallways: not selected for small dungeons, first, last, and 65% of all other rooms
|
||||
if len(rooms) > 3 and i > 1 and i < len(rooms) - 2 and random.random() < 0.35:
|
||||
self.walled_rooms.append(False)
|
||||
continue
|
||||
self.walled_rooms.append(True)
|
||||
X, Y, W, H = g.data
|
||||
for x in range(X, X+W):
|
||||
self.grid.at((x, Y)).walkable = False
|
||||
|
|
@ -138,13 +181,45 @@ class Level:
|
|||
# self.grid.at((0, y)).walkable = False
|
||||
self.grid.at((self.width-1, y)).walkable = False
|
||||
|
||||
def generate(self, target_rooms = 5, features=None):
|
||||
if features is None:
|
||||
shuffled = ["boulder", "button"]
|
||||
random.shuffle(shuffled)
|
||||
features = ["spawn"] + shuffled + ["exit", "treasure"]
|
||||
# Binary space partition to reach given # of rooms
|
||||
def dig_path(self, start:"Tuple[int, int]", end:"Tuple[int, int]", walkable=True, color=None, sprite=None):
|
||||
print(f"Digging: {start} -> {end}")
|
||||
# get x1,y1 and x2,y2 coordinates: top left and bottom right points on the rect formed by two random points, one from each of the 2 rooms
|
||||
x1 = min([start[0], end[0]])
|
||||
x2 = max([start[0], end[0]])
|
||||
dw = x2 - x1
|
||||
y1 = min([start[1], end[1]])
|
||||
y2 = max([start[1], end[1]])
|
||||
dh = y2 - y1
|
||||
|
||||
# random: top left or bottom right as the corner between the paths
|
||||
tx, ty = (x1, y1) if random.random() >= 0.5 else (x2, y2)
|
||||
|
||||
for x in range(x1, x1+dw):
|
||||
try:
|
||||
if walkable:
|
||||
self.grid.at((x, ty)).walkable = walkable
|
||||
if color:
|
||||
self.grid.at((x, ty)).color = color
|
||||
if sprite is not None:
|
||||
self.grid.at((x, ty)).tilesprite = sprite
|
||||
except:
|
||||
pass
|
||||
for y in range(y1, y1+dh):
|
||||
try:
|
||||
if walkable:
|
||||
self.grid.at((tx, y)).walkable = True
|
||||
if color:
|
||||
self.grid.at((tx, y)).color = color
|
||||
if sprite is not None:
|
||||
self.grid.at((tx, y)).tilesprite = sprite
|
||||
except:
|
||||
pass
|
||||
|
||||
def generate(self, level_plan): #target_rooms = 5, features=None):
|
||||
self.reset()
|
||||
target_rooms = len(level_plan)
|
||||
if type(level_plan) is set:
|
||||
level_plan = random.choice(list(level_plan))
|
||||
while len(self.graph.walk()) < target_rooms:
|
||||
self.split(single=len(self.graph.walk()) > target_rooms * .5)
|
||||
|
||||
|
|
@ -152,44 +227,59 @@ class Level:
|
|||
#self.fill_rooms()
|
||||
self.wall_rooms()
|
||||
rooms = self.graph.walk()
|
||||
feature_coords = {}
|
||||
feature_coords = []
|
||||
prev_room = None
|
||||
for room in rooms:
|
||||
if not features: break
|
||||
f = features.pop(0)
|
||||
feature_coords[f] = room_coord(room, margin=4 if f in ("boulder",) else 1)
|
||||
print(level_plan)
|
||||
for room_num, room in enumerate(rooms):
|
||||
room_plan = level_plan[room_num]
|
||||
if type(room_plan) == str: room_plan = [room_plan] # single item plans became single-character plans...
|
||||
for f in room_plan:
|
||||
#feature_coords.append((f, room_coord(room, margin=4 if f in ("boulder",) else 1)))
|
||||
# boulders are breaking my brain. If I can't get boulders away from walls with margin, I'm just going to dig them out.
|
||||
if f == "boulder":
|
||||
x, y = room_coord(room, margin=0)
|
||||
if x < 2: x += 1
|
||||
if y < 2: y += 1
|
||||
if x > self.grid.grid_size[0] - 2: x -= 1
|
||||
if y > self.grid.grid_size[1] - 2: y -= 1
|
||||
for _x in (1, 0, -1):
|
||||
for _y in (1, 0, -1):
|
||||
self.grid.at((x + _x, y + _y)).walkable = True
|
||||
feature_coords.append((f, (x, y)))
|
||||
else:
|
||||
feature_coords.append((f, room_coord(room, margin=0)))
|
||||
print(feature_coords[-1])
|
||||
|
||||
## Hallway generation
|
||||
# plow an inelegant path
|
||||
if prev_room:
|
||||
start = room_coord(prev_room, margin=2)
|
||||
end = room_coord(room, margin=2)
|
||||
# get x1,y1 and x2,y2 coordinates: top left and bottom right points on the rect formed by two random points, one from each of the 2 rooms
|
||||
x1 = min([start[0], end[0]])
|
||||
x2 = max([start[0], end[0]])
|
||||
dw = x2 - x1
|
||||
y1 = min([start[1], end[1]])
|
||||
y2 = max([start[1], end[1]])
|
||||
dh = y2 - y1
|
||||
#print(x1, y1, x2, y2, dw, dh)
|
||||
|
||||
# random: top left or bottom right as the corner between the paths
|
||||
tx, ty = (x1, y1) if random.random() >= 0.5 else (x2, y2)
|
||||
|
||||
for x in range(x1, x1+dw):
|
||||
self.grid.at((x, ty)).walkable = True
|
||||
#self.grid.at((x, ty)).color = (255, 0, 0)
|
||||
#self.grid.at((x, ty)).tilesprite = -1
|
||||
for y in range(y1, y1+dh):
|
||||
self.grid.at((tx, y)).walkable = True
|
||||
#self.grid.at((tx, y)).color = (0, 255, 0)
|
||||
#self.grid.at((tx, y)).tilesprite = -1
|
||||
self.dig_path(start, end, color=(0, 64, 0))
|
||||
prev_room = room
|
||||
|
||||
|
||||
# Tile painting
|
||||
possibilities = None
|
||||
while possibilities or possibilities is None:
|
||||
possibilities = ct.wfc_pass(self.grid, possibilities)
|
||||
|
||||
## "hallway room" repainting
|
||||
#for i, hall_room in enumerate(rooms):
|
||||
# if self.walled_rooms[i]:
|
||||
# print(f"walled room: {hall_room}")
|
||||
# continue
|
||||
# print(f"hall room: {hall_room}")
|
||||
# x, y, w, h = hall_room.data
|
||||
# for _x in range(x+1, x+w-1):
|
||||
# for _y in range(y+1, y+h-1):
|
||||
# self.grid.at((_x, _y)).walkable = False
|
||||
# self.grid.at((_x, _y)).tilesprite = -1
|
||||
# self.grid.at((_x, _y)).color = (0, 0, 0) # pit!
|
||||
# targets = adjacent_rooms(hall_room, rooms)
|
||||
# print(targets)
|
||||
# for k, v in targets.items():
|
||||
# self.dig_path(hall_room.center(), v, color=(64, 32, 32))
|
||||
# for _, p in feature_coords:
|
||||
# if hall_room.contains(p): self.dig_path(hall_room.center(), p, color=(92, 48, 48))
|
||||
|
||||
return feature_coords
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue