def get_attack_effects(line: GenieGameEntityGroup, location_ref: str, projectile: int = -1) -> list[ForwardRef]: """ Creates effects that are used for attacking (unit command: 7) :param line: Unit/Building line that gets the ability. :type line: ...dataformat.converter_object.ConverterObjectGroup :param location_ref: Reference to API object the effects are added to. :type location_ref: str :returns: The forward references for the effects. :rtype: list """ dataset = line.data if projectile != 1: current_unit = line.get_head_unit() else: projectile_id = line.get_head_unit( )["attack_projectile_secondary_unit_id"].get_value() current_unit = dataset.genie_units[projectile_id] effects = [] armor_lookup_dict = internal_name_lookups.get_armor_class_lookups( dataset.game_version) # FlatAttributeChangeDecrease effect_parent = "engine.effect.discrete.flat_attribute_change.FlatAttributeChange" attack_parent = "engine.effect.discrete.flat_attribute_change.type.FlatAttributeChangeDecrease" attacks = current_unit["attacks"].get_value() for attack in attacks.values(): armor_class = attack["type_id"].get_value() attack_amount = attack["amount"].get_value() class_name = armor_lookup_dict[armor_class] attack_ref = f"{location_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(line, location_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"{location_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) line.add_raw_api_object(attack_raw_api_object) attack_forward_ref = ForwardRef(line, attack_ref) effects.append(attack_forward_ref) # Fallback effect fallback_effect = dataset.pregen_nyan_objects[( "effect.discrete.flat_attribute_change." "fallback.AoE2AttackFallback")].get_nyan_object() effects.append(fallback_effect) return effects
def get_attack_resistances(line: GenieGameEntityGroup, ability_ref: str) -> list[ForwardRef]: """ Creates resistances that are used for attacking (unit command: 7) :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 = line.get_head_unit() dataset = line.data armor_lookup_dict = internal_name_lookups.get_armor_class_lookups( dataset.game_version) resistances = [] # FlatAttributeChangeDecrease resistance_parent = "engine.resistance.discrete.flat_attribute_change.FlatAttributeChange" armor_parent = "engine.resistance.discrete.flat_attribute_change.type.FlatAttributeChangeDecrease" if current_unit.has_member("armors"): armors = current_unit["armors"].get_value() else: # TODO: Trees and blast defense armors = {} for armor in armors.values(): armor_class = armor["type_id"].get_value() armor_amount = armor["amount"].get_value() class_name = armor_lookup_dict[armor_class] armor_ref = f"{ability_ref}.{class_name}" armor_raw_api_object = RawAPIObject(armor_ref, class_name, dataset.nyan_api_objects) armor_raw_api_object.add_raw_parent(armor_parent) armor_location = ForwardRef(line, ability_ref) armor_raw_api_object.set_location(armor_location) # Type type_ref = f"util.attribute_change_type.types.{class_name}" change_type = dataset.pregen_nyan_objects[ type_ref].get_nyan_object() armor_raw_api_object.add_raw_member("type", change_type, resistance_parent) # Block value # ================================================================================= amount_name = f"{ability_ref}.{class_name}.BlockAmount" amount_raw_api_object = RawAPIObject(amount_name, "BlockAmount", dataset.nyan_api_objects) amount_raw_api_object.add_raw_parent( "engine.util.attribute.AttributeAmount") amount_location = ForwardRef(line, armor_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", armor_amount, "engine.util.attribute.AttributeAmount") line.add_raw_api_object(amount_raw_api_object) # ================================================================================= amount_forward_ref = ForwardRef(line, amount_name) armor_raw_api_object.add_raw_member("block_value", amount_forward_ref, resistance_parent) line.add_raw_api_object(armor_raw_api_object) armor_forward_ref = ForwardRef(line, armor_ref) resistances.append(armor_forward_ref) # Fallback effect fallback_effect = dataset.pregen_nyan_objects[( "resistance.discrete.flat_attribute_change." "fallback.AoE2AttackFallback")].get_nyan_object() resistances.append(fallback_effect) return resistances
def get_convert_effects(line: GenieGameEntityGroup, location_ref: str) -> list[ForwardRef]: """ Creates effects that are used for conversion (unit command: 104) :param line: Unit/Building line that gets the ability. :type line: ...dataformat.converter_object.ConverterObjectGroup :param location_ref: Reference to API object the effects are added to. :type location_ref: str :returns: The forward references for the effects. :rtype: list """ current_unit = line.get_head_unit() dataset = line.data effects = [] effect_parent = "engine.effect.discrete.convert.Convert" convert_parent = "engine.effect.discrete.convert.type.AoE2Convert" unit_commands = current_unit["unit_commands"].get_value() for command in unit_commands: # Find the Heal command. type_id = command["type"].get_value() if type_id == 104: skip_guaranteed_rounds = -1 * command["work_value1"].get_value( ) skip_protected_rounds = -1 * command["work_value2"].get_value() break else: # Return the empty set return effects # Unit conversion convert_ref = f"{location_ref}.ConvertUnitEffect" convert_raw_api_object = RawAPIObject(convert_ref, "ConvertUnitEffect", dataset.nyan_api_objects) convert_raw_api_object.add_raw_parent(convert_parent) convert_location = ForwardRef(line, location_ref) convert_raw_api_object.set_location(convert_location) # Type type_ref = "util.convert_type.types.UnitConvert" change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object() convert_raw_api_object.add_raw_member("type", change_type, effect_parent) # Min success (optional; not added because there is none in AoE2) # Max success (optional; not added because there is none in AoE2) # Chance # hardcoded resource chance_success = dataset.genie_civs[0]["resources"][182].get_value( ) / 100 convert_raw_api_object.add_raw_member("chance_success", chance_success, effect_parent) # Fail cost (optional; not added because there is none in AoE2) # Guaranteed rounds skip convert_raw_api_object.add_raw_member("skip_guaranteed_rounds", skip_guaranteed_rounds, convert_parent) # Protected rounds skip convert_raw_api_object.add_raw_member("skip_protected_rounds", skip_protected_rounds, convert_parent) line.add_raw_api_object(convert_raw_api_object) attack_forward_ref = ForwardRef(line, convert_ref) effects.append(attack_forward_ref) # Building conversion convert_ref = f"{location_ref}.ConvertBuildingEffect" convert_raw_api_object = RawAPIObject(convert_ref, "ConvertBuildingUnitEffect", dataset.nyan_api_objects) convert_raw_api_object.add_raw_parent(convert_parent) convert_location = ForwardRef(line, location_ref) convert_raw_api_object.set_location(convert_location) # Type type_ref = "util.convert_type.types.BuildingConvert" change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object() convert_raw_api_object.add_raw_member("type", change_type, effect_parent) # Min success (optional; not added because there is none in AoE2) # Max success (optional; not added because there is none in AoE2) # Chance # hardcoded resource chance_success = dataset.genie_civs[0]["resources"][182].get_value( ) / 100 convert_raw_api_object.add_raw_member("chance_success", chance_success, effect_parent) # Fail cost (optional; not added because there is none in AoE2) # Guaranteed rounds skip convert_raw_api_object.add_raw_member("skip_guaranteed_rounds", 0, convert_parent) # Protected rounds skip convert_raw_api_object.add_raw_member("skip_protected_rounds", 0, convert_parent) line.add_raw_api_object(convert_raw_api_object) attack_forward_ref = ForwardRef(line, convert_ref) effects.append(attack_forward_ref) return effects
def get_heal_effects(line: GenieGameEntityGroup, location_ref: str) -> list[ForwardRef]: """ Creates effects that are used for healing (unit command: 105) :param line: Unit/Building line that gets the ability. :type line: ...dataformat.converter_object.ConverterObjectGroup :param location_ref: Reference to API object the effects are added to. :type location_ref: str :returns: The forward references for the effects. :rtype: list """ current_unit = line.get_head_unit() dataset = line.data effects = [] effect_parent = "engine.effect.continuous.flat_attribute_change.FlatAttributeChange" heal_parent = "engine.effect.continuous.flat_attribute_change.type.FlatAttributeChangeIncrease" unit_commands = current_unit["unit_commands"].get_value() heal_command = None for command in unit_commands: # Find the Heal command. type_id = command["type"].get_value() if type_id == 105: heal_command = command break else: # Return the empty set return effects heal_rate = heal_command["work_value1"].get_value() heal_ref = f"{location_ref}.HealEffect" heal_raw_api_object = RawAPIObject(heal_ref, "HealEffect", dataset.nyan_api_objects) heal_raw_api_object.add_raw_parent(heal_parent) heal_location = ForwardRef(line, location_ref) heal_raw_api_object.set_location(heal_location) # Type type_ref = "util.attribute_change_type.types.Heal" change_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object() heal_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_heal.AoE2MinChangeAmount")].get_nyan_object() heal_raw_api_object.add_raw_member("min_change_rate", min_value, effect_parent) # Max value (optional; not added because there is none in AoE2) # Change rate # ================================================================================= rate_name = f"{location_ref}.HealEffect.ChangeRate" rate_raw_api_object = RawAPIObject(rate_name, "ChangeRate", dataset.nyan_api_objects) rate_raw_api_object.add_raw_parent( "engine.util.attribute.AttributeRate") rate_location = ForwardRef(line, heal_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", heal_rate, "engine.util.attribute.AttributeRate") line.add_raw_api_object(rate_raw_api_object) # ================================================================================= rate_forward_ref = ForwardRef(line, rate_name) heal_raw_api_object.add_raw_member("change_rate", rate_forward_ref, effect_parent) # Ignore protection heal_raw_api_object.add_raw_member("ignore_protection", [], effect_parent) line.add_raw_api_object(heal_raw_api_object) heal_forward_ref = ForwardRef(line, heal_ref) effects.append(heal_forward_ref) return effects
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
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
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
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
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
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
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]