From d05eec6c0363a9e9124ab63c16430e5cd7db12ce Mon Sep 17 00:00:00 2001 From: Dan Dembinski Date: Wed, 26 May 2021 16:36:59 -0400 Subject: [PATCH] Part 2. --- actions.py | 37 ++++++++++++++++--- engine.py | 14 ++++---- game_map.py | 98 +++++++-------------------------------------------- main.py | 8 ++++- tile_types.py | 37 +++++++++++++++++++ 5 files changed, 98 insertions(+), 96 deletions(-) create mode 100644 tile_types.py diff --git a/actions.py b/actions.py index 89bd1b8..3df62ab 100644 --- a/actions.py +++ b/actions.py @@ -1,13 +1,42 @@ -class Action: - pass +from __future__ import annotations +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from engine import Engine + from entity import Entity + + +class Action: + def perform(self, engine: Engine, entity: Entity) -> None: + """Perform this action with the objects needed to determine its scope + + 'engine' is the scope this action is being perfrmed in. + + 'entity is the object performing the action. + + This method must be overridden by Action subclasses + """ + raise NotImplementedError() class EscapeAction(Action): - pass + def perform(self, engine: Engine, entity: Entity) -> None: + raise SystemExit() class MovementAction(Action): def __init__(self, dx: int, dy: int): super().__init__() self.dx = dx - self.dy = dy \ No newline at end of file + self.dy = dy + + def perform(self, engine: Engine, entity: Entity) -> None: + dest_x = entity.x + self.dx + dest_y = entity.y + self.dy + + if not engine.game_map.in_bounds(dest_x, dest_y): + return # Destination is out of bounds. + if not engine.game_map.tiles["walkable"][dest_x, dest_y]: + return # Destination is blocked by a tile + + entity.move(self.dx, self.dy) \ No newline at end of file diff --git a/engine.py b/engine.py index e3430d2..0960c78 100644 --- a/engine.py +++ b/engine.py @@ -5,12 +5,15 @@ from tcod.console import Console from actions import EscapeAction, MovementAction from entity import Entity +from game_map import GameMap from input_handlers import EventHandler + class Engine: - def __init__(self, entities: Set[Entity], event_handler: EventHandler, player: Entity): + def __init__(self, entities: Set[Entity], event_handler: EventHandler, game_map: GameMap, player: Entity): self.entities = entities self.event_handler = event_handler + self.game_map = game_map self.player = player def handle_events(self, events: Iterable[Any]) -> None: @@ -20,15 +23,14 @@ class Engine: if action is None: continue - if isinstance(action, MovementAction): - self.player.move(dx=action.dx, dy=action.dy) - elif isinstance(action, EscapeAction): - raise SystemExit() + action.perform(self, self.player) def render(self, console: Console, context: Context) -> None: + self.game_map.render(console) + for entity in self.entities: console.print(entity.x, entity.y, entity.char, fg=entity.color) context.present(console) - console.clear() \ No newline at end of file + console.clear() diff --git a/game_map.py b/game_map.py index e14d5a0..136703a 100644 --- a/game_map.py +++ b/game_map.py @@ -1,90 +1,18 @@ -from map_objects import * -from random import randint +import numpy as np +from tcod.console import Console + +import tile_types class GameMap: - def __init__(self, width, height): - self.width = width - self.height = height - self.tiles = self.initialize_tiles() + def __init__(self, width: int, height: int): + self.width, self.height = width, height + self.tiles = np.full((width,height), fill_value=tile_types.floor, order="F") - def initialize_tiles(self): - tiles = [[Tile(True) for y in range(self.height)] for x in range(self.width)] + self.tiles[30:33, 22] = tile_types.wall - return tiles + def in_bounds(self, x: int, y: int) -> bool: + """Return True if x and y are inside the bounds of this map""" + return 0 <= x < self.width and 0 <= y < self.height - def is_blocked(self, x, y): - if self.tiles[x][y].blocked: - return True - return False - - def create_room(self, room): - # go through the tiles in the rectangle and make them passable - for x in range(room.x1 + 1, room.x2): - for y in range(room.y1 +1, room.y2): - self.tiles[x][y].blocked = False - self.tiles[x][y].block_sight = False - - def create_h_tunnel(self, x1, x2, y): - for x in range(min(x1, x2), max(x1, x2) + 1): - self.tiles[x][y].blocked = False - self.tiles[x][y].block_sight = False - - def create_v_tunnel(self, y1, y2, x): - for y in range(min(y1, y2), max(y1, y2) + 1): - self.tiles[x][y].blocked = False - self.tiles[x][y].block_sight = False - - - def make_map(self, max_rooms, room_min_size, room_max_size, map_width, map_height, player): - rooms = [] - num_rooms = 0 - - for r in range(max_rooms): - # print(rooms) - # print(num_rooms) - # random width and height - w = randint(room_min_size, room_max_size) - h = randint(room_min_size, room_max_size) - # random position without going out of bounds - x = randint(0, map_width - w - 1) - y = randint(0, map_height - h - 1) - - #call Rect Class - new_room = Rect(x, y, w, h) - # if rooms == []: - # print('first') - # rooms.append(new_room) - - - #run through other rooms to check for overlap - for other_room in rooms: - if new_room.intersect(other_room): - break - else: - # no overlap, room is valid - # paint to map's tiles - self.create_room(new_room) - - #center coords of new room - (new_x, new_y) = new_room.center() - - if num_rooms == 0: - #first room where player starts - player.x = new_x - player.y = new_y - else: - - # all rooms after the first are connected - # center coords of previous room - (prev_x, prev_y) = rooms[num_rooms - 1].center() - - if randint(0, 1) == 1: - #first horizontally then vertically - self.create_h_tunnel(prev_x, new_x, prev_y) - self.create_v_tunnel(prev_y, new_y, new_x) - else: - #first vertically then horizontally - self.create_v_tunnel(prev_y, new_y, prev_x) - self.create_h_tunnel(prev_x, new_x, new_y) - rooms.append(new_room) - num_rooms += 1 + def render(self, console: Console) -> None: + console.tiles_rgb[0:self.width, 0:self.height] = self.tiles["dark"] \ No newline at end of file diff --git a/main.py b/main.py index 64c4131..db937cd 100644 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import tcod from engine import Engine from entity import Entity +from game_map import GameMap from input_handlers import EventHandler @@ -10,6 +11,9 @@ def main(): screen_width = 80 screen_height = 50 + map_width = 80 + map_height = 45 + room_max_size = 10 room_min_size = 6 max_rooms = 30 @@ -29,7 +33,9 @@ def main(): npc = Entity(int(screen_width/2), int(screen_height/2), '@', (255, 255, 0)) entities = {npc, player} - engine = Engine(entities=entities, event_handler=event_handler, player=player) + game_map = GameMap(map_width, map_height) + + engine = Engine(entities=entities, event_handler=event_handler, game_map=game_map, player=player) with tcod.context.new_terminal( screen_width, diff --git a/tile_types.py b/tile_types.py new file mode 100644 index 0000000..7ad014b --- /dev/null +++ b/tile_types.py @@ -0,0 +1,37 @@ +from typing import Tuple + +import numpy as np + +graphic_dt = np.dtype( + [ + ("ch", np.int32), + ("fg", "3b"), + ("bg", "3B"), + ] +) + +tile_dt = np.dtype( + [ + ("walkable", np.bool), + ("transparent", np.bool), + ("dark", graphic_dt), + ] +) + + +def new_tile( + *, + walkable: int, + transparent: int, + dark: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]], +) -> np.ndarray: + """Helper function for defining individual tile types""" + return np.array((walkable, transparent, dark), dtype=tile_dt) + + +floor = new_tile( + walkable=True, transparent=True, dark=(ord(" "),(255,255,255), (50,50, 150)), +) +wall = new_tile( + walkable=False, transparent=True, dark=(ord(" "), (255,255,255), (0,0,100)), +) \ No newline at end of file