Refactor map generator package

- Move room generators to map.generators.room
- Move corridor generators to map.generators.corridor

Generators have a generate() method that generates the things they place,
and an apply() method that applies their objects to a grid of tiles.
This commit is contained in:
Eryn Wells 2023-02-09 16:07:29 -08:00
parent 843aa2823f
commit 9a04692539
7 changed files with 391 additions and 262 deletions

View file

@ -0,0 +1,92 @@
'''
Defines an abstract CorridorGenerator and several concrete subclasses. These classes generate corridors between rooms.
'''
import random
from itertools import pairwise
from typing import List
import tcod
import numpy as np
from ... import log
from ...geometry import Point
from ..room import Room
from ..tile import Empty, Floor, Wall
class CorridorGenerator:
'''
Corridor generators produce corridors between rooms.
'''
def generate(self, rooms: List[Room]) -> bool:
'''Generate corridors given a list of rooms.'''
raise NotImplementedError()
def apply(self, tiles: np.ndarray):
'''Apply corridors to a tile grid.'''
raise NotImplementedError()
class ElbowCorridorGenerator(CorridorGenerator):
'''
Generators corridors using a simple "elbow" algorithm:
```
For each pair of rooms:
1. Find the midpoint of the bounding rect of each room
2. Calculate an elbow point
3. Draw a path from the midpoint of the first room to the elbow point
4. Draw a path from the elbow point to the midpoint of the second room
```
'''
def __init__(self):
self.corridors: List[List[Point]] = []
def generate(self, rooms: List[Room]) -> bool:
for (left_room, right_room) in pairwise(rooms):
left_room_bounds = left_room.bounds
right_room_bounds = right_room.bounds
log.MAP.debug(' left: %s, %s', left_room, left_room_bounds)
log.MAP.debug('right: %s, %s', right_room, right_room_bounds)
start_point = left_room_bounds.midpoint
end_point = right_room_bounds.midpoint
# Randomly choose whether to move horizontally then vertically or vice versa
if random.random() < 0.5:
corner = Point(end_point.x, start_point.y)
else:
corner = Point(start_point.x, end_point.y)
log.MAP.debug('Digging a tunnel between %s and %s with corner %s', start_point, end_point, corner)
log.MAP.debug('|-> start: %s', left_room_bounds)
log.MAP.debug('`-> end: %s', right_room_bounds)
corridor: List[Point] = []
for x, y in tcod.los.bresenham(tuple(start_point), tuple(corner)).tolist():
corridor.append(Point(x, y))
for x, y in tcod.los.bresenham(tuple(corner), tuple(end_point)).tolist():
corridor.append(Point(x, y))
self.corridors.append(corridor)
return True
def apply(self, tiles):
for corridor in self.corridors:
for pt in corridor:
tiles[pt.x, pt.y] = Floor
for neighbor in pt.neighbors:
if not (0 <= neighbor.x < tiles.shape[0] and 0 <= neighbor.y < tiles.shape[1]):
continue
if tiles[neighbor.x, neighbor.y] == Empty:
tiles[neighbor.x, neighbor.y] = Wall
class NetHackCorridorGenerator(CorridorGenerator):
'''A corridor generator that produces doors and corridors that look like Nethack's Dungeons of Doom levels.'''