Difference between revisions of "Mission Scripting"

From FA Forever Wiki
Jump to navigation Jump to search
m (this was always here)
Line 1,125: Line 1,125:
Here is a list of tools you require to add sounds to your map (or mod):
Here is a list of tools you require to add sounds to your map (or mod):
*Cross Platform Audio Creation Tool (XACT) - this is available from microsoft as part of the DirectX SDK [https://www.microsoft.com/en-us/download/details.aspx?id=6812 here]
*Cross Platform Audio Creation Tool '''''version 2.0''''' (XACT) - this is available from microsoft as part of the DirectX SDK [https://www.microsoft.com/en-us/download/details.aspx?id=6812 here]
*The sound files you want to put in, formatted as .wav files
*The sound files you want to put in, formatted as .wav files
*(Optional) Some Sound editing software, just to make it easier if some of the sounds are not quite right.
*(Optional) Some Sound editing software, just to make it easier if some of the sounds are not quite right.

Revision as of 20:52, 19 January 2016


Currently there is no campaign editor for creating custom missions for Supreme Commander, but every mission is written in lua and it's easy to edit. You don't need to have too much experience with code since most of it is already used in the official campaign missions. In this tutorial I will explain how to create a mission and as an example we will use Prothyon - 16 DEMO. In order to open it in Map Editor, you will need to put this file into map folder and change path in Scenario file.

Tools we need

Sources of inspiration

You can always look in one of the original campaign files to find the right code. There is a list of them. In order to open them in Map Editor, use files from this link. Maps from Forged Alliance missions couldn't be opened directly so I used only the heightmap. Its not perfect but it is enough to see how it works. Don't forget you can also look at custom map scripts for useful code snippets, some of which aren't present in the original campaign. Additionally, you can look at in game lua files, and take apart the functions there for your own uses.

It is better to look at Forged Alliance missions because the code there is simpler and easier to read and understand. Also AI was improved a lot there. So for creating AI we will use FA missions.

Map Editor sees only maps located in this folder.

C:\Program Files (x86)\THQ\Gas Powered Games\Supreme Commander\maps

All functions used in missions are defined in lua files that are in your FA installation. You can study then to find out more option you can use in your missions. They are commented and easy to understand.

Supreme Commander - Forged Alliance\gamedata\lua.scd
Supreme Commander - Forged Alliance\gamedata\mohodata.scd

How it works

Each mission is basically a map with extra files for AI and extended script file. In our example we can see several files. Always name these files same as the name of the folder they're in.

Folder is called Prothyon16_DEMO and these are the files in it:

  • Prothyon16_DEMO.scmap - This file is missing since map Shards was used instead.
  • Prothyon16_DEMO_m1uefai.lua - File controling UEF AI from first objective.
  • Prothyon16_DEMO_m2uefai.lua - File controling UEF AI from second objective.
  • Prothyon16_DEMO_m3uefai.lua - File controling UEF AI from third objective.
  • Prothyon16_DEMO_save.lua - Armies and markers are saved here.
  • Prothyon16_DEMO_scenario.lua - Basic information are stored here. Path to other files, mission name, armies, etc...
  • Prothyon16_DEMO_script.lua - Main file that is in control of everything.
  • Prothyon16_DEMO_strings.lua - Voice overs and taunts.

How to test missions

You can test your mission offline, which allows restarting and even saving, so its usually faster. In order to test your mission start the FAF version of FA from


You can create a shortcut on your desktop of that executable, go into its properties (right click) and change the target to include a command line switch:

ForgedAlliance.exe /init init_coop.lua
  • Make sure you have the Coop patch downloaded if its different from FAF patch - to do this simply host a coop game and close the dialog as soon as its finished patching.
  • You don't need to restart FA each time you modify a script. You can just restart the map inside FA.
  • Run FA in windowed mode and use the game log for debug. The default hotkey for showing the log window is: Ctrl + F9 (in some cases, just F9). It can be opened during the map loading screen. If you make a mistake in any of the files, the log will show you what you did wrong, giving the file and line where the error is.


Game uses this file to get basic information about the map. Name, path to other files, armies, etc. It is created with map, once you save your map. You can edit these information.

  • name - Name of your map that will be displayed in game lobby.
  • description - Description of your map that will be displayed in game lobby.
  • type - Change this value to campaign_coop.
  • preview - Keep this empty to have working map preview in FAF lobby.
  • size - Size of the map, do not change it.
  • map - Path to map file.
  • save - Path to save file.
  • script - Path to script file.
  • norushradius - This value is set only for custom map, not missions. Keep this value at 0.000000
  • norushoffsetX_Player - Each army has to have X and Y value
  • norushoffsetY_Player - Each army has to have X and Y value

All armies have to be listed here. This part is created once Games configuration is set in Map Editor. Coop armies are the last ones. Example:

Configurations = {
       ['standard'] = {
           teams = {
               { name = 'FFA', armies = {'Player','UEF','UEFAlly','Objective','Seraphim','Coop1','Coop2','Coop3',} },
           customprops = {


This file contains all data about armies, markers you've created in Map Editor.



Alliances among armies can be changed in Armies tab by clicking on the army you want to edit. They need to be set for every army. By default all alliances are set to Neutral.

AI Faction

You also need to set AI faction here else AI won't do anything in it's base. Since Map Editor is from vanilla times it supports only original factions. If your AI is Seraphim, you will have to edit save.lua file, open it and find Seraphim army and change the value.

faction = 3,

AI Plan

Last thing that needs to be changed is AI Plan. Since we script our AI using functions from Forged Alliance we need to set it to no plan. We can't do this directly in Map Editor so we need to edit our save.lua file again. Do this for every army you have.

plans = '/lua/ai/opai/defaultblankplanlist.lua',


This part will take most of the time to create. Script file is in control of everything. Here we will import other files we will use, set how the mission will start, end and everything what will happen in between. As an example we will use Prothyon16_DEMO_script.lua. Open that file while reading this tutorial because there won't be every line in this tutorial. I'm going to highlight only the most important parts of it.

First thing to do is import other files. That will be at the bery beginning of script. We can add these files as we're adding new functions to our script. Basic ones that we will surely use are:

local Objectives = import('/lua/ScenarioFramework.lua').Objectives
local ScenarioFramework = import('/lua/ScenarioFramework.lua')
local ScenarioPlatoonAI = import('/lua/ScenarioPlatoonAI.lua')
local ScenarioUtils = import('/lua/sim/ScenarioUtilities.lua')
local Utilities = import('/lua/utilities.lua')

Other files to import are AI files, Voice overs and if we use more function, we add those files later.

Next we set variables for all armies and other things.

  • ScenarioInfo.HumanPlayers = {} - Sets the table with human player.
    • It is filled depending on how many Coop players are in game.
    • We will use this table later for setting up restrictions and other things.
  • local SkipNIS1 = false - if set to true it will allow us to skip cinematics missin intro if we have one.
    • You can set one for all cinematics or one for each, which is recommended.

Start up

Beginning of the mission is handled by two functions. One will spawn all the initial units, second one will take care of everything else.

First one is caller OnPopulate.

function OnPopulate(scenario)
    -- body
  • ScenarioUtils.InitializeScenarioArmies() - Calls for InitializeScenarioArmies function from ScenarioUtils file that we imported at the beginning.

Army Colors

There are two ways to set color. We can choose from preset colors that are defines in ScenarioFramework or pick our own color by using RGB Color Code. We need to choose army that we're setting color to as we defined them at the beginnig of script

SetArmyColor('UEFAlly', 71, 134, 226)
SC Icon.png Supreme Commander Colors FA Icon.png Forged Alliance Colors FAF Logo2.png FAF Colors
SetAeonColor 41, 191, 41
SetAeonPlayerColor 36, 182, 36
67, 110, 238
19, 28, 211
SetAeonAllyColor 165, 200, 102 SetAeonEvilColor 159, 216, 2 232, 10, 10 95, 1, 167
SetAeonNeutralColor 16, 86, 16 SetAeonAlly1Color 16, 86, 16 97, 109, 126 255, 50, 255
SetCybranColor 128, 39, 37 SetAeonAlly2Color 123, 255, 125 250, 250, 0 255, 191, 128
SetCybranAllyColor 219, 74, 58 SetCybranPlayerColor 231, 3, 3 255, 135, 62 183, 101, 24
SetCybranNeutralColor 165, 9, 1 SetCybranEvilColor 225, 70, 0 255, 255, 255 144, 20, 39
SetUEFColor 41, 40, 140 SetCybranAllyColor 130, 33, 30 145, 97, 255 47, 79, 79
SetUEFAllyColor 71, 114, 148 SetUEFPlayerColor 41, 41, 225 255, 136, 255 64, 191, 64
SetUEFNeutralColor 16, 16, 86 SetUEFAlly1Color 81, 82, 241 46, 139, 87 102, 255, 204
SetCoalitionColor 80, 80, 240 SetUEFAlly2Color 133, 148, 255
SetNeutralColor 211, 211, 180 SetSeraphimColor 167, 150, 2
SetLoyalistColor 0, 100, 0

Unit Cap

Army unit capacity can be changed using SetArmyUnitCap function, we need to specify army and value we want to set. Example:

SetArmyUnitCap(UEF, 1000)
  • Sets 1000 unit cap for UEF army, do so for all armies you want.
    • This can be adjusted during mission, just set new value and it will over ride the previous one.

Spawning Units and Bases

There are several functions to spawn units. We can either spawn only one unit specified in editor/save file or whole group. Depending on chosen function we can just spawn them, spawn as wreckage, add veterancy level, etc...

This is an example of basic function to spawn unit group

ScenarioUtils.CreateArmyGroup('Player', 'Starting Base')
  • Spawns unit group named in editor Starting Base for Player army
    • Use this function to spawn any group of units you prepared in editor.

This code will create specific unit from editor and also allow us to work with it, in this case set it so it can't be reclaimed. If we would add parameter true in the function, our group would get spawned as wreckage.

ScenarioInfo.Gate = ScenarioUtils.CreateArmyUnit('Player', 'Gate')

For more information how to spawn units, look in ScenarioUtilities

Supreme Commander - Forged Alliance\gamedata\mohodata.scd\lua\sim\ScenarioUtilities.lua
Initial Patrols

You can add initial patrols that don't count into attack platoons you have ready in your AI file.

local units = ScenarioUtils.CreateArmyGroupAsPlatoon('UEF', 'EastBaseAirDef', 'GrowthFormation')
   for k, v in units:GetPlatoonUnits() do
       ScenarioFramework.GroupPatrolRoute({v}, ScenarioPlatoonAI.GetRandomPatrolRoute(ScenarioUtils.ChainToPositions('M1_East_Base_Air_Defence_Chain')))
  • local units - Sets the variable units
    • First group needs to be local units = , other groups can only units =
  • CreateArmyGroupAsPlatoon - spawn this group.
    • 'UEF' - army this group belongs to.
    • 'EastBaseAirDef' - Name of the group as it is in Map Editor/save.lua file
    • 'GrowthFormation' - formation this group will use. Other option is AttackFormation
  • Next part gives command to this group to patrol using 'M1_East_Base_Air_Defence_Chain' specified in editor/save.lua file.
    • ScenarioFramework.GroupPatrolRoute and ScenarioPlatoonAI.GetRandomPatrolRoute - for air units using random patrol route.
    • ScenarioFramework.GroupPatrolChain - for land units
      • Pay attention to this, one is Route, other one is Chain, it's easy to do mistake while copy pasting this.

Setting up AI

If we've already created AI, now is the time to activate it for the first mission. We need to import it's file at the beginning of the script,

local M1UEFAI = import('/maps/Prothyon16_DEMO/Prothyon16_DEMO_m1uefai.lua')

so we can call for functions that will activate it, there are two main bases in Prothyon - 16 DEMO, therefore two functions:


We can also add some starting recources to AI by using this code:

ArmyBrains[UEF]:GiveResource('MASS', 4000)
ArmyBrains[UEF]:GiveResource('ENERGY', 6000)

Cheat Economy/Build power

AI that is used in missions has already set buff for build power. It was 2x more build power on factories and 3x more build power on engineers. This is causing AI to use much more resources. Good way how to add more resources to AI the code below that allow are to multiply mass and energy income as much as we want. It can be set for every AI differently and changed during mission. You can ask why should be AI allowed to have more economy than player? The answer is simple, AI can't use resources efficiently so it needs to have more than player to be able to give a challenge. As you will learn in Creating AI part, everything AI builds are just scripted, predefined attacks, using marker chains we defined. AI can react to certain situations we add, but it will never be as good as player.

In order to use this buff we need to import file with buff definitions at the beginning of the script

local Buff = import('/lua/sim/Buff.lua')

This code will allow us to change multipliers to economy income. We just need to set our values and specify army that will be affected.

buffDef = Buffs['CheatIncome']
   buffAffects = buffDef.Affects
   buffAffects.EnergyProduction.Mult = 1
   buffAffects.MassProduction.Mult = 1.5

       for _, u in GetArmyBrain(UEF):GetPlatoonUniquelyNamed('ArmyPool'):GetPlatoonUnits() do
               Buff.ApplyBuff(u, 'CheatIncome')

For changing values later in the mission or setting them for another army we need to use only this part:

buffAffects.EnergyProduction.Mult = 1
buffAffects.MassProduction.Mult = 1.8

for _, u in GetArmyBrain(UEF):GetPlatoonUniquelyNamed('ArmyPool'):GetPlatoonUnits() do
    Buff.ApplyBuff(u, 'CheatIncome')
  • GPG used code that was giving resources to AI every few second, which was basically infinite eco and killing economy structures of AI didnt have too much of an effect. With the code above, this problem is removed.

Second function we will use on start up is OnStart funciton

function OnStart(scenario)
    -- body


All restrictions will be set in this function. For units we will use this code:

for _, player in ScenarioInfo.HumanPlayers do
        ScenarioFramework.AddRestriction(player, categories.TECH2 + categories.TECH3 + categories.EXPERIMENTAL)
  • first line is choosing who will be affected by these restrictions, in this case all human players
  • second line is the actual restriction
  • player - Army that will be restricted, all human players in this one.
  • categories.TECH2 - Units we are restricting.
  • Check the list of all categories.
    • We can restrict whole unit groups that has something in common using one of these categories or specific units, using unit IDs
  • Using this code you can restrict units for AI as well
    • ScenarioFramework.AddRestriction(UEF, categories.TECH1 * categories.ENGINEER) - would restrict T1 engineers for UEF army.
      • Usually you don't need to put restrictions to AI since it builds only units you specify, but it is usefull if you don't want AI to build lower tech engineers for example.
  • Similar code is used for removing restrictions, only thing that will change is AddRestriction to RemoveRestriction
    • This can be used anywhere duting the mission, usually as a result of some objective or when starting another objective.

To restrict ACU upgrades there is this function:

  • It's a table with all upgrades we want to restrict. Full list here.
  • To unlock ACU upgrades, set new restriction without those upgrades that you want to allow.

Other Things

We can set here camera angle so it doesn't start all the way zoomed out, revealing how the whole map looks.

Cinematics.CameraMoveToMarker(ScenarioUtils.GetMarker('Cam_1_1'), 0)
  • For this to work we need to import Cinematics file, more info in Cinematic Intro

Last thing to do is to set which function will follow, if we didn't spawn ACU yet, we can do it during cinematics or after first objective is assigned. In our example next function is IntroMission1NIS. But before we get there we need to set what will happen at the end of the game.

End Game

Cinematic Intro

Cinematics are usually used as intro to main objectives, but it can be used anywhere. First thing to do is to import file with their functions we will use. As with every file importing, put it at the beginning of the script.

local Cinematics = import('/lua/cinematics.lua')

If we haven't set playable area yet, now is the time to do so.

ScenarioFramework.SetPlayableArea('M1_Area', false)
  • 'M1_Area' - Name of our area as it's set in Map Editor.
  • Second value false/true can play 'Operation Area Expanded' voice. Set to true if you want this.

Camera Info Markers

Setting camera angle is happening in map editor.

  • Select Markers layer and place Camera Info Marker anywhere on the map.
  • Rotate view by holding ALT + Middle Mouse Button
  • Once you have angle ready, right click on the Camera Info Marker in the markers list and select Set Orientation
  • Rename your marker so it's easier to use in script.
  • Place as many of those markers as you want. In script you will be setting how fast will the camera be moving through them.

Setting up Cinematics

At the beginning we prepared variable to skip this upcoming cinematics. Now it's time to set it. Beacuse we will be spawning ACUs during cinematics, we need to set it twice, once with camera movent in first part, then just the ACU spawning in second part.

if not SkipNIS1 then
    -- First part, everything that will happen during cinematics
    -- Second Part, everything that will happen if we're skipping cinematics

First we need to enter Cinematic mode


We can set several visual markers to get Vision on area we're looking. Any Blank Marker can be used for this. But it's recommended to create a new one.

local VisMarker1_1 = ScenarioFramework.CreateVisibleAreaLocation(30, ScenarioUtils.MarkerToPosition('M1_Vis_1_1'), 0, ArmyBrains[Player])
  • 30 - vision radius
  • ScenarioUtils.MarkerToPosition('M1_Vis_1_1') - name of out marker
  • 0 - how long to provide vision in seconds
    • 0 = infinite time
  • ArmyBrains[Player] - army that will get vision over this marker

After camera moves away from these markers, we can destroy them by using this code:

          ScenarioFramework.ClearIntel(ScenarioUtils.MarkerToPosition('M1_Vis_1_1'), 40)
  • You can additionslly clear intel that was given from these markers to force player to send scouts.
    • ScenarioUtils.MarkerToPosition('M1_Vis_1_1') - Marker to clear intel around.
    • 40 - radius, should be bigger by 10 than was the radius of vision marker to be sure no intel will left.

If we have Camera Info Markers ready from Editor, we can use them now. It's handles by one function:

Cinematics.CameraMoveToMarker(ScenarioUtils.GetMarker('Cam_1_2'), 15)
  • 'Cam_1_2' - Marker to move camera to.
  • 15 - Travel time to this marker in seconds.
  • Voice Overs can be placed here.

After the last Camero Info marker we need to leave cinematics:


Spawning ACUs

First we spawn Player's ACU, it's the same function we use for spawning any unit. This is the time to add trigger, if ACU dies, player loses. Also pausing ACU explosions so camera will catch it.

ScenarioInfo.PlayerCDR = ScenarioUtils.CreateArmyUnit('Player', 'Commander')
ScenarioFramework.CreateUnitDeathTrigger(PlayerDeath, ScenarioInfo.PlayerCDR)

We can add effect depending where ACU is spawning. Second one is for spawning in Quantum Gate.


Now similar thing for Coop players. It's up to you if you want to set death trigger also on Coop ACU's.

ScenarioInfo.CoopCDR = {}
local tblArmy = ListArmies()
coop = 1
for iArmy, strArmy in pairs(tblArmy) do
    if iArmy >= ScenarioInfo.Coop1 then
        ScenarioInfo.CoopCDR[coop] = ScenarioUtils.CreateArmyUnit(strArmy, 'Commander')
        coop = coop + 1

At the end we add what function in next:


Mission Objective

Before we get to first objective there is one more function in which we'll set mission number.

function IntroMission1()
    ScenarioInfo.MissionNumber = 1


Objective types

There are several objective types, each require different target data to work.

  • Kill
  • Capture
  • KillOrCapture
  • Reclaim
  • Locate
  • SpecificUnitsInArea
  • CategoriesInArea
  • Protect
  • Timer
  • Camera

Basic data every objective needs:

ScenarioInfo.Name = Objectives.Type(
  • Name - Name of our objective for script. M1P1 for Mission 1 Primary 1 etc...
  • Objectives.Type - Now we choose type of the objective
  • 'primary' - Objective priority. Others are secondary, bonus.
  • 'incomplete' - Objective isn't completed yet. Other is complete.
  • 'Title' and 'Description' - Will be shown with objective on UI.
  • 'Action' - This is required by only some objective types. Others can't have it.
  • Last one is table with target. There are several data that can be here depending on objective type
FlashVisible = true,
AlwaysVisible = true,
MarkUnits = true,
MarkArea = true,
ShowProgress = true,
PercentProgress = true,
ShowFaction = 'Faction',
NumRequired = 'Number',

Units = {},
Area = 'Area',
Requirements = {
    {Area = 'Area', Category = categories.type, CompareOp = , Value = Number, ArmyIndex = Army},
Category = categories.UnitID,

Timer = Number, # if nil, requires a manual update for completion
ExpireResult = 'complete',
  • FlashVisible - Intel flash over a target to show it on the map.
  • AlwaysVisible - Constant vision on the target.
  • MarkUnits - Highlights target units on the map.
  • MarkArea - Highlighs target area on the map.
  • ShowProgress - Shows progress of the objective. (2/8, etc...)
  • PercentProgress - Shows progress of the objective in percents.
  • ShowFaction - Icon of objective is defined by units that are targeted, this over rides it and whows faction icon instead.
  • NumRequired - How many units are required to complete objective, usually used with Capture
  • Units - Table with target units
  • Area - Name of the area as we named it in Map Editor/save.lua file.
  • Requirements - Used for CategoriesInArea objective type.
    • Area - Name of the area as we named it in Map Editor/save.lua file.
    • Category - Unit categories, it can be one of these or unit IDs.
    • CompareOp - Options are: '<=', '>=', '<', '>', '=='
      • == - is checked only once per 5 ticks.
    • Value - Number we require.
    • ArmyIndex - Army we defined at the beginning of script.
  • Category - Forces icon of this unit.
  • Timer - Number in seconds.
  • ExpireResult - What happens when time runs up. Options are: complete, failed

Detailed description is in SimObjectives file

Supreme Commander - Forged Alliance\gamedata\lua.scd\lua\SimObjectives.lua

Now we will set up our first objective:

ScenarioInfo.M1P1 = Objectives.CategoriesInArea(
    'primary',                      # type
    'incomplete',                   # complete
    'Destroy UEF Forward Bases',    # title
    'Eliminate the marked UEF structures to establish a foothold on the main island.',  # description
    'kill',                         # action
    {                               # target
        MarkUnits = true,
        Requirements = {
                Area = 'M1_UEF_WestBase_Area',
                Category = categories.FACTORY,
                CompareOp = '<=',
                Value = 0,
                ArmyIndex = UEF,

Then we set what will happen when objective is complete. In our example we will move to another mission.

        if(result) then
            ScenarioFramework.Dialogue(OpStrings.BeachBaseDestroyed, IntroMission2, true)
  • Voice over that will tell us we finished objective and move us to another mission.

Creating AI

Campaign AI is different from skirmish AI. Everything that AI will do is scripted, from base design, expantions to every attack platoon. Each army AI has it's own file for every mission unless it survives to another mission. In that case, platoons will get different orders in each mission.

Things to do in Map Editor

We need to use Map Editor to design base and set up markers and chains AI will use. Lets start with base template

Base template

Go to Armies tab. Create new unit group in your army. It's better to have several subgroups so you always find the right one fast.

  • Enable View - Debug - Skirts to see how much space buildings take.
  • For building placement use also Options - AutoSnap to o-grid. You need to 'shake' every building you place to be properly alligned in the grid.

With group selected, right click on map and add structures.

  • Units can be copy pasted
  • Make sure that buildings and units are in the right group.
  • Units can be rotated by holding R key and moving cursor.
  • Building can be rotated only by setting Abs Yaw in Armies tab. (0-360)
  • Units are grouped by their ID's
First letter indicates unit pack
Second letter is faction
Third letter for type
Units added in one of the Supreme Commander patches a
Aeon.png Aeon a
o Units for objectives in Supreme Commancder e Uef.png UEF b Base
u Units from Supreme Commancder r Cybran.png Cybran c Civilian
x Units added in Forged Alliance s Seraphim.png Seraphim l Land
z Units added in FAF o Operation
s Naval

Markers and Chains

Base Markers
Rally point on the left and Base Marker in the middle.

Each base requires two markers to work and a lot more for sending units. There are two marker types we are going to use. Rally Point and Blank Marker.

Base Marker

First thing to to is to set up Base Marker around which the base will be defined.

  • AI will use factories and units only in radius around this marker.
  • Go to Markers layer and place Blank Marker in the middle of your base.
    • It's highly recommended to rename your markers to keep track on them. In our case: M1_West_Base_Marker.
    • Radius of base markers should not be overlapping!
Rally Point

Another one is Rally Point.

  • You can put different rally points for each factory types.
  • Rally Point marker needs to be in Base Marker radius
    • If you have platoon of many units and some of them will get outside of the Base Marker while waiting on the Rally Point, these units won't be sent by AI on attack and will stay on that place untill they are destroyed.
Marker Chains

Next step is set up all kinds of chains for units. Click on Chains tab and create a new chain New map icon.PNG. While having this chain selected, put Blank Markers on the map and they will create a chain. You can also add existing marker to chain by clicking on MarkerToChain.PNG. Marker can be also deleted from chain, but save your map first before doing so, editor might sometimes crash during it!

  • Set up as many chains as you want, with more chains AI will be harder to predict where it'll send units.
  • Don't forget to rename your chains, that can be done by double clicking on chain name. Markers can be renamed only in Markers tab.
  • First marker of the chain should be relatively close to rally point. It's not necessary but without that AI won't stop to attack enemy units if set on patrol and just go to first marker.
  • Markers in chain for land and naval attacks should not be too close together, since units are moving in formation and they tend to regroup at every marker.

Once you have your base and markers ready, we can move to next part which will be putting the code together.

Creating AI lua file

We need to create new lua file. Name this file same as your folder name plus specific name for this file. It's good to follow some simple format to know what this file is on the first sight. For example:

  • m1 - First mission.
  • uef - Army the AI belongs to.
  • ai - So we know its file for AI.

At the beginning of this file we will import other lua files.

This one will manage our base. Build and rebuild buildings, assembly attack platoons and send then on the field.

local BaseManager = import('/lua/ai/opai/basemanager.lua')

We will use functions from this file to give out platoons orders. Attack, patrol etc...

local SPAIFileName = '/lua/scenarioplatoonai.lua'

Next step is setting locals, here we will give index to our army, it's the same on as in script file.

local UEF = 2

Now we will create base managers to all of our bases. Every base needs it's base manager. In Prothyon there are two bases in first missions. The name of base managers doesn't matter but it's better to stick to one format. I't using similar name format as in FA missions.

  • UEF - Faction of the base
  • M1 - Mission number
  • Westbase - Name of the base
  • BaseManager.CreateBaseManager() - will call CreateBaseManager() function from file we imported at the beginning.
local UEFM1WestBase = BaseManager.CreateBaseManager()
local UEFM1EastBase = BaseManager.CreateBaseManager()


Next step is to set up specific base. This function will be called from out script file.

function UEFM1WestBaseAI()
   # ----------------
   # UEF M1 West Base
   # ----------------
   UEFM1WestBase:Initialize(ArmyBrains[UEF], 'M1_WestLand_Base', 'M1_West_Base_Marker', 40, {M1_WestBase = 100})
   UEFM1WestBase:StartNonZeroBase({8, 5})
   UEFM1WestBase:SetActive('AirScouting', true)
   UEFM1WestBase:SetActive('LandScouting', true)
   UEFM1WestBase:SetBuild('Defenses', true)

   UEFM1WestBase:AddBuildGroup('M1_WestBaseExtended', 90, false)

  • For each base we need to set several values.
  • First line initializes our base
    • ArmyBrains[UEF] - Army that will control this base
    • 'M1_WestLand_Base' - Name of out base, it can be whatever.
    • 'M1_West_Base_Marker' - Marker in save file. AI will use only factories, and send units, that are around this marker in radius we set as next.
    • 40 - Radius around our marker in game units.
      • Radius of base markers should not be overlapping!
    • {M1_WestBase = 100} - Name of unit group as we wrote in Map Editor / save.lua file and priority AI will rebuild buildings with.
  • Second line starts the base
    • StartNonZeroBase - Means that base will spawn, all units that are in M1_WestBase group.
      • StarEmptyBase - If you want this base to be build later by other base manager.
    • ({8, 5}) - First number is total count of engineers AI will maintain in base, second number is count of permanent assisting engineers. This means in out example there are 3 construction engineers. They are also assisting unit production if there are no more structures to build.
  • Next 3 lines will activates some base functionality that are disabled by default.
  • This base was designed so only bigger part of it is spawned (M1_WestBase) and rest is build during missions.
    • AddBuildGroup - adds group created in map editor to base manager
      • 'M1_WestBaseExtended' - Name of the group as it is in Map Editor / save.lua file.
      • 90 - priority
      • false - This means that the group will not be spawned at the beginning. Use true if you want this group spawned from the beginnig.
  • Last lines are for functions that will assembly attacks.
    • UEFM1WestBaseAirAttacks() - All air attacks in that will be build in this base.
    • All attacks could be in one function but that would be terrible mess and it would be really hard to find specific attack for you and everyone else who would be trying to learn from it.
    • You can group your attacks as your want, for example Air, Land, Naval, Drops, etc...


Now, when we have base working we want to set up attack that will be built and sent from this base. There are two way to set up platoons. We either use already existing platoon from lua files or create completely new one. For basic attacks like, group of tanks or bombers we can use the easy variation and using already created platoon. I didn't manage to get naval platoons working properly so I'm using custom ones.

Existing Platoon Templates

Files with templates we can use are located in:

Supreme Commander - Forged Alliance\gamedata\lua.scd\lua\AI\OpAI

If we want to used unit types from these files, only thing we need is to set which file to use and child type

  • AI will keep this platoon alive, this platoon will be rebuilt once every unit from it is destroyed.

There is an example of simple attack. AI will build platoon of 4 bombers and send them on patrol over selected marker chain.

function UEFM1WestBaseAirAttacks()
   local opai = nil

   # ------------------------------------
   # UEF M1 Land Base Op AI, Air Attacks
   # ------------------------------------

   # Builds platoon of 4 Bombers
   opai = UEFM1WestBase:AddOpAI('AirAttacks', 'M1_WestAirAttack1',
           MasterPlatoonFunction = {SPAIFileName, 'PatrolThread'},
           PlatoonData = {
               PatrolChain = 'M1_Land_Attack_Chain'
           Priority = 100,
   opai:SetChildQuantity('Bombers', 4)
  • function UEFM1WestBaseAirAttacks() - Our function that we're calling from out base.
  • local opai = nil - Adds variable to our function. Needs to be set once in each function with platoons.
  • Description of our attack, everything after # sign will game take as a comment.
  • Next several lines are creating the actual attack.
  • opai = UEFM1WestBase:AddOpAI Creates function for our attack
    • UEFM1WestBase - Will define in which base will be this platoon created.
    • AddOpAI - calls for the function that creates the platoon
    • 'AirAttacks' - Name of the file we use to create platoon where the template is stored.
    • 'M1_WestAirAttack1' - Our name for our platoon. Make sure you don't have two platoons with same name!
    • MasterPlatoonFunction - What should this platoon do.
      • SPAIFileName - our file with orders we imported at the beginning.
      • 'PatrolThread' - Function from the file we are going to use.
        • Most of the attacks are set as a patrol.
        • 'PatrolThread' - Patrol over the defined chain.
        • 'PatrolChainPickerThread' - Randomly picks one chain from out table of chains.
        • 'RandomDefensePatrolThread' - Assign random patrol route for each unit from platoon using defined chain.
    • PlatoonData - Table with data that our function requires.
      • PatrolChain = 'M1_Land_Attack_Chain' - Name of our chain as it is in Map Editor / save.lua file.
        • If we are using PatrolChainPickerThread function, we need to give more chains in a table.
        • Make sure you change PatrolChain to PatrolChains
    • Priority = 100 - Platoons with higher priority will be build first, obviously.
  • opai:SetChildQuantity('Bombers', 4) - Type and number of units we want to be built.
    • There can be more unit types at once.
    • opai:SetChildQuantity({'Bombers', 'Interceptors'}, 8) - Platoon of 4 Bombers and 4 Interceptors

We can also add build condition. In this example, platoon will be built only if player has more than 20 air mobile units.

opai:AddBuildCondition('/lua/editor/otherarmyunitcountbuildconditions.lua', 'BrainGreaterThanOrEqualNumCategory',
       {'default_brain', 'Player', 20, categories.ALLUNITS * categories.MOBILE})
  • '/lua/editor/otherarmyunitcountbuildconditions.lua' - path to file with build condition
  • 'BrainGreaterThanOrEqualNumCategory' - when is this condition met
  • 'default_brain' - our AI army
  • 'Player' - target army
  • 20 - number of units
  • categories.ALLUNITS * categories.MOBILE - units we're looking for

Lua files with all build conditins available are in this folder:

Supreme Commander - Forged Alliance\gamedata\lua.scd\lua\editor
Custom Unit Platoons

In our custom template we can set as many unit types and unit count as we want. We will need to know ID's of the units so Unit Database will come in handy.

   Temp = {
       { 'ues0201', 1, 4, 'Attack', 'GrowthFormation' },   # Destroyers
       { 'ues0202', 1, 2, 'Attack', 'GrowthFormation' },   # Cruisers
       { 'xes0205', 1, 2, 'Attack', 'GrowthFormation' },   # Shield Boat
   Builder = {
       BuilderName = 'NavyAttackBuilder2',
       PlatoonTemplate = Temp,
       InstanceCount = 1,
       Priority = 400,
       PlatoonType = 'Sea',
       RequiresConstruction = true,
       LocationType = 'SouthNavalBase',
       BuildConditions = {
           { '/lua/editor/otherarmyunitcountbuildconditions.lua', 'BrainGreaterThanOrEqualNumCategory',
       {'default_brain', 'Player', 20, categories.NAVAL * categories.MOBILE + categories.uel0203}},
       PlatoonAIFunction = {SPAIFileName, 'PatrolChainPickerThread'},     
       PlatoonData = {
           PatrolChains = {'M3_Air_Base_NavalAttack_Chain2', 'M3_Air_Base_NavalAttack_Chain4'}
   ArmyBrains[UEF]:PBMAddPlatoon( Builder )
  • If this is our first custom template in function then we need to put local = in front of Temp and Builder
  • Temp = {} - Our custom template.
    • 'NavalAttackTemp2' - Name of our template, make sure it's unique in your mission!
    • 'NoPlan' - We will specify what platoon will do few lines below.
    • { 'ues0201', 1, 4, 'Attack', 'GrowthFormation' }, # Destroyers - table for one unit
      • 'ues0201' - Unit ID
      • 1 -
      • 4 - Number of units to be built
      • 'Attack' - Basic unit function. Other typer are Support, Scout, Artillery
      • 'GrowthFormation' - There are 2 formations used in FA, this one is more compact. AttackFormation is more wide open.
      • # Destroyers - Note to keep track on units.
  • Builder = {} - This will build our template.
    • BuilderName = 'NavyAttackBuilder2' - Name of our builder, make sure it's unique in your mission!
    • PlatoonTemplate = Temp - Using template we created few lines above.
    • InstanceCount = 1 - How many of these platoon we want.
    • PlatoonType = 'Sea' - Specify factory type that this template should be sent it.
      • Other types are Land, Air, Gate
    • RequiresConstruction = true, - True since we need the platoon to be built by AI.
    • LocationType = 'SouthNavalBase' - Name of our base as it is in Map Editor / save.lua file.
    • BuildConditions - This is optional if we want the platoon to be built only if certain conditions are met.
  • On last line our custom platoon will get finally assembled
    • ArmyBrains[UEF] - Army that will use this template
    • PBMAddPlatoon( Builder ) - Adds our platoon builder that contains our template.


Now when we have our AI ready, we need to import it's file to the beginning of script to use it.

local M1UEFAI = import('/maps/Prothyon16_DEMO/Prothyon16_DEMO_m1uefai.lua')

Next step is to activate it. This line will handle that:

  • This calls for UEFM1WestBaseAI() function in M1UEFAI that we imported at the beginning of script file.
  • Every base is usually defined by it's own function. So to activate them all, you need to call for all functions.

Voice Overs

Voice Over Example

Voice overs are a table of several variables. They could be defined it script file but to keep better track on them, they have their own file called _string.lua and are imported into script. In our example it is.


Right now there in no other way where to have videos and sound files than in Forged Alliance installation folder:

Supreme Commander - Forged Alliance\movies
Supreme Commander - Forged Alliance\sounds\Voice\us


One VO can contain more than one videos, they will be played one after another. The format is:

VOname = {
  {text = , vid = , bank = , cue = , faction = },
  {text = , vid = , bank = , cue = , faction = },
  {text = , vid = , bank = , cue = , faction = },

And in our example:

# Third objective intro 1 / Actor: Gyle / Update 22/05/2015 / VO Ready
airbase1 = {
  {text = '[Gyle]: The island is now secure.', vid = 'Pro_16_airbase1.sfd', bank = 'G_VO1', cue = '21airbase1', faction = 'UEF'},
  • First line is description of our VO.
  • airbase1 - is unique name for this VO that will be called in script.
    • text = '[Gyle]: The island is now secure.' - This will be displayed in VO box in game.
    • vid = 'Pro_16_airbase1.sfd' - Name of VO's video file.
    • bank = 'G_VO1' - Name of VO's sound file.
    • cue = '21airbase1' - Specific track inside of the sound file.
    • faction = 'UEF' - Faction displayed with VO. Options are: UEF, Aeon, Cybran, Seraphim, NONE.


In order to use VO's we created in string file, we need to import this file at the beginning of the script

local OpStrings = import('/maps/Prothyon16_DEMO/Prothyon16_DEMO_strings.lua')

Function with VO is called Dialogue and it's defined in ScenarioFramework.lua so we need to import this file as well.

local ScenarioFramework = import('/lua/ScenarioFramework.lua')
  • If you want to get more detailed information what excatly this function do, look inside ScenarioFramework

Now we have everything ready to use VOs in our code. The format for is following:

ScenarioFramework.Dialogue(OpStrings.VOname, callback, critical)
  • ScenarioFramework.Dialogue - Calls for Dialogue function in ScenarioFramework that we imported at the beginning
  • OpStrings.VOname - calls for our specific VO we created in string.lua file.
  • callback - Callback function that this VO will start. If there is no, then type nil
  • critical - true or false / nothing. If set to true, this will make sure this VO will be always played. WIll not be blocked by some other VO.
    • Story VO's are usually set to true, while taunts are not that important.

Example without and with callback fucntion:

ScenarioFramework.Dialogue(OpStrings.intro1, nil, true)
ScenarioFramework.Dialogue(OpStrings.airbase1, IntroMission3)


Adding videos to supcom can be very troublesome if you don't know how, and don't have the right tools. Luckily, this wiki explains it all for you!

Required Tools:

  • SFDmux - this is a hard to find software which you need to create .sfd files from m1v files.
  • TMPGEnc - you can use this to convert .wav files to .m1v files, for SFDmux to use.
  • (Optional) Video editing software - so you can create and convert the videos to the right format.

The required tools are very difficult to find, so both of them are available for download here.

  • First open TMPGEnc and select the path to your video file.
  • You can mess around with the settings, or you can load settings included in the download; for this example, Example_VOs.mcf was used.
  • Once you are happy with how it looks, select the checkbox Video only under Stream Type and press Start.
  • Next put your newly-created .m1v file into the SFDmux folder, and rename the file to video.m1v.
  • Run convert video without audio.bat to convert the file.
  • You can edit the .bat file with notepad to convert a file under a different name, or do many at once; see Example batch file.bat for the settings that were used for the mission.
  • Rename your .sfd file so it matches what you have specified in your mission script and then place it into your game directory:
Supreme Commander - Forged Alliance\movies


To add audio to your mission, the audio files first need to be prepared - supcom only accepts audio as wavebank (.xwb) and soundbank (.xsb) formats, both of which are needed to work.

Some useful words you should know about:

  • A wave is the term used for .wav sound files in XACT.
  • A wavebank is a file which contains the actual audio files needed for your project - it can contain many files at once, in any order.
  • A soundbank file uses the files from the wavebank, and arranges them into sounds.
  • A cue is inside the soundbank and is what the game uses to call the sounds from the soundbank. A cue can call multiple sounds in different orders in needed.

Here is a list of tools you require to add sounds to your map (or mod):

  • Cross Platform Audio Creation Tool version 2.0 (XACT) - this is available from microsoft as part of the DirectX SDK here
  • The sound files you want to put in, formatted as .wav files
  • (Optional) Some Sound editing software, just to make it easier if some of the sounds are not quite right.

Everything else can be done from inside the tool. If you mess around with it, you should be able to work out how it works in 5 minutes, there is also a pretty good documentation of the tool here. Once you have made your wavebank (.xwb) and soundbank (.xsb) files, you need to place them into:

Supreme Commander - Forged Alliance\sounds\Voice\us

Note that the sounds will only work if you are running the us localization of supcom. For the sounds to work with other languages, you need to put the files into their respective localization folders.




ACU Upgrades
A ACU.PNG Aeon Cybran ACU.png Cybran UEF ACU.png UEF Sera ACU.png Seraphim
  • AdvancedEngineering
  • T3Engineering
  • ChronoDampener
  • CrysalisBeam (Range)
  • EnhancedSensors
  • HeatSink (Fire Rate)
  • ResourceAllocationAdvanced
  • Shield
  • ShieldHeavy
  • ResourceAllocation
  • Teleporter
  • AdvancedEngineering
  • T3Engineering
  • CloakingGenerator
  • CoolingUpgrade (Gun)
  • MicrowaveLaserGenerator
  • NaniteTorpedoTube
  • StealthGenerator
  • ResourceAllocation
  • Teleporter
  • AdvancedEngineering
  • T3Engineering
  • DamageStabilization (Nano)
  • HeavyAntiMatterCannon (Gun)
  • LeftPod
  • RightPod
  • Shield
  • ShieldGeneratorField
  • TacticalMissile
  • TacticalNukeMissile
  • ResourceAllocation
  • Teleporter
  • AdvancedEngineering
  • T3Engineering
  • AdvancedRegenAura
  • BlastAttack (Splash Gun)
  • DamageStabilization (Nano)
  • DamageStabilizationAdvanced
  • Missile
  • RateOfFire (Gun)
  • RegenAura
  • ResourceAllocationAdvanced
  • ResourceAllocation
  • Teleporter
sACU Upgrades
A SACU.PNG Aeon Cybran SCU.png Cybran UEF SCU.png UEF Sera SCU.png Seraphim
  • EngineeringFocusingModule
  • ResourceAllocation
  • Shield
  • ShieldHeavy
  • StabilitySuppressant (Gun)
  • SystemIntegrityCompensator (Nano)
  • Sacrifice
  • Teleporter
  • CloakingGenerator
  • EMPCharge
  • FocusConvertor (Gun)
  • NaniteMissileSystem (AA)
  • ResourceAllocation
  • SelfRepairSystem
  • StealthGenerator
  • Switchback (Engineering)
  • AdvancedCoolingUpgrade (Rate of Fire)
  • HighExplosiveOrdnance (Splash)
  • Pod
  • RadarJammer
  • ResourceAllocation
  • SensorRangeEnhancer
  • ShieldGeneratorField
  • Shield
  • DamageStabilization (Nano)
  • EngineeringThroughput
  • EnhancedSensors
  • Missile
  • Overcharge
  • Shield
  • Teleporter