Overview
Game Mode scripts are one of the most common forms of scripting for Age of Empires IV, allowing you to define game parameters, starting conditions, and similar elements.
This guide will cover how to work with some central aspects game mode scripts for use in creating game modes of your own.
Opening a Script
After you have created a new Game Mode mod, a new Game Mode script will automatically open, appearing as a tab labelled [your_game_mode_name].scar.
This tab will appear as one of the options along the top of the Render View. Select it to begin editing your script.
If you close your script and need to reopen it, you can drag this file from the Asset Explorer onto the Render View.
Your script is located in Mod > Scar > WinConditions and is named [your_game_mode_name].scar
Imported Scripts
You can use the import() function to initialize another script alongside your game mode. This is useful for leveraging existing game scripts such as surrender.scar, which controls the behavior of the pause menu's Surrender button.
You can also create multiple scar scripts in your mod and import them in [your_game_mode_name].scar to run them with your Game Mode. This is a great way of organizing your code and can allow for additional modularization if you wish to create a sub-script that runs with multiple Game Mode mods.
The Blank Template for a Game Mode includes a number of game scripts that you are most likely to find useful. Feel free to remove these scripts if they are not needed for your specific Game Mode.
Scripting Framework
When a player selects your Game Mode and starts a match, your script will begin running during the loading sequence and will continue to run throughout the match.
The Blank Template includes a number of functions that will run automatically at different points in this process, detailed below:
Function |
Occurrence |
_OnGameSetup | Called during load as part of the game setup sequence |
_PreInit | Called before initialization, preceding other module OnInit functions |
_OnInit | Called on match initialization before handing control to the player |
_Start | Called after initialization is done when game is fading up from black |
_OnPlayerDefeated | Called when Core_SetPlayerDefeated() is invoked. Signals that a player has been eliminated from play due to defeat. |
_OnGameOver | When a victory condition is met, a module must call Core_OnGameOver() in order to invoke this delegate and notify all modules that the match is about to end. Generally used for clean up (removal of rules, objectives, and UI elements specific to the module). |
Function Names
In the Blank Template, these functions are prepended with "Mod" (e.g. Mod_OnGameSetup). This is because Age of Empires IV registers the script with the name "Mod" in the data section of the script.
You can change this to another name if you wish, but if you do, you will also need to update the scripting framework functions. For example, if you register the script as "TowerDefense," the _OnGameSetup function would only be called with the name TowerDefense_OnGameSetup().
Useful Concepts
The full possibilities of scripting are near limitless, and are beyond the scope of this guide.
However, we have outlined a number of things we think you will find useful when creating your first Game Mode mod.
Many of the examples below can be explored in greater detail by selecting the template that includes Examples when first creating your Game Mode mode.
Options
Options allow you to create UI elements that appear in the Custom/Skirmish lobby when your Game Mode mod is selected. The match host can then use these options to make adjustments to your Game Mode before a game.
To achieve this, perform the following steps:
- Create the necessary UI elements in the Win Condition file. For additional information on editing win conditions, see: Editing a Win Condition
- Update your .scar script to check which options the match host has selected and alter the Game Mode behavior based on their selection. This is detailed below.
Updating Your Script
-
Create an empty LUA Table. This is needed to store the options the match host has selected.
function Mod_OnGameSetup()
options_selected = {}
end
-
Call the Setup_GetWinConditionOptions function and pass in your empty table as the parameter. This will populate the table with the match host's selections.
function Mod_OnInit()
options_selected = {}
Setup_GetWinConditionOptions(options_selected)
end
-
Use an If statement to check the data in your table and assign unique behavior to each of the possible options. In the example below, the script is checking the match host selection from a drop-down list.
Note: The data inside your table uses the naming convention you used for your option keys in the Win Condition file. In this example, my_option_section, my_drop_down, my_first_option, and my_second_option are all custom keys.
function Mod_OnInit()
options_selected = {}
Setup_GetWinConditionOptions(options_selected)
if
options_selected.my_option_section.my_drop_down.enum_value == options_selected.my_option_section.my_drop_down.enum_items.my_first_option then
-- Do something
if
the first item is selected from the drop-down list
elseif options_selected.my_option_section.my_drop_down.enum_value == options_selected.my_option_section.my_drop_down.enum_items.my_second_option then
-- Do something
else
if
the second item is selected from the drop-down list
end
end
Win Condition Options Example
An example of a win condition option and its associated script is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.
Rules
Rules are useful for calling functions at specific moments in your match.
The following table outlines the type of Rules you have at your disposal.
Rule Type |
Function |
Description |
One Shot | Rule_AddOneShot(FunctionName, Delay in seconds) | Calls a function after a delay. |
Interval | Rule_AddInterval(FunctionName, Interval in seconds) | Calls a function on an interval. |
Global Event | Rule_AddGlobalEvent(FunctionName, Global Event key) | Calls a function when a game event occurs. To see a list of events, type "GE" in your script and you will be able to browse them. For example, GE_OnConstructionComplete will call a function whenever a building is constructed by any player in the match. |
To create a new rule, perform the following steps:
-
Create the Function that will be called by your rule.
In this example, a function called Mod_GiveResources has been created.function Mod_GiveResources()
-- Do something here
end
-
Call the Rule Function in your script. This can be done anywhere, but is often done when the match starts in a function like OnInit.
In this example, the Interval rule will call Mod_GiveResources every 60 seconds.function Mod_OnInit()
Rule_AddInterval(Mod_GiveResources,
60
)
end
Rule Type Examples
An example of each rule type is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.
Players
Many functions in Scardocs require player data to perform an action on.
For example, when you want to command or spawn units in the game, you must first know which player's units you are controlling or spawning. Likewise, when you create an objective, you need to know which player you are creating an objective for, since each player may have a unique objective. For example, one player may be prompted to attack, while another may be prompted to defend.
The two primary methods of identifying players in a match are outlined below.
Using the Local Player
Game Mode scripts run locally on the computers of all players in a match.
When we use the term "local player," we are referring to the player who is running the script. This means that a function referencing the local player can perform different actions depending on the computer it is running on.
For example, an objective might increment whenever a player enters a zone on the map. When a unit enters this zone, the mod could check if the local player owns this unit and update their objective accordingly. In this way, each player's objective would reflect their own progress towards their individual goal.
The example below demonstrates how to get the local player's data, check their civilization, and explore the entire map if they selected the Mongol civilization.
function Mod_ExploreMap() -- Gets the local player's data localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer()) -- Get player's civilization name local player_civ = Player_GetRaceName(localPlayer.id) -- If the local player is using the Mongol civilization if player_civ == "mongol" then -- Explore the map for the local player FOW_PlayerExploreAll(localPlayer.id) end end |
Using the PLAYERS Table
When a match starts, every player in the match, including the AI, will be added to a global PLAYERS table.
You can use a For loop to cycle through the players and get information about them, such as their name or civilization. This is useful if you wish to perform an action on every player in the match (e.g. setting the starting resources and Age for all players).
The example below shows a loop that cycles through each player in the match. You can see that the player.id value is passed as a parameter for several functions, including setting each player's Age to the Imperial Age, their pop cap to 50, and their starting resources.
function Mod_OnInit() for i, player in pairs(PLAYERS) do -- Set player starting Ages to Imperial -- Ages are mapped to: Dark Age = 1 , Feudal Age = 2 , Castle Age = 3 , Imperial Age = 4 Player_SetCurrentAge(player.id, 4 ) -- Set player starting resources -- RT stands for Resource Type Player_SetResource(player.id, RT_Food, 1000 ) Player_SetResource(player.id, RT_Wood, 50 ) Player_SetResource(player.id, RT_Gold, 0 ) Player_SetResource(player.id, RT_Stone, 0 ) -- Set starting population cap to 50 Player_SetMaxPopulation(player.id, CT_Personnel, 50 ) end end |
PLAYER Table Examples
An example of a PLAYERS table is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.
Creating Objectives
Objectives are displayed to players in the match in the top left corner of the screen. You can create a new objective using the Obj_Create function.
To create a new objective, perform the following steps:
-
Get the local player's information and store it in a variable. Objectives are created for each player individually. By getting the local player, we can call our function once and create an objective for all players in the match.
function Mod_SetupObjective()
-- Gets the local player's data
localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
end
-
Use the Obj_Create() function to create an objective. You will want to assign this objective to a variable so you can reference and modify it later.
function Mod_SetupObjective()
-- Gets the local player's data
localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Creates a
new
objective
objective = Obj_Create(localPlayer.id,
"Destroy the enemy houses"
, Loc_Empty(),
"icons\\races\\common\\victory_conditions\\victory_condition_conquest"
,
"ConquestObjectiveTemplate"
, localPlayer.raceName, OT_Primary,
0
)
end
-
Configure the starting state of your objective. In the below example we use a number of Obj_ function to set things like visibility and starting progress. The functionality of each Obj_ function is described in the script comments.
function Mod_SetupObjective()
-- Gets the local player's data
localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Creates a
new
objective
objective = Obj_Create(localPlayer.id,
"Destroy the enemy houses"
, Loc_Empty(),
"icons\\races\\common\\victory_conditions\\victory_condition_conquest"
,
"ConquestObjectiveTemplate"
, localPlayer.raceName, OT_Primary,
0
)
-- Sets the objective's state to incomplete
Obj_SetState(objective, OS_Incomplete)
-- Sets the objective to visible so players can see it
Obj_SetVisible(objective,
true
)
-- Sets the progress bar element of the objective to visible so players can see it
Obj_SetProgressVisible(objective,
true
)
-- Sets the objective progress type to a counter
Obj_SetCounterType(objective, COUNTER_CountUpTo)
-- Set the starting objective progress to
0
Obj_SetCounterCount(objective,
0
)
-- Set the maximum objective progress, in
this
case
there are
5
houses the player must destroy
Obj_SetCounterMax(objective,
5
)
-- Set the objective progress bar percentage value
Obj_SetProgress(objective,
0
/
5
)
end
-
When the player makes progress and/or completes the objective, use the Obj_ functions to reflect that progress to the player.
function Mod_OnHouseDestroyed()
-- Increment houses destroyed count
houses_destroyed = houses_destroyed +
1
-- Update the objective progress
Obj_SetCounterCount(objective, houses_destroyed)
-- Update the objective progress bar percentage value
Obj_SetProgress(objective, houses_destroyed /
5
)
-- If all houses are destroyed
if
houses_destroyed ==
5
then
-- Sets the objective's state to incomplete
Obj_SetState(objective, OS_Complete)
end
end
Objective Example
A functional example of an objective is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.
Finding Objects
It is often useful to find objects such as units and buildings on the map so you can either determine aspects about them (e.g their location), or perform actions on them (e.g. commanding them to move).
The following example outlines how to find the Town Center of each player so we can reveal their locations at the start of the match.
-
Create a for loop that cycles through each player in the match.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
end
end
-
Get all of the entities a player owns and store them in an entity group (egroup for short) using the Player_GetEntities function.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
-- Get the player's entities and place them into an ENTITY GROUP
local eg_player_entities = Player_GetEntities(player.id)
end
end
-
Use the EGroup_Filter function to filter out any entities that are not of the type "town_center". Then, get the first entry in your egroup using the EGroup_GetEntityAt function.
Because this function is running at the start of the match, you know that each player will only have one.
Note: You can browse all of the available entity blueprints in the Age of Empires IV Content Editor by selecting File > Asset Wizard, adding an Attributes mod, and then selecting Attributes > ebps from the toolbar. From there, you can select an entity blueprint and view its type_ext to determine all of the types it is associated with.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
-- Get the player's entities and place them into an ENTITY GROUP
local eg_player_entities = Player_GetEntities(player.id)
-- Filter out everything in the ENTITY GROUP except
for
the Town Center
EGroup_Filter(eg_player_entities,
"town_center"
, FILTER_KEEP)
-- Get the Town Center ENTITY by getting the first entry in the ENTITY GROUP we just filtered
local entity = EGroup_GetEntityAt(eg_player_entities,
1
)
end
end
-
Now that you have found the Town Center entity, you can grab some of its information such as its ID and position using the Entity_GetID and Entity_GetPosition functions.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
-- Get the player's entities and place them into an ENTITY GROUP
local eg_player_entities = Player_GetEntities(player.id)
-- Filter out everything in the ENTITY GROUP except
for
the Town Center
EGroup_Filter(eg_player_entities,
"town_center"
, FILTER_KEEP)
-- Get the Town Center ENTITY by getting the first entry in the ENTITY GROUP we just filtered
local entity = EGroup_GetEntityAt(eg_player_entities,
1
)
-- Get the Town Center's ENTITY ID
local entity_id = Entity_GetID(entity)
-- Get the Town Center's position
local position = Entity_GetPosition(entity)
end
end
-
You may want to reference a player's Town Center information later. To allow for this, create a table in the player's data and store the Town Center information you just gathered.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
-- Get the player's entities and place them into an ENTITY GROUP
local eg_player_entities = Player_GetEntities(player.id)
-- Filter out everything in the ENTITY GROUP except
for
the Town Center
EGroup_Filter(eg_player_entities,
"town_center"
, FILTER_KEEP)
-- Get the Town Center ENTITY by getting the first entry in the ENTITY GROUP we just filtered
local entity = EGroup_GetEntityAt(eg_player_entities,
1
)
-- Get the Town Center's ENTITY ID
local entity_id = Entity_GetID(entity)
-- Get the Town Center's position
local position = Entity_GetPosition(entity)
-- Store the player's Town Center information so it can be referenced later
player.town_center = {
entity = entity,
entity_id = entity_id,
position = position,
}
end
end
-
Use the FOW_RevealArea function to reveal the fog of war surrounding each player's Town Center for the first 30 seconds of the match.
-- This function finds the starting Town Center
for
all players in the match, reveals it to all other players, and increases its production speed
function Mod_FindTownCenter()
-- For every player in the match
for
i, player in pairs(PLAYERS)
do
-- Get the player's entities and place them into an ENTITY GROUP
local eg_player_entities = Player_GetEntities(player.id)
-- Filter out everything in the ENTITY GROUP except
for
the Town Center
EGroup_Filter(eg_player_entities,
"town_center"
, FILTER_KEEP)
-- Get the Town Center ENTITY by getting the first entry in the ENTITY GROUP we just filtered
local entity = EGroup_GetEntityAt(eg_player_entities,
1
)
-- Get the Town Center's ENTITY ID
local entity_id = Entity_GetID(entity)
-- Get the Town Center's position
local position = Entity_GetPosition(entity)
-- Store the player's Town Center information so it can be referenced later
player.town_center = {
entity = entity,
entity_id = entity_id,
position = position,
}
-- Reveal Town Center locations
for
the first
30
seconds of the match
FOW_RevealArea(player.town_center.position,
40
,
30
)
end
end
Town Center Examples
A functional example of finding a player's town center is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.
Spawning Units
In the Age of Empires IV Content Editor, units are referred to as "squads."
The data that determines how a squad appears and behaves is located in the squad's blueprint, also known as an sbp.
To spawn units, perform the following steps:
-
Get the data of the player you want to spawn units for.
In this example, we are keeping things simple by using the local player.function Mod_SpawnUnits()
-- Gets the local player's data
local localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
end
-
Get the squad blueprint (sbp) of the unit type you wish to spawn using the BP_GetSquadBlueprint function. You can browse all of the available squad blueprints in the Age of Empires IV Content Editor by navigating to File > Asset Wizard, adding an Attributes mod, and then selecting Attributes > sbps from the toolbar.
Note: For simplicity, this example uses the English spearmen blueprint. However, you may want your script to be more robust and check the player's civilization to determine what squad blueprint to spawn. An example of this can be found by creating a new Game Mode using the Template with Examples.
function Mod_SpawnUnits()
-- Gets the local player's data
local localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Get the English Spearmen squad blueprint
local sbp_spearman = BP_GetSquadBlueprint(
"unit_spearman_4_eng"
)
end
-
Identify a spawn position for your units.
In this example, the Util_GetOffsetPosition function is being used to get a position near the player's starting Town Center.Note: In this example it is assumed that the position of the player's starting Town Center has already been identified. If you are not sure how to accomplish this, see the section on Finding Squads and Entities.
function Mod_SpawnUnits()
-- Gets the local player's data
local localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Get the English Spearmen squad blueprint
local sbp_spearman = BP_GetSquadBlueprint(
"unit_spearman_4_eng"
)
-- Get a position offset from the player's Town Center
local spawn_position = Util_GetOffsetPosition(player.town_center.position,
20
,
10
)
end
-
Create a new squad group (sgroup for short) using the SGroup_CreateIfNotFound function. The function accepts a string as a parameter that is used by other SGroup_ functions when an SGroupID is required.
Note: A squad group is similar to a container for a bundle of units. It allows you to perform actions on all squads/units in the group simultaneously.
function Mod_SpawnUnits()
-- Gets the local player's data
local localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Get the English Spearmen squad blueprint
local sbp_spearman = BP_GetSquadBlueprint(
"unit_spearman_4_eng"
)
-- Get a position offset from the player's Town Center
local spawn_position = Util_GetOffsetPosition(player.town_center.position,
20
,
10
)
-- Create a SQUAD GROUP (SGROUP) that will act as a container
for
the spawned SQUADS
local sg_player_spearmen = SGroup_CreateIfNotFound(
"spearmen"
)
end
-
Spawn your units using the UnitEntry_DeploySquads function. This function requires a player id, squad group, a table of data that contains the number of each squad blueprint you are spawning, and the location the units will be spawned at.
Note: For simplicity, this example only spawns multiple English spearmen. However, the table can contain multiple squad blueprints if desired.
function Mod_SpawnUnits()
-- Gets the local player's data
local localPlayer = Core_GetPlayersTableEntry(Game_GetLocalPlayer())
-- Get the English Spearmen squad blueprint
local sbp_spearman = BP_GetSquadBlueprint(
"unit_spearman_4_eng"
)
-- Get a position offset from the player's Town Center
local spawn_position = Util_GetOffsetPosition(player.town_center.position,
20
,
10
)
-- Create a SQUAD GROUP (SGROUP) that will act as a container
for
the spawned SQUADS
local sg_player_spearmen = SGroup_CreateIfNotFound(
"spearmen"
)
-- This function spawns
16
English Spearmen near the player's starting Town Center
UnitEntry_DeploySquads(localPlayer.id, sg_player_spearmen, {{sbp = sbp_spearman, numSquads =
16
}}, spawn_position)
end
Spawning and Commanding Unit Examples
A functional example of spawning and commanding units is included in the Content Creator.
To view it, launch the Age of Empires IV Content Editor and create a new Game Mode by selecting Template with Examples.