going-rogue/roguebasin/engine.py

101 lines
3.3 KiB
Python

#!/usr/bin/env python3
# Eryn Wells <eryn@erynwells.me>
import logging
import random
import tcod
from . import monsters
from .events import EventHandler
from .geometry import Direction, Size
from .map import Map
from .monsters import Monster
from .object import Entity, Hero
from dataclasses import dataclass
from typing import MutableSet
LOG = logging.getLogger('engine')
EVENT_LOG = logging.getLogger('events')
@dataclass
class Configuration:
map_size: Size
class Engine:
def __init__(self, event_handler: EventHandler, configuration: Configuration):
self.event_handler = event_handler
self.configuration = configuration
self.rng = tcod.random.Random()
self.map = Map(configuration.map_size)
first_room = self.map.generator.rooms[0]
hero_start_position = first_room.center
self.hero = Hero(position=hero_start_position)
self.entities: MutableSet[Entity] = {self.hero}
for room in self.map.rooms:
should_spawn_monster_chance = random.random()
if should_spawn_monster_chance < 0.4:
continue
floor = list(room.walkable_tiles)
while True:
random_start_position = random.choice(floor)
if not any(ent.position == random_start_position for ent in self.entities):
break
for _ in range(2):
spawn_monster_chance = random.random()
if spawn_monster_chance > 0.8:
monster = Monster(monsters.Troll, position=random_start_position)
else:
monster = Monster(monsters.Orc, position=random_start_position)
LOG.info('Spawning monster %s', monster)
self.entities.add(monster)
self.update_field_of_view()
def handle_event(self, event: tcod.event.Event):
action = self.event_handler.dispatch(event)
if not action:
return
action.perform(self, self.hero)
directions = list(Direction.all())
moved_entities: MutableSet[Entity] = {self.hero}
for ent in self.entities:
if ent == self.hero:
continue
while True:
new_position = ent.position + random.choice(directions)
overlaps_with_previously_moved_entity = any(new_position == moved_ent.position for moved_ent in moved_entities)
if not overlaps_with_previously_moved_entity and self.map.tile_is_walkable(new_position):
ent.position = new_position
moved_entities.add(ent)
break
self.update_field_of_view()
def print_to_console(self, console):
self.map.print_to_console(console)
for ent in self.entities:
# Only print entities that are in the field of view
if not self.map.visible[tuple(ent.position)]:
continue
ent.print_to_console(console)
def update_field_of_view(self) -> None:
'''Compute visible area of the map based on the player's position and point of view.'''
self.map.visible[:] = tcod.map.compute_fov(
self.map.tiles['transparent'],
tuple(self.hero.position),
radius=8)
# Visible tiles should be added to the explored list
self.map.explored |= self.map.visible