1
0
Fork 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 entity import Entity
class Action:
def perform(self, engine: Engine, entity: Entity) -> None:
"""Perform this action with the objects needed to determine its scope.
@ -36,8 +37,8 @@ class MovementAction(Action):
dest_y = entity.y + self.dy
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]:
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.console import Console
from tcod.map import compute_fov
from entity import Entity
from game_map import GameMap
@ -10,16 +11,17 @@ from input_handlers import EventHandler
class Engine:
def __init__(
self,
entities: Set[Entity],
event_handler: EventHandler,
game_map: GameMap,
player: Entity
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
self.update_fov()
def handle_events(self, events: Iterable[Any]) -> None:
for event in events:
@ -30,11 +32,27 @@ class Engine:
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:
self.game_map.render(console)
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
context.present(console)

View File

@ -9,9 +9,25 @@ class GameMap:
self.width, self.height = width, height
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:
"""Return True if x and y are inside the bounds of the map."""
return 0 <= x < self.width and 0 <= y < self.height
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.
("transparent", np.bool), # True if this tile doesn't block 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
walkable: 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:
"""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(
walkable=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(
walkable=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)),
)