Doors #156

Merged
ynerant merged 9 commits from doors into map_generation 2021-01-10 22:54:14 +00:00
9 changed files with 65 additions and 39 deletions

View File

@ -2,17 +2,17 @@
####### ############# ####### #############
#.H...# #...........# #.H...# #...........#
#.....# #####...........# #.....# #####...........#
#.....# #............H..# #.....# #...&........H..#
#.##### #.###...........# #.##### #.###...........#
#.# #.# #...........# #.# #.# #...........#
#.# #.# ############# #.# #.# #############
#.# #.# #.# #.#
#.#### #.# #.#### #.#
#....# #.# #....# #.#
####.###################.# ####&###################&#
#.....................# ################# #.....................# #################
#.....................# #...............# #.....................# #...............#
#.....................#######...............# #.....................#######...............#
#...........................................# #.....................&.....&...............#
#.....................#######...............# #.....................#######...............#
####################### ################# ####################### #################

View File

@ -82,6 +82,7 @@ TexturePack.ASCII_PACK = TexturePack(
BOW=')', BOW=')',
CHEST='', CHEST='',
CHESTPLATE='(', CHESTPLATE='(',
DOOR='&',
EAGLE='µ', EAGLE='µ',
EMPTY=' ', EMPTY=' ',
EXPLOSION='%', EXPLOSION='%',
@ -124,6 +125,8 @@ TexturePack.SQUIRREL_PACK = TexturePack(
BOW='🏹', BOW='🏹',
CHEST='🧰', CHEST='🧰',
CHESTPLATE='🦺', CHESTPLATE='🦺',
DOOR=('🚪', curses.COLOR_WHITE, (1000, 1000, 1000),
curses.COLOR_WHITE, (1000, 1000, 1000)),
EAGLE='🦅', EAGLE='🦅',
EMPTY=' ', EMPTY=' ',
EXPLOSION='💥', EXPLOSION='💥',

View File

@ -6,7 +6,7 @@ from random import randint
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from .items import Item from .items import Item
from ..interfaces import FightingEntity, InventoryHolder from ..interfaces import FightingEntity, InventoryHolder, Tile
from ..translations import gettext as _ from ..translations import gettext as _
@ -152,6 +152,12 @@ class Player(InventoryHolder, FightingEntity):
return True return True
elif entity.is_item(): elif entity.is_item():
entity.hold(self) entity.hold(self)
tile = self.map.tiles[y][x]
if tile == Tile.DOOR and move_if_possible:
# Open door
self.map.tiles[y][x] = Tile.FLOOR
self.map.compute_visibility(y, x, self.vision)
return super().check_move(y, x, move_if_possible)
return super().check_move(y, x, move_if_possible) return super().check_move(y, x, move_if_possible)
def save_state(self) -> dict: def save_state(self) -> dict:

View File

@ -199,7 +199,9 @@ class Game:
self.map_index = 0 self.map_index = 0
return return
while self.map_index >= len(self.maps): while self.map_index >= len(self.maps):
self.maps.append(broguelike.Generator().run()) m = broguelike.Generator().run()
m.logs = self.logs
self.maps.append(m)
new_map = self.map new_map = self.map
new_map.floor = self.map_index new_map.floor = self.map_index
old_map.remove_entity(self.player) old_map.remove_entity(self.player)
@ -417,6 +419,7 @@ class Game:
self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]] self.maps = [Map().load_state(map_dict) for map_dict in d["maps"]]
for i, m in enumerate(self.maps): for i, m in enumerate(self.maps):
m.floor = i m.floor = i
m.logs = self.logs
except KeyError as error: except KeyError as error:
self.message = _("Some keys are missing in your save file.\n" self.message = _("Some keys are missing in your save file.\n"
"Your save seems to be corrupt. It got deleted.")\ "Your save seems to be corrupt. It got deleted.")\

View File

@ -390,6 +390,7 @@ class Tile(Enum):
WALL = auto() WALL = auto()
FLOOR = auto() FLOOR = auto()
LADDER = auto() LADDER = auto()
DOOR = auto()
@staticmethod @staticmethod
def from_ascii_char(ch: str) -> "Tile": def from_ascii_char(ch: str) -> "Tile":
@ -430,7 +431,7 @@ class Tile(Enum):
""" """
Is this Tile a wall? Is this Tile a wall?
""" """
return self == Tile.WALL return self == Tile.WALL or self == Tile.DOOR
def is_ladder(self) -> bool: def is_ladder(self) -> bool:
""" """

View File

@ -26,9 +26,11 @@ DEFAULT_PARAMS = {
"spawn_per_region": [1, 2], "spawn_per_region": [1, 2],
} }
def dist(level, y1, x1, y2, x2):
def dist(level: List[List[Tile]], y1: int, x1: int, y2: int, x2: int) -> int:
""" """
Compute the minimum walking distance between points (y1, x1) and (y2, x2) on a Tile grid Compute the minimum walking distance between points (y1, x1) and (y2, x2)
on a Tile grid
""" """
# simple breadth first search # simple breadth first search
copy = [[t for t in row] for row in level] copy = [[t for t in row] for row in level]
@ -97,8 +99,7 @@ class Generator:
making (door_y, door_x) in the room correspond with (y, x) in the level making (door_y, door_x) in the room correspond with (y, x) in the level
""" """
rh, rw = len(room), len(room[0]) rh, rw = len(room), len(room[0])
# maybe place Tile.DOOR here instead ? level[y][x] = Tile.DOOR
level[y][x] = Tile.FLOOR
for ry in range(rh): for ry in range(rh):
for rx in range(rw): for rx in range(rw):
if room[ry][rx] == Tile.FLOOR: if room[ry][rx] == Tile.FLOOR:
@ -248,8 +249,8 @@ class Generator:
if room[y][x] == Tile.EMPTY and \ if room[y][x] == Tile.EMPTY and \
Generator.build_door(room, y, x, dy, dx, length): Generator.build_door(room, y, x, dy, dx, length):
break break
else: else: # pragma: no cover
return None, None return None, None, None, None
return y + length * dy, x + length * dx, dy, dx return y + length * dy, x + length * dx, dy, dx
@ -324,8 +325,8 @@ class Generator:
top left corner of the room on the level, then log them as a top left corner of the room on the level, then log them as a
spawnable region spawnable region
""" """
if self.queued_area != None: if self.queued_area is not None:
translated_area = [[y+ry, x+rx] for ry, rx in self.queued_area] translated_area = [[y + ry, x + rx] for ry, rx in self.queued_area]
self.spawn_areas.append(translated_area) self.spawn_areas.append(translated_area)
self.queued_area = None self.queued_area = None
@ -334,11 +335,6 @@ class Generator:
Populate every spawnable area with some randomly chosen, randomly Populate every spawnable area with some randomly chosen, randomly
placed entity 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"] min_c, max_c = self.params["spawn_per_region"]
for region in self.spawn_areas: for region in self.spawn_areas:
entity_count = randint(min_c, max_c) entity_count = randint(min_c, max_c)

View File

@ -134,13 +134,13 @@ class TestEntities(unittest.TestCase):
self.map.remove_entity(entity2) self.map.remove_entity(entity2)
# Test following the player and finding the player as target # Test following the player and finding the player as target
self.player.move(5, 5) self.player.move(6, 5)
fam.move(4, 5) fam.move(5, 5)
fam.target = None fam.target = None
self.player.move_down() self.player.move_down()
self.map.tick(self.player) self.map.tick(self.player)
self.assertTrue(fam.target == self.player) self.assertTrue(fam.target == self.player)
self.assertEqual(fam.y, 5) self.assertEqual(fam.y, 6)
self.assertEqual(fam.x, 5) self.assertEqual(fam.x, 5)
# Test random move # Test random move

View File

@ -728,6 +728,7 @@ class TestGame(unittest.TestCase):
self.game.player.inventory.clear() self.game.player.inventory.clear()
ring = RingCritical() ring = RingCritical()
ring.hold(self.game.player) ring.hold(self.game.player)
self.game.display_actions(DisplayActions.REFRESH)
old_critical = self.game.player.critical old_critical = self.game.player.critical
self.game.handle_key_pressed(KeyValues.EQUIP) self.game.handle_key_pressed(KeyValues.EQUIP)
self.assertEqual(self.game.player.critical, self.assertEqual(self.game.player.critical,
@ -951,3 +952,18 @@ class TestGame(unittest.TestCase):
# Exit the menu # Exit the menu
self.game.handle_key_pressed(KeyValues.SPACE) self.game.handle_key_pressed(KeyValues.SPACE)
self.assertEqual(self.game.state, GameMode.PLAY) self.assertEqual(self.game.state, GameMode.PLAY)
def test_doors(self) -> None:
"""
Check that the user can open doors.
"""
self.game.state = GameMode.PLAY
self.game.player.move(9, 8)
self.assertEqual(self.game.map.tiles[10][8], Tile.DOOR)
# Open door
self.game.handle_key_pressed(KeyValues.DOWN)
self.assertEqual(self.game.map.tiles[10][8], Tile.FLOOR)
self.assertEqual(self.game.player.y, 10)
self.assertEqual(self.game.player.x, 8)
self.game.display_actions(DisplayActions.REFRESH)

View File

@ -26,16 +26,17 @@ class TestBroguelike(unittest.TestCase):
def is_connex(self, grid: List[List[Tile]]) -> bool: def is_connex(self, grid: List[List[Tile]]) -> bool:
h, w = len(grid), len(grid[0]) h, w = len(grid), len(grid[0])
y, x = randint(0, h - 1), randint(0, w - 1) y, x = -1, -1
while not (grid[y][x].can_walk()): while not grid[y][x].can_walk():
y, x = randint(0, h - 1), randint(0, w - 1) y, x = randint(0, h - 1), randint(0, w - 1)
queue = Map.neighbourhood(grid, y, x) queue = Map.neighbourhood(grid, y, x)
while queue: while queue:
y, x = queue.pop() y, x = queue.pop()
if grid[y][x].can_walk(): if grid[y][x].can_walk() or grid[y][x] == Tile.DOOR:
grid[y][x] = Tile.WALL grid[y][x] = Tile.WALL
queue += Map.neighbourhood(grid, y, x) queue += Map.neighbourhood(grid, y, x)
return not any([t.can_walk() for row in grid for t in row]) return not any([t.can_walk() or t == Tile.DOOR
for row in grid for t in row])
def test_build_doors(self) -> None: def test_build_doors(self) -> None:
m = self.stom(". .\n. .\n. .\n") m = self.stom(". .\n. .\n. .\n")