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 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 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 GameState(GameStateBase):
    def __init__(self, player_name=""):
        super(GameState, self).__init__(player_name)
        self.dungeon = Dungeon(self)
        self._init_player_position()

    def _init_player_position(self):
        first_level = self.dungeon.get_dungeon_level(1)
        self.dungeon_level = first_level
        for stairs in first_level.up_stairs:
            move_succeded = self.player.mover.move_push_over(stairs.position.value, first_level)
            if move_succeded:
                self.camera.center_on_entity(self.player)
                teleport_monsters(self.player)
                return
        raise Exception("Could not put player at first up stairs.") class GameStateDummy(GameStateInterface):
    def __init__(self):
        super(GameStateDummy, self).__init__()
        self.dungeon = Dungeon(self)
        self.player = new_player(self)
        self.player.description.name = "Mr. Test Hero"
        reset_globals(self.player)

    def update(self):
        dungeon_level = self.player.dungeon_level.value
        dungeon_level.tick(gametime.normal_energy_gain) 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 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_] def new_player(game_state):
    """
    A composite component representing the player character.
    """
    player = Composite()
    player.set_child(DataPoint(DataTypes.GAME_STATE, game_state))

    player.set_child(DataPoint(DataTypes.RACE, Races.HUMAN))

    player.set_child(DataPoint(DataTypes.ENERGY, -gametime.single_turn))
    player.set_child(DataPoint(DataTypes.UNARMED_CRIT_CHANCE, 0.15))
    player.set_child(DataPoint(DataTypes.CRIT_CHANCE, 0.0))
    player.set_child(DataPoint(DataTypes.ACCURACY, 10))
    player.set_child(DataPoint(DataTypes.STRENGTH, 5))
    player.set_child(DataPoint(DataTypes.EVASION, 10))
    player.set_child(DataPoint(DataTypes.ARMOR, 4))
    player.set_child(DataPoint(DataTypes.STEALTH, 6))

    player.set_child(DataPoint(DataTypes.COUNTER_ATTACK_CHANCE, 0.0))
    player.set_child(DataPoint(DataTypes.DEFENCIVE_ATTACK_CHANCE, 0.0))
    player.set_child(DataPoint(DataTypes.OFFENCIVE_ATTACK_CHANCE, 0.0))

    player.set_child(Flag("is_player"))
    player.set_child(Flag("is_named"))
    player.set_child(StatusFlags([StatusFlags.CAN_OPEN_DOORS, StatusFlags.IS_ALIVE]))

    player.set_child(DataPoint(DataTypes.GAME_PIECE_TYPE, GamePieceTypes.ENTITY))

    player.set_child(DataPoint(DataTypes.MOVEMENT_SPEED, gametime.single_turn))
    player.set_child(DataPoint(DataTypes.MELEE_SPEED, gametime.single_turn))
    player.set_child(DataPoint(DataTypes.THROW_SPEED, gametime.double_turn))
    player.set_child(DataPoint(DataTypes.SHOOT_SPEED, gametime.single_turn))
    player.set_child(DataPoint(DataTypes.THROW_ITEM_SPEED, gametime.single_turn))
    player.set_child(DataPoint(DataTypes.CAST_SPEED, gametime.single_turn))

    player.set_child(DataPoint(DataTypes.SIGHT_RADIUS, constants.COMMON_SIGHT_RADIUS))
    player.set_child(DataPoint(DataTypes.FACTION, Factions.PLAYER))
    player.set_child(DataPoint(DataTypes.INTELLIGENCE, IntelligenceLevel.NORMAL))

    player.set_child(DungeonLevel())
    player.set_child(Position())

    player.set_child(Mover())
    player.set_child(PlayerStepper())

    player.set_child(UnarmedAttacker())
    player.set_child(ThrowRockAttacker())
    player.set_child(Dodger())
    player.set_child(ArmorChecker())
    player.set_child(ResistanceChecker())

    player.set_child(Description("CO", "The Brave"))
    player.set_child(GraphicChar(None, colors.WHITE, icon.GUNSLINGER_THIN))
    player.set_child(StatusDescriptionBar())
    player.set_child(CharPrinter())

    player.set_child(HealthModifier())
    player.set_child(BleedWhenDamaged())
    player.set_child(LoseParalyzeWhenDamaged())

    set_counter_attack(player)
    set_attack_enemy_i_step_next_to(player)
    set_attack_enemy_stepping_next_to_me(player)

    player.set_child(PlayerThrowStoneAction())

    player.set_child(DungeonMask())
    player.set_child(Vision())
    player.set_child(AwarenessChecker())

    player.set_child(MemoryMap())
    player.set_child(Inventory())
    player.set_child(InputActor())
    player.set_child(Path())
    player.set_child(Equipment())
    player.set_child(EffectQueue())
    player.set_child(PickUpItemAction())
    player.set_child(LeaveCorpseOnDeath())
    return player 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 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 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 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.
from actor import DoNothingActor
from compositecore import Composite
from equipment import Equipment
from gamestate import GameState, GameStateDummy
from graphic import CharPrinter
from inventory import Inventory
from player import new_player
from position import DungeonLevel, Position
from stats import DataPoint, DataTypes
from vision import Vision
import dungeonlevelfactory
import gametime

__author__ = 'co'


dungeon1 = ["#####",
            "#._.#",
            "#.#.#",
            "#...#",
            "#####"]
def get_test_level():
    return dungeonlevelfactory.dungeon_level_from_lines(dungeon1)

def get_dummy_actor():
    dummy_actor = Composite()
    dummy_actor.set_child(DoNothingActor())
    dummy_actor.set_child(Equipment())
    dummy_actor.set_child(DataPoint(DataTypes.ENERGY, -gametime.single_turn))
    dummy_actor.set_child(DataPoint(DataTypes.OFFENCIVE_ATTACK_CHANCE, 0.0))

    dummy_actor.set_child(DataPoint(DataTypes.COUNTER_ATTACK_CHANCE, 0.0))