From 327cc90b2e182d00bf77beb7a33da9b6a7de78b9 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 11 Mar 2023 01:09:53 -0800 Subject: [PATCH] Remove QuitAction and ActionWithActor! Move quit event handling to the interface and flatten the Action class hierarchy. There are no longer any actions that don't take an Actor. This has the happy side effect of resolving some pylint errors too. :) --- erynrl/actions/action.py | 18 ++++-------------- erynrl/actions/game.py | 20 ++++++-------------- erynrl/ai.py | 6 +++--- erynrl/engine.py | 8 ++++---- erynrl/events.py | 8 +------- erynrl/interface/events.py | 7 ++++++- 6 files changed, 24 insertions(+), 43 deletions(-) diff --git a/erynrl/actions/action.py b/erynrl/actions/action.py index 584d102..c2a9660 100644 --- a/erynrl/actions/action.py +++ b/erynrl/actions/action.py @@ -12,6 +12,10 @@ if TYPE_CHECKING: class Action: '''An action with no specific actor''' + def __init__(self, actor: Actor): + super().__init__() + self.actor = actor + # pylint: disable=unused-argument def perform(self, engine: 'Engine') -> ActionResult: '''Perform this action. @@ -42,17 +46,3 @@ class Action: def __repr__(self): return f'{self.__class__.__name__}()' - - -class ActionWithActor(Action): - '''An action that assigned to an actor''' - - def __init__(self, actor: Actor): - super().__init__() - self.actor = actor - - def __str__(self) -> str: - return f'{self.__class__.__name__} for {self.actor!s}' - - def __repr__(self): - return f'{self.__class__.__name__}({self.actor!r})' diff --git a/erynrl/actions/game.py b/erynrl/actions/game.py index ad960fa..71d7485 100644 --- a/erynrl/actions/game.py +++ b/erynrl/actions/game.py @@ -11,7 +11,6 @@ Action : Base class of all actions BumpAction WalkAction MeleeAction - ExitAction WaitAction ''' @@ -22,21 +21,14 @@ from .. import items from .. import log from ..geometry import Vector from ..object import Actor, Item -from .action import Action, ActionWithActor +from .action import Action from .result import ActionResult if TYPE_CHECKING: from ..engine import Engine -class ExitAction(Action): - '''Exit the game.''' - - def perform(self, engine: 'Engine') -> ActionResult: - raise SystemExit() - - -class MoveAction(ActionWithActor): +class MoveAction(Action): '''An abstract Action that requires a direction to complete.''' def __init__(self, actor: Actor, direction: Vector): @@ -157,7 +149,7 @@ class MeleeAction(MoveAction): return self.success() -class WaitAction(ActionWithActor): +class WaitAction(Action): '''Wait a turn''' def perform(self, engine: 'Engine') -> ActionResult: @@ -174,7 +166,7 @@ class WaitAction(ActionWithActor): return self.success() -class DieAction(ActionWithActor): +class DieAction(Action): '''Kill an Actor''' def perform(self, engine: 'Engine') -> ActionResult: @@ -193,7 +185,7 @@ class DieAction(ActionWithActor): return self.success() -class DropItemAction(ActionWithActor): +class DropItemAction(Action): '''Drop an item''' def __init__(self, actor: 'Actor', item: 'Item'): @@ -205,7 +197,7 @@ class DropItemAction(ActionWithActor): return self.success() -class HealAction(ActionWithActor): +class HealAction(Action): '''Heal a target actor some number of hit points''' def __init__(self, actor: 'Actor', hit_points_to_recover: int): diff --git a/erynrl/ai.py b/erynrl/ai.py index cca9331..c37b186 100644 --- a/erynrl/ai.py +++ b/erynrl/ai.py @@ -7,7 +7,7 @@ import numpy as np import tcod from . import log -from .actions.action import ActionWithActor +from .actions.action import Action from .actions.game import BumpAction, WaitAction from .components import Component from .geometry import Direction, Point @@ -26,7 +26,7 @@ class AI(Component): super().__init__() self.entity = entity - def act(self, engine: 'Engine') -> Optional[ActionWithActor]: + def act(self, engine: 'Engine') -> Optional[Action]: '''Produce an action to perform''' raise NotImplementedError() @@ -38,7 +38,7 @@ class HostileEnemy(AI): beeline for her. ''' - def act(self, engine: 'Engine') -> Optional[ActionWithActor]: + def act(self, engine: 'Engine') -> Optional[Action]: visible_tiles = tcod.map.compute_fov( engine.map.tiles['transparent'], pov=tuple(self.entity.position), diff --git a/erynrl/engine.py b/erynrl/engine.py index d394852..8b13350 100644 --- a/erynrl/engine.py +++ b/erynrl/engine.py @@ -9,7 +9,7 @@ import tcod from . import log from . import monsters -from .actions.action import Action, ActionWithActor +from .actions.action import Action from .actions.result import ActionResult from .ai import HostileEnemy from .configuration import Configuration @@ -110,7 +110,7 @@ class Engine: def process_input_action(self, action: Action): '''Process an Action from player input''' - if not isinstance(action, ActionWithActor): + if not isinstance(action, Action): action.perform(self) return @@ -159,7 +159,7 @@ class Engine: if action: self._perform_action_until_done(action) - def _perform_action_until_done(self, action: ActionWithActor) -> ActionResult: + def _perform_action_until_done(self, action: Action) -> ActionResult: '''Perform the given action and any alternate follow-up actions until the action chain is done.''' result = action.perform(self) @@ -184,7 +184,7 @@ class Engine: if log.ACTIONS_TREE.isEnabledFor(log.INFO) and self.map.visible[tuple(action.actor.position)]: if result.alternate: - alternate_string = f'{result.alternate.__class__.__name__}[{result.alternate.actor.symbol}]' + alternate_string = f'{result.alternate.__class__.__name__}[{result.alternate.actor}]' else: alternate_string = str(result.alternate) log.ACTIONS_TREE.info( diff --git a/erynrl/events.py b/erynrl/events.py index cc6928b..3a9bcf0 100644 --- a/erynrl/events.py +++ b/erynrl/events.py @@ -6,7 +6,7 @@ import tcod import tcod.event as tev from .actions.action import Action -from .actions.game import BumpAction, ExitAction, WaitAction +from .actions.game import BumpAction, WaitAction from .geometry import Direction if TYPE_CHECKING: @@ -51,9 +51,6 @@ class EngineEventHandler(tev.EventDispatch[Action]): return action - def ev_quit(self, event: tcod.event.Quit) -> Optional[Action]: - return ExitAction() - class GameOverEventHandler(tev.EventDispatch[Action]): '''When the game is over (the hero dies, the player quits, etc), this event handler takes over.''' @@ -61,6 +58,3 @@ class GameOverEventHandler(tev.EventDispatch[Action]): def __init__(self, engine: 'Engine'): super().__init__() self.engine = engine - - def ev_quit(self, event: tev.Quit) -> Optional[Action]: - return ExitAction() diff --git a/erynrl/interface/events.py b/erynrl/interface/events.py index bd8e5fc..09d0447 100644 --- a/erynrl/interface/events.py +++ b/erynrl/interface/events.py @@ -2,7 +2,7 @@ '''Defines event handling mechanisms.''' -from typing import TYPE_CHECKING +from typing import NoReturn, TYPE_CHECKING from tcod import event as tev @@ -42,6 +42,11 @@ class InterfaceEventHandler(tev.EventDispatch[bool]): def ev_mousebuttonup(self, event: tev.MouseButtonUp) -> bool: return self._handle_event(event) + def ev_quit(self, event: tev.Quit) -> NoReturn: + # TODO: Maybe show a "do you want to quit?" alert? + # TODO: Probably inform the engine that we're shutting down. + raise SystemExit() + def _handle_event(self, event: tev.Event) -> bool: for handler in self._handlers: if handler and handler.dispatch(event):