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