FROST_SLOW_STATUS_DESCRIPTION = StatusDescription("Slow", GraphicChar(None, colors.CYAN, icon.SLOW_ICON), "The frost has slowed your movement speed lowered.") class Action(Leaf):
    """
    Abstract component, all sub components of this type should define an
    action. A entity should be able to use the parent entity
    using the action.
    """
    def __init__(self, name="XXX_Action_Name_XXX", display_order=100, energy_cost=gametime.single_turn):
        super(Action, self).__init__()
        self.tags.add("user_action")
        self.name = name
        self.display_order = display_order
        self.energy_cost = energy_cost

    def __call__(self, *args, **kwargs):
        self.act(*args, **kwargs)

    def delayed_act(self, *args, **kwargs):
        return DelayedFunctionCall(self.act, *args, **kwargs)

    def delayed_can_act(self, *args, **kwargs):
        return DelayedFunctionCall(self.can_act, *args, **kwargs)

    def act(self, **kwargs):
        """
        The action that this component defines.
        """
        pass

    def can_act(self, **kwargs):
        """
        Returns true if it is valid to call the act method.
        """
        return True

    def add_energy_spent_to_entity(self, entity):
        """
        Help method for spending energy for the act performing entity.
        """
        entity.actor.newly_spent_energy += self.energy_cost class DamageTypes(object):
    PHYSICAL = "physical_damage_type"
    MAGIC = "magic_damage_type"
    BLUNT = "blunt_damage_type"
    PIERCING = "piercing_damage_type"
    CUTTING = "cutting_damage_type"
    BLEED = "bleed_damage_type"
    ACID = "acid_damage_type"
    POISON = "poison_damage_type"
    LIGHTNING = "lightning_damage_type"
    FIRE = "fire_damage_type"
    REFLECT = "reflect_damage_type"

    IGNORE_ARMOR = "ignore_armor_damage_type"

    FALL = "fall_damage_type" def calculate_damage(damage_min, damage_max, bonus_damage, damage_multiplier):
    if damage_max < damage_min:
        return damage_min
    return (random.randrange(damage_min, damage_max + 1) + bonus_damage) * damage_multiplier def new_fire_cloud(game_state, density):
    fire = Composite()
    set_cloud_components(game_state, fire, density)
    set_non_flying_cloud_components(fire)
    fire.graphic_char.icon = icon.FIRE
    fire.graphic_char.color_fg = colors.RED
    fire.set_child(Description("Fire", "Don't get burnt."))
    fire.set_child(SpreadToFlammableDisappearActor())
    fire.set_child(FireDamageShareTileEffect())
    fire.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_fire_cloud))
    fire.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.FIRE))
    return fire def new_explosion_cloud(game_state, density):
    explosion = Composite()
    set_cloud_components(game_state, explosion, density)
    explosion.graphic_char.color_fg = colors.YELLOW
    explosion.set_child(Description("Explosion", "Don't go near it."))
    explosion.set_child(DisappearCloudActor())
    explosion.set_child(ExplosionDamageShareTileEffect())
    explosion.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_explosion_cloud))
    explosion.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.EXPLOSION))
    return explosion class EntityShareTileEffect(Leaf):
class PoisonEntityEffectFactory(object):
class AddEffectToOtherSeenEntities(Leaf):
class HealAnEntityOnDeath(Leaf):
    def __init__(self):
    def share_tile_effect_tick(self, sharing_entity, time_spent):
    def effect(self, **kwargs):
    def can_effect(self, **kwargs):
    def __init__(self, source_entity, total_damage, turn_interval, turns_to_live):
    def __call__(self):
    def __init__(self, effect_factory, ttl=1):
    def before_tick(self, time):
    def __init__(self, source_entity):
    def on_tick(self, time): class Leaf(Component):
    """
    Abstract leaf class of composite design pattern.

    Component classes of leaf type should inherit from this class.
    """

    def __init__(self):
        super(Leaf, self).__init__()

    def get_relative_of_type(self, component_type):
        return self.parent.get_child_or_ancestor(component_type)

    def has_relative_of_type(self, component_type):
        return self.parent.has_child_or_ancestor(component_type) class GraphicChar(Leaf):
    """
    Composites holding this has a graphical representation as a char.
    """

    def __init__(self, color_bg, color_fg, icon):
        super(GraphicChar, self).__init__()
        self.component_type = "graphic_char"
        self._color_bg = color_bg
        self._color_fg = color_fg
        self._icon = icon

    @property
    def color_bg(self):
        if self._color_bg:
            return self._color_bg
        elif self.next:
            return self.next.color_bg
        return None


    @color_bg.setter
    def color_bg(self, value):
        self._color_bg = value

    @property
    def color_fg(self):
        if self._color_fg:
            return self._color_fg
        elif self.next:
            return self.next.color_fg
        return None

    @color_fg.setter
    def color_fg(self, value):
        self._color_fg = value

    @property
    def icon(self):
        if self._icon:
            return self._icon
        elif self.next:
            return self.next.icon
        return None

    @icon.setter
    def icon(self, value):
        self._icon = value

    def copy(self):
        """
        Makes a copy of this component.
        """
        return GraphicChar(self.color_bg, self.color_fg, self.icon) EQUIP_MESSAGE = "%(source_entity)s equips %(item)s."
UNEQUIP_MESSAGE = "%(source_entity)s puts away %(item)s."
HEAL_MESSAGE = "%(source_entity)s heals %(target_entity)s for %(health)s health."
HEALTH_POTION_MESSAGE = "The health potion heals %(target_entity)s for %(health)s health."
DISSOLVE_MESSAGE = "%(source_entity)s dissolves %(target_entity)s for %(damage)s damage."
HIT_MESSAGE = "%(source_entity)s hits %(target_entity)s for %(damage)s damage."
MISS_MESSAGE = "%(source_entity)s misses %(target_entity)s."
CRIT_MESSAGE = "%(source_entity)s critically hits %(target_entity)s for %(damage)s damage."
HAUNT_MESSAGE = "%(source_entity)s haunts the %(target_entity)s!"
HEART_STOP_MESSAGE = "%(target_entity)s clutches its heart."
DARKNESS_MESSAGE = "An unnatural darkness fills the dungeon."
PICK_UP_MESSAGE = "You pick up %(item)s."
POISON_MESSAGE = "%(target_entity)s takes %(damage)s in poison damage."
BLEED_MESSAGE = "%(target_entity)s is bleeding out %(damage)s damage."
DOWN_STAIRS_HEAL_MESSAGE = "Your feel vitalized by your progress, you regain %(health)s health."
FALL_DOWN_MESSAGE = "You take a damage of %(damage)s from the fall."
DRINK_FOUNTAIN_MESSAGE = "You drink from the fountain, your max health increases by %(health)s."
HURT_BY_EXPLOSION = "The explosion blasts %(target_entity)s for %(damage)s damage."
HURT_BY_FIRE = "The fire burns %(target_entity)s for %(damage)s damage."
WONT_BREAK_OUT_OF_WEB_MESSAGE = "%(target_entity)s is stuck in the spider web."
BREAKS_OUT_OF_WEB_MESSAGE = "%(target_entity)s breaks free."
WANT_TO_JUMP_DOWN_CHASM = "Are you sure you want to drop down the chasm?"
PRESS_ENTER_TO_ACCEPT = "Press ENTER to accept, another key to reject."
PLAYER_TELEPORT_MESSAGE = "Your surroundings seem different."
PLAYER_SWITCH_MESSAGE = "Your surrounding is replaced."
PLAYER_MAP_MESSAGE = "You surroundings suddenly seem familiar."
PLAYER_PUSH_SCROLL_MESSAGE = "You as you form the words of the scroll your voice turns into a strong wind."
GLASS_TURNING_MESSAGE = "You surroundings suddenly seem more transparent."
SWAP_DEVICE_MESSAGE = "Your surroundings has changed."
ZAP_DEVICE_MESSAGE = "%(source_entity)s zaps %(target_entity)s for %(damage)s damage!"
HEALTH_DEVICE_MESSAGE = "%(target_entity)s is healed by the device for %(health)s health."
STARE_PARALYZE_MESSAGE = "%(source_entity)s stares at %(target_entity)s, it's paralyzed!."
NO_LONGER_PARALYZED_MESSAGE = "%(source_entity)s is no longer paralyzed."
FROST_POTION_DRINK_MESSAGE = "That potion was cold as ice! You feel yourself slow down."
FROST_POTION_BREAKS_MESSAGE = "The potion smashes to the ground and creates a freezing cloud of mist!"
POTION_SMASHES_AGAINST_FLOOR_MESSAGE = "The potion smashes to the ground and breaks into pieces."
FLAME_POTION_DRINK_MESSAGE = "As you drink the potion the liquid ignites!"
FLAME_POTION_BREAKS_MESSAGE = "The potion smashes to the ground and creates a roaring fire!"
POISON_POTION_DRINK_MESSAGE = "The potion tastes really nasty, you feel sick!"
POISON_POTION_BREAK_MESSAGE = "The potion smashes to the ground and creates a poisonous cloud of gas!"
POTION_SMASH_TO_GROUND = "The %(target_entity)s smashes to the ground and breaks into pieces."
ITEM_HITS_THE_GROUND_HEAVY = "The %(target_entity)s hits the ground with a thud."
ITEM_HITS_THE_GROUND_LIGHT = "The %(target_entity)s falls to the ground."
ITEM_FALLS_DOWN_CHASM = "The %(target_entity)s falls into the chasm and disappears."
ENTITY_EXPLODES = "%(target_entity)s explodes!"
class Messenger(object):
class Message(object):
    def __init__(self):
    def has_new_message(self):
    def has_new_message(self, value):
    def send_visual_message(self, new_message, position):
    def send_global_message(self, new_message):
    def _message(self, new_message):
    def tail(self, length):
    def clear(self):
    def __init__(self, message):
    def increase(self):
    def tick(self):
    def __str__(self): class TryPutToSleep(Leaf):
    def __init__(self):
        super(TryPutToSleep, self).__init__()
        self.component_type = "try_put_to_sleep"

    def first_tick(self, time):
        if self.parent_may_sleep():
            self.parent.set_child(SleepingEntity())
        self.parent.remove_component(self)

    def parent_may_sleep(self):
        return self.parent.status_flags.has_status(StatusFlags.IS_ALIVE) def extend_points(points):
    result = set(points)
    for point in points:
        for d in direction.DIRECTIONS:
            result.add(geo.add_2d(point, d))
    return result class MissileHitDetection(object):
    """
    Is able to detect collisions on a given path.
    """
    def __init__(self, passes_entity=False, passes_solid=False):
        self.passes_entity = passes_entity
        self.passes_solid = passes_solid

    def get_path_taken(self, path, dungeon_level):
        """
        Returns a list of points of the path until the collision occured.
        """
        for index, point in enumerate(path):
            tile = dungeon_level.get_tile_or_unknown(point)
            if tile.get_terrain().has("is_solid") and not self.passes_solid:
                return self._last_n_or_default(path, index, None)
            if tile.has_entity() and not self.passes_entity:
                return self._last_n_or_default(path, index + 1, None)
        return self._last_n_or_default(path, index + 1, None)

    def _last_n_or_default(self, the_list, n, default):
        """
        Help function for getting last n elements of a list or default value.
        """
        if n > len(the_list):
            return default
        else:
            return the_list[:n] class DataPoint(Leaf):
    """
    Class for components holding a single data point.
    """
    def __init__(self, component_type, value, tags=[]):
        super(DataPoint, self).__init__()
        self.tags |= set(tags)
        self.component_type = component_type
        self.value = value class DataTypes:
    CLASS = "job"
    RACE = "race"

    ENERGY = "energy"
    CRIT_MULTIPLIER = "crit_multiplier"
    UNARMED_CRIT_CHANCE = "unarmed_crit_chance"
    CRIT_CHANCE = "crit_chance"
    CRIT_CHANCE_WEAPON = "crit_chance_weapon_effect"
    STRENGTH = "strength"
    ARMOR = "armor"
    ACCURACY = "accuracy"
    DAMAGE = "damage"
    STEALTH = "stealth"
    AWARENESS = "awareness"
    EVASION = "evasion"

    COUNTER_ATTACK_CHANCE = "counter_attack_chance"
    OFFENCIVE_ATTACK_CHANCE = "offencive_attack_chance"
    DEFENCIVE_ATTACK_CHANCE = "defencive_attack_chance"

    MELEE_SPEED = "melee_speed"
    SHOOT_SPEED = "shoot_speed"
    THROW_SPEED = "throw_speed"
    THROW_ITEM_SPEED = "throw_item_speed"
    CAST_SPEED = "cast_speed"

    MELEE_DAMAGE_MULTIPLIER = "melee_damage_multiplier"
    THROW_DAMAGE_MULTIPLIER = "throw_damage_multiplier"

    INTELLIGENCE = "intelligence"
    GAME_PIECE_TYPE = "game_piece_type"
    MOVEMENT_SPEED = "movement_speed"
    FACTION = "faction"

    WEIGHT = "weight"
    WEAPON_RANGE = "weapon_range"

    SIGHT_RADIUS = "sight_radius"
    SKIP_ACTION_CHANCE = "skip_action_chance"

    DENSITY = "density"
    CLOUD_TYPE = "cloud_type"
    CLONE_FUNCTION = "clone_function"

    MINIMUM_DEPTH = "minimum_depth"

    GAME_STATE = "game_state" class StatusFlags(Leaf):
    """
    Composites holding this has status flags, describing their behaviour.
    """
    INVISIBILE = 0
    SEE_INVISIBILITY = 1
    FLYING = 2
    IS_ALIVE = 3
    CAN_OPEN_DOORS = 4
    HAS_HEART = 5

    def __init__(self, status_flags=[]):
        super(StatusFlags, self).__init__()
        self.component_type = "status_flags"
        self._status_flags = set(status_flags)
        self._temp_status_flags = set()

    def has_status(self, status):
        """
        Returns True if parent entity has the status given.
        """
        if not self.next is None:
            return (status in self._status_flags or
                    status in self._temp_status_flags or
                    next.has_status(status))
        else:
            return (status in self._status_flags or
                    status in self._temp_status_flags)

    def add_temp_status(self, status):
        """
        Adds a temporary status that will be removed the next tick.
        """
        self._temp_status_flags.add(status)

    def before_tick(self, time):
        self._temp_status_flags = set() class GlassWall(Composite):
    def __init__(self):
        super(GlassWall, self).__init__()
        self.set_child(DataPoint(DataTypes.GAME_PIECE_TYPE, GamePieceTypes.TERRAIN))
        self.set_child(Mover())
        self.set_child(Position())
        self.set_child(DungeonLevel())
        self.set_child(GraphicChar(colors.FLOOR_BG, colors.WHITE, icon.GLASS_WALL))
        self.set_child(CharPrinter())
        self.set_child(Flag("is_solid")) class TriggeredEffect(Leaf):
    def __init__(self, component_type):
        super(TriggeredEffect, self).__init__()
        self.component_type = component_type
        self.tags.add("triggered_effect")

    def trigger(self, **kwargs):
        pass

    def can_trigger(self, **kwargs):
        return True
import random
import action
import animation
import colors
import direction
import entityeffect
import gametime
import geometry
import menufactory
import messenger
import rng
import util
from time import sleep
from Status import FROST_SLOW_STATUS_DESCRIPTION
from action import Action
from attacker import DamageTypes, calculate_damage
from cloud import new_fire_cloud, new_explosion_cloud
from compositecommon import PoisonEntityEffectFactory, frost_effect_factory
from compositecore import Leaf
from dummyentities import dummy_flyer_open_doors
from graphic import GraphicChar
from messenger import msg
from monsteractor import TryPutToSleep
from shapegenerator import extend_points
from shoot import MissileHitDetection
from stats import DataPoint, DataTypes
from statusflags import StatusFlags
from terrain import GlassWall
from triggeredeffect import TriggeredEffect
        if not any(entities_in_sight):
            return
        entities_in_sight.append(source_entity)

        positions = [e.position.value for e in entities_in_sight]
        random.shuffle(positions)

        for entity in entities_in_sight:
            entity.mover.try_remove_from_dungeon()

        for entity in entities_in_sight:
            entity.mover.try_move(positions.pop(), dungeon_level)
        msg.send_global_message(messenger.SWAP_DEVICE_MESSAGE)


class HeartStopTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(HeartStopTriggeredEffect, self).__init__("heart_stop_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        ttl = gametime.single_turn * (random.randrange(3) + 2)
        entities = [entity for entity in source_entity.dungeon_level.value.entities
                    if entity.status_flags.has_status(StatusFlags.HAS_HEART) and not entity is source_entity]
        if len(entities) < 1:
            return
        target = random.sample(entities, 1)[0]
        heart_stop_effect = entityeffect.HeartStop(source_entity, time_to_live=ttl)
        target.effect_queue.add(heart_stop_effect)


class BlinksTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(BlinksTriggeredEffect, self).__init__("blinks_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        min_blinks = 2
        max_blinks = 6
        times = random.randrange(min_blinks, max_blinks + 1)
        for _ in range(times):
            self._blinks(source_entity)

    def _blinks(self, source_entity):
        player = source_entity
        entities = [entity for entity in source_entity.vision.get_seen_entities()] + [player]  # And the player too.
        for target_entity in entities:
            self._blink(target_entity)
        player.game_state.value.dungeon_needs_redraw = True
        player.game_state.value.force_draw()
        sleep(0.2)  # todo: standardise frame show time

    def _blink(self, entity):
        sight = entity.sight_radius.value
        possible_destinations = []
        for x in range(-sight, sight + 1):
            for y in range(-sight, sight + 1):
                if x == 0 and y == 0:
                    continue
                p = geometry.add_2d((x, y), entity.position.value)
                if entity.dungeon_mask.can_see_point(p):
                    possible_destinations.append(p)
        random.shuffle(possible_destinations)
        for position in possible_destinations:
            is_safe = (entity.dungeon_level.value.get_tile_or_unknown(position).get_terrain().has("is_floor")
                       or entity.status_flags.has_status(StatusFlags.FLYING))
            if is_safe and entity.mover.try_move(position):
                break


class HealAllSeenTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(HealAllSeenTriggeredEffect, self).__init__("heal_all_seen_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        player = source_entity
        entities = [entity for entity in source_entity.vision.get_seen_entities()
                    if entity.health.is_damaged()] + [player]  # And the player too.
        if len(entities) <= 0:
            return
        min_heal = 3
        max_heal = 15
        for target_entity in entities:
            heal = random.randrange(min_heal, max_heal + 1)
            heal_effect = entityeffect.Heal(target_entity, heal, heal_message=messenger.HEALTH_DEVICE_MESSAGE)
            target_entity.effect_queue.add(heal_effect)


class ZapRandomSeenEntityTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(ZapRandomSeenEntityTriggeredEffect, self).__init__("zap_random_seen_entity_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        entities = [entity for entity in source_entity.vision.get_seen_entities()
                    if not entity is source_entity]
        if len(entities) <= 0:
            return
        random.shuffle(entities)
        missile_hit_detector = MissileHitDetection(passes_entity=True, passes_solid=False)
        dungeon_level = source_entity.dungeon_level.value
        zap_graphic = GraphicChar(None, colors.LIGHT_ORANGE, "*")
        for entity in entities:
            path = util.get_path(source_entity.position.value, entity.position.value)
            path = path[1:]
            new_path = missile_hit_detector.get_path_taken(path, dungeon_level)
            if len(path) == len(new_path):
                self.zap_path(new_path, source_entity)
                animation.animate_path(source_entity.game_state.value, path, zap_graphic)
                break

    def zap_path(self, path, source_entity):
        for position in path:
            for entity in source_entity.dungeon_level.value.get_tile_or_unknown(position).get_entities():
                self.zap_entity(source_entity, entity)

    def zap_entity(self, source_entity, target_entity):
        damage_min = 1
        damage_max = 20

        damage_types = [DamageTypes.LIGHTNING]