class EntityShareTileEffect(Leaf):
    """
    Defines an effect that sharing tile with this parent entity will result in.
    """

    def __init__(self):
        super(EntityShareTileEffect, self).__init__()
        self.tags.add("entity_share_tile_effect")

    def share_tile_effect_tick(self, sharing_entity, time_spent):
        if (not sharing_entity is self.parent and
                self.can_effect(source_entity=self.parent, target_entity=sharing_entity, time=time_spent)):
            self.effect(source_entity=self.parent, target_entity=sharing_entity, time=time_spent)

    def effect(self, **kwargs):
        pass

    def can_effect(self, **kwargs):
        return True 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 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) 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 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 def teleport_monsters(player):
    positions = (player.dungeon_level.value.get_walkable_positions(player, player.position.value))
    random_positions = random.sample(positions, len(positions))
    max_tries = 30
    while len(player.vision.get_seen_entities()) > 0 and max_tries > 0:
        entity = player.vision.get_seen_entities()[0]
        for position in random_positions:
            teleport_successful = entity.mover.try_move(position)
            if teleport_successful:
                break
        max_tries -= 1 class Stepper(Leaf):
    """
    Component for moving and checking if a move is legal.

    will also interact with what it bumps into.
    """

    def __init__(self):
        super(Stepper, self).__init__()
        self.component_type = "stepper"

    def try_push_in_direction(self, direction):
        return self._try_move_to_destination(geometry.add_2d(self.parent.position.value, direction))

    def _try_move_to_destination(self, position):
        """
        This method should wrap the move component for the stepper.
        No other method should call mover.
        """
        if self.parent.mover.try_move(position):
            self._on_successful_step()
            return True
        return False

    def _on_successful_step(self):
        position = self.parent.position.value
        surrounding_points = [geometry.add_2d(d, position) for d in direction.DIRECTIONS]
        for p in surrounding_points:
            entities = self.parent.dungeon_level.value.get_tile_or_unknown(p).get_entities()
            if len(entities) > 0:
                entity = entities[0]
                for e in entity.get_children_with_tag(trigger.ENEMY_STEPPING_NEXT_TO_ME_TRIGGER_TAG):
                    if e.can_trigger(source_entity=entity, target_entity=self.parent):
                        e.trigger(source_entity=entity, target_entity=self.parent)
                for e in self.parent.get_children_with_tag(trigger.STEP_NEXT_TO_ENEMY_TRIGGER_TAG):
                    if e.can_trigger(source_entity=self.parent, target_entity=entity):
                        e.trigger(source_entity=self.parent, target_entity=entity)

    def try_step_in_direction(self, direction):
        return self.try_move_or_bump(geometry.add_2d(self.parent.position.value, direction))

    def try_bump_terrain(self, position):
        terrain_to_step = \
            self.parent.dungeon_level.value.get_tile_or_unknown(position).get_terrain()
        if (terrain_to_step.has("bump_action") and
                terrain_to_step.bump_action.can_bump(self.parent)):
            terrain_to_step.bump_action.bump(self.parent)
            return True
        return False

    def try_attack(self, position):
        entity = (self.parent.dungeon_level.value.get_tile(position).get_first_entity())
        if self.parent.has("melee_attacker") and entity:
            return self.parent.melee_attacker.try_hit(entity)

    def try_move_or_bump(self, position):
        """
        Tries to move the entity to a position.

        If there is a unfriendly entity in the way hit it instead.
        If there is a door in the way try to open it.
        If an action is taken return True otherwise return False.

        Args:
            position (int, int): The position the entity tries to move to.
        Returns:
            Energy spent
        """
        if self.try_bump_terrain(position):
            return self.parent.movement_speed.value
        if self.try_attack(position):
            return self.parent.melee_speed.value
        if self._try_move_to_destination(position):
            return self.parent.movement_speed.value
        return 0 class AfterRemoveEffect(Leaf):
    TAG = "after_remove_effect"

    def __init__(self):
        super(AfterRemoveEffect, self).__init__()
        self.tags.add(AfterRemoveEffect.TAG)

    def effect(self):
        pass 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() def get_possible_powers(current_powers):
    return get_possible_powers_of_list(current_powers, new_power_list()) 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 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 Immunities(object):
    SPIDER_WEB = "spider_web_immunity" 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 Flags(object):
    FLAMMABLE = "flammable" 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)
import random
import entityeffect
import menufactory
import messenger
import rng
import state
import action
import colors
import icon
from compositecommon import EntityShareTileEffect
from compositecore import Composite, Leaf
from graphic import GraphicChar, CharPrinter
from mover import Mover, teleport_monsters, Stepper, AfterRemoveEffect
from position import Position, DungeonLevel
from sacrifice import get_possible_powers
from stats import DataPoint, DataTypes, GamePieceTypes, Immunities, Flag
from statusflags import Flags
from text import Description


def set_dungeon_feature_components(dungeon_feature):
    dungeon_feature.set_child(DataPoint(DataTypes.GAME_PIECE_TYPE, GamePieceTypes.DUNGEON_FEATURE))
    dungeon_feature.set_child(Position())
    dungeon_feature.set_child(DungeonLevel())
    dungeon_feature.set_child(GraphicChar(None, colors.RED, icon.FOUNTAIN_FULL))
    dungeon_feature.set_child(CharPrinter())
    dungeon_feature.set_child(Mover())
    dungeon_feature.set_child(IsDungeonFeature())


def new_stairs_down():
    """
    Stairs Down allows the player to descend to the next level.
    """
    stairs = Composite()
    set_dungeon_feature_components(stairs)
    stairs.set_child(Description("Stairs Down",
                                 ("A dark pass way downward.",
                                  "what horrors awaits there?")))
    stairs.set_child(GraphicChar(None, colors.WHITE, icon.STAIRS_DOWN))
    stairs.set_child(DescendStairsAction())
    stairs.set_child(Flag("is_stairs_down"))
    return stairs


def new_stairs_up():
    """
    Stairs up allows the player to ascend to the next level.
    """
    stairs = Composite()
    set_dungeon_feature_components(stairs)
    stairs.set_child(Description("Stairs Up",
                                 ("A way back, when the ",
                                  "nightmare becomes too real.")))
    stairs.set_child(GraphicChar(None, colors.WHITE, icon.STAIRS_UP))
    stairs.set_child(Flag("is_stairs_up"))
    return stairs


def new_spider_web():
    """
    Spider web the player or other entities can get caught in.
    """
    web = Composite()
    set_dungeon_feature_components(web)
    web.set_child(Description("Spider Web",
                              "A spider made this web, touch it and you might get stuck"))
    web.set_child(GraphicChar(None, colors.WHITE, icon.SPIDER+2))
    web.set_child(DataPoint(DataTypes.STRENGTH, 10))
    web.set_child(StuckInSpiderWebShareTileEffect())
    web.set_child(Flag(Flags.FLAMMABLE))
    return web

class StuckInSpiderWebShareTileEffect(EntityShareTileEffect):
    def __init__(self):
        super(StuckInSpiderWebShareTileEffect, self).__init__()
        self.component_type = "stuck_in_spider_web_share_tile_effect"

    def effect(self, **kwargs):
        target_entity = kwargs["target_entity"]
        source_entity = kwargs["source_entity"]

        if not target_entity.has(Immunities.SPIDER_WEB) and target_entity.has("effect_queue"):
            stuck_in_web = StuckInWebStepperSpoof(self.parent)
            add_spoof_effect = entityeffect.AddSpoofChild(source_entity, stuck_in_web, time_to_live=1)
            target_entity.effect_queue.add(add_spoof_effect)


class StuckInWebStepperSpoof(Stepper):
    def __init__(self, web):
        super(StuckInWebStepperSpoof, self).__init__()
        self.component_type = "stepper"
        self.web = web
        self.strength = web.strength.value

    def try_move_or_bump(self, position):
        my_strength = self.parent.strength.value
        if rng.stat_check(my_strength, self.strength):
            messenger.msg.send_visual_message(messenger.BREAKS_OUT_OF_WEB_MESSAGE % {"target_entity": self.parent.description.long_name}, self.parent.position.value)
            self.web.mover.try_remove_from_dungeon()
            return self.next.try_move_or_bump(position)
        messenger.msg.send_visual_message(messenger.WONT_BREAK_OUT_OF_WEB_MESSAGE % {"target_entity": self.parent.description.long_name}, self.parent.position.value)
        return self.parent.movement_speed.value


def new_fountain():
    """
    Drinking from the fountain makes the player stronger.
    """
    fountain = Composite()
    set_dungeon_feature_components(fountain)

    fountain.set_child(Description("Fountain",