Openage Converter Progress Report

In this thread, I will post updates about the implementation status of the openage converter. What the converter is all about can be read in the Github PR. I don’t know how much into I’ll go into detail with each post, but just let’s see how it goes.

What’s missing?


  • Parser for .dat files
    • AoE1 (original)


  • Diffing with other versions/mods (e.g. AoE2 with a misc mod)
  • Abilities (6/55)
    • Apply Effect group: -
    • Configuration group: Cloak, DetectCloak
    • Container group: -
    • Control properties group: -
    • Movement group: Fly
    • Production group: -
    • Resource group: RegenerateResourceSpot, RegenerateAttribute
    • State machine group: -
    • Trading group: ExchangeResources
  • Modifiers


  • Split game into multiple modpacks (base, graphics, sounds, translation, extra)
  • File size optimization

Hitbox is now implemented which is rather unremarkable, since that just takes the values from the original game.

        Hitbox.hitbox = ArcherHitbox

            Hitbox.radius_x = 0.2f
            Hitbox.radius_y = 0.2f
            Hitbox.radius_z = 2.0f

(Archer Hitbox)

ProductionQueue is more interesting as that is something that is non-existent in AoE1 (original) and hardcoded in AoE2. For AoE2 we give this ability to every building that creates at least one unit and set the queue size to 14 (the queue size of AoE2 is 15, but we subtract the unit that’s in production). Techs cannot be queued in AoC, so I didn’t add them to the production modes.

        ProductionQueue.size = 14
        ProductionQueue.production_modes = {CreatablesMode}

            Creatables.exclude = {}

(Barracks production queue)

We need more progress posts like this. In this way we can what’s going on and what’s not. Use the chat as well whatever you are doing. Like what stuffs you coded, plans, etc.

game is 2d if I remember? What’s Z gonna do? Future 3d measures?

The sprites are 2D, but the Hitbox is 3D (= a square or a cylinder)

How can it help in 2d games like AoE2?

It is necessary for hit and collision detection. The units always have a 3D-coordinate in the world, so the hitbox is just an offset area from this coordinate where they can get hit.

I have now implemented the Projectile and ShootProjectile abilities. The latter enables a unit to spawn a projectile (another GameEntity object). ShootProjectile is not that complex since it just contains a bunch of configuration values for the spawn area. The damaging ability is also stored in the projectile, not the shooting unit, so the mechanic can be best described as “fire-and-forget”.

The spawned projectile - which is now also able to be generated by the converter - gets the Projectile ability as configuration. For example, this ability stores the accuracy and the arc for targeting. The projectile entity will also get abilities for attack (ApplyDiscreteEffect) and despawning later on when I implement them.

        Projectile.arc = -3
        Projectile.accuracy = {Accuracy}
        Projectile.target_mode = CurrentPosition
        Projectile.ignored_types = {}
        Projectile.unignored_entities = {}

            Accuracy.accuracy = 80.0f
            Accuracy.accuracy_dispersion = 0.33f
            Accuracy.dispersion_dropoff = InverseLinear
            Accuracy.target_types = {Building, Unit}
            Accuracy.blacklisted_entities = {}

(Projectile ability of the archer’s arrow)


What are the other stuffs going to be added and how they will be handled?

New abilities for resource were added yesterday and today. I implemented the conversion for Harvestable, DropSite and DropResources. We are now at a point where only specific units receive these abilities, so some additional checks on units were required. For example, a unit must have at least one resource storage in AoE2 to get the Harvestable ability during the conversion. So far these checks are relatively simple, but converting the various Gather abilities of the villagers will probably be more challenging.

    Harvestable.resources = FoodResourceSpot
    Harvestable.harvest_progress = {}
    Harvestable.restock_progress = {}
    Harvestable.gatherer_limit = inf
    Harvestable.harvestable_by_default = True

        ResourceSpot.resource = Food
        ResourceSpot.max_amount = 125
        ResourceSpot.starting_amount = 125
        ResourceSpot.decay_rate = 0.0f

(Harvestable ability of the berry bush)

I also took some time to clean up the code base and remove redundancy. The Python objects for unit and building lines have received a common super class because many functions were just the same. Additionally, the converter now converts ambient objects like trees, gold/stone mines or cactuses (or cacti?) that were previously ignored. The only really important objects missing are relics and cliffs, but that’s something for next time.

how can I get python side only working? Some guides on that?

The converter is already Python-only. It can be used with the ./run convert command.

So I will need to do all those C++ building process? No way to avoid those? Cython is still needed?

It’s recommended, although I think it would work without the Cython stuff if you convert it with the --no-media flag.

Next up was Gather or the multiple instances of the ability, respectively. This turned out to be easier than I thought. Most of the members like gather_rate can be easily mapped 1-to1. The only complex thing to figure out is linking the resource spot objects. For that purpose, the converter now searches the unit commands for the class and unit IDs of the gather targets. Then it searches all unit lines and checks if they are a) harvestable and b) match one of the class or unit IDs. Once we have figured out the targeted groups, the resource spots can be easily addressed with ExpectedPointer instances.

        Gather.auto_resume = True
        Gather.resume_search_range = inf
        Gather.targets = {SheepResourceSpot, TurkeyResourceSpot}
        Gather.carry_capacity = 10
        Gather.gather_rate = GatherRate
        Gather.carry_progress = {}

            ResourceRate.type = Food
            ResourceRate.rate = 0.33f

(Gathering sheep/turkey meat with the villager)

1 Like

Progress has been good over the last days, with 11 more abilities done (3 from the Resource group, 8 from the storage group -> see the next post).

The last one’s from the resource group were Restock, which is only used for farms, as well as Herd and Herdable. Restock is assigned to the villager unit as ReseedFarm and uses the same values as for building the farm as AoE2 does not make a distinction between building and restocking. I even assigned the Cost object from the farm’s creatable to the restocking cost, so that the cost for restocking does not have to be upgraded separately. However, I noticed that we need a new modifier for the restocking cost in the API. That will be added in the next revision.

    Restock.auto_restock = True = FarmResourceSpot
    Restock.restock_time = 15.0f
    Restock.manual_cost = FarmCost
    Restock.auto_cost = FarmCost
    Restock.amount = 175

(Restocking farms with the villager)

There’s not much to say about Herd and Herdable since those mirror exactly what they do in AoE2: Changing the ownership of sheep when a unit comes in range. Herdable is given to any line in the livestock class (ID 58), Herd to any line that is not an animal or a trebuchet.

    Herd.range = 3.0f
    Herd.strength = 0
    Herd.allowed_types = {Herdable}
    Herd.blacklisted_game_entities = {}

(Herding for every unit in the game)


After dealing with the leftover resource abilities I finally had time to deal with garrisoning. The challenge here is there’s not one unique AoE2 garrison system, but rather a mix of 4 systems that work ever so slightly different from each other. There are

  • "Real" garrisons, i.e. buildings that units can go in and out
  • Production garrisons where units can only be produced into the building, but can only exit and never enter again
  • Unit garrisons (e.g. the ram/transport ships) which also can have a speed boost effect for certain garrisoned units for the ram
  • Inventories (= monks with relics) which are technically not garrisons, but they work the same way as garrisons in openage

So to solve this conundrum, I implemented a method that can determine which type of garrison a unit has. I also created a (rather clunky) function in the processor that – using the mentioned method – creates two lists for every line to store

  1. what entities this line can garrison
  2. where this line can garrison

This is useful because I can just iterate over these lists when assigning the abilities, instead of having to search for the matching garrisons/garrisoned units every time.

The rest is pretty straight forward: Depending on the type of garrison, the converter decides which of the abilities Storage, TransferStorage, RemoveStorage, CollectStorage should be assigned. Similarly, the units which can be garrisoned can have EnterContainer or ExitContainer configured depending on the type of their possible garrisons.

    EnterContainer.allowed_containers = {TransportShipContainer, RamContainer, TownCenterContainer, TowerContainer, CastleContainer, BombardTowerContainer}
    EnterContainer.allowed_types = {Unit, Building}
    EnterContainer.blacklisted_game_entities = {}

(The ram’s EnterContainer ability which I just noticed allows the ram to enter itself. That should be fixed soon :smile: )


Is there different versions of converter going on? Like default still exists and need to run specific command to get nyan version working?

It’s all in this PR:

The converter in there only outputs the new modpack format.

3 posts were split to a new topic: Building new converter fails