I've been looking for a mission scripting tutorial a few months ago but found nothing, excepted some little things here and here, but there's not a 'true' tutorial about the mission scripting.
I apologize if there's another complete tutorial on gametoast that I missed. Also, I've found some parts of code on discussions on GT but I don't remember where, so I'll make credits when I'll find where I've got the code.
That being said let's start !
WHAT'S 'MISSION SCRIPTING' ?
You know, in the campaign, you have missions with objectives to do. Here, we'll see how to add these objectives to your map !
STEPS
0.5 Before Starting
Hidden/Spoiler:
-I'll do this on a conquest lua
-I might do some stupid english grammar errors
-I'll probably say wrong things while explaining some code (tell me when I'm wrong, if I am)
-You MUST get Notepad ++ (So useful) or, better, Marth8880' Lua API extension for Visual Studio Code
-There's this program, can be useful ! (mission scripter) http://www.gametoast.com/viewtopic.php?f=64&t=12029
-While reading this, play badass music, like this https://www.youtube.com/watch?v=Tj4FA7BGGkQ&t=1926s
-The mission will be for the ATT team (republic and empire if I'm right)
-I might do some stupid english grammar errors
-I'll probably say wrong things while explaining some code (tell me when I'm wrong, if I am)
-You MUST get Notepad ++ (So useful) or, better, Marth8880' Lua API extension for Visual Studio Code
-There's this program, can be useful ! (mission scripter) http://www.gametoast.com/viewtopic.php?f=64&t=12029
-While reading this, play badass music, like this https://www.youtube.com/watch?v=Tj4FA7BGGkQ&t=1926s
-The mission will be for the ATT team (republic and empire if I'm right)
1 Get our mission start
Hidden/Spoiler:
That's the code that starts the mission.
[code]SetAIDifficulty(0, 2, "hard")
AllowAISpawn(ATT, true)
AllowAISpawn(DEF, true)
EnableSPScriptedHeroes()
ScriptCB_SetGameRules("campaign")
DisableAIAutoBalance()
onfirstspawn = OnCharacterSpawn(
function(character)
if IsCharacterHuman(character) then
ReleaseCharacterSpawn(onfirstspawn)
onfirstspawn = nil
objectives_timer = CreateTimer("objectives_timer")
SetTimerValue(objectives_timer, 2)
StartTimer(objectives_timer)
begin_objectives = OnTimerElapse(
function(timer)
StartObjectives ()
ScriptCB_EnableCommandPostVO(0)
StartTimer(timeoutTimer)
end,
objectives_timer
)
end
end
)[/code]
You have to put that right after [code]function ScriptPostLoad()[/code].
--
[code]SetAIDifficulty(0, 2, "hard")
AllowAISpawn(ATT, true)
AllowAISpawn(DEF, true)
EnableSPScriptedHeroes()
ScriptCB_SetGameRules("campaign")
DisableAIAutoBalance()[/code]
I think everything here explains itself - I don't know what DisableAIAutoBalance() do, but it's something that's on every mission of the vanilla game, so that may be important.
--
[code]onfirstspawn = OnCharacterSpawn(
function(character)
if IsCharacterHuman(character) then
ReleaseCharacterSpawn(onfirstspawn)
onfirstspawn = nil
objectives_timer = CreateTimer("objectives_timer")
SetTimerValue(objectives_timer, 2)
StartTimer(objectives_timer)
begin_objectives = OnTimerElapse(
function(timer)
StartObjectives ()
ScriptCB_EnableCommandPostVO(0)
StartTimer(timeoutTimer)
end,
objectives_timer
)
end
end
)[/code]
This will check if at your first spawn you're a human player, and if you're a human player, it will start a timer of 2 secs. After 2 secs, your first objective start !
[code]SetAIDifficulty(0, 2, "hard")
AllowAISpawn(ATT, true)
AllowAISpawn(DEF, true)
EnableSPScriptedHeroes()
ScriptCB_SetGameRules("campaign")
DisableAIAutoBalance()
onfirstspawn = OnCharacterSpawn(
function(character)
if IsCharacterHuman(character) then
ReleaseCharacterSpawn(onfirstspawn)
onfirstspawn = nil
objectives_timer = CreateTimer("objectives_timer")
SetTimerValue(objectives_timer, 2)
StartTimer(objectives_timer)
begin_objectives = OnTimerElapse(
function(timer)
StartObjectives ()
ScriptCB_EnableCommandPostVO(0)
StartTimer(timeoutTimer)
end,
objectives_timer
)
end
end
)[/code]
You have to put that right after [code]function ScriptPostLoad()[/code].
--
[code]SetAIDifficulty(0, 2, "hard")
AllowAISpawn(ATT, true)
AllowAISpawn(DEF, true)
EnableSPScriptedHeroes()
ScriptCB_SetGameRules("campaign")
DisableAIAutoBalance()[/code]
I think everything here explains itself - I don't know what DisableAIAutoBalance() do, but it's something that's on every mission of the vanilla game, so that may be important.
--
[code]onfirstspawn = OnCharacterSpawn(
function(character)
if IsCharacterHuman(character) then
ReleaseCharacterSpawn(onfirstspawn)
onfirstspawn = nil
objectives_timer = CreateTimer("objectives_timer")
SetTimerValue(objectives_timer, 2)
StartTimer(objectives_timer)
begin_objectives = OnTimerElapse(
function(timer)
StartObjectives ()
ScriptCB_EnableCommandPostVO(0)
StartTimer(timeoutTimer)
end,
objectives_timer
)
end
end
)[/code]
This will check if at your first spawn you're a human player, and if you're a human player, it will start a timer of 2 secs. After 2 secs, your first objective start !
Hidden/Spoiler:
We'll do 2 objectives (of course you can add more) :
-Destroy an object
-Capture a CP
--FIRST OBJECTIVE--
Here's what you'll have to put right after the first step [code]
--objective: assault
Dobjekt = Target:New{name = "object_name"}
Dobjekt.OnDestroy = function(self)
end
Objective1 = ObjectiveAssault:New{teamATT = ATT, teamDEF = DEF,
text = "level.AAA.objectives.1", popupText = "level.AAA.objectives.1"}
Objective1:AddTarget(Dobjekt)
Objective1.OnStart = function(self)
end
Objective1.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
end
[/code]
*Be sure to replace AAA by your 3 letter world name, and replace Objective1 by Objective2 if it's your second obj, Objective3 if it's your third one etc...
--
[code]Dobjekt = Target:New{name = "object_name"}
Dobjekt.OnDestroy = function(self)
end[/code]
This sets up what object in your map needs to be destroyed.
--
[code]Objective2 = ObjectiveAssault:New{teamATT = ATT, teamDEF = DEF,
text = "level.AAA.objectives.1", popupText = "level.AAA.objectives.1"}[/code]
That's about the localization.
[code]level.AAA.objectives.1[/code] is the path in the localization tool, and it will show the text and the popup text that will apear at the beginning of the objective
--
[code]Objective1:AddTarget(Dobjekt)
Objective1.OnStart = function(self)
end[/code]
Not very important yet, but it needs to be there.
--
[code]Objective1.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
end[/code]
End the objective and shows 'objective complete'
--SECOND OBJECTIVE--
What you need to add right after the first objective
[code]--objective: conquest
Objective2CP = CommandPost:New{name = "CP2"}
Objective2 = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF, text = "level.AAA.objectives.2", popupText = "level.AAA.objectives.2", AIGoalWeight = 0}
Objective1:AddCommandPost(Objective2CP)
Objective2:AddHint("level.geo1.hints.capture_cp")
Objective2.OnStart = function(self)
AICanCaptureCP("CP2", ATT, false)
AICanCaptureCP("CP2", DEF, false)
att_obj1_aigoal = AddAIGoal(ATT, "Defend", 50, "CP2")
def_obj1_aigoal = AddAIGoal(DEF, "Defend", 50, "CP2")
att_obj1_aigoal2 = AddAIGoal(ATT, "Deathmatch", 100)
def_obj1_aigoal2 = AddAIGoal(DEF, "Deathmatch", 100)
end
Objective2.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
DeleteAIGoal(att_obj1_aigoal)
DeleteAIGoal(att_obj1_aigoal2)
DeleteAIGoal(def_obj1_aigoal)
DeleteAIGoal(def_obj1_aigoal2)
SetProperty("CP2", "Team", 1)
SetProperty("CP2", "CaptureRegion", "")
end[/code]
--
*Be sure to replace AAA by your 3 letter world name, CP2 by the name of your CP, and replace Objective1 by Objective2 if it's your second obj, Objective3 if it's your thirs one etc...
[code]Objective2CP = CommandPost:New{name = "CP2"}
Objective2 = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF, text = "level.AAA.objectives.2", popupText = "level.AAA.objectives.2", AIGoalWeight = 0}
Objective1:AddCommandPost(Objective2CP)[/code]
Defines which CP needs to be captured and where is the text and the popup text (like for the first objective).
--
[code]Objective2:AddHint("level.geo1.hints.capture_cp")
Objective2.OnStart = function(self)
AICanCaptureCP("CP2", ATT, false)
AICanCaptureCP("CP2", DEF, false)
att_obj1_aigoal = AddAIGoal(ATT, "Defend", 50, "CP2")
def_obj1_aigoal = AddAIGoal(DEF, "Defend", 50, "CP2")
att_obj1_aigoal2 = AddAIGoal(ATT, "Deathmatch", 100)
def_obj1_aigoal2 = AddAIGoal(DEF, "Deathmatch", 100)
end[/code]
Add goals to the AI ; I think you can see yourself what everything does here ^^
--
[code]Objective2.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
DeleteAIGoal(att_obj1_aigoal)
DeleteAIGoal(att_obj1_aigoal2)
DeleteAIGoal(def_obj1_aigoal)
DeleteAIGoal(def_obj1_aigoal2)
SetProperty("CP2", "Team", 1)
SetProperty("CP2", "CaptureRegion", "")
end[/code]
When the objective is done, this part will delete AI goals and show 'objective complete'
-Destroy an object
-Capture a CP
--FIRST OBJECTIVE--
Here's what you'll have to put right after the first step [code]
--objective: assault
Dobjekt = Target:New{name = "object_name"}
Dobjekt.OnDestroy = function(self)
end
Objective1 = ObjectiveAssault:New{teamATT = ATT, teamDEF = DEF,
text = "level.AAA.objectives.1", popupText = "level.AAA.objectives.1"}
Objective1:AddTarget(Dobjekt)
Objective1.OnStart = function(self)
end
Objective1.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
end
[/code]
*Be sure to replace AAA by your 3 letter world name, and replace Objective1 by Objective2 if it's your second obj, Objective3 if it's your third one etc...
--
[code]Dobjekt = Target:New{name = "object_name"}
Dobjekt.OnDestroy = function(self)
end[/code]
This sets up what object in your map needs to be destroyed.
--
[code]Objective2 = ObjectiveAssault:New{teamATT = ATT, teamDEF = DEF,
text = "level.AAA.objectives.1", popupText = "level.AAA.objectives.1"}[/code]
That's about the localization.
[code]level.AAA.objectives.1[/code] is the path in the localization tool, and it will show the text and the popup text that will apear at the beginning of the objective
--
[code]Objective1:AddTarget(Dobjekt)
Objective1.OnStart = function(self)
end[/code]
Not very important yet, but it needs to be there.
--
[code]Objective1.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
end[/code]
End the objective and shows 'objective complete'
--SECOND OBJECTIVE--
What you need to add right after the first objective
[code]--objective: conquest
Objective2CP = CommandPost:New{name = "CP2"}
Objective2 = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF, text = "level.AAA.objectives.2", popupText = "level.AAA.objectives.2", AIGoalWeight = 0}
Objective1:AddCommandPost(Objective2CP)
Objective2:AddHint("level.geo1.hints.capture_cp")
Objective2.OnStart = function(self)
AICanCaptureCP("CP2", ATT, false)
AICanCaptureCP("CP2", DEF, false)
att_obj1_aigoal = AddAIGoal(ATT, "Defend", 50, "CP2")
def_obj1_aigoal = AddAIGoal(DEF, "Defend", 50, "CP2")
att_obj1_aigoal2 = AddAIGoal(ATT, "Deathmatch", 100)
def_obj1_aigoal2 = AddAIGoal(DEF, "Deathmatch", 100)
end
Objective2.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
DeleteAIGoal(att_obj1_aigoal)
DeleteAIGoal(att_obj1_aigoal2)
DeleteAIGoal(def_obj1_aigoal)
DeleteAIGoal(def_obj1_aigoal2)
SetProperty("CP2", "Team", 1)
SetProperty("CP2", "CaptureRegion", "")
end[/code]
--
*Be sure to replace AAA by your 3 letter world name, CP2 by the name of your CP, and replace Objective1 by Objective2 if it's your second obj, Objective3 if it's your thirs one etc...
[code]Objective2CP = CommandPost:New{name = "CP2"}
Objective2 = ObjectiveConquest:New{teamATT = ATT, teamDEF = DEF, text = "level.AAA.objectives.2", popupText = "level.AAA.objectives.2", AIGoalWeight = 0}
Objective1:AddCommandPost(Objective2CP)[/code]
Defines which CP needs to be captured and where is the text and the popup text (like for the first objective).
--
[code]Objective2:AddHint("level.geo1.hints.capture_cp")
Objective2.OnStart = function(self)
AICanCaptureCP("CP2", ATT, false)
AICanCaptureCP("CP2", DEF, false)
att_obj1_aigoal = AddAIGoal(ATT, "Defend", 50, "CP2")
def_obj1_aigoal = AddAIGoal(DEF, "Defend", 50, "CP2")
att_obj1_aigoal2 = AddAIGoal(ATT, "Deathmatch", 100)
def_obj1_aigoal2 = AddAIGoal(DEF, "Deathmatch", 100)
end[/code]
Add goals to the AI ; I think you can see yourself what everything does here ^^
--
[code]Objective2.OnComplete = function(self)
ShowMessageText("game.objectives.complete", ATT)
DeleteAIGoal(att_obj1_aigoal)
DeleteAIGoal(att_obj1_aigoal2)
DeleteAIGoal(def_obj1_aigoal)
DeleteAIGoal(def_obj1_aigoal2)
SetProperty("CP2", "Team", 1)
SetProperty("CP2", "CaptureRegion", "")
end[/code]
When the objective is done, this part will delete AI goals and show 'objective complete'
Hidden/Spoiler:
That may be the easiest part !
Open the localization tool and :
-Add a scope to level and name it AAA (where AAA is your 3 letters world name)
-Add a scope to AAA and name it objectives
-Add any number of keys to objectives ! The keys have to get the same name in the lua - in this tutorial we need 2 scopes : 1 and 2, because in your lua there's
[code]level.AAA.objectives.1[/code]
or
[code]level.AAA.objectives.2[/code]
That's it !
Open the localization tool and :
-Add a scope to level and name it AAA (where AAA is your 3 letters world name)
-Add a scope to AAA and name it objectives
-Add any number of keys to objectives ! The keys have to get the same name in the lua - in this tutorial we need 2 scopes : 1 and 2, because in your lua there's
[code]level.AAA.objectives.1[/code]
or
[code]level.AAA.objectives.2[/code]
That's it !
Hidden/Spoiler:
At the top of your lua, add this
[code]ScriptCB_DoFile("setup_teams")
ScriptCB_DoFile("ObjectiveConquest")
ScriptCB_DoFile("ObjectiveAssault")
ScriptCB_DoFile("MultiObjectiveContainer")
ScriptCB_SetGameRules("campaign")[/code]
This will load every scripts we'll need for our mission.
--
Right before [code]---------------------------------------------------------------------------
-- FUNCTION: ScriptInit
-- PURPOSE: This function is only run once
-- INPUT:
-- OUTPUT:
-- NOTES: The name, 'ScriptInit' is a chosen convention, and each
-- mission script must contain a version of this function, as
-- it is called from C to start the mission.
---------------------------------------------------------------------------
function ScriptInit()
ReadDataFile("ingame.lvl")
SetMaxFlyHeight(10000)
SetMaxPlayerFlyHeight(10000)[/code]
Add this
[code]function StartObjectives()
objectiveSequence = MultiObjectiveContainer:New{delayVictoryTime = 2.0 }
objectiveSequence:AddObjectiveSet(Objective1)
objectiveSequence:AddObjectiveSet(Objective2)
objectiveSequence:Start()
end[/code]
This is super important too : it's that part that loads your objectives ! If there's something wrong here, your objectives will just not show up and you'll get a regular conquest map.
--
[code]objectiveSequence = MultiObjectiveContainer:New{delayVictoryTime = 2.0 }[/code]
At delayVictoryTime = 2.0 enter the number of objectives in your mission. Here we put 2.0 because we have 2 objectives.
--
[code]objectiveSequence:AddObjectiveSet(Objective1)
objectiveSequence:AddObjectiveSet(Objective2)[/code]
Add [code]objectiveSequence:AddObjectiveSet(Objective*)[/code] when you create a new objective. For exemple there's 7 objectives in your map, so you'll have 7 times this [code]objectiveSequence:AddObjectiveSet(Objective*)[/code]
[code]ScriptCB_DoFile("setup_teams")
ScriptCB_DoFile("ObjectiveConquest")
ScriptCB_DoFile("ObjectiveAssault")
ScriptCB_DoFile("MultiObjectiveContainer")
ScriptCB_SetGameRules("campaign")[/code]
This will load every scripts we'll need for our mission.
--
Right before [code]---------------------------------------------------------------------------
-- FUNCTION: ScriptInit
-- PURPOSE: This function is only run once
-- INPUT:
-- OUTPUT:
-- NOTES: The name, 'ScriptInit' is a chosen convention, and each
-- mission script must contain a version of this function, as
-- it is called from C to start the mission.
---------------------------------------------------------------------------
function ScriptInit()
ReadDataFile("ingame.lvl")
SetMaxFlyHeight(10000)
SetMaxPlayerFlyHeight(10000)[/code]
Add this
[code]function StartObjectives()
objectiveSequence = MultiObjectiveContainer:New{delayVictoryTime = 2.0 }
objectiveSequence:AddObjectiveSet(Objective1)
objectiveSequence:AddObjectiveSet(Objective2)
objectiveSequence:Start()
end[/code]
This is super important too : it's that part that loads your objectives ! If there's something wrong here, your objectives will just not show up and you'll get a regular conquest map.
--
[code]objectiveSequence = MultiObjectiveContainer:New{delayVictoryTime = 2.0 }[/code]
At delayVictoryTime = 2.0 enter the number of objectives in your mission. Here we put 2.0 because we have 2 objectives.
--
[code]objectiveSequence:AddObjectiveSet(Objective1)
objectiveSequence:AddObjectiveSet(Objective2)[/code]
Add [code]objectiveSequence:AddObjectiveSet(Objective*)[/code] when you create a new objective. For exemple there's 7 objectives in your map, so you'll have 7 times this [code]objectiveSequence:AddObjectiveSet(Objective*)[/code]
That's it for now ! I don't have enough time now to make something more complete, but I'm gonna update this a lot in the next days !
I hope this tutorial has helped you