DAMAGE_REFLECT_STATUS_DESCRIPTION = StatusDescription("Damage Reflect", GraphicChar(None, colors.CYAN, icon.ARMOR_STAT), "When you take damage there is a chance that each enemy you see will take one point of damage.") 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 DoNothingActor(Actor):
    """
    Entities with this actor will do nothing.
    """
    def __init__(self):
        super(DoNothingActor, self).__init__()

    def act(self):
        """
        Just returns energy spent, nothing is done.
        """
        return gametime.single_turn def new_frost_cloud(game_state, density):
    cloud = Composite()
    set_cloud_components(game_state, cloud, density)
    cloud.graphic_char.color_fg = colors.LIGHT_BLUE
    cloud.set_child(CloudActor())
    cloud.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_frost_cloud))
    cloud.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.FROST))
    cloud.set_child(FrostCloudShareTileEffect())
    return cloud def new_poison_cloud(game_state, density):
    cloud = Composite()
    set_cloud_components(game_state, cloud, density)
    cloud.graphic_char.color_fg = colors.GREEN
    cloud.set_child(CloudActor())
    cloud.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_poison_cloud))
    cloud.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.POISON))
    cloud.set_child(PoisonCloudShareTileEffect())
    return cloud 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 Composite(Component):
    """
    Abstract composite class of composite design pattern.

    Component classes of composite type should inherit from this class.
    Composite objects may hold other Components.

    component_type is needed if this composite will be a child.
    """

    def __init__(self, component_type=None):
        super(Composite, self).__init__()
        self.component_type = component_type
        self._spoofed_children = {}
        self._children = {}
        self._children_tag_table = {}

    def __getinitargs__(self):
        return ()

    def get_child(self, component_type):
        return self.__getattr__(component_type)

    def set_child(self, child):
        """
        Adds a child component to this component.
        If the child already has a parent an exception is thrown.
        """
        if child.tags is None:
            raise Exception("Missing tags when: Component {0} tried ta add_child "
                            "component: {1} to its children. ".format(str(self), str(child)))
        if child.component_type is None:
            raise Exception("Missing component_type when: Component {0} tried ta add_child "
                            "component: {1} to its children. ".format(str(self), str(child)))
        if not child._parent is None:
            raise Exception("Component {0} tried ta add_child "
                            "component: {1} to its children. "
                            "But it already "
                            "had parent: {2}.".format(str(self), str(child), str(child.parent)))
        if self.has(child.component_type):
            self.remove_component_of_type(child.component_type)
        self._children[child.component_type] = child
        self._add_child_to_tag_table(child)
        child.parent = self

    def add_spoof_child(self, child):
        """
        Adds a spoofed child component to this composite.
        If the child already has a parent an exception is thrown.
        """
        if child.tags is None:
            raise Exception("Component {0} tried to add_child "
                            "component: {1} to its children. "
                            "But tags "
                            "was not set.".format(str(self), str(child)))
        if child.component_type is None:
            raise Exception("Component {0} tried to add_child "
                            "component: {1} to its children. "
                            "But component_type "
                            "was not set.".format(str(self), str(child)))
        if not child._parent is None and child.parent != self:
            raise Exception("Component {0} tried to add_child "
                            "component: {1} to its children. "
                            "But it already "
                            "had parent: {2}.".format(str(self), str(child), str(child.parent)))
        if not child.component_type in self._spoofed_children:
            self._spoofed_children[child.component_type] = []
        self._spoofed_children[child.component_type].append(child)
        self._add_child_to_tag_table(child)
        child.parent = self

    def _add_child_to_tag_table(self, child):
        for tag in child.tags:
            if not tag in self._children_tag_table:
                self._children_tag_table[tag] = []
            self._children_tag_table[tag].append(child)

    def _remove_child_from_tag_table(self, child):
        for tag in child.tags:
            if tag in self._children_tag_table:
                self._children_tag_table[tag].remove(child)

    def reset_spoofed_children(self):
        """
        Removes all spoofed children.
        """
        for child_type in self._spoofed_children.values():
            for child in child_type:
                self.remove_component(child)
        self._spoofed_children = {}  # Unecessary?

    def remove_component(self, child):
        """
        Removes a child component from this component.
        """
        if (child.component_type in self._children and
                    child is self._children[child.component_type]):
                child.parent = None
                del self._children[child.component_type]
        if (child.component_type in self._spoofed_children and
                    child in self._spoofed_children[child.component_type]):
            self._spoofed_children[child.component_type].remove(child)
            child.parent = None
        self._remove_child_from_tag_table(child)
        return child

    def remove_component_of_type(self, component_type):
        """
        Removes a child component of a type of this component.
        """
        if component_type in self._children:
            return self.remove_component(self._children[component_type])
        return None

    def update(self):
        """
        Runs update on all child components.
        """
        map(lambda x: x.update(), self._children.values())

    def first_tick(self, time):
        """
        Runs first_tick on all child components.
        """
        visited_component_types = set()
        for component_type, component in self._spoofed_children.iteritems():
            visited_component_types.add(component_type)
            component[0].first_tick(time)
        components_without_spoof = [component for key, component in self._children.iteritems()
                                    if not key in visited_component_types]
        for component in components_without_spoof:
            component.first_tick(time)

    def before_tick(self, time):
        """
        Runs before_tick on all child components.
        """
        visited_component_types = set()
        for component_type, component in self._spoofed_children.iteritems():
            visited_component_types.add(component_type)
            component[0].before_tick(time)
        components_without_spoof = [component for key, component in self._children.iteritems()
                                    if not key in visited_component_types]
        for component in components_without_spoof:
            component.before_tick(time)

    def on_tick(self, time):
        """
        Runs on_tick on all child components.
        """
        visited_component_types = set()
        for component_type, component in self._spoofed_children.iteritems():
            visited_component_types.add(component_type)
            component[0].on_tick(time)
        components_without_spoof = [component for key, component in self._children.iteritems()
                                    if not key in visited_component_types]
        for component in components_without_spoof:
            component.on_tick(time)

    def after_tick(self, time):
        """
        Runs after_tick on all child components.
        """
        visited_component_types = set()
        for component_type, component in self._spoofed_children.iteritems():
            visited_component_types.add(component_type)
            component[0].after_tick(time)
        components_without_spoof = [component for key, component in self._children.iteritems()
                                    if not key in visited_component_types]
        for component in components_without_spoof:
            component.after_tick(time)

    def send_message(self, message):
        """
        Sends message to all child components.
        """
        map(lambda x: x.send_message(message), self._children.values())

    def __getattr__(self, component_type):
        if component_type == "_spoofed_children" or component_type == "_children":
            raise AttributeError("Tried to access field {0} from composite {1} "
                                 "But it doesn't exist.".format(str(component_type),
                                                                str(self)))
        try:
            if (component_type in self._spoofed_children and
                        len(self._spoofed_children[component_type]) > 0):
                return self._spoofed_children[component_type][0]
            return self._children[component_type]
        except KeyError:
            error_message = ("Tried to access component {0} from composite {1} "
                             "But it doesn't exist.".format(str(component_type), str(self)))
            if self.has("description"):
                error_message += "\n The name of the composite is: " + self.description.name
            raise AttributeError(error_message)

    def get_original_child(self, component_type):
        """
        Gets the "real" child of the composite.
        """
        return self._children[component_type]

    def get_spoofed_children_of_type(self, component_type):
        try:
            return self._spoofed_children[component_type]
        except KeyError:
            raise Exception("Tried to access components of type"
                            " {0} from composite {1} "
                            "But it doesn't exist.".format(str(component_type),
                                                           str(self)))

    def has(self, component_type):
        """
        Returns true if this component has a child of the given type.

        False otherwise.
        """
        return component_type in self._children

    def has_tag(self, tag):
        """
        Returns true if this component has the given tag.

        False otherwise.
        """
        return tag in self.tags

    # TODO: rename to descendant
    def get_children_with_tag(self, tag):
        """
        Gets the list of all children with the given tag.
        """
        if tag in self._children_tag_table:
            result = self._children_tag_table[tag]
        else:
            result = []
        composite_children = [c for c in self._children.values() if isinstance(c, Composite)]
        grand_children = []
        for c in composite_children:
            grand_children += c.get_children_with_tag(tag)
        return result + grand_children

    def get_child_or_ancestor(self, component_type):
        if component_type in self._children:
            return self._children[component_type]
        elif self._parent:
            return self.parent.get_child_or_ancestor(component_type)
        else:
            raise Exception("Tried to find relative of type: [" + component_type + "] but it doesn't exist")

    def has_child_or_ancestor(self, component_type):
        if component_type in self._children:
            return True
        elif self._parent:
            return self.parent.get_child_or_ancestor(component_type)
        else:
            return False 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) class CharPrinter(Leaf):
    def __init__(self):
        super(CharPrinter, self).__init__()
        self.component_type = "char_printer"
        self._temp_animation_frames = []

    def _tick_animation(self):
        frame = None
        if len(self._temp_animation_frames) > 0:
            frame = self._temp_animation_frames.pop(0)
        return frame

    def clear_animation(self):
        self._temp_animation_frames = []

    def draw(self, position, the_console=0):
        """
        Draws the char on the given position on the console.
        """
        animation_frame = self._tick_animation()
        if animation_frame:
            return draw_graphic_char_to_console(position, animation_frame, the_console)
        draw_graphic_char_to_console(position, self.parent.graphic_char, the_console)

    def draw_unseen(self, screen_position, the_console=0):
        """
        Draws the char as it looks like outside the field of view.
        """
        self._tick_animation()
        console.console.set_colors_and_symbol(screen_position,
                                              colors.UNSEEN_FG,
                                              colors.UNSEEN_BG,
                                              self.parent.graphic_char.icon,
                                              console=the_console)
    def draw_unvisited(self, screen_position, the_console=0):
        """
        Draws the char as it looks like outside the field of view.
        """
        self._tick_animation()
        console.console.set_colors_and_symbol(screen_position,
                                              colors.DARK_PURPLE,
                                              colors.UNSEEN_BG,
                                              self.parent.graphic_char.icon,
                                              console=the_console)

    def append_graphic_char_temporary_frames(self, graphic_char_frames, animation_delay=settings.ANIMATION_DELAY):
        """
        Appends frames to the graphic char animation frame queue.

        These chars will be drawn as an effect,
        the regular chars won't be drawn until the animation frame queue is empty.
        """
        frames = _expand_frames(graphic_char_frames, animation_delay)
        self._temp_animation_frames.extend(frames)

    def append_fg_color_blink_frames(self, frame_colors, animation_delay=settings.ANIMATION_DELAY):
        """
        Appends frames to  the graphic char animation frame queue. With only fg_color changed.

        These chars will be drawn as an effect,
        the regular chars won't be drawn until the animation frame queue is empty.
        """
        color_bg = self.parent.graphic_char.color_bg
        symbol = self.parent.graphic_char.icon
        frames = [GraphicChar(color_bg, color, symbol) for color in frame_colors]
        self.append_graphic_char_temporary_frames(frames, animation_delay)

    def append_default_graphic_frame(self, frames=settings.ANIMATION_DELAY):
        self.append_graphic_char_temporary_frames([self.parent.graphic_char], frames)

    def copy(self):
        """
        Makes a copy of this component.
        """
        copy = CharPrinter()
        copy._temp_animation_frames = self._temp_animation_frames
        return copy class ReflectDamageTakenEffect(DamageTakenEffect):
    def __init__(self):
        super(ReflectDamageTakenEffect, self).__init__()
        self.component_type = "reflect_damage"
        self.damage = 1

    def effect(self, damage, source_entity, damage_types=[]):
        if (damage >= 1 and
                source_entity and
                source_entity.has("health_modifier") and
                not DamageTypes.REFLECT in damage_types and
                source_entity != self.parent and
                rng.coin_flip()):
            damage_effect = entityeffect.UndodgeableAttackEntityEffect(self.parent, self.damage,
                                                                       [DamageTypes.MAGIC, DamageTypes.REFLECT])
            source_entity.effect_queue.add(damage_effect) class Stacker(Leaf):
    """
    Parent component is Stacker.

    Should only be used for items where all instances are equal.
    """

    def __init__(self, stack_type, max_size, size=1):
        super(Stacker, self).__init__()
        self.component_type = "stacker"
        self.stack_type = stack_type
        self.max_size = max_size
        self.size = size

    def is_full(self):
        return self.size >= self.max_size class AddSpoofChildEquipEffect(Leaf):
    def __init__(self, spoof_child_factory, status_icon=None):
        super(AddSpoofChildEquipEffect, self).__init__()
        self.component_type = "equipment_add_spoof_component_" + spoof_child_factory().component_type
        self.tags.add("equipped_effect")

        self.spoof_child_factory = spoof_child_factory
        self.status_icon = status_icon

    def equipped_effect(self, entity):
        """
        Causes the entity that equips this have a spoofed component child.
        """
        entity.effect_queue.add(entityeffect.AddSpoofChild(entity, self.spoof_child_factory(), 1))
        if self.status_icon:
            entity.effect_queue.add(entityeffect.StatusIconEntityEffect(entity, self.status_icon, 1)) class HealTriggeredEffect(TriggeredEffect):
    def __init__(self, min_heal, max_heal, message=None):
        super(HealTriggeredEffect, self).__init__("heal_triggered_effect")
        self.max_heal = max_heal
        self.min_heal = min_heal
        self.message = message

    def trigger(self, **kwargs):
        target_entity = kwargs[action.TARGET_ENTITY]
        heal = random.randrange(self.min_heal, self.max_heal + 1)
        heal_effect = entityeffect.Heal(target_entity, heal, heal_message=self.message)
        target_entity.effect_queue.add(heal_effect) class ApplyPoisonTriggeredEffect(TriggeredEffect):
    def __init__(self, min_damage, max_damage):
        super(ApplyPoisonTriggeredEffect, self).__init__("apply_poison_triggered_effect")
        self.max_damage = max_damage
        self.min_damage = min_damage

    def trigger(self, **kwargs):
        target_entity = kwargs[action.TARGET_ENTITY]
        damage = random.randrange(self.min_damage, self.max_damage + 1)
        damage_effect_factory = PoisonEntityEffectFactory(target_entity, damage, 2, random.randrange(8, 12))
        target_entity.effect_queue.add(damage_effect_factory()) class CreateFlameCloudTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(CreateFlameCloudTriggeredEffect, self).__init__("create_flame_cloud_triggered_effect")

    def trigger(self, **kwargs):
        min_fire_time = 3
        max_fire_time = 8
        game_state = kwargs[action.GAME_STATE]
        source_entity = kwargs[action.SOURCE_ENTITY]
        target_position = kwargs[action.TARGET_POSITION]
        put_tile_and_surrounding_tiles_on_fire(source_entity.dungeon_level.value, target_position, min_fire_time,
                                               max_fire_time, game_state) class ApplyFrostTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(ApplyFrostTriggeredEffect, self).__init__("apply_frost_triggered_action")

    def trigger(self, **kwargs):
        target_entity = kwargs[action.TARGET_ENTITY]
        slow_turns = random.randrange(10, 19)
        msg.send_global_message(messenger.FROST_POTION_DRINK_MESSAGE)
        target_entity.effect_queue.add(entityeffect.AddSpoofChild(None, frost_effect_factory(),
                                                                  slow_turns * gametime.single_turn, meld_id="frost",
                                                                  status_description=FROST_SLOW_STATUS_DESCRIPTION)) class ReEquipAction(Action):
    """
    An Item with this component can be equipped on an entity.

    If an item is already in that
    equipment slot that item will be unequipped first.
    """

    def __init__(self):
        super(ReEquipAction, self).__init__()
        self.component_type = "reequip_action"
        self.tags.add("reequip_action")
        self.name = "Equip"
        self.display_order = 90

    def act(self, **kwargs):
        """
        Will attempt to equip the parent item to the given entity.
        """
        source_entity = kwargs[action.SOURCE_ENTITY]
        if action.EQUIPMENT_SLOT in kwargs:
            equipment_slot = kwargs[action.EQUIPMENT_SLOT]
        else:
            equipment_slot = self.get_equipment_slot(source_entity)
        old_item = None
        if source_entity.equipment.slot_is_equiped(equipment_slot):
            old_item = source_entity.equipment.unequip(equipment_slot)
        self._re_equip(source_entity, equipment_slot)
        if not old_item is None:
            source_entity.inventory.try_add(old_item)
        self.add_energy_spent_to_entity(source_entity)

    def get_equipment_slot(self, source_entity):
        """
        Finds the right equipment slot.
        """
        open_slots = (source_entity.equipment.get_open_slots_of_type
                      (self.parent.equipment_type.value))
        if len(open_slots) > 0:
            return open_slots[0]
        else:
            return (source_entity.equipment.get_slots_of_type
                    (self.parent.equipment_type.value))[0]

    def can_act(self, **kwargs):
        """
        Returns true if it is a valid action to reequip that item.
        """
        return True

    def _re_equip(self, target_entity, equipment_slot):
        """
        Performs the actual reequip.
        """
        re_equip_effect = entityeffect.ReEquip(target_entity, equipment_slot, self.parent)
        target_entity.effect_queue.add(re_equip_effect)
        target_entity.inventory.remove_item(self.parent) class DropItemTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(DropItemTriggeredEffect, self).__init__("drop_item_triggered_effect")

    def trigger(self, **kwargs):
        """
        Attempts to drop the parent item at the entity's feet.
        """
        source_entity = kwargs[action.SOURCE_ENTITY]
        item = self.get_relative_of_type("item").value
        if not source_entity.inventory is None:
            if source_entity.inventory.try_drop_item(item):
                util.add_energy_spent_to_entity(source_entity, gametime.single_turn)
        return class AddEnergySpentEffect(TriggeredEffect):
    def __init__(self, energy_cost=gametime.single_turn):
        super(AddEnergySpentEffect, self).__init__("add_energy_spent_effect")
        self.energy_cost = energy_cost

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        source_entity.actor.newly_spent_energy += self.energy_cost class PutToSleepTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(PutToSleepTriggeredEffect, self).__init__("put_to_sleep_effect")

    def trigger(self, **kwargs):
        """
        When an entity reads a scroll of sleep it will put enemies in sight to sleep.
        """
        target_entity = kwargs[action.TARGET_ENTITY]
        entities_in_sight = target_entity.vision.get_seen_entities_closest_first()
        for entity in entities_in_sight:
            entity.set_child(TryPutToSleep()) class RemoveItemEffect(TriggeredEffect):
    def __init__(self):
        super(RemoveItemEffect, self).__init__("remove_item_effect")

    def trigger(self, **kwargs):
        target_entity = kwargs[action.SOURCE_ENTITY]
        item = self.get_relative_of_type("item").value
        target_entity.inventory.remove_one_item_from_stack(item) class FlashItemEffect(TriggeredEffect):
    def __init__(self):
        super(FlashItemEffect, self).__init__("flash_item_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        item = self.get_relative_of_type("item").value
        _item_flash_animation(source_entity, item) class LocalMessageEffect(TriggeredEffect):
    def __init__(self, message):
        super(LocalMessageEffect, self).__init__("local_message_effect")
        self.message = message

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        target_entity = kwargs[action.TARGET_ENTITY]
        message_arguments = {}
        if source_entity and source_entity.has("description"):
            message_arguments["source_entity"] = source_entity.description.long_name
        if target_entity and target_entity.has("description"):
            message_arguments["target_entity"] = target_entity.description.long_name

        msg.send_visual_message(self.message % message_arguments, source_entity.position.value) class TeleportTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(TeleportTriggeredEffect, self).__init__("teleport_triggered_effect")

    def trigger(self, **kwargs):
        """
        teleports target entity.
        """
        target_entity = kwargs[action.TARGET_ENTITY]
        msg.send_global_message(messenger.PLAYER_TELEPORT_MESSAGE)
        teleport_effect = entityeffect.Teleport(target_entity)
        target_entity.effect_queue.add(teleport_effect) class SingleSwapTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(SingleSwapTriggeredEffect, self).__init__("single_swap_triggered_effect")

    def trigger(self, **kwargs):
        """
        swaps target entity with another random entity on the floor.
        """
        source_entity = kwargs[action.SOURCE_ENTITY]
        dungeon_level = source_entity.dungeon_level.value
        other_entities = [e for e in dungeon_level.entities if not e is source_entity]
        if not any(other_entities):
            return

        random.shuffle(other_entities)

        other_entity = other_entities[0]
        other_pos = other_entity.position.value
        my_pos = source_entity.position.value

        other_entity.mover.try_remove_from_dungeon()
        source_entity.mover.try_remove_from_dungeon()

        source_entity.mover.try_move(other_pos, dungeon_level)
        other_entity.mover.try_move(my_pos, dungeon_level)
        source_entity.game_state.value.dungeon_needs_redraw = True
        msg.send_global_message(messenger.PLAYER_SWITCH_MESSAGE) class MagicMappingTriggeredEffect(TriggeredEffect):
    def __init__(self, energy_cost=gametime.single_turn):
        super(MagicMappingTriggeredEffect, self).__init__("drop_item_triggered_effect")
        self.energy_cost = energy_cost

    def trigger(self, **kwargs):
        """
        Attempts to drop the parent item at the entity's feet.
        """
        target_entity = kwargs[action.TARGET_ENTITY]
        msg.send_global_message(messenger.PLAYER_MAP_MESSAGE)
        dungeon_level = target_entity.dungeon_level.value
        walkable_positions = dungeon_level.get_walkable_positions(dummy_flyer_open_doors, target_entity.position.value)
        map_positions = extend_points(walkable_positions)
        for p in map_positions:
            tile = dungeon_level.get_tile_or_unknown(p)
            target_entity.memory_map.gain_knowledge_of_terrain_of_tile(tile, p, dungeon_level.depth)
        target_entity.game_state.value.dungeon_needs_redraw = True class PushOthersTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(PushOthersTriggeredEffect, self).__init__("push_others_triggered_effect")

    def trigger(self, **kwargs):
        """
        Attempts to drop the parent item at the entity's feet.
        """
        target_entity = kwargs[action.TARGET_ENTITY]
        entities_in_sight = target_entity.vision.get_seen_entities_closest_first()
        entities_in_sight.reverse()
        if not any(entities_in_sight):
            return
        min_push = 2
        max_push = 4
        entity_direction = {}
        entity_push_steps = {}
        max_push_distance = 0
        for entity in entities_in_sight:
            push_direction = geometry.other_side_of_point_direction(target_entity.position.value, entity.position.value)
            entity_direction[entity] = push_direction
            entity_push_steps[entity] = random.randrange(min_push, max_push + 1)
            max_push_distance = max(entity_push_steps[entity], max_push_distance)
        for index in range(max_push_distance):
            for entity in entities_in_sight:
                if (entity_push_steps[entity] <= index or
                        self._entity_is_about_to_fall(entity)):
                    break
                entity.stepper.try_push_in_direction(entity_direction[entity])
            target_entity.game_state.value.dungeon_needs_redraw = True
            target_entity.game_state.value.force_draw()
            sleep(0.07)  # todo: standardise frame show time
        msg.send_global_message(messenger.PLAYER_PUSH_SCROLL_MESSAGE)

    def _entity_is_about_to_fall(self, entity):
        if entity.status_flags.has_status(StatusFlags.FLYING):
            return False
        dungeon_level = entity.dungeon_level.value
        tile = dungeon_level.get_tile_or_unknown(entity.position.value)
        result = tile.get_terrain().has("is_chasm")
        return result class EquipmentType(Leaf):
    """
    Holds the equipment type of a equipment item.
    """

    def __init__(self, equipment_type):
        super(EquipmentType, self).__init__()
        self.component_type = "equipment_type"
        self.value = equipment_type class PlayerAutoPickUp(Leaf):
    """
    Items with this component, should trigger the player auto pick up of the item.
    """

    def __init__(self):
        super(PlayerAutoPickUp, self).__init__()
        self.component_type = "player_auto_pick_up" class ItemType(Leaf):
    """
    Enumerator class denoting different item types. Inventory is sorted on ItemType.
    """
    POTION = 0
    SCROLL = 1
    BOMB = 2
    DEVICE = 3
    WEAPON = 4
    ARMOR = 5
    JEWELLRY = 6
    AMMO = 7
    ENERGY_SHPERE = 7

    ALL = [POTION, BOMB, DEVICE, WEAPON, ARMOR, JEWELLRY, AMMO, ENERGY_SHPERE]

    def __init__(self, item_type):
        super(ItemType, self).__init__()
        self.component_type = "item_type"
        self.value = item_type def _item_flash_animation(entity, item):
    entity.char_printer.append_graphic_char_temporary_frames([item.graphic_char]) class CreateCloudTriggeredEffect(TriggeredEffect):
    def __init__(self, cloud_factory, density=32):
        super(CreateCloudTriggeredEffect, self).__init__("create_cloud_triggered_effect")
        self.density = density
        self.cloud_factory = cloud_factory

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        target_position = kwargs[action.TARGET_POSITION]
        game_state = kwargs[action.GAME_STATE]
        steam = self.cloud_factory(game_state, self.density)
        steam.mover.try_move(target_position, source_entity.dungeon_level.value) class MoveTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(MoveTriggeredEffect, self).__init__("move_triggered_effect")

    def trigger(self, **kwargs):
        target_entity = kwargs[action.TARGET_ENTITY]
        source_entity = kwargs[action.SOURCE_ENTITY]
        target_position = kwargs[action.TARGET_POSITION]
        target_entity.mover.try_move(target_position, source_entity.dungeon_level.value) class ExplodeTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(ExplodeTriggeredEffect, self).__init__("explode_triggered_effect")

    def trigger(self, **kwargs):
        #message = messenger.ENTITY_EXPLODES % {"target_entity": self.parent.description.name}
        source_entity = kwargs[action.SOURCE_ENTITY]
        target_position = kwargs[action.TARGET_POSITION]
        game_state = kwargs[action.GAME_STATE]
        explosion = new_explosion_cloud(game_state, 1)
        explosion.graphic_char.color_fg = colors.YELLOW
        explosion.mover.replace_move(target_position, source_entity.dungeon_level.value)
        for d in direction.DIRECTIONS:
            if d in direction.AXIS_DIRECTIONS:
                color = colors.ORANGE
            else:
                color = colors.RED
            point = geometry.add_2d(d, target_position)
            explosion = new_explosion_cloud(game_state, 1)
            explosion.graphic_char.color_fg = color
            explosion.mover.replace_move(point, source_entity.dungeon_level.value) class RemoveAChargeEffect(TriggeredEffect):
    def __init__(self):
        super(RemoveAChargeEffect, self).__init__("remove_a_charge_effect")

    def trigger(self, **kwargs):
        self.parent.item.value.charge.charges -= 1

    def can_trigger(self, **kwargs):
        return self.parent.item.value.charge.charges > 0 class DarknessTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(DarknessTriggeredEffect, self).__init__("darkness_triggered_effect")

    def trigger(self, **kwargs):
        ttl = gametime.single_turn * rng.random_variance(10, 5)
        source_entity = kwargs[action.SOURCE_ENTITY]
        entities = source_entity.dungeon_level.value.entities
        for entity in entities:
            sight_radius_spoof = DataPoint(DataTypes.SIGHT_RADIUS, 1)
            darkness_effect = entityeffect.AddSpoofChild(source_entity, sight_radius_spoof, time_to_live=ttl)
            entity.effect_queue.add(darkness_effect) class WallToGlassTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(WallToGlassTriggeredEffect, self).__init__("wall_to_glass_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        sight_radius = source_entity.sight_radius.value
        dungeon_level = source_entity.dungeon_level.value
        top = source_entity.position.value[1] - sight_radius
        left = source_entity.position.value[0] - sight_radius
        turned_something_to_glass = False
        for x in range(left, left + 2 * sight_radius + 1):
            for y in range(top, top + 2 * sight_radius + 1):
                try:
                    turned_something_to_glass = self._turn_to_glass_if_wall((x, y), dungeon_level)
                    dungeon_level.signal_terrain_changed((x, y))
                except IndexError:
                    continue
        if turned_something_to_glass:
            msg.send_global_message(messenger.GLASS_TURNING_MESSAGE)

    def _turn_to_glass_if_wall(self, position, dungeon_level):
        terrain = dungeon_level.get_tile(position).get_terrain()
        if terrain.has("is_wall"):
            glass_wall = GlassWall()
            glass_wall.mover.replace_move(position, dungeon_level)
            return True
        return False 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 SwapsTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(SwapsTriggeredEffect, self).__init__("swaps_glass_triggered_effect")

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        dungeon_level = source_entity.dungeon_level.value
        entities_in_sight = source_entity.vision.get_seen_entities()
        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 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]
        if target_entity.has("effect_queue"):
            damage = calculate_damage(damage_min, damage_max, 0, 1)
            damage_effect = entityeffect.UndodgeableAttackEntityEffect(source_entity, damage, damage_types,
                                                                       hit_message=messenger.ZAP_DEVICE_MESSAGE)
            target_entity.effect_queue.add(damage_effect) 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 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 ChooseChargeDeviceTriggeredEffect(TriggeredEffect):
    def __init__(self):
        super(ChooseChargeDeviceTriggeredEffect, self).__init__("choose_charge_device_triggered_effect")

    def can_trigger(self, **kwargs):
        print kwargs
        return kwargs[action.GAME_STATE].player.inventory.has_item_of_type(ItemType.DEVICE)

    def trigger(self, **kwargs):
        source_entity = kwargs[action.SOURCE_ENTITY]
        game_state = kwargs[action.GAME_STATE]
        callback = lambda item, source_entity=source_entity, **kwargs: self._charge_device(item, source_entity)
        choose_device_menu = menufactory.item_type_menu_callback_menu(source_entity,
                                                                      game_state.menu_prompt_stack,
                                                                      ItemType.DEVICE, "Device to Charge:", callback)
        game_state.start_prompt(choose_device_menu)

    def _charge_device(self, device, entity):
        device.charge.charges += 1
        entity.inventory.remove_one_item_from_stack(self.parent.item.value) class PlayerThrowItemAction(PlayerMissileAction):
    """
    This action will prompt the player to throw the parent item.
    """

    @property
    def missile_graphic(self):
        return self.parent.graphic_char

    def __init__(self):
        super(PlayerThrowItemAction, self).__init__()
        self.component_type = "player_throw_item_action"
        self.name = "Throw"
        self.display_order = 95

    # todo: will be handled properly by triggered effect. This whole file needs refactoring, ill deal with it then.
    def add_energy_spent_to_entity(self, entity):
        pass

    def max_missile_distance(self, **kwargs):
        """
        The distance the item can be thrown by the player.
        """
        source_entity = kwargs[SOURCE_ENTITY]
        return source_entity.strength.value * 4 - self.parent.weight.value

    def remove_from_inventory(self, source_entity):
        """
        Removes the parent item from the inventory.
        """
        source_entity.inventory.remove_item(self.parent)

    def handle_hit_ground(self, game_state, position, source_entity):
        self.handle_item_hit("hit_floor_action_tag", game_state, position, source_entity)

    def handle_hit_chasm(self, game_state, position, source_entity):
        self.handle_item_hit("hit_chasm_action_tag", game_state, position, source_entity)

    def handle_item_hit(self, tag, game_state, position, source_entity):
        for c in self.parent.get_children_with_tag(tag):
            if c.can_trigger(source_entity=source_entity, target_entity=self.parent,
                             game_state=game_state, target_position=position):
                c.trigger(source_entity=source_entity, target_entity=self.parent,
                          game_state=game_state, target_position=position)

    def missile_hit_effect(self, dungeon_level, position, game_state, source_entity):
        if is_hitting_ground(dungeon_level, position):
            self.handle_hit_ground(game_state, position, source_entity)
        else:
            self.handle_hit_chasm(game_state, position, source_entity) class Mover(Leaf):
    """
    Component for moving and checking if a move is legal.
    """

    def __init__(self):
        super(Mover, self).__init__()
        self.component_type = "mover"

    def can_move(self, new_position, new_dungeon_level=None):
        """
        Checks if parent component can move to new position.
        """
        if new_dungeon_level is None:
            new_dungeon_level = self.parent.dungeon_level.value
        if not new_dungeon_level.get_tile_or_unknown(new_position):
            return False
        try:
            new_tile = new_dungeon_level.get_tile_or_unknown(new_position)
        except IndexError:
            return False
        return self._can_fit_on_tile(new_tile) and self.can_move_to_terrain(new_tile.get_terrain())

    def move_push_over(self, new_position, dungeon_level=None):
        """
        Will move parent to new position.
        If that position already is occupied try move the old occupant to an adjacent tile.
        The old occupant may be removed if all adjacent tiles are occupied already.

        Returns true if it is successful, false otherwise.
        """
        if dungeon_level is None:
            dungeon_level = self.parent.dungeon_level.value
        if self.try_move(new_position, dungeon_level):
            return True
        old_occupants = self.get_defying_occupants(new_position, dungeon_level)
        for old_occupant in old_occupants:
            old_occupant.mover.try_remove_from_dungeon()

        self.replace_move(new_position, dungeon_level)

        for old_occupant in old_occupants:
            old_occupant.mover.try_move_roll_over(new_position, dungeon_level)
        return True

    def get_defying_occupants(self, position, dungeon_level=None):
        if dungeon_level is None:
            dungeon_level = self.parent.dungeon_level.value
        tile = dungeon_level.get_tile(position)
        return tile.game_pieces[self.parent.game_piece_type.value]

    def try_move_roll_over(self, new_position, new_dungeon_level=None):
        """
        Tries to move parent to new position.
        Or an adjacent tile if it is already occupied.

        Returns true if it is successful, false otherwise.
        """
        if self.try_move(new_position, new_dungeon_level):
            return True
            # Do not shuffle public constants!
        directions = list(direction.DIRECTIONS)
        random.shuffle(directions)
        for d in directions:
            destination = geometry.add_2d(d, new_position)
            if self.try_move(destination, new_dungeon_level):
                return True
        return False

    def try_move(self, new_position, new_dungeon_level=None):
        """
        Tries to move parent to new position.

        Returns true if it is successful, false otherwise.
        """
        if new_dungeon_level is None:
            new_dungeon_level = self.parent.dungeon_level.value
        if self.can_move(new_position, new_dungeon_level):
            self._move(new_position, new_dungeon_level)
            return True
        return False

    def _move(self, new_position, dungeon_level):
        """
        Moves parent to new position, assumes that it fits there.
        """
        self._remove_from_old_tile()
        dungeon_level.get_tile(new_position).add(self.parent)
        self.parent.position.value = new_position
        if not self.has_sibling("dungeon_level"):
            self.parent.set_child(DungeonLevel())
        self.parent.dungeon_level.value = dungeon_level

    def replace_move(self, new_position, new_dungeon_level=None):
        """
        Moves parent to new position and replaces what was already there.
        """
        if new_dungeon_level is None:
            new_dungeon_level = self.parent.dungeon_level.value
        if not new_dungeon_level.has_tile(new_position):
            return False
        new_tile = new_dungeon_level.get_tile(new_position)
        self._remove_from_old_tile()
        piece_type = self.parent.game_piece_type.value
        new_place = new_tile.game_pieces[piece_type]
        for piece in new_place:
            piece.mover.try_remove_from_dungeon()
        return self.try_move(new_position, new_dungeon_level)

    def _can_fit_on_tile(self, tile):
        """
        Checks if the parent can fit on the tile.
        """
        piece_type = self.parent.game_piece_type
        return len(tile.game_pieces[piece_type.value]) < max_instances_of_composite_on_tile(self.parent)

    def can_move_to_terrain(self, terrain_to_pass):
        if terrain_to_pass is None or terrain_to_pass.has("is_chasm"):
            return True
        elif terrain_to_pass.has("is_solid"):
            return False
        else:
            return self.can_pass_terrain(terrain_to_pass)

    def can_pass_terrain(self, terrain_to_pass):
        """
        Checks if the parent can move through a terrain.
        """
        if terrain_to_pass is None:
            return True
        if self.has_sibling("status_flags"):
            status_flags = self.parent.status_flags
            if terrain_to_pass.has("is_chasm") and status_flags.has_status(StatusFlags.FLYING):
                return True
            if (status_flags.has_status(StatusFlags.CAN_OPEN_DOORS) and
                    terrain_to_pass.has("is_door")):
                return True
        if not terrain_to_pass.has("is_solid") and not terrain_to_pass.has("is_chasm"):
            return True
        return False

    def try_remove_from_dungeon(self):
        """
        Tries to remove parent from dungeon.
        """
        if not self.has_sibling("dungeon_level") or self.parent.dungeon_level.value is None:
            return True
        if self._remove_from_old_tile():
            self.parent.dungeon_level.value = None
            return True
        return False

    def _remove_from_old_tile(self):
        """
        Removes parent from previous tile.
        """
        if not self.has_sibling("dungeon_level") or self.parent.dungeon_level.value is None:
            return True
        position = self.parent.position.value
        tile_i_might_be_on = (self.parent.dungeon_level.
                              value.get_tile(position))
        if tile_i_might_be_on.remove(self.parent):
            for c in self.parent.get_children_with_tag(AfterRemoveEffect.TAG):
                c.effect()
            return True
        return False class Position(Leaf):
    """
    Composites holding this has a position in the dungeon.
    """
    def __init__(self):
        super(Position, self).__init__()
        self.component_type = "position"
        self._value = (-1, -1)

    @property
    def value(self):
        """
        Gets the dungeon_level the entity is currently in.
        """
        return self._value

    @value.setter
    def value(self, new_value):
        self._value = new_value
        if self.has_parent() and not self.value is None:
            self.parent.send_message(CompositeMessage.POSITION_CHANGED) class DungeonLevel(Leaf):
    """
    Composites holding this is in a DungeonLevel.
    """
    def __init__(self):
        super(DungeonLevel, self).__init__()
        self.component_type = "dungeon_level"
        self._value = None
        self.last_dungeon_level = None

    @property
    def value(self):
        """
        Gets the dungeon_level the entity is currently in.
        """
        return self._value

    @value.setter
    def value(self, new_dungeon_level):
        """
        Sets current dungeon_level of the entity.
        Also updates the visibility/solidity of the dungeon_level tiles.
        """
        if not self._value is new_dungeon_level:
            old_dungeon_level = self._value
            self._value = new_dungeon_level
            self._signal_dungeon_level_changed()
            if(not old_dungeon_level is None and
               self.has_sibling("actor")):
                old_dungeon_level.remove_actor_if_present(self.parent)
                self.last_dungeon_level = old_dungeon_level
        if self.last_dungeon_level is None:
            self.last_dungeon_level = new_dungeon_level

    def _signal_dungeon_level_changed(self):
        """
        Is called when dungeon level has changed.
        """
        if self.has_parent() and not self.value is None:
            self.parent.send_message(CompositeMessage.DUNGEON_LEVEL_CHANGED)
            if self.has_sibling("actor"):
                self.value.add_actor_if_not_present(self.parent)
            if self.has_sibling("is_dungeon_feature"):
                self.value.add_dungeon_feature_if_not_present(self.parent)

    def on_parent_changed(self):
        """
        When the parent changes try to add it to the dungeon.
        """
        self._signal_dungeon_level_changed() 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 Flag(Leaf):
    """
    Component which only has a component type. Composites with this component has this flag.
    """
    def __init__(self, component_type):
        super(Flag, self).__init__()
        self.component_type = component_type 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 GamePieceTypes(DataPoint):
    ENTITY = 0
    CLOUD = 1
    ITEM = 2
    DUNGEON_FEATURE = 3
    DUNGEON_TRASH = 4
    TERRAIN = 5

    MAX_INSTANCES_ON_TILE = {ENTITY: 1,
                             CLOUD: 1,
                             ITEM: 1,
                             DUNGEON_FEATURE: 1,
                             DUNGEON_TRASH: 1,
                             TERRAIN: 1} class Description(Leaf):
    """
    Composites holding this has some describing text and a name.
    """
    def __init__(self, name, description):
        super(Description, self).__init__()
        self.component_type = "description"
        self._name = name
        self.description = description

    @property
    def name(self):
        return self._name

    @property
    def long_name(self):
        if self.parent.has("is_named"):
            return self._name
        else:
            return "the " + self._name
    @name.setter
    def name(self, value):
        self._name = value

    def copy(self):
        """
        Makes a copy of this component.
        """
        return Description(self.name, self.description) class StatBonusEquipEffect(EquippedEffect):
    def __init__(self, stat, bonus):
        super(StatBonusEquipEffect, self).__init__()
        self.component_type = "equip_stat_bonus_effect_" + stat
        self.stat = stat
        self.bonus = bonus

    def equipped_effect(self, entity):
        """
        Causes the entity that equips this have a bonus to one stat.
        """
        entity.add_spoof_child(DataPointBonusSpoof(self.stat, self.bonus))
        self._equipped_effect(entity)

    def _equipped_effect(self, entity):
        pass class LifeStealEffect(EquippedEffect):
    def __init__(self):
        super(LifeStealEffect, self).__init__()
        self.component_type = "equipment_life_steal_effect"

    def equipped_effect(self, entity):
        """
        Causes seen entities to heal holder of this effect upon death.
        """
        effect = AddEffectToOtherSeenEntities(HealAnEntityDeathFactory(entity))
        entity.effect_queue.add(entityeffect.AddSpoofChild(entity, effect, 1))
        entity.effect_queue.add(entityeffect.StatusIconEntityEffect(entity, LIFE_STEAL_STATUS_DESCRIPTION,
                                                                    1, "life_steal_effect")) class SetInvisibilityFlagEquippedEffect(EquippedEffect):
    def __init__(self):
        super(SetInvisibilityFlagEquippedEffect, self).__init__()
        self.component_type = "equipment_invisibility_effect"

    def equipped_effect(self, entity):
        """
        Causes the entity that equips this item to become invisible.
        """
        invisible_flag = entity.StatusFlags.INVISIBILE
        invisibility_effect = entityeffect.StatusAdder(self.parent, self.parent,
                                                       invisible_flag, time_to_live=1)
        self.parent.effect_queue.add(invisibility_effect) class Trigger(Leaf):
    def __init__(self, extra_tags=[]):
        super(Trigger, self).__init__()
        self.component_type = "trigger"
        self.tags |= set(extra_tags)

    def trigger(self, **kwargs):
        for c in self.parent.get_children_with_tag("triggered_effect"):
            if c.can_trigger(**kwargs):
                c.trigger(**kwargs)

    def can_trigger(self, **kwargs):
        return all(c.can_trigger(**kwargs) for c in self.parent.get_children_with_tag("triggered_effect"))
import random
import messenger
import action
import colors
import equipment
import gametime
import icon
from Status import DAMAGE_REFLECT_STATUS_DESCRIPTION
from action import Action
from actor import DoNothingActor
from cloud import new_frost_cloud, new_poison_cloud
from compositecore import Leaf, Composite
from graphic import GraphicChar, CharPrinter
from health import ReflectDamageTakenEffect
from item_components import Stacker, \
    AddSpoofChildEquipEffect, HealTriggeredEffect, ApplyPoisonTriggeredEffect, CreateFlameCloudTriggeredEffect, \
    ApplyFrostTriggeredEffect, ReEquipAction, DropItemTriggeredEffect, \
    AddEnergySpentEffect, PutToSleepTriggeredEffect, RemoveItemEffect, FlashItemEffect, LocalMessageEffect, \
    TeleportTriggeredEffect, SingleSwapTriggeredEffect, MagicMappingTriggeredEffect, PushOthersTriggeredEffect, \
    EquipmentType, PlayerAutoPickUp, ItemType, _item_flash_animation, CreateCloudTriggeredEffect, MoveTriggeredEffect, \
    ExplodeTriggeredEffect, RemoveAChargeEffect, DarknessTriggeredEffect, WallToGlassTriggeredEffect, \
    HeartStopTriggeredEffect, SwapsTriggeredEffect, ZapRandomSeenEntityTriggeredEffect, HealAllSeenTriggeredEffect, \
    BlinksTriggeredEffect, ChooseChargeDeviceTriggeredEffect
from missileaction import PlayerThrowItemAction
from mover import Mover
from position import Position, DungeonLevel
from stats import DataPoint, Flag, DataTypes, GamePieceTypes
from text import Description
from equipmenteffect import StatBonusEquipEffect, LifeStealEffect, SetInvisibilityFlagEquippedEffect
from trigger import Trigger
    boots.set_child(DataPoint(DataTypes.WEIGHT, 2))
    boots.set_child(GraphicChar(None, colors.BLUE, icon.BOOTS))
    boots.set_child(StatBonusEquipEffect("armor", 0))
    boots.set_child(StatBonusEquipEffect(DataTypes.STEALTH, 3))
    boots.set_child(EquipmentType(equipment.EquipmentTypes.BOOTS))
    return boots


def new_leather_cap(game_state):
    """
    A composite component representing a Armor item.
    """
    cap = Composite()
    set_item_components(cap, game_state)
    set_armor_components(cap)
    cap.set_child(Description("Leather Cap",
                              "An old cap made out of leather, this should keep some harm away."))
    cap.set_child(DataPoint(DataTypes.WEIGHT, 4))
    cap.set_child(GraphicChar(None, colors.ORANGE_D, icon.HELM))
    cap.set_child(StatBonusEquipEffect("armor", 1))
    cap.set_child(EquipmentType(equipment.EquipmentTypes.HEADGEAR))
    return cap


def set_ring_components(item):
    item.set_child(EquipmentType(equipment.EquipmentTypes.RING))
    item.set_child(ItemType(ItemType.JEWELLRY))
    item.set_child(ReEquipAction())
    item.set_child(DataPoint(DataTypes.WEIGHT, 2))


def set_amulet_components(item):
    item.set_child(EquipmentType(equipment.EquipmentTypes.AMULET))
    item.set_child(ItemType(ItemType.JEWELLRY))
    item.set_child(ReEquipAction())
    item.set_child(DataPoint(DataTypes.WEIGHT, 3))


def new_ring_of_invisibility(game_state):
    ring = Composite()
    set_item_components(ring, game_state)
    set_ring_components(ring)
    ring.set_child(GraphicChar(None, colors.CYAN, icon.RING))
    ring.set_child(SetInvisibilityFlagEquippedEffect())
    ring.set_child(Description("Ring of Invisibility",
                               "The metal is warm to your skin, "
                               "this ring will make you invisible"))
    return ring


def new_ring_of_evasion(game_state):
    ring = Composite()
    set_item_components(ring, game_state)
    set_ring_components(ring)
    ring.set_child(GraphicChar(None, colors.GREEN, icon.RING))
    ring.set_child(StatBonusEquipEffect("evasion", 3))
    ring.set_child(Description("Ring of Evasion",
                               "The ring is light on your finger, "
                               "Its magic powers makes it easier for you to dodge attacks."))
    return ring


def new_ring_of_stealth(game_state):
    ring = Composite()
    set_item_components(ring, game_state)
    set_ring_components(ring)
    ring.set_child(GraphicChar(None, colors.BLUE, icon.RING))
    ring.set_child(StatBonusEquipEffect("stealth", 3))
    ring.set_child(Description("Ring of Stealth",
                               "The ring is smooth to your skin, "
                               "Its magic powers makes it easier for you to sneak past enemies."))
    return ring


def new_ring_of_strength(game_state):
    ring = Composite()
    set_item_components(ring, game_state)
    set_ring_components(ring)
    ring.set_child(GraphicChar(None, colors.ORANGE, icon.RING))
    ring.set_child(StatBonusEquipEffect("strength", 3))
    ring.set_child(Description("Ring of Strength",
                               "The ring feels unnaturally heavy, "
                               "Its magic powers makes you stronger."))
    return ring


def new_amulet_of_reflect_damage(game_state):
    amulet = Composite()
    set_item_components(amulet, game_state)
    set_amulet_components(amulet)
    amulet.set_child(GraphicChar(None, colors.CYAN, icon.AMULET))
    amulet.set_child(AddSpoofChildEquipEffect(ReflectDamageTakenEffect,
                                              DAMAGE_REFLECT_STATUS_DESCRIPTION))
    amulet.set_child(Description("Amulet of Reflection",
                                 "The amulet feels cold and heavy,"
                                 "it is made of enchanted silver, "
                                 "Its magic powers will damage those who hurt you."))
    return amulet


def new_amulet_of_life_steal(game_state):
    amulet = Composite()
    set_item_components(amulet, game_state)
    set_amulet_components(amulet)
    amulet.set_child(GraphicChar(None, colors.RED, icon.AMULET))
    amulet.set_child(LifeStealEffect())
    amulet.set_child(Description("Amulet of Vampirism",
                                 "The gem at the center pulsates when blood is near."
                                 "Its magic powers will heal when you see a creature die."))
    return amulet


def set_armor_components(item):
    item.set_child(ItemType(ItemType.ARMOR))
    item.set_child(ReEquipAction())
    return item


def set_potion_components(item):
    item.set_child(ItemType(ItemType.POTION))

    item.set_child(PlayerAutoPickUp())