1
0

Use FOV rendering, completing part 4

This commit is contained in:
Timothy Warren 2022-01-07 14:18:47 -05:00
parent 3506edd85f
commit 7a3614e963
4 changed files with 56 additions and 14 deletions

View File

@ -6,6 +6,7 @@ if TYPE_CHECKING:
from engine import Engine from engine import Engine
from entity import Entity from entity import Entity
class Action: class Action:
def perform(self, engine: Engine, entity: Entity) -> None: def perform(self, engine: Engine, entity: Entity) -> None:
"""Perform this action with the objects needed to determine its scope. """Perform this action with the objects needed to determine its scope.
@ -36,8 +37,8 @@ class MovementAction(Action):
dest_y = entity.y + self.dy dest_y = entity.y + self.dy
if not engine.game_map.in_bounds(dest_x, dest_y): if not engine.game_map.in_bounds(dest_x, dest_y):
return # Destination is out of bounds return # Destination is out of bounds
if not engine.game_map.tiles["walkable"][dest_x, dest_y]: if not engine.game_map.tiles["walkable"][dest_x, dest_y]:
return # Destination is blocked by a tile. return # Destination is blocked by a tile.
entity.move(self.dx, self.dy); entity.move(self.dx, self.dy)

View File

@ -2,6 +2,7 @@ from typing import Set, Iterable, Any
from tcod.context import Context from tcod.context import Context
from tcod.console import Console from tcod.console import Console
from tcod.map import compute_fov
from entity import Entity from entity import Entity
from game_map import GameMap from game_map import GameMap
@ -10,16 +11,17 @@ from input_handlers import EventHandler
class Engine: class Engine:
def __init__( def __init__(
self, self,
entities: Set[Entity], entities: Set[Entity],
event_handler: EventHandler, event_handler: EventHandler,
game_map: GameMap, game_map: GameMap,
player: Entity player: Entity
): ):
self.entities = entities self.entities = entities
self.event_handler = event_handler self.event_handler = event_handler
self.game_map = game_map self.game_map = game_map
self.player = player self.player = player
self.update_fov()
def handle_events(self, events: Iterable[Any]) -> None: def handle_events(self, events: Iterable[Any]) -> None:
for event in events: for event in events:
@ -30,11 +32,27 @@ class Engine:
action.perform(self, self.player) action.perform(self, self.player)
# Update the FOV before the player's next action.
self.update_fov()
def update_fov(self) -> None:
"""Recompute the visible area based on the player's point of view."""
self.game_map.visible[:] = compute_fov(
self.game_map.tiles["transparent"],
(self.player.x, self.player.y),
radius=8,
)
# If a tile is "visible" it should be added to "explored"
self.game_map.explored |= self.game_map.visible
def render(self, console: Console, context: Context) -> None: def render(self, console: Console, context: Context) -> None:
self.game_map.render(console) self.game_map.render(console)
for entity in self.entities: for entity in self.entities:
console.print(entity.x, entity.y, entity.char, fg=entity.color) # Only print entities that are in FOV
if self.game_map.visible[entity.x, entity.y]:
console.print(entity.x, entity.y, entity.char, fg=entity.color)
# Actually output to screen # Actually output to screen
context.present(console) context.present(console)

View File

@ -9,9 +9,25 @@ class GameMap:
self.width, self.height = width, height self.width, self.height = width, height
self.tiles = np.full((width, height), fill_value=tile_types.wall, order="F") self.tiles = np.full((width, height), fill_value=tile_types.wall, order="F")
self.visible = np.full((width, height), fill_value=False, order="F") # Tiles the player can currently see
self.explored = np.full((width, height), fill_value=False, order="F") # Tiles the player has seen before
def in_bounds(self, x: int, y: int) -> bool: def in_bounds(self, x: int, y: int) -> bool:
"""Return True if x and y are inside the bounds of the map.""" """Return True if x and y are inside the bounds of the map."""
return 0 <= x < self.width and 0 <= y < self.height return 0 <= x < self.width and 0 <= y < self.height
def render(self, console: Console): def render(self, console: Console):
console.tiles_rgb[0: self.width, 0: self.height] = self.tiles["dark"] """
Renders the map.
If a tile is in the "visible" array, then draw it with the "light" colors.
If it isn't, but it's in the "explored" array, then draw it with the "dark" colors.
Otherwise, the default is "SHROUD".
:param console:
:return:
"""
console.tiles_rgb[0: self.width, 0: self.height] = np.select(
condlist=[self.visible, self.explored],
choicelist=[self.tiles["light"], self.tiles["dark"]],
default=tile_types.SHROUD
)

View File

@ -17,6 +17,7 @@ tile_dt = np.dtype(
("walkable", np.bool), # True if this tile can be walked over. ("walkable", np.bool), # True if this tile can be walked over.
("transparent", np.bool), # True if this tile doesn't block FOV. ("transparent", np.bool), # True if this tile doesn't block FOV.
("dark", graphic_dt), # Graphics for when this tile is not in FOV. ("dark", graphic_dt), # Graphics for when this tile is not in FOV.
("light", graphic_dt), # Graphics for when the tile is in FOV.
] ]
) )
@ -25,20 +26,26 @@ def new_tile(
*, # Enforce the use of keywords, so that parameter order doesn't matter *, # Enforce the use of keywords, so that parameter order doesn't matter
walkable: int, walkable: int,
transparent: int, transparent: int,
dark: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]] dark: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]],
light: Tuple[int, Tuple[int, int, int], Tuple[int, int, int]]
) -> np.ndarray: ) -> np.ndarray:
"""Helper function for defining individual tile types""" """Helper function for defining individual tile types"""
return np.array((walkable, transparent, dark), dtype=tile_dt) return np.array((walkable, transparent, dark, light), dtype=tile_dt)
# SHROUD represents unexplored, unseen tiles
SHROUD = np.array((ord(" "), (255, 255, 255), (0, 0, 0)), dtype=graphic_dt)
floor = new_tile( floor = new_tile(
walkable=True, walkable=True,
transparent=True, transparent=True,
dark=(ord(" "), (255, 255, 255), (50, 50, 150)) dark=(ord(" "), (255, 255, 255), (50, 50, 150)),
light=(ord(" "), (255, 255, 255), (200, 180, 50)),
) )
wall = new_tile( wall = new_tile(
walkable=False, walkable=False,
transparent=False, transparent=False,
dark=(ord(" "), (255, 255, 255), (0, 0, 100)) dark=(ord(" "), (255, 255, 255), (0, 0, 100)),
light=(ord(" "), (255, 255, 255), (130, 110, 50)),
) )