예제 #1
0
class Variable(AoE2Object):
    """Object for handling a variable."""

    _link_list = [
        RetrieverObjectLink("variable_id", "Triggers",
                            "variable_data[__index__].variable_id"),
        RetrieverObjectLink("name", "Triggers",
                            "variable_data[__index__].variable_name"),
    ]

    def __init__(self, variable_id: int, name: str):
        self.variable_id = variable_id
        self.name = name

        super().__init__()
예제 #2
0
class MapManagerDE(MapManager):

    _link_list = [
        RetrieverObjectLink("map_color_mood", "Map", "map_color_mood"),
        RetrieverObjectLink("collide_and_correct", "Map", "collide_and_correct"),
        RetrieverObjectLink("villager_force_drop", "Map", "villager_force_drop"),
        RetrieverObjectLink("map_width", "Map", "map_width"),
        RetrieverObjectLink("map_height", "Map", "map_height"),
        RetrieverObjectLink("terrain", "Map", "terrain_data", process_as_object=TerrainTile),
        RetrieverObjectLink("script_name", "Map", "script_name", Support(since=1.40)),
    ]

    def __init__(self,
                 map_color_mood: str,
                 collide_and_correct: bool,
                 villager_force_drop: bool,
                 map_width: int,
                 map_height: int,
                 terrain: List[TerrainTile],
                 script_name: str
                 ):

        self.map_color_mood = map_color_mood
        self.collide_and_correct = collide_and_correct
        self.villager_force_drop = villager_force_drop
        self.script_name = script_name

        super().__init__(map_width, map_height, terrain)
class UnitManagerDE(UnitManager):
    _link_list = [
        RetrieverObjectLink("units",
                            "Units",
                            "players_units[].units",
                            process_as_object=Unit)
    ]

    def __init__(self, units: List[List[Unit]]):
        super().__init__(units)
예제 #4
0
class Trigger(AoE2Object):
    """Object for handling a trigger."""

    _link_list = [
        RetrieverObjectLink("name", "Triggers",
                            "trigger_data[__index__].trigger_name"),
        RetrieverObjectLink("description", "Triggers",
                            "trigger_data[__index__].trigger_description"),
        RetrieverObjectLink(
            "description_stid", "Triggers",
            "trigger_data[__index__].description_string_table_id"),
        RetrieverObjectLink("display_as_objective", "Triggers",
                            "trigger_data[__index__].display_as_objective"),
        RetrieverObjectLink("short_description", "Triggers",
                            "trigger_data[__index__].short_description"),
        RetrieverObjectLink(
            "short_description_stid", "Triggers",
            "trigger_data[__index__].short_description_string_table_id"),
        RetrieverObjectLink("display_on_screen", "Triggers",
                            "trigger_data[__index__].display_on_screen"),
        RetrieverObjectLink(
            "description_order", "Triggers",
            "trigger_data[__index__].objective_description_order"),
        RetrieverObjectLink("enabled", "Triggers",
                            "trigger_data[__index__].enabled"),
        RetrieverObjectLink("looping", "Triggers",
                            "trigger_data[__index__].looping"),
        RetrieverObjectLink("header", "Triggers",
                            "trigger_data[__index__].make_header"),
        RetrieverObjectLink("mute_objectives", "Triggers",
                            "trigger_data[__index__].mute_objectives"),
        RetrieverObjectLink("conditions",
                            "Triggers",
                            "trigger_data[__index__].condition_data",
                            process_as_object=Condition),
        RetrieverObjectLink(
            "condition_order", "Triggers",
            "trigger_data[__index__].condition_display_order_array"),
        RetrieverObjectLink("effects",
                            "Triggers",
                            "trigger_data[__index__].effect_data",
                            process_as_object=Effect),
        RetrieverObjectLink(
            "effect_order", "Triggers",
            "trigger_data[__index__].effect_display_order_array"),
        RetrieverObjectLink("trigger_id", retrieve_instance_number=True),
    ]

    def __init__(
        self,
        name: str,
        description: str = "",
        description_stid: int = -1,
        display_as_objective: int = 0,
        short_description: str = "",
        short_description_stid: int = -1,
        display_on_screen: int = 0,
        description_order: int = 0,
        enabled: int = 1,
        looping: int = 0,
        header: int = 0,
        mute_objectives: int = 0,
        conditions: List[Condition] = None,
        condition_order: List[int] = None,
        effects: List[Effect] = None,
        effect_order: List[int] = None,
        trigger_id: int = -1,
    ):
        if conditions is None:
            conditions = []
        if condition_order is None:
            condition_order = []
        if effects is None:
            effects = []
        if effect_order is None:
            effect_order = []

        self.name: str = name
        self.description: str = description
        self.description_stid: int = description_stid
        self.display_as_objective: int = display_as_objective
        self.short_description: str = short_description
        self.short_description_stid: int = short_description_stid
        self.display_on_screen: int = display_on_screen
        self.description_order: int = description_order
        self.enabled: int = enabled
        self.looping: int = looping
        self.header: int = header
        self.mute_objectives: int = mute_objectives
        self._condition_hash = hash_list(conditions)
        self.conditions: List[Condition] = conditions
        self.condition_order: List[int] = condition_order
        self._effect_hash = hash_list(effects)
        self.effects: List[Effect] = effects
        self.effect_order: List[int] = effect_order
        self.trigger_id: int = trigger_id

        self.new_effect = NewEffectSupport(self)
        self.new_condition = NewConditionSupport(self)

        super().__init__()

    def __deepcopy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            if k in ['new_effect', 'new_condition']:
                continue
            setattr(result, k, self._deepcopy_entry(k, v))
        return result

    @property
    def condition_order(self):
        if list_changed(self.conditions, self._condition_hash):
            update_order_array(self._condition_order, len(self.conditions))
            self._condition_hash = hash_list(self.conditions)
        return self._condition_order

    @condition_order.setter
    def condition_order(self, val):
        self._condition_order = val

    @property
    def effect_order(self):
        if list_changed(self.effects, self._effect_hash):
            update_order_array(self._effect_order, len(self.effects))
            self._effect_hash = hash_list(self.effects)
        return self._effect_order

    @effect_order.setter
    def effect_order(self, val):
        self._effect_order = val

    @property
    def conditions(self) -> List[Condition]:
        return self._conditions

    @conditions.setter
    def conditions(self, val: List[Condition]) -> None:
        self._conditions = val
        self.condition_order = list(range(0, len(val)))

    @property
    def effects(self) -> List[Effect]:
        return self._effects

    @effects.setter
    def effects(self, val: List[Effect]) -> None:
        self._effects = val
        self.effect_order = list(range(0, len(val)))

    def _add_effect(self,
                    effect_type: EffectId,
                    ai_script_goal=None,
                    armour_attack_quantity=None,
                    armour_attack_class=None,
                    quantity=None,
                    tribute_list=None,
                    diplomacy=None,
                    object_list_unit_id=None,
                    source_player=None,
                    target_player=None,
                    technology=None,
                    string_id=None,
                    display_time=None,
                    trigger_id=None,
                    location_x=None,
                    location_y=None,
                    location_object_reference=None,
                    area_x1=None,
                    area_y1=None,
                    area_x2=None,
                    area_y2=None,
                    object_group=None,
                    object_type=None,
                    instruction_panel_position=None,
                    attack_stance=None,
                    time_unit=None,
                    enabled=None,
                    food=None,
                    wood=None,
                    stone=None,
                    gold=None,
                    item_id=None,
                    flash_object=None,
                    force_research_technology=None,
                    visibility_state=None,
                    scroll=None,
                    operation=None,
                    object_list_unit_id_2=None,
                    button_location=None,
                    ai_signal_value=None,
                    object_attributes=None,
                    variable=None,
                    timer=None,
                    facet=None,
                    play_sound=None,
                    message=None,
                    player_color=None,
                    sound_name=None,
                    selected_object_ids=None) -> Effect:
        """Used to add new effect to trigger. Please use trigger.new_effect.<effect_name> instead"""
        def get_default_effect_attributes(eff_type):
            """Gets the default effect attributes based on a certain effect type, with exception handling"""
            try:
                return effect_dataset.default_attributes[eff_type]
            except KeyError:
                effect = EffectId(eff_type)
                raise UnsupportedAttributeError(
                    f"The effect {effect.name} is not supported in scenario version {self._scenario_version}"
                ) from None

        effect_defaults = get_default_effect_attributes(effect_type)
        effect_attr = {}
        for key, value in effect_defaults.items():
            effect_attr[key] = (locals()[key]
                                if locals()[key] is not None else value)
        new_effect = Effect(**effect_attr)
        self.effects.append(new_effect)
        return new_effect

    def _add_condition(self,
                       condition_type: ConditionId,
                       quantity=None,
                       attribute=None,
                       unit_object=None,
                       next_object=None,
                       object_list=None,
                       source_player=None,
                       technology=None,
                       timer=None,
                       area_x1=None,
                       area_y1=None,
                       area_x2=None,
                       area_y2=None,
                       object_group=None,
                       object_type=None,
                       ai_signal=None,
                       inverted=None,
                       variable=None,
                       comparison=None,
                       target_player=None,
                       unit_ai_action=None,
                       xs_function=None) -> Condition:
        """Used to add new condition to trigger. Please use trigger.new_condition.<condition_name> instead"""
        def get_default_condition_attributes(cond_type):
            """Gets the default condition attributes based on a certain condition type, with exception handling"""
            try:
                return condition_dataset.default_attributes[cond_type]
            except KeyError:
                condition = ConditionId(cond_type)
                raise UnsupportedAttributeError(
                    f"The condition {condition.name} is not supported in scenario version {self._scenario_version}"
                ) from None

        condition_defaults = get_default_condition_attributes(condition_type)
        condition_attr = {}
        for key, value in condition_defaults.items():
            condition_attr[key] = (locals()[key]
                                   if locals()[key] is not None else value)
        new_condition = Condition(**condition_attr)
        self.conditions.append(new_condition)
        return new_condition

    def get_effect(self,
                   effect_index: int = None,
                   display_index: int = None) -> Effect:
        if not exclusive_if(effect_index is not None, display_index
                            is not None):
            raise ValueError(
                f"Please identify an effect using either effect_index or display_index."
            )

        if effect_index is None:
            effect_index = self.effect_order[display_index]

        return self.effects[effect_index]

    def get_condition(self,
                      condition_index: int = None,
                      display_index: int = None) -> Condition:
        if not exclusive_if(condition_index is not None, display_index
                            is not None):
            raise ValueError(
                f"Please identify a condition using either condition_index or display_index."
            )

        if condition_index is None:
            condition_index = self.condition_order[display_index]

        return self.conditions[condition_index]

    def remove_effect(self,
                      effect_index: int = None,
                      display_index: int = None,
                      effect: Effect = None) -> None:
        if not exclusive_if(effect_index is not None, display_index
                            is not None, effect is not None):
            raise ValueError(
                f"Please identify an effect using either effect_index, display_index or effect."
            )

        if effect is not None:
            effect_index = self.effects.index(effect)

        if effect_index is None:
            effect_index = self.effect_order[display_index]
        else:
            display_index = self.effect_order.index(effect_index)

        del self.effects[effect_index]
        del self.effect_order[display_index]

        self.effect_order = [
            x - 1 if x > effect_index else x for x in self.effect_order
        ]

    def remove_condition(self, condition_index: int = None, display_index: int = None, condition: Condition = None) \
            -> None:
        if not exclusive_if(condition_index is not None, display_index
                            is not None, condition is not None):
            raise ValueError(
                f"Please identify a condition using either condition_index, display_index or condition."
            )

        if condition is not None:
            condition_index = self.conditions.index(condition)

        if condition_index is None:
            condition_index = self.condition_order[display_index]
        else:
            display_index = self.condition_order.index(condition_index)

        del self.conditions[condition_index]
        del self.condition_order[display_index]

        self.condition_order = [
            x - 1 if x > condition_index else x for x in self.condition_order
        ]

    def get_content_as_string(self) -> str:
        return_string = ""
        data_tba = {'enabled': self.enabled != 0, 'looping': self.looping != 0}

        if self.description != "":
            data_tba['description'] = f"'{self.description}'"
        if self.description_stid != -1:
            data_tba['description_stid'] = self.description_stid
        if self.short_description != "":
            data_tba['short_description'] = f"'{self.short_description}'"
        if self.short_description_stid != -1:
            data_tba['short_description_stid'] = self.short_description_stid
        if self.display_as_objective != 0:
            data_tba['display_as_objective'] = (self.display_as_objective != 0)
        if self.display_on_screen != 0:
            data_tba['display_on_screen'] = (self.display_on_screen != 0)
        if self.description_order != 0:
            data_tba['description_order'] = self.description_order
        if self.header != 0:
            data_tba['header'] = (self.header != 0)
        if self.mute_objectives != 0:
            data_tba['mute_objectives'] = (self.mute_objectives != 0)

        for key, value in data_tba.items():
            return_string += f"\t\t{key}: {value}\n"

        if len(self.condition_order) > 0:
            return_string += "\t\tconditions:\n"
            for c_display_order, condition_id in enumerate(
                    self.condition_order):
                condition = self.conditions[condition_id]

                return_string += f"\t\t\t{condition_dataset.condition_names[condition.condition_type]} " \
                                 f"[Index: {condition_id}, Display: {c_display_order}]:\n"
                return_string += condition.get_content_as_string()

        if len(self.effect_order) > 0:
            return_string += "\t\teffects:\n"
            for e_display_order, effect_id in enumerate(self.effect_order):
                effect = self.effects[effect_id]

                return_string += f"\t\t\t{effect_dataset.effect_names[effect.effect_type]}" \
                                 f" [Index: {effect_id}, Display: {e_display_order}]:\n"
                return_string += effect.get_content_as_string()

        return return_string
예제 #5
0
class Unit(AoE2Object):
    _link_list = [
        RetrieverObjectLink("player", retrieve_history_number=0),
        RetrieverObjectLink("x", "Units",
                            "players_units[__index__].units[__index__].x"),
        RetrieverObjectLink("y", "Units",
                            "players_units[__index__].units[__index__].y"),
        RetrieverObjectLink("z", "Units",
                            "players_units[__index__].units[__index__].z"),
        RetrieverObjectLink(
            "reference_id", "Units",
            "players_units[__index__].units[__index__].reference_id"),
        RetrieverObjectLink(
            "unit_const", "Units",
            "players_units[__index__].units[__index__].unit_const"),
        RetrieverObjectLink(
            "status", "Units",
            "players_units[__index__].units[__index__].status"),
        RetrieverObjectLink(
            "rotation", "Units",
            "players_units[__index__].units[__index__].rotation"),
        RetrieverObjectLink(
            "initial_animation_frame", "Units",
            "players_units[__index__].units[__index__].initial_animation_frame"
        ),
        RetrieverObjectLink(
            "garrisoned_in_id", "Units",
            "players_units[__index__].units[__index__].garrisoned_in_id"),
    ]

    def __init__(self, player: PlayerId, x: float, y: float, z: float,
                 reference_id: int, unit_const: int, status: int,
                 rotation: float, initial_animation_frame: int,
                 garrisoned_in_id: int):
        raise_if_not_int_subclass([unit_const])

        self._player: PlayerId = PlayerId(player)
        """
        PLEASE NOTE: This is an internal (read-only) value for ease of access. It accurately represent the actual 
        player controlling the unit but is not directly connected to it. Changing this value will have no impact to your
        scenario.
        To change which player controls this unit, use:
            unit_manager.change_ownership(Unit, to_player)
        """
        self.x: float = x
        self.y: float = y
        self.z: float = z
        self.reference_id: int = reference_id
        self.unit_const: int = unit_const
        self.status: int = status
        self.rotation: float = rotation
        self.initial_animation_frame: int = initial_animation_frame
        self.garrisoned_in_id: int = garrisoned_in_id

        super().__init__()

    @property
    def player(self) -> PlayerId:
        """
        PLEASE NOTE: This is an internal (read-only) value for ease of access. It DOES accurately represent the actual
        player controlling the unit BUT IT IS NOT directly connected to it. Changing this value will have no impact to
        your scenario.
        To change which player controls this unit, use:
            unit_manager.change_ownership(Unit, to_player)
        """
        return self._player

    @property
    def tile(self) -> Tile:
        return Tile(math.floor(self.x), math.floor(self.y))
        # Floor x and y as location (0.9, 0.9) is still Tile[x=0, y=0]

    @tile.setter
    def tile(self, tile: Tile) -> None:
        self.x = tile.x
        self.y = tile.y

    @property
    def name(self) -> str:
        unit_enum = helper.get_enum_from_unit_const(self.unit_const)
        if unit_enum:
            return pretty_format_name(unit_enum.name)
        else:
            return f"Unknown{self.unit_const}"  # e.g."Unknown411"
class TriggerManager(AoE2Object):
    """Manager of the everything trigger related."""

    _link_list = [
        RetrieverObjectLink("triggers", "Triggers", "trigger_data", process_as_object=Trigger),
        RetrieverObjectLink("trigger_display_order", "Triggers", "trigger_display_order_array"),
    ]

    def __init__(self,
                 triggers: List[Trigger],
                 trigger_display_order: List[int],
                 ):

        self._trigger_hash = hash_list(triggers)
        self.triggers: List[Trigger] = triggers
        self.trigger_display_order: List[int] = trigger_display_order

        super().__init__()

    @property
    def triggers(self):
        return self._triggers

    @triggers.setter
    def triggers(self, value):
        self._trigger_hash = hash_list(value)
        self._triggers = value
        self.trigger_display_order = list(range(len(value)))

    @property
    def trigger_display_order(self):
        if list_changed(self.triggers, self._trigger_hash):
            update_order_array(self._trigger_display_order, len(self.triggers))
            self._trigger_hash = hash_list(self.triggers)
        return self._trigger_display_order

    @trigger_display_order.setter
    def trigger_display_order(self, val):
        self._trigger_display_order = val

    def copy_trigger_per_player(self,
                                from_player,
                                trigger_select,
                                change_from_player_only=False,
                                include_player_source=True,
                                include_player_target=False,
                                trigger_ce_lock=None,
                                include_gaia: bool = False,
                                create_copy_for_players: List[IntEnum] = None) -> Dict[PlayerId, Trigger]:
        """
        Copies a trigger for all or a selection of players. Every copy will change desired player attributes with it.

        Args:
            from_player (IntEnum): The central player this trigger is created for. This is the player that will not get
                a copy.
            trigger_select (TriggerSelect): An object used to identify which trigger to select.
            change_from_player_only (bool): If set to True, only change player attributes in effects and conditions that
                are equal to the player defined using the `from_player` parameter.
            include_player_source (bool): If set to True, allow player source attributes to be changed while copying.
                Player source attributes are attributes where a player is defined to perform an action such as create an
                object. If set to False these attributes will remain unchanged.
            include_player_target (bool): If set to True, allow player target attributes to be changed while copying.
                Player target attributes are attributes where a player is defined as the target such as change ownership
                or sending resources. If set to False these attributes will remain unchanged.
            trigger_ce_lock (TriggerCELock): The TriggerCELock object. Used to lock certain (types) of conditions or
                effects from being changed while copying.
            include_gaia (bool): If True, GAIA is included in the copied list. (Also when `create_copy_for_players` is
                defined)
            create_copy_for_players (List[IntEnum]): A list of Players to create a copy for. The `from_player` will be
                excluded from this list.

        Returns:
            A dict with all the new created triggers. The key is the player for which the trigger is
                created using the IntEnum associated with it. Example:
                {PlayerId.TWO: Trigger, PlayerId.FIVE: Trigger}

        Raises:
            ValueError: if more than one trigger selection is used. Any of (trigger_index, display_index or trigger)
                Or if Both `include_player_source` and `include_player_target` are `False`

        :Authors:
            KSneijders

        """
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
        if not include_player_source and not include_player_target:
            raise ValueError("Cannot exclude player source and target.")

        if create_copy_for_players is None:
            create_copy_for_players = [
                PlayerId.ONE, PlayerId.TWO, PlayerId.THREE, PlayerId.FOUR,
                PlayerId.FIVE, PlayerId.SIX, PlayerId.SEVEN, PlayerId.EIGHT
            ]
        if include_gaia and PlayerId.GAIA not in create_copy_for_players:
            create_copy_for_players.append(PlayerId.GAIA)

        alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)

        return_dict: Dict[PlayerId, Trigger] = {}
        for player in create_copy_for_players:
            if not player == from_player:
                new_trigger = self.copy_trigger(TS.trigger(trigger))
                new_trigger.name += f" (p{player})"
                return_dict[player] = new_trigger

                for cond_x in alter_conditions:
                    cond = new_trigger.conditions[cond_x]
                    # Player not set
                    if cond.source_player == -1:
                        continue
                    # Player not equal to 'from_player'
                    if change_from_player_only:
                        if not cond.source_player == from_player:
                            continue
                    # Change source player
                    if include_player_source:
                        cond.source_player = PlayerId(player)
                    # Change target player
                    if include_player_target:
                        cond.target_player = PlayerId(player)
                for effect_x in alter_effects:
                    effect = new_trigger.effects[effect_x]
                    # Player not set
                    if effect.source_player == -1:
                        continue
                    # Player not equal to 'from_player'
                    if change_from_player_only:
                        if not effect.source_player == from_player:
                            continue
                    # Change source player
                    if include_player_source:
                        effect.source_player = PlayerId(player)
                    # Change target player
                    if include_player_target:
                        effect.target_player = PlayerId(player)

        return return_dict

    def copy_trigger(self, trigger_select) -> Trigger:
        """
        Creates an exact copy (deepcopy) of this trigger.

        Args:
            trigger_select (TriggerSelect): An object used to identify which trigger to select.

        Returns:
            The newly copied trigger
        """
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)

        deepcopy_trigger = copy.deepcopy(trigger)
        deepcopy_trigger.name += " (copy)"
        deepcopy_trigger.trigger_id = len(self.triggers)
        self.triggers.append(deepcopy_trigger)

        return deepcopy_trigger

    def copy_trigger_tree_per_player(self,
                                     from_player,
                                     trigger_select,
                                     change_from_player_only=False,
                                     include_player_source=True,
                                     include_player_target=False,
                                     trigger_ce_lock=None,
                                     include_gaia: bool = False,
                                     create_copy_for_players: List[IntEnum] = None,
                                     group_triggers_by=None):
        """
        Copies an entire trigger tree for all or a selection of players. Every copy will change desired player
        attributes with it. Trigger trees are triggers linked together using EffectId.(DE)ACTIVATE_TRIGGER.

        Args:
            from_player (IntEnum): The central player this trigger is created for. This is the player that will not get
                a copy.
            trigger_select (TriggerSelect): An object used to identify which trigger to select.
            change_from_player_only (bool): If set to True, only change player attributes in effects and conditions that
                are equal to the player defined using the `from_player` parameter.
            include_player_source (bool): If set to True, allow player source attributes to be changed while copying.
                Player source attributes are attributes where a player is defined to perform an action such as create an
                object. If set to False these attributes will remain unchanged.
            include_player_target (bool): If set to True, allow player target attributes to be changed while copying.
                Player target attributes are attributes where a player is defined as the target such as change ownership
                or sending resources. If set to False these attributes will remain unchanged.
            trigger_ce_lock (TriggerCELock): The TriggerCELock object. Used to lock certain (types) of conditions or
                effects from being changed while copying.
            include_gaia (bool): If True, GAIA is included in the copied list. (Also when `create_copy_for_players` is
                defined)
            create_copy_for_players (List[IntEnum]): A list of Players to create a copy for. The `from_player` will be
                excluded from this list.
            group_triggers_by (GroupBy): How to group the newly added triggers.

        Returns:
            The newly created triggers in a dict using the Player as key and as value with a list of triggers
        """
        if group_triggers_by is None:
            group_triggers_by = GroupBy.NONE

        trigger_index, display_index, source_trigger = self._validate_and_retrieve_trigger_info(trigger_select)

        known_node_indexes = [trigger_index]
        self._find_trigger_tree_nodes_recursively(source_trigger, known_node_indexes)

        new_triggers = {}
        id_swap = {}
        for index in known_node_indexes:
            triggers = self.copy_trigger_per_player(
                from_player,
                TS.index(index),
                change_from_player_only,
                include_player_source,
                include_player_target,
                trigger_ce_lock,
                include_gaia,
                create_copy_for_players,
            )
            for player, trigger in triggers.items():
                id_swap.setdefault(index, {})[player] = trigger.trigger_id
                new_triggers.setdefault(player, []).append(trigger)

        for player, triggers in new_triggers.items():
            for trigger in triggers:
                activation_effects = [
                    effect for effect in trigger.effects if
                    effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER]
                ]
                for effect in activation_effects:
                    effect.trigger_id = id_swap[effect.trigger_id][player]

        # Group by logic
        if group_triggers_by == GroupBy.TRIGGER:
            for index, source_trigger_id in enumerate(known_node_indexes):
                for player, trigger in [(player, triggers[index]) for player, triggers in new_triggers.items()]:
                    # When going negative (going 'below' the source already happens at insert @ 0
                    display_index_offset = player - from_player if from_player <= player else 0
                    source_trigger_display_index = self.trigger_display_order.index(source_trigger_id)
                    self.trigger_display_order.remove(trigger.trigger_id)
                    new_display_index = max(0, source_trigger_display_index + display_index_offset)
                    self.trigger_display_order.insert(
                        new_display_index,
                        trigger.trigger_id
                    )
        elif group_triggers_by == GroupBy.PLAYER:
            source_trigger_display_index = display_index
            source_trigger_offset = 0
            # Group known tree nodes
            for tree_trigger_id in known_node_indexes:
                self.trigger_display_order.remove(tree_trigger_id)
                self.trigger_display_order.insert(
                    source_trigger_display_index + source_trigger_offset,
                    tree_trigger_id
                )
                source_trigger_offset += 1
            # Group copied triggers
            for player, triggers in new_triggers.items():
                source_trigger_display_index = self.trigger_display_order.index(source_trigger.trigger_id)
                source_trigger_offset = 0
                display_index_offset = (player - from_player if from_player <= player else 0) * len(known_node_indexes)
                for trigger in triggers:
                    final_offset = max(source_trigger_display_index + display_index_offset + source_trigger_offset, 0)
                    self.trigger_display_order.remove(trigger.trigger_id)
                    self.trigger_display_order.insert(
                        final_offset,
                        trigger.trigger_id
                    )
                    source_trigger_offset += 1

        return new_triggers

    def copy_trigger_tree(self, trigger_select: TriggerSelect) -> List[Trigger]:
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)

        known_node_indexes = [trigger_index]
        self._find_trigger_tree_nodes_recursively(trigger, known_node_indexes)

        new_triggers = []
        id_swap = {}
        for index in known_node_indexes:
            trigger = self.copy_trigger(TS.index(index))
            new_triggers.append(trigger)
            id_swap[index] = trigger.trigger_id

        for trigger in new_triggers:
            activation_effects = [
                effect for effect in trigger.effects if
                effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER]
            ]
            for effect in activation_effects:
                effect.trigger_id = id_swap[effect.trigger_id]

        return new_triggers

    def replace_player(self, trigger_select, to_player, only_change_from=None, include_player_source=True,
                       include_player_target=False, trigger_ce_lock=None) -> Trigger:
        """
        Replaces player attributes. Specifically useful if multiple players are used in the same trigger.

        Args:
            trigger_select (TriggerSelect): An object used to identify which trigger to select.
            to_player (PlayerId): The player the attributes are changed to.
            only_change_from (PlayerId): Can only change player attributes if the player is equal to the given value
            include_player_source (bool): If set to True, allow player source attributes to be changed while replacing.
                Player source attributes are attributes where a player is defined to perform an action such as create an
                object. If set to False these attributes will remain unchanged.
            include_player_target (bool): If set to True, allow player target attributes to be changed while replacing.
                Player target attributes are attributes where a player is defined as the target such as change ownership
                or sending resources. If set to False these attributes will remain unchanged.
            trigger_ce_lock (TriggerCELock): The TriggerCELock object. Used to lock certain (types) of conditions or
                effects from being changed.

        Returns:
            The given trigger with the proper player attributes changed
        """
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
        alter_conditions, alter_effects = TriggerManager._find_alterable_ce(trigger, trigger_ce_lock)

        for cond_x in alter_conditions:
            cond = trigger.conditions[cond_x]
            if not cond.source_player == -1 and include_player_source:
                if only_change_from is not None and only_change_from != cond.source_player:
                    continue
                cond.source_player = PlayerId(to_player)
            if not cond.target_player == -1 and include_player_target:
                if only_change_from is not None and only_change_from != cond.target_player:
                    continue
                cond.target_player = PlayerId(to_player)
        for effect_x in alter_effects:
            effect = trigger.effects[effect_x]
            if not effect.source_player == -1 and include_player_source:
                if only_change_from is not None and only_change_from != effect.source_player:
                    continue
                effect.source_player = PlayerId(to_player)
            if not effect.target_player == -1 and include_player_target:
                if only_change_from is not None and only_change_from != effect.target_player:
                    continue
                effect.target_player = PlayerId(to_player)

        return trigger

    def add_trigger(self, name, description=None, description_stid=None, display_as_objective=None,
                    short_description=None, short_description_stid=None, display_on_screen=None, description_order=None,
                    enabled=None, looping=None, header=None, mute_objectives=None, conditions=None,
                    effects=None) -> Trigger:
        """
        Adds a new trigger to the scenario.

        Args:
            name (str): The name for the trigger
            description (str): The trigger description
            description_stid (int): The trigger description string table ID
            display_as_objective (bool): Display the trigger as objective
            short_description (str): The short trigger description
            short_description_stid (int): The short trigger description string table ID
            display_on_screen (bool): Display the trigger objective on screen
            description_order (int): ?
            enabled (bool): If the trigger is enabled from the start.
            looping (bool): If the trigger loops.
            header (bool): Turn objective into header
            mute_objectives (bool): Mute objectives
            conditions (List): A list of condition managers
            effects (List): A list of effect managers

        Returns:
            The newly created trigger

        """
        keys = [
            'description', 'description_stid', 'display_as_objective', 'short_description',
            'short_description_stid', 'display_on_screen', 'description_order', 'enabled', 'looping', 'header',
            'mute_objectives', 'conditions', 'effects'
        ]
        trigger_attr = {}
        for key in keys:
            if locals()[key] is not None:
                trigger_attr[key] = locals()[key]
        new_trigger = Trigger(name=name, trigger_id=len(self.triggers), **trigger_attr)
        self.triggers.append(new_trigger)
        return new_trigger

    def get_trigger(self, trigger_select: TriggerSelect) -> Trigger:
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)
        return trigger

    def remove_trigger(self, trigger_select: TriggerSelect) -> None:
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)

        for x in self.trigger_display_order:
            if x > trigger_index:
                self.get_trigger(TS.index(x)).trigger_id -= 1

        del self.triggers[trigger_index]
        del self.trigger_display_order[display_index]

        self.trigger_display_order = [x - 1 if x > trigger_index else x for x in self.trigger_display_order]

    def _find_trigger_tree_nodes_recursively(self, trigger, known_node_indexes: List[int]) -> None:
        found_node_indexes = TriggerManager._find_trigger_tree_nodes(trigger)
        unknown_node_indexes = [i for i in found_node_indexes if i not in known_node_indexes]

        if len(unknown_node_indexes) == 0:
            return

        known_node_indexes += unknown_node_indexes

        for index in unknown_node_indexes:
            self._find_trigger_tree_nodes_recursively(self.triggers[index], known_node_indexes)

    def _validate_and_retrieve_trigger_info(self, trigger_select) -> (int, int, Trigger):
        trigger = trigger_select.trigger
        trigger_index = trigger_select.trigger_index
        display_index = trigger_select.display_index

        if trigger is not None:
            trigger_index = trigger.trigger_id
            display_index = self.trigger_display_order.index(trigger_index)
        elif trigger_index is not None:
            trigger = self.triggers[trigger_index]
            display_index = self.trigger_display_order.index(trigger_index)
        elif display_index is not None:
            trigger_index = self.trigger_display_order[display_index]
            trigger = self.triggers[trigger_index]

        return trigger_index, display_index, trigger

    def get_summary_as_string(self) -> str:
        return_string = "\nTrigger Summary:\n"

        triggers = self.triggers
        display_order = self.trigger_display_order

        if len(display_order) == 0:
            return_string += "\t<< No Triggers >>"

        longest_trigger_name = -1
        longest_index_notation = -1
        for display, trigger_index in enumerate(display_order):
            trigger_name = triggers[trigger_index].name
            longest_trigger_name = max(longest_trigger_name, len(trigger_name))

            longest_index_notation = max(
                longest_index_notation,
                helper.get_int_len(display) + helper.get_int_len(trigger_index)
            )

        longest_trigger_name += 3
        for display, trigger_index in enumerate(display_order):
            trigger = triggers[trigger_index]
            trigger_name = trigger.name

            name_buffer = longest_trigger_name - len(trigger_name)
            index_buffer = longest_index_notation - (helper.get_int_len(display) + helper.get_int_len(trigger_index))
            return_string += "\t" + trigger_name + (" " * name_buffer)
            return_string += f" [Index: {trigger_index}, Display: {display}] {' ' * index_buffer}"

            return_string += "\t(conditions: " + str(len(trigger.conditions)) + ", "
            return_string += " effects: " + str(len(trigger.effects)) + ")\n"

        return return_string

    def get_content_as_string(self) -> str:
        return_string = "\nTriggers:\n"

        if len(self.triggers) == 0:
            return_string += "\t<<No triggers>>\n"

        for trigger_index in self.trigger_display_order:
            return_string += self.get_trigger_as_string(TS.index(trigger_index)) + "\n"

        return_string += "Variables:\n"

        return return_string

    def get_trigger_as_string(self, trigger_select: TriggerSelect) -> str:
        trigger_index, display_index, trigger = self._validate_and_retrieve_trigger_info(trigger_select)

        return_string = "\t'" + trigger.name + "'"
        return_string += " [Index: " + str(trigger_index) + ", Display: " + str(display_index) + "]" + ":\n"

        return_string += trigger.get_content_as_string()

        return return_string

    @staticmethod
    def _find_alterable_ce(trigger, trigger_ce_lock) -> (List[int], List[int]):
        lock_conditions = trigger_ce_lock.lock_conditions if trigger_ce_lock is not None else False
        lock_effects = trigger_ce_lock.lock_effects if trigger_ce_lock is not None else False
        lock_condition_type = trigger_ce_lock.lock_condition_type if trigger_ce_lock is not None else []
        lock_effect_type = trigger_ce_lock.lock_effect_type if trigger_ce_lock is not None else []
        lock_condition_ids = trigger_ce_lock.lock_condition_ids if trigger_ce_lock is not None else []
        lock_effect_ids = trigger_ce_lock.lock_effect_ids if trigger_ce_lock is not None else []

        alter_conditions: List[int] = []
        alter_effects: List[int] = []
        if not lock_conditions:
            for i, cond in enumerate(trigger.conditions):
                if i not in lock_condition_ids and cond.condition_type not in lock_condition_type:
                    alter_conditions.append(i)
        if not lock_effects:
            for i, effect in enumerate(trigger.effects):
                if i not in lock_effect_ids and effect.effect_type not in lock_effect_type:
                    alter_effects.append(i)

        return alter_conditions, alter_effects

    @staticmethod
    def _find_trigger_tree_nodes(trigger: Trigger) -> List[int]:
        return [
            effect.trigger_id for effect in trigger.effects if
            effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER]
        ]
예제 #7
0
class Effect(AoE2Object):
    """Object for handling an effect."""

    _link_list = [
        RetrieverObjectLink(
            "effect_type", "Triggers",
            "trigger_data[__index__].effect_data[__index__].effect_type"),
        RetrieverObjectLink(
            "ai_script_goal", "Triggers",
            "trigger_data[__index__].effect_data[__index__].ai_script_goal"),
        RetrieverObjectLink(
            "armour_attack_quantity", "Triggers",
            "trigger_data[__index__].effect_data[__index__].armour_attack_quantity"
        ),
        RetrieverObjectLink(
            "armour_attack_class", "Triggers",
            "trigger_data[__index__].effect_data[__index__].armour_attack_class"
        ),
        RetrieverObjectLink(
            "quantity", "Triggers",
            "trigger_data[__index__].effect_data[__index__].quantity"),
        RetrieverObjectLink(
            "tribute_list", "Triggers",
            "trigger_data[__index__].effect_data[__index__].tribute_list"),
        RetrieverObjectLink(
            "diplomacy", "Triggers",
            "trigger_data[__index__].effect_data[__index__].diplomacy"),
        RetrieverObjectLink(
            "object_list_unit_id", "Triggers",
            "trigger_data[__index__].effect_data[__index__].object_list_unit_id"
        ),
        RetrieverObjectLink(
            "source_player", "Triggers",
            "trigger_data[__index__].effect_data[__index__].source_player"),
        RetrieverObjectLink(
            "target_player", "Triggers",
            "trigger_data[__index__].effect_data[__index__].target_player"),
        RetrieverObjectLink(
            "technology", "Triggers",
            "trigger_data[__index__].effect_data[__index__].technology"),
        RetrieverObjectLink(
            "string_id", "Triggers",
            "trigger_data[__index__].effect_data[__index__].string_id"),
        RetrieverObjectLink(
            "display_time", "Triggers",
            "trigger_data[__index__].effect_data[__index__].display_time"),
        RetrieverObjectLink(
            "trigger_id", "Triggers",
            "trigger_data[__index__].effect_data[__index__].trigger_id"),
        RetrieverObjectLink(
            "location_x", "Triggers",
            "trigger_data[__index__].effect_data[__index__].location_x"),
        RetrieverObjectLink(
            "location_y", "Triggers",
            "trigger_data[__index__].effect_data[__index__].location_y"),
        RetrieverObjectLink(
            "location_object_reference", "Triggers",
            "trigger_data[__index__].effect_data[__index__].location_object_reference"
        ),
        RetrieverObjectLink(
            "area_1_x", "Triggers",
            "trigger_data[__index__].effect_data[__index__].area_1_x"),
        RetrieverObjectLink(
            "area_1_y", "Triggers",
            "trigger_data[__index__].effect_data[__index__].area_1_y"),
        RetrieverObjectLink(
            "area_2_x", "Triggers",
            "trigger_data[__index__].effect_data[__index__].area_2_x"),
        RetrieverObjectLink(
            "area_2_y", "Triggers",
            "trigger_data[__index__].effect_data[__index__].area_2_y"),
        RetrieverObjectLink(
            "object_group", "Triggers",
            "trigger_data[__index__].effect_data[__index__].object_group"),
        RetrieverObjectLink(
            "object_type", "Triggers",
            "trigger_data[__index__].effect_data[__index__].object_type"),
        RetrieverObjectLink(
            "instruction_panel_position", "Triggers",
            "trigger_data[__index__].effect_data[__index__].instruction_panel_position"
        ),
        RetrieverObjectLink(
            "attack_stance", "Triggers",
            "trigger_data[__index__].effect_data[__index__].attack_stance"),
        RetrieverObjectLink(
            "time_unit", "Triggers",
            "trigger_data[__index__].effect_data[__index__].time_unit"),
        RetrieverObjectLink(
            "enabled", "Triggers",
            "trigger_data[__index__].effect_data[__index__].enabled"),
        RetrieverObjectLink(
            "food", "Triggers",
            "trigger_data[__index__].effect_data[__index__].food"),
        RetrieverObjectLink(
            "wood", "Triggers",
            "trigger_data[__index__].effect_data[__index__].wood"),
        RetrieverObjectLink(
            "stone", "Triggers",
            "trigger_data[__index__].effect_data[__index__].stone"),
        RetrieverObjectLink(
            "gold", "Triggers",
            "trigger_data[__index__].effect_data[__index__].gold"),
        RetrieverObjectLink(
            "item_id", "Triggers",
            "trigger_data[__index__].effect_data[__index__].item_id"),
        RetrieverObjectLink(
            "flash_object", "Triggers",
            "trigger_data[__index__].effect_data[__index__].flash_object"),
        RetrieverObjectLink(
            "force_research_technology", "Triggers",
            "trigger_data[__index__].effect_data[__index__].force_research_technology"
        ),
        RetrieverObjectLink(
            "visibility_state", "Triggers",
            "trigger_data[__index__].effect_data[__index__].visibility_state"),
        RetrieverObjectLink(
            "scroll", "Triggers",
            "trigger_data[__index__].effect_data[__index__].scroll"),
        RetrieverObjectLink(
            "operation", "Triggers",
            "trigger_data[__index__].effect_data[__index__].operation"),
        RetrieverObjectLink(
            "object_list_unit_id_2", "Triggers",
            "trigger_data[__index__].effect_data[__index__].object_list_unit_id_2"
        ),
        RetrieverObjectLink(
            "button_location", "Triggers",
            "trigger_data[__index__].effect_data[__index__].button_location"),
        RetrieverObjectLink(
            "ai_signal_value", "Triggers",
            "trigger_data[__index__].effect_data[__index__].ai_signal_value"),
        RetrieverObjectLink(
            "object_attributes", "Triggers",
            "trigger_data[__index__].effect_data[__index__].object_attributes"
        ),
        RetrieverObjectLink(
            "variable", "Triggers",
            "trigger_data[__index__].effect_data[__index__].variable"),
        RetrieverObjectLink(
            "timer", "Triggers",
            "trigger_data[__index__].effect_data[__index__].timer"),
        RetrieverObjectLink(
            "facet", "Triggers",
            "trigger_data[__index__].effect_data[__index__].facet"),
        RetrieverObjectLink(
            "play_sound", "Triggers",
            "trigger_data[__index__].effect_data[__index__].play_sound"),
        RetrieverObjectLink(
            "player_color", "Triggers",
            "trigger_data[__index__].effect_data[__index__].player_color",
            Support(since=1.40)),
        RetrieverObjectLink(
            "message", "Triggers",
            "trigger_data[__index__].effect_data[__index__].message"),
        RetrieverObjectLink(
            "sound_name", "Triggers",
            "trigger_data[__index__].effect_data[__index__].sound_name"),
        RetrieverObjectLink(
            "selected_object_ids", "Triggers",
            "trigger_data[__index__].effect_data[__index__].selected_object_ids"
        ),
    ]

    def __init__(
        self,
        effect_type: int = None,
        ai_script_goal: int = None,
        armour_attack_quantity: int = None,
        armour_attack_class: int = None,
        quantity: int = None,
        tribute_list: int = None,
        diplomacy: int = None,
        object_list_unit_id: int = None,
        source_player: int = None,
        target_player: int = None,
        technology: int = None,
        string_id: int = None,
        display_time: int = None,
        trigger_id: int = None,
        location_x: int = None,
        location_y: int = None,
        location_object_reference: int = None,
        area_1_x: int = None,
        area_1_y: int = None,
        area_2_x: int = None,
        area_2_y: int = None,
        object_group: int = None,
        object_type: int = None,
        instruction_panel_position: int = None,
        attack_stance: int = None,
        time_unit: int = None,
        enabled: int = None,
        food: int = None,
        wood: int = None,
        stone: int = None,
        gold: int = None,
        item_id: int = None,
        flash_object: int = None,
        force_research_technology: int = None,
        visibility_state: int = None,
        scroll: int = None,
        operation: int = None,
        object_list_unit_id_2: int = None,
        button_location: int = None,
        ai_signal_value: int = None,
        object_attributes: int = None,
        variable: int = None,
        timer: int = None,
        facet: int = None,
        play_sound: int = None,
        player_color: int = None,
        message: str = None,
        sound_name: str = None,
        selected_object_ids: List[int] = None,
    ):
        raise_if_not_int_subclass(
            [object_list_unit_id, technology, object_list_unit_id_2])

        if selected_object_ids is None:
            selected_object_ids = []

        self.effect_type: int = effect_type
        self.ai_script_goal: int = ai_script_goal
        self.armour_attack_quantity: int = armour_attack_quantity
        self.armour_attack_class: int = armour_attack_class
        self.quantity: int = quantity
        self.tribute_list: int = tribute_list
        self.diplomacy: int = diplomacy
        self.object_list_unit_id: int = object_list_unit_id
        self.source_player: int = source_player
        self.target_player: int = target_player
        self.technology: int = technology
        self.string_id: int = string_id
        self.display_time: int = display_time
        self.trigger_id: int = trigger_id
        self.location_x: int = location_x
        self.location_y: int = location_y
        self.location_object_reference: int = location_object_reference
        self.area_1_x: int = area_1_x
        self.area_1_y: int = area_1_y
        self.area_2_x: int = area_2_x
        self.area_2_y: int = area_2_y
        self.object_group: int = object_group
        self.object_type: int = object_type
        self.instruction_panel_position: int = instruction_panel_position
        self.attack_stance: int = attack_stance
        self.time_unit: int = time_unit
        self.enabled: int = enabled
        self.food: int = food
        self.wood: int = wood
        self.stone: int = stone
        self.gold: int = gold
        self.item_id: int = item_id
        self.flash_object: int = flash_object
        self.force_research_technology: int = force_research_technology
        self.visibility_state: int = visibility_state
        self.scroll: int = scroll
        self.operation: int = operation
        self.object_list_unit_id_2: int = object_list_unit_id_2
        self.button_location: int = button_location
        self.ai_signal_value: int = ai_signal_value
        self.object_attributes: int = object_attributes
        self.variable: int = variable
        self.timer: int = timer
        self.facet: int = facet
        self.play_sound: int = play_sound
        self.player_color: int = player_color
        self.message: str = message
        self.sound_name: str = sound_name
        self.selected_object_ids: List[int] = selected_object_ids

        super().__init__()

    @property
    def selected_object_ids(self) -> List[int]:
        return self._selected_object_ids

    @selected_object_ids.setter
    def selected_object_ids(self, val: List[int]):
        val = listify(val)
        self._selected_object_ids = val

    def get_content_as_string(self) -> str:
        if self.effect_type not in effects.attributes:  # Unknown effect
            attributes_list = effects.empty_attributes
        else:
            attributes_list = effects.attributes[self.effect_type]

        return_string = ""
        for attribute in attributes_list:
            attribute_value = getattr(self, attribute)
            if attribute == "effect_type" or attribute_value in [[], [-1], "",
                                                                 " ", -1]:
                continue
            return_string += "\t\t\t\t" + attribute + ": " + str(
                attribute_value) + "\n"

        if return_string == "":
            return "\t\t\t\t<< No Attributes >>\n"

        return return_string
예제 #8
0
class Condition(AoE2Object):
    """Object for handling a condition."""

    _link_list = [
        RetrieverObjectLink("condition_type", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].condition_type"),
        RetrieverObjectLink("quantity", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].quantity"),
        RetrieverObjectLink("attribute", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].attribute"),
        RetrieverObjectLink("unit_object", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].unit_object"),
        RetrieverObjectLink("next_object", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].next_object"),
        RetrieverObjectLink("object_list", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].object_list"),
        RetrieverObjectLink("source_player", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].source_player"),
        RetrieverObjectLink("technology", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].technology"),
        RetrieverObjectLink("timer", "Triggers", "trigger_data[__index__].condition_data[__index__].timer"),
        RetrieverObjectLink("area_1_x", "Triggers", "trigger_data[__index__].condition_data[__index__].area_1_x"),
        RetrieverObjectLink("area_1_y", "Triggers", "trigger_data[__index__].condition_data[__index__].area_1_y"),
        RetrieverObjectLink("area_2_x", "Triggers", "trigger_data[__index__].condition_data[__index__].area_2_x"),
        RetrieverObjectLink("area_2_y", "Triggers", "trigger_data[__index__].condition_data[__index__].area_2_y"),
        RetrieverObjectLink("object_group", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].object_group"),
        RetrieverObjectLink("object_type", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].object_type"),
        RetrieverObjectLink("ai_signal", "Triggers", "trigger_data[__index__].condition_data[__index__].ai_signal"),
        RetrieverObjectLink("inverted", "Triggers", "trigger_data[__index__].condition_data[__index__].inverted"),
        RetrieverObjectLink("variable", "Triggers", "trigger_data[__index__].condition_data[__index__].variable"),
        RetrieverObjectLink("comparison", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].comparison"),
        RetrieverObjectLink("target_player", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].target_player"),
        RetrieverObjectLink("xs_function", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].xs_function", Support(since=1.40)),
        RetrieverObjectLink("unit_ai_action", "Triggers",
                            "trigger_data[__index__].condition_data[__index__].unit_ai_action", Support(since=1.40)),
    ]

    def __init__(self,
                 condition_type: int = None,
                 quantity: int = None,
                 attribute: int = None,
                 unit_object: int = None,
                 next_object: int = None,
                 object_list: int = None,
                 source_player: IntEnum = None,
                 technology: IntEnum = None,
                 timer: int = None,
                 area_1_x: int = None,
                 area_1_y: int = None,
                 area_2_x: int = None,
                 area_2_y: int = None,
                 object_group: int = None,
                 object_type: int = None,
                 ai_signal: int = None,
                 inverted: int = None,
                 variable: int = None,
                 comparison: int = None,
                 target_player: IntEnum = None,
                 unit_ai_action: int = None,
                 xs_function: str = None,
                 ):
        raise_if_not_int_subclass([object_list, technology])

        self.condition_type: int = condition_type
        self.quantity: int = quantity
        self.attribute: int = attribute
        self.unit_object: int = unit_object
        self.next_object: int = next_object
        self.object_list: int = object_list
        self.source_player: int = source_player
        self.technology: int = technology
        self.timer: int = timer
        self.area_1_x: int = area_1_x
        self.area_1_y: int = area_1_y
        self.area_2_x: int = area_2_x
        self.area_2_y: int = area_2_y
        self.object_group: int = object_group
        self.object_type: int = object_type
        self.ai_signal: int = ai_signal
        self.inverted: int = inverted
        self.variable: int = variable
        self.comparison: int = comparison
        self.target_player: int = target_player
        self.unit_ai_action: int = unit_ai_action
        self.xs_function: str = xs_function

        super().__init__()

    def get_content_as_string(self) -> str:
        if self.condition_type not in conditions.attributes:
            attributes_list = conditions.empty_attributes
        else:
            attributes_list = conditions.attributes[self.condition_type]

        return_string = ""
        for attribute in attributes_list:
            attr = getattr(self, attribute)
            if attribute == "condition_type" or attr in [[], [-1], [''], "", " ", -1]:
                continue
            return_string += "\t\t\t\t" + attribute + ": " + str(attr) + "\n"

        if return_string == "":
            return "\t\t\t\t<< No Attributes >>\n"

        return return_string
class TriggerManagerDE(TriggerManager):
    _link_list = [
        RetrieverObjectLink("triggers",
                            "Triggers",
                            "trigger_data",
                            process_as_object=Trigger),
        RetrieverObjectLink("trigger_display_order", "Triggers",
                            "trigger_display_order_array"),
        RetrieverObjectLink("variables",
                            "Triggers",
                            "variable_data",
                            process_as_object=Variable),
    ]

    def __init__(self, triggers: List[Trigger],
                 trigger_display_order: List[int], variables: List[Variable]):
        self.variables: List[Variable] = variables

        super().__init__(triggers, trigger_display_order)

    def add_variable(self, name: str, variable_id: int = -1) -> Variable:
        """
        Adds a variable.

        Args:
            name (str): The name for the variable
            variable_id (int): The ID of the variable. If left empty (default: -1), lowest available value will be used

        Returns:
            The newly added Variable
        """
        list_of_var_ids = [var.variable_id for var in self.variables]
        if variable_id == -1:
            for i in range(256):
                if i not in list_of_var_ids:
                    variable_id = i
                    break
            if variable_id == -1:
                raise IndexError(
                    f"No variable ID available. All in use? In use: ({list_of_var_ids}/256)"
                )
        if not (0 <= variable_id <= 255):
            raise ValueError(
                "Variable ID has to fall between 0 and 255 (incl).")
        if variable_id in list_of_var_ids:
            raise ValueError("Variable ID already in use.")

        new_variable = Variable(variable_id=variable_id, name=name)
        self.variables.append(new_variable)
        return new_variable

    def get_variable(self,
                     variable_id: int = None,
                     variable_name: str = None) -> Variable:
        if not exclusive_if(variable_id is not None, variable_name
                            is not None):
            raise ValueError(
                "Select a variable using either the variable_id or variable_name parameters."
            )
        for variable in self.variables:
            if variable.variable_id == variable_id or variable.name == variable_name:
                return variable

    def get_summary_as_string(self) -> str:
        return_string = super().get_summary_as_string()

        return_string += "\nVariables Summary:\n"
        if len(self.variables) == 0:
            return_string += "\t<< No Variables >>"

        longest_variable_name = -1
        for variable in self.variables:
            longest_variable_name = max(longest_variable_name,
                                        len(variable.name))

        longest_variable_name += 3
        for index, variable in enumerate(self.variables):
            var_name = variable.name
            name_buffer = " " * (longest_variable_name - len(var_name))
            return_string += f"\t{var_name}{name_buffer}[Index: {variable.variable_id}]\n"

        return return_string

    def get_content_as_string(self) -> str:
        return_string = super().get_content_as_string()

        if len(self.variables) == 0:
            return_string += "\t<<No Variables>>\n"

        for variable in self.variables:
            return_string += f"\t'{variable.name}' [Index: {variable.variable_id}]\n"

        return return_string
예제 #10
0
class UnitManager(AoE2Object):
    """Manager of the everything trigger related."""

    _link_list = [
        RetrieverObjectLink("units",
                            "Units",
                            "players_units[].units",
                            process_as_object=Unit)
    ]

    def __init__(self, units: List[List[Unit]]):
        self.units = units

        super().__init__()

    @property
    def units(self):
        return self._units

    @units.setter
    def units(self, value: List[List[Unit]]):
        def _raise():
            raise ValueError(
                "Units should be list with 9 sub lists, example: [[Unit], [Unit, Unit], ...]"
            )

        if len(value) > 9:
            _raise()
        elif len(value) < 9:
            value.extend([[] for _ in range(9 - len(value))])

        self._units = value

    def add_unit(
        self,
        player: PlayerId,
        unit_const: int,
        x: float,
        y: float,
        z: float = 0,
        rotation: float = 0,
        garrisoned_in_id: int = -1,
        animation_frame: int = 0,
        status: int = 2,
        reference_id: int = None,
    ) -> Unit:
        """
        Adds a unit to the scenario.

        Args:
            player: The player the unit belongs to.
            unit_const: Defines what unit you're placing. The IDs used in the unit/buildings dataset.
            x: The x location in the scenario.
            y: The y location in the scenario.
            z: The z (height) location in the scenario.
            rotation: The rotation of the unit.
            garrisoned_in_id: The reference_id of another unit this unit is garrisoned in.
            animation_frame: The animation frame of the unit.
            status: Unknown - Always 2. 0-6 no difference (?) | 7-255 makes it disappear. (Except from the mini-map)
            reference_id: The reference ID of this unit. Normally added automatically. Used for garrisoning or reference
                in triggers
        Returns:
            The Unit created
        """
        if reference_id is None:
            reference_id = self.get_new_reference_id()

        unit = Unit(
            player=player,
            x=x,
            y=y,
            z=z,
            reference_id=reference_id,
            unit_const=unit_const,
            status=status,
            rotation=rotation,
            initial_animation_frame=animation_frame,
            garrisoned_in_id=garrisoned_in_id,
        )

        self.units[player.value].append(unit)
        return unit

    def get_player_units(self, player: PlayerId) -> List[Unit]:
        """
        Returns a list of UnitObjects for the given player.

        Raises:
            ValueError: If player is not between 0 (GAIA) and 8 (EIGHT)
        """
        if not 0 <= player.value <= 8:
            raise ValueError("Player must have a value between 0 and 8")
        return self.units[player.value]

    def get_all_units(self) -> List[Unit]:
        units = []
        for player_units in self.units:
            units += player_units
        return units

    def remove_eye_candy(self) -> None:
        eye_candy_ids = [
            1351, 1352, 1353, 1354, 1355, 1358, 1359, 1360, 1361, 1362, 1363,
            1364, 1365, 1366
        ]
        self.units[0] = [
            gaia_unit for gaia_unit in self.units[0]
            if gaia_unit.unit_const not in eye_candy_ids
        ]

    def get_units_in_area(self,
                          x1: float = None,
                          y1: float = None,
                          x2: float = None,
                          y2: float = None,
                          tile1: Tile = None,
                          tile2: Tile = None,
                          unit_list: List[Unit] = None,
                          players: List[PlayerId] = None,
                          ignore_players: List[PlayerId] = None):
        """
        Returns all units in the square with left corner (x1, y1) and right corner (x2, y2). Both corners inclusive.

        Args:
            x1: The X location of the left corner
            y1: The Y location of the left corner
            x2: The X location of the right corner
            y2: The Y location of the right corner
            tile1: The x,y location of the 1st corner as Tile Object
            tile2: The x,y location of the 2nd corner as Tile Object
            unit_list: (Optional) A list of units (Defaults to all units in the map, including GAIA (Trees etc.)
            players: (Optional) A list of Players which units need to be selected from the selected area
            ignore_players: (Optional) A list of Players which units need to be ignored from the selected area

        Raises:
            ValueError: if not all 4 (x1, y1, x2 and y2) are used simultaneously.
                Or if both (tile1 and tile2) are not used simultaneously.
                Or if any of the 4 (x1, y1, x2, y2) is used together with any of (tile1, tile2). Use one or the other.
                Or if players and ignore_players are used simultaneously.

        :Authors:
            KSneijders (https://github.com/KSneijders/)
            T-West (https://github.com/twestura/)
        """
        if (x1 is not None or y1 is not None or x2 is not None
                or y2 is not None) and any([tile1, tile2]):
            raise ValueError(
                "Cannot use both x1,y1,x2,y2 notation and tile1,tile2 notation at the same time"
            )
        if (x1 is not None or y1 is not None or x2 is not None or y2 is not None) and \
                (x1 is None or y1 is None or x2 is None or y2 is None):
            raise ValueError("Cannot use some but not all from x1,y1,x2,y2.")
        if (not all([tile1, tile2])) and any([tile1, tile2]):
            raise ValueError("Cannot use one from tile1, tile2. Use both.")
        if players is not None and ignore_players is not None:
            raise ValueError(
                "Cannot use both whitelist (players) and blacklist (ignore_players) at the same time"
            )

        if tile1:
            x1 = tile1.x1
            y1 = tile1.y1
            x2 = tile2.x2
            y2 = tile2.y2

        if players is not None:
            players = players
        elif ignore_players is not None:
            players = [p for p in PlayerId if p not in ignore_players]
        else:
            players = [p for p in PlayerId]

        if unit_list is None:
            unit_list = self.get_all_units()

        return [
            unit for unit in unit_list if x1 <= unit.x <= x2
            and y1 <= unit.y <= y2 and unit.player in players
        ]

    def change_ownership(self, unit: Unit, to_player: PlayerId) -> None:
        """
        Changes a unit's ownership to the given player.

        Args:
            unit: The unit object which ownership will be changed
            to_player: The player that'll get ownership over the unit (using PlayerId enum)
        """
        for i, player_unit in enumerate(self.units[unit.player.value]):
            if player_unit == unit:
                del self.units[unit.player.value][i]
                self.units[to_player.value].append(unit)
                unit._player = PlayerId(to_player)
                return

    def get_new_reference_id(self) -> int:
        highest_id = 0  # If no units, default to 0
        for player in range(0, 9):
            for unit in self.units[player]:
                if highest_id < unit.reference_id:
                    highest_id = unit.reference_id
        return highest_id + 1

    def remove_unit(self, reference_id: int = None, unit: Unit = None) -> None:
        """
        Removes a unit. Please note that `unit=...` is a lot faster than `reference_id=...` due to reference_id having
        to search through all units on the map. And unit has an ownership (player) attribute which is used for knowing
        which list to remove the unit from.

        Args:
            reference_id (int): The id of the unit. Note that this is NOT a unit constant (So NOT: UnitInfo.ARCHER)
            unit (Unit): The Unit object to be removed.
        """
        if reference_id is not None and unit is not None:
            raise ValueError(
                "Cannot use both unit_ref_id and unit arguments. Use one or the other."
            )
        if reference_id is None and unit is None:
            raise ValueError(
                "Both unit_ref_id and unit arguments were unused. Use one.")

        if reference_id is not None:
            for player in range(0, 9):
                for i, unit in enumerate(self.units[player]):
                    if unit.reference_id == reference_id:
                        del self.units[player][i]
                        return
        elif unit is not None:
            self.units[unit.player.value].remove(unit)
예제 #11
0
class MapManager(AoE2Object):
    """Manager of the everything map related."""

    _link_list = [
        RetrieverObjectLink("map_width", "Map", "map_width"),
        RetrieverObjectLink("map_height", "Map", "map_height"),
        RetrieverObjectLink("terrain",
                            "Map",
                            "terrain_data",
                            process_as_object=TerrainTile),
    ]

    def __init__(self, map_width: int, map_height: int,
                 terrain: List[TerrainTile]):
        self._map_width = map_width
        self._map_height = map_height
        self.terrain = terrain
        super().__init__()

    @property
    def map_width(self) -> int:
        return self._map_width

    @property
    def map_height(self) -> int:
        return self._map_height

    @property
    def map_size(self) -> int:
        if self._map_height == self._map_width:
            return self._map_height
        else:
            raise ValueError(
                "Map is not a square. Use the attributes 'map_width' and 'map_height' instead."
            )

    @map_size.setter
    def map_size(self, size: int):
        new_length = size * size
        difference = new_length - len(self.terrain)

        self._map_width = size
        self._map_height = size

        if difference < 0:
            self.terrain = self.terrain[:new_length]
        elif difference > 0:
            for _ in range(difference):
                self.terrain.append(
                    TerrainTile(TerrainId.GRASS_1, elevation=1, layer=-1))

    def create_hill(self, x1, y1, x2, y2, elevation) -> None:
        """
        Function that takes the coordinates and the height of a plateau and applies it to the map
        by also setting the surrounding slopes so that it is smooth.

        Args:
            x1 (int): The x coordinate of the west corner
            y1 (int): The y coordinate of the west corner
            x2 (int): The x coordinate of the east corner
            y2 (int): The y coordinate of the east corner
            elevation (int): The elevation of the map. Default in-game = 1, in-game max = 7. If the given value is over
                20 the game camera will 'clip' into the hill. So the in-game camera hovers around the height of 20/21
                when fully zoomed in, without Ultra Graphics.

        :Author:
            pvallet
        """
        for x in range(max(0, x1 - elevation),
                       min(self.map_size, x2 + elevation)):
            for y in range(max(0, y1 - elevation),
                           min(self.map_size, y2 + elevation)):
                if x1 <= x <= x2 and y1 <= y <= y2:
                    intended_elevation = elevation
                else:
                    distance_to_hill = max(x1 - x, x - x2, y1 - y, y - y2)
                    intended_elevation = elevation - distance_to_hill

                tile = self.terrain[helper.xy_to_i(x, y, self.map_size)]
                tile.elevation = max(intended_elevation, tile.elevation)