From ae1c7f5ce2dec8b5605a0e2f6cdd76f06c712362 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 28 May 2022 08:52:54 -0700 Subject: [PATCH] Refactor events into their own package Most of the existing actions are game actions (they control the player character) so they live in actions.game. Eventually, there will be modules for different kinds of actions that only apply to, e.g. modal UI. --- erynrl/actions/__init__.py | 4 ++ erynrl/actions/action.py | 45 ++++++++++++ erynrl/{actions.py => actions/game.py} | 94 +++----------------------- erynrl/actions/result.py | 47 +++++++++++++ erynrl/ai.py | 3 +- erynrl/events.py | 11 ++- 6 files changed, 114 insertions(+), 90 deletions(-) create mode 100644 erynrl/actions/__init__.py create mode 100644 erynrl/actions/action.py rename erynrl/{actions.py => actions/game.py} (73%) create mode 100644 erynrl/actions/result.py diff --git a/erynrl/actions/__init__.py b/erynrl/actions/__init__.py new file mode 100644 index 0000000..11a8220 --- /dev/null +++ b/erynrl/actions/__init__.py @@ -0,0 +1,4 @@ +# Eryn Wells + +from .action import Action +from .result import ActionResult diff --git a/erynrl/actions/action.py b/erynrl/actions/action.py new file mode 100644 index 0000000..3dd865a --- /dev/null +++ b/erynrl/actions/action.py @@ -0,0 +1,45 @@ +# Eryn Wells + +from typing import TYPE_CHECKING, Optional + +from ..object import Actor +from .result import ActionResult + +if TYPE_CHECKING: + from ..engine import Engine + +class Action: + '''An action that an Entity should perform.''' + + def __init__(self, actor: Optional[Actor] = None): + self.actor = actor + + def perform(self, engine: 'Engine') -> ActionResult: + '''Perform this action. + + Parameters + ---------- + engine : Engine + The game engine + + Returns + ------- + ActionResult + A result object reflecting how the action was handled, and what follow-up actions, if any, are needed to + complete the action. + ''' + raise NotImplementedError() + + def failure(self) -> ActionResult: + '''Create an ActionResult indicating failure with no follow-up''' + return ActionResult(self, success=False) + + def success(self) -> ActionResult: + '''Create an ActionResult indicating success with no follow-up''' + return ActionResult(self, success=True) + + 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.py b/erynrl/actions/game.py similarity index 73% rename from erynrl/actions.py rename to erynrl/actions/game.py index c15ea2c..3d03df0 100644 --- a/erynrl/actions.py +++ b/erynrl/actions/game.py @@ -18,96 +18,24 @@ Action : Base class of all actions WaitAction ''' -from typing import Optional, TYPE_CHECKING +from typing import TYPE_CHECKING -from . import items -from . import log -from .geometry import Direction -from .object import Actor, Item +from .. import items +from .. import log +from ..geometry import Direction +from ..object import Actor, Item +from .action import Action +from .result import ActionResult if TYPE_CHECKING: - from .engine import Engine - -class ActionResult: - '''The result of an Action. - - `Action.perform()` returns an instance of this class to inform the caller of the result - - Attributes - ---------- - action : Action - The Action that was performed - success : bool, optional - True if the action succeeded - done : bool, optional - True if the action is complete, and no follow-up action is needed - alternate : Action, optional - An alternate action to perform if this action failed - ''' - - def __init__(self, action: 'Action', *, - success: Optional[bool] = None, - done: Optional[bool] = None, - alternate: Optional['Action'] = None): - self.action = action - self.alternate = alternate - - if success is not None: - self.success = success - elif alternate: - self.success = False - else: - self.success = True - - if done is not None: - self.done = done - elif self.success: - self.done = True - else: - self.done = not alternate - - def __repr__(self): - return f'{self.__class__.__name__}({self.action!r}, success={self.success}, done={self.done}, alternate={self.alternate!r})' - -class Action: - '''An action that an Entity should perform.''' - - def __init__(self, actor: Optional[Actor]): - self.actor = actor - - def perform(self, engine: 'Engine') -> ActionResult: - '''Perform this action. - - Parameters - ---------- - engine : Engine - The game engine - - Returns - ------- - ActionResult - A result object reflecting how the action was handled, and what follow-up actions, if any, are needed to - complete the action. - ''' - raise NotImplementedError() - - def failure(self) -> ActionResult: - '''Create an ActionResult indicating failure with no follow-up''' - return ActionResult(self, success=False) - - def success(self) -> ActionResult: - '''Create an ActionResult indicating success with no follow-up''' - return ActionResult(self, success=True) - - def __str__(self) -> str: - return f'{self.__class__.__name__} for {self.actor!s}' - - def __repr__(self): - return f'{self.__class__.__name__}({self.actor!r})' + from ..engine import Engine class ExitAction(Action): '''Exit the game.''' + def __init__(self): + super().__init__(None) + def perform(self, engine: 'Engine') -> ActionResult: raise SystemExit() diff --git a/erynrl/actions/result.py b/erynrl/actions/result.py new file mode 100644 index 0000000..c853bc5 --- /dev/null +++ b/erynrl/actions/result.py @@ -0,0 +1,47 @@ +# Eryn Wells + +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from .action import Action + +class ActionResult: + '''The result of an Action. + + `Action.perform()` returns an instance of this class to inform the caller of the result + + Attributes + ---------- + action : Action + The Action that was performed + success : bool, optional + True if the action succeeded + done : bool, optional + True if the action is complete, and no follow-up action is needed + alternate : Action, optional + An alternate action to perform if this action failed + ''' + + def __init__(self, action: 'Action', *, + success: Optional[bool] = None, + done: Optional[bool] = None, + alternate: Optional['Action'] = None): + self.action = action + self.alternate = alternate + + if success is not None: + self.success = success + elif alternate: + self.success = False + else: + self.success = True + + if done is not None: + self.done = done + elif self.success: + self.done = True + else: + self.done = not alternate + + def __repr__(self): + return f'{self.__class__.__name__}({self.action!r}, success={self.success}, done={self.done}, alternate={self.alternate!r})' diff --git a/erynrl/ai.py b/erynrl/ai.py index 5653ecc..2301a74 100644 --- a/erynrl/ai.py +++ b/erynrl/ai.py @@ -7,7 +7,8 @@ import numpy as np import tcod from . import log -from .actions import Action, BumpAction, WaitAction +from .actions import Action +from .actions.game import BumpAction, WaitAction from .components import Component from .geometry import Direction, Point from .object import Entity diff --git a/erynrl/events.py b/erynrl/events.py index 0c736b3..9d9a69f 100644 --- a/erynrl/events.py +++ b/erynrl/events.py @@ -7,7 +7,8 @@ from typing import Optional, TYPE_CHECKING import tcod from . import log -from .actions import Action, ExitAction, RegenerateRoomsAction, BumpAction, WaitAction +from .actions.action import Action +from .actions.game import ExitAction, RegenerateRoomsAction, BumpAction, WaitAction from .geometry import Direction, Point if TYPE_CHECKING: @@ -46,7 +47,7 @@ class EventHandler(tcod.event.EventDispatch[Action]): self.engine.process_input_action(action) def ev_quit(self, event: tcod.event.Quit) -> Optional[Action]: - return ExitAction(self.engine.hero) + return ExitAction() class MainGameEventHandler(EventHandler): ''' @@ -80,7 +81,7 @@ class MainGameEventHandler(EventHandler): case tcod.event.KeySym.y: action = BumpAction(hero, Direction.NorthWest) case tcod.event.KeySym.ESCAPE: - action = ExitAction(hero) + action = ExitAction() case tcod.event.KeySym.SPACE: action = RegenerateRoomsAction(hero) case tcod.event.KeySym.PERIOD: @@ -100,11 +101,9 @@ class GameOverEventHandler(EventHandler): def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]: action: Optional[Action] = None - hero = self.engine.hero - sym = event.sym match sym: case tcod.event.KeySym.ESCAPE: - action = ExitAction(hero) + action = ExitAction() return action