Get SaintAxe&Ror SpeechMaker Now And build your conversations easily ! |
This is a page that displays some short How-To to help using OFDR Mission Editor for basic mission editing.
These How-To are not the "best way" or are not endorsed by CodeMasters in any way. They are just the result of the long time I spent coding a lot of Single Player / Coop missions with simple and complex scripting (+ 115) .
Link : Official LUA Documentation
HOWTO 2 : Assaulting the convoy
This is a very simple mission that shows you how to work with a convoy.
The scenario is :
The player echelon (echSAR) has prepared an ambush and wait until the convoy is coming. The convoy contains 1 Tank + crew , and 3 Trucks + crew , and 2 Special Ops echelons that are in the trucks. The player hit the tank with the AT Weapon making the convoy stop. As the tank is destroyed, the people dismount their vehicles and start a counter attack. When all PLA are dead, the objective is completed. End of the story.
Here is the complete level.lua :
-- -------------------------------------------------------- -- -- __ _ _ _ __ -- -- / _\ __ _(_)_ __ | |_ /_\ __ _____ /__\ ___ _ __ -- -- \ \ / _` | | '_ \| __|//_\\\ \/ / _ \/ \/// _ \| '__| -- -- _\ \ (_| | | | | | |_/ _ \> < __/ _ \ (_) | | -- -- \__/\__,_|_|_| |_|\__\_/ \_/_/\_\___\/ \_/\___/|_| -- -- -- -- -------------------------------------------------------- -- -- OFP Dragon Rising -- HOWTO 2 : Assaulting the convoy -- Author : Thierry Godin (SaintAxe&Ror) -- Web : http://sar.n1bus.eu -- Date de creation : 02/01/2012 -- 1.0.1 -- updated : 06/01/2012 -- fixed : fixed typo (EAgressive -> EMoodAggressive) function onCreate() -- Speech conversations = scripts.library.convLib.convLib:new() speech() end function onMissionStart() canCountConvoy = false OFP:displaySystemMessage("HOWTO 2 : Assaulting the convoy - 1.0.1") OFP:displaySystemMessage("Thanks for playing SaintAxe&Ror mission") OFP:displaySystemMessage("Other stuff here = http://sar.n1bus.eu") OFP:displaySystemMessage("Have fun !") OFP:setObjectiveState("objConvoy", "IN_PROGRESS") OFP:setObjectiveVisibility("objConvoy", true) OFP:setInvulnerable("echSAR", true) setConvoyID = OFP:activateEntitySet("setConvoy") end -- --------------------------------------------------------------- -- SPAWN -- --------------------------------------------------------------- function onSpawnedReady( setName, setID, tableOfEntities, errorCode ) if setID == setConvoyID then OFP:setMood("eConvTank", "EMoodCautious", "ADDTOEND") for i = 1, 2 do OFP:setMood("eSo" .. i, "EMoodCautious", "ADDTOEND") end for i = 1, 3 do OFP:setMood("eConvTruck" .. i, "EMoodCautious", "ADDTOEND") end OFP:addTimer("TimerConvoy", 7000) end end -- ------------------------------- -- --------------------------------------------------------------- -- CONVOY -- --------------------------------------------------------------- function onTimer_TimerConvoy() OFP:removeTimer("TimerConvoy") OFP:setMaxSpeed("convTank", 25) OFP:move("eConvTank", "wpConvoyp", "ADDTOEND") local distance = 30 for i = 1, 3 do OFP:follow("eConvTruck" .. i, "convTank", (distance * i), "ADDTOEND") OFP:setLights("convTruck" ..i, true) end conversations:start(spConvoyStart) canCountConvoy = true end function onDeath_convTank(victim, killer) conversations:start(spTankDestroyed) counterAttack() end function counterAttack() if OFP:isAlive("eConvTank") then OFP:forceDismountVehicle("eConvTank", "OVERRIDE") OFP:setMood("eConvTank", "EMoodAggressive", "ADDTOEND") OFP:defendPerimeter("eConvTank", "ADDTOEND") end for i = 1, 2 do if OFP:isAlive("eSo" .. i) then OFP:forceDismountVehicle("eSo" .. i, "OVERRIDE") OFP:setFormation("eSo" .. i, "F_DIAMOND", "ADDTOEND") OFP:setMood("eSo" .. i, "EMoodAggressive", "ADDTOEND") OFP:assault("eSo" .. i, "echSAR", "ADDTOEND") end end for i = 1, 3 do if OFP:isAlive("eConvTruck" .. i) then OFP:forceDismountVehicle("eConvTruck" .. i, "OVERRIDE") OFP:setMood("eConvTruck" .. i, "EMoodAggressive", "ADDTOEND") OFP:defendPerimeter("eConvTruck" .. i, "ADDTOEND") end end conversations:start(spCounterAttack) end function onDeath(victim, killer) if OFP:getSide(victim) == 1 then if canCountConvoy == true then if not OFP:isAlive("grpConvoy") then OFP:setObjectiveState("objConvoy", "COMPLETED") conversations:start(spConvoyOut) OFP:addTimer("TimerEnd", 10000) canCountConvoy = false end end end end -- ------------------------------- -- --------------------------------------------------------------- -- SPEECHS -- --------------------------------------------------------------- function speech() spConvoyStart = conversations:add({ {actor = "player", say = "spc1_x02_hid_02"}, -- I hear Transports, sounds like a convoy. {actor = "radio", say = "inf1_03_dg1_01"}, -- You are cleared to proceed, seize your objective... {actor = "player", say = "inf1_03_hun_02"}, -- It's probably going to get a lot tougher from here on in. {actor = "player", say = "inf1_02_hun_01"}, -- This is it men. Get ready. … Go Go Go! }, true); spTankDestroyed = conversations:add({ {actor = "player", say = "spc2_05_m04_01"}, -- Oh that had to hurt. {actor = "player", say = "spc7_08_m06_01"}, -- We've taken out the first vehicle. The convoy is stopping. }, true); spCounterAttack = conversations:add({ {actor = "player", say = "spc7_09_m06_01"}, -- Infantry from the convoy is assaulting our position. {actor = "radio", say = "spc7_11_sab_01"}, -- Take them all out, make sure none of them get through. {actor = "player", say = "inf2_09_m03_01"}, -- Solid copy. }, true); spConvoyOut = conversations:add({ {actor = "player", say = "spc7_13_m06_01"}, -- Success. The convoy is out of commission. {actor = "player", say = "inf7_x06_hun_06"}, -- Oorah! {actor = "player", say = "spc1_13_m04_01"}, -- Objectives complete. }, true); end function onSpeechEnd(entity, speech, id) finished = conversations:onEnd(id); end -- ------------------------------- function onTimer_TimerEnd() OFP:removeTimer("TimerEnd") if OFP:getObjectiveState("objConvoy") == "COMPLETED" then OFP:missionCompleted() else OFP:missionFailed() end end function onAllPlayersDead() OFP:missionFailedKIA(); end
1. The convoy is activated :
Before the convoy starts, we verify that all the stuff are well rendered.
function onSpawnedReady( setName, setID, tableOfEntities, errorCode ) if setID == setConvoyID then OFP:setMood("eConvTank", "EMoodCautious", "ADDTOEND") for i = 1, 2 do OFP:setMood("eSo" .. i, "EMoodCautious", "ADDTOEND") end for i = 1, 3 do OFP:setMood("eConvTruck" .. i, "EMoodCautious", "ADDTOEND") end OFP:addTimer("TimerConvoy", 7000) end end
We use the onSpawnedReady function to be sure that all the units and vehicles are really in-game (rendered). In the same time , we set the crews of each vehicle. Then we use a timer to let all to be rendered nicely.
2. Convoy starts :
function onTimer_TimerConvoy() OFP:removeTimer("TimerConvoy") OFP:setMaxSpeed("convTank", 25) OFP:move("eConvTank", "wpConvoyp", "ADDTOEND") local distance = 30 for i = 1, 3 do OFP:follow("eConvTruck" .. i, "convTank", (distance * i), "ADDTOEND") OFP:setLights("convTruck" ..i, true) end conversations:start(spConvoyStart) canCountConvoy = true end
We set the max speed for the tank to 25 , because if the tank goes too fast, the convoy will be broken and the others vehicles will become crazy. Then we order the Tank to move along the path. Then we order the other trucks to follow the tank (not the crew of the tank !).
local distance = 30 for i = 1, 3 do OFP:follow("eConvTruck" .. i, "convTank", (distance * i), "ADDTOEND") OFP:setLights("convTruck" ..i, true) end
We declare the variable "distance " with a value of 30 (meters). In the above loop, each time the loop is performed, the distance will be increased to n * distance value. So , the Truck 1 will follow the tank at 30 meters, the Truck 2 will follow the tank at 60 meters, and the Tank 3 will follow the tank at 90 meters. Ha, and it's a new year, we turn the lights on!
You cannot set the convoy like this :
Tank moves, Truck 1 follows the Tank, Truck 2 follows the Truck1, and Truck 3 follows the Truck2. That doesn't work !. (If that works properly, it's a miracle). Each members of a convoy must follow the first member (1st vehicle).
3. Hit the tank + counter attack :
function onDeath_convTank(victim, killer) conversations:start(spTankDestroyed) counterAttack() end function counterAttack() if OFP:isAlive("eConvTank") then OFP:forceDismountVehicle("eConvTank", "OVERRIDE") OFP:setMood("eConvTank", "EMoodAggressive", "ADDTOEND") OFP:defendPerimeter("eConvTank", "ADDTOEND") end for i = 1, 2 do if OFP:isAlive("eSo" .. i) then OFP:forceDismountVehicle("eSo" .. i, "OVERRIDE") OFP:setFormation("eSo" .. i, "F_DIAMOND", "ADDTOEND") OFP:setMood("eSo" .. i, "EMoodAggressive", "ADDTOEND") OFP:assault("eSo" .. i, "echSAR", "ADDTOEND") end end for i = 1, 3 do if OFP:isAlive("eConvTruck" .. i) then OFP:forceDismountVehicle("eConvTruck" .. i, "OVERRIDE") OFP:setMood("eConvTruck" .. i, "EMoodAggressive", "ADDTOEND") OFP:defendPerimeter("eConvTruck" .. i, "ADDTOEND") end end conversations:start(spCounterAttack) end
The counter attack will start once the Tank is destroyed.
I've put the counter attack in one function . It's easier to maintain.
In the counterAttack() function, we check if an echelon is alive before making any change. If the echelon is alive we order to dismount the vehicle, we change Mood to EMoodAggressive , because we want PLA very combative now , and we order them to defend the perimeter. In the same time we order the 2 SpecOps fireteams to assault the player echelon.
4. Kill all PLA. How-To know if they are all dead :
In the onMissionStart() function we have declared a variable called "canCountConvoy " and set it to false . When we have activated the convoy set , we then set canCountConvoy to true .
function onDeath(victim, killer) if OFP:getSide(victim) == 1 then if canCountConvoy == true then if not OFP:isAlive("grpConvoy") then OFP:setObjectiveState("objConvoy", "COMPLETED") conversations:start(spConvoyOut) OFP:addTimer("TimerEnd", 10000) canCountConvoy = false end end end end
We use the onDeath() event to detect if a unit is killed or not.
First we check if the victim is PLA. We do not want the function being used if the victim is a USM. This is a waste of energy. As we set the canCountConvoy to true, the function will work. We have put all the pla units in a single group called "grpConvoy ". When we test if the group is alive, that means : if one member of the group is alive. And when we test if not the group is alive, that means if not one unit of the group is alive (they are all dead !). Then we set the objective to COMPLETED , and we set a timer to let some time to the sentence to be said and to not terminate the game so brutally.
Download
Download MSSN file : SAR_HT2_Convoy.zip
