An Introductory Guide to Customizing Additional Hotkeys

Both AoE3 and AOM allow players to customize hotkeys that have not been preset in the game by writing map commands in the user.con file. As far as I know, custom hotkeys have been used on a small scale for over a decade, but I haven’t seen a proper guide, so I decided to write this to share my experience with customizing hotkeys.

Please note: Some operations that can be achieved through the map command are considered by some people to be unsportsmanlike (or rather unfair) or even cheating (You can achieve a macro-like effect through this). If you want to use your customized hotkeys in the competition, please consult your tournament organizer first.

First, create user.con file in Startup folder with the following path:
C:\Users\<Your user name>\Games\Age of Empires 3 DE\<Your steam profile ID>\Startup\

Then edit the file and add the map command to it.
The map command template is as follows:
map(<eventString>, <contextString>, <commandString>)
They are all string-type variables, so they need to be quoted like this:
map("t", "game", "uiFindType(\"TownCenter\")")
You will also notice that the third variable also has its own string variable, in which case you need to replace the " with escape character \".

And then I will explain the purpose of the three variables.

  1. <eventString>
    Event strings represents the key or key combination input that triggers the hotkey command. You can refer to the hotkey settings in the game for this part.

  2. <contextString>
    Context Strings is used to distinguish the usage circumstances of these hotkeys to prevent key conflicts, most of them are used to limit the usage circumstances to when certain types of buildings are selected, and their names are self-explanatory.

  • Global context, I can’t tell the difference.
    world
    game

  • Some Screen-specific context, self-explanatory.
    Photo
    mainGameMenu
    techTreeScreen

  • Building Construction Context
    BuildAccel

  • 'x’Building Selected Context

TownCenterAccel
TownCenterAllyAccel
MarketAccel
BarracksAccel
StableAccel
ArtilleryDepotAccel
BlockhouseAccel
FortFrontierAccel
GalleonAccel
FluytAccel
TradingPostAccel
LivestockPenAccel
DockAccel
CoveredWagonAccel
OutpostWagonAccel
FortWagonAccel
FactoryWagonAccel
ChurchAccel
NoblesHutAccel
NativeEmbassyAccel
SaloonAccel
WarHutAccel
CorralAccel
FarmAccel
CommunityPlazaAccel
TeepeeAccel
AutogatherersAccel
VillageAccel
ConsulateAccel
CastleAccel
WarAcademyAccel
MonasteryAccel
WonderAccel
KallankaAccel
FactoryAccel
ShrineAccel
IncaStrongholdAccel
AgraFortAccel
CaravanseraiAccel
RicePaddyAccel
WarCampAccel
PalaceAccel
TowerAfricanAccel
UniversityAccel
HouseAfricanAccel
MonasteryEthiopianAccel
LombardAccel
CommanderyAccel
FoundationAccel
SocketAccel
  • Untested
    ResearchAccel
    CommandAccel

For more specific usage, please refer to the in-game hotkeys preset files (original format XML):
defaultkeymapgrid.txt (33.5 KB)
defaultkeymaplegacy.txt (401.5 KB)
defaultkeymapy.txt (23.4 KB)

  1. <commandString>
    Commandstring is used to write some official preset commands, which will result in various hotkey effects after execution. Some command explanations that I think are necessary are listed below. The functions of other commands can be found in the three “defaultkeymap” attachments in the previous section. The explanation for the following commands comes from official documents and has been revised or supplemented by me.
  • Find sth. Commands
    uiFindIdleType([string typeName], [bool shiftSelectsAll]) : finds the next idle unit of the given type in the arbitrary order of unit ID, so that it can be called repeatedly to cycle. bool type variables = true or false
    uiFindIdleType2([string typeName], [bool shiftSelectsAll]) : finds the next idle unit of the given type in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindAllOfSelectedType() : finds all units of the same type as the selected unit.
    uiFindAllOfType([string typeName]) : finds all units of the same type.
    uiFindAllOfTwoTypes([string typeName1], [string typeName2]) : finds all units belonging to any of the two provided types.
    uiFindAllOfTypeIdle([string typeName]) : finds all idle units of the same type.
    uiFindAllOfTypeIdle2([string typeName]) : finds all idle units of the same type.
    uiFindTownBellTC() : finds the next town center that has the town bell active, so that it can be called repeatedly to cycle.
    uiFindType([string typeName]) : finds the next unit(idle or not) of the given type in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindTwoTypes([string typeName1], [string typeName2]) : finds the next unit of the given two types in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindAnyOfTypes("[string typeName1, typeName2, typeName3, ...]") : finds the next unit of any of the given types(separated by commas or spaces) in the arbitrary order of unit ID, so that it can be called repeatedly to cycle
    uiFindNativeSite : finds the next Trading Post over a Native Site in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindTradeRouteSite : finds the next Trading Post over a Trade Route Site in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindNativeSiteSubCiv : finds the next Trading Post over a Native Site belonging to the given subCiv in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindFattenedHerdable : finds the next fattened herdable in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindAllFattenedHerdables : finds all fattened herdables.
    uiFindResourceGatherers ([string resourcetypeName]) : finds the next resource gatherer unit of the given resource type in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    [string resourcetypeName] = “Food”, “Wood”, “Gold”, “XP”,“Trade”, “Influence” or “Ships”
    uiFindGatherersNotGathering () : finds the gatherer unit that’s not gathering in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiFindDancers () : finds the next native settler dancing at the firepit/CommunityPlaza in the arbitrary order of unit ID, so that it can be called repeatedly to cycle.
    uiLookAtSelection : moves the camera to see the first selected unit.
    uiCreateNumberGroup([integer GroupID]) : creates a number group with the currently selected units.
    [GroupID] = 0~9
    uiClearNumberGroup([integer GroupID]) : erases the given number group.
    uiSelectNumberGroup([integer GroupID]) : selects the units in the given number group.
    uiAddSelectNumberGroup([integer GroupID]) : adds the units in the given number group to current selection.
    uiRemoveFromAnyNumberGroup : removes current selection from any army.
    uiLookAtNumberGroup([integer GroupID]) : moves the camera to see the given number group.
    [typeName] can input ProtoName or <unittype>, I will list some of the <unittype> related to unit counters, the rest can be found from protoy.xml (need to be extracted from the data.bar using the Resource Manager).
    LogicalTypeLandEconomy : Land Villager
    AbstractPet
    Hero
    Mercenary
    MercType2 : Spy-like Unit that effective against mercenaries and heroes.
    Guardian : Treasure guardians
    AbstractInfantry
    AbstractCavalry
    AbstractLightInfantry : Shock Infantry “Two-legged cavalry”
    AbstractArtillery
    AbstractHeavyInfantry
    AbstractSkirmisher : Light Infantry
    AbstractCounterSkirmisher : Light Infantry that excels at countering other Light Infantry.
    AbstractHeavyCavalry
    AbstractCoyoteMan : Hand Shock Infantry “Two-legged Heavy Cavalry”
    AbstractLightCavalry : Light Ranged Cavalry
    AbstractRangedShockInfantry : Ranged Shock Infantry “Two-legged Light Ranged Cavalry”
    AbstractFishingBoat
    AbstractWarShip

  • Do sth. Commands
    doAbilityInType([string protoPowerName], [string ProtoName]) : use ability in proto unit type if the player has one.
    doAbilityInSelected([string protoPowerName]) : use ability in current unit selection.
    doCommandInSelected([string commandName]) : use command in current unit selection.
    researchTechInSelected([string techName]) : research a tech in current unit selection.
    commandResearchInSelected([string commandName]) : researches a protounit command associated with a tech in current unit selection.
    commandTransformInSelected : researches the transform command set in protoUnit data for each unit in current unit selection.
    trainInSelected([string ProtoName], [integer traincount]) : tries to train the selected unit type in any valid selected unit.
    tis([string ProtoName], [integer traincount]) : just like train in selected, but more abbreviated.
    cancelAllQueuedInSelected() : cancel all queued units and researches in all valid selected units.
    sendFakeCardInSelected([string techName]) : send a fake card through current unit selection.
    homeCityTrain2([integer playerID], [integer cardIndex], [bool extendedDeck]) : Sends the given HC card in the home city without open HC panel, playerID = -1 cardIndex = 0~39, if extendedDeck = true, send federal card instead.
    uiPoliticianUIInSelected([string techName]) : research an age-up/wonder tech in current unit selection.(Only the panel is called out, still need to manually select)
    uiConsulateUIInSelected() : research a pick consulate alliance tech in current unit selection.(Only the panel is called out, still need to manually select)
    uiDeleteSelectedUnit : deletes selected unit.
    uiDeleteAllSelectedUnit : deletes all selected unit without confirmation.
    activateCommandPanel([integer rowNumber], [integer columnNumber]) : Presses a button on the command panel at the specified location with 0,0 being top left.
    shiftActivateCommandPanel([integer rowNumber], [integer columnNumber]) : Shift-Presses a button on the command panel at the specified location with 0,0 being top left.
    activateCommandButton([integer columnNumber]) : Presses a button on the command panel command button row at the specified index.
    [ProtoName] can input ProtoName found from protoy.xml
    [protoPowerName] can input Ability Name found from abilities.xml
    [commandName] can input protounitcommand Name found from protounitcommands.xml
    [techName] can input ProtoName found from techtreey.xml
    All the above in-program names can be found by searching the in-game names in the stringtabley.xml to obtain the string _locid and then searching for <displaynameid>.

  1. Some examples of map commands
    map("numpad4", "game", "uiFindAllOfTypeIdle(\"WallConnector\")uiDeleteAllSelectedUnits")
    The famous easy pillarless-walling hotkeys
    map("numpad4", "game", "uiFindAllOfType(\"AbstractInfantry\") uiCreateNumberGroup(0) uiFindAllOfType(\"Hero\")uiRemoveFromAnyNumberGroup uiSelectNumberGroup(0)")
    Finds all infantry and culls heroes from them.
    map("numpad4", "game", "doAbilityInType(\"ypPowerSmokeBomb\", \"ypmonkjapanese\") doAbilityInType(\"ypPowerSmokeBomb\", \"ypmonkjapanese2\")")
    Let two Japanese monks throw smoke bombs simultaneously.

Tip: You need to restart the game for the changes to the user.con to take effect.

If you have any questions or suggestions, please feel free to let me know.

Enjoy!

11 Likes

so i can select all infantery without selecting my hero with 1 hotkey? that would be great

2 Likes

Thank you so much for this! I’ve been looking for precisely such a detailed guide to learn how the hotkeys are created.

1 Like

map("numpad4", "game", "doAbilityInType(\"ypPowerSmokeBomb\", \"ypmonkjapanese\") doAbilityInType(\"ypPowerSmokeBomb\", \"ypmonkjapanese2\")")
Let two Japanese monks throw smoke bombs simultaneously.

for multiplayer?

Technically, all hotkeys work in both singleplayer and multiplayer.
The Japanese start with two Sohei Archers; both are counted as separate units, their in-program names are ypmonkjapanese and ypmonkjapanese2, respectively.
And the Smoke Bomb ability has shared the same cooldown for both monks since update 13.27885, but since the ability doesn’t go on cooldown until either monk successfully returns, you can still make them all return to the HC gatherpoint by having the monks use smoke bombs in succession. The hotkey just simplifies this.

Its really useful,Thank you!!
Can you answer one more question please?
As we all know,Hold down shift and click the Idle villager flag button in the top left corner ,we can select idle villagers group by group,each idle villager of same group are at the same place.
Can we do this by hotkey(without move mouse to the top left corner) :grey_question:
(Suppose bind this functionality to “Tap”)

1 Like

gooood job bro, hopes dev can formulate a usage rule of customized hotkeys. because nowadays most of the matches define this as cheating.

the alternative is to already being at the spot with idles, then ctrl double click one of them, that selects all idles on camera

1 Like

map("shift-m", "game", "uiFindIdleType(\"ValidIdleVillager\",true)")
Without mouse? Yes.
One key? No, since the <eventString> must contain “shift” to achieve this effect.

1 Like

Oh my god,that’s exactly what I wanted,you are such a genius!!!

omg genius, ive played 20 years age of empires and i didnt know it
any other tip i must get?

ill try to learn how to do it

a way to make the villager shoot to the animals to herd them but just 1 hit and then moving to the minegold or other placed assigned would be good saving a lot of time, that cannot be done with shift click.

image

how do i click on the militar quee and the camera dont move? that would help a lot

somebody knows how to remove hero and warrior priestesses from select all militar units?

map(“numpad6”, “game”, “uiFindAllOfType("LogicalTypeLandMilitary") uiCreateNumberGroup(0) uiFindAllOfType("AbstractHealer")uiRemoveFromAnyNumberGroup uiSelectNumberGroup(0)”)

i dont know how to add /hero/ to this in order that healers and hero are removed from finding

Does that work ? If it does, try this.

map("numpad6", "game", "uiFindAllOfType(\"LogicalTypeLandMilitary\") uiCreateNumberGroup(0) uiFindAllOfType(\"Hero\")uiRemoveFromAnyNumberGroup uiFindAllOfType(\"AbstractFindHealer\")uiRemoveFromAnyNumberGroup uiSelectNumberGroup(0)")

Hmm. Not sure what I did wrong. Morgan Black Oberhau Attack ability hotkey using the letter “o” in user.con (C:\Users\xxxxx\Games\Age of Empires 3 DE\xxxxxxxxxxxxx\Startup)

map(“o”, “game”, “doAbilityInType("ypPowerOberhauAttack", "ypSPCMorgan")”)

I’m trying to find the in-game hotkeys preset files you mention, are they extracted from a .bar? Thank you for this contribution btw it’s amazing.

Hello, could you help me set up a shortcut key? I’d like to use this shortcut key to select all infantry units, excluding priests and scouts, as well as any ninja reconnaissance units. Additionally, I want to include heroes in the selection. I know that scouts and ninja reconnaissance units share the tag ‘AbstractCanSeeStealth,’ but this tag also includes heroes. Furthermore, priests have the tag ‘AbstractFindHealer.’

I’ve tried the following approach, but it doesn’t seem to work:

map("shift-k", "game", "uiFindAllOfType(\"AbstractInfantry\") uiCreateNumberGroup(0) uiFindTwoTypes(\"AbstractFindHealer\",\"AbstractCanSeeStealth\") uiRemoveFromAnyNumberGroup uiFindAllOfType(\"Hero\") uiCreateNumberGroup(0) uiSelectNumberGroup(0)")

i tried it and it doesnt make them throw the bomb, they just get back to homecity

how do i selecto oly skirmisher type, it didnt work