From 018dbe35b8da29142ea946ddee9f25b528745f10 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Mon, 10 Jan 2022 13:47:10 -0500 Subject: [PATCH] Refactor movement to allow diagonal movement, add basic attack logs --- actions.py | 24 +++++++++++++++------ components/ai.py | 6 +++--- entity.py | 2 +- input_handlers.py | 53 +++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/actions.py b/actions.py index 70dc44e..5bf200a 100644 --- a/actions.py +++ b/actions.py @@ -4,11 +4,11 @@ from typing import Optional, Tuple, TYPE_CHECKING, overload if TYPE_CHECKING: from engine import Engine - from entity import Entity + from entity import Actor, Entity class Action: - def __init__(self, entity: Entity) -> None: + def __init__(self, entity: Actor) -> None: super().__init__() self.entity = entity @@ -40,7 +40,7 @@ class WaitAction(Action): class ActionWithDirection(Action): - def __init__(self, entity, dx: int, dy: int): + def __init__(self, entity: Actor, dx: int, dy: int): super().__init__(entity) self.dx = dx @@ -56,14 +56,26 @@ class ActionWithDirection(Action): """Return the blocking entity at this action's destination.""" return self.engine.game_map.get_blocking_entity_at_location(*self.dest_xy) + @property + def target_actor(self) -> Optional[Actor]: + """Return the actor at this action's destination.""" + return self.engine.game_map.get_actor_at_location(*self.dest_xy) + class MeleeAction(ActionWithDirection): def perform(self) -> None: - target = self.blocking_entity + target = self.target_actor if not target: return # No entity to attack. - print(f"You kick the {target.name}, much to its annoyance!") + damage = self.entity.fighter.power - target.fighter.defense + + attack_desc = f"{self.entity.name.capitalize()} attacks {target.name}" + if damage > 0: + print(f"{attack_desc} for {damage} hit points.") + target.fighter.hp -= damage + else: + print(f"{attack_desc} but does no damage.") class MovementAction(ActionWithDirection): @@ -82,7 +94,7 @@ class MovementAction(ActionWithDirection): class BumpAction(ActionWithDirection): def perform(self) -> None: - if self.blocking_entity: + if self.target_actor: return MeleeAction(self.entity, self.dx, self.dy).perform() else: return MovementAction(self.entity, self.dx, self.dy).perform() diff --git a/components/ai.py b/components/ai.py index f3e664c..e77c061 100644 --- a/components/ai.py +++ b/components/ai.py @@ -33,7 +33,7 @@ class BaseAI(Action, BaseComponent): cost[entity.x, entity.y] += 10 # Create a graph from the cost array and pass that graph to a new pathfinder. - graph = tcod.path.SimpleGraph(cost, cardinal=2, diagonal=3) + graph = tcod.path.SimpleGraph(cost=cost, cardinal=2, diagonal=3) pathfinder = tcod.path.Pathfinder(graph) pathfinder.add_root((self.entity.x, self.entity.y)) # Start position @@ -69,7 +69,7 @@ class HostileEnemy(BaseAI): return MovementAction( self.entity, dest_x - self.entity.x, - dest_y - self.entity.y + dest_y - self.entity.y, ).perform() - return WaitAction(self.entity).perform() \ No newline at end of file + return WaitAction(self.entity).perform() diff --git a/entity.py b/entity.py index 70eb73e..2bbe40e 100644 --- a/entity.py +++ b/entity.py @@ -86,7 +86,7 @@ class Actor(Entity): blocks_movement=True ) - self.ai = Optional[BaseAI] = ai_cls(self) + self.ai: Optional[BaseAI] = ai_cls(self) self.fighter = fighter self.fighter.entity = self diff --git a/input_handlers.py b/input_handlers.py index 609d8ef..bdc924c 100644 --- a/input_handlers.py +++ b/input_handlers.py @@ -4,11 +4,49 @@ from typing import Optional, TYPE_CHECKING import tcod.event -from actions import Action, EscapeAction, BumpAction +from actions import Action, BumpAction, EscapeAction, WaitAction if TYPE_CHECKING: from engine import Engine +MOVE_KEYS = { + # Arrow keys + tcod.event.K_UP: (0, -1), + tcod.event.K_DOWN: (0, 1), + tcod.event.K_LEFT: (-1, 0), + tcod.event.K_RIGHT: (1, 0), + tcod.event.K_HOME: (-1, -1), + tcod.event.K_END: (-1, 1), + tcod.event.K_PAGEUP: (1, -1), + tcod.event.K_PAGEDOWN: (1, 1), + + # Numpad keys + tcod.event.K_KP_1: (-1, 1), + tcod.event.K_KP_2: (0, 1), + tcod.event.K_KP_3: (1, 1), + tcod.event.K_KP_4: (-1, 0), + tcod.event.K_KP_6: (1, 0), + tcod.event.K_KP_7: (-1, -1), + tcod.event.K_KP_8: (0, -1), + tcod.event.K_KP_9: (1, -1), + + # Vi keys + tcod.event.K_h: (-1, 0), + tcod.event.K_j: (0, 1), + tcod.event.K_k: (0, -1), + tcod.event.K_l: (1, 0), + tcod.event.K_y: (-1, -1), + tcod.event.K_u: (1, -1), + tcod.event.K_b: (-1, 1), + tcod.event.K_n: (1, 1), +} + +WAIT_KEYS = { + tcod.event.K_PERIOD, + tcod.event.K_KP_5, + tcod.event.K_CLEAR, +} + class EventHandler(tcod.event.EventDispatch[Action]): def __init__(self, engine: Engine): @@ -33,14 +71,11 @@ class EventHandler(tcod.event.EventDispatch[Action]): player = self.engine.player - if key == tcod.event.K_UP: - action = BumpAction(player, dx=0, dy=-1) - elif key == tcod.event.K_DOWN: - action = BumpAction(player, dx=0, dy=1) - elif key == tcod.event.K_LEFT: - action = BumpAction(player, dx=-1, dy=0) - elif key == tcod.event.K_RIGHT: - action = BumpAction(player, dx=1, dy=0) + if key in MOVE_KEYS: + dx, dy = MOVE_KEYS[key] + action = BumpAction(player, dx, dy) + elif key in WAIT_KEYS: + action = WaitAction(player) elif key == tcod.event.K_ESCAPE: action = EscapeAction(player)