Redo tiles to use numpy datatypes

This gives us some nice optimizations when splatting the map to the console.
This commit is contained in:
Eryn Wells 2022-05-01 00:09:12 -07:00
parent e7a5af59ed
commit 5302b5cf5e
3 changed files with 48 additions and 32 deletions

View file

@ -41,12 +41,12 @@ class Engine:
if isinstance(action, MovePlayerAction): if isinstance(action, MovePlayerAction):
map_size = self.configuration.map_size map_size = self.configuration.map_size
new_player_position = Point(max(0, min(map_size.width - 1, self.player.x + action.direction[0])), new_player_position = Point(self.player.x + action.direction[0],
max(0, min(map_size.height - 1, self.player.y + action.direction[1]))) self.player.y + action.direction[1])
can_move_to_level_position = not self.map.tile_for_point(new_player_position).blocks_movement can_move_to_map_position = self.map.tile_is_in_bounds(new_player_position) and self.map.tile_is_walkable(new_player_position)
overlaps_an_object = any(new_player_position.x == obj.x and new_player_position.y == obj.y for obj in self.objects) overlaps_an_object = any(new_player_position.x == obj.x and new_player_position.y == obj.y for obj in self.objects)
if can_move_to_level_position and not overlaps_an_object: EVENT_LOG.debug(f'Attempting to move player to {new_player_position}; can_move:{can_move_to_map_position} overlaps:{overlaps_an_object}')
EVENT_LOG.debug(f'Moving player to {new_player_position}; can_move:{can_move_to_level_position} overlaps:{overlaps_an_object}') if can_move_to_map_position and not overlaps_an_object:
self.player.move_to(new_player_position) self.player.move_to(new_player_position)
if isinstance(action, ExitAction): if isinstance(action, ExitAction):

View file

@ -2,9 +2,10 @@
# Eryn Wells <eryn@erynwells.me> # Eryn Wells <eryn@erynwells.me>
import logging import logging
import numpy as np
import tcod import tcod
from .geometry import Point, Rect, Size from .geometry import Point, Rect, Size
from .tile import Tile from .tile import Floor, Wall
from typing import List from typing import List
LOG = logging.getLogger('map') LOG = logging.getLogger('map')
@ -13,7 +14,7 @@ class Map:
def __init__(self, size: Size): def __init__(self, size: Size):
self.size = size self.size = size
self.tiles: List[List[Tile]] = [[Tile(False) for y in range(size.height)] for x in range(size.width)] self.tiles = np.full(self.size.as_tuple, fill_value=Floor, order="F")
self.rng = tcod.random.Random() self.rng = tcod.random.Random()
@ -24,8 +25,11 @@ class Map:
self.update_tiles() self.update_tiles()
def tile_for_point(self, point: Point) -> Tile: def tile_is_in_bounds(self, point: Point) -> bool:
return self.tiles[point.x][point.y] return 0 <= point.x < self.size.width and 0 <= point.y < self.size.height
def tile_is_walkable(self, point: Point) -> bool:
return self.tiles[point.x, point.y]['walkable']
def generate_partitions(self): def generate_partitions(self):
bsp = tcod.bsp.BSP(x=0, y=0, width=self.size.width, height=self.size.height) bsp = tcod.bsp.BSP(x=0, y=0, width=self.size.width, height=self.size.height)
@ -65,21 +69,23 @@ class Map:
return rooms return rooms
def update_tiles(self): def update_tiles(self):
for column in self.tiles: # Fill the whole map with walls
for tile in column: width, height = self.size.as_tuple
tile.blocks_movement = False self.tiles[0:width, 0:height] = Wall
tile.blocks_sight = False
# Dig out rooms
for room in self.rooms: for room in self.rooms:
for y in range(room.min_y, room.max_y + 1): for y in range(room.min_y, room.max_y + 1):
for x in range(room.min_x, room.max_x + 1): for x in range(room.min_x, room.max_x + 1):
if y == room.min_y or y == room.max_y or x == room.min_x or x == room.max_x: self.tiles[x, y] = Floor
self.tiles[x][y].blocks_movement = True
def print_to_console(self, console: tcod.Console): def print_to_console(self, console: tcod.Console) -> None:
# for part in self.partitions: # for part in self.partitions:
# console.draw_frame(part.x, part.y, part.width, part.height, bg=(40, 40, 80), clear=True, decoration="···· ····") # console.draw_frame(part.x, part.y, part.width, part.height, bg=(40, 40, 80), clear=True, decoration="···· ····")
for room in self.rooms: # for room in self.rooms:
console.draw_frame(room.origin.x, room.origin.y, room.size.width, room.size.height, # console.draw_frame(room.origin.x, room.origin.y, room.size.width, room.size.height,
fg=(255, 255, 255), bg=(80, 40, 40), clear=True) # fg=(255, 255, 255), bg=(80, 40, 40), clear=True)
size = self.size
console.tiles_rgb[0:size.width, 0:size.height] = self.tiles["dark"]

View file

@ -1,19 +1,29 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Eryn Wells <eryn@erynwells.me> # Eryn Wells <eryn@erynwells.me>
import tcod import numpy as np
from typing import Optional from typing import Tuple
class Tile: graphic_datatype = np.dtype([
class Color: # Character, a Unicode codepoint represented as an int32
WALL = tcod.Color(255, 255, 255) ('ch', np.int32),
GROUND = tcod.Color(33, 33, 33) # Foreground color, three bytes
('fg', '3B'),
# Background color, three bytes
('bg', '3B'),
])
def __init__(self, blocks_movement: bool, blocks_sight: Optional[bool] = None): tile_datatype = np.dtype([
self.blocks_movement = blocks_movement # Bool indicating whether this tile can be traversed
('walkable', np.bool),
# Bool indicating whether this tile is transparent
('transparent', np.bool),
# A graphic struct (as above) defining the look of this tile when it's not visible
('dark', graphic_datatype),
])
# If blocks_sight isn't explicitly given, tiles that block movement also block sight. def tile(*, walkable: int, transparent: int, dark: Tuple[int, Tuple[int, int, int], Tuple[int, int ,int]]) -> np.ndarray:
if blocks_sight is None: return np.array((walkable, transparent, dark), dtype=tile_datatype)
self.blocks_sight = blocks_movement
else: Floor = tile(walkable=True, transparent=True, dark=(ord(' '), (255, 255, 255), (50, 50, 150)))
self.blocks_sight = blocks_sight Wall = tile(walkable=False, transparent=False, dark=(ord(' '), (255, 255, 255), (0, 0, 150)))