Add an Engine class and move all the engine-y bits over there from main

This commit is contained in:
Eryn Wells 2022-04-30 23:30:23 -07:00
parent dc78669abf
commit d56bcc7b4a
3 changed files with 68 additions and 73 deletions

62
roguebasin/engine.py Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
# Eryn Wells <eryn@erynwells.me>
import logging
import tcod
from .actions import ExitAction, MovePlayerAction, RegenerateRoomsAction
from .events import EventHandler
from .geometry import Point, Size
from .map import Map
from .object import Object
from typing import AbstractSet
LOG = logging.getLogger('engine')
EVENT_LOG = logging.getLogger('events')
class Configuration:
def __init__(self, map_size: Size):
self.map_size = map_size
self.random_seed = None
class Engine:
def __init__(self, event_handler: EventHandler, configuration: Configuration):
self.event_handler = event_handler
self.configuration = configuration
self.rng = tcod.random.Random(seed=configuration.random_seed)
map_size = configuration.map_size
self.map = Map(map_size)
self.player = Object('@', tcod.white, x=int(map_size.width / 2), y=int(map_size.height / 2))
self.objects: AbstractSet[Object] = {self.player}
for _ in range(self.rng.randint(1, 15)):
self.objects.add(Object('@', color=tcod.yellow, x=self.rng.randint(0, map_size.width), y=self.rng.randint(0, map_size.height)))
def handle_event(self, event: tcod.event.Event):
action = self.event_handler.dispatch(event)
if not action:
return
if isinstance(action, MovePlayerAction):
map_size = self.configuration.map_size
new_player_position = Point(max(0, min(map_size.width - 1, self.player.x + action.direction[0])),
max(0, min(map_size.height - 1, self.player.y + action.direction[1])))
can_move_to_level_position = not self.map.tile_for_point(new_player_position).blocks_movement
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'Moving player to {new_player_position}; can_move:{can_move_to_level_position} overlaps:{overlaps_an_object}')
self.player.move_to(new_player_position)
if isinstance(action, ExitAction):
raise SystemExit()
# if isinstance(action, RegenerateRoomsAction):
# partitions, rooms = generate_rooms(random)
def print_to_console(self, console):
self.map.print_to_console(console)
for obj in self.objects:
obj.print_to_console(console)

View file

@ -10,6 +10,7 @@ import logging
import os.path
import random
import tcod
from .engine import Configuration, Engine
from .events import EventHandler, ExitAction, MovePlayerAction, RegenerateRoomsAction
from .geometry import Point, Rect, Size, Vector
from .object import Object
@ -75,83 +76,15 @@ def main(argv):
level = [[Tile(False) for y in range(MAP_HEIGHT)] for x in range(MAP_WIDTH)]
random = tcod.random.Random()
partitions, rooms = generate_rooms(random)
objects = [PLAYER, NPC]
event_handler = EventHandler()
configuration = Configuration(map_size=Size(MAP_WIDTH, MAP_HEIGHT))
engine = Engine(event_handler, configuration)
with tcod.context.new(columns=console.width, rows=console.height, tileset=tileset) as context:
while True:
#
# Draw
#
console.clear()
for part in partitions:
console.draw_frame(part.x, part.y, part.width, part.height, bg=(40, 40, 80), clear=True, decoration="···· ····")
for room in rooms:
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)
for obj in objects:
obj.print(console)
engine.print_to_console(console)
context.present(console)
#
# Handle Events
#
for event in tcod.event.wait():
action = event_handler.dispatch(event)
if not action:
continue
if isinstance(action, MovePlayerAction):
new_player_position = Point(max(0, min(CONSOLE_WIDTH, PLAYER.x + action.direction[0])),
max(0, min(CONSOLE_HEIGHT, PLAYER.y + action.direction[1])))
can_move_to_level_position = not level[new_player_position.x][new_player_position.y].blocks_movement
overlaps_an_object = any(new_player_position.x == obj.x and new_player_position.y == obj.y for obj in objects)
if can_move_to_level_position and not overlaps_an_object:
LOG.debug(f'Moving player to {new_player_position}; can_move:{can_move_to_level_position} overlaps:{overlaps_an_object}')
PLAYER.move_to(new_player_position)
if isinstance(action, ExitAction):
raise SystemExit()
if isinstance(action, RegenerateRoomsAction):
partitions, rooms = generate_rooms(random)
def generate_rooms(random: tcod.random.Random) -> List[Rect]:
bsp = tcod.bsp.BSP(x=0, y=0, width=MAP_WIDTH, height=MAP_HEIGHT)
bsp.split_recursive(
depth=4,
min_width=8, min_height=8,
max_horizontal_ratio=1.5, max_vertical_ratio=1.5)
partitions = []
rooms = []
indent = 0
for node in bsp.pre_order():
if node.children:
LOG.debug(f'{" " * indent}{Rect(node.x, node.y, node.width, node.height)}')
indent += 2
# TODO: Connect the two child rooms
else:
LOG.debug(f'{" " * indent}{Rect(node.x, node.y, node.width, node.height)} (room)')
size = Size(random.randint(5, min(15, max(5, node.width - 2))),
random.randint(5, min(15, max(5, node.height - 2))))
origin = Point(node.x + random.randint(1, max(1, node.width - size.width - 1)),
node.y + random.randint(1, max(1, node.height - size.height - 1)))
room = Rect(origin.x, origin.y, size.width, size.height)
LOG.debug(f'{" " * indent}`-> {room}')
partitions.append(node)
rooms.append(room)
indent -= 2
return partitions, rooms
engine.handle_event(event)

View file

@ -39,5 +39,5 @@ class Object:
self.__x = point.x
self.__y = point.y
def print(self, console: tcod.Console) -> None:
def print_to_console(self, console: tcod.Console) -> None:
console.print(x=self.__x, y=self.__y, string=self.__symbol, fg=self.__color)