Merge remote branch

This commit is contained in:
Charles Peyrat
2021-01-10 23:02:34 +01:00
57 changed files with 1955 additions and 991 deletions

View File

@ -1,10 +1,10 @@
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
# SPDX-License-Identifier: GPL-3.0-or-later
from random import random, randint, shuffle, choice, choices
from random import choice, choices, randint, random, shuffle
from typing import List, Tuple
from ..interfaces import Map, Tile, Entity
from ..interfaces import Entity, Map, Tile
DEFAULT_PARAMS = {
"width": 120,
@ -20,10 +20,10 @@ DEFAULT_PARAMS = {
"max_h_corr": 12,
"large_circular_room": .10,
"circular_holes": .5,
"loop_tries" : 40,
"loop_max" : 5,
"loop_threshold" : 15,
"spawn_per_region" : [1, 2],
"loop_tries": 40,
"loop_max": 5,
"loop_threshold": 15,
"spawn_per_region": [1, 2],
}
def dist(level, y1, x1, y2, x2):
@ -31,7 +31,7 @@ def dist(level, y1, x1, y2, x2):
Compute the minimum walking distance between points (y1, x1) and (y2, x2) on a Tile grid
"""
# simple breadth first search
copy = [[t for t in l] for l in level]
copy = [[t for t in row] for row in level]
dist = -1
queue, next_queue = [[y1, x1]], [0]
while next_queue:
@ -40,7 +40,7 @@ def dist(level, y1, x1, y2, x2):
while queue:
y, x = queue.pop()
copy[y][x] = Tile.EMPTY
if y == y2 and x == x2:
if y == y2 and x == x2:
return dist
for y, x in Map.neighbourhood(copy, y, x):
if copy[y][x].can_walk():
@ -48,6 +48,7 @@ def dist(level, y1, x1, y2, x2):
queue = next_queue
return -1
class Generator:
def __init__(self, params: dict = None):
self.params = params or DEFAULT_PARAMS
@ -104,7 +105,7 @@ class Generator:
level[y - door_y + ry][x - door_x + rx] = Tile.FLOOR
@staticmethod
def add_loop(level: List[List[Tile]], y: int, x: int) -> None:
def add_loop(level: List[List[Tile]], y: int, x: int) -> bool:
"""
Try to add a corridor between two far apart floor tiles, passing
through point (y, x).
@ -127,23 +128,26 @@ class Generator:
if not(0 <= x1 <= x2 < w and 0 <= y1 <= y2 < h):
continue
def verify_sides():
def verify_sides() -> bool:
# switching up dy and dx here pivots the axis, so
# (y+dx, x+dy) and (y-dx, x-dy) are the tiles adjacent to
# (y, x), but not on the original axis
for Dx, Dy in [[dy, dx], [-dy, -dx]]:
for i in range(1, y2-y1+x2-x1):
if not(0<= y1+Dy+i*dy < h and 0 <= x1+Dx+i*dx < w) or \
level[y1+Dy+i*dy][x1+Dx+i*dx].can_walk():
for delta_x, delta_y in [[dy, dx], [-dy, -dx]]:
for i in range(1, y2 - y1 + x2 - x1):
if not (0 <= y1 + delta_y + i * dy < h
and 0 <= x1 + delta_x + i * dx < w) or \
level[y1 + delta_y + i * dy][x1 + delta_x
+ i * dx]\
.can_walk():
return False
return True
# if adding the path would make the two tiles significantly closer
# and its sides don't touch already placed terrain, build it
if dist(level, y1, x1, y2, x2) < 20 and verify_sides():
y, x = y1+dy, x1+dx
y, x = y1 + dy, x1 + dx
while level[y][x] == Tile.EMPTY:
level[y][x] = Tile.FLOOR
y, x = y+dy, x+dx
y, x = y + dy, x + dx
return True
return False
@ -187,7 +191,8 @@ class Generator:
return 0, 0, 0, 0
@staticmethod
def build_door(room, y, x, dy, dx, length):
def build_door(room: List[List[Tile]], y: int, x: int,
dy: int, dx: int, length: int) -> bool:
"""
Tries to build the exit from the room at given coordinates
Depending on parameter length, it will either attempt to build a
@ -206,7 +211,7 @@ class Generator:
and room[ny][nx] != Tile.EMPTY:
return False
# see if the path ahead is clear. needed in the case of non convex room
for i in range(length+1):
for i in range(length + 1):
if room[y + i * dy][x + i * dx] != Tile.EMPTY:
return False
for i in range(length):
@ -214,8 +219,8 @@ class Generator:
return True
@staticmethod
def attach_door(room: List[List[Tile]], h_sup: int, w_sup: int,
h_off: int, w_off: int) -> Tuple[int, int, int, int]:
def attach_door(room: List[List[Tile]], h_sup: int, w_sup: int,
h_off: int, w_off: int) -> Tuple[int, int, int, int]:
"""
Attach an exit to the room. If extra space was allocated to
the grid, make sure a corridor is properly built
@ -300,7 +305,7 @@ class Generator:
"""
return self.create_circular_room()
def register_spawn_area(self, area:List[List[Tile]]):
def register_spawn_area(self, area: List[List[Tile]]) -> None:
"""
Register all floor positions relative to the input grid
for later use
@ -312,7 +317,7 @@ class Generator:
spawn_positions.append([y, x])
self.queued_area = spawn_positions
def update_spawnable(self, y, x):
def update_spawnable(self, y: int, x: int) -> None:
"""
Convert previous spawn positions relative to the room grid to actual
actual spawn positions on the level grid, using the position of the
@ -324,11 +329,16 @@ class Generator:
self.spawn_areas.append(translated_area)
self.queued_area = None
def populate(self, rv):
def populate(self, rv: Map) -> None:
"""
Populate every spawnable area with some randomly chosen, randomly
placed entity
"""
if self.queued_area is not None:
translated_area = [[y + ry, x + rx] for ry, rx in self.queued_area]
self.spawn_areas.append(translated_area)
self.queued_area = None
min_c, max_c = self.params["spawn_per_region"]
for region in self.spawn_areas:
entity_count = randint(min_c, max_c)
@ -349,7 +359,7 @@ class Generator:
# the starting room must have no corridor
mem, self.params["corridor_chance"] = self.params["corridor_chance"], 0
starting_room, _, _, _, _ = self.create_random_room(spawnable = False)
starting_room, _, _, _, _ = self.create_random_room(spawnable=False)
dim_v, dim_h = len(starting_room), len(starting_room[0])
# because Generator.room_fits checks that the exit door is correctly
# placed, but the starting room has no exit door, we find a positoin
@ -399,7 +409,7 @@ class Generator:
while tries < self.params["loop_tries"] and \
loops < self.params["loop_max"]:
tries += 1
y, x = randint(0, height-1), randint(0, width-1)
y, x = randint(0, height - 1), randint(0, width - 1)
loops += self.add_loop(level, y, x)
# place an exit ladder