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
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
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
def get_repair_effects(line: GenieGameEntityGroup, location_ref: str) -> list[ForwardRef]: """ Creates effects that are used for repairing (unit command: 106) :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 api_objects = dataset.nyan_api_objects name_lookup_dict = internal_name_lookups.get_entity_lookups( dataset.game_version) effects = [] effect_parent = "engine.effect.continuous.flat_attribute_change.FlatAttributeChange" repair_parent = "engine.effect.continuous.flat_attribute_change.type.FlatAttributeChangeIncrease" repairable_lines = [] repairable_lines.extend(dataset.building_lines.values()) for unit_line in dataset.unit_lines.values(): if unit_line.is_repairable(): repairable_lines.append(unit_line) for repairable_line in repairable_lines: game_entity_name = name_lookup_dict[ repairable_line.get_head_unit_id()][0] repair_name = f"{game_entity_name}RepairEffect" repair_ref = f"{location_ref}.{repair_name}" repair_raw_api_object = RawAPIObject(repair_ref, repair_name, dataset.nyan_api_objects) repair_raw_api_object.add_raw_parent(repair_parent) repair_location = ForwardRef(line, location_ref) repair_raw_api_object.set_location(repair_location) line.add_raw_api_object(repair_raw_api_object) # Type type_ref = f"util.attribute_change_type.types.{game_entity_name}Repair" change_type = dataset.pregen_nyan_objects[ type_ref].get_nyan_object() repair_raw_api_object.add_raw_member("type", change_type, effect_parent) # Min value (optional; not added because buildings don't block repairing) # Max value (optional; not added because there is none in AoE2) # Change rate # ================================================================================= rate_name = f"{location_ref}.{repair_name}.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, repair_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") # Hardcoded repair rate: # - Buildings: 750 HP/min = 12.5 HP/s # - Ships/Siege: 187.5 HP/min = 3.125 HP/s if isinstance(repairable_line, GenieBuildingLineGroup): repair_rate = 12.5 else: repair_rate = 3.125 rate_raw_api_object.add_raw_member( "rate", repair_rate, "engine.util.attribute.AttributeRate") line.add_raw_api_object(rate_raw_api_object) # ================================================================================= rate_forward_ref = ForwardRef(line, rate_name) repair_raw_api_object.add_raw_member("change_rate", rate_forward_ref, effect_parent) # Ignore protection repair_raw_api_object.add_raw_member("ignore_protection", [], effect_parent) # Repair cost property_ref = f"{repair_ref}.Cost" property_raw_api_object = RawAPIObject(property_ref, "Cost", dataset.nyan_api_objects) property_raw_api_object.add_raw_parent( "engine.effect.property.type.Cost") property_location = ForwardRef(line, repair_ref) property_raw_api_object.set_location(property_location) line.add_raw_api_object(property_raw_api_object) cost_ref = f"{game_entity_name}.CreatableGameEntity.{game_entity_name}RepairCost" cost_forward_ref = ForwardRef(repairable_line, cost_ref) property_raw_api_object.add_raw_member( "cost", cost_forward_ref, "engine.effect.property.type.Cost") property_forward_ref = ForwardRef(line, property_ref) properties = { api_objects["engine.effect.property.type.Cost"]: property_forward_ref } repair_raw_api_object.add_raw_member("properties", properties, "engine.effect.Effect") repair_forward_ref = ForwardRef(line, repair_ref) effects.append(repair_forward_ref) return effects
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_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 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_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
def get_convert_resistances(line: GenieGameEntityGroup, ability_ref: str) -> list[ForwardRef]: """ Creates resistances that are used for conversion (unit command: 104) :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 """ dataset = line.data resistances = [] # AoE2Convert resistance_parent = "engine.resistance.discrete.convert.Convert" convert_parent = "engine.resistance.discrete.convert.type.AoE2Convert" resistance_ref = f"{ability_ref}.Convert" resistance_raw_api_object = RawAPIObject(resistance_ref, "Convert", dataset.nyan_api_objects) resistance_raw_api_object.add_raw_parent(convert_parent) resistance_location = ForwardRef(line, ability_ref) resistance_raw_api_object.set_location(resistance_location) # Type if isinstance(line, GenieUnitLineGroup): type_ref = "util.convert_type.types.UnitConvert" else: type_ref = "util.convert_type.types.BuildingConvert" convert_type = dataset.pregen_nyan_objects[type_ref].get_nyan_object() resistance_raw_api_object.add_raw_member("type", convert_type, resistance_parent) # Chance resist # hardcoded resource chance_resist = dataset.genie_civs[0]["resources"][77].get_value( ) / 100 resistance_raw_api_object.add_raw_member("chance_resist", chance_resist, resistance_parent) if isinstance(line, GenieUnitLineGroup): guaranteed_rounds = dataset.genie_civs[0]["resources"][ 178].get_value() protected_rounds = dataset.genie_civs[0]["resources"][ 179].get_value() else: guaranteed_rounds = dataset.genie_civs[0]["resources"][ 180].get_value() protected_rounds = dataset.genie_civs[0]["resources"][ 181].get_value() # Guaranteed rounds resistance_raw_api_object.add_raw_member("guaranteed_resist_rounds", guaranteed_rounds, convert_parent) # Protected rounds resistance_raw_api_object.add_raw_member("protected_rounds", protected_rounds, convert_parent) # Protection recharge resistance_raw_api_object.add_raw_member( "protection_round_recharge_time", 0.0, convert_parent) line.add_raw_api_object(resistance_raw_api_object) resistance_forward_ref = ForwardRef(line, resistance_ref) resistances.append(resistance_forward_ref) return resistances
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 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
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)
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]
def get_construct_effects(line: GenieGameEntityGroup, location_ref: str) -> list[ForwardRef]: """ Creates effects that are used for construction (unit command: 101) :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 name_lookup_dict = internal_name_lookups.get_entity_lookups( dataset.game_version) effects = [] progress_effect_parent = "engine.effect.continuous.time_relative_progress.TimeRelativeProgressChange" progress_construct_parent = "engine.effect.continuous.time_relative_progress.type.TimeRelativeProgressIncrease" attr_effect_parent = "engine.effect.continuous.time_relative_attribute.TimeRelativeAttributeChange" attr_construct_parent = "engine.effect.continuous.time_relative_attribute.type.TimeRelativeAttributeIncrease" constructable_lines = [] constructable_lines.extend(dataset.building_lines.values()) for constructable_line in constructable_lines: game_entity_name = name_lookup_dict[ constructable_line.get_head_unit_id()][0] # Construction progress contruct_progress_name = f"{game_entity_name}ConstructProgressEffect" contruct_progress_ref = f"{location_ref}.{contruct_progress_name}" contruct_progress_raw_api_object = RawAPIObject( contruct_progress_ref, contruct_progress_name, dataset.nyan_api_objects) contruct_progress_raw_api_object.add_raw_parent( progress_construct_parent) contruct_progress_location = ForwardRef(line, location_ref) contruct_progress_raw_api_object.set_location( contruct_progress_location) # Type type_ref = f"util.construct_type.types.{game_entity_name}Construct" change_type = dataset.pregen_nyan_objects[ type_ref].get_nyan_object() contruct_progress_raw_api_object.add_raw_member( "type", change_type, progress_effect_parent) # Total change time change_time = constructable_line.get_head_unit( )["creation_time"].get_value() contruct_progress_raw_api_object.add_raw_member( "total_change_time", change_time, progress_effect_parent) line.add_raw_api_object(contruct_progress_raw_api_object) contruct_progress_forward_ref = ForwardRef(line, contruct_progress_ref) effects.append(contruct_progress_forward_ref) # HP increase during construction contruct_hp_name = f"{game_entity_name}ConstructHPEffect" contruct_hp_ref = f"{location_ref}.{contruct_hp_name}" contruct_hp_raw_api_object = RawAPIObject(contruct_hp_ref, contruct_hp_name, dataset.nyan_api_objects) contruct_hp_raw_api_object.add_raw_parent(attr_construct_parent) contruct_hp_location = ForwardRef(line, location_ref) contruct_hp_raw_api_object.set_location(contruct_hp_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() contruct_hp_raw_api_object.add_raw_member("type", change_type, attr_effect_parent) # Total change time change_time = constructable_line.get_head_unit( )["creation_time"].get_value() contruct_hp_raw_api_object.add_raw_member("total_change_time", change_time, attr_effect_parent) # Ignore protection contruct_hp_raw_api_object.add_raw_member("ignore_protection", [], attr_effect_parent) line.add_raw_api_object(contruct_hp_raw_api_object) contruct_hp_forward_ref = ForwardRef(line, contruct_hp_ref) effects.append(contruct_hp_forward_ref) 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 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 get_heal_resistances(line: GenieGameEntityGroup, ability_ref: str) -> list[ForwardRef]: """ Creates resistances that are used for healing (unit command: 105) :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 """ dataset = line.data resistances = [] resistance_parent = "engine.resistance.continuous.flat_attribute_change.FlatAttributeChange" heal_parent = "engine.resistance.continuous.flat_attribute_change.type.FlatAttributeChangeIncrease" resistance_ref = f"{ability_ref}.Heal" resistance_raw_api_object = RawAPIObject(resistance_ref, "Heal", dataset.nyan_api_objects) resistance_raw_api_object.add_raw_parent(heal_parent) resistance_location = ForwardRef(line, ability_ref) resistance_raw_api_object.set_location(resistance_location) # Type type_ref = "util.attribute_change_type.types.Heal" 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}.Heal.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) line.add_raw_api_object(resistance_raw_api_object) resistance_forward_ref = ForwardRef(line, resistance_ref) resistances.append(resistance_forward_ref) return resistances
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 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
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)