DCS lua Scripting Examples

[If this is the wrong place for this let me know]

For those interested in extending their missions beyond the DCS Mission Editor (ME) I thought I’d start a topic on using lua. You can ‘embed’ lua scripts using the ME but I find this clunky and harder to manage. So, here we are.

5 Likes

First up: “Modular-izing” your lua code

NOTE: There’s likely many ways to do this. This is just how I did it. It does not depend on any third-party code (mist, etc). Also, you don’t have to ‘un-sanitize’ your install (give it access to the OS) as the one important function works without doing this.

We will demo how to load scripts that you can edit freely withOUT having to reload the file each time. THAT is a big deal, really.

When I refer to LOCAL or INTERNAL:
By ‘LOCAL’ I mean the files on your local drive. INTERNAL means the files stored in the mission (miz) file.

What you’ll need:
Text editor: ranked by my preference concerning capabilities

  • Notepad (Windows): it will let you edit text but that’s about it.
  • Notepad++: Better. Tell it you’re using lua and you get syntax highlighting plus perhaps more if you search around
  • Visual Studio Code (VSC): Much nicer. Beyond being just a text editor you can install extension that let you do syntax coloring and more. Even debugging (though these sometimes have some annoying ‘bugs’ too).

The Magic: never have to reload a script file again.

You will need to ‘doScript File’ on one file but after that never again - you edit all the OTHER files in your text editor; on each run this will load the most recent, edited version. of your code.

This post uses an example mission file (.miz file) and I’ll reference using that. It will be linked to in the next 'How To" below - making the helicopter you’re escorting oribt [nearly] in place on command.

Need 2 files here;

  • A ‘loader’ file: “AI Escort Loader.lua”. This is the file that allows you to make all this work.
  • One, or more, code files. We have only one for this demo: “AIEscort.lua”. This file does all the heavy-lifting - the work.

ME Work
For this setup I have:

  • 1 CH-47 with several waypoints
  • 1 F-18 Hornet, set to ‘player’
  • Nevada map (it loads faster - if you’d like I’ll reproduce on a free map)

Create your files (2 in this case) in a folder with a handy name, ie; “MY SCRIPT/ESCORT CHOPPER”

Loader file: AI Escort Loader.lua
In this file enter the following:

loadfile(“X:\SCRIPTS\ESCORT CHOPPER\AIEscort.lua”) ()

Where “X:\…” is the folder you created. You don’t have to have the ‘AIEscort.lua’ file created, yet. We’ll do this shortly.

The ‘loadfile’ + the ‘()’ empty parentheses is the key here: it will load that file, chew on it and ‘expose’ the code in it to the lua engine inside DCS. It does this on each run of your mission.

NOTE you have to have the ‘source’ files inside your mission at some point using, again, Do Script file - AND they should be loaded BEFORE you call any of your code.

As is we are working with what I’ll call ‘local’ source files - that’s why you entered the full path above. For a release build of your mission you need a trigger that skips the Do Script File(AI Escort Loader.lua), an example:

IF flag(99) == true - load the ‘loader’ lua file.
IF flag(99) == off (false) - load the code files only (AI Escort.lua here) from the miz file. That’s why you must place them inside the miz file to be used during a release build.

ME Triggers
Somewhere near the start add a trigger:

Flag(99) == true/on
Once | time > 3 | Do Script File: load the file “AI Escort Loader.lua” - browse to that file and enter it.

The code:

  • Create a file with your code
  • Call the functions in your code files using:

On some condition (I use a menu item)

Do Script( MyCode.saySomething(“Hello!”)

This assumes your code contains a table called ‘MyCode’ with the function 'saysomething(_text) in it, like:

Inside AI Escort.lua file:

MyCode = {}
MyCode.saySomething = function(_text)
trigger.action.outText(_text, 3, false)
end

  1. When you’re happy with how the LOCAL versions of the scripts work you WILL have to perform a ‘DO SCRIPT FILE’ on all of them in the ME. So I’d wait util whatever your testing is working out (I do this externally with an app; it opens the miz and inserts the local files into the proper DCS folder)

  2. To toggle between the 2 versions you need a trigger at the start, before using any of the code.

I use the flag(99) but it’s up to personal preference/convention.

NOTE: when you change flag(nn) (99 in in this case) the Me WILL FORGET what the flag number is - it will default to ‘1’. Will drive you batty if you don’t catch this.

Goes like this:

IF flag(99) == true/on THEN : load the LOCAL version.
IF flag(99) == off THEN load the INTERNAL files (inside the miz file)

flag(99) is set inside the Mission Start trigger to either ‘true’ or ‘off’

The lua engine will find the code either way - it doesn’t matter ‘where’ it comes from as long as the code is defined.

Looks like this (not much help but maybe?)
image

The SWITCHED CONDITION bits are just simulating some condition that makes the chopper hold or continue, flag(1) and flag(2) here.

4 Likes

Getting an AI Chopper to fly a route and ‘orbit’ when we say so, then resume route

LINK UPDATED << to zip file containing the code files and mission (miz) file

Updated the link - I left the flag(99), see below, in the ‘ON’ position so I set it to flag(99) = OFF, so it will use the internal files.

Uses the Nevada map (loads really fast for testing stuff)


You will need to update the first line in the AI Escort Loader.lua to refer to where your script files are on your PC

In sim you need to access the F10 menu (however you have it bound) then your choices are “Orbit” or “Continue”. On “Orbit” the CH47 will turn in circles, on “Continue” it will resume its route.


Issue: ‘StopRoute’ works for ground units (say we’re escorting a convoy here, not a chopper) but does not work for aircraft. While you could figure out a way, I guess, using triggers, with embedded lua scripts too, this to me, makes things a bit more ‘tidy’.

The ME doesn’t have a ‘popTask’ trigger, which is necessary for this example. It only has ‘push’/'set’task. pushTask says, “do this now”. Fine, but what about when I want it to STOP doing that, and continue on its route? setTask just gives it another task - but we want it to do what it was doing before.

Via the F10 menu (see miz file) you can make them ‘orbit’/‘continue’ over and over.

NOTE: This didn’t format here the way I wanted, comments, so refer to the source code in the linked to zip file.

Escort =
{
groupName = nil,
lastPosn3d = {x = 0, y = 0,z = 0} – save position of our escorted chopper
}

Escort.main = function(_escortGroupName)

Escort.groupName = _escortGroupName
timer.scheduleFunction(Escort.loop, nil, timer.getTime() + 3)
trigger.action.outText("calling loop..", 3, false)

end

Escort.loop = function()

local grp = Group.getByName(Escort.groupName)
local u = grp:getUnit(1)
Escort.lastPosn3d = u:getPoint() 
return timer.getTime() + 3 -- do this every 3 sec's or a variable to simulate reaction time

end

Escort.hold = function()

local task = {
    id = 'Orbit', 
      params = { 
        pattern = "Circle",
        point = {x = Escort.lastPosn3d.x, y = Escort.lastPosn3d.z},
    } 
}

local grp = Group.getByName(Escort.groupName)
local grpCntrlr = grp:getController()
grpCntrlr:pushTask(task)
trigger.action.outText("holding..", 3, false)

end

Escort.continue = function ()

local grp = Group.getByName(Escort.groupName)
local grpCntrlr = grp:getController()
grpCntrlr:popTask()
trigger.action.outText("continuing..", 3, false)

end

Standby…I have an urgent ‘honey-do’ to do…BRB…will post the link below in a bit

Ok, back, where was I…

The ‘loop’ fxn above is necessary because DCS only knows waypoints. If the chopper is on a route (like in the linked file) there might be a LONG distance from where it is back to the fix it last passed. So, when you give the ‘orbit’ task will do it but it will go ALL THE WAY back to that wp.

I’d rather they just orbit where they are, roughly. The update rate (last line in the ‘loop’ fxn) is set to 3, which is seconds. You’ll need to change as you’d like.

Note we’re not storing more than that one point here - it gets overwritten each time. Also, you don’t really need the loop: just get the getPoint() when you give the command (this is how I do it in the larger system). I merely added that for those that might not understand the scheduleFunction() thingy. Your imagination can take over from there.

3 Likes

I’ll have to come back and read through this when I have to sit and understand it properly. Thank you so much for taking the time to put this together!

1 Like

Ok, I’m back. Sorry forgot I told my better half I’d help her do some rough carpentry…how could I forget that :thinking:

See above…