1
0
python-roguelike/components/consumable.py

123 lines
4.0 KiB
Python
Raw Normal View History

from __future__ import annotations
from typing import Optional, TYPE_CHECKING
import actions
import color
2022-01-14 16:45:07 -05:00
import components.ai
import components.inventory
from components.base_component import BaseComponent
from exceptions import Impossible
2022-01-14 16:45:07 -05:00
from input_handlers import SingleRangedAttackHandler
if TYPE_CHECKING:
from entity import Actor, Item
class Consumable(BaseComponent):
parent: Item
def get_action(self, consumer: Actor) -> Optional[actions.Action]:
"""Try to return the action for this item."""
return actions.ItemAction(consumer, self.parent)
def activate(self, action: actions.ItemAction) -> None:
"""Invoke this item's ability.
`action` is the context for this activation.
"""
raise NotImplementedError()
def consume(self) -> None:
"""Remove the consumed item from its containing inventory."""
entity = self.parent
inventory = entity.parent
if isinstance(inventory, components.inventory.Inventory):
inventory.items.remove(entity)
2022-01-14 16:45:07 -05:00
class ConfusionConsumable(Consumable):
def __init__(self, number_of_turns: int):
self.number_of_turns = number_of_turns
def get_action(self, consumer: Actor) -> Optional[actions.Action]:
self.engine.message_log.add_message(
"Select a target location.",
color.needs_target
)
self.engine.event_handler = SingleRangedAttackHandler(
self.engine,
callback=lambda xy: actions.ItemAction(consumer, self.parent, xy),
)
return None
def activate(self, action: actions.ItemAction) -> None:
consumer = action.entity
target = action.target_actor
if not self.engine.game_map.visible[action.target_xy]:
raise Impossible("You cannot target an area that you cannot see.")
if not target:
raise Impossible("You must select an enemy to target.")
if target is consumer:
raise Impossible("You cannot confuse yourself!")
self.engine.message_log.add_message(
f"The eyes of the {target.name} look vacant, as it starts to stumble around!",
color.status_effect_applied,
)
target.ai = components.ai.ConfusedEnemy(
entity=target,
previous_ai=target.ai,
turns_remaining=self.number_of_turns,
)
self.consume()
class HealingConsumable(Consumable):
def __init__(self, amount: int):
self.amount = amount
def activate(self, action: actions.ItemAction) -> None:
consumer = action.entity
amount_recovered = consumer.fighter.heal(self.amount)
if amount_recovered > 0:
self.engine.message_log.add_message(
f"You consume the {self.parent.name}, and recover {amount_recovered} HP!",
color.health_recovered
)
self.consume()
else:
raise Impossible(f"Your health is already full.")
2022-01-13 14:56:38 -05:00
class LightningDamageConsumable(Consumable):
def __init__(self, damage: int, maximum_range: int):
self.damage = damage
self.maximum_range = maximum_range
def activate(self, action: actions.ItemAction) -> None:
consumer = action.entity
target = None
closest_distance = self.maximum_range + 1.0
for actor in self.engine.game_map.actors:
if actor is not consumer and self.parent.gamemap.visible[actor.x, actor.y]:
distance = consumer.distance(actor.x, actor.y)
if distance < closest_distance:
target = actor
closest_distance = distance
if target:
self.engine.message_log.add_message(
f"A lightning bolt stikes the {target.name} with a loud thunder, for {self.damage} damage!"
)
target.fighter.take_damage(self.damage)
self.consume()
else:
raise Impossible("No enemy is close enough to strike.")