Restructure event handling
Events start in the Interface. The interface gets first crack at any incoming events. If the interface doesn't handle the event, it is given to the engine. The engine has an EngineEventHandler that yields actions just like the event handler prior to this change. The interface's event handler passes events to each window in the interface. Windows can choose to handle events however they like, and they return a bool indicating whether the event was fully handled.
This commit is contained in:
parent
ee1c6f2222
commit
003aedf30e
6 changed files with 197 additions and 116 deletions
|
@ -9,6 +9,7 @@ from . import log
|
||||||
from .configuration import Configuration, FontConfiguration, FontConfigurationError
|
from .configuration import Configuration, FontConfiguration, FontConfigurationError
|
||||||
from .engine import Engine
|
from .engine import Engine
|
||||||
from .geometry import Size
|
from .geometry import Size
|
||||||
|
from .interface import Interface
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv, *a, **kw):
|
def parse_args(argv, *a, **kw):
|
||||||
|
@ -45,15 +46,15 @@ def main(argv):
|
||||||
|
|
||||||
configuration = Configuration(
|
configuration = Configuration(
|
||||||
console_font_configuration=font_config,
|
console_font_configuration=font_config,
|
||||||
map_size=Size(80, 24),
|
map_size=Size(80, 40),
|
||||||
sandbox=args.sandbox)
|
sandbox=args.sandbox)
|
||||||
|
|
||||||
engine = Engine(configuration)
|
engine = Engine(configuration)
|
||||||
|
interface = Interface(configuration.console_size, engine)
|
||||||
tileset = configuration.console_font_configuration.tileset
|
tileset = configuration.console_font_configuration.tileset
|
||||||
console = tcod.Console(*configuration.console_size.numpy_shape, order='F')
|
|
||||||
with tcod.context.new(columns=console.width, rows=console.height, tileset=tileset) as context:
|
with tcod.context.new(columns=interface.console.width, rows=interface.console.height, tileset=tileset) as context:
|
||||||
engine.run_event_loop(context, console)
|
interface.run_event_loop(context)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
'''Defines the core game engine.'''
|
'''Defines the core game engine.'''
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from typing import TYPE_CHECKING, List, MutableSet, NoReturn, Optional
|
from typing import TYPE_CHECKING, List, MutableSet, Optional
|
||||||
|
|
||||||
import tcod
|
import tcod
|
||||||
|
|
||||||
|
@ -13,20 +13,21 @@ from .actions.action import Action, ActionWithActor
|
||||||
from .actions.result import ActionResult
|
from .actions.result import ActionResult
|
||||||
from .ai import HostileEnemy
|
from .ai import HostileEnemy
|
||||||
from .configuration import Configuration
|
from .configuration import Configuration
|
||||||
from .events import GameOverEventHandler, MainGameEventHandler
|
from .events import EngineEventHandler, GameOverEventHandler
|
||||||
from .geometry import Point, Size
|
from .geometry import Point
|
||||||
from .interface import Interface
|
|
||||||
from .map import Map
|
from .map import Map
|
||||||
from .map.generator import RoomsAndCorridorsGenerator
|
from .map.generator import RoomsAndCorridorsGenerator
|
||||||
from .map.generator.cellular_atomata import CellularAtomataMapGenerator
|
from .map.generator.cellular_atomata import CellularAtomataMapGenerator
|
||||||
from .map.generator.room import BSPRectMethod, CellularAtomatonRoomMethod, OrRoomMethod, RoomGenerator, RandomRectMethod, RectangularRoomMethod
|
from .map.generator.room import (
|
||||||
|
BSPRectMethod,
|
||||||
|
CellularAtomatonRoomMethod,
|
||||||
|
OrRoomMethod,
|
||||||
|
RoomGenerator,
|
||||||
|
RectangularRoomMethod)
|
||||||
from .map.generator.corridor import ElbowCorridorGenerator
|
from .map.generator.corridor import ElbowCorridorGenerator
|
||||||
from .messages import MessageLog
|
from .messages import MessageLog
|
||||||
from .object import Actor, Entity, Hero, Monster
|
from .object import Actor, Entity, Hero, Monster
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .events import EventHandler
|
|
||||||
|
|
||||||
|
|
||||||
class Engine:
|
class Engine:
|
||||||
'''The main game engine.
|
'''The main game engine.
|
||||||
|
@ -76,7 +77,7 @@ class Engine:
|
||||||
ElbowCorridorGenerator())
|
ElbowCorridorGenerator())
|
||||||
self.map = Map(config, map_generator)
|
self.map = Map(config, map_generator)
|
||||||
|
|
||||||
self.event_handler: 'EventHandler' = MainGameEventHandler(self)
|
self.event_handler = EngineEventHandler(self)
|
||||||
|
|
||||||
self.__current_mouse_point: Optional[Point] = None
|
self.__current_mouse_point: Optional[Point] = None
|
||||||
self.__mouse_path_points: Optional[List[Point]] = None
|
self.__mouse_path_points: Optional[List[Point]] = None
|
||||||
|
@ -112,37 +113,16 @@ class Engine:
|
||||||
|
|
||||||
self.update_field_of_view()
|
self.update_field_of_view()
|
||||||
|
|
||||||
# Interface elements
|
|
||||||
self.interface = Interface(Size(80, 50), self.map, self.message_log)
|
|
||||||
self.message_log.add_message('Greetings adventurer!', fg=(127, 127, 255), stack=False)
|
self.message_log.add_message('Greetings adventurer!', fg=(127, 127, 255), stack=False)
|
||||||
|
|
||||||
def print_to_console(self, console):
|
|
||||||
'''Print the whole game to the given console.'''
|
|
||||||
self.map.highlight_points(self.__mouse_path_points or [])
|
|
||||||
|
|
||||||
sorted_entities = sorted(self.entities, key=lambda e: e.render_order.value)
|
|
||||||
self.interface.update(self.current_turn, self.hero, sorted_entities)
|
|
||||||
|
|
||||||
self.interface.draw(console)
|
|
||||||
|
|
||||||
def run_event_loop(self, context: tcod.context.Context, console: tcod.Console) -> NoReturn:
|
|
||||||
'''Run the event loop forever. This method never returns.'''
|
|
||||||
while True:
|
|
||||||
console.clear()
|
|
||||||
self.print_to_console(console)
|
|
||||||
context.present(console)
|
|
||||||
|
|
||||||
self.begin_turn()
|
|
||||||
self.event_handler.handle_events(context)
|
|
||||||
self.finish_turn()
|
|
||||||
|
|
||||||
def process_input_action(self, action: Action):
|
def process_input_action(self, action: Action):
|
||||||
'''Process an Action from player input'''
|
'''Process an Action from player input'''
|
||||||
|
|
||||||
if not isinstance(action, ActionWithActor):
|
if not isinstance(action, ActionWithActor):
|
||||||
action.perform(self)
|
action.perform(self)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.begin_turn()
|
||||||
|
|
||||||
log.ACTIONS_TREE.info('Processing Hero Actions')
|
log.ACTIONS_TREE.info('Processing Hero Actions')
|
||||||
log.ACTIONS_TREE.info('|-> %s', action.actor)
|
log.ACTIONS_TREE.info('|-> %s', action.actor)
|
||||||
|
|
||||||
|
@ -160,6 +140,8 @@ class Engine:
|
||||||
self.process_entity_actions()
|
self.process_entity_actions()
|
||||||
self.update_field_of_view()
|
self.update_field_of_view()
|
||||||
|
|
||||||
|
self.finish_turn()
|
||||||
|
|
||||||
def process_entity_actions(self):
|
def process_entity_actions(self):
|
||||||
'''Run AI for entities that have them, and process actions from those AIs'''
|
'''Run AI for entities that have them, and process actions from those AIs'''
|
||||||
hero_position = self.hero.position
|
hero_position = self.hero.position
|
||||||
|
|
|
@ -1,65 +1,26 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
'''Defines event handling mechanisms.'''
|
|
||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
import tcod
|
import tcod
|
||||||
|
import tcod.event as tev
|
||||||
|
|
||||||
from . import log
|
|
||||||
from .actions.action import Action
|
from .actions.action import Action
|
||||||
from .actions.game import ExitAction, RegenerateRoomsAction, BumpAction, WaitAction
|
from .actions.game import BumpAction, ExitAction, RegenerateRoomsAction, WaitAction
|
||||||
from .geometry import Direction, Point
|
from .geometry import Direction
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .engine import Engine
|
from .engine import Engine
|
||||||
|
|
||||||
|
|
||||||
class EventHandler(tcod.event.EventDispatch[Action]):
|
class EngineEventHandler(tev.EventDispatch[Action]):
|
||||||
'''Abstract event handler class'''
|
'''Handles event on behalf of the game engine, dispatching Actions back to the engine.'''
|
||||||
|
|
||||||
def __init__(self, engine: 'Engine'):
|
def __init__(self, engine: 'Engine'):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
|
|
||||||
def handle_events(self, context: tcod.context.Context):
|
def ev_keydown(self, event: tev.KeyDown) -> Optional[Action]:
|
||||||
'''Wait for events and handle them.'''
|
|
||||||
for event in tcod.event.wait():
|
|
||||||
context.convert_event(event)
|
|
||||||
self.handle_event(event)
|
|
||||||
|
|
||||||
def handle_event(self, event: tcod.event.Event):
|
|
||||||
'''
|
|
||||||
Handle an event by transforming it into an Action and processing it until it is completed. If the Action
|
|
||||||
succeeds, also process actions from other Entities.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
event : tcod.event.Event
|
|
||||||
The event to handle
|
|
||||||
'''
|
|
||||||
action = self.dispatch(event)
|
|
||||||
|
|
||||||
# Unhandled event. Ignore it.
|
|
||||||
if not action:
|
|
||||||
log.EVENTS.debug('Unhandled event: %s', event)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.engine.process_input_action(action)
|
|
||||||
|
|
||||||
def ev_quit(self, event: tcod.event.Quit) -> Optional[Action]:
|
|
||||||
return ExitAction()
|
|
||||||
|
|
||||||
|
|
||||||
class MainGameEventHandler(EventHandler):
|
|
||||||
'''
|
|
||||||
Handler of `tcod` events for the main game.
|
|
||||||
|
|
||||||
Receives input from the player and dispatches actions to the game engine to interat with the hero and other objects
|
|
||||||
in the game.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]:
|
|
||||||
action: Optional[Action] = None
|
action: Optional[Action] = None
|
||||||
|
|
||||||
hero = self.engine.hero
|
hero = self.engine.hero
|
||||||
|
@ -84,8 +45,6 @@ class MainGameEventHandler(EventHandler):
|
||||||
action = BumpAction(hero, Direction.NorthEast)
|
action = BumpAction(hero, Direction.NorthEast)
|
||||||
case tcod.event.KeySym.y:
|
case tcod.event.KeySym.y:
|
||||||
action = BumpAction(hero, Direction.NorthWest)
|
action = BumpAction(hero, Direction.NorthWest)
|
||||||
case tcod.event.KeySym.ESCAPE:
|
|
||||||
action = ExitAction()
|
|
||||||
case tcod.event.KeySym.SPACE:
|
case tcod.event.KeySym.SPACE:
|
||||||
action = RegenerateRoomsAction()
|
action = RegenerateRoomsAction()
|
||||||
case tcod.event.KeySym.PERIOD:
|
case tcod.event.KeySym.PERIOD:
|
||||||
|
@ -94,22 +53,16 @@ class MainGameEventHandler(EventHandler):
|
||||||
|
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def ev_mousemotion(self, event: tcod.event.MouseMotion) -> Optional[Action]:
|
def ev_quit(self, event: tcod.event.Quit) -> Optional[Action]:
|
||||||
mouse_point = Point(event.tile.x, event.tile.y)
|
return ExitAction()
|
||||||
if not self.engine.map.tile_is_in_bounds(mouse_point):
|
|
||||||
mouse_point = None
|
|
||||||
self.engine.update_mouse_point(mouse_point)
|
|
||||||
|
|
||||||
|
|
||||||
class GameOverEventHandler(EventHandler):
|
class GameOverEventHandler(tev.EventDispatch[Action]):
|
||||||
'''When the game is over (the hero dies, the player quits, etc), this event handler takes over.'''
|
'''When the game is over (the hero dies, the player quits, etc), this event handler takes over.'''
|
||||||
|
|
||||||
def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]:
|
def __init__(self, engine: 'Engine'):
|
||||||
action: Optional[Action] = None
|
super().__init__()
|
||||||
|
self.engine = engine
|
||||||
|
|
||||||
sym = event.sym
|
def ev_quit(self, event: tev.Quit) -> Optional[Action]:
|
||||||
match sym:
|
return ExitAction()
|
||||||
case tcod.event.KeySym.ESCAPE:
|
|
||||||
action = ExitAction()
|
|
||||||
|
|
||||||
return action
|
|
||||||
|
|
|
@ -4,15 +4,18 @@
|
||||||
The game's graphical user interface
|
The game's graphical user interface
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from typing import List
|
from typing import NoReturn
|
||||||
|
|
||||||
|
from tcod import event as tev
|
||||||
from tcod.console import Console
|
from tcod.console import Console
|
||||||
|
from tcod.context import Context
|
||||||
|
|
||||||
from .color import HealthBar
|
from .color import HealthBar
|
||||||
|
from .events import InterfaceEventHandler
|
||||||
from .percentage_bar import PercentageBar
|
from .percentage_bar import PercentageBar
|
||||||
from .window import Window, MapWindow
|
from .window import Window, MapWindow
|
||||||
|
from ..engine import Engine
|
||||||
from ..geometry import Point, Rect, Size
|
from ..geometry import Point, Rect, Size
|
||||||
from ..map import Map
|
|
||||||
from ..messages import MessageLog
|
from ..messages import MessageLog
|
||||||
from ..object import Entity, Hero
|
from ..object import Entity, Hero
|
||||||
|
|
||||||
|
@ -20,25 +23,59 @@ from ..object import Entity, Hero
|
||||||
class Interface:
|
class Interface:
|
||||||
'''The game's user interface'''
|
'''The game's user interface'''
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
def __init__(self, size: Size, engine: Engine):
|
||||||
def __init__(self, size: Size, map: Map, message_log: MessageLog):
|
self.engine = engine
|
||||||
self.map_window = MapWindow(Rect(Point(0, 0), Size(size.width, size.height - 5)), map)
|
|
||||||
self.info_window = InfoWindow(Rect(Point(0, size.height - 5), Size(28, 5)))
|
|
||||||
self.message_window = MessageLogWindow(Rect(Point(28, size.height - 5), Size(size.width - 28, 5)), message_log)
|
|
||||||
|
|
||||||
def update(self, turn_count: int, hero: Hero, entities: List[Entity]):
|
self.console = Console(*size.numpy_shape, order='F')
|
||||||
|
|
||||||
|
self.map_window = MapWindow(
|
||||||
|
Rect.from_raw_values(0, 0, size.width, size.height - 5),
|
||||||
|
engine.map)
|
||||||
|
self.info_window = InfoWindow(
|
||||||
|
Rect.from_raw_values(0, size.height - 5, 28, 5))
|
||||||
|
self.message_window = MessageLogWindow(
|
||||||
|
Rect.from_raw_values(28, size.height - 5, size.width - 28, 5),
|
||||||
|
engine.message_log)
|
||||||
|
|
||||||
|
self.event_handler = InterfaceEventHandler(self)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
'''Update game state that the interface needs to render'''
|
'''Update game state that the interface needs to render'''
|
||||||
self.info_window.turn_count = turn_count
|
self.info_window.turn_count = self.engine.current_turn
|
||||||
|
|
||||||
|
hero = self.engine.hero
|
||||||
self.info_window.update_hero(hero)
|
self.info_window.update_hero(hero)
|
||||||
|
|
||||||
self.map_window.update_drawable_map_bounds(hero)
|
self.map_window.update_drawable_map_bounds(hero)
|
||||||
self.map_window.entities = entities
|
|
||||||
|
|
||||||
def draw(self, console: Console):
|
sorted_entities = sorted(self.engine.entities, key=lambda e: e.render_order.value)
|
||||||
|
self.map_window.entities = sorted_entities
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
'''Draw the UI to the console'''
|
'''Draw the UI to the console'''
|
||||||
self.map_window.draw(console)
|
self.map_window.draw(self.console)
|
||||||
self.info_window.draw(console)
|
self.info_window.draw(self.console)
|
||||||
self.message_window.draw(console)
|
self.message_window.draw(self.console)
|
||||||
|
|
||||||
|
def run_event_loop(self, context: Context) -> NoReturn:
|
||||||
|
'''Run the event loop forever. This method never returns.'''
|
||||||
|
while True:
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
self.console.clear()
|
||||||
|
self.draw()
|
||||||
|
context.present(self.console)
|
||||||
|
|
||||||
|
for event in tev.wait():
|
||||||
|
did_handle = self.event_handler.dispatch(event)
|
||||||
|
if did_handle:
|
||||||
|
continue
|
||||||
|
|
||||||
|
action = self.engine.event_handler.dispatch(event)
|
||||||
|
if not action:
|
||||||
|
# The engine didn't handle the event, so just drop it.
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.engine.process_input_action(action)
|
||||||
|
|
||||||
|
|
||||||
class InfoWindow(Window):
|
class InfoWindow(Window):
|
||||||
|
|
50
erynrl/interface/events.py
Normal file
50
erynrl/interface/events.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
'''Defines event handling mechanisms.'''
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from tcod import event as tev
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import Interface
|
||||||
|
|
||||||
|
|
||||||
|
class InterfaceEventHandler(tev.EventDispatch[bool]):
|
||||||
|
'''The event handler for the user interface.'''
|
||||||
|
|
||||||
|
def __init__(self, interface: 'Interface'):
|
||||||
|
super().__init__()
|
||||||
|
self.interface = interface
|
||||||
|
|
||||||
|
self._handlers = []
|
||||||
|
self._refresh_handlers()
|
||||||
|
|
||||||
|
def _refresh_handlers(self):
|
||||||
|
self._handlers = [
|
||||||
|
self.interface.map_window.event_handler,
|
||||||
|
self.interface.message_window.event_handler,
|
||||||
|
self.interface.info_window.event_handler,
|
||||||
|
]
|
||||||
|
|
||||||
|
def ev_keydown(self, event: tev.KeyDown) -> bool:
|
||||||
|
return self._handle_event(event)
|
||||||
|
|
||||||
|
def ev_keyup(self, event: tev.KeyUp) -> bool:
|
||||||
|
return self._handle_event(event)
|
||||||
|
|
||||||
|
def ev_mousemotion(self, event: tev.MouseMotion) -> bool:
|
||||||
|
return self._handle_event(event)
|
||||||
|
|
||||||
|
def ev_mousebuttondown(self, event: tev.MouseButtonDown) -> bool:
|
||||||
|
return self._handle_event(event)
|
||||||
|
|
||||||
|
def ev_mousebuttonup(self, event: tev.MouseButtonUp) -> bool:
|
||||||
|
return self._handle_event(event)
|
||||||
|
|
||||||
|
def _handle_event(self, event: tev.Event) -> bool:
|
||||||
|
for handler in self._handlers:
|
||||||
|
if handler and handler.dispatch(event):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
|
@ -1,8 +1,9 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from tcod import event as tev
|
||||||
from tcod.console import Console
|
from tcod.console import Console
|
||||||
|
|
||||||
from .. import log
|
from .. import log
|
||||||
|
@ -12,11 +13,46 @@ from ..object import Entity, Hero
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
'''A user interface window. It can be framed.'''
|
'''A user interface window. It can be framed and it can handle events.'''
|
||||||
|
|
||||||
def __init__(self, bounds: Rect, *, framed: bool = True):
|
class EventHandler(tev.EventDispatch[bool]):
|
||||||
|
'''
|
||||||
|
Handles events for a Window. Event dispatch methods return True if the event
|
||||||
|
was handled and no further action is needed.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, window: 'Window'):
|
||||||
|
super().__init__()
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
def mouse_point_for_event(self, event: tev.MouseState) -> Point:
|
||||||
|
'''
|
||||||
|
Return the mouse point in tiles for a window event. Raises a ValueError
|
||||||
|
if the event is not a mouse event.
|
||||||
|
'''
|
||||||
|
if not isinstance(event, tev.MouseState):
|
||||||
|
raise ValueError("Can't get mouse point for non-mouse event")
|
||||||
|
|
||||||
|
return Point(event.tile.x, event.tile.y)
|
||||||
|
|
||||||
|
def ev_keydown(self, event: tev.KeyDown) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def ev_keyup(self, event: tev.KeyUp) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def ev_mousemotion(self, event: tev.MouseMotion) -> bool:
|
||||||
|
mouse_point = self.mouse_point_for_event(event)
|
||||||
|
|
||||||
|
if mouse_point not in self.window.bounds:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __init__(self, bounds: Rect, *, framed: bool = True, event_handler: Optional['EventHandler'] = None):
|
||||||
self.bounds = bounds
|
self.bounds = bounds
|
||||||
self.is_framed = framed
|
self.is_framed = framed
|
||||||
|
self.event_handler = event_handler or self.__class__.EventHandler(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def drawable_bounds(self) -> Rect:
|
def drawable_bounds(self) -> Rect:
|
||||||
|
@ -28,6 +64,14 @@ class Window:
|
||||||
return self.bounds.inset_rect(1, 1, 1, 1)
|
return self.bounds.inset_rect(1, 1, 1, 1)
|
||||||
return self.bounds
|
return self.bounds
|
||||||
|
|
||||||
|
def convert_console_point(self, point: Point) -> Optional[Point]:
|
||||||
|
'''
|
||||||
|
Converts a point in console coordinates to window-relative coordinates.
|
||||||
|
If the point is out of bounds of the window, return None.
|
||||||
|
'''
|
||||||
|
converted_point = point - Vector.from_point(self.bounds.origin)
|
||||||
|
return converted_point if converted_point in self.bounds else None
|
||||||
|
|
||||||
def draw(self, console: Console):
|
def draw(self, console: Console):
|
||||||
'''Draw the window to the conole'''
|
'''Draw the window to the conole'''
|
||||||
if self.is_framed:
|
if self.is_framed:
|
||||||
|
@ -46,6 +90,20 @@ class Window:
|
||||||
class MapWindow(Window):
|
class MapWindow(Window):
|
||||||
'''A Window that displays a game map'''
|
'''A Window that displays a game map'''
|
||||||
|
|
||||||
|
class EventHandler(Window.EventHandler):
|
||||||
|
'''An event handler for the MapWindow.'''
|
||||||
|
|
||||||
|
def ev_mousemotion(self, event: tev.MouseMotion) -> bool:
|
||||||
|
mouse_point = self.window.convert_console_point(self.mouse_point_for_event(event))
|
||||||
|
if not mouse_point:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# TODO: Convert window point to map point
|
||||||
|
# TODO: Perform a path finding operation from the hero to the mouse point
|
||||||
|
# TODO: Highlight those points on the map
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
# pylint: disable=redefined-builtin
|
||||||
def __init__(self, bounds: Rect, map: Map, **kwargs):
|
def __init__(self, bounds: Rect, map: Map, **kwargs):
|
||||||
super().__init__(bounds, **kwargs)
|
super().__init__(bounds, **kwargs)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue