Beispiel #1
0
    def production_queue_ability(line: GenieGameEntityGroup) -> ForwardRef:
        """
        Adds the ProductionQueue ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        ability_ref = f"{game_entity_name}.ProductionQueue"
        ability_raw_api_object = RawAPIObject(ability_ref, "ProductionQueue",
                                              dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent(
            "engine.ability.type.ProductionQueue")
        ability_location = ForwardRef(line, game_entity_name)
        ability_raw_api_object.set_location(ability_location)

        # Size
        size = 22

        ability_raw_api_object.add_raw_member(
            "size", size, "engine.ability.type.ProductionQueue")

        # Production modes
        modes = []

        mode_name = f"{game_entity_name}.ProvideContingent.CreatablesMode"
        mode_raw_api_object = RawAPIObject(mode_name, "CreatablesMode",
                                           dataset.nyan_api_objects)
        mode_raw_api_object.add_raw_parent(
            "engine.util.production_mode.type.Creatables")
        mode_location = ForwardRef(line, ability_ref)
        mode_raw_api_object.set_location(mode_location)

        # RoR allows all creatables in production queue
        mode_raw_api_object.add_raw_member(
            "exclude", [], "engine.util.production_mode.type.Creatables")

        mode_forward_ref = ForwardRef(line, mode_name)
        modes.append(mode_forward_ref)

        ability_raw_api_object.add_raw_member(
            "production_modes", modes, "engine.ability.type.ProductionQueue")

        line.add_raw_api_object(mode_raw_api_object)
        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref
Beispiel #2
0
    def resistance_ability(line: GenieGameEntityGroup) -> ForwardRef:
        """
        Adds the Resistance ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]
        ability_ref = f"{game_entity_name}.Resistance"
        ability_raw_api_object = RawAPIObject(ability_ref, "Resistance",
                                              dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent("engine.ability.type.Resistance")
        ability_location = ForwardRef(line, game_entity_name)
        ability_raw_api_object.set_location(ability_location)

        # Resistances
        resistances = []
        resistances.extend(
            AoCEffectSubprocessor.get_attack_resistances(line, ability_ref))
        if isinstance(line, (GenieUnitLineGroup, GenieBuildingLineGroup)):
            # TODO: Conversion resistance
            # resistances.extend(RoREffectSubprocessor.get_convert_resistances(line,
            #                                                                  ability_ref))

            if isinstance(line,
                          GenieUnitLineGroup) and not line.is_repairable():
                resistances.extend(
                    AoCEffectSubprocessor.get_heal_resistances(
                        line, ability_ref))

            if isinstance(line, GenieBuildingLineGroup):
                resistances.extend(
                    AoCEffectSubprocessor.get_construct_resistances(
                        line, ability_ref))

            if line.is_repairable():
                resistances.extend(
                    AoCEffectSubprocessor.get_repair_resistances(
                        line, ability_ref))

        ability_raw_api_object.add_raw_member(
            "resistances", resistances, "engine.ability.type.Resistance")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref
Beispiel #3
0
    def create_animation(line: GenieGameEntityGroup, animation_id: int,
                         nyan_patch_ref: str, animation_name: str,
                         filename_prefix: str) -> ForwardRef:
        """
        Generates an animation for an ability.
        """
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        animation_ref = f"{nyan_patch_ref}.{animation_name}Animation"
        animation_obj_name = f"{animation_name}Animation"
        animation_raw_api_object = RawAPIObject(animation_ref,
                                                animation_obj_name,
                                                dataset.nyan_api_objects)
        animation_raw_api_object.add_raw_parent(
            "engine.util.graphics.Animation")
        animation_location = ForwardRef(line, nyan_patch_ref)
        animation_raw_api_object.set_location(animation_location)

        if animation_id in dataset.combined_sprites.keys():
            animation_sprite = dataset.combined_sprites[animation_id]

        else:
            animation_filename = "%s%s" % (
                filename_prefix, name_lookup_dict[line.get_head_unit_id()][1])

            animation_sprite = CombinedSprite(animation_id, animation_filename,
                                              dataset)
            dataset.combined_sprites.update(
                {animation_sprite.get_id(): animation_sprite})

        animation_sprite.add_reference(animation_raw_api_object)

        animation_raw_api_object.add_raw_member(
            "sprite", animation_sprite, "engine.util.graphics.Animation")

        line.add_raw_api_object(animation_raw_api_object)

        animation_forward_ref = ForwardRef(line, animation_ref)

        return animation_forward_ref
Beispiel #4
0
    def shoot_projectile_ability(
            converter_group: ConverterObjectGroup,
            line: GenieGameEntityGroup,
            container_obj_ref: str,
            command_id: int,
            diff: ConverterObject = None) -> list[ForwardRef]:
        """
        Creates a patch for the Selectable ability of a line.

        :param converter_group: Group that gets the patch.
        :type converter_group: ...dataformat.converter_object.ConverterObjectGroup
        :param line: Unit/Building line that has the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param container_obj_ref: Reference of the raw API object the patch is nested in.
        :type container_obj_ref: str
        :param diff: A diff between two ConvertObject instances.
        :type diff: ...dataformat.converter_object.ConverterObject
        :returns: The forward references for the generated patches.
        :rtype: list
        """
        head_unit_id = line.get_head_unit_id()
        tech_id = converter_group.get_id()
        dataset = line.data

        patches = []

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)
        tech_lookup_dict = internal_name_lookups.get_tech_lookups(
            dataset.game_version)
        command_lookup_dict = internal_name_lookups.get_command_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[head_unit_id][0]
        ability_name = command_lookup_dict[command_id][0]

        changed = False
        if diff:
            diff_animation = diff["attack_sprite_id"]
            diff_comm_sound = diff["command_sound_id"]
            diff_min_range = diff["weapon_range_min"]
            diff_max_range = diff["weapon_range_min"]
            diff_reload_time = diff["attack_speed"]
            # spawn delay also depends on animation
            diff_spawn_delay = diff["frame_delay"]
            diff_spawn_area_offsets = diff["weapon_offset"]

            if any(not isinstance(value, NoDiffMember)
                   for value in (diff_animation, diff_comm_sound,
                                 diff_min_range, diff_max_range,
                                 diff_reload_time, diff_spawn_delay,
                                 diff_spawn_area_offsets)):
                changed = True

        if changed:
            patch_target_ref = f"{game_entity_name}.{ability_name}"
            patch_target_forward_ref = ForwardRef(line, patch_target_ref)

            # Wrapper
            wrapper_name = f"Change{game_entity_name}{ability_name}Wrapper"
            wrapper_ref = f"{container_obj_ref}.{wrapper_name}"
            wrapper_raw_api_object = RawAPIObject(wrapper_ref, wrapper_name,
                                                  dataset.nyan_api_objects)
            wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

            if isinstance(line, GenieBuildingLineGroup):
                # Store building upgrades next to their game entity definition,
                # not in the Age up techs.
                wrapper_raw_api_object.set_location(
                    "data/game_entity/generic/%s/" %
                    (name_lookup_dict[head_unit_id][1]))
                wrapper_raw_api_object.set_filename(
                    f"{tech_lookup_dict[tech_id][1]}_upgrade")

            else:
                wrapper_raw_api_object.set_location(
                    ForwardRef(converter_group, container_obj_ref))

            # Nyan patch
            nyan_patch_name = f"Change{game_entity_name}{ability_name}"
            nyan_patch_ref = f"{container_obj_ref}.{wrapper_name}.{nyan_patch_name}"
            nyan_patch_location = ForwardRef(converter_group, wrapper_ref)
            nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                     nyan_patch_name,
                                                     dataset.nyan_api_objects,
                                                     nyan_patch_location)
            nyan_patch_raw_api_object.add_raw_parent(
                "engine.util.patch.NyanPatch")
            nyan_patch_raw_api_object.set_patch_target(
                patch_target_forward_ref)

            if not isinstance(diff_animation, NoDiffMember):
                animations_set = []
                diff_animation_id = diff_animation.get_value()
                if diff_animation_id > -1:
                    # Patch the new animation in
                    animation_forward_ref = AoCUpgradeAbilitySubprocessor.create_animation(
                        converter_group, line, diff_animation_id,
                        nyan_patch_ref, ability_name,
                        "%s_" % command_lookup_dict[command_id][1])
                    animations_set.append(animation_forward_ref)

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "animations", animations_set,
                    "engine.ability.property.type.Animated",
                    MemberOperator.ASSIGN)

            if not isinstance(diff_comm_sound, NoDiffMember):
                sounds_set = []
                diff_comm_sound_id = diff_comm_sound.get_value()
                if diff_comm_sound_id > -1:
                    # Patch the new sound in
                    sound_forward_ref = AoCUpgradeAbilitySubprocessor.create_sound(
                        converter_group, diff_comm_sound_id, nyan_patch_ref,
                        ability_name,
                        "%s_" % command_lookup_dict[command_id][1])
                    sounds_set.append(sound_forward_ref)

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "sounds", sounds_set,
                    "engine.ability.property.type.CommandSound",
                    MemberOperator.ASSIGN)

            if not isinstance(diff_min_range, NoDiffMember):
                min_range = diff_min_range.get_value()

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "min_range", min_range,
                    "engine.ability.type.ShootProjectile", MemberOperator.ADD)

            if not isinstance(diff_max_range, NoDiffMember):
                max_range = diff_max_range.get_value()

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "max_range", max_range,
                    "engine.ability.type.ShootProjectile", MemberOperator.ADD)

            if not isinstance(diff_reload_time, NoDiffMember):
                reload_time = diff_reload_time.get_value()

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "reload_time", reload_time,
                    "engine.ability.type.ShootProjectile", MemberOperator.ADD)

            if not isinstance(diff_spawn_delay, NoDiffMember):
                if not isinstance(diff_animation, NoDiffMember):
                    attack_graphic_id = diff_animation.get_value()

                else:
                    attack_graphic_id = diff_animation.value.get_value()

                attack_graphic = dataset.genie_graphics[attack_graphic_id]
                frame_rate = attack_graphic.get_frame_rate()
                frame_delay = diff_spawn_delay.get_value()
                spawn_delay = frame_rate * frame_delay

                nyan_patch_raw_api_object.add_raw_patch_member(
                    "spawn_delay", spawn_delay,
                    "engine.ability.type.ShootProjectile",
                    MemberOperator.ASSIGN)

            if not isinstance(diff_spawn_area_offsets, NoDiffMember):
                diff_spawn_area_x = diff_spawn_area_offsets[0]
                diff_spawn_area_y = diff_spawn_area_offsets[1]
                diff_spawn_area_z = diff_spawn_area_offsets[2]

                if not isinstance(diff_spawn_area_x, NoDiffMember):
                    spawn_area_x = diff_spawn_area_x.get_value()

                    nyan_patch_raw_api_object.add_raw_patch_member(
                        "spawning_area_offset_x", spawn_area_x,
                        "engine.ability.type.ShootProjectile",
                        MemberOperator.ADD)

                if not isinstance(diff_spawn_area_y, NoDiffMember):
                    spawn_area_y = diff_spawn_area_y.get_value()

                    nyan_patch_raw_api_object.add_raw_patch_member(
                        "spawning_area_offset_y", spawn_area_y,
                        "engine.ability.type.ShootProjectile",
                        MemberOperator.ADD)

                if not isinstance(diff_spawn_area_z, NoDiffMember):
                    spawn_area_z = diff_spawn_area_z.get_value()

                    nyan_patch_raw_api_object.add_raw_patch_member(
                        "spawning_area_offset_z", spawn_area_z,
                        "engine.ability.type.ShootProjectile",
                        MemberOperator.ADD)

            patch_forward_ref = ForwardRef(converter_group, nyan_patch_ref)
            wrapper_raw_api_object.add_raw_member("patch", patch_forward_ref,
                                                  "engine.util.patch.Patch")

            converter_group.add_raw_api_object(wrapper_raw_api_object)
            converter_group.add_raw_api_object(nyan_patch_raw_api_object)

            wrapper_forward_ref = ForwardRef(converter_group, wrapper_ref)
            patches.append(wrapper_forward_ref)

        return patches
Beispiel #5
0
    def resource_cost_upgrade(converter_group: ConverterObjectGroup,
                              line: GenieGameEntityGroup,
                              value: typing.Union[int, float],
                              operator: MemberOperator,
                              team: bool = False) -> list[ForwardRef]:
        """
        Creates a patch for the resource modify effect (ID: 100).

        :param converter_group: Tech/Civ that gets the patch.
        :type converter_group: ...dataformat.converter_object.ConverterObjectGroup
        :param line: Unit/Building line that has the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param value: Value used for patching the member.
        :type value: int, float
        :param operator: Operator used for patching the member.
        :type operator: MemberOperator
        :returns: The forward references for the generated patches.
        :rtype: list
        """
        head_unit = line.get_head_unit()
        head_unit_id = line.get_head_unit_id()
        dataset = line.data

        patches = []

        obj_id = converter_group.get_id()
        if isinstance(converter_group, GenieTechEffectBundleGroup):
            tech_lookup_dict = internal_name_lookups.get_tech_lookups(
                dataset.game_version)
            obj_name = tech_lookup_dict[obj_id][0]

        else:
            civ_lookup_dict = internal_name_lookups.get_civ_lookups(
                dataset.game_version)
            obj_name = civ_lookup_dict[obj_id][0]

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[head_unit_id][0]

        for resource_amount in head_unit["resource_cost"].get_value():
            resource_id = resource_amount["type_id"].get_value()

            resource_name = ""
            if resource_id == -1:
                # Not a valid resource
                continue

            if resource_id == 0:
                resource_name = "Food"

            elif resource_id == 1:
                resource_name = "Carbon"

            elif resource_id == 2:
                resource_name = "Ore"

            elif resource_id == 3:
                resource_name = "Nova"

            else:
                # Other resource ids are handled differently
                continue

            # Skip resources that are only expected to be there
            if not resource_amount["enabled"].get_value():
                continue

            patch_target_ref = "%s.CreatableGameEntity.%sCost.%sAmount" % (
                game_entity_name, game_entity_name, resource_name)
            patch_target_forward_ref = ForwardRef(line, patch_target_ref)

            # Wrapper
            wrapper_name = f"Change{game_entity_name}{resource_name}CostWrapper"
            wrapper_ref = f"{obj_name}.{wrapper_name}"
            wrapper_location = ForwardRef(converter_group, obj_name)
            wrapper_raw_api_object = RawAPIObject(wrapper_ref, wrapper_name,
                                                  dataset.nyan_api_objects,
                                                  wrapper_location)
            wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

            # Nyan patch
            nyan_patch_name = f"Change{game_entity_name}{resource_name}Cost"
            nyan_patch_ref = f"{obj_name}.{wrapper_name}.{nyan_patch_name}"
            nyan_patch_location = ForwardRef(converter_group, wrapper_ref)
            nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                     nyan_patch_name,
                                                     dataset.nyan_api_objects,
                                                     nyan_patch_location)
            nyan_patch_raw_api_object.add_raw_parent(
                "engine.util.patch.NyanPatch")
            nyan_patch_raw_api_object.set_patch_target(
                patch_target_forward_ref)

            nyan_patch_raw_api_object.add_raw_patch_member(
                "amount", value, "engine.util.resource.ResourceAmount",
                operator)

            patch_forward_ref = ForwardRef(converter_group, nyan_patch_ref)
            wrapper_raw_api_object.add_raw_member("patch", patch_forward_ref,
                                                  "engine.util.patch.Patch")

            if team:
                team_property = dataset.pregen_nyan_objects[
                    "util.patch.property.types.Team"].get_nyan_object()
                properties = {
                    dataset.nyan_api_objects["engine.util.patch.property.type.Diplomatic"]:
                    team_property
                }
                wrapper_raw_api_object.add_raw_member(
                    "properties", properties, "engine.util.patch.Patch")

            converter_group.add_raw_api_object(wrapper_raw_api_object)
            converter_group.add_raw_api_object(nyan_patch_raw_api_object)

            wrapper_forward_ref = ForwardRef(converter_group, wrapper_ref)
            patches.append(wrapper_forward_ref)

        return patches
Beispiel #6
0
    def get_creatable_game_entity(line: GenieGameEntityGroup) -> None:
        """
        Creates the CreatableGameEntity object for a unit/building line. In comparison
        to the AoC version, ths replaces some unit class IDs and removes garrison
        placement modes.

        :param line: Unit/Building line.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        """
        if isinstance(line, GenieVillagerGroup):
            current_unit = line.variants[0].line[0]

        else:
            current_unit = line.line[0]

        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        obj_ref = f"{game_entity_name}.CreatableGameEntity"
        obj_name = f"{game_entity_name}Creatable"
        creatable_raw_api_object = RawAPIObject(obj_ref, obj_name,
                                                dataset.nyan_api_objects)
        creatable_raw_api_object.add_raw_parent(
            "engine.util.create.CreatableGameEntity")

        # Get train location of line
        train_location_id = line.get_train_location_id()
        if isinstance(line, GenieBuildingLineGroup):
            train_location = dataset.unit_lines[train_location_id]
            train_location_name = name_lookup_dict[train_location_id][0]

        else:
            train_location = dataset.building_lines[train_location_id]
            train_location_name = name_lookup_dict[train_location_id][0]

        # Add object to the train location's Create ability
        creatable_location = ForwardRef(train_location,
                                        f"{train_location_name}.Create")

        creatable_raw_api_object.set_location(creatable_location)

        # Game Entity
        game_entity_forward_ref = ForwardRef(line, game_entity_name)
        creatable_raw_api_object.add_raw_member(
            "game_entity", game_entity_forward_ref,
            "engine.util.create.CreatableGameEntity")

        # TODO: Variants
        variants_set = []

        creatable_raw_api_object.add_raw_member(
            "variants", variants_set, "engine.util.create.CreatableGameEntity")

        # Cost (construction)
        cost_name = f"{game_entity_name}.CreatableGameEntity.{game_entity_name}Cost"
        cost_raw_api_object = RawAPIObject(cost_name,
                                           f"{game_entity_name}Cost",
                                           dataset.nyan_api_objects)
        cost_raw_api_object.add_raw_parent(
            "engine.util.cost.type.ResourceCost")
        creatable_forward_ref = ForwardRef(line, obj_ref)
        cost_raw_api_object.set_location(creatable_forward_ref)

        payment_mode = dataset.nyan_api_objects[
            "engine.util.payment_mode.type.Advance"]
        cost_raw_api_object.add_raw_member("payment_mode", payment_mode,
                                           "engine.util.cost.Cost")

        if isinstance(line, GenieBuildingLineGroup) or line.get_class_id() in (
                2, 13, 20, 21, 22):
            # Cost (repair) for buildings
            cost_repair_name = (f"{game_entity_name}.CreatableGameEntity."
                                f"{game_entity_name}RepairCost")
            cost_repair_raw_api_object = RawAPIObject(
                cost_repair_name, f"{game_entity_name}RepairCost",
                dataset.nyan_api_objects)
            cost_repair_raw_api_object.add_raw_parent(
                "engine.util.cost.type.ResourceCost")
            creatable_forward_ref = ForwardRef(line, obj_ref)
            cost_repair_raw_api_object.set_location(creatable_forward_ref)

            payment_repair_mode = dataset.nyan_api_objects[
                "engine.util.payment_mode.type.Adaptive"]
            cost_repair_raw_api_object.add_raw_member("payment_mode",
                                                      payment_repair_mode,
                                                      "engine.util.cost.Cost")
            line.add_raw_api_object(cost_repair_raw_api_object)

        cost_amounts = []
        cost_repair_amounts = []
        for resource_amount in current_unit["resource_cost"].value:
            resource_id = resource_amount["type_id"].value

            resource = None
            resource_name = ""
            if resource_id == -1:
                # Not a valid resource
                continue

            if resource_id == 0:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Food"].get_nyan_object()
                resource_name = "Food"

            elif resource_id == 1:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Wood"].get_nyan_object()
                resource_name = "Wood"

            elif resource_id == 2:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Stone"].get_nyan_object()
                resource_name = "Stone"

            elif resource_id == 3:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Gold"].get_nyan_object()
                resource_name = "Gold"

            else:
                # Other resource ids are handled differently
                continue

            # Skip resources that are only expected to be there
            if not resource_amount["enabled"].value:
                continue

            amount = resource_amount["amount"].value

            cost_amount_name = f"{cost_name}.{resource_name}Amount"
            cost_amount = RawAPIObject(cost_amount_name,
                                       f"{resource_name}Amount",
                                       dataset.nyan_api_objects)
            cost_amount.add_raw_parent("engine.util.resource.ResourceAmount")
            cost_forward_ref = ForwardRef(line, cost_name)
            cost_amount.set_location(cost_forward_ref)

            cost_amount.add_raw_member("type", resource,
                                       "engine.util.resource.ResourceAmount")
            cost_amount.add_raw_member("amount", amount,
                                       "engine.util.resource.ResourceAmount")

            cost_amount_forward_ref = ForwardRef(line, cost_amount_name)
            cost_amounts.append(cost_amount_forward_ref)
            line.add_raw_api_object(cost_amount)

            if isinstance(line, GenieBuildingLineGroup) or\
                    line.get_class_id() in (2, 13, 20, 21, 22):
                # Cost for repairing = half of the construction cost
                cost_amount_name = f"{cost_repair_name}.{resource_name}Amount"
                cost_amount = RawAPIObject(cost_amount_name,
                                           f"{resource_name}Amount",
                                           dataset.nyan_api_objects)
                cost_amount.add_raw_parent(
                    "engine.util.resource.ResourceAmount")
                cost_forward_ref = ForwardRef(line, cost_repair_name)
                cost_amount.set_location(cost_forward_ref)

                cost_amount.add_raw_member(
                    "type", resource, "engine.util.resource.ResourceAmount")
                cost_amount.add_raw_member(
                    "amount", amount / 2,
                    "engine.util.resource.ResourceAmount")

                cost_amount_forward_ref = ForwardRef(line, cost_amount_name)
                cost_repair_amounts.append(cost_amount_forward_ref)
                line.add_raw_api_object(cost_amount)

        cost_raw_api_object.add_raw_member(
            "amount", cost_amounts, "engine.util.cost.type.ResourceCost")

        if isinstance(line, GenieBuildingLineGroup) or line.get_class_id() in (
                2, 13, 20, 21, 22):
            cost_repair_raw_api_object.add_raw_member(
                "amount", cost_repair_amounts,
                "engine.util.cost.type.ResourceCost")

        cost_forward_ref = ForwardRef(line, cost_name)
        creatable_raw_api_object.add_raw_member(
            "cost", cost_forward_ref, "engine.util.create.CreatableGameEntity")
        # Creation time
        if isinstance(line, GenieUnitLineGroup):
            creation_time = current_unit["creation_time"].value

        else:
            # Buildings are created immediately
            creation_time = 0

        creatable_raw_api_object.add_raw_member(
            "creation_time", creation_time,
            "engine.util.create.CreatableGameEntity")

        # Creation sound
        creation_sound_id = current_unit["train_sound_id"].value

        # Create sound object
        obj_name = f"{game_entity_name}.CreatableGameEntity.Sound"
        sound_raw_api_object = RawAPIObject(obj_name, "CreationSound",
                                            dataset.nyan_api_objects)
        sound_raw_api_object.add_raw_parent("engine.util.sound.Sound")
        sound_location = ForwardRef(line, obj_ref)
        sound_raw_api_object.set_location(sound_location)

        # Search for the sound if it exists
        creation_sounds = []
        if creation_sound_id > -1:
            # Creation sound should be civ agnostic
            genie_sound = dataset.genie_sounds[creation_sound_id]
            file_id = genie_sound.get_sounds(civ_id=-1)[0]

            if file_id in dataset.combined_sounds:
                creation_sound = dataset.combined_sounds[file_id]
                creation_sound.add_reference(sound_raw_api_object)

            else:
                creation_sound = CombinedSound(
                    creation_sound_id, file_id,
                    f"creation_sound_{creation_sound_id}", dataset)
                dataset.combined_sounds.update({file_id: creation_sound})
                creation_sound.add_reference(sound_raw_api_object)

            creation_sounds.append(creation_sound)

        sound_raw_api_object.add_raw_member("play_delay", 0,
                                            "engine.util.sound.Sound")
        sound_raw_api_object.add_raw_member("sounds", creation_sounds,
                                            "engine.util.sound.Sound")

        sound_forward_ref = ForwardRef(line, obj_name)
        creatable_raw_api_object.add_raw_member(
            "creation_sounds", [sound_forward_ref],
            "engine.util.create.CreatableGameEntity")

        line.add_raw_api_object(sound_raw_api_object)

        # Condition
        unlock_conditions = []
        enabling_research_id = line.get_enabling_research_id()
        if enabling_research_id > -1:
            unlock_conditions.extend(
                AoCAuxiliarySubprocessor.get_condition(line, obj_ref,
                                                       enabling_research_id))

        creatable_raw_api_object.add_raw_member(
            "condition", unlock_conditions,
            "engine.util.create.CreatableGameEntity")

        # Placement modes
        placement_modes = []
        if isinstance(line, GenieBuildingLineGroup):
            # Buildings are placed on the map
            # Place mode
            obj_name = f"{game_entity_name}.CreatableGameEntity.Place"
            place_raw_api_object = RawAPIObject(obj_name, "Place",
                                                dataset.nyan_api_objects)
            place_raw_api_object.add_raw_parent(
                "engine.util.placement_mode.type.Place")
            place_location = ForwardRef(
                line, f"{game_entity_name}.CreatableGameEntity")
            place_raw_api_object.set_location(place_location)

            # Tile snap distance (uses 1.0 for grid placement)
            place_raw_api_object.add_raw_member(
                "tile_snap_distance", 1.0,
                "engine.util.placement_mode.type.Place")
            # Clearance size
            clearance_size_x = current_unit["clearance_size_x"].value
            clearance_size_y = current_unit["clearance_size_y"].value
            place_raw_api_object.add_raw_member(
                "clearance_size_x", clearance_size_x,
                "engine.util.placement_mode.type.Place")
            place_raw_api_object.add_raw_member(
                "clearance_size_y", clearance_size_y,
                "engine.util.placement_mode.type.Place")

            # Allow rotation
            place_raw_api_object.add_raw_member(
                "allow_rotation", True,
                "engine.util.placement_mode.type.Place")

            # Max elevation difference
            elevation_mode = current_unit["elevation_mode"].value
            if elevation_mode == 2:
                max_elevation_difference = 0

            elif elevation_mode == 3:
                max_elevation_difference = 1

            else:
                max_elevation_difference = MemberSpecialValue.NYAN_INF

            place_raw_api_object.add_raw_member(
                "max_elevation_difference", max_elevation_difference,
                "engine.util.placement_mode.type.Place")

            line.add_raw_api_object(place_raw_api_object)

            place_forward_ref = ForwardRef(line, obj_name)
            placement_modes.append(place_forward_ref)

        else:
            placement_modes.append(
                dataset.
                nyan_api_objects["engine.util.placement_mode.type.Eject"])

        creatable_raw_api_object.add_raw_member(
            "placement_modes", placement_modes,
            "engine.util.create.CreatableGameEntity")

        line.add_raw_api_object(creatable_raw_api_object)
        line.add_raw_api_object(cost_raw_api_object)
Beispiel #7
0
 def add_unique_entity(self, entity_group: GenieGameEntityGroup):
     """
     Adds a unique unit to the civilization.
     """
     self.unique_entities.update(
         {entity_group.get_head_unit_id(): entity_group})
Beispiel #8
0
    def ballistics_upgrade(converter_group: ConverterObjectGroup,
                           line: GenieGameEntityGroup,
                           value: typing.Any,
                           operator: MemberOperator,
                           team: bool = False) -> list[ForwardRef]:
        """
        Creates a patch for the ballistics modify effect (ID: 19).

        :param converter_group: Tech/Civ that gets the patch.
        :type converter_group: ...dataformat.converter_object.ConverterObjectGroup
        :param line: Unit/Building line that has the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param value: Value used for patching the member.
        :type value: Any
        :param operator: Operator used for patching the member.
        :type operator: MemberOperator
        :returns: The forward references for the generated patches.
        :rtype: list
        """
        head_unit = line.get_head_unit()
        head_unit_id = line.get_head_unit_id()
        dataset = line.data

        patches = []

        if value == 0:
            target_mode = dataset.nyan_api_objects[
                "engine.util.target_mode.type.CurrentPosition"]

        elif value == 1:
            target_mode = dataset.nyan_api_objects[
                "engine.util.target_mode.type.ExpectedPosition"]

        obj_id = converter_group.get_id()
        if isinstance(converter_group, GenieTechEffectBundleGroup):
            tech_lookup_dict = internal_name_lookups.get_tech_lookups(
                dataset.game_version)
            obj_name = tech_lookup_dict[obj_id][0]

        else:
            civ_lookup_dict = internal_name_lookups.get_civ_lookups(
                dataset.game_version)
            obj_name = civ_lookup_dict[obj_id][0]

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[head_unit_id][0]

        projectile_id0 = head_unit["projectile_id0"].value
        if projectile_id0 > -1:
            patch_target_ref = f"{game_entity_name}.ShootProjectile.Projectile0.Projectile"
            patch_target_forward_ref = ForwardRef(line, patch_target_ref)

            # Wrapper
            wrapper_name = f"Change{game_entity_name}Projectile0TargetModeWrapper"
            wrapper_ref = f"{obj_name}.{wrapper_name}"
            wrapper_location = ForwardRef(converter_group, obj_name)
            wrapper_raw_api_object = RawAPIObject(wrapper_ref, wrapper_name,
                                                  dataset.nyan_api_objects,
                                                  wrapper_location)
            wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

            # Nyan patch
            nyan_patch_name = f"Change{game_entity_name}Projectile0TargetMode"
            nyan_patch_ref = f"{obj_name}.{wrapper_name}.{nyan_patch_name}"
            nyan_patch_location = ForwardRef(converter_group, wrapper_ref)
            nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                     nyan_patch_name,
                                                     dataset.nyan_api_objects,
                                                     nyan_patch_location)
            nyan_patch_raw_api_object.add_raw_parent(
                "engine.util.patch.NyanPatch")
            nyan_patch_raw_api_object.set_patch_target(
                patch_target_forward_ref)

            nyan_patch_raw_api_object.add_raw_patch_member(
                "target_mode", target_mode, "engine.ability.type.Projectile",
                operator)

            patch_forward_ref = ForwardRef(converter_group, nyan_patch_ref)
            wrapper_raw_api_object.add_raw_member("patch", patch_forward_ref,
                                                  "engine.util.patch.Patch")

            if team:
                team_property = dataset.pregen_nyan_objects[
                    "util.patch.property.types.Team"].get_nyan_object()
                properties = {
                    dataset.nyan_api_objects["engine.util.patch.property.type.Diplomatic"]:
                    team_property
                }
                wrapper_raw_api_object.add_raw_member(
                    "properties", properties, "engine.util.patch.Patch")

            converter_group.add_raw_api_object(wrapper_raw_api_object)
            converter_group.add_raw_api_object(nyan_patch_raw_api_object)

            wrapper_forward_ref = ForwardRef(converter_group, wrapper_ref)
            patches.append(wrapper_forward_ref)

        return patches
Beispiel #9
0
    def shoot_projectile_ability(line: GenieGameEntityGroup, command_id: int) -> ForwardRef:
        """
        Adds the ShootProjectile ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        current_unit = line.get_head_unit()
        current_unit_id = line.get_head_unit_id()
        dataset = line.data
        api_objects = dataset.nyan_api_objects

        name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version)
        command_lookup_dict = internal_name_lookups.get_command_lookups(dataset.game_version)

        ability_name = command_lookup_dict[command_id][0]

        game_entity_name = name_lookup_dict[current_unit_id][0]
        ability_ref = f"{game_entity_name}.{ability_name}"
        ability_raw_api_object = RawAPIObject(ability_ref, ability_name, dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent("engine.ability.type.ShootProjectile")
        ability_location = ForwardRef(line, game_entity_name)
        ability_raw_api_object.set_location(ability_location)

        # Ability properties
        properties = {}

        # Animation
        ability_animation_id = current_unit["attack_sprite_id"].value
        if ability_animation_id > -1:
            property_ref = f"{ability_ref}.Animated"
            property_raw_api_object = RawAPIObject(property_ref,
                                                   "Animated",
                                                   dataset.nyan_api_objects)
            property_raw_api_object.add_raw_parent("engine.ability.property.type.Animated")
            property_location = ForwardRef(line, ability_ref)
            property_raw_api_object.set_location(property_location)

            line.add_raw_api_object(property_raw_api_object)

            animations_set = []
            animation_forward_ref = AoCAbilitySubprocessor.create_animation(
                line,
                ability_animation_id,
                property_ref,
                ability_name,
                f"{command_lookup_dict[command_id][1]}_"
            )
            animations_set.append(animation_forward_ref)
            property_raw_api_object.add_raw_member("animations",
                                                   animations_set,
                                                   "engine.ability.property.type.Animated")

            property_forward_ref = ForwardRef(line, property_ref)
            properties.update({
                api_objects["engine.ability.property.type.Animated"]: property_forward_ref
            })

        # Command Sound
        ability_comm_sound_id = current_unit["command_sound_id"].value
        if ability_comm_sound_id > -1:
            property_ref = f"{ability_ref}.CommandSound"
            property_raw_api_object = RawAPIObject(property_ref,
                                                   "CommandSound",
                                                   dataset.nyan_api_objects)
            property_raw_api_object.add_raw_parent("engine.ability.property.type.CommandSound")
            property_location = ForwardRef(line, ability_ref)
            property_raw_api_object.set_location(property_location)

            line.add_raw_api_object(property_raw_api_object)

            sounds_set = []
            sound_forward_ref = AoCAbilitySubprocessor.create_sound(line,
                                                                    ability_comm_sound_id,
                                                                    property_ref,
                                                                    ability_name,
                                                                    "command_")
            sounds_set.append(sound_forward_ref)
            property_raw_api_object.add_raw_member("sounds",
                                                   sounds_set,
                                                   "engine.ability.property.type.CommandSound")

            property_forward_ref = ForwardRef(line, property_ref)
            properties.update({
                api_objects["engine.ability.property.type.CommandSound"]: property_forward_ref
            })

        # Diplomacy settings
        property_ref = f"{ability_ref}.Diplomatic"
        property_raw_api_object = RawAPIObject(property_ref,
                                               "Diplomatic",
                                               dataset.nyan_api_objects)
        property_raw_api_object.add_raw_parent("engine.ability.property.type.Diplomatic")
        property_location = ForwardRef(line, ability_ref)
        property_raw_api_object.set_location(property_location)

        line.add_raw_api_object(property_raw_api_object)

        diplomatic_stances = [dataset.nyan_api_objects["engine.util.diplomatic_stance.type.Self"]]
        property_raw_api_object.add_raw_member("stances", diplomatic_stances,
                                               "engine.ability.property.type.Diplomatic")

        property_forward_ref = ForwardRef(line, property_ref)
        properties.update({
            api_objects["engine.ability.property.type.Diplomatic"]: property_forward_ref
        })

        ability_raw_api_object.add_raw_member("properties",
                                              properties,
                                              "engine.ability.Ability")

        # Projectile
        projectiles = []
        projectile_primary = current_unit["projectile_id0"].value
        if projectile_primary > -1:
            projectiles.append(ForwardRef(line,
                                          f"{game_entity_name}.ShootProjectile.Projectile0"))

        ability_raw_api_object.add_raw_member("projectiles",
                                              projectiles,
                                              "engine.ability.type.ShootProjectile")

        # Projectile count (does not exist in RoR)
        min_projectiles = 1
        max_projectiles = 1

        ability_raw_api_object.add_raw_member("min_projectiles",
                                              min_projectiles,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("max_projectiles",
                                              max_projectiles,
                                              "engine.ability.type.ShootProjectile")

        # Range
        min_range = current_unit["weapon_range_min"].value
        ability_raw_api_object.add_raw_member("min_range",
                                              min_range,
                                              "engine.ability.type.ShootProjectile")

        max_range = current_unit["weapon_range_max"].value
        ability_raw_api_object.add_raw_member("max_range",
                                              max_range,
                                              "engine.ability.type.ShootProjectile")

        # Reload time and delay
        reload_time = current_unit["attack_speed"].value
        ability_raw_api_object.add_raw_member("reload_time",
                                              reload_time,
                                              "engine.ability.type.ShootProjectile")

        if ability_animation_id > -1:
            animation = dataset.genie_graphics[ability_animation_id]
            frame_rate = animation.get_frame_rate()

        else:
            frame_rate = 0

        spawn_delay_frames = current_unit["frame_delay"].value
        spawn_delay = frame_rate * spawn_delay_frames
        ability_raw_api_object.add_raw_member("spawn_delay",
                                              spawn_delay,
                                              "engine.ability.type.ShootProjectile")

        # Projectile delay (unused because RoR has no multiple projectiles)
        ability_raw_api_object.add_raw_member("projectile_delay",
                                              0.0,
                                              "engine.ability.type.ShootProjectile")

        # Turning
        if isinstance(line, GenieBuildingLineGroup):
            require_turning = False

        else:
            require_turning = True

        ability_raw_api_object.add_raw_member("require_turning",
                                              require_turning,
                                              "engine.ability.type.ShootProjectile")

        # Manual aiming
        manual_aiming_allowed = line.get_head_unit_id() in (35, 250)
        ability_raw_api_object.add_raw_member("manual_aiming_allowed",
                                              manual_aiming_allowed,
                                              "engine.ability.type.ShootProjectile")

        # Spawning area
        spawning_area_offset_x = current_unit["weapon_offset"][0].value
        spawning_area_offset_y = current_unit["weapon_offset"][1].value
        spawning_area_offset_z = current_unit["weapon_offset"][2].value

        ability_raw_api_object.add_raw_member("spawning_area_offset_x",
                                              spawning_area_offset_x,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("spawning_area_offset_y",
                                              spawning_area_offset_y,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("spawning_area_offset_z",
                                              spawning_area_offset_z,
                                              "engine.ability.type.ShootProjectile")

        # Spawn Area (does not exist in RoR)
        spawning_area_width = 0
        spawning_area_height = 0
        spawning_area_randomness = 0

        ability_raw_api_object.add_raw_member("spawning_area_width",
                                              spawning_area_width,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("spawning_area_height",
                                              spawning_area_height,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("spawning_area_randomness",
                                              spawning_area_randomness,
                                              "engine.ability.type.ShootProjectile")

        # Restrictions on targets (only units and buildings allowed)
        allowed_types = [
            dataset.pregen_nyan_objects["util.game_entity_type.types.Building"].get_nyan_object(),
            dataset.pregen_nyan_objects["util.game_entity_type.types.Unit"].get_nyan_object()
        ]
        ability_raw_api_object.add_raw_member("allowed_types",
                                              allowed_types,
                                              "engine.ability.type.ShootProjectile")
        ability_raw_api_object.add_raw_member("blacklisted_entities",
                                              [],
                                              "engine.ability.type.ShootProjectile")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref
Beispiel #10
0
    def game_entity_stance_ability(line: GenieGameEntityGroup) -> ForwardRef:
        """
        Adds the GameEntityStance ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        current_unit = line.get_head_unit()
        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        ability_ref = f"{game_entity_name}.GameEntityStance"
        ability_raw_api_object = RawAPIObject(ability_ref,
                                              "GameEntityStance",
                                              dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent("engine.ability.type.GameEntityStance")
        ability_location = ForwardRef(line, game_entity_name)
        ability_raw_api_object.set_location(ability_location)

        # Stances
        search_range = current_unit["search_radius"].value
        stance_names = ["Aggressive", "StandGround"]

        # Attacking is preferred
        ability_preferences = []
        if line.is_projectile_shooter():
            ability_preferences.append(ForwardRef(line, f"{game_entity_name}.Attack"))

        elif line.is_melee() or line.is_ranged():
            if line.has_command(7):
                ability_preferences.append(ForwardRef(line, f"{game_entity_name}.Attack"))

            if line.has_command(105):
                ability_preferences.append(ForwardRef(line, f"{game_entity_name}.Heal"))

        # Units are preferred before buildings
        type_preferences = [
            dataset.pregen_nyan_objects["util.game_entity_type.types.Unit"].get_nyan_object(),
            dataset.pregen_nyan_objects["util.game_entity_type.types.Building"].get_nyan_object(),
        ]

        stances = []
        for stance_name in stance_names:
            stance_api_ref = f"engine.util.game_entity_stance.type.{stance_name}"

            stance_ref = f"{game_entity_name}.GameEntityStance.{stance_name}"
            stance_raw_api_object = RawAPIObject(stance_ref, stance_name, dataset.nyan_api_objects)
            stance_raw_api_object.add_raw_parent(stance_api_ref)
            stance_location = ForwardRef(line, ability_ref)
            stance_raw_api_object.set_location(stance_location)

            # Search range
            stance_raw_api_object.add_raw_member("search_range",
                                                 search_range,
                                                 "engine.util.game_entity_stance.GameEntityStance")

            # Ability preferences
            stance_raw_api_object.add_raw_member("ability_preference",
                                                 ability_preferences,
                                                 "engine.util.game_entity_stance.GameEntityStance")

            # Type preferences
            stance_raw_api_object.add_raw_member("type_preference",
                                                 type_preferences,
                                                 "engine.util.game_entity_stance.GameEntityStance")

            line.add_raw_api_object(stance_raw_api_object)
            stance_forward_ref = ForwardRef(line, stance_ref)
            stances.append(stance_forward_ref)

        ability_raw_api_object.add_raw_member("stances",
                                              stances,
                                              "engine.ability.type.GameEntityStance")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref
Beispiel #11
0
    def apply_discrete_effect_ability(
        line: GenieGameEntityGroup,
        command_id: int,
        ranged: bool = False,
        projectile: int = -1
    ) -> ForwardRef:
        """
        Adds the ApplyDiscreteEffect ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        if isinstance(line, GenieVillagerGroup):
            current_unit = line.get_units_with_command(command_id)[0]
            current_unit_id = current_unit["id0"].value

        else:
            current_unit = line.get_head_unit()
            current_unit_id = line.get_head_unit_id()

        head_unit_id = line.get_head_unit_id()
        dataset = line.data
        api_objects = dataset.nyan_api_objects

        name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version)
        command_lookup_dict = internal_name_lookups.get_command_lookups(dataset.game_version)
        gset_lookup_dict = internal_name_lookups.get_graphic_set_lookups(dataset.game_version)

        game_entity_name = name_lookup_dict[head_unit_id][0]

        ability_name = command_lookup_dict[command_id][0]

        if ranged:
            ability_parent = "engine.ability.type.RangedDiscreteEffect"

        else:
            ability_parent = "engine.ability.type.ApplyDiscreteEffect"

        if projectile == -1:
            ability_ref = f"{game_entity_name}.{ability_name}"
            ability_raw_api_object = RawAPIObject(ability_ref,
                                                  ability_name,
                                                  dataset.nyan_api_objects)
            ability_raw_api_object.add_raw_parent(ability_parent)
            ability_location = ForwardRef(line, game_entity_name)
            ability_raw_api_object.set_location(ability_location)

            if command_id == 104:
                # Get animation from commands proceed sprite
                unit_commands = current_unit["unit_commands"].value
                for command in unit_commands:
                    type_id = command["type"].value

                    if type_id != command_id:
                        continue

                    ability_animation_id = command["proceed_sprite_id"].value
                    break

                else:
                    ability_animation_id = -1

            else:
                ability_animation_id = current_unit["attack_sprite_id"].value

        else:
            ability_ref = (f"{game_entity_name}.ShootProjectile."
                           f"Projectile{projectile}.{ability_name}")
            ability_raw_api_object = RawAPIObject(ability_ref,
                                                  ability_name,
                                                  dataset.nyan_api_objects)
            ability_raw_api_object.add_raw_parent(ability_parent)
            ability_location = ForwardRef(line,
                                          (f"{game_entity_name}.ShootProjectile."
                                           f"Projectile{projectile}"))
            ability_raw_api_object.set_location(ability_location)

            ability_animation_id = -1

        # Ability properties
        properties = {}

        # Animated
        if ability_animation_id > -1:
            property_ref = f"{ability_ref}.Animated"
            property_raw_api_object = RawAPIObject(property_ref,
                                                   "Animated",
                                                   dataset.nyan_api_objects)
            property_raw_api_object.add_raw_parent("engine.ability.property.type.Animated")
            property_location = ForwardRef(line, ability_ref)
            property_raw_api_object.set_location(property_location)

            line.add_raw_api_object(property_raw_api_object)

            animations_set = []
            animation_forward_ref = AoCAbilitySubprocessor.create_animation(
                line,
                ability_animation_id,
                property_ref,
                ability_name,
                f"{command_lookup_dict[command_id][1]}_"
            )
            animations_set.append(animation_forward_ref)
            property_raw_api_object.add_raw_member("animations", animations_set,
                                                   "engine.ability.property.type.Animated")

            property_forward_ref = ForwardRef(line, property_ref)
            properties.update({
                api_objects["engine.ability.property.type.Animated"]: property_forward_ref
            })

            # Create custom civ graphics
            handled_graphics_set_ids = set()
            for civ_group in dataset.civ_groups.values():
                civ = civ_group.civ
                civ_id = civ_group.get_id()

                # Only proceed if the civ stores the unit in the line
                if current_unit_id not in civ["units"].value.keys():
                    continue

                civ_animation_id = civ["units"][current_unit_id]["attack_sprite_id"].value

                if civ_animation_id != ability_animation_id:
                    # Find the corresponding graphics set
                    graphics_set_id = -1
                    for set_id, items in gset_lookup_dict.items():
                        if civ_id in items[0]:
                            graphics_set_id = set_id
                            break

                    # Check if the object for the animation has been created before
                    obj_exists = graphics_set_id in handled_graphics_set_ids
                    if not obj_exists:
                        handled_graphics_set_ids.add(graphics_set_id)

                    obj_prefix = f"{gset_lookup_dict[graphics_set_id][1]}{ability_name}"
                    filename_prefix = (f"{command_lookup_dict[command_id][1]}_"
                                       f"{gset_lookup_dict[graphics_set_id][2]}_")
                    AoCAbilitySubprocessor.create_civ_animation(line,
                                                                civ_group,
                                                                civ_animation_id,
                                                                ability_ref,
                                                                obj_prefix,
                                                                filename_prefix,
                                                                obj_exists)

        # Command Sound
        if projectile == -1:
            ability_comm_sound_id = current_unit["command_sound_id"].value

        else:
            ability_comm_sound_id = -1

        if ability_comm_sound_id > -1:
            property_ref = f"{ability_ref}.CommandSound"
            property_raw_api_object = RawAPIObject(property_ref,
                                                   "CommandSound",
                                                   dataset.nyan_api_objects)
            property_raw_api_object.add_raw_parent("engine.ability.property.type.CommandSound")
            property_location = ForwardRef(line, ability_ref)
            property_raw_api_object.set_location(property_location)

            line.add_raw_api_object(property_raw_api_object)

            sounds_set = []

            if projectile == -1:
                sound_obj_prefix = ability_name

            else:
                sound_obj_prefix = "ProjectileAttack"

            sound_forward_ref = AoCAbilitySubprocessor.create_sound(line,
                                                                    ability_comm_sound_id,
                                                                    property_ref,
                                                                    sound_obj_prefix,
                                                                    "command_")
            sounds_set.append(sound_forward_ref)
            property_raw_api_object.add_raw_member("sounds", sounds_set,
                                                   "engine.ability.property.type.CommandSound")
            property_forward_ref = ForwardRef(line, property_ref)
            properties.update({
                api_objects["engine.ability.property.type.CommandSound"]: property_forward_ref
            })

        # Diplomacy settings
        property_ref = f"{ability_ref}.Diplomatic"
        property_raw_api_object = RawAPIObject(property_ref,
                                               "Diplomatic",
                                               dataset.nyan_api_objects)
        property_raw_api_object.add_raw_parent("engine.ability.property.type.Diplomatic")
        property_location = ForwardRef(line, ability_ref)
        property_raw_api_object.set_location(property_location)

        line.add_raw_api_object(property_raw_api_object)

        diplomatic_stances = [dataset.nyan_api_objects["engine.util.diplomatic_stance.type.Self"]]
        property_raw_api_object.add_raw_member("stances", diplomatic_stances,
                                               "engine.ability.property.type.Diplomatic")

        property_forward_ref = ForwardRef(line, property_ref)
        properties.update({
            api_objects["engine.ability.property.type.Diplomatic"]: property_forward_ref
        })

        ability_raw_api_object.add_raw_member("properties",
                                              properties,
                                              "engine.ability.Ability")

        if ranged:
            # Min range
            min_range = current_unit["weapon_range_min"].value
            ability_raw_api_object.add_raw_member("min_range",
                                                  min_range,
                                                  "engine.ability.type.RangedDiscreteEffect")

            # Max range
            max_range = current_unit["weapon_range_max"].value
            ability_raw_api_object.add_raw_member("max_range",
                                                  max_range,
                                                  "engine.ability.type.RangedDiscreteEffect")

        # Effects
        batch_ref = f"{ability_ref}.Batch"
        batch_raw_api_object = RawAPIObject(batch_ref, "Batch", dataset.nyan_api_objects)
        batch_raw_api_object.add_raw_parent("engine.util.effect_batch.type.UnorderedBatch")
        batch_location = ForwardRef(line, ability_ref)
        batch_raw_api_object.set_location(batch_location)

        line.add_raw_api_object(batch_raw_api_object)

        # Effects
        effects = []
        if command_id == 7:
            # Attack
            if projectile != 1:
                effects = AoCEffectSubprocessor.get_attack_effects(line, batch_ref)

            else:
                effects = AoCEffectSubprocessor.get_attack_effects(line, batch_ref, projectile=1)

        elif command_id == 104:
            # TODO: Convert
            # effects = AoCEffectSubprocessor.get_convert_effects(line, ability_ref)
            pass

        batch_raw_api_object.add_raw_member("effects",
                                            effects,
                                            "engine.util.effect_batch.EffectBatch")

        batch_forward_ref = ForwardRef(line, batch_ref)
        ability_raw_api_object.add_raw_member("batches",
                                              [batch_forward_ref],
                                              "engine.ability.type.ApplyDiscreteEffect")

        # Reload time
        if projectile == -1:
            reload_time = current_unit["attack_speed"].value

        else:
            reload_time = 0

        ability_raw_api_object.add_raw_member("reload_time",
                                              reload_time,
                                              "engine.ability.type.ApplyDiscreteEffect")

        # Application delay
        if projectile == -1:
            apply_graphic = dataset.genie_graphics[ability_animation_id]
            frame_rate = apply_graphic.get_frame_rate()
            frame_delay = current_unit["frame_delay"].value
            application_delay = frame_rate * frame_delay

        else:
            application_delay = 0

        ability_raw_api_object.add_raw_member("application_delay",
                                              application_delay,
                                              "engine.ability.type.ApplyDiscreteEffect")

        # Allowed types (all buildings/units)
        if command_id == 104:
            # Convert
            allowed_types = [
                dataset.pregen_nyan_objects["util.game_entity_type.types.Unit"].get_nyan_object()
            ]

        else:
            allowed_types = [
                dataset.pregen_nyan_objects["util.game_entity_type.types.Unit"].get_nyan_object(),
                dataset.pregen_nyan_objects["util.game_entity_type.types.Building"].get_nyan_object(
                )
            ]

        ability_raw_api_object.add_raw_member("allowed_types",
                                              allowed_types,
                                              "engine.ability.type.ApplyDiscreteEffect")

        if command_id == 104:
            # Convert
            blacklisted_entities = []
            for unit_line in dataset.unit_lines.values():
                if unit_line.has_command(104):
                    # Blacklist other monks
                    blacklisted_name = name_lookup_dict[unit_line.get_head_unit_id()][0]
                    blacklisted_entities.append(ForwardRef(unit_line, blacklisted_name))
                    continue

        else:
            blacklisted_entities = []

        ability_raw_api_object.add_raw_member("blacklisted_entities",
                                              blacklisted_entities,
                                              "engine.ability.type.ApplyDiscreteEffect")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref
Beispiel #12
0
    def get_attack_effects(
        tech_group: GenieTechEffectBundleGroup,
        line: GenieGameEntityGroup,
        diff: ConverterObject,
        ability_ref: str
    ) -> list[ForwardRef]:
        """
        Upgrades effects that are used for attacking (unit command: 7)

        :param tech_group: Tech that gets the patch.
        :type tech_group: ...dataformat.converter_object.ConverterObjectGroup
        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param diff: A diff between two ConvertObject instances.
        :type diff: ...dataformat.converter_object.ConverterObject
        :param ability_ref: Reference of the ability raw API object the effects are added to.
        :type ability_ref: str
        :returns: The forward references for the effects.
        :rtype: list
        """
        head_unit_id = line.get_head_unit_id()
        tech_id = tech_group.get_id()
        dataset = line.data

        patches = []

        name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version)
        armor_lookup_dict = internal_name_lookups.get_armor_class_lookups(dataset.game_version)
        tech_lookup_dict = internal_name_lookups.get_tech_lookups(dataset.game_version)

        tech_name = tech_lookup_dict[tech_id][0]

        diff_attacks = diff["attacks"].value
        for diff_attack in diff_attacks.values():
            if isinstance(diff_attack, NoDiffMember):
                continue

            if isinstance(diff_attack, LeftMissingMember):
                # Create a new attack effect, then patch it in
                attack = diff_attack.ref

                armor_class = attack["type_id"].value
                attack_amount = attack["amount"].value

                if armor_class == -1:
                    continue

                class_name = armor_lookup_dict[armor_class]

                # FlatAttributeChangeDecrease
                effect_parent = "engine.effect.discrete.flat_attribute_change.FlatAttributeChange"
                attack_parent = "engine.effect.discrete.flat_attribute_change.type.FlatAttributeChangeDecrease"

                patch_target_ref = f"{ability_ref}.Batch"
                patch_target_forward_ref = ForwardRef(line, patch_target_ref)

                # Wrapper
                wrapper_name = f"Add{class_name}AttackEffectWrapper"
                wrapper_ref = f"{tech_name}.{wrapper_name}"
                wrapper_raw_api_object = RawAPIObject(wrapper_ref,
                                                      wrapper_name,
                                                      dataset.nyan_api_objects)
                wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

                if isinstance(line, GenieBuildingLineGroup):
                    # Store building upgrades next to their game entity definition,
                    # not in the Age up techs.
                    wrapper_raw_api_object.set_location(("data/game_entity/generic/"
                                                         f"{name_lookup_dict[head_unit_id][1]}/"))
                    wrapper_raw_api_object.set_filename(f"{tech_lookup_dict[tech_id][1]}_upgrade")

                else:
                    wrapper_raw_api_object.set_location(ForwardRef(tech_group, tech_name))

                # Nyan patch
                nyan_patch_name = f"Add{class_name}AttackEffect"
                nyan_patch_ref = f"{tech_name}.{wrapper_name}.{nyan_patch_name}"
                nyan_patch_location = ForwardRef(tech_group, wrapper_ref)
                nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                         nyan_patch_name,
                                                         dataset.nyan_api_objects,
                                                         nyan_patch_location)
                nyan_patch_raw_api_object.add_raw_parent("engine.util.patch.NyanPatch")
                nyan_patch_raw_api_object.set_patch_target(patch_target_forward_ref)

                # New attack effect
                # ============================================================================
                attack_ref = f"{nyan_patch_ref}.{class_name}"
                attack_raw_api_object = RawAPIObject(attack_ref,
                                                     class_name,
                                                     dataset.nyan_api_objects)
                attack_raw_api_object.add_raw_parent(attack_parent)
                attack_location = ForwardRef(tech_group, nyan_patch_ref)
                attack_raw_api_object.set_location(attack_location)

                # Type
                type_ref = f"util.attribute_change_type.types.{class_name}"
                change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object()
                attack_raw_api_object.add_raw_member("type",
                                                     change_type,
                                                     effect_parent)

                # Min value (optional)
                min_value = dataset.pregen_nyan_objects[("effect.discrete.flat_attribute_change."
                                                         "min_damage.AoE2MinChangeAmount")].get_nyan_object()
                attack_raw_api_object.add_raw_member("min_change_value",
                                                     min_value,
                                                     effect_parent)

                # Max value (optional; not added because there is none in AoE2)

                # Change value
                # =================================================================================
                amount_name = f"{nyan_patch_ref}.{class_name}.ChangeAmount"
                amount_raw_api_object = RawAPIObject(
                    amount_name, "ChangeAmount", dataset.nyan_api_objects)
                amount_raw_api_object.add_raw_parent("engine.util.attribute.AttributeAmount")
                amount_location = ForwardRef(line, attack_ref)
                amount_raw_api_object.set_location(amount_location)

                attribute = dataset.pregen_nyan_objects["util.attribute.types.Health"].get_nyan_object(
                )
                amount_raw_api_object.add_raw_member("type",
                                                     attribute,
                                                     "engine.util.attribute.AttributeAmount")
                amount_raw_api_object.add_raw_member("amount",
                                                     attack_amount,
                                                     "engine.util.attribute.AttributeAmount")

                line.add_raw_api_object(amount_raw_api_object)
                # =================================================================================
                amount_forward_ref = ForwardRef(line, amount_name)
                attack_raw_api_object.add_raw_member("change_value",
                                                     amount_forward_ref,
                                                     effect_parent)

                # Ignore protection
                attack_raw_api_object.add_raw_member("ignore_protection",
                                                     [],
                                                     effect_parent)

                # Effect is added to the line, so it can be referenced by other upgrades
                line.add_raw_api_object(attack_raw_api_object)
                # ============================================================================
                attack_forward_ref = ForwardRef(line, attack_ref)
                nyan_patch_raw_api_object.add_raw_patch_member("effects",
                                                               [attack_forward_ref],
                                                               "engine.util.effect_batch.EffectBatch",
                                                               MemberOperator.ADD)

                patch_forward_ref = ForwardRef(tech_group, nyan_patch_ref)
                wrapper_raw_api_object.add_raw_member("patch",
                                                      patch_forward_ref,
                                                      "engine.util.patch.Patch")

                tech_group.add_raw_api_object(wrapper_raw_api_object)
                tech_group.add_raw_api_object(nyan_patch_raw_api_object)

                wrapper_forward_ref = ForwardRef(tech_group, wrapper_ref)
                patches.append(wrapper_forward_ref)

            elif isinstance(diff_attack, RightMissingMember):
                # Patch the effect out of the ability
                attack = diff_attack.ref

                armor_class = attack["type_id"].value
                class_name = armor_lookup_dict[armor_class]

                patch_target_ref = f"{ability_ref}.Batch"
                patch_target_forward_ref = ForwardRef(line, patch_target_ref)

                # Wrapper
                wrapper_name = f"Remove{class_name}AttackEffectWrapper"
                wrapper_ref = f"{tech_name}.{wrapper_name}"
                wrapper_raw_api_object = RawAPIObject(wrapper_ref,
                                                      wrapper_name,
                                                      dataset.nyan_api_objects)
                wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

                if isinstance(line, GenieBuildingLineGroup):
                    # Store building upgrades next to their game entity definition,
                    # not in the Age up techs.
                    wrapper_raw_api_object.set_location(("data/game_entity/generic/"
                                                         f"{name_lookup_dict[head_unit_id][1]}/"))
                    wrapper_raw_api_object.set_filename(f"{tech_lookup_dict[tech_id][1]}_upgrade")

                else:
                    wrapper_raw_api_object.set_location(ForwardRef(tech_group, tech_name))

                # Nyan patch
                nyan_patch_name = f"Remove{class_name}AttackEffect"
                nyan_patch_ref = f"{tech_name}.{wrapper_name}.{nyan_patch_name}"
                nyan_patch_location = ForwardRef(tech_group, wrapper_ref)
                nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                         nyan_patch_name,
                                                         dataset.nyan_api_objects,
                                                         nyan_patch_location)
                nyan_patch_raw_api_object.add_raw_parent("engine.util.patch.NyanPatch")
                nyan_patch_raw_api_object.set_patch_target(patch_target_forward_ref)

                attack_ref = f"{ability_ref}.{class_name}"
                attack_forward_ref = ForwardRef(line, attack_ref)
                nyan_patch_raw_api_object.add_raw_patch_member("effects",
                                                               [attack_forward_ref],
                                                               "engine.util.effect_batch.EffectBatch",
                                                               MemberOperator.SUBTRACT)

                patch_forward_ref = ForwardRef(tech_group, nyan_patch_ref)
                wrapper_raw_api_object.add_raw_member("patch",
                                                      patch_forward_ref,
                                                      "engine.util.patch.Patch")

                tech_group.add_raw_api_object(wrapper_raw_api_object)
                tech_group.add_raw_api_object(nyan_patch_raw_api_object)

                wrapper_forward_ref = ForwardRef(tech_group, wrapper_ref)
                patches.append(wrapper_forward_ref)

            else:
                diff_armor_class = diff_attack["type_id"]
                if not isinstance(diff_armor_class, NoDiffMember):
                    # If this happens then the attacks are out of order
                    # and we have to try something else
                    raise Exception(f"Could not create effect upgrade for line {repr(line)}: "
                                    "Out of order")

                armor_class = diff_armor_class.ref.value
                attack_amount = diff_attack["amount"].value

                class_name = armor_lookup_dict[armor_class]

                patch_target_ref = f"{ability_ref}.Batch.{class_name}.ChangeAmount"
                patch_target_forward_ref = ForwardRef(line, patch_target_ref)

                # Wrapper
                wrapper_name = f"Change{class_name}AttackWrapper"
                wrapper_ref = f"{tech_name}.{wrapper_name}"
                wrapper_raw_api_object = RawAPIObject(wrapper_ref,
                                                      wrapper_name,
                                                      dataset.nyan_api_objects)
                wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

                if isinstance(line, GenieBuildingLineGroup):
                    # Store building upgrades next to their game entity definition,
                    # not in the Age up techs.
                    wrapper_raw_api_object.set_location(("data/game_entity/generic/"
                                                         f"{name_lookup_dict[head_unit_id][1]}/"))
                    wrapper_raw_api_object.set_filename(f"{tech_lookup_dict[tech_id][1]}_upgrade")

                else:
                    wrapper_raw_api_object.set_location(ForwardRef(tech_group, tech_name))

                # Nyan patch
                nyan_patch_name = f"Change{class_name}Attack"
                nyan_patch_ref = f"{tech_name}.{wrapper_name}.{nyan_patch_name}"
                nyan_patch_location = ForwardRef(tech_group, wrapper_ref)
                nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                         nyan_patch_name,
                                                         dataset.nyan_api_objects,
                                                         nyan_patch_location)
                nyan_patch_raw_api_object.add_raw_parent("engine.util.patch.NyanPatch")
                nyan_patch_raw_api_object.set_patch_target(patch_target_forward_ref)

                nyan_patch_raw_api_object.add_raw_patch_member("amount",
                                                               attack_amount,
                                                               "engine.util.attribute.AttributeAmount",
                                                               MemberOperator.ADD)

                patch_forward_ref = ForwardRef(tech_group, nyan_patch_ref)
                wrapper_raw_api_object.add_raw_member("patch",
                                                      patch_forward_ref,
                                                      "engine.util.patch.Patch")

                tech_group.add_raw_api_object(wrapper_raw_api_object)
                tech_group.add_raw_api_object(nyan_patch_raw_api_object)

                wrapper_forward_ref = ForwardRef(tech_group, wrapper_ref)
                patches.append(wrapper_forward_ref)

        return patches
Beispiel #13
0
    def cost_carbon_upgrade(converter_group: ConverterObjectGroup,
                            line: GenieGameEntityGroup,
                            value: typing.Union[int, float],
                            operator: MemberOperator,
                            team: bool = False) -> list[ForwardRef]:
        """
        Creates a patch for the carbon cost modify effect (ID: 104).

        :param converter_group: Tech/Civ that gets the patch.
        :type converter_group: ...dataformat.converter_object.ConverterObjectGroup
        :param line: Unit/Building line that has the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param value: Value used for patching the member.
        :type value: int, float
        :param operator: Operator used for patching the member.
        :type operator: MemberOperator
        :returns: The forward references for the generated patches.
        :rtype: list
        """
        head_unit_id = line.get_head_unit_id()
        dataset = line.data

        patches = []

        obj_id = converter_group.get_id()
        if isinstance(converter_group, GenieTechEffectBundleGroup):
            tech_lookup_dict = internal_name_lookups.get_tech_lookups(
                dataset.game_version)
            obj_name = tech_lookup_dict[obj_id][0]

        else:
            civ_lookup_dict = internal_name_lookups.get_civ_lookups(
                dataset.game_version)
            obj_name = civ_lookup_dict[obj_id][0]

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[head_unit_id][0]

        patch_target_ref = (f"{game_entity_name}.CreatableGameEntity."
                            f"{game_entity_name}Cost.CarbonAmount")
        patch_target_forward_ref = ForwardRef(line, patch_target_ref)

        # Wrapper
        wrapper_name = f"Change{game_entity_name}CarbonCostWrapper"
        wrapper_ref = f"{obj_name}.{wrapper_name}"
        wrapper_location = ForwardRef(converter_group, obj_name)
        wrapper_raw_api_object = RawAPIObject(wrapper_ref, wrapper_name,
                                              dataset.nyan_api_objects,
                                              wrapper_location)
        wrapper_raw_api_object.add_raw_parent("engine.util.patch.Patch")

        # Nyan patch
        nyan_patch_name = f"Change{game_entity_name}CarbonCost"
        nyan_patch_ref = f"{obj_name}.{wrapper_name}.{nyan_patch_name}"
        nyan_patch_location = ForwardRef(converter_group, wrapper_ref)
        nyan_patch_raw_api_object = RawAPIObject(nyan_patch_ref,
                                                 nyan_patch_name,
                                                 dataset.nyan_api_objects,
                                                 nyan_patch_location)
        nyan_patch_raw_api_object.add_raw_parent("engine.util.patch.NyanPatch")
        nyan_patch_raw_api_object.set_patch_target(patch_target_forward_ref)

        nyan_patch_raw_api_object.add_raw_patch_member(
            "amount", value, "engine.util.resource.ResourceAmount", operator)

        patch_forward_ref = ForwardRef(converter_group, nyan_patch_ref)
        wrapper_raw_api_object.add_raw_member("patch", patch_forward_ref,
                                              "engine.util.patch.Patch")

        if team:
            team_property = dataset.pregen_nyan_objects[
                "util.patch.property.types.Team"].get_nyan_object()
            properties = {
                dataset.nyan_api_objects["engine.util.patch.property.type.Diplomatic"]:
                team_property
            }
            wrapper_raw_api_object.add_raw_member("properties", properties,
                                                  "engine.util.patch.Patch")

        converter_group.add_raw_api_object(wrapper_raw_api_object)
        converter_group.add_raw_api_object(nyan_patch_raw_api_object)

        wrapper_forward_ref = ForwardRef(converter_group, wrapper_ref)
        patches.append(wrapper_forward_ref)

        return patches
    def get_creatable_game_entity(line: GenieGameEntityGroup) -> None:
        """
        Creates the CreatableGameEntity object for a unit/building line.

        :param line: Unit/Building line.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        """
        if isinstance(line, GenieVillagerGroup):
            current_unit = line.variants[0].line[0]

        else:
            current_unit = line.line[0]

        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)
        civ_lookup_dict = internal_name_lookups.get_civ_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        obj_ref = f"{game_entity_name}.CreatableGameEntity"
        obj_name = f"{game_entity_name}Creatable"
        creatable_raw_api_object = RawAPIObject(obj_ref, obj_name,
                                                dataset.nyan_api_objects)
        creatable_raw_api_object.add_raw_parent(
            "engine.util.create.CreatableGameEntity")

        # Get train location of line
        train_location_id = line.get_train_location_id()
        if isinstance(line, GenieBuildingLineGroup):
            train_location = dataset.unit_lines[train_location_id]
            train_location_name = name_lookup_dict[train_location_id][0]

        else:
            train_location = dataset.building_lines[train_location_id]
            train_location_name = name_lookup_dict[train_location_id][0]

        # Location of the object depends on whether it'a a unique unit or a normal unit
        if line.is_unique():
            # Add object to the Civ object
            enabling_research_id = line.get_enabling_research_id()
            enabling_research = dataset.genie_techs[enabling_research_id]
            enabling_civ_id = enabling_research["civilization_id"].get_value()

            civ = dataset.civ_groups[enabling_civ_id]
            civ_name = civ_lookup_dict[enabling_civ_id][0]

            creatable_location = ForwardRef(civ, civ_name)

        else:
            # Add object to the train location's Create ability
            creatable_location = ForwardRef(train_location,
                                            f"{train_location_name}.Create")

        creatable_raw_api_object.set_location(creatable_location)

        # Game Entity
        game_entity_forward_ref = ForwardRef(line, game_entity_name)
        creatable_raw_api_object.add_raw_member(
            "game_entity", game_entity_forward_ref,
            "engine.util.create.CreatableGameEntity")

        # TODO: Variants
        variants_set = []

        creatable_raw_api_object.add_raw_member(
            "variants", variants_set, "engine.util.create.CreatableGameEntity")

        # Cost (construction)
        cost_name = f"{game_entity_name}.CreatableGameEntity.{game_entity_name}Cost"
        cost_raw_api_object = RawAPIObject(cost_name,
                                           f"{game_entity_name}Cost",
                                           dataset.nyan_api_objects)
        cost_raw_api_object.add_raw_parent(
            "engine.util.cost.type.ResourceCost")
        creatable_forward_ref = ForwardRef(line, obj_ref)
        cost_raw_api_object.set_location(creatable_forward_ref)

        payment_mode = dataset.nyan_api_objects[
            "engine.util.payment_mode.type.Advance"]
        cost_raw_api_object.add_raw_member("payment_mode", payment_mode,
                                           "engine.util.cost.Cost")

        if line.is_repairable():
            # Cost (repair) for buildings
            cost_repair_name = "%s.CreatableGameEntity.%sRepairCost" % (
                game_entity_name, game_entity_name)
            cost_repair_raw_api_object = RawAPIObject(
                cost_repair_name, f"{game_entity_name}RepairCost",
                dataset.nyan_api_objects)
            cost_repair_raw_api_object.add_raw_parent(
                "engine.util.cost.type.ResourceCost")
            creatable_forward_ref = ForwardRef(line, obj_ref)
            cost_repair_raw_api_object.set_location(creatable_forward_ref)

            payment_repair_mode = dataset.nyan_api_objects[
                "engine.util.payment_mode.type.Adaptive"]
            cost_repair_raw_api_object.add_raw_member("payment_mode",
                                                      payment_repair_mode,
                                                      "engine.util.cost.Cost")
            line.add_raw_api_object(cost_repair_raw_api_object)

        cost_amounts = []
        cost_repair_amounts = []
        for resource_amount in current_unit["resource_cost"].get_value():
            resource_id = resource_amount["type_id"].get_value()

            resource = None
            resource_name = ""
            if resource_id == -1:
                # Not a valid resource
                continue

            if resource_id == 0:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Food"].get_nyan_object()
                resource_name = "Food"

            elif resource_id == 1:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Carbon"].get_nyan_object()
                resource_name = "Carbon"

            elif resource_id == 2:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Ore"].get_nyan_object()
                resource_name = "Ore"

            elif resource_id == 3:
                resource = dataset.pregen_nyan_objects[
                    "util.resource.types.Nova"].get_nyan_object()
                resource_name = "Nova"

            else:
                # Other resource ids are handled differently
                continue

            # Skip resources that are only expected to be there
            if not resource_amount["enabled"].get_value():
                continue

            amount = resource_amount["amount"].get_value()

            cost_amount_name = f"{cost_name}.{resource_name}Amount"
            cost_amount = RawAPIObject(cost_amount_name,
                                       f"{resource_name}Amount",
                                       dataset.nyan_api_objects)
            cost_amount.add_raw_parent("engine.util.resource.ResourceAmount")
            cost_forward_ref = ForwardRef(line, cost_name)
            cost_amount.set_location(cost_forward_ref)

            cost_amount.add_raw_member("type", resource,
                                       "engine.util.resource.ResourceAmount")
            cost_amount.add_raw_member("amount", amount,
                                       "engine.util.resource.ResourceAmount")

            cost_amount_forward_ref = ForwardRef(line, cost_amount_name)
            cost_amounts.append(cost_amount_forward_ref)
            line.add_raw_api_object(cost_amount)

            if line.is_repairable():
                # Cost for repairing = half of the construction cost
                cost_amount_name = f"{cost_repair_name}.{resource_name}Amount"
                cost_amount = RawAPIObject(cost_amount_name,
                                           f"{resource_name}Amount",
                                           dataset.nyan_api_objects)
                cost_amount.add_raw_parent(
                    "engine.util.resource.ResourceAmount")
                cost_forward_ref = ForwardRef(line, cost_repair_name)
                cost_amount.set_location(cost_forward_ref)

                cost_amount.add_raw_member(
                    "type", resource, "engine.util.resource.ResourceAmount")
                cost_amount.add_raw_member(
                    "amount", amount / 2,
                    "engine.util.resource.ResourceAmount")

                cost_amount_forward_ref = ForwardRef(line, cost_amount_name)
                cost_repair_amounts.append(cost_amount_forward_ref)
                line.add_raw_api_object(cost_amount)

        cost_raw_api_object.add_raw_member(
            "amount", cost_amounts, "engine.util.cost.type.ResourceCost")

        if line.is_repairable():
            cost_repair_raw_api_object.add_raw_member(
                "amount", cost_repair_amounts,
                "engine.util.cost.type.ResourceCost")

        cost_forward_ref = ForwardRef(line, cost_name)
        creatable_raw_api_object.add_raw_member(
            "cost", cost_forward_ref, "engine.util.create.CreatableGameEntity")
        # Creation time
        if isinstance(line, GenieUnitLineGroup):
            creation_time = current_unit["creation_time"].get_value()

        else:
            # Buildings are created immediately
            creation_time = 0

        creatable_raw_api_object.add_raw_member(
            "creation_time", creation_time,
            "engine.util.create.CreatableGameEntity")

        # Creation sound
        creation_sound_id = current_unit["train_sound_id"].get_value()

        # Create sound object
        obj_name = f"{game_entity_name}.CreatableGameEntity.Sound"
        sound_raw_api_object = RawAPIObject(obj_name, "CreationSound",
                                            dataset.nyan_api_objects)
        sound_raw_api_object.add_raw_parent("engine.util.sound.Sound")
        sound_location = ForwardRef(line, obj_ref)
        sound_raw_api_object.set_location(sound_location)

        # Search for the sound if it exists
        creation_sounds = []
        if creation_sound_id > -1:
            genie_sound = dataset.genie_sounds[creation_sound_id]
            file_ids = genie_sound.get_sounds(civ_id=-1)

            if file_ids:
                file_id = genie_sound.get_sounds(civ_id=-1)[0]

                if file_id in dataset.combined_sounds:
                    creation_sound = dataset.combined_sounds[file_id]
                    creation_sound.add_reference(sound_raw_api_object)

                else:
                    creation_sound = CombinedSound(
                        creation_sound_id, file_id,
                        f"creation_sound_{creation_sound_id}", dataset)
                    dataset.combined_sounds.update({file_id: creation_sound})
                    creation_sound.add_reference(sound_raw_api_object)

                creation_sounds.append(creation_sound)

        sound_raw_api_object.add_raw_member("play_delay", 0,
                                            "engine.util.sound.Sound")
        sound_raw_api_object.add_raw_member("sounds", creation_sounds,
                                            "engine.util.sound.Sound")

        sound_forward_ref = ForwardRef(line, obj_name)
        creatable_raw_api_object.add_raw_member(
            "creation_sounds", [sound_forward_ref],
            "engine.util.create.CreatableGameEntity")

        line.add_raw_api_object(sound_raw_api_object)

        # Condition
        unlock_conditions = []
        enabling_research_id = line.get_enabling_research_id()
        if enabling_research_id > -1:
            unlock_conditions.extend(
                AoCAuxiliarySubprocessor.get_condition(line, obj_ref,
                                                       enabling_research_id))

        creatable_raw_api_object.add_raw_member(
            "condition", unlock_conditions,
            "engine.util.create.CreatableGameEntity")

        # Placement modes
        placement_modes = []
        if isinstance(line, GenieBuildingLineGroup):
            # Buildings are placed on the map
            # Place mode
            obj_name = f"{game_entity_name}.CreatableGameEntity.Place"
            place_raw_api_object = RawAPIObject(obj_name, "Place",
                                                dataset.nyan_api_objects)
            place_raw_api_object.add_raw_parent(
                "engine.util.placement_mode.type.Place")
            place_location = ForwardRef(
                line, f"{game_entity_name}.CreatableGameEntity")
            place_raw_api_object.set_location(place_location)

            # Tile snap distance (uses 1.0 for grid placement)
            place_raw_api_object.add_raw_member(
                "tile_snap_distance", 1.0,
                "engine.util.placement_mode.type.Place")
            # Clearance size
            clearance_size_x = current_unit["clearance_size_x"].get_value()
            clearance_size_y = current_unit["clearance_size_y"].get_value()
            place_raw_api_object.add_raw_member(
                "clearance_size_x", clearance_size_x,
                "engine.util.placement_mode.type.Place")
            place_raw_api_object.add_raw_member(
                "clearance_size_y", clearance_size_y,
                "engine.util.placement_mode.type.Place")

            # Allow rotation
            place_raw_api_object.add_raw_member(
                "allow_rotation", True,
                "engine.util.placement_mode.type.Place")

            # Max elevation difference
            elevation_mode = current_unit["elevation_mode"].get_value()
            if elevation_mode == 2:
                max_elevation_difference = 0

            elif elevation_mode == 3:
                max_elevation_difference = 1

            else:
                max_elevation_difference = MemberSpecialValue.NYAN_INF

            place_raw_api_object.add_raw_member(
                "max_elevation_difference", max_elevation_difference,
                "engine.util.placement_mode.type.Place")

            line.add_raw_api_object(place_raw_api_object)

            place_forward_ref = ForwardRef(line, obj_name)
            placement_modes.append(place_forward_ref)

            if line.get_class_id() == 39:
                # Gates
                obj_name = f"{game_entity_name}.CreatableGameEntity.Replace"
                replace_raw_api_object = RawAPIObject(obj_name, "Replace",
                                                      dataset.nyan_api_objects)
                replace_raw_api_object.add_raw_parent(
                    "engine.util.placement_mode.type.Replace")
                replace_location = ForwardRef(
                    line, f"{game_entity_name}.CreatableGameEntity")
                replace_raw_api_object.set_location(replace_location)

                # Game entities (only stone wall)
                wall_line_id = 117
                wall_line = dataset.building_lines[wall_line_id]
                wall_name = name_lookup_dict[117][0]
                game_entities = [ForwardRef(wall_line, wall_name)]
                replace_raw_api_object.add_raw_member(
                    "game_entities", game_entities,
                    "engine.util.placement_mode.type.Replace")

                line.add_raw_api_object(replace_raw_api_object)

                replace_forward_ref = ForwardRef(line, obj_name)
                placement_modes.append(replace_forward_ref)

        else:
            placement_modes.append(
                dataset.
                nyan_api_objects["engine.util.placement_mode.type.Eject"])

            # OwnStorage mode
            obj_name = f"{game_entity_name}.CreatableGameEntity.OwnStorage"
            own_storage_raw_api_object = RawAPIObject(obj_name, "OwnStorage",
                                                      dataset.nyan_api_objects)
            own_storage_raw_api_object.add_raw_parent(
                "engine.util.placement_mode.type.OwnStorage")
            own_storage_location = ForwardRef(
                line, f"{game_entity_name}.CreatableGameEntity")
            own_storage_raw_api_object.set_location(own_storage_location)

            # Container
            container_forward_ref = ForwardRef(
                train_location, "%s.Storage.%sContainer" %
                (train_location_name, train_location_name))
            own_storage_raw_api_object.add_raw_member(
                "container", container_forward_ref,
                "engine.util.placement_mode.type.OwnStorage")

            line.add_raw_api_object(own_storage_raw_api_object)

            own_storage_forward_ref = ForwardRef(line, obj_name)
            placement_modes.append(own_storage_forward_ref)

        creatable_raw_api_object.add_raw_member(
            "placement_modes", placement_modes,
            "engine.util.create.CreatableGameEntity")

        line.add_raw_api_object(creatable_raw_api_object)
        line.add_raw_api_object(cost_raw_api_object)
Beispiel #15
0
    def regenerate_attribute_ability(line: GenieGameEntityGroup) -> ForwardRef:
        """
        Adds the RegenerateAttribute ability to a line.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :returns: The forward references for the ability.
        :rtype: list
        """
        current_unit = line.get_head_unit()
        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        attribute = None
        attribute_name = ""
        if current_unit_id == 125:
            # Monk; regenerates Faith
            attribute = dataset.pregen_nyan_objects[
                "util.attribute.types.Faith"].get_nyan_object()
            attribute_name = "Faith"

        elif current_unit_id == 692:
            # Berserk: regenerates Health
            attribute = dataset.pregen_nyan_objects[
                "util.attribute.types.Health"].get_nyan_object()
            attribute_name = "Health"

        else:
            return []

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        ability_name = f"Regenerate{attribute_name}"
        ability_ref = f"{game_entity_name}.{ability_name}"
        ability_raw_api_object = RawAPIObject(ability_ref, ability_name,
                                              dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent(
            "engine.ability.type.RegenerateAttribute")
        ability_location = ForwardRef(line, game_entity_name)
        ability_raw_api_object.set_location(ability_location)

        # Attribute rate
        # ===============================================================================
        rate_name = f"{attribute_name}Rate"
        rate_ref = f"{game_entity_name}.{ability_name}.{rate_name}"
        rate_raw_api_object = RawAPIObject(rate_ref, rate_name,
                                           dataset.nyan_api_objects)
        rate_raw_api_object.add_raw_parent(
            "engine.util.attribute.AttributeRate")
        rate_location = ForwardRef(line, ability_ref)
        rate_raw_api_object.set_location(rate_location)

        # Attribute
        rate_raw_api_object.add_raw_member(
            "type", attribute, "engine.util.attribute.AttributeRate")

        # Rate
        attribute_rate = 0
        if current_unit_id == 125:
            # stored in civ resources
            attribute_rate = dataset.genie_civs[0]["resources"][35].get_value()

        elif current_unit_id == 692:
            # stored in unit, but has to get converted to amount/second
            heal_timer = current_unit["heal_timer"].get_value()
            attribute_rate = 1 / heal_timer

        rate_raw_api_object.add_raw_member(
            "rate", attribute_rate, "engine.util.attribute.AttributeRate")

        line.add_raw_api_object(rate_raw_api_object)
        # ===============================================================================
        rate_forward_ref = ForwardRef(line, rate_ref)
        ability_raw_api_object.add_raw_member(
            "rate", rate_forward_ref,
            "engine.ability.type.RegenerateAttribute")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return [ability_forward_ref]
Beispiel #16
0
    def get_repair_resistances(line: GenieGameEntityGroup,
                               ability_ref: str) -> list[ForwardRef]:
        """
        Creates resistances that are used for repairing (unit command: 106)

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param ability_ref: Reference of the ability raw API object the effects are added to.
        :type ability_ref: str
        :returns: The forward references for the effects.
        :rtype: list
        """
        current_unit_id = line.get_head_unit_id()
        dataset = line.data
        api_objects = dataset.nyan_api_objects

        resistances = []

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        resistance_parent = "engine.resistance.continuous.flat_attribute_change.FlatAttributeChange"
        repair_parent = "engine.resistance.continuous.flat_attribute_change.type.FlatAttributeChangeIncrease"

        resistance_ref = f"{ability_ref}.Repair"
        resistance_raw_api_object = RawAPIObject(resistance_ref, "Repair",
                                                 dataset.nyan_api_objects)
        resistance_raw_api_object.add_raw_parent(repair_parent)
        resistance_location = ForwardRef(line, ability_ref)
        resistance_raw_api_object.set_location(resistance_location)

        # Type
        type_ref = f"util.attribute_change_type.types.{game_entity_name}Repair"
        change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object()
        resistance_raw_api_object.add_raw_member("type", change_type,
                                                 resistance_parent)

        # Block rate
        # =================================================================================
        rate_name = f"{ability_ref}.Repair.BlockRate"
        rate_raw_api_object = RawAPIObject(rate_name, "BlockRate",
                                           dataset.nyan_api_objects)
        rate_raw_api_object.add_raw_parent(
            "engine.util.attribute.AttributeRate")
        rate_location = ForwardRef(line, resistance_ref)
        rate_raw_api_object.set_location(rate_location)

        attribute = dataset.pregen_nyan_objects[
            "util.attribute.types.Health"].get_nyan_object()
        rate_raw_api_object.add_raw_member(
            "type", attribute, "engine.util.attribute.AttributeRate")
        rate_raw_api_object.add_raw_member(
            "rate", 0.0, "engine.util.attribute.AttributeRate")

        line.add_raw_api_object(rate_raw_api_object)
        # =================================================================================
        rate_forward_ref = ForwardRef(line, rate_name)
        resistance_raw_api_object.add_raw_member("block_rate",
                                                 rate_forward_ref,
                                                 resistance_parent)

        # Stacking of villager repair HP increase
        construct_property = dataset.pregen_nyan_objects[
            "resistance.property.types.BuildingRepair"].get_nyan_object()
        properties = {
            api_objects["engine.resistance.property.type.Stacked"]:
            construct_property
        }

        # Add the predefined property
        resistance_raw_api_object.add_raw_member(
            "properties", properties, "engine.resistance.Resistance")

        line.add_raw_api_object(resistance_raw_api_object)
        resistance_forward_ref = ForwardRef(line, resistance_ref)
        resistances.append(resistance_forward_ref)

        return resistances
Beispiel #17
0
    def get_construct_resistances(line: GenieGameEntityGroup,
                                  ability_ref: str) -> list[ForwardRef]:
        """
        Creates resistances that are used for constructing (unit command: 101)

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param ability_ref: Reference of the ability raw API object the effects are added to.
        :type ability_ref: str
        :returns: The forward references for the effects.
        :rtype: list
        """
        current_unit_id = line.get_head_unit_id()
        dataset = line.data
        api_objects = dataset.nyan_api_objects

        resistances = []

        name_lookup_dict = internal_name_lookups.get_entity_lookups(
            dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        progress_resistance_parent = "engine.resistance.continuous.time_relative_progress.TimeRelativeProgressChange"
        progress_construct_parent = "engine.resistance.continuous.time_relative_progress.type.TimeRelativeProgressIncrease"
        attr_resistance_parent = "engine.resistance.continuous.time_relative_attribute.TimeRelativeAttributeChange"
        attr_construct_parent = "engine.resistance.continuous.time_relative_attribute.type.TimeRelativeAttributeIncrease"

        # Progress
        resistance_ref = f"{ability_ref}.ConstructProgress"
        resistance_raw_api_object = RawAPIObject(resistance_ref,
                                                 "ConstructProgress",
                                                 dataset.nyan_api_objects)
        resistance_raw_api_object.add_raw_parent(progress_construct_parent)
        resistance_location = ForwardRef(line, ability_ref)
        resistance_raw_api_object.set_location(resistance_location)

        # Type
        type_ref = f"util.construct_type.types.{game_entity_name}Construct"
        change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object()
        resistance_raw_api_object.add_raw_member("type", change_type,
                                                 progress_resistance_parent)

        line.add_raw_api_object(resistance_raw_api_object)
        resistance_forward_ref = ForwardRef(line, resistance_ref)
        resistances.append(resistance_forward_ref)

        # Stacking of villager construction times
        construct_property = dataset.pregen_nyan_objects[
            "resistance.property.types.BuildingConstruct"].get_nyan_object()
        properties = {
            api_objects["engine.resistance.property.type.Stacked"]:
            construct_property
        }

        # Add the predefined property
        resistance_raw_api_object.add_raw_member(
            "properties", properties, "engine.resistance.Resistance")

        # Health
        resistance_ref = f"{ability_ref}.ConstructHP"
        resistance_raw_api_object = RawAPIObject(resistance_ref, "ConstructHP",
                                                 dataset.nyan_api_objects)
        resistance_raw_api_object.add_raw_parent(attr_construct_parent)
        resistance_location = ForwardRef(line, ability_ref)
        resistance_raw_api_object.set_location(resistance_location)

        # Type
        type_ref = f"util.attribute_change_type.types.{game_entity_name}Construct"
        change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object()
        resistance_raw_api_object.add_raw_member("type", change_type,
                                                 attr_resistance_parent)

        # Stacking of villager construction HP increase
        construct_property = dataset.pregen_nyan_objects[
            "resistance.property.types.BuildingConstruct"].get_nyan_object()
        properties = {
            api_objects["engine.resistance.property.type.Stacked"]:
            construct_property
        }

        # Add the predefined property
        resistance_raw_api_object.add_raw_member(
            "properties", properties, "engine.resistance.Resistance")

        line.add_raw_api_object(resistance_raw_api_object)
        resistance_forward_ref = ForwardRef(line, resistance_ref)
        resistances.append(resistance_forward_ref)

        return resistances
Beispiel #18
0
    def projectile_ability(line: GenieGameEntityGroup, position: int = 0) -> ForwardRef:
        """
        Adds a Projectile ability to projectiles in a line. Which projectile should
        be added is determined by the 'position' argument.

        :param line: Unit/Building line that gets the ability.
        :type line: ...dataformat.converter_object.ConverterObjectGroup
        :param position: When 0, gives the first projectile its ability. When 1, the second...
        :type position: int
        :returns: The forward reference for the ability.
        :rtype: ...dataformat.forward_ref.ForwardRef
        """
        current_unit = line.get_head_unit()
        current_unit_id = line.get_head_unit_id()
        dataset = line.data

        name_lookup_dict = internal_name_lookups.get_entity_lookups(dataset.game_version)

        game_entity_name = name_lookup_dict[current_unit_id][0]

        # First projectile is mandatory
        obj_ref = f"{game_entity_name}.ShootProjectile.Projectile{position}"
        ability_ref = f"{game_entity_name}.ShootProjectile.Projectile{position}.Projectile"
        ability_raw_api_object = RawAPIObject(ability_ref,
                                              "Projectile",
                                              dataset.nyan_api_objects)
        ability_raw_api_object.add_raw_parent("engine.ability.type.Projectile")
        ability_location = ForwardRef(line, obj_ref)
        ability_raw_api_object.set_location(ability_location)

        # Arc
        if position == 0:
            projectile_id = current_unit["projectile_id0"].value

        else:
            raise Exception("Invalid position")

        projectile = dataset.genie_units[projectile_id]
        arc = degrees(projectile["projectile_arc"].value)
        ability_raw_api_object.add_raw_member("arc",
                                              arc,
                                              "engine.ability.type.Projectile")

        # Accuracy
        accuracy_name = (f"{game_entity_name}.ShootProjectile."
                         f"Projectile{position}.Projectile.Accuracy")
        accuracy_raw_api_object = RawAPIObject(accuracy_name, "Accuracy", dataset.nyan_api_objects)
        accuracy_raw_api_object.add_raw_parent("engine.util.accuracy.Accuracy")
        accuracy_location = ForwardRef(line, ability_ref)
        accuracy_raw_api_object.set_location(accuracy_location)

        accuracy_value = current_unit["accuracy"].value
        accuracy_raw_api_object.add_raw_member("accuracy",
                                               accuracy_value,
                                               "engine.util.accuracy.Accuracy")

        accuracy_dispersion = 0
        accuracy_raw_api_object.add_raw_member("accuracy_dispersion",
                                               accuracy_dispersion,
                                               "engine.util.accuracy.Accuracy")
        dropoff_type = dataset.nyan_api_objects["engine.util.dropoff_type.type.NoDropoff"]
        accuracy_raw_api_object.add_raw_member("dispersion_dropoff",
                                               dropoff_type,
                                               "engine.util.accuracy.Accuracy")

        allowed_types = [
            dataset.pregen_nyan_objects["util.game_entity_type.types.Building"].get_nyan_object(),
            dataset.pregen_nyan_objects["util.game_entity_type.types.Unit"].get_nyan_object()
        ]
        accuracy_raw_api_object.add_raw_member("target_types",
                                               allowed_types,
                                               "engine.util.accuracy.Accuracy")
        accuracy_raw_api_object.add_raw_member("blacklisted_entities",
                                               [],
                                               "engine.util.accuracy.Accuracy")

        line.add_raw_api_object(accuracy_raw_api_object)
        accuracy_forward_ref = ForwardRef(line, accuracy_name)
        ability_raw_api_object.add_raw_member("accuracy",
                                              [accuracy_forward_ref],
                                              "engine.ability.type.Projectile")

        # Target mode
        target_mode = dataset.nyan_api_objects["engine.util.target_mode.type.CurrentPosition"]
        ability_raw_api_object.add_raw_member("target_mode",
                                              target_mode,
                                              "engine.ability.type.Projectile")

        # Ingore types; buildings are ignored unless targeted
        ignore_forward_refs = [
            dataset.pregen_nyan_objects["util.game_entity_type.types.Building"].get_nyan_object()
        ]
        ability_raw_api_object.add_raw_member("ignored_types",
                                              ignore_forward_refs,
                                              "engine.ability.type.Projectile")
        ability_raw_api_object.add_raw_member("unignored_entities",
                                              [],
                                              "engine.ability.type.Projectile")

        line.add_raw_api_object(ability_raw_api_object)

        ability_forward_ref = ForwardRef(line, ability_raw_api_object.get_id())

        return ability_forward_ref