Building the maps for Regicide Rumble 3

Last weekend, Regicide Rumble 3 happened. Regicide Rumble 3 (abbreviated RR3) was an Age of Empires 2 event organized and casted by T90Official on Twitch that had players of different skill levels compete for some prize money over three days in Regicide games1 of Age of Empires 2.

In this blog post, we will not focus too much on the (amazing) event, but on how we built the maps that have been used in that event.

The Task

About one month ago T90Official gathered a bunch of channel moderators and map makers and laid out his plans for the event: There should be multiple games over three days on different maps. Since the focus should be on Regicide, anything that makes sniping Kings easier should be done. That included removing HP bonuses and technologies for buildings that could protect the King, as well as disabling alternative win conditions like Relic or Wonder victory.

In the Regicide Rumble 1 and 2 events, there have been matches that took multiple hours to finish and that blew up the planned schedule for the games. To prevent this from happening again this time, a weakened version of a Battle Royale storm should push out players from the edges of the maps towards the center after a certain amount of time.

Battle Royale in Age of Empires 2 is not a novel idea, a few maps have been created already (mainly by HenkDeSuperNerd) that contain an area effect which damages units and buildings and grows over time, forcing players towards the middle of the map. We will not go into the details of the actual mechanism that creates the “storm” in this article – the relavant part is that the mechanic is based on chains of decaying animals which are placed around the edges of the map. Once the initial animal fully decays, it spawns a loop of other units that repeatedly cause blast damage to an area around them. Having all the edges full of these animals with different decay times creates “storm zones” that get activated one after another.

Slide 1
You can clearly see the storm zones in this screenshot of the Battle Royale Land Nomad map.

Those existing Battle Royale map scripts are highly specialized and a lot of work went into their creation. Creating and testing ten new map scripts with a similar but weaker version of the storm within a month and out of thin air2 would be totally unfeasible, as much was clear from the beginning.

Now we had to decide between two alternatives: Create a generic snippet that we can add to an existing random map script which turns it into a Battle Royale like map (with our custom weak storm). Or create ZR maps and place the animals that spawn the storm directly in the scenario file.

Excursus: ZR maps

Regular random map scripts are just text – in a strange markup format, yes, but still just text. They define everything about how the map shall look in vague generic terms. Their goal is to procedurally generate maps that look similar, but not the same.

ZR maps are different: They usually contain a scenario file that exactly defines the positions of terrains, possibly some other objects, and the starting positions of players, and a slimmed down random map script that defines all the rest in the usual vague terms, bringing variation onto the scenario. Unlike in regular scenarios, Triggers are ignored in ZR maps, so we can’t do shenanigans with those unfortunately.

Automation Is King3

The first option we had was tempting: The grandmaster of Age of Empires 2 Battle Royale maps, HenkDeSuperNerd, already had a snippet ready that could supposedly add the Battle Royale ring of decaying animals around any map. It was largely untested though and the effects it could have on the map generation were not investigated yet.

In contrast, the mechanics of ZR maps are well understood and controllable. For this reason, we went with the second option, which seemed to be the safer one regarding the time frame of less than a month.

Our process looked as follows:

  1. Select maps
  2. Adapt maps (like removing some spawn conditions for certain resources or removing animals)
  3. Add features: Custom regicide, Nerfs (for defensive buildings and civilisations), Storm constants
  4. Split script into Scenario and RMS part (one script to generate the scenario file from, one script to put into the final ZR map)
  5. Generate scenario file
  6. Add storm to scenario
  7. Create ZR map from scenario and RMS part

Having to do that for ten or more maps is tedious work. Also, at the time we did not even know which maps would be selected in the end. Without automation, that would have been a few unhappy hours of monotonous error-prone work for a few of us. And if we wanted to change something, we would have to do many of the steps again.

We had to change a lot of things. More on that in a bit.

Going through the above steps with each new iteration would have been an absolute pain. Luckily, we were able to automate some steps:

Building the maps for Regicide Rumble 3
M: manual work, A: automatable

I created a python script that automatically patched the code for our desired custom features into the maps that we had selected and adapted manually beforehand. The script also separated the sections of the rms that were needed for the scenario generation from the sections that were to be put into the ZR map.

The scenario generation had to be done by hand unfortunately. Since the scenario files are static and we desire fair starting positions for all players, a manual review would be needed anyway before continuing. Luckily, the rms part for generating scenarios does usually not change at all while testing custom map features, so we could generate those once and then use them again and again in the final steps.

Adding the storm spawners to the scenarios was a bit tricky. Our first approach was to manually “paint” a zone of terrain around the generated scenarios on which the map script would then place the storm spawners, like it is done in the existing Battle Royale maps. That would have meant monotonous repetetive work.

Luckily though, we had a breakthrough after a few days: I was able to use the agescx python library to modify the generated scenario programmatically! What would have taken at least multiple minutes per map before was now a matter of seconds. And not only that: We were suddenly able to place arbitrary objects at arbitrary positions on the map. Something that can hardly be achieved in random map scripts alone. We basically had full control. I wrote a second python script that created copies of our scenarios and modified them to our specifications. Mainly: placing the storm spawners precisely at the edges, with a specific distance to each other.

The last part, combining the scenario and the RMS file, is also easily automated, since ZR maps are just zip files without compression that contain the scenario file and the rms file.

We finally had a semi-automated pipeline in which we could tweak a script and and produce 17 new versions of our maps within seconds. Now it was just a matter of making it all work.

It Would Be A Lot Of Fun, Were It Not For The Players

During testing on community games friday, we found out that the features we initially used had some issues. For example we tried to disable Relic victory by spawning in the Relics after the game start, by placing Priests with 0 HP that hold a relic. When the priest dies (which he does immediately), he drops the relic, and it looks like it has been there all along. Turns out that Burmese can see the locations of the relics on the map, and they converted the dead priests while they died. Due to a bug in the game, this led to the dead priests taking up population space for the Burmese player, who had to build a few extra houses at the start of the game in order to produce own units. A similar thing happened when the storm was active and players converted some of the Gaia units on the edge which we used to deal the damage. The dead units took up population space, and CaptureAge showed us the discrepancy between units and used up population space clear as day, making us wonder if we were suddenly unable to do simple maths.

The relic issue was simply fixed by disabling Relic victory in a differenty way: By just placing 504 relics into an inaccessible corner. Can’t get all relics if you can’t get to all relics! The storm however we had to revamp multiple times until we had a conversion resistant version that worked reliably.

Tweaks, Tweaks, Tweaks

To understand why the final maps look like they look, we have to dive into the issues we had along the way. Our biggest concern has always been players messing with the storm mechanic. They could disable or speed up storm spawners, intentionally or unintentionally, or break a whole lot of other things. So we put a ring on it. Or rather around it. To prevent players from accessing the storm spawners with units.

Building the maps for Regicide Rumble 3
An early iteration. The jesus deer are storm spawners, the rocks prevent players from accessing the deer.

The first iteration of our storm had six waves, but did unfortunately not reliably damage units. So we switched up the storm units a bit and got it to do damage reliably, but only with three waves.

This also brought a new issue: Players were suddenly able to convert one of the gaia towers (wtf) used in the storm cycle. This led to deforestation on the map and annoying “Tower built!” noises and chat spam, making it impossible for the unfortunate player to read chat messages from the other players. That would be deadly in a game mode where communication with the players to plot against others is essential for success.

Our first attempt to solve this new issue was to move the barrier further away from the storm spawners, so that players could not get close enough to convert them anymore. Taking away more space from the already crammed map would shrink it way to much, so instead we increased the map size by ten tiles in each direction and moved the storm spawners outwards, effectively turning a “Large”map (220×220 tiles) into a “Giant” map (240×240 tiles). The resulting empty space was filled with deep water (to prevent resources like gold, stone, wood, or relics from spawning there) and stone heads on top (just for looks). I also had to fix “holes” in the map that resulted from moving the whole map down and to the right by ten tiles after increasing the map size. Looking at those “holes” for more than one second would reliably crash the game – not a good thing in general, and especially if you want to stream to an audience of 2000+ people around the world. The fix led to hills in the water, but it worked. Good enough for me!

Building the maps for Regicide Rumble 3
The water has hills, but at least the hills don’t have eyes.
The flying panthers are part of the storm spawners.

Now that we had that “dead space” around the outside, we could also have some fun with it, and write the name of the event with haystacks or something silly like that. We also placed three rings of torches on the map that indicated how far each “storm wave” would reach, since the storm currently lacks a visual indicator (apart from slowly killing units I guess).

Sadly, ten tiles is not enough to prevent for example a Cannon Galleon with a line of sight of 15 or a Trebuchet with a line of sight of 18 from converting those damn towers. In the end, we managed to fix the issue by replacing the bear in the damage cycle with a macaw. Nobody knows exactly why that worked, but it worked! Good enough for me.

The last week before the event was spent fixing small issues in the map scripts (our step 2), like resources not spawning in the center island on Migration, and adding a large patch of wood in the center of the scenario files of Grand Bara, Chaos Pit, Steppe, and King of Kings, to prevent players from running out of wood if the game goes long. We even managed to fit Pilgrims with a custom storm layout that only storms from two of the four sides, and has storm spawners on the eight starting islands in the corner of the map.

The morning before the start of the event, somebody noticed that players start with five out of five population after we had removed the spawns of extra villagers and houses the night before. This meant that players cannot start creating villagers right away, but would have to build a house first – a big deviation from the usual build order at the start of the game. That issue could luckily be fixed manually by replacing the scout with a horse, which does not take up population space, bringing it down to four out of five with room for one more. That was a (preventable) close call, but it worked out in the end.

Thank You

Before we go into what could be done better I would like to thank the whole team of Regicide Rumble 3. It was a pleasure working with so many motivated people of different skill levels across time zones. It’s quite funny when you wake up at eight in the morning to a new storm mechanic suggestion and reply with “I will try that out after work in about 10-12 hours, thanks”. Together, we did things I think had never been done before. Certainly, somebody will prove me wrong about that in the comments.

Light And Shadow

I used a git repository on GitHub for managing the scripts that were used. Sadly, not many in the map making community are proficient with git, so we could not use that directly to collaborate. Also, the python scripts were probably not usable for non-programmers. Making the whole process more accessible for non-programmers would be desirable.

During the whole time, I was the only one to have access to the unpatched scenario files since they were in the non-versioned output folder. That turned out to bite us when we added the clumps of wood in the centers of some scenario files. Motivated team members edited what they had access to – the patched scenario files from the ZR maps. Those were then not suitable to use in the pipeline because they already contained the storm spawners and torches. The work and had to be redone with the plain scenario files. The unpatched scenarios should definitely have been versioned and published in the git repository.

Also, due to my shallow understanding of what to look for when generating the scenario files, some maps had generations that were let’s say not ideal in the end.

Finally, it would probably have been sufficient to only place the storm spawners and torches in the scenario files, since we got the storm pretty tamper-proof. No stone barriers needed, no extra large maps with pixelated text on the border necessary.

You live and you learn. The Kings are dead, long live the Snipers!

  1. Regicide is the game mode where you lose the game if your King unit dies. See also.
  2. not thin air exactly, since the base scripts are there already, but still a lot of work to be done
  3. An obvious pun to make when writing about Regicide.
  4. In this case, 50 was an arbitrarily selected “high enough” number.

Everything is Regicide – now with Treason

In Everything is Regicide with UserPatch 1.5 I described how to turn any random map script into a custom regicide map, with the help of UserPatch 1.5 and the guard_state command.

However, regicide is only half the fun without the Treason1 technology. For the mere cost of 400 gold, it briefly reveals the positions of all enemy kings. Treason can also be researched repeatedly, making it easier for you to hunt those kings down and eventually kill them, defeating the player instantly.

Treason is only available in regicide games, where it replaces the Spies technology2. Treason could hence not be used on the custom regicide maps above. The latest update of UserPatch 1.5 however allows us to manually enable the technology in our random map script, without the use of any mod!

Adding Treason to Custom Regicide Maps

First we need to define three constants at the top of the script (if they are not defined already):

#const GAIA_SET_PLAYER_DATA -10
#const DATA_MODE_FLAGS 1
#const ATTR_SET 0

Now we can mess with the availability of Treason and Spies in the <PLAYER_SETUP> section by adding an effect_amount command:

<PLAYER_SETUP>
  […]
  effect_amount GAIA_SET_PLAYER_DATA DATA_MODE_FLAGS ATTR_SET 1

The value 1 at the end enables the Treason technology, the value 2 disables the Spies technology, and the value 3 does both (so Treason is available while Spies is not).

I have of course updated all the custom King of the Hill Regicide maps over at Github to include both Treason and Spies: click me

Have fun!

Does it have to be a king?

The guard_state option not only works with kings, but with arbitrary units.

Supposedly, Treason also works with other units that a guard_state is set for. If there is a king, it is revealed, else one guard_state unit is revealed. This behaviour is not necessarily ideal, but without doubt better than before.

  1. (the medieval kind)
  2. For the mere price of 200 Gold per enemy villager (min. 200, max 30.000), Spies reveals the line of sight of all enemy players’ units.

Everything is Regicide with UserPatch 1.5

Regicide is a fun game mode in Age of Empires II where each player controls one1 king, and as soon as a player controls zero kings, the player drops out of the game.

King of the Hill is a fun game mode in Age of Empires II where there is an indestructible monument in the center of the map. When a player has at least one unit around the monument and all other players don’t, the player gets control of the monument. The first time this happens in a game, a countdown of 550 in-game years starts. A player who controls the monument when the countdown hits zero wins the game. When control of the monument switches and the countdown is below 100 in-game years, it is reset to 100 in-game years.

In regular gameplay, these gamemodes are mutually exclusive: You can either play Regicide and hunt kings (cruel, I know!), or you can play King of the Hill and desperately throw your armies into the middle. But you could not do both at the same time.

Now, with the endless possibilities that UserPatch 1.5 has brought upon us, you actually can. But how? Let us find out.

Random maps in Age of Empires II are generated using random map scripts. A random map script is a text file with the extension .rms that contains instructions on how to generate the terrain and distribute players, units and resources on a map. In addition to the maps that come with the original game, many interesting and fun random map scripts have been developed by the community.

Let’s look at what a map would have to do in order to provide Regicide or King of the Hill capabilities, respectively.

We can easily see that the mechanics of Regicide are rather easy to implement:

begin loop
    for each player
        if player does not control king
            player loses
        endif
    endfor
end loop

We need to put a king on the map (easy), and when a player has no king, the player must drop out (???).

The mechanics of King of the Hill are rather complicated in comparison:

begin loop
    if there are units around the monument
        if no countdown is running
            countdown = 550
            start countdown
            give control of the monument to a player who has units around the monument
        else
            if player who controls monument has no units around the monument
                give control of the monument to a player who has units around the monument
                if countdown < 100
                    countdown = 100
                endif
            endif
        endif
    endif
    if countdown == 0
        player who controls monument wins
    endif
end loop

We need to put a monument in the dead center of the map (not sure if we can hit the center, but approximately it’s definitely possible) and handle a countdown (???) as well as ownership changes (???).

From what we gathered so far, it looks way more practical to create a random map script which simulates a Regicide game, and to then use it with the King of the Hill game mode, where game itself handles all the monument placement and especially the gameplay logic for us.

UserPatch 1.5 introduces a new statement into Random Map Scripting: guard_state. It is a very powerful statement which can serve two functions at once: Generate a resource trickle and define a victory/defeat condition. This makes it a bit tricky to use.

Everything is Regicide with UserPatch 1.5

The reference text for guard_state from the UserPatch 1.5 beta documentation.

Our plan for creating a King of the Hill Regicide map is as follows:

  1. Take practically any old random map script
  2. Place a king unit, even if the game mode is not Regicide
  3. Use guard_state to make a player lose if the player does not control any king unit
  4. Maybe adjust the starting resources
  5. Play the map with the King of the Hill game mode
  6. Profit!

Let’s go through these step by step.

1. Take practically any old random map script

I take the WSVG Single Maps v2 by HJ1, because they contain many standard maps, but frankly, it does not really matter.

2. Place a king unit, even if the game mode is not Regicide

While in theory only the king is necessary, we usually want to recreate the Regicide look and feel, which includes additional villagers (no problem), a castle from the start (no problem) and different resources (welp… you can’t have everything.)

The random map script will probably contain a part like this in the <OBJECTS_GENERATION> section2:

/* SPECIAL STUFF FOR REGICIDE */

if REGICIDE
create_object VILLAGER
{
 number_of_objects 7
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object KING
{
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object CASTLE
{
 set_place_for_every_player
 min_distance_to_players 10
 max_distance_to_players 10
}

endif

This places additional villagers, a king, and a castle. We simply copy this part, but replace “if REGICIDE” with “if KING_OT_HILL”:

/* SPECIAL STUFF FOR REGICIDE */

if REGICIDE
(…)
endif

if KING_OT_HILL
create_object VILLAGER
{
 number_of_objects 7
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object KING
{
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object CASTLE
{
 set_place_for_every_player
 min_distance_to_players 10
 max_distance_to_players 10
}

endif

Note: This way, the map will only work with King of the Hill or Regicide game modes. If we want this map to be usable for any other game mode as well, we can instead simply remove the “if REGICIDE” and “endif” parts.

If you can’t find a regicide section in your random map script file, just copy it from this article and paste it into the <OBJECTS_GENERATION> section. If there is no such section, that’s very strange, but you can add that as well in that case.

Now that we get kings in King of the Hill, we can move on to the interesting part. Simply losing your king won’t kill you… yet.

3. Use guard_state to make a player lose if the player does not control any king unit

We need to tell guard_state to watch out for kings. Also, we need to set guard-flag-victory. We do not want to use the resource trickle functionality, but have to fill all parameters. Hence, we add the resource identifiers at the top of the file, before any section. In the <PLAYER SETUP> section, we add our guard_state rule with an arbitrary resource and a ResourceDelta of zero.

The top of our random map script file now looks like this3:

#const AMOUNT_FOOD 0
#const AMOUNT_WOOD 1
#const AMOUNT_STONE 2
#const AMOUNT_GOLD 3

<PLAYER_SETUP>
 random_placement
 guard_state KING AMOUNT_WOOD 0 1

Turns out that we are done. The King of the Hill Regicide game is ready to be played. But wait, there’s more!

(If there is no <PLAYER_SETUP> section in your random map script file, just add it. It might also not be at the top.)

4. Maybe adjust the starting resources

In regular games, each player starts with 200 wood, 200 food, 100 gold and 200 stone when using standard resources4. In Regicide games however, players start with 500 wood, 500 food, 0 gold and 150 stone. We can adjust for that by introducing further declarations at the top of our random map script as well as four effect_amount statements in the <PLAYER_SETUP> section, which adjust the starting resource values. The top of our file now looks like this:

#const AMOUNT_FOOD 0
#const AMOUNT_WOOD 1
#const AMOUNT_STONE 2
#const AMOUNT_GOLD 3
#const STARTING_FOOD 91
#const STARTING_WOOD 92
#const STARTING_STONE 93
#const STARTING_GOLD 94
#const MOD_RESOURCE 1
#const ATTR_SET 0


  random_placement
  guard_state KING AMOUNT_WOOD 0 1
  effect_amount MOD_RESOURCE STARTING_WOOD ATTR_SET 300
  effect_amount MOD_RESOURCE STARTING_FOOD ATTR_SET 300
  effect_amount MOD_RESOURCE STARTING_GOLD ATTR_SET -100
  effect_amount MOD_RESOURCE STARTING_STONE ATTR_SET -50

Don’t be fooled: It might say “ATTR_SET”, but the values are actually added to the initial values, they do not replace them.

5. Play the map with the King of the Hill game mode

We move the .rms file into the game folder for random map scripts, most likely C:/Program Files (x86)/Steam/steamapps/common/Age2HD/Random or C:/Program Files/Microsoft Games/Age of Empires II/Random.

We start up the game.

We create a new game with a custom map and select our random map script.

We start the game.

We see the monument in the middle.

We see our king.

We delete our king.

We lose.

We are happy that it works.

6. Profit!

Check out these maps that I already modified and threw on GitHub for your playing pleasure.

Everything is Regicide with UserPatch 1.5

This is a fake screenshot that I took with the HD edition because I wanted an image in this article, but did not want to reboot my computer in order to start up Voobly.

Drawbacks and fun ideas

King of the Hill Regicide games lack two main things one thing: The starting resources of a Regicide game, which are different from a standard random map game, and the Treason technology, which reveals the positions of all enemy kings on the map for a few seconds.

The “king” unit does not necessarily need to be a king. Basically any unit that the players can’t produce themselves will do5. Why not use Joan of Arc? Or even a building? You can do anything. Anything at all. The only limit is yourself.

TL;DR

Add this to your random map script in order to turn it into a King of the Hill Regicide map:

At the top:

#const AMOUNT_FOOD 0 
#const AMOUNT_WOOD 1 
#const AMOUNT_STONE 2 
#const AMOUNT_GOLD 3
#const STARTING_FOOD 91
#const STARTING_WOOD 92
#const STARTING_STONE 93
#const STARTING_GOLD 94
#const MOD_RESOURCE 1
#const ATTR_SET 0

In the <PLAYER_SETUP> section:

guard_state KING AMOUNT_WOOD 0 1
effect_amount MOD_RESOURCE STARTING_WOOD ATTR_SET 300
effect_amount MOD_RESOURCE STARTING_FOOD ATTR_SET 300
effect_amount MOD_RESOURCE STARTING_GOLD ATTR_SET -100
effect_amount MOD_RESOURCE STARTING_STONE ATTR_SET -50

In the <OBJECTS_GENERATION> section:

if KING_OT_HILL
create_object VILLAGER
{
 number_of_objects 7
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object KING
{
 set_place_for_every_player
 min_distance_to_players 6
 max_distance_to_players 6
}

create_object CASTLE
{
 set_place_for_every_player
 min_distance_to_players 10
 max_distance_to_players 10
}

endif

[Update 2017-09-22]: Added the adjustment of starting resources, big thanks to TriRem!

  1. In rare settings, that may also be two or more kings.
  2. Random map scripts are organized in sections.
  3. We actually don’t even need all resources declared. But maybe we want to extend the map later. Adding them doesn’t hurt.
  4. We ignore civilization “bonuses” here as they do not interfere with what we are doing.
  5. We can also use units that the players can produce, like villagers or scouts. But that would kind of foil the base idea of Regicide.