class StatusDescriptionBar(Leaf):
    def __init__(self):
        super(StatusDescriptionBar, self).__init__()
        self.component_type = "status_bar"
        self.statuses = []

    def clear(self):
        self.statuses = []

    def add(self, status_icon):
        self.statuses.append(status_icon)

    def first_tick(self, time):
        self.clear() 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 animate_flight(game_state, path, symbol_char, color_fg):
    flight_animation = MissileAnimation(game_state, symbol_char, color_fg, path)
    flight_animation.run_animation() class Dodger(Leaf):
    """
    Component for calculating dodge.
    """

    def __init__(self):
        super(Dodger, self).__init__()
        self.component_type = "dodger"

    def is_a_hit(self, accuracy):
        """
        Returns true if it is a hit, false otherwise.
        """
        accuracy = max(accuracy, 1)
        evasion = max(self.parent.evasion.value, 1)
        return rng.stat_check(accuracy, evasion) class DamageTypes(object):
    PHYSICAL = "physical_damage_type"
    MAGIC = "magic_damage_type"
    BLUNT = "blunt_damage_type"
    PIERCING = "piercing_damage_type"
    CUTTING = "cutting_damage_type"
    BLEED = "bleed_damage_type"
    ACID = "acid_damage_type"
    POISON = "poison_damage_type"
    LIGHTNING = "lightning_damage_type"
    FIRE = "fire_damage_type"
    REFLECT = "reflect_damage_type"

    IGNORE_ARMOR = "ignore_armor_damage_type"

    FALL = "fall_damage_type" class ArmorChecker(Leaf):
    """
    Component for calculating dodge.
    """

    def __init__(self):
        super(ArmorChecker, self).__init__()
        self.component_type = "armor_checker"

    def get_damage_after_armor(self, damage, damage_types):
        """
        Returns the damage taken after it goes through the armor.
        """
        armor = self.parent.armor.value
        if armor_will_block_attack(damage_types):
            if damage <= armor:
                damage_reduction = armor / 4
            else:
                damage_reduction = armor / 8
            if damage_reduction <= 0:
                return damage
            return max(damage - random.randrange(0, damage_reduction + 1), 0)
        return damage class ResistanceChecker(Leaf):
    """
    Component for calculating dodge.
    """

    def __init__(self):
        super(ResistanceChecker, self).__init__()
        self.component_type = "resistance_checker"

    def get_damage_after_resistance(self, damage, damage_types):
        """
        Returns the damage taken after it goes through the armor.
        """
        if self.is_immune(damage_types):
            return 0
        return damage

    def is_immune(self, damage_types):
        immunities = [component.immunity for component in self.parent.get_children_with_tag("immunity")]
        for immunity in immunities:
            if immunity in damage_types:
                return True
        return False class FireImmunity(Leaf):
    def __init__(self):
        super(FireImmunity, self).__init__()
        self.component_type = "fire_immunity"
        self.tags.add("immunity")
        self.immunity = DamageTypes.FIRE class PoisonImmunity(Leaf):
    def __init__(self):
        super(PoisonImmunity, self).__init__()
        self.component_type = "poison_immunity"
        self.tags.add("immunity")
        self.immunity = DamageTypes.POISON class PushBackAttacker(AttackerBase):
    """
    Component for attacking and checking if an attacking is legal. Attacks will cause Knock Back.
    """

    def __init__(self):
        super(PushBackAttacker, self).__init__()
        self.component_type = "melee_attacker"

    def _on_hit(self, target_entity):
        self._knock_away_entity(target_entity)

    def _knock_away_entity(self, target_entity):
        if rng.coin_flip():
            knock_position = geometry.other_side_of_point(self.parent.position.value,
                                                          target_entity.position.value)
            old_target_position = target_entity.position.value
            target_entity.mover.try_move(knock_position)
            self.parent.mover.try_move(old_target_position)
            if rng.coin_flip():
                entity_stunned_turn(self.parent, target_entity) class UnarmedAttacker(AttackerBase):
    def __init__(self):
        super(UnarmedAttacker, self).__init__()
        self.component_type = "melee_attacker"

    @property
    def damage(self):
        damage_multiplier = 1
        if self.parent.has("throw_damage_multiplier"):
            damage_multiplier = self.parent.throw_damage_multiplier.value
        return int(2 * self.parent.strength.value * damage_multiplier / 3)

    def can_hit(self, entity):
        if geometry.chess_distance(self.parent.position.value, entity.position.value) > 1:
            return False

        # TODO This check should not be here!
        if entity is None or entity.faction.value == self.parent.faction.value:
            return False
        return True

    def _hit(self, target_entity):
        self._unarmed_damage().damage_entity(self.parent, target_entity)

    def _unarmed_damage(self):
        """
        Calculates an instance of damage
        caused by an unarmed hit by the entity.
        o
        """
        damage_types = [DamageTypes.BLUNT, DamageTypes.PHYSICAL]
        attack_effects = [effect for effect in self.parent.get_children_with_tag("attack_effect")]
        damage_min = self.damage
        damage_max = damage_min + 3
        return Attack(damage_min, damage_max, damage_types, self.accuracy,
                      crit_chance=self.crit_chance,
                      crit_multiplier=self.crit_multiplier,
                      target_entity_effects=attack_effects) class ThrowRockAttacker(AttackerBase):
    def __init__(self):
        super(ThrowRockAttacker, self).__init__()
        self.component_type = "ranged_attacker"

    @property
    def damage(self):
        damage_multiplier = 1
        if self.parent.has("throw_damage_multiplier"):
            damage_multiplier = self.parent.throw_damage_multiplier.value
        return int(2 * self.parent.strength.value * damage_multiplier / 3)

    @property
    def accuracy(self):
        return self.parent.accuracy.value

    @property
    def crit_chance(self):
        return self.parent.unarmed_crit_chance.value

    @property
    def crit_multiplier(self):
        if self.parent.has("crit_multiplier"):
            return self.parent.crit_multiplier.value
        return DEFAULT_CRIT_MULTIPLIER

    def can_hit(self, entity):
        #TODO: faction test does not belong here!
        return not(entity is None or entity.faction.value == self.parent.faction.value)

    def _hit(self, target_entity):
        """
        Makes entity to hit the target entity with the force of a thrown rock.
        """
        damage_types = [DamageTypes.BLUNT, DamageTypes.PHYSICAL]
        damage_min = self.damage
        damage_max = 3
        thrown_damage = Attack(damage_min, damage_max, damage_types, self.accuracy,
                               crit_chance=self.crit_chance,
                               crit_multiplier=self.crit_multiplier)
        thrown_damage.damage_entity(self.parent, target_entity) def new_fire_cloud(game_state, density):
    fire = Composite()
    set_cloud_components(game_state, fire, density)
    set_non_flying_cloud_components(fire)
    fire.graphic_char.icon = icon.FIRE
    fire.graphic_char.color_fg = colors.RED
    fire.set_child(Description("Fire", "Don't get burnt."))
    fire.set_child(SpreadToFlammableDisappearActor())
    fire.set_child(FireDamageShareTileEffect())
    fire.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_fire_cloud))
    fire.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.FIRE))
    return fire def new_dust_cloud(game_state, density):
    cloud = Composite()
    set_cloud_components(game_state, cloud, density)
    cloud.graphic_char.color_fg = colors.LIGHT_ORANGE
    cloud.set_child(CloudActor())
    cloud.set_child(DataPoint(DataTypes.CLONE_FUNCTION, new_dust_cloud))
    cloud.set_child(DustLowerHitOfEntityShareTileEffect())
    cloud.set_child(DataPoint(DataTypes.CLOUD_TYPE, CloudTypes.DUST))
    return cloud 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 Component(object):
    """
    Abstract base class of composite design pattern.

    New classes should not inherit from this class but from
    the Leaf or Composite classes instead.

    Attributes:
        parent (Component): The parent component.
        Is None if this is a root component.
        component_type (string): The component_type, a composite can only
        have one active child of a given component_type.
        tags (Set of strings): Tags can be used as a second
        means of identifying a component.
    """

    def __init__(self):
        self._parent = None
        self.component_type = None
        self.tags = set()
        self.to_be_removed = False

    @property
    def parent(self):
        if self._parent is None:
            raise LookupError("Tried to get parent of {0}, "
                              "but parent was not set".format(str(self)))
        return self._parent

    @parent.setter
    def parent(self, value):
        self._parent = value
        if not value is None:
            self.on_parent_changed()

    def has_parent(self):
        return not self._parent is None

    @property
    def next(self):
        """
        Gets the next sibling of the same type,
        allows components to decorate components of the same type.
        """
        if not self.has_parent():
            return None
        if self.parent.get_original_child(self.component_type) is self:
            return None
        siblings = \
            self.parent.get_spoofed_children_of_type(self.component_type)
        next_index = siblings.index(self) + 1
        if len(siblings) > next_index:
            return siblings[next_index]
        return self.parent.get_original_child(self.component_type)

    def on_parent_changed(self):
        """
        A method hook called when the parent changes.
        """
        pass

    def update(self, *args, **kw):
        """
        A method hook for updating the component tree.
        """
        pass

    def first_tick(self, time):
        """
        A method hook for updating the component tree before tick.
        """
        pass

    def before_tick(self, time):
        """
        A method hook for updating the component tree before tick.
        """
        pass

    def on_tick(self, time):
        """
        A method hook for updating the component tree on tick.
        """
        pass

    def after_tick(self, time):
        """
        A method hook for updating the component tree after tick.
        """
        pass

    def send_message(self, message):
        """
        A method hook for broadcasting a message down the component tree.
        """
        pass

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

        False otherwise.
        """
        if self._parent is None:
            raise Exception("ERROR: Tries to find sibling {0} "
                            "of component {1} but it "
                            "has no parent!".format(str(component_type),
                                                    str(self)))
        return self.parent.has(component_type) 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 DungeonMask(Leaf):
    """
    Holds the visibility mask and solidity mask of the entity
    """

    def __init__(self):
        super(DungeonMask, self).__init__()
        self._dungeon_map = None
        self.dungeon_map_needs_total_update = True
        self.last_sight_radius = -1
        self.component_type = "dungeon_mask"
        self._dirty_point_list = []

    @property
    def dungeon_map(self):
        if self._dungeon_map is None:
            self.init_dungeon_map_if_has_dungeon_level()
        return self._dungeon_map

    @dungeon_map.setter
    def dungeon_map(self, value):
        self._dungeon_map = value

    def __getstate__(self):
        state = dict(self.__dict__)
        del state["_dungeon_map"]
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.dungeon_map_needs_total_update = True
        self.dungeon_map = None

    def init_dungeon_map_if_has_dungeon_level(self):
        """
        Initiates the dungeon map of a dungeon_level, if available.
        """
        if self.has_sibling("dungeon_level") and not self.parent.dungeon_level.value is None:
            self._init_dungeon_map()
            self.dungeon_map_needs_total_update = True

    def _init_dungeon_map(self):
        """
        Initiates the dungeon map of a dungeon_level.
        """
        self.dungeon_map = libtcod.map_new(self.parent.dungeon_level.value.width,
                                           self.parent.dungeon_level.value.height)

    def signal_dirty_point(self, point):
        self._dirty_point_list.append(point)

    def can_see_point(self, point):
        """
        Checks if a particular point is visible to this entity.

        Args:
            point (int, int): The point to check.
        """
        x, y = point
        return libtcod.map_is_in_fov(self.dungeon_map, x, y)

    def on_tick(self, _):
        sight_radius = self.parent.sight_radius.value
        if not self.last_sight_radius == sight_radius:
            self.update_fov()

    def update_fov(self):
        """
        Calculates the Field of Vision from the dungeon_map.
        """
        x, y = self.parent.position.value
        sight_radius = self.parent.sight_radius.value
        libtcod.map_compute_fov(self.dungeon_map, x, y,
                                sight_radius, True)
        self.last_sight_radius = sight_radius

    def print_walkable_map(self):
        """
        Prints a map of where this entity is allowed to walk.
        """
        for y in range(libtcod.map_get_height(self.dungeon_map)):
            line = ""
            for x in range(libtcod.map_get_width(self.dungeon_map)):
                if libtcod.map_is_walkable(self.dungeon_map, x, y):
                    line += " "
                else:
                    line += "#"
            print(line)

    def print_is_transparent_map(self):
        """
        Prints a map of what this entity can see through.
        """
        for y in range(libtcod.map_get_height(self.dungeon_map)):
            line = ""
            for x in range(libtcod.map_get_width(self.dungeon_map)):
                if libtcod.map_is_transparent(self.dungeon_map, x, y):
                    line += " "
                else:
                    line += "#"
            print(line)

    def print_visible_map(self):
        """
        Prints a map of what this entity sees right now.
        """
        for y in range(libtcod.map_get_height(self.dungeon_map)):
            line = ""
            for x in range(libtcod.map_get_width(self.dungeon_map)):
                if libtcod.map_is_in_fov(self.dungeon_map, x, y):
                    line += " "
                else:
                    line += "#"
            print(line)

    def before_tick(self, time):
        self.update_dungeon_map_if_its_old()

    def update_dungeon_map_if_its_old(self):
        """
        Updates the dungeon map it is older than the latest change.
        """
        if self.dungeon_map_needs_total_update:
            self.update_dungeon_map()
            self.update_fov()
        elif len(self._dirty_point_list) > 0:
            for point in self._dirty_point_list:
                x, y = point
                self.update_dungeon_map_point(x, y)
            self._dirty_point_list = []
            self.update_fov()

    def update_dungeon_map_point(self, x, y):
        dungeon_level = self.parent.dungeon_level.value
        terrain = dungeon_level.get_tile_or_unknown((x, y)).get_terrain()
        dungeon_feature = dungeon_level.get_tile_or_unknown((x, y)).get_dungeon_feature()
        is_opaque = terrain.has("is_opaque") or (dungeon_feature and dungeon_feature.has("is_opaque"))
        libtcod.map_set_properties(self.dungeon_map, x, y, 0 if is_opaque else 1,
                                   self.parent.mover.can_pass_terrain(terrain))

    def update_dungeon_map(self):
        """
        Updates the dungeon map.
        """
        dungeon_level = self.parent.dungeon_level.value
        for y in range(dungeon_level.height):
            for x in range(dungeon_level.width):
                self.update_dungeon_map_point(x, y)
        self.dungeon_map_needs_total_update = False
        self._dirty_point_list = []

    def send_message(self, message):
        """
        Handles received messages.
        """
        if message == CompositeMessage.DUNGEON_LEVEL_CHANGED:
            self.init_dungeon_map_if_has_dungeon_level()
            self.update_dungeon_map()
            self.update_fov()
        if message == CompositeMessage.POSITION_CHANGED:
            self.update_fov() class Path(Leaf):
    """
    Composites holding this has a path that it may step through.
    """

    def __init__(self):
        super(Path, self).__init__()
        self._path = None
        self.position_list = []
        self.component_type = "path"

    def __getstate__(self):
        state = dict(self.__dict__)
        del state["_path"]
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self._path = None

    @property
    def path(self):
        if self._path is None:
            self.init_path()
        return self._path

    def init_path(self):
        """
        Initiates the path using the dungeon map, from the DungeonMask module.
        """
        dungeon_map = self.parent.dungeon_mask.dungeon_map
        self._path = libtcod.path_new_using_map(dungeon_map, 1.0)

    def has_path(self):
        """
        Returns True if the entity has a path to walk.
        """
        if len(self.position_list) > 0:
            return True
        return False

    def try_step_path(self):
        """
        Tries to step the entity along the path, relies on the mover module.
        """
        if not self.has_path():
            return 0
        next_point = self.position_list.pop()
        if not geometry.chess_distance(next_point, self.parent.position.value) == 1:
            self.set_line_path(next_point)
            next_point = self.position_list.pop()
        energy_spent = self.parent.stepper.try_move_or_bump(next_point)
        if energy_spent <= 0:
            energy_spent = self.try_step_left_or_right(next_point)
            if energy_spent <= 0:
                self.clear()
        return energy_spent

    def try_step_left_or_right(self, next_point):
        step_direction = geometry.sub_2d(next_point, self.parent.position.value)
        if step_direction not in direction.DIRECTIONS:
            return False
        alternate_directions = [direction.turn_slight_left(step_direction),
                                direction.turn_slight_right(step_direction)]
        random.shuffle(alternate_directions)
        for d in alternate_directions:
            new_position = geometry.add_2d(d, self.parent.position.value)
            terrain = self.parent.dungeon_level.value.get_tile_or_unknown(new_position).get_terrain()
            if self.parent.mover.can_pass_terrain(terrain):
                energy_spent = self.parent.stepper.try_move_or_bump(new_position)
                if energy_spent > 0:
                    return energy_spent
        return 0

    def compute_path(self, destination):
        sx, sy = self.parent.position.value
        dx, dy = destination
        libtcod.path_compute(self.path, sx, sy, dx, dy)
        self.clear()
        x, y = libtcod.path_walk(self.path, True)
        while not x is None:
            self.position_list.insert(0, (x, y))
            x, y = libtcod.path_walk(self.path, True)

    def set_line_path(self, destination):
        sx, sy = self.parent.position.value
        dx, dy = destination
        libtcod.line_init(sx, sy, dx, dy)
        self.clear()
        x, y = libtcod.line_step()
        while not x is None:
            self.position_list.insert(0, (x, y))
            x, y = libtcod.line_step()

    def clear(self):
        self.position_list = []

    def draw(self, camera):
        points = list(self.position_list)
        points.reverse()
        point = None
        for point in points:
            if not self.parent.mover.can_pass_terrain(
                    self.parent.dungeon_level.value.get_tile_or_unknown(point).get_terrain()):
                break
            console.set_symbol(camera.dungeon_to_screen_position(point), icon.FOOT_STEPS)
            console.set_color_fg(camera.dungeon_to_screen_position(point), colors.GRAY)
        if point:
            console.set_symbol(camera.dungeon_to_screen_position(point), "X")
            console.set_color_fg(camera.dungeon_to_screen_position(point), colors.LIGHT_ORANGE)

    def send_message(self, message):
        if message == CompositeMessage.DUNGEON_LEVEL_CHANGED:
            self.init_path() class EffectQueue(Leaf):
    def __init__(self):
        super(EffectQueue, self).__init__()
        self.component_type = "effect_queue"
        self._effect_queue = [None for _ in range(len(EffectTypes.ALLTYPES))]
        for effect_type in EffectTypes.ALLTYPES:
            self._effect_queue[effect_type] = []

    @property
    def effects(self):
        return [effect for effect_group in self._effect_queue for effect in effect_group]

    def add(self, effect):
        if effect.meld_id:
            if not any(effect.meld_id == e.meld_id for e in self.effects):
                self._add(effect)
            else:
                [e for e in self.effects][0].meld(effect)
        else:
            self._add(effect)

    def _add(self, effect):
        self._effect_queue[effect.effect_type].append(effect)
        effect.queue = self

    def remove(self, effect):
        self._effect_queue[effect.effect_type].remove(effect)
        effect.queue = None

    def remove_effects_with_id(self, effect_id):
        for index in range(len(self._effect_queue)):
            self._effect_queue[index] = [effect for effect in self._effect_queue[index]
                                         if not effect.effect_id == effect_id]

    # TODO this is old lookup if can remove.
    def remove_status_adder_of_status(self, id_to_remove):
        for index in range(len(self.queue.effects)):
            self.queue.effects[index] = [effect for effect in self.queue.effects[index]
                                         if not effect.effect_id == id_to_remove]

    def update(self, time):
        for effect_type_queue in EffectTypes.ALLTYPES:
            for effect in self._effect_queue[effect_type_queue]:
                effect.update(time) class AddSpoofChild(EntityEffect):
    def __init__(self, source_entity, spoof_child, time_to_live, message_effect=None, meld_id=None, effect_id=None,
                 status_description=None):
        super(AddSpoofChild, self).__init__(source_entity=source_entity,
                                            effect_type=EffectTypes.ADD_SPOOF_CHILD,
                                            time_to_live=time_to_live,
                                            meld_id=meld_id,
                                            effect_id=effect_id)
        self.status_description = status_description
        self.message_effect = message_effect
        self.spoof_child = spoof_child

    def update(self, time_spent):
        self.target_entity.add_spoof_child(self.spoof_child)
        if self.message_effect:
            self.message()
        self.update_status_icon()
        self.tick(time_spent)

    def update_status_icon(self):
        if self.status_description and self.target_entity.has("status_bar"):
            self.target_entity.status_bar.add(self.status_description)

    def message(self):
        messenger.msg.send_visual_message(
            self.message_effect % {"source_entity": self.source_entity.description.long_name,
                                   "target_entity": self.target_entity.description.long_name},
            self.target_entity.position.value) class EffectStackID(object):
    SLIME_DISSOLVE = "slime_dissolve" class UndodgeableDamagAndBlockSameEffect(EntityEffect):
    def __init__(self, source_entity, damage, damage_types,
                 damage_message, meld_id, status_description=None, time_to_live=1):
        super(UndodgeableDamagAndBlockSameEffect, self).__init__(source_entity=source_entity,
                                                                 effect_type=EffectTypes.DAMAGE,
                                                                 time_to_live=time_to_live,
                                                                 meld_id=meld_id)
        self.damage = damage
        self.damage_types = damage_types
        self.damage_message = damage_message
        self.status_description = status_description

    def send_damage_message(self, damage_caused):
        messenger.msg.send_visual_message(
            self.damage_message % {"source_entity": self.source_entity.description.long_name,
                                   "target_entity": self.target_entity.description.long_name,
                                   "damage": str(damage_caused)},
            self.target_entity.position.value)

    def update(self, time_spent):
        if not self.target_entity.resistance_checker.is_immune(self.damage_types):
            if self.time_alive == 0:
                damage_after_armor = self.target_entity.armor_checker.get_damage_after_armor(self.damage,
                                                                                             self.damage_types)
                damage_after_resist = self.target_entity.resistance_checker.get_damage_after_resistance(
                    damage_after_armor, self.damage_types)
                damage_caused = self.target_entity.health_modifier.hurt(damage_after_resist, entity=self.source_entity)
                self.send_damage_message(damage_caused)
            self.update_status_icon()
        self.tick(time_spent)

    def update_status_icon(self):
        if self.status_description and self.target_entity.has("status_bar"):
            self.target_entity.status_bar.add(self.status_description) class HealthRegain(EntityEffect):
    def __init__(self, source_entity, health, turn_interval, time_to_live, meld_id=None):
        super(HealthRegain, self).__init__(source_entity=source_entity, effect_type=EffectTypes.HEAL,
                                           meld_id=meld_id, time_to_live=time_to_live)
        self.health = health
        self.time_interval = turn_interval * gametime.single_turn
        self.time_until_next_heal = self.time_interval

    def update(self, time_spent):
        if self.time_until_next_heal <= 0:
            self.target_entity.health_modifier.heal(self.health)
            self.time_until_next_heal = self.time_interval
        self.time_until_next_heal -= time_spent
        self.tick(time_spent) 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 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 Health(Leaf):
    """
    Health Component. Composites holding this has health points.

    Attributes:
        _health_counter (Counter): Holds the min, max and current health.
    """

    def __init__(self, max_hp):
        super(Health, self).__init__()
        self.component_type = "health"
        self.hp = counter.Counter(max_hp, max_hp)
        self.killer = None

    def is_dead(self):
        """
        Returns True if the entity is considered dead.
        """
        return self.hp.value == 0

    def is_damaged(self):
        """
        Returns True if the entity is considered dead.
        """
        return self.hp.value != self.hp.max_value class HealthModifier(Leaf):
    def __init__(self):
        super(HealthModifier, self).__init__()
        self.component_type = "health_modifier"

    def hurt(self, damage, entity=None, damage_types=[]):
        """
        Damages the entity by reducing hp by damage.
        """
        if damage == 0 and rng.coin_flip():
            damage = 1  # You should never be completely safe
        if damage == 0:
            return damage
        self.parent.health.hp.decrease(damage)
        self._animate_hurt(damage_types)
        if self.parent.health.is_dead():
            self.parent.health.killer = entity
        self._call_damage_taken_effect(damage, entity, damage_types)
        return damage

    def _animate_hurt(self, damage_types):
        """
        Adds a blink animation to hurt entity.
        """
        frame_length = max(settings.ANIMATION_DELAY / 2, 1)
        hurt_colors = self._get_hurt_colors(damage_types)
        self.parent.char_printer.append_fg_color_blink_frames(hurt_colors, frame_length)

    def _get_hurt_colors(self, damage_types):
        if DamageTypes.POISON in damage_types:
            return [colors.GREEN, colors.GREEN_D]
        return [colors.LIGHT_PINK, colors.RED]

    def heal(self, health):
        """
        Heals increases the current hp by health.
        """
        if not self.parent.health.hp.value == self.parent.health.hp.max_value:
            self.parent.health.hp.increase(health)
            self._animate_heal()
        return health

    def _animate_heal(self):
        heart_graphic_char = GraphicChar(None, colors.RED, icon.HEALTH_GAIN_ICON)
        self.parent.char_printer.append_graphic_char_temporary_frames([heart_graphic_char])

    def increases_max_hp(self, amount):
        """
        Increases max hp and heals the same amount.
        """
        hp = self.parent.health.hp
        hp.max_value = hp.max_value + amount
        hp.increase(amount)
        self._animate_heal()
        self._animate_max_hp_gain()

    def decreases_max_hp(self, amount):
        """
        Decreases max hp and heals the same amount.
        """
        hp = self.parent.health.hp
        hp.max_value = hp.max_value - amount
        hp.value = min(hp.value, hp.max_value)

    def _animate_max_hp_gain(self):
        heart_graphic_char = GraphicChar(None, colors.CYAN, icon.HEALTH_GAIN_ICON)
        self.parent.char_printer.append_graphic_char_temporary_frames([heart_graphic_char])

    def _call_damage_taken_effect(self, damage, entity, damage_types):
        """
        Calls all on damage taken effects.
        @param damage: The amount of damage taken.
        @param entity: The entity that caused the damage.
        """

        effects = self.parent.get_children_with_tag("damage_taken_effect")
        for effect in effects:
            effect.effect(damage, entity, damage_types)

    def kill(self, entity=None):
        """
        Removes all hp, this kills the entity.
        """
        if entity:
            self.parent.health.killer = entity
        self.parent.health.hp.set_min() class BleedWhenDamaged(DamageTakenEffect):
    """
    When parent takes damage, it will bleed pools of blood on the terrain.
    """

    def __init__(self):
        super(BleedWhenDamaged, self).__init__()
        self.component_type = "bleed_on_damaged"

        # Why does this key break the ratman!?
        #self.component_type = "bleed_when_damaged"

    def put_blood_on_tile(self, dungeon_level, position):
        the_terrain = dungeon_level.get_tile_or_unknown(position).get_terrain()
        if the_terrain.has("is_wall"):
            the_terrain.graphic_char.color_fg = colors.RED
        elif not the_terrain.has("is_chasm"):
            spawn_blood_on_position(position, dungeon_level)

    def effect(self, damage, source_entity, damage_types=[]):
        dungeon_level = self.parent.dungeon_level.value
        is_bleed_damage = DamageTypes.BLEED in damage_types
        if damage / float(self.parent.health.hp.max_value) > 0.2 or is_bleed_damage:
            self.put_blood_on_tile(dungeon_level, self.parent.position.value)

        if damage / float(self.parent.health.hp.max_value) > 0.4:
            point_behind = self._get_point_behind_unless_solid(source_entity.position.value, 1, dungeon_level)
            shape = shapegenerator.random_explosion_not_through_solid(point_behind, 2, dungeon_level)
            for point in shape:
                self.put_blood_on_tile(dungeon_level, point)

        if damage / float(self.parent.health.hp.max_value) > 0.8:
            point_behind = self._get_point_behind_unless_solid(source_entity.position.value, 2, dungeon_level)
            shape = shapegenerator.random_explosion_not_through_solid(point_behind, min(damage / 2, 7), dungeon_level)
            for point in shape:
                self.put_blood_on_tile(dungeon_level, point)

    def _get_point_behind_unless_solid(self, enemy_position, distance, dungeon_level):
        """
        Gets point right behind me seen from my enemy.
        """
        right_behind_direction = geometry.other_side_of_point_direction(enemy_position, self.parent.position.value)
        last_result = self.parent.position.value
        for _ in range(distance):
            result = geometry.add_2d(right_behind_direction, last_result)
            if position_is_solid(result, dungeon_level):
                return last_result
            last_result = result
        return result class Inventory(Leaf):
    """
    Holds the Items an entity is carrying.
    """

    def __init__(self):
        super(Inventory, self).__init__()
        self._items = []
        self._item_capacity = ITEM_CAPACITY
        self.component_type = "inventory"

    @property
    def items(self):
        return self._items

    def get_items_sorted(self):
        return sorted(self.items, key=lambda e: e.item_type.value)

    def add_item_no_stack(self, item):
        if not item.dungeon_level.value is None:
            success = item.mover.try_remove_from_dungeon()
            if not success:
                raise Exception("ERROR: Tried to remove item: " + str(item) + "but could not.")
        self._items.append(item)

    def try_stack_item(self, stackable_item):
        stackable_items = self.get_stackable_items_of_type(stackable_item.stacker.stack_type)
        for item in stackable_items:
            items_to_move = min(item.stacker.max_size - item.stacker.size, stackable_item.stacker.size)
            item.stacker.size += items_to_move
            stackable_item.stacker.size -= items_to_move
            if stackable_item.stacker.size == 0:
                break

    def try_add(self, item):
        """
        Tries to add an item to the inventory.

        Returns True on success otherwise False.
        """
        if not self.has_room_for_item(item):
            return False
        else:
            if item.has("stacker"):
                self.try_stack_item(item)
                if item.stacker.size == 0:
                    item.mover.try_remove_from_dungeon()
                    return True
            self.add_item_no_stack(item)
            return True

    def has_room_for_item(self, other_item):
        """
        Returns true if the inventory has room for another item.
        """
        stack_successful = False
        if other_item.has("stacker"):
            stack_successful = self.can_stack_new_item(other_item)
        return stack_successful or len(self._items) + 1 <= self._item_capacity

    def can_stack_new_item(self, stackable_item):
        stackable_items = self.get_stackable_items_of_type(stackable_item.stacker.stack_type)
        for item in stackable_items:
            if not item.stacker.is_full():
                return True
        return False

    def get_stackable_items_of_type(self, stack_type):
        return [item for item in self._items
                if item.has("stacker") and
                   item.stacker.stack_type == stack_type]

    def can_drop_item(self, item):
        """
        Returns true if it is a legal action to drop the item.
        """
        return item.mover.can_move(self.parent.position,
                                   self.parent.dungeon_level)

    def try_drop_item(self, item):
        """
        Tries to drop an item to the ground.

        Returns True on success otherwise False.
        """
        drop_successful = item.mover.try_move(self.parent.position.value,
                                              self.parent.dungeon_level.value)
        if drop_successful:
            self.remove_item(item)
        return drop_successful

    def remove_item(self, item):
        """
        Removes item from the inventory.
        """
        self._items.remove(item)

    def remove_one_item_from_stack(self, item):
        """
        Removes one instance of an item from the inventory.

        Works like remove_item but does not remove an entire stack of items.
        """
        if item.has("stacker"):
            item.stacker.size -= 1
            if item.stacker.size <= 0:
                self._items.remove(item)
        else:
            self._items.remove(item)

    def has_item(self, item):
        """
        Returns true if the item instance is in the inventory, false otherwise.
        """
        return item in self._items

    def has_item_of_type(self, item_type):
        """
        Returns true if the item instance is in the inventory, false otherwise.
        """
        return any([item for item in self._items if item.item_type.value == item_type])

    def is_empty(self):
        """
        Returns true the inventory is empty, false otherwise.
        """
        return len(self._items) <= 0

    def items_of_equipment_type(self, type_):
        """
        Returns a list of all items in the inventory of the given type.
        """
        return [item for item in self._items
                if item.has("equipment_type") and
                   item.equipment_type.value == type_] class MonsterThrowStoneAction(MonsterMissileAction):
    def __init__(self, weight=100):
        super(MonsterThrowStoneAction, self).__init__(2, 4, GraphicChar(None, colors.GRAY, icon.STONE), weight)
        self.component_type = "monster_throw_stone_action"

    def add_energy_spent_to_entity(self, entity):
        """
        Help method for spending energy for the act performing entity.
        """
        entity.actor.newly_spent_energy += entity.throw_speed.value

    def missile_hit_effect(self, dungeon_level, position):
        rock_hit_position(dungeon_level, position, self.parent) class MonsterThrowRockAction(MonsterThrowStoneAction):
    def __init__(self, weight):
        super(MonsterThrowRockAction, self).__init__(weight=weight)
        self.component_type = "monster_throw_rock_action"
        self.missile_graphic = GraphicChar(None, colors.GRAY, icon.DUNGEON_WALLS_ROW) class SpiritMissile(MonsterMagicRangeAction):
    def __init__(self, weight=100):
        super(SpiritMissile, self).__init__(1, GraphicChar(None, colors.LIGHT_BLUE, icon.BIG_CENTER_DOT), weight)
        self.component_type = "monster_range_attack_action" class MonsterHealTargetEntityEffect(MonsterMissileApplyEntityEffect):
    def __init__(self,  weight=100):
        heart_graphic = GraphicChar(None, colors.RED, icon.HEART)
        super(MonsterHealTargetEntityEffect, self).__init__(2, 4, heart_graphic, weight)
        self.component_type = "monster_range_heal_action"
        self.target_chooser_function = GetSuitableHealingTarget()

    def effect_factory(self):
        return Heal(self.parent, random.randrange(1, 3)) class MonsterTripTargetEffect(MonsterMissileApplyEntityEffect):
    def __init__(self,  weight=100):
        missile_graphic = GraphicChar(None, colors.YELLOW, icon.MAGIC_SHOT)
        super(MonsterTripTargetEffect, self).__init__(2, 4, missile_graphic, weight)
        self.component_type = "monster_range_trip_action"

    def effect_factory(self):
        return AddSpoofChild(self.parent, RandomStepper(), gametime.single_turn) class ParalyzeOnStare(MonsterTargetAction):
    def __init__(self, min_range, max_range, weight=100):
        super(ParalyzeOnStare, self).__init__(min_range, max_range, weight)
        self.component_type = "monster_paralyze_on_stare"

    def can_act(self, **kwargs):
        possible_targets = self.get_target_options()
        return any([target for target in possible_targets
                    if target.dungeon_mask.can_see_point(self.parent.position.value)])

    def act(self, destination):
        if not self.parent.dungeon_mask.can_see_point(destination):
            return
        if coin_flip():  # Should be replaced by spell resist.
            return
        targets = self.parent.dungeon_level.value.get_tile_or_unknown(destination).get_entities()
        if not any(targets):
            return
        for target_entity in targets:
            if target_entity.dungeon_mask.can_see_point(self.parent.position.value):
                target_entity.effect_queue.add(self.effect_factory())
        self.add_energy_spent_to_entity(self.parent)

    def effect_factory(self):
        min_turns = 1
        max_turns = 3
        turns = random.randrange(min_turns, max_turns + 1)
        return AddSpoofChild(self.parent, ParalyzedActor(), turns * gametime.single_turn,
                             message_effect=STARE_PARALYZE_MESSAGE, effect_id="paralyze") class ChasePlayerActor(MonsterActor):
    """
    Standard Monster AI will chase the player.
    """

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

    def no_action_taken(self):
        return self.newly_spent_energy <= 0

    def _act(self):
        self.parent.dungeon_mask.update_fov()
        self.newly_spent_energy = 0

        self.update_monster_actor_state()

        self.set_path_to_player_if_seen()
        if not self.parent.path.has_path() and not self._is_standing_on_player():
            self.set_path_to_random_walkable_point()

        # Do action
        self.try_do_weighted_action()
        if self.no_action_taken() and self.parent.path.has_path():
            self.newly_spent_energy += self.parent.path.try_step_path()
        if self.no_action_taken():
            self.newly_spent_energy += gametime.single_turn
        return self.newly_spent_energy

    def _is_standing_on_player(self):
        player = self.get_player_if_seen()
        if player is None:
            return False
        return self.parent.position.value == player.position.value class MonsterActorState(Leaf):
    """
    Holds the monster actor state of the parent. This value is used in AI decisions.
    """
    WANDERING = 0
    HUNTING = 1
    SLEEPING = 2

    def __init__(self, state=WANDERING):
        super(MonsterActorState, self).__init__()
        self.component_type = "monster_actor_state"
        self._value = state

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, new_value):
        if (self._value != MonsterActorState.HUNTING and
                    MonsterActorState.HUNTING == new_value):
            found_gfx = GraphicChar(None, colors.BLUE, "!")
            (self.parent.char_printer.
             append_graphic_char_temporary_frames([found_gfx]))
            msg.send_visual_message(self.parent.entity_messages.notice, self.parent.position.value)
            if self.parent.has("sleeping"):
                self.parent.effect_queue.add(RemoveChildEffect(self.parent, "sleeping", time_to_live=1))

        self._value = new_value class HuntPlayerIfHurtMe(DamageTakenEffect):
    def __init__(self):
        super(HuntPlayerIfHurtMe, self).__init__()
        self.component_type = "hunt_player_if_hurt_me"

    def effect(self, _, source_entity, damage_types=[]):
        if source_entity and source_entity.has("is_player"):
            self.parent.monster_actor_state.value = MonsterActorState.HUNTING class KeepPlayerAtDistanceActor(MonsterActor):
    """
    Standard Monster AI will chase the player.
    """

    def __init__(self, optimal_distance):
        super(KeepPlayerAtDistanceActor, self).__init__()
        self.optimal_distance = optimal_distance

    def _act(self):
        self.parent.dungeon_mask.update_fov()
        self.newly_spent_energy = 0

        self.update_monster_actor_state()

        self.try_do_weighted_action()
        if not self._keep_player_at_distance():
            self.try_step_random_direction()

        if self.newly_spent_energy == 0:
            self.newly_spent_energy = gametime.single_turn
        return self.newly_spent_energy

    def distance_to_optimal_distance(self, position1, position2):
        return abs(geo.chess_distance(position1, position2) - self.optimal_distance)

    def _keep_player_at_distance(self):
        player = self.get_player_if_seen()
        if player is None:
            return False

        if not self.parent.monster_actor_state.value == MonsterActorState.HUNTING:
            return False

        current_distance_to_optimal = self.distance_to_optimal_distance(self.parent.position.value,
                                                                        player.position.value)
        if current_distance_to_optimal == 0:
            return True

        for d in direction.get_shuffled_directions():
            possible_step = geo.add_2d(d, self.parent.position.value)
            if (self.distance_to_optimal_distance(possible_step, player.position.value)
                    < current_distance_to_optimal):
                energy_used = self.parent.stepper.try_step_in_direction(d)
                if energy_used > 0:
                    self.newly_spent_energy += energy_used
                    return True
        return False class MonsterWeightedStepAction(MonsterWeightedAction):
    def __init__(self, weight=100):
        super(MonsterWeightedStepAction, self).__init__(weight)
        self.component_type = "monster_weighted_step_action"

    def _act(self, **kwargs):
        self.parent.actor.newly_spent_energy += self.parent.path.try_step_path() class StartHuntOnAttack(BeforeAttackEffect):
    def __init__(self):
        super(StartHuntOnAttack, self).__init__(1.0)
        self.component_type = "start_hunt_on_attack"

    def before_attack_effect(self, source_entity, target_entity):
        if target_entity.has("is_player"):
            self.parent.monster_actor_state.value = MonsterActorState.HUNTING class SummonEntityMonsterAction(MonsterWeightedAction):
    def __init__(self, entity_factory, max_entities, weight=100):
        super(SummonEntityMonsterAction, self).__init__(weight)
        self.component_type = "monster_summon_entity_action"
        self.entity_factory = entity_factory
        self._current_entities = []
        self.max_entities = max_entities

    def act(self, **kwargs):
        self._current_entities = [e for e in self._current_entities if not e.health.is_dead()]
        print self._current_entities
        if len(self._current_entities) >= self.max_entities:
            return
        entity = self.entity_factory(self.parent.game_state.value)
        position = self.parent.position.value
        dungeon_level = self.parent.dungeon_level.value
        if not entity.mover.try_move_roll_over(position, dungeon_level):
            del entity
        else:
            self._current_entities.append(entity)
        return self.parent.melee_speed.value * 2 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 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 SlimeCanShareTileEntityMover(Mover):
    """
    Parent entities with this mover may enter tiles of other entities.
    """

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

    def _can_fit_on_tile(self, tile):
        """
        Slime monsters fight by entering the tile of another entity.
        Therefore it must override '_can_fit_on_tile' so it doesn't think
        A tile with an entity is too crowded.
        """
        piece_type = self.parent.game_piece_type.value
        entities_on_tile = tile.game_pieces[piece_type]
        if (len(entities_on_tile) == 0 or
                (len(entities_on_tile) == 1 and not entities_on_tile[0].has("is_slime"))):
            return True
        return False class CautiousStepper(Stepper):
    def __init__(self):
        super(CautiousStepper, self).__init__()
        self.component_type = "stepper"

    def dares_to_step(self, position):
        current_position_dangerous = \
            is_tile_dangerous(self.parent.dungeon_level.value.get_tile_or_unknown(self.parent.position.value),
                              self.parent)
        if current_position_dangerous:
            return True  # If the current tile is dangerous any tile is ok. Panic!
        return not is_tile_dangerous(self.parent.dungeon_level.value.get_tile_or_unknown(position), self.parent)

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

        If there is a door in the way try to open it.
        If there is a unfriendly entity in the way hit it instead.
        **If there is a danger on that tile don't step.**
        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.dares_to_step(position):
            move_successful = self._try_move_to_destination(position)
            return self.parent.movement_speed.value if move_successful else 0
        return 0 class TolerateDamage(Leaf):
    """
    Signifies that that the entity won't mind taking damage of a type.
    """

    def __init__(self, damage_type):
        super(TolerateDamage, self).__init__()
        self.component_type = "tolerate_damage_" + str(damage_type)
        self.tags.add("tolerate_damage")
        self.damage_type = damage_type class PrintDeathMessageOnDeath(Leaf):
    """
    Will print death message when parent Entity dies.
    """
    def __init__(self):
        super(PrintDeathMessageOnDeath, self).__init__()
        self.component_type = "print_death_message_on_death"

    def on_tick(self, time):
        if self.parent.health.is_dead():
            msg.send_visual_message(self.parent.entity_messages.death, self.parent.position.value) class LeaveCorpseOnDeath(Leaf):
    """
    Will remove the parent from the dungeon when parent Entity dies.
    """
    def __init__(self):
        super(LeaveCorpseOnDeath, self).__init__()
        self.component_type = "leave_corpse_on_death"

    def on_tick(self, time):
        if self.parent.health.is_dead():
            spawner.spawn_corpse_of_entity(self.parent) class RemoveEntityOnDeath(Leaf):
    """
    Will remove the parent from the dungeon when parent Entity dies.
    """
    def __init__(self):
        super(RemoveEntityOnDeath, self).__init__()
        self.component_type = "remove_on_death"

    def after_tick(self, time):
        if self.parent.health.is_dead():
            self.parent.dungeon_level.value
            self.parent.mover.try_remove_from_dungeon() class LeaveCorpseTurnIntoEntityOnDeath(Leaf):
    """
    Will remove the parent from the dungeon when parent Entity dies.
    """
    def __init__(self, entity_factory, fail_chance):
        super(LeaveCorpseTurnIntoEntityOnDeath, self).__init__()
        self.component_type = "leave_corpse_on_death"
        self.entity_factory = entity_factory
        self.fail_chance = fail_chance

    def on_tick(self, time):
        if self.parent.health.is_dead():
            if random.uniform(0, 1) < self.fail_chance:
                spawner.spawn_corpse_turn_into_entity(self.parent, self.entity_factory)
            else:
                spawner.spawn_corpse_of_entity(self.parent) 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 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 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 Factions(DataPoint):
    PLAYER = 0
    MONSTER = 1 class IntelligenceLevel(DataPoint):
    MINDLESS = 0
    PLANT = 1
    ANIMAL = 2
    NORMAL = 3
    HIGH = 4 class Immunities(object):
    SPIDER_WEB = "spider_web_immunity" 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 StatusFlags(Leaf):
    """
    Composites holding this has status flags, describing their behaviour.
    """
    INVISIBILE = 0
    SEE_INVISIBILITY = 1
    FLYING = 2
    IS_ALIVE = 3
    CAN_OPEN_DOORS = 4
    HAS_HEART = 5

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

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

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

    def before_tick(self, time):
        self._temp_status_flags = set() class 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 EntityMessages(Leaf):
    """
    Holds the text messages that may be sent by the parent entity.
    """
    def __init__(self, notice, death):
        super(EntityMessages, self).__init__()
        self.component_type = "entity_messages"
        self.notice = notice
        self.death = death def entity_skip_turn(source_entity, target_entity):
    add_spoof_effect = AddSpoofChild(source_entity, DoNothingActor(), gametime.single_turn)
    target_entity.effect_queue.add(add_spoof_effect) def entity_skip_step(source_entity, target_entity):
    add_spoof_effect = AddSpoofChild(source_entity, ImmobileStepper(), gametime.single_turn)
    target_entity.effect_queue.add(add_spoof_effect) class Vision(Leaf):
    """
    Holds functions that exposes what the parent entity can see.
    """

    def __init__(self):
        super(Vision, self).__init__()
        self.component_type = "vision"

        self._seen_entities_cache = []
        self._seen_entities_cache_timestamp = -1

    def get_seen_entities(self):
        """
        Gets all entities seen by this entity not including self.
        """
        if self._seen_entities_cache_timestamp < turn.current_turn:
            self._calculate_seen_entities()
        return self._seen_entities_cache

    def _calculate_seen_entities(self):
        if not self.parent.dungeon_level.value is None:
            seen_entities = []
            for entity in self.parent.dungeon_level.value.entities:
                if self.parent.dungeon_mask.can_see_point(entity.position.value):
                    seen_entities.append(entity)
            self._seen_entities_cache = [entity for entity in seen_entities if not entity is self.parent]
            self._seen_entities_cache_timestamp = turn.current_turn

    def get_seen_entities_closest_first(self):
        """
        Gets all seen entities sorted on distance from self not including self.
        """
        return sorted(self.get_seen_entities(), key=lambda entity:
                      (geometry.chess_distance(self.parent.position.value, entity.position.value),
                       -entity.health.hp.ratio_of_full(), hash(entity)))

    def get_closest_seen_entity(self):
        """
        Gets the closest of all seen entities not including self.
        """
        closest_seen_entities = self.get_seen_entities_closest_first()
        if len(closest_seen_entities) < 1:
            return None
        return closest_seen_entities[0]  # The first is oneself. class AwarenessChecker(Leaf):
    """
    Composites holding this can make awareness checks.
    """

    def __init__(self):
        super(AwarenessChecker, self).__init__()
        self.component_type = "awareness_checker"

    def check(self, stealth):
        """
        Performs a notice check.
        @param stealth: The stealth determines how hard it is to notice.
        @return: True if the notice check is successful False otherwise.
        """
        return rng.stat_check(self.parent.awareness.value, stealth) class Equipment(Leaf):
    def __init__(self):
        super(Equipment, self).__init__()
        self.component_type = "equipment"
        self._equipment = {
            EquipmentSlots.HEADGEAR: None,
            EquipmentSlots.ARMOR: None,
            EquipmentSlots.BOOTS: None,

            EquipmentSlots.RIGHT_RING: None,
            EquipmentSlots.LEFT_RING: None,
            EquipmentSlots.AMULET: None,

            EquipmentSlots.MELEE_WEAPON: None,
            EquipmentSlots.RANGED_WEAPON: None
        }

    def get(self, equipment_slot):
        return self._equipment[equipment_slot]

    def has_type(self, equipment_type):
        slots = [slot for slot in EquipmentSlots.ALL
                 if slot.equipment_type == equipment_type]
        return not len(slots) < 0

    def slot_is_equiped(self, equipment_slot):
        return not self._equipment[equipment_slot] is None

    def unequip(self, equipment_slot):
        equipment = self._equipment[equipment_slot]
        for e in equipment.get_children_with_tag("unequip_effect"):
            e.effect()
        self._equipment[equipment_slot] = None
        return equipment

    def can_unequip_to_inventory(self, equipment_slot):
        item = self.get(equipment_slot)
        return (self.slot_is_equiped(equipment_slot) and
                self.parent.inventory.has_room_for_item(item))

    def unequip_to_inventory(self, equipment_slot):
        equipment = self.unequip(equipment_slot)
        succeded = self.parent.inventory.try_add(equipment)
        return succeded

    def get_slots_of_type(self, equipment_type):
        return [slot for slot in EquipmentSlots.ALL
                if slot.equipment_type == equipment_type]

    def get_open_slots_of_type(self, equipment_type):
        return [slot for slot in self.get_slots_of_type(equipment_type)
                if self._equipment[slot] is None]

    def _all_slots_of_type_are_used(self, equipment_type):
        return len(self.get_open_slots_of_type(equipment_type)) < 1

    def can_equip(self, equipment):
        if self._all_slots_of_type_are_used(equipment.equipment_type.value):
            return False
        return True

    def try_equip(self, equipment):
        if self.can_equip(equipment):
            self._equip(equipment)
            return True
        return False

    def force_equip(self, equipment):
        self._equip_into_slot(equipment, self.get_slots_of_type(equipment.equipment_type.value)[0])

    def _equip(self, equipment):
        open_slots = self.get_open_slots_of_type(equipment.equipment_type.value)
        self._equip_into_slot(equipment, open_slots[0])

    def _equip_into_slot(self, equipment, slot):
        self._equipment[slot] = equipment
        for e in equipment.get_children_with_tag("on_equip_effect"):
            e.effect(self.parent)

    def _reset_equipment_spoofed_children(self):
        for _, equipment in self._equipment.iteritems():
            if equipment:
                equipment.reset_spoofed_children()

    def first_tick(self, time_spent):
        self._reset_equipment_spoofed_children()
        self.execute_equip_effects()

    def execute_equip_effects(self):
        for equipment_slot in EquipmentSlots.ALL:
            if self.slot_is_equiped(equipment_slot):
                equipment = self._equipment[equipment_slot]
                for e in equipment.get_children_with_tag("equipped_effect"):
                    e.equipped_effect(self.parent)

    def print_equipment(self):
        print("###############################")
        for slot, content in self._equipment.iteritems():
            print(slot, content)
        print("###############################")

    def after_tick(self, time):
        """
        Runs after_tick on all child components.
        """
        for key, e in self._equipment.iteritems():
            if e:
                e.after_tick(time) class PoisonAttackEffect(AttackEffectWithItemStat):
    def __init__(self, effect_chance):
        super(PoisonAttackEffect, self).__init__(effect_chance)
        self.effect_chance = effect_chance
        self.component_type = "poison_attack_effect"

    def attack_effect(self, source_entity, target_entity):
        min_damage = 4
        max_damage = 8
        turn_interval = 2
        turns = 20
        factory = PoisonEntityEffectFactory(source_entity, random.randrange(min_damage, max_damage), turn_interval, turns)
        target_entity.effect_queue.add(factory())

    def _item_stat(self):
        return ItemStat("bleed_weapon_effect", self.effect_chance, colors.RED, "Bleed",
                        ItemStat.PERCENT_FORMAT, order=30, is_common_stat=False)
import random
import constants
import direction
import geometry
import messenger
import rng
import colors
import gametime
import icon
from Status import StatusDescriptionBar
from actor import DoNothingActor
from animation import animate_flight
from attacker import Dodger, DamageTypes, ArmorChecker, ResistanceChecker, FireImmunity, PoisonImmunity, PushBackAttacker, UnarmedAttacker, ThrowRockAttacker
from cloud import new_fire_cloud, new_dust_cloud
from compositecommon import EntityShareTileEffect
from compositecore import Composite, Leaf, Component
from dungeonfeature import new_spider_web
from dungeonmask import DungeonMask, Path
from entityeffect import EffectQueue, AddSpoofChild, EffectStackID, UndodgeableDamagAndBlockSameEffect, HealthRegain
from graphic import CharPrinter, GraphicChar
from health import Health, HealthModifier, BleedWhenDamaged
from inventory import Inventory
from missileaction import MonsterThrowStoneAction, MonsterThrowRockAction, SpiritMissile, MonsterHealTargetEntityEffect, MonsterTripTargetEffect, \
    ParalyzeOnStare
from monsteractor import ChasePlayerActor, MonsterActorState, HuntPlayerIfHurtMe, KeepPlayerAtDistanceActor, MonsterWeightedStepAction, \
    StartHuntOnAttack
from monsterspells import SummonEntityMonsterAction
from mover import Mover, Stepper, SlimeCanShareTileEntityMover, CautiousStepper, TolerateDamage
from ondeath import PrintDeathMessageOnDeath, LeaveCorpseOnDeath, RemoveEntityOnDeath, LeaveCorpseTurnIntoEntityOnDeath
from position import Position, DungeonLevel
from stats import Flag, DataPoint, DataTypes, Factions, IntelligenceLevel, Immunities
from stats import GamePieceTypes
from statusflags import StatusFlags
from text import Description, EntityMessages
from util import entity_skip_turn, entity_skip_step
from vision import Vision, AwarenessChecker
from equipment import Equipment
from equipmenteffect import PoisonAttackEffect
    salamander = Composite()
    set_monster_components(salamander, gamestate)
    set_beast_components(salamander)

    salamander.set_child(Description("Salamander", "A salamander, it can start small fires."))
    salamander.set_child(EntityMessages("The salamander looks at you.", "The salamander stops moving."))
    salamander.set_child(GraphicChar(None, colors.RED, icon.SALAMANDER))

    salamander.set_child(Health(20))
    salamander.set_child(DataPoint(DataTypes.STRENGTH, 3))
    salamander.set_child(DataPoint(DataTypes.EVASION, 8))
    salamander.set_child(DataPoint(DataTypes.ACCURACY, 10))
    salamander.set_child(DataPoint(DataTypes.ARMOR, 4))
    salamander.set_child(DataPoint(DataTypes.AWARENESS, 5))

    salamander.set_child(NaturalHealthRegain())
    salamander.set_child(PutAdjacentTilesOnFire())
    salamander.set_child(FireImmunity())
    salamander.set_child(TolerateDamage(DamageTypes.FIRE))
    salamander.set_child(DataPoint(DataTypes.MINIMUM_DEPTH, 4))

    return salamander


def new_cyclops(game_state):
    cyclops = Composite()
    set_monster_components(cyclops, game_state)
    set_humanoid_components(cyclops)
    cyclops.set_child(EntityMessages("The eye of the cyclops watches at you.",
                                     "The cyclops is dead."))
    cyclops.set_child(Description("Cyclops",
                                  "A Giant with a single disgusting eye, it's looking for prey."))
    cyclops.set_child(GraphicChar(None, colors.LIGHT_ORANGE, icon.CYCLOPS))
    cyclops.set_child(Health(45))
    cyclops.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.single_turn))
    cyclops.set_child(DataPoint(DataTypes.THROW_SPEED, gametime.double_turn))
    cyclops.set_child(DataPoint(DataTypes.STRENGTH, 21))
    cyclops.set_child(DataPoint(DataTypes.EVASION, 7))
    cyclops.set_child(DataPoint(DataTypes.ACCURACY, 9))
    cyclops.set_child(DataPoint(DataTypes.ARMOR, 6))
    cyclops.set_child(DataPoint(DataTypes.AWARENESS, 3))
    cyclops.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.one_and_half_turn))
    cyclops.set_child(DataPoint(DataTypes.THROW_SPEED, gametime.double_turn))
    cyclops.set_child(DataPoint(DataTypes.MELEE_DAMAGE_MULTIPLIER, 0.5))
    cyclops.set_child(ThrowRockAttacker())

    cyclops.set_child(MonsterThrowRockAction(900))
    cyclops.set_child(DataPoint(DataTypes.MINIMUM_DEPTH, 5))
    return cyclops


def new_jericho(gamestate):
    jericho = new_ratman(gamestate)
    jericho.description.name = "Jericho"
    jericho.entity_messages.death_message = "Jericho the quick is no more."
    jericho.set_child(Flag("is_named"))
    jericho.graphic_char.color_fg = colors.YELLOW
    jericho.actor.energy_recovery = gametime.double_energy_gain
    return jericho


def set_ghost_components(composite):
    composite.set_child(StatusFlags([StatusFlags.CAN_OPEN_DOORS, StatusFlags.FLYING]))
    composite.set_child(PoisonImmunity())
    composite.set_child(TolerateDamage(DamageTypes.POISON))
    composite.set_child(TolerateDamage(DamageTypes.BLEED))


def new_ghost(gamestate):
    ghost = Composite()
    set_monster_components(ghost, gamestate)
    set_ghost_components(ghost)

    ghost.set_child(EntityMessages("The ghost sees you.", "The ghost fades away."))
    ghost.set_child(Description("Ghost", "A spirit of a hunted creature."))
    ghost.set_child(GraphicChar(None, colors.LIGHT_BLUE, icon.GHOST))
    ghost.set_child(Health(1))
    ghost.set_child(DataPoint(DataTypes.STRENGTH, 2))
    ghost.set_child(DataPoint(DataTypes.EVASION, 14))
    ghost.set_child(DataPoint(DataTypes.ACCURACY, 9))
    ghost.set_child(DataPoint(DataTypes.ARMOR, 0))
    ghost.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.single_turn + gametime.one_third_turn))
    ghost.set_child(DataPoint(DataTypes.AWARENESS, 8))

    ghost.set_child(KeepPlayerAtDistanceActor(4))
    ghost.set_child(SpiritMissile(150))
    ghost.set_child(AddGhostReviveToSeenEntities())
    ghost.set_child(DataPoint(DataTypes.MINIMUM_DEPTH, 2))
    return ghost


def new_pixie(gamestate):
    pixie = Composite()
    set_monster_components(pixie, gamestate)
    set_humanoid_components(pixie)
    pixie.set_child(StatusFlags([StatusFlags.CAN_OPEN_DOORS, StatusFlags.FLYING,
                                 StatusFlags.IS_ALIVE, StatusFlags.HAS_HEART]))

    pixie.set_child(EntityMessages("The pixie sees you.", "The pixie falls."))
    pixie.set_child(Description("Pixie", "A small humanoid with insect wings."))
    pixie.set_child(GraphicChar(None, colors.PINK, icon.PIXIE))
    pixie.set_child(Health(12))
    pixie.set_child(DataPoint(DataTypes.STRENGTH, 2))
    pixie.set_child(DataPoint(DataTypes.EVASION, 12))
    pixie.set_child(DataPoint(DataTypes.ACCURACY, 10))
    pixie.set_child(DataPoint(DataTypes.ARMOR, 2))
    pixie.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.two_thirds_turn))
    pixie.set_child(DataPoint(DataTypes.AWARENESS, 8))

    pixie.set_child(KeepPlayerAtDistanceActor(4))
    pixie.set_child(MonsterHealTargetEntityEffect(50))
    pixie.set_child(MonsterTripTargetEffect(50))
    pixie.set_child(DataPoint(DataTypes.MINIMUM_DEPTH, 6))
    return pixie


def set_slime_components(slime):
    slime.set_child(StatusFlags([StatusFlags.IS_ALIVE]))
    slime.set_child(DataPoint(DataTypes.INTELLIGENCE, IntelligenceLevel.PLANT))
    slime.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.single_turn + gametime.one_third_turn))

    slime.set_child(Flag("is_slime"))