From e74431086157a792b442f76f20587a30b387b7c3 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 22:51:01 +0100 Subject: [PATCH 1/8] Place doors at the beginning of the corridor --- squirrelbattle/display/texturepack.py | 3 +++ squirrelbattle/interfaces.py | 1 + squirrelbattle/mapgeneration/broguelike.py | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index 16cad4f..d8d04cc 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -82,6 +82,7 @@ TexturePack.ASCII_PACK = TexturePack( BOW=')', CHEST='□', CHESTPLATE='(', + DOOR='&', EAGLE='µ', EMPTY=' ', EXPLOSION='%', @@ -124,6 +125,8 @@ TexturePack.SQUIRREL_PACK = TexturePack( BOW='🏹', CHEST='🧰', CHESTPLATE='🦺', + DOOR=('🚪', curses.COLOR_WHITE, (1000, 1000, 1000), + curses.COLOR_WHITE, (1000, 1000, 1000)), EAGLE='🦅', EMPTY=' ', EXPLOSION='💥', diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index a17ab45..ea39cac 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -390,6 +390,7 @@ class Tile(Enum): WALL = auto() FLOOR = auto() LADDER = auto() + DOOR = auto() @staticmethod def from_ascii_char(ch: str) -> "Tile": diff --git a/squirrelbattle/mapgeneration/broguelike.py b/squirrelbattle/mapgeneration/broguelike.py index 8ae6582..c537cf8 100644 --- a/squirrelbattle/mapgeneration/broguelike.py +++ b/squirrelbattle/mapgeneration/broguelike.py @@ -83,8 +83,7 @@ class Generator: def place_room(level: List[List[Tile]], y: int, x: int, room: List[List[Tile]], door_y: int, door_x: int) -> None: rh, rw = len(room), len(room[0]) - # maybe place Tile.DOOR here ? - level[y][x] = Tile.FLOOR + level[y][x] = Tile.DOOR for ry in range(rh): for rx in range(rw): if room[ry][rx] == Tile.FLOOR: From 6c0aaffd77b315e0cb285e45709c138dab915514 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 22:53:27 +0100 Subject: [PATCH 2/8] Doors are walls --- squirrelbattle/interfaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index ea39cac..80be06b 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -431,7 +431,7 @@ class Tile(Enum): """ Is this Tile a wall? """ - return self == Tile.WALL + return self == Tile.WALL or self == Tile.DOOR def is_ladder(self) -> bool: """ From 11daa8573ceabe55f7aaf346be60246fc5862c36 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 22:59:34 +0100 Subject: [PATCH 3/8] The players can open doors --- squirrelbattle/display/texturepack.py | 2 +- squirrelbattle/entities/player.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/display/texturepack.py b/squirrelbattle/display/texturepack.py index d8d04cc..92df405 100644 --- a/squirrelbattle/display/texturepack.py +++ b/squirrelbattle/display/texturepack.py @@ -126,7 +126,7 @@ TexturePack.SQUIRREL_PACK = TexturePack( CHEST='🧰', CHESTPLATE='🦺', DOOR=('🚪', curses.COLOR_WHITE, (1000, 1000, 1000), - curses.COLOR_WHITE, (1000, 1000, 1000)), + curses.COLOR_WHITE, (1000, 1000, 1000)), EAGLE='🦅', EMPTY=' ', EXPLOSION='💥', diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 17ee7df..a04eed2 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -6,7 +6,7 @@ from random import randint from typing import Dict, Optional, Tuple from .items import Item -from ..interfaces import FightingEntity, InventoryHolder +from ..interfaces import FightingEntity, InventoryHolder, Tile from ..translations import gettext as _ @@ -152,6 +152,12 @@ class Player(InventoryHolder, FightingEntity): return True elif entity.is_item(): 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) def save_state(self) -> dict: From 8f845d1e4cb32c257ab496d15727c7a19d6ced88 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 23:03:24 +0100 Subject: [PATCH 4/8] Doors don't break the connexity of map --- squirrelbattle/tests/mapgeneration_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/squirrelbattle/tests/mapgeneration_test.py b/squirrelbattle/tests/mapgeneration_test.py index d840b69..3879f8f 100644 --- a/squirrelbattle/tests/mapgeneration_test.py +++ b/squirrelbattle/tests/mapgeneration_test.py @@ -27,15 +27,16 @@ class TestBroguelike(unittest.TestCase): def is_connex(self, grid: List[List[Tile]]) -> bool: h, w = len(grid), len(grid[0]) y, x = randint(0, h - 1), randint(0, w - 1) - while not (grid[y][x].can_walk()): + while not (grid[y][x].can_walk() or grid[y][x] == Tile.DOOR): y, x = randint(0, h - 1), randint(0, w - 1) queue = Map.neighbourhood(grid, y, x) while queue: 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 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: m = self.stom(". .\n. .\n. .\n") From b0ca1d4edfa7835107f0b4f345dce0b92778a2d0 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 23:05:49 +0100 Subject: [PATCH 5/8] Cover everytime the map generation test --- squirrelbattle/tests/mapgeneration_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/squirrelbattle/tests/mapgeneration_test.py b/squirrelbattle/tests/mapgeneration_test.py index 3879f8f..5fa19fd 100644 --- a/squirrelbattle/tests/mapgeneration_test.py +++ b/squirrelbattle/tests/mapgeneration_test.py @@ -26,8 +26,8 @@ class TestBroguelike(unittest.TestCase): def is_connex(self, grid: List[List[Tile]]) -> bool: h, w = len(grid), len(grid[0]) - y, x = randint(0, h - 1), randint(0, w - 1) - while not (grid[y][x].can_walk() or grid[y][x] == Tile.DOOR): + y, x = -1, -1 + while not grid[y][x].can_walk(): y, x = randint(0, h - 1), randint(0, w - 1) queue = Map.neighbourhood(grid, y, x) while queue: From 60675d78593e69c0d89cbc4def3047cd7b9ac5d2 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 23:21:28 +0100 Subject: [PATCH 6/8] Cover doors code --- squirrelbattle/assets/example_map.txt | 6 +++--- squirrelbattle/tests/entities_test.py | 6 +++--- squirrelbattle/tests/game_test.py | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/squirrelbattle/assets/example_map.txt b/squirrelbattle/assets/example_map.txt index be2e798..68c3ae1 100644 --- a/squirrelbattle/assets/example_map.txt +++ b/squirrelbattle/assets/example_map.txt @@ -2,17 +2,17 @@ ####### ############# #.H...# #...........# #.....# #####...........# - #.....# #............H..# + #.....# #...&........H..# #.##### #.###...........# #.# #.# #...........# #.# #.# ############# #.# #.# #.#### #.# #....# #.# - ####.###################.# + ####&###################&# #.....................# ################# #.....................# #...............# #.....................#######...............# - #...........................................# + #.....................&.....&...............# #.....................#######...............# ####################### ################# diff --git a/squirrelbattle/tests/entities_test.py b/squirrelbattle/tests/entities_test.py index a0e2548..396d87c 100644 --- a/squirrelbattle/tests/entities_test.py +++ b/squirrelbattle/tests/entities_test.py @@ -134,13 +134,13 @@ class TestEntities(unittest.TestCase): self.map.remove_entity(entity2) # Test following the player and finding the player as target - self.player.move(5, 5) - fam.move(4, 5) + self.player.move(6, 5) + fam.move(5, 5) fam.target = None self.player.move_down() self.map.tick(self.player) self.assertTrue(fam.target == self.player) - self.assertEqual(fam.y, 5) + self.assertEqual(fam.y, 6) self.assertEqual(fam.x, 5) # Test random move diff --git a/squirrelbattle/tests/game_test.py b/squirrelbattle/tests/game_test.py index dbeaa9e..5716854 100644 --- a/squirrelbattle/tests/game_test.py +++ b/squirrelbattle/tests/game_test.py @@ -728,6 +728,7 @@ class TestGame(unittest.TestCase): self.game.player.inventory.clear() ring = RingCritical() ring.hold(self.game.player) + self.game.display_actions(DisplayActions.REFRESH) old_critical = self.game.player.critical self.game.handle_key_pressed(KeyValues.EQUIP) self.assertEqual(self.game.player.critical, @@ -951,3 +952,18 @@ class TestGame(unittest.TestCase): # Exit the menu self.game.handle_key_pressed(KeyValues.SPACE) 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) From 65ae99a26db613fad98cf83a8c74d2865711470e Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 23:41:51 +0100 Subject: [PATCH 7/8] The logs of the map was not updated --- squirrelbattle/game.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/squirrelbattle/game.py b/squirrelbattle/game.py index 6b8bc0f..2e467fb 100644 --- a/squirrelbattle/game.py +++ b/squirrelbattle/game.py @@ -199,7 +199,9 @@ class Game: self.map_index = 0 return 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.floor = self.map_index 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"]] for i, m in enumerate(self.maps): m.floor = i + m.logs = self.logs except KeyError as error: self.message = _("Some keys are missing in your save file.\n" "Your save seems to be corrupt. It got deleted.")\ From 588357e5bf9d6d0154701c9ce337230330e7a8e8 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 10 Jan 2021 23:49:43 +0100 Subject: [PATCH 8/8] Linting --- squirrelbattle/mapgeneration/broguelike.py | 45 ++++++++++------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/squirrelbattle/mapgeneration/broguelike.py b/squirrelbattle/mapgeneration/broguelike.py index 4a97e44..f746f37 100644 --- a/squirrelbattle/mapgeneration/broguelike.py +++ b/squirrelbattle/mapgeneration/broguelike.py @@ -26,9 +26,11 @@ DEFAULT_PARAMS = { "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 copy = [[t for t in row] for row in level] @@ -60,9 +62,9 @@ class Generator: room: List[List[Tile]], door_y: int, door_x: int, dy: int, dx: int) -> bool: """ - Using point (door_y, door_x) in the room as a reference and placing it + Using point (door_y, door_x) in the room as a reference and placing it over point (y, x) in the level, returns whether or not the room fits - here + here """ lh, lw = len(level), len(level[0]) rh, rw = len(room), len(room[0]) @@ -93,7 +95,7 @@ class Generator: def place_room(level: List[List[Tile]], y: int, x: int, room: List[List[Tile]], door_y: int, door_x: int) -> None: """ - Mutates level in place to add the room. Placement is determined by + Mutates level in place to add the room. Placement is determined by making (door_y, door_x) in the room correspond with (y, x) in the level """ rh, rw = len(room), len(room[0]) @@ -106,11 +108,11 @@ class Generator: @staticmethod def add_loop(level: List[List[Tile]], y: int, x: int) -> bool: """ - Try to add a corridor between two far apart floor tiles, passing + Try to add a corridor between two far apart floor tiles, passing through point (y, x). """ h, w = len(level), len(level[0]) - + if level[y][x] != Tile.EMPTY: return False @@ -128,8 +130,8 @@ class Generator: continue 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 + # 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 delta_x, delta_y in [[dy, dx], [-dy, -dx]]: for i in range(1, y2 - y1 + x2 - x1): @@ -194,8 +196,8 @@ class Generator: 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 - simple door, or a long corridor. Return value is a boolean + Depending on parameter length, it will either attempt to build a + simple door, or a long corridor. Return value is a boolean signifying whether or not the exit was successfully built """ rh, rw = len(room), len(room[0]) @@ -247,15 +249,15 @@ class Generator: if room[y][x] == Tile.EMPTY and \ Generator.build_door(room, y, x, dy, dx, length): break - else: - return None, None + else: # pragma: no cover + return None, None, None, None return y + length * dy, x + length * dx, dy, dx def create_circular_room(self, spawnable: bool = True) \ -> Tuple[List[List[Tile]], int, int, int, int]: """ - Create and return as a tile grid a room that is circular in shape, and + Create and return as a tile grid a room that is circular in shape, and may have a center, also circular hole Also return door info so we know how to place the room in the level """ @@ -298,7 +300,7 @@ class Generator: def create_random_room(self, spawnable: bool = True) \ -> Tuple[List[list], int, int, int, int]: """ - Randomly select a room shape and return one such room along with its + Randomly select a room shape and return one such room along with its door info. Set spawnable to False is the room should be marked as a potential spawning region on the map """ @@ -319,12 +321,12 @@ class Generator: 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 - top left corner of the room on the level, then log them as a + actual spawn positions on the level grid, using the position of the + top left corner of the room on the level, then log them as a spawnable region """ - if self.queued_area != None: - translated_area = [[y+ry, x+rx] for ry, rx in self.queued_area] + 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 @@ -333,11 +335,6 @@ class Generator: 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)