When more than one item can be picked up, and options window is shown. Stubs for "using" an item are in place.
147 lines
3.7 KiB
Python
147 lines
3.7 KiB
Python
from __future__ import annotations
|
|
from typing import List, TYPE_CHECKING
|
|
|
|
from floor_map import FloorMap
|
|
|
|
if TYPE_CHECKING:
|
|
from engine import Engine
|
|
from entity import Entity
|
|
|
|
class BaseAction:
|
|
entity: Entity #the entity to which this action applies
|
|
|
|
def __init__(self, entity):
|
|
self.entity = entity
|
|
|
|
def perform(self) -> bool:
|
|
"""return True if the game state should be progressed"""
|
|
raise NotImplementedError()
|
|
|
|
|
|
class QuitAction(BaseAction):
|
|
def __init__(self):
|
|
"""override the base __init__, as no entity is needed"""
|
|
pass
|
|
|
|
def perform(self) -> bool:
|
|
raise SystemExit()
|
|
|
|
|
|
class WaitAction(BaseAction):
|
|
def perform(self) -> bool:
|
|
return True
|
|
|
|
|
|
class MovementAction(BaseAction):
|
|
"""Move an Entity within the map"""
|
|
def __init__(self, entity, xdir: int, ydir: int):
|
|
super().__init__(entity)
|
|
self.xdir = xdir
|
|
self.ydir = ydir
|
|
|
|
def perform(self) -> bool:
|
|
dest_x = self.entity.x + self.xdir
|
|
dest_y = self.entity.y + self.ydir
|
|
|
|
floor_map: FloorMap = self.entity.floor_map
|
|
|
|
#bounds and collision checks
|
|
if not floor_map.in_bounds(dest_x, dest_y):
|
|
return False
|
|
if not floor_map.tiles["walkable"][dest_x, dest_y]:
|
|
return False
|
|
if floor_map.get_all_entities_at(dest_x, dest_y, unwalkable_only=True):
|
|
return False
|
|
|
|
self.entity.set_pos(dest_x, dest_y)
|
|
return True
|
|
|
|
class MeleeAction(BaseAction):
|
|
"""Melee attack from the Entity towards a target"""
|
|
def __init__(self, entity, xdir: int, ydir: int):
|
|
super().__init__(entity)
|
|
self.xdir = xdir
|
|
self.ydir = ydir
|
|
|
|
def perform(self) -> bool:
|
|
dest_x = self.entity.x + self.xdir
|
|
dest_y = self.entity.y + self.ydir
|
|
|
|
targets = self.entity.floor_map.get_all_entities_at(dest_x, dest_y, unwalkable_only=True)
|
|
|
|
if not targets:
|
|
return False
|
|
|
|
target = targets.pop()
|
|
|
|
if not target or not target.stats:
|
|
return False
|
|
|
|
#TODO: better combat system
|
|
|
|
#calculate damage
|
|
damage = self.entity.stats.attack - target.stats.defense
|
|
|
|
#calculate message output
|
|
engine: Engine = self.entity.floor_map.engine
|
|
msg_text = f"{self.entity.name} attacked {target.name}"
|
|
|
|
if damage > 0:
|
|
msg_text += f" for {damage} damage"
|
|
else:
|
|
msg_text += f" but was ineffective"
|
|
|
|
engine.message_log.add_message(text = msg_text)
|
|
|
|
#actually applying the change here, so the player's death event is at the bottom of the message log
|
|
target.stats.current_hp -= damage
|
|
|
|
return True
|
|
|
|
class BumpAction(BaseAction):
|
|
"""Move an Entity within the map, or attack a target if one is found"""
|
|
def __init__(self, entity, xdir: int, ydir: int):
|
|
super().__init__(entity)
|
|
self.xdir = xdir
|
|
self.ydir = ydir
|
|
|
|
def perform(self) -> bool:
|
|
dest_x = self.entity.x + self.xdir
|
|
dest_y = self.entity.y + self.ydir
|
|
|
|
if self.entity.floor_map.get_all_entities_at(dest_x, dest_y, unwalkable_only=True):
|
|
return MeleeAction(self.entity, self.xdir, self.ydir).perform()
|
|
else:
|
|
return MovementAction(self.entity, self.xdir, self.ydir).perform()
|
|
|
|
|
|
class PickupAction(BaseAction):
|
|
"""Pickup an item at the entity's location"""
|
|
def perform(self) -> bool:
|
|
x = self.entity.x
|
|
y = self.entity.y
|
|
|
|
floor_map: FloorMap = self.entity.floor_map
|
|
|
|
item_stack: List[Entity] = floor_map.get_all_entities_at(x, y, items_only=True)
|
|
|
|
if len(item_stack) == 0:
|
|
return False
|
|
elif len(item_stack) == 1:
|
|
floor_map.entities.remove(item_stack[0])
|
|
self.entity.inventory.insert(item_stack[0])
|
|
else:
|
|
options: List[str] = []
|
|
for item in item_stack:#not pythonic, IDC
|
|
options.append(item.name)
|
|
|
|
from event_handlers import OptionSelector #circular imports are a pain
|
|
floor_map.engine.event_handler = OptionSelector(
|
|
floor_map.engine,
|
|
floor_map.engine.event_handler,
|
|
options,
|
|
lambda x: (floor_map.entities.remove(item_stack[x]), self.entity.inventory.insert(item_stack[x])),
|
|
)
|
|
|
|
return True
|