Implement confusion scrolls
This commit is contained in:
parent
bebd9d617e
commit
621d4780e8
@ -1,11 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Tuple, TYPE_CHECKING
|
||||
import random
|
||||
from typing import List, Optional, Tuple, TYPE_CHECKING
|
||||
|
||||
import numpy as np # type: ignore
|
||||
import tcod
|
||||
|
||||
from actions import Action, MeleeAction, MovementAction, WaitAction
|
||||
from actions import Action, BumpAction, MeleeAction, MovementAction, WaitAction
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from entity import Actor
|
||||
@ -43,6 +44,51 @@ class BaseAI(Action):
|
||||
return [(index[0], index[1]) for index in path]
|
||||
|
||||
|
||||
class ConfusedEnemy(BaseAI):
|
||||
"""
|
||||
A confused enemy will stumble around aimlessly for a given number of turns, then
|
||||
reverts back to its previous AI. If an actor occupies a tile it is randomly
|
||||
moving into, it will attack.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
entity: Actor,
|
||||
previous_ai: Optional[BaseAI],
|
||||
turns_remaining: int
|
||||
):
|
||||
super().__init__(entity)
|
||||
|
||||
self.previous_ai = previous_ai
|
||||
self.turns_remaining = turns_remaining
|
||||
|
||||
def perform(self) -> None:
|
||||
# Rever the AI back to the original state if the effect has run its course.
|
||||
if self.turns_remaining <= 0:
|
||||
self.engine.message_log.add_message(f"The {self.entity.name} is no longer confused.")
|
||||
self.entity.ai = self.previous_ai
|
||||
else:
|
||||
# Pick a random direction
|
||||
direction_x, direction_y = random.choice(
|
||||
[
|
||||
(-1, -1), # Northwest
|
||||
(0, -1), # North
|
||||
(1, -1), # Northeast
|
||||
(-1, 0), # West
|
||||
(1, 0), # East
|
||||
(-1, 1), # Southwest
|
||||
(0, 1), # South
|
||||
(1, 1), # Southeast
|
||||
]
|
||||
)
|
||||
|
||||
self.turns_remaining -= 1
|
||||
|
||||
# The actor will either try to move or attack in the chosen random direction.
|
||||
# It's possible the actor will just bump into the wall, wasting a turn.
|
||||
return BumpAction(self.entity, direction_x, direction_y).perform()
|
||||
|
||||
|
||||
class HostileEnemy(BaseAI):
|
||||
def __init__(self, entity: Actor):
|
||||
super().__init__(entity)
|
||||
|
@ -4,9 +4,11 @@ from typing import Optional, TYPE_CHECKING
|
||||
|
||||
import actions
|
||||
import color
|
||||
import components.ai
|
||||
import components.inventory
|
||||
from components.base_component import BaseComponent
|
||||
from exceptions import Impossible
|
||||
from input_handlers import SingleRangedAttackHandler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from entity import Actor, Item
|
||||
@ -34,6 +36,46 @@ class Consumable(BaseComponent):
|
||||
inventory.items.remove(entity)
|
||||
|
||||
|
||||
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
|
||||
|
@ -30,13 +30,18 @@ troll = Actor(
|
||||
inventory=Inventory(capacity=0),
|
||||
)
|
||||
|
||||
confusion_scroll = Item(
|
||||
char="~",
|
||||
color=(207, 63, 255),
|
||||
name="Confusion Scroll",
|
||||
consumable=consumable.ConfusionConsumable(number_of_turns=10),
|
||||
)
|
||||
health_potion = Item(
|
||||
char="!",
|
||||
color=(127, 0, 255),
|
||||
name="Health Potion",
|
||||
consumable=consumable.HealingConsumable(amount=4),
|
||||
)
|
||||
|
||||
lightning_scroll = Item(
|
||||
char="~",
|
||||
color=(255, 255, 0),
|
||||
|
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import overload, Optional, TYPE_CHECKING
|
||||
from typing import overload, Callable, Optional, Tuple, TYPE_CHECKING
|
||||
|
||||
import tcod.event
|
||||
|
||||
@ -294,6 +294,22 @@ class LookHandler(SelectIndexHandler):
|
||||
self.engine.event_handler = MainGameEventHandler(self.engine)
|
||||
|
||||
|
||||
class SingleRangedAttackHandler(SelectIndexHandler):
|
||||
"""Handles targeting a single enemy. Only the enemy selected will be affected."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
engine: Engine,
|
||||
callback: Callable[[Tuple[int, int]], Optional[Action]]
|
||||
):
|
||||
super().__init__(engine)
|
||||
|
||||
self.callback = callback
|
||||
|
||||
def on_index_selected(self, x: int, y: int) -> Optional[Action]:
|
||||
return self.callback((x, y))
|
||||
|
||||
|
||||
class MainGameEventHandler(EventHandler):
|
||||
def ev_keydown(self, event: tcod.event.KeyDown) -> Optional[Action]:
|
||||
action: Optional[Action] = None
|
||||
|
@ -70,6 +70,8 @@ def place_entities(
|
||||
|
||||
if item_chance < 0.7:
|
||||
entity_factories.health_potion.spawn(dungeon, x, y)
|
||||
elif item_chance < 0.9:
|
||||
entity_factories.confusion_scroll.spawn(dungeon, x, y)
|
||||
else:
|
||||
entity_factories.lightning_scroll.spawn(dungeon, x, y)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user