Moose User Guide
Moose User Guide
M OO SE
MISSION OBJECT ORIENTED SCRIPTING ENVIRONMENT
v1.0
1
MOOSE User Guide
Table of Contents
Document Purpose and Aim ................................................................ 1
The different paths .......................................................................... 2
How to Identify which parts to read. Who am i? ..................... 3
Beginner ...........................................................................................................................3
Intermediate ...................................................................................................................3
Advanced ...........................................................................................................................4
i
MOOSE User Guide
Limitations ...................................................................................................................52
The Guide covers all the individual elements needed to produce a MOOSE script and is
quite broad. It doesn’t duplicate available scripting documentation but does provide
you the tools and locations of such documentation, along with helpful pointers.
This document has been written and ratified by the team that maintains MOOSE. We
hope it answers questions you may have regarding MOOSE scripting, as well as
encourage you to get out there and write great scripts!
1
MOOSE User Guide
The “Beginner” entry point to learning MOOSE, is for those who have no interest in
learning a programming language and see MOOSE as a way to cut down the time they
need to spend on mission design, not increase it. This would include making best use of
the modules like RAT, Range, Dispatchers, Airboss, Fox. This is not necessarily people new
to everything around Lua and scripting, but people investing the least time for the most
gain.
The “Intermediate” starting point is for people that have a positive desire to learn more
scripting and are willing to invest some time for a medium term goal. They will be looking
at writing small scripts using several full classes going together, with some Core MOOSE
functions. These people will explore more and are open to small scripts and building upon
that.
The “Advanced” starting point is for confident people that can already understand most
of the key elements of scripting and are mostly looking at developing new scripts using
Core MOOSE, rather than using the large classes. They may not want to help out in the
community, but they probably have some experiences to offer other people.
Does learning MOOSE mean you will learn Lua? Do you need to learn Lua to use MOOSE?
Well, MOOSE and Lua are so entwined that in the process of leaning MOOSE you will also
absorb a good deal of Lua naturally. But you don’t need to learn Lua to begin using
MOOSE. In fact, MOOSE helps you learn basic Lua faster, and without you realising it is
happening.
2
MOOSE User Guide
Tools: Notepad++ (NPP), Baretail log tailer, Moose.lua, bookmarking links to the MOOSE
demo missions and MOOSE documentation, in your browser.
Starting Skills: Basic DCS mission Editor skills, reasonable PC/Windows Skills.
Process: Install NPP, and install Baretail. Then download Moose.lua and go to the MOOSE
demo missions and download and launch and tweak. Read the module documentation
for extra configuration.
Suggested content: Airboss, RAT, A2A_Dispatcher, RANGE, FOX and full modules that
require little or well documented configuration.
Limitations/when to move on: You won’t have Intellisense (a script environment feature
that provides MOOSE-specific auto-complete); and you probably cannot progress further
than simple lines of SPAWN code until you have. You will need to install LDT to learn more
advanced MOOSE scripting, using Intellisense; and you will have to learn how to debug,
serialise and log-tail next.
Intermediate
I want to write short scripts mainly to spawn things, make messages on screen, use timers
and make groups do things. I am open to learning more but want to start simple. I will
need help finding out how to write basic code, but I do not want to be intimidated from
the outset.
Tools: Lua Development Tools as an IDE, Log Tailer, Moose.lua static version, bookmark
links to the MOOSE demo missions and MOOSE documentation.
Starting Skills: Familiar with running scripts in Mission Editor, can create a basic script to
start with and find the documentation and check logs for errors.
Process: Install & configure LDT (or optionally Visual Studio Code, VSC), install Baretail,
download Moose.lua. then go to the MOOSE demo missions, download them and try
them out for ideas.
3
MOOSE User Guide
Suggested content: Look at SPAWN as a class, use and understand Wrappers, complete
this guide’s contents and full module demos (AIRBOSS, RAT, Dispatchers etc) and
download and tweak them as desired. Read the documentation so you get an idea
what they do. Will now begin experimenting and seeing what happens in the logs and
this will be how to learn from now on.
Limitations/when to move on: This segment takes a long time. Expanding your knowledge
is an ongoing process. The signs that you have grown beyond intermediate is that you
find most of your answers on the StackExchange web site, rather than the documentation.
You are learning more about Lua itself than MOOSE, you occasionally slip into SSE Lua to
test out MOOSE and you find the error in MOOSE pretty quickly.
Advanced
“I am familiar with scripting, I need to use a proper integrated development environment
(IDE) and tackle learning MOOSE with familiar tools. I am an adept self trouble-shooter
and only need a few pointers. I am going to continue writing my own custom scripts using
the core of moose ”
Tools: LDT configured with Intellisense and debugger, Github (GH) Desktop, signed up to
GH as a contributor, log tailer, Moose.lua static or dynamic version, MOOSE demo
missions, MOOSE documentation, Hoggit Wiki, Lua documentation (esp ch1-4),
Troubleshooting guide
Process: Setup LDT or VSC and a debugger as well as a log tailer. Setup your workflow to
run mission scripts directly from disk Read through the Hoggit Wiki to understand the
limitations of the DCS SSE. Read first four chapters of Lua online manual to understand
Lua’s main differences, Learning MOOSE from the CORE classes - WRAPPERS(GROUP,
UNIT, STATIC), MESSAGE, SCHEDULER, SET, ZONE, EVENT, POINT, CONTROLLABLE
Suggested content: Using MOOSE CORE classes over modules. Using core Lua like table
manipulation.
4
MOOSE User Guide
What is a Script?
A script is a plain text set of instructions read by a computer and processed. Scripts are
often used to interface between a complex compiled language and a simpler
language. For example, we write batch files to Windows because DOS was written in a
compiled language. DOS helps us navigate a computer file system, via a script.
DCS has a Simulator Scripting Engine (SSE) which acts as an interface between the DCS
application and the Lua scripting language.
What is MOOSE?
Mission Object Oriented Scripting Environment, is a scripting framework that attempts to
make the scripting of Lua instructions to the SSE, easier, simpler and shorter. You write a
script, using MOOSE’s words, in Lua to the SSE, which talks to DCS, which makes things
happen in the game. It is over 5MB of code, with as many words as the Bible and the
core of it was written over several years by one person. MOOSE is the brain-child of an
extremely talented programmer; FlightControl. If you want to know more about this topic,
check out FC’s “for dummies” videos found here:
https://youtu.be/ZqvdUFhKX4o?t=618
5
MOOSE User Guide
WARNING! GitHub is not user friendly when downloading large files, so, to download
Moose.lua:
• Click on Moose.lua at the link. Invokes a new window
• Click on Download button on the right. You will see the contents of Moose.lua
but not get the file!
• Right-click anywhere on the page. This invokes a selection list.
You can download Moose.lua from the develop branch also, and if you search the
“Include” folder you can find stripped down versions without documentation
(Moose_.lua). You can identify the version of MOOSE by the date it was created.
6
MOOSE User Guide
Confirm the script has been added by opening the .miz file with a zip utility and observe
the script files in the |10n\DEFAULT folder. Any reasonable number of different scripts can
be included in the mission and all will run independently, barring conflicting variables.
7
MOOSE User Guide
All of this is actually inside the Moose.lua file. You need to read the documentation for
the full modules to understand how to tweak what they do. But you do not need to worry
about all the CORE of Moose and scripting right now!
MOOSE is a community project and support are community based, please remember
when posting a question:
• Before posting anything, read your logs. We will ask what they say.
• Post your mission and your logs along with what you expected to happen and
what actually happened. Some things need the miz, the script and the log all
together.
• Do not use vague words this stuff is hard to help with! Be specific.
• The less detail you offer, the less chance you can be helped.
• Don’t ask people to check your script. That is what a computer is for.
• Don’t say it doesn’t work. Or is it broken. Say what it actually does.
8
MOOSE User Guide
Intermediate scripting
Welcome to the start of the Intermediate Scripting section of the complete guide. The
intermediate guide is everything. Very few people can skip the contents of the
Intermediate level, so it encompasses almost the entire guide. If a chapter has not been
marked as “Intermediate” it can most likely be skipped as optional.
There are places where the complexity gets a little intense. Please use this as a reference
and do not panic if things get beyond you. Take a break, do something different, enjoy
your nightmare and ask the therapist about it the next day. Or Discord.
The shopping list for what you need to set out on beginning to script is a lot longer than
the one you had before. This is the journey ahead:
9
MOOSE User Guide
Hoggit Bookmarks.
Grimes, who has long been a great help to DCS scripters, keeps these resources current,
mostly Hoggit Wiki pages. The functions are super important, but these pages hold more
details like enumerators and weapon types and categories that every MOOSE script
writer eventually needs.
https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation
https://wiki.hoggitworld.com/view/Category:Singleton_Functions
https://wiki.hoggitworld.com/view/Category:Class_Functions
Lua-Specific Bookmarks.
At some point all Lua script writers will encounter issues requiring the information held by
these two references:
http://Lua-users.org/wiki/TutorialDirectory
http://www.lua.org/manual/5.1/
10
MOOSE User Guide
Stackoverflow.com and the ED Forums hold much valuable information for Lua scripters,
as well.
Sometimes short MOOSE scripts are called snippets. We have included a few in the
Appendix and the MOOSE documentation includes many of them. We plan to create a
repository of useful snippets at some point.
DCS introduces new program code at each patch, occasionally breaking a MOOSE
function. Please report any non-functioning official MOOSE example scripts or missions
here:
https://github.com/FlightControl-Master/MOOSE/issues
MOOSE Documentation
MOOSE commands, called “methods”, are organized into categories, or “classes”.
MOOSE documentation describes in detail how to employ the MOOSE methods; so when
confused, refer first to the docs. Find them online here:
https://flightcontrol-master.github.io/MOOSE_DOCS/Documentation/index.html
https://github.com/FlightControl-Master/MOOSE_DOCS/tree/master/Documentation
Checkpoint reached:
➢ Bookmark our resources like documentation
➢ Discover the location of the Moose.lua (dev and released versions)
➢ Become familiar with researching the MOOSE documentation.
11
MOOSE User Guide
The video doesn’t cover everything, especially how to recover when things go wrong or
when LDT needs repair after installation. Be aware that LDT forces a single specific
workflow that may not suit a mature developer, but less-accomplished coders may find
LDT a helpful script-writing tool. FlightControl produced a 21-minute video on the
differences between certain text editors and the advantages of LDT here:
https://youtu.be/fEbQDbxNGb8
This video likely provides a lot of information superfluous to a novice’s needs, but it is
worth watching, as it covers most of the available options. Download Eclipse Lua
Development Tools from here:
https://www.eclipse.org/ldt/
https://forums.eagle.ru/showpost.php?p=4244387&postcount=3
Bottom line: scripts must be written on some text editor and much better tools than
Windows Notepad do exist.
Checkpoint reached:
➢ Setup Eclipse LDT (or VSC if proficient)
12
MOOSE User Guide
Your Saved Games folder has a folder with the name of your install. It can be “DCS” or
“DCS.openbeta” or something else, so beware if you have messed about with folder
names in the past. C:\Users\<USERNAME>\Saved Games\DCS.openbeta\Logs\dcs.log
Log tailing
https://www.baremetalsoft.com/baretail/ Baretail is a tiny free program will update in real
time as your mission progresses; so not only can you follow your logs as they happen, but
they highlight errors which can be very helpful in understanding where you messed up.
Glogg also does the same thing and you can get it here https://glogg.bonnefon.org/
You can also tail in Notepad++. We only recommend a tailer because actually you get
to learn a lot about DCS just having a glance at the logs. Watching the script progress in
realtime enables you to see the exact moment your script messed up, which is usually
right away. And imagine if you weren’t watching the logs, you stopped and went to
Discord and said, “Help, my script isn’t working”. No one would answer because you’ve
provided no information (and people can tell you didn’t read anything) you will become
stuck and frustrated, just because of a typo you missed.
Script troubleshooting and debugging are required skills during code creation; and the
DCS logs (refer to the next section) comprise the primary tool for understanding how a
script malfunctioned and to learn script debugging.
No one writes perfect code: all script writers make mistakes! Text copy-and-paste stands
alone as the most common script-killer (followed closely by general stupidity, adult
beverage imbibing, blindness, fatigue and dementia). Shadowze, a major contributor to
the MOOSE community, has prepared an excellent guide to script troubleshooting,
located here:
http://www.havoc-company.com/forum/viewtopic.php?f=30&t=1341
13
MOOSE User Guide
Before asking for help, please do read and understand all parts of this debugging guide,
especially with respect to:
● Having a good log tailer with highlighting and watching your logs in real time
● Understanding the logs’ feedback, interpreting errors, how to deal with them
● Identifying common issues. “X is nil, function not found,” etc.
● Debugging a script with env.info, messages and similar
● Adept at serializing a table to discover its contents
2020-04-16 23:44:34.375 INFO SCRIPTING: *** MOOSE STATIC INCLUDE START ***
Then follows the mission ‘registration’ of all the groups and units in the mission, followed by
real time recording of mission events. Look first for the key word ERROR instead of INFO.
An ‘error’ in programming parlance does not necessarily indicate a mistake. Rather,
‘error’ is a level of logging that displays text to a file, generally with the ‘log’ as a filetype.
Logging levels vary according to each development studio and some generic examples
include:
1. FATAL
2. ERROR
3. WARN
4. INFO
5. DEBUG
6. TRACE
DCS World will create env.info (an INFO log), env.warning (a WARNING log) and env.error
(an ERROR log). Each of these log types outputs lines of text to the DCS.log. An env.error()
halts the current program execution, whereas env.info does not. The log prints the event
logging level, the source and then the output, i.e.
WARNING EDCORE <error detail>
INFO SOUND <error detail>
Errors with the error level of “Scripting” may halt the script execution, depending on what
code block is running. In older versions of DCS, a message box popped up when this
happened, and the entire game screeched to a halt! That behavior can still be enabled
but no good reason exists to do so. Refer to:
https://wiki.hoggitworld.com/view/DCS_func_setErrorMessageBoxEnabled
14
MOOSE User Guide
A properly configured log tailer highlights errors via a regular expression or ‘regex’; and
good practice dictates highlighting env.info’s by putting a keyword in all the error
logging when writing the code. The log tailer can be configured to color-code these key
words to identify the Script errors from other types.
Log text interpretation skills improve as more logs are examined and with accumulating
experience in discerning important log events from ‘normal’ ones (didn’t we say that
some errors are normal?). The more time spent with a log tailer open and scrolling in real
time, the faster mission script errors will become apparent.
Debugging
When a script misbehaves for an unknown reason, add extra lines of script code that
output additional information into the logs. This can be as simple as having logic loops
detailed with a few log entries to show you which way the script decided to go:
if something1 then
env.info(“something1 is true”)
else
env.info(“something2 is true”)
end
Serializing mitigates the limitation that only text can be output by making an unreadable
datatype into a readable string. In other words, you cannot write a table to text in
something like a message, because a message requires the string datatype. To discover
the contents of a DCS table or something from MOOSE, look inside a SPAWN or ZONE
object. Lua has built-in serializing functions, and the MiST imported serializing functions are
available. Here is a custom serializing function for printing suspected faulty code to
screen and logs:
function s(obj) -- obj is the data parameter you want to deserialise
local Result = routines.utils.oneLineSerialize(obj)
MESSAGE:New(Result,10):ToAll()
env.info(Result)
return Result
15
MOOSE User Guide
end
This function can take most objects and return them in a human readable form. It helps
when you try to access something and get nil.
“My script doesn’t work. I want to do X and I explicitly followed the instructions, but it’s
broken”
The words, “broken” and “doesn’t work” are banned words, for they offer no meaningful
information; and MOOSE troubleshooting skills do not include mind reading. The first
analytical question is “what script?”, followed by “broken how?”
Another one:
To such a question a reviewer will immediately respond with “What did DCS.log say?”,
because the human eye reads code much less effectively than a computer (code is
read and executed in a millisecond). In fact, reviewers concentrate first on the visual
patterns and syntax, spelling and counting brackets, which strongly rely on how the text is
formatted. In Discord, even using the markdowns, a reader still relies on how the author
wrote the code, his whitespaces, bracketing habits and such. Use these markdowns in
Discord:
```lua
[code]
```
They make code more readable. Even then, always upload the logs, preferably snipped
around the error message. You did look, right? If you upload a mission, make sure it is free
of mods. Even one of the pay maps will eliminate some potential assistance.
Another one:
No, if a mission designer believes a script will enhance his mission, he must start learning to
write Lua. This User Guide makes a good place to start, and all the MOOSE guardians do
still try to encourage new scripters.
16
MOOSE User Guide
So the minimum requirement for a good request for help is: the script itself, preferably
whole, unless one’s MOOSE competence allows providing an analyzable snippet, and
the logs, preferably snipped to the only error in the logs that appears to be relevant, as
well as the line number indicated in the provided script like so:
MyCode = Rubbish[0] 🡨 Line 147
The logs almost always accurately record a scripting error, stating the error clock time,
script line number and a description of the error in ‘loggese’. Eliminating the error involves
reading the logs with a log tailer, interpreting the ‘loggese’, identifying the line and taking
corrective action.
Example errors:
2019-06-10 22:19:24.687 ERROR DCS: Mission script error: : [string
"Spawn:New("test"):InitCleanUp(120):InitLimit(3,0):Spawn()"]:1: attempt to index
global 'Spawn' (a nil value)
stack traceback:
[C]: ?
[string "Spawn:New("test"):InitCleanUp(120):InitLimit(3,0):Spawn()"]:1: in main
chunk
The above example comes from a DO SCRIPT trigger, and the key error text reads:
()"]:1: attempt to index global 'Spawn' (a nil value)
So, either the first or last ‘Spawn’ could trigger the error. Reading the docs on SPAWN
reveals that the case use is all UPPERCASE and ‘Spawn’ should have been ‘SPAWN’; thus,
this is a typo caused by this author quickly typing into a do script (with no Intellisense
warning of the mistake).
17
MOOSE User Guide
(tail call): ?
(tail call): ?.
● :707: - - the first line on which the error was encountered, followed up by two other
files with separate lines
● Attempt to index field ‘?’ (a nil value) – This is a classic table indexing issue. The
script attempted to look at a table at the requested point and found nothing
there, either due to an index error or no data existed at that location.
The last two lines show a “chain” of issues, in that the error propagated from one file, that
pointed to another, that pointed to another. These could have been separate functions
in the same script or in different files. As it happens, the error appears to result from an
add-on hook in the ‘Hooks’ folder that is causing this error every time an EVENT fires. (we
don’t have a solution as yet for this current error, but that is not important, since we have
included it only for illustrative purposes)
Checkpoint reached:
➢ Installed a log tailer to track dcs logs in real time
➢ Learn about the DCS log and errors
➢ Learn how to add debugging
➢ Learn some Troubleshooting processes
18
MOOSE User Guide
White Space
Lua ignores white space between lines and symbols, as well as at the beginning and end
of lines.
Comments
Good script writing practice includes liberally adding non-executing comments to help
others understand (and remind yourself) the intended behaviors of various sections of the
script. A double hyphen “--” identifies a single line comment and Lua ignores anything
following this symbol. Enclose multi-line comments with double hyphen, double square
brackets, like so:
Variables
Variables are named ‘buckets’ of data created by the scripter, much like words defined
in a dictionary. The names can consist of any combination of letters in the English
alphabet and underscores. They are case sensitive and the only prohibited characters
are numbers, special characters ( ( ) . % + - * ? [] {} ^ $, etc.), apart from “_underscore”,
and these reserved words:
19
MOOSE User Guide
The key word “local”, identifies a “local variable”. Unlike global variables, local variables
have their scope limited to the block where they are declared. A block is the body of a
control structure, the body of a function, or a chunk (the file or string with the code where
the variable is declared). Using local variables helps avoid the potential conflict of two
different blocks of code containing the same variable name for different purposes. In
such case Lua will regard the last identically named variable and ignore all others.
Functions
Functions are what make programs output ‘something’. The Lua language, the SSE and
MOOSE all contain some pre-defined functions. Functions start with the keyword
“function” and end with the key word “end”; and they can frequently become long and
complex.
A function can (and should) return “something” after being called. Calling a function by
writing its name produces the result directly. Function results can just return true or false, a
number from a calculation or a variable. Functions can be reused, in that you can make
your entire script a function, and then call it twice with two lines, rather than by twice
copy-and-pasting the entire script.
Booleans
A datatype of Boolean is either true or false https://www.lua.org/pil/2.2.html
Strings.
Lua strings are a data type consisting of text in either single or double quotes, for
example: “I have a $30.00 bar tab”. Lua treats strings as literal constants or variables,
accepting them, as written, for use in the script. https://www.lua.org/pil/2.4.html
Relational Operators.
< > <= >= == ~= https://www.lua.org/pil/3.2.html
When evaluating two things you can use the signs above; less than, greater than, less
than or equal to… etc. Note that novice scripters frequently confuse == and =. In Lua the
equals sign on its own assigns a variable to a value, whereas the double equals sign tests
if one value is identical to another.
Statements.
if … then … else … elseif … end https://www.lua.org/pil/4.3.1.html
‘if’ statements are the bread and butter method for creating logic. They are best used in
“either … or” type decisions.
Logical operators.
And, or, not. https://www.lua.org/pil/3.3.html
Lua stops processing a statement when it returns true, so put the more important and
common evaluations on the left side of statements.
20
MOOSE User Guide
Script Processing.
DCS program Scripts processes scripts from top to bottom once per occasion the script is
called. Whilst scripts can have loops, schedules and triggers firing, generally they
complete running in a millisecond or so. DCS can have any amount of scripts run without
limit. You can run separate scripts or put them together in one big super script. Once the
script has executed it will live in memory and if there are no more triggers or timers, it will
likely do nothing other than what it did at the instant it was run.
Lua stores variables and functions in memory for use when called by the script, and the
script will error if it encounters an object or variable not previously defined. Certain classes
like EVENT and SCHEDULER are structured to repeat as the mission progresses; therefore, if
a scripted action needs to occur sometime after initial script processing, that action must
be initiated either by one of the special repeating class methods or by an ME trigger
condition. Remember that any reasonable number of different scripts can be triggered
to run by the ME trigger feature conditions.
Tables.
A tutorial on Lua tables can be found here:
http://lua-users.org/wiki/TablesTutorial
Tables will start easy and get hard. They are used frequently to store lists of group names
for random selection in MOOSE. Be aware that the Lua language has some
unique/unusual rules for tables. One will eventually need to understand indexing and
Lua’s automatic indexing system. What is an Index? An index is a little tag that organises
table items so they can be found faster, bit like a large ring binder with coloured tabs
sticking out to mark interesting parts. The most common and practical index method is
numerical (1, 2, 3, 4, 5 etc.). Here is a trivial Lua table example:
Table = {‘thing1’,’thing2’}
Lua automatically assigns thing1 the index of 1 since it’s the first item in the list. When no
index is specified, Lua decides its own Indexing, starting with [1] (not zero like other
languages); so for
then Chest[3] is “Shirts”. One can define the indexes manually for other reasons:
Table = {[1]=’thing1’}
In that case, [1] is the index for ‘thing1’. Other terms for index are “key, value pair” or
associative arrays. In Lua, the key can be another string like
Chest[“First Drawer”]
Returns ‘socks’.
21
MOOSE User Guide
Note that tables do not require indexes. In such case Lua orders the table’s items
haphazardly. See more on pairs and ipairs. Note also that Lua ignores a trailing comma
after the last table entry:
Concatenation.
Concatenation is joining two things together, usually to output them as a single line of
text, where part of the text is evaluated code. Concatenation is a double fullstop/period
-- not one, not three, but two. Four is way off. Concatenation plays the very important
role of attaching strings of words to functions that turn into words to read a complete
sentence in English.
A = “Mickey Mouse”
MESSAGE:New( “My tank is called “.. A, 15 ):ToAll()
For Loops.
‘For loops’ access a table item when its table location, or its existence, is unknown. The
‘for loop’ iterates the table (sequentially selects each table item) to test each item
against the ‘for loop’s’ conditions. For using for with key, value in pairs and ipairs see:
https://www.lua.org/pil/4.3.4.html
https://www.lua.org/pil/4.3.5.html
Example:
Chest = { “Socks”, “Underwear”, “Shirts”, “Trousers” }
for i = 1, #(Chest) do
if i = “Socks” then
Chest[i] = “Ties”
end
end
This says, in English: The variable “i”, represents a number from one to the number of
entrys in the table ‘Chest’. Do count from 1 to that total entry number, and check if
Chest[i] == “socks” for each index, then replace Chest[i]’s value with the string “ties”.
After executing, the table Chest should look like,
The code did this: Is “socks” == “socks”? Yes, ok replace that entry with “ties”.
‘For loops’ are more commonly used instead of ‘while loops’ in DCS, as ‘while loops’ can
become an infinite loop that would crash DCS. “While 1” loops are not used in the main
programming block of DCS.
22
MOOSE User Guide
This concludes our treatment of the basic anatomy of Lua. Lua has many more features,
but this presentation covers 99% of DCS scripting possibilities.
Checkpoint reached:
➢ Read Lua online Ch1-4 quickly
➢ Learn Lua 101, basic rules
23
MOOSE User Guide
Other base classes create the Schedulers, Finite State Machines and tools that MOOSE
grows from.
MOOSE wraps the existing SSE classes like GROUP and handles their registration into its
own database for further use. After that comes higher level functional classes that
actually do things, but also some foundation code layers like Detection, Spawn and Task,
which build into entire AI dispatchers that detect, spawn and task AI entities. The
functional level also has standalone mission environment classes like Random Air Traffic
(RAT), Airboss, Missile Trainer, etc. that can live on their own at the high-level Functional
Class area. This is all explained in FlightControl’s initial “for dummies” videos found here:
24
MOOSE User Guide
What ED’s API does not allow is access to manipulate clients or their aircraft in
multiplayer. The API restricts available information and control, and does not allow
changing AI behaviour itself, only to initiate pre-scripted behaviours. MOOSE uses and
organizes these API’s to make them more accessible to the script writer. Scripts that might
take ten lines of raw API become two in MOOSE. MOOSE takes an API function like
“coalition.addGroup()” and makes it into a MOOSE function called “SPAWN:New()”.
SPAWN typically requires one line of script, whereas coalition.addGroup consists of a very
large table of groups and units and other things, totalling over 50 lines or more. Moreover,
MOOSE creates entire modules, such as A2A_Dispatcher that greatly simplify processes
that would prove daunting using raw API methods.
It would be a good idea at this point for a new scripter to understand exactly how limited
MOOSE really is, but reading the Hoggit wiki:
https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation
https://wiki.hoggitworld.com/view/Category:Singleton_Functions
https://wiki.hoggitworld.com/view/Category:Class_Functions
The point of this is to counter questions like, “Can I spawn a client slot, can I damage a
client plane, can I get the tgp loadout from the plane” The thing is, you just can’t do so
many things and until, as a scripter you understand what you can, and cannot do, you
will be eternally facing brick walls, confused and frustrated.
● In the Mission Editor triggers, as a DO SCRIPT - a small text box appears for directly
writing or pasting code.
● Lua can also be directly written into a conditional mission trigger of “Lua
PREDICATE”. It requires a Lua statement to be either true or false (Boolean)
response.
● Another Lua Predicate exists at an advanced waypoint action where a DO SCRIPT
can be written (or pasted).
● Selected advanced tasks like ON LANDING also have text boxes for scripted
conditions.
We are mostly concerned with delivering scripts from the main triggers, DO SCRIPT or DO
SCRIPT FILE. We can run as many as we like, join different blocks and so on, but the one
problem we face is that to change a script in DCS, we have to return to the Mission
25
MOOSE User Guide
Editor, open the triggers and re embed the new script. This is a lot of loading time which is
why using a faster workflow is absolutely critical to your success.
This line in a DO SCRIPT trigger action text box calls the script directly from its hard drive
location. Then, the scripter can modify his work in the script editor and run the mission
directly from the ME without restarting DCS and without re-saving the mission. LSHIFT+R is
the most useful script editing key bind in DCS, as it restarts the mission. This procedure also
implements printing the script file name (if you use multiple scripts) in the DCS.log, as well
as printing the script line numbers of log entries, to facilitate locating script errors.
This method also benefits from having errors in that script read out with accurate line
numbers, because embedded scripts are decompressed into the Temp directory and
given temporary names.
As another option, one can also launch the mission with the revised script without
restarting the mission, by placing the DO SCRIPT as an action called by a Menu Item.
Make the DO SCRIPT CONTINUOUS, by creating a user flag that resets to a constant
number on every activation of the F10 menu item. Then the script can be run multiple
times until it performs correctly without restarting the mission or returning to the Mission
Editor. A good, fast workflow is explained in detail in this video:
https://youtu.be/BMKBXjjKiDI
If you set this workflow up this way, you are likely to succeed in learning to script because
it will be a faster learning experience. A good workflow is vital.
Checkpoint reached:
➢ Understand the limits of the SSE by reading Hoggit
➢ Learn how to run and execute a MOOSE script
➢ Learn how to execute a script dynamically with ‘loadfile’
26
MOOSE User Guide
All things appear from SPAWN (out of nowhere, apparently). We present this class first,
because it has proved the most useful, powerful, easy and impactful function on the
scripting learning path. Literally everything dynamic and interesting starts from the ‘rabbit
out of the hat’, i.e. SPAWN. This is especially true for mission designers who are sick of
flying their own static missions. Head to the SPAWN docs and have a read to become
familiar with the class’s capabilities. Next grab a demo mission and experiment to see
what it does.
Analyzing SPAWN.
Now try to follow and understand the MOOSE file that corresponds to the SPA-14, SPAWN
demo mission:
The wording implies something is being created; however, having understood the
previous section on Lua’s basics we should look at this in a structural way first to
understand it better:
Variable assignment =
CLASSFunction:Function(arguments):Function(arguments):Function(arguments)
The ‘Something’ of our previous examination of SPAWN has become “:Do something (this
way) :Do something (this way) :Do something (this way)”.
In this case, the variable Spawn_Vehicle_1 holds all the information returned by the
running of these three following functions. The variable does nothing on its own, and is
optional in this case because SpawnScheduled does the actual spawning, but is useful
for later reference. In this case Spawn_Vehicle_1 holds the ‘Object’ that is the Spawn
instructions used to create a copy of the ME template “Spawn Vehicle 1”.
The three functions execute according to their arguments, and the SPAWN
documentation describes how the arguments of the functions of New(), InitLimit() and
SpawnScheduled() control the result. For New(), it’s saying, use the late-activated Group
called “Spawn Vehicle 1”. For InitLimit(), it’s saying ‘limit the spawning to 5 units and
unlimited groups’; and for SpawnScheduled() it’s saying ‘spawn every 5 seconds and
vary the spawn time by up to half either way of the original 5 seconds’ (i.e. 2.5 - 7.5
seconds).
27
MOOSE User Guide
Do note that the last function, SpawnScheduled() is the function that actually places the
template copy in the game world. Without SpawnScheduled(), all the defining
characteristics of the SPAWN have been created, but the object itself has not been
commanded to spawn. This peculiarity of SPAWN provides reason enough to read the
documentation.
The bucket (variable) is renamed MySpawn. The method segments appear in multiple
lines (remember Lua ignores white space); and, finally, SpawnScheduled() directly
spawns a copy of the ME template in DCS from the information in the variable MySpawn.
Observe that the variable MySpawn was assigned to the MOOSE object defined by the
characteristics of New() and InitLimit().
In other words, the colon serves to automatically pass the Object into the next function,
thus enabling “:function():function()” type of writing, a common occurrence in MOOSE.
Absorbing all this information resembles drinking from a firehose; but no easy way exists to
explain programming basics. The next steps will involve some trial and lots of error.
SPAWN in upper case represents, in this example, a previously defined object, named
variable (a group in this case), recognizable by observing the next part.
SpawnInZone describes what the method does with the spawn group object. Guess
what? This method spawns the object in a zone!
previously defined variable, one that defines the Zone for example, or a key word or
symbol included in Lua’s native lexicon. RandomizeGroup, for instance, must be a
boolean and MinHeight a numerical value (in meters).
Group Name is the name of an activated group placed with the DCS ME. Any plain text
in Lua (a string) must be enclosed in quote marks, like “Mustang”, otherwise Lua assumes
the text is a variable and will throw a fit if it thinks it has encountered a variable that has
not been previously defined.
SPAWN:New(SpawnTemplatePrefix)
These methods with atypical syntax can usually be recognized by the word “New”, as
well as by the fact that substituting a variable for SPAWN strips the method of any logical
utility. What would Variable:New() do?? “Some ME Group” is the name of a group
placed in the ME as late-activated and is termed a “template,” because the SPAWN
method uses that group’s characteristics (number of units, type of vehicle, path etc.) to
create a copy of the template in the mission.
Spawn_Group_1 = Spawn_Vehicle_1:Spawn()
The simplest adaptation of this script would change the line 1 spawn template name
from “Red Bandit 1” to the actual group name in your custom mission in the ME. Note
that in this script the spawn occurs immediately when the trigger DO SCRIPT FILE action
conditions are met.
29
MOOSE User Guide
Example 2. This somewhat more complex example creates a polygon zone and sends a
message when a designated group enters the zone.
--[[ Instantiate ME activated vehicle group Polygon Grp, whose route path defines the
polygon zone. Substitute your ME group name --]]
–[[ Instantiate a new polygon zone object in the mission with zone name Polygon A.
GroupPolygon defines the zone.--]]
function()
-- Condition statement checked at every run of the scheduler executes when true.
Messager:Stop()
--[[ Schedule parameters. {} == empty table. 0 == no delay from mission start, 1 == 1 sec.
intervals. --]]
{}, 0, 1 )
Example 3. This script was developed to overcome a DCS bug of seemingly invulnerable
AAA units.
30
MOOSE User Guide
-- When all of MG_Group are destroyed, stop monitoring hits and send message
if MG_Live < 1 then
MG_Group:UnHandleEvent( EVENTS.Hit )
MESSAGE:New( "All MG units destroyed. Great job.", 15 ):ToAll()
Next steps
Stop using the demo missions.miz and start experimenting with MOOSE functions. Run the
SPAWN demos. Experiment with them. Work with unfamiliar functions and Lua statements
and see where they break. This stage of learning MOOSE focuses on understanding
MOOSE’s capabilities, its potential. A MOOSE scripter must know the functions and what
they produce. It’s a chicken and egg scenario. How can one begin to create novel
mission activity without knowing the MOOSE tools available and their limitations?
This is an iterative cycle. The scripter discovers a few functions that inspire an idea. Then
they research the MOOSE resources to determine how the idea might be realized in a
mission and attempt to achieve it. If successful, great! If not, try to achieve some similar
activity and so on. Do not hesitate to try new things, as broken missions are infinitely
repairable.
Allen Saunders once said, “Life is what happens to you while you are busy making other
plans”; and learning to code is quite similar. That is, learning occurs whilst making code
mistakes and corrections, especially if the error was exasperating and time-consuming.
31
MOOSE User Guide
Watching videos and reading books as a learning experience, though useful, pale in
comparison.
Expect to experiment with SPAWN in one- or two-line scenarios for several days,
discovering its power and its limitations. SPAWN’s more advanced features include:
● OnSpawnGroup()
● Randomization
● Multiple spawns from an array in a “for loop”
● InitLimit() can fail because one neglected to read the arguments or doesn’t know
that UNIT and GROUP classes have different functions.
● Spawn() returns the group name
● Using ZONEs and tables
Be aware that while experimenting with SPAWN, one may well encounter shortfalls in the
documentation for InitCleanup(), try it (incorrectly) on ground groups and discover that
using InitCleanup() inside air bases, as opposed to outside air bases, yields different
results.
Checkpoint reached
➢ Learn about SPAWN (Moose 101) and create your first scripts using this class
32
MOOSE User Guide
In the SSE, to issue a command or option to a group, you find the group, find its controller,
send the controller a message to set the option.
local _grp = getGroup("Green State")
33
MOOSE User Guide
In MOOSE, because GROUP inherits CONTROLLABLE, you can issue the command to the
GROUP directly, without having to get the controller of the Group.
GreenStateGroup = GROUP:FindByName( "Green State" )
GreenStateGroup:OptionAlarmStateGreen()
So here we see directly an advantage of OOP and inheritance in simplification. This is just
one example, but there are many more.
The documentation of MOOSE requires some explanation. You notice the dot notation
continuing in the docs, e.g. Core.Zone. This means Zone is a Core Class.
• Database: MOOSE maintains a Database of all the mission objects and keeps this
updated throughout the mission.
• Zone: Everything to do with classic zones and polygon and moving zones.
• Point: Vector2, Vector3 and game coordinates and positions.
• Menu: Constructing F10 menus
• Message: Constructing messages to screen
• FSM: Finite State Machine is a class for handling and creating FSM processes
https://en.wikipedia.org/wiki/Event-driven_finite-state_machine
• Event: create Event Handlers for retrieving data from in game events fired by the
DCS SSE.
• Scheduler: This class handles and simplifies usage of the timer functions in DCS
• SET: Sets are automated tables of groups or units or other things that will be
maintained by MOOSE.
What is an Object?
In this case an object is literally a table, depending on what the object is, it might have its
name, its ID, and some other details. To see the contents, go to the debugging section in
the Advanced Troubleshooting chapter and use the provided function to serialise a table
to text and have a look. So, when we say the Group Object, this is a reference to the
Group in DCS. Not actually its name, but the collection of its name, its units, its ID, where it
is and so on.
What is a Wrapper?
A wrapper is another word for the Object. But for MOOSE the object is wrapped in a
more Moosey way. Wrapper classes contain group, unit, airbase, static, client, cargo,
controllable, positionable. The wrapper classes tend to focus on “things” in DCS.
34
MOOSE User Guide
35
MOOSE User Guide
With no Index bar the simplest way of searching for a class will be using the page search
CTRL+F, to look for Wrapper.Group. Let’s go to Group and see how the page is
formatted:
36
MOOSE User Guide
Finally, if you follow the link of the summary method you will come to the methods
detailed description. This may require some explaining if you are unfamiliar with how
functions are explained in documentation.
The Parameters will list, in order, the expected things you must type into the method to
make it not error. In the example above, the 1st parameter of GROUP:HasAttribute() is
“attribute”. “Attribute” must be of the datatype “string”. The important clue here is the
datatype is put right next to the parameter. Valid datatypes are normal Lua datatypes
like:
• String (“text”)
• Number (12)
• Boolean (true or false)
• Table (a table {} )
• nil and functions are also datatypes that can be used as Parameters. Often a
function is written to accept nil.
Optional Parameters
One thing to look out for is if the word (optional)
37
MOOSE User Guide
This means that you do not need to enter a parameter at all for the method to work on
the GROUP. This is because the method most likely has a default. The notes will make that
clearer.
The last section shows you the Return value, or what you get back if you use the function
correctly. It will also explain the datatype to expect.
“self” is a common return when nothing else might be applicable – i.e. the method is
doing something like making a tank shoot, that is the result of the method, but you know
you sent it correctly if the return value was checked and was something that the
documentation said it should be.
The final thing to note on Documentation pages is that if there are inherited classes that
you can use the Method on, they are listed under the main class. This will give the
appearance that the documentation repeats itself a lot, but since inheritance is such a
major part of OOP, the documentation of inherited classes is significant!
Checkpoint reached
➢ Learn how methods are called
➢ Start to understand about Object Oriented inheritance and hierarchy
➢ Learn some of the structures in Moose – core, wrapper, functional etc.
➢ Learn how to use the Moose Documentation
38
MOOSE User Guide
Wrapper.GROUP
The GROUP wrapper is one of the most interesting MOOSE classes because it reveals the
great depth of information obtainable from certain game entities. Testing can reveal
numerous group attributes, such as existence in a zone, being alive and being airborne,
as well as its speed, type, number, absolute in-game position, and count the number of
its units existing in a zone. One of MOOSE’s most confusing aspects for a new scripter
involves finding the name of a group spawned dynamically. The SSE returns the group
name to the script when a SPAWN is commanded, like this:
MySpawn = SPAWN:New(‘template’) -- Instantiates the Spawn Object, copying
attributes of ‘template’
HisName = MyGroup:GetName() -- Knowing the Group Object you can use GROUP
functions on it
Read the GROUP Wrapper class docs first to become familiar with the type of information
available. Then experiment with SPAWN to discover its group properties and print them to
screen. This is the optimum path to initial MOOSE scripting skill, but expect mastering the
GROUP methods to present some challenges. Group property information derives from
the group section of the mission table, as discussed previously in The Mission.miz\Mission
Table section.
Core.MESSAGE
MESSAGE is a logical next function to learn after SPAWN because it sends text to screen
very easily, and MESSAGE has few methods beyond its main function:
MESSAGE:New(“This is a Message to everyone”, 15):ToAll()
Note that the message text could have been assigned to a variable, as in
MyMessage = “This is a Message to everyone”
MESSAGE:New( MyMessage, 15):ToAll()
Core.SET
SET’s are similar to Lua tables and perform the important function of forming a single
collection of like objects, for example GROUP’s, UNIT’s or ZONEs etc. A scripter can create
a set of groups based on their:
39
MOOSE User Guide
● Name Prefix
● Coalition or Country
● Category (Airplanes, vehicles etc)
● Presence in a ZONE
Then s/he can manipulate the SET’s items, iterate (sequentially perform functions on)
each item of the SET or count them. Consider the function
SET_GROUP.ForEachGroupCompletelyInZone
Determine how to check whether or not the set’s groups have arrived at a certain place.
Sets are super powerful, and knowing group names is unnecessary, as the names can be
obtained by iterating the set.
Core.Zones
SPAWN section has many references to ZONES and this section examines in more detail
the very powerful ZONE class in MOOSE. The ZONE class implements the use of scripting to
control the behavior of DCS mission entities based on map location, for example
spawning a group at a particular area. Zones can be created in the ME or directly in a
script using the ZONE methods. A good first zone scripting exercise is creating a polygon
zone, a process impossible with the ME. Refer to the polygon zone example in Adapting
An Existing Script.
Example:
Zone_Veh = GROUP:FindByName( "3R_Zone_Veh" ) -- Vehicle that defines the zone
(3R_Zone_Veh)
Core.SCHEDULER
Now, things get interesting, because the SCHEDULER class introduces the element of time
to a script making it one of the most powerful classes in MOOSE. When called, a script
runs once and is ignored thereafter; so, if a script action must occur repeatedly or after
the initial script reading, like an aircraft entering a zone, the script must have a method to
monitor mission progress over time to identify the conditions that trigger the action.
Hence, on a schedule specified by the designer, the SCHEDULER periodically runs its set
of actions (the SCHEDULER function). It can be configured to start after a selected mission
time and then run on a specified frequency that is modified by a random factor, if
desired.
40
MOOSE User Guide
The main function resembles a “for loop” in normal Lua scripting. A schedule can check
virtually anything repeatedly. The following schedule runs after one second and again
every ten seconds forever.
SCHEDULER:New( nil, function()
if … then
--do stuff
end
end,
{}, 1, 10 )
The following schedule checks the mission every 5 seconds, after the first 15 seconds, to
identify when the ME User Flag 10 == 1, then causes the unit named Fire Group to begin
emitting red smoke. This is a simplified example that can be easily duplicated with ME
triggers, but the available actions in MOOSE could be much more complex than
anything reproducible with ME triggers.
if trigger.misc.getUserFlag( '10' ) == 1
then Smoker:SmokeRed()
end
end, {},
15, 5
Progress Recap
With the classes introduced thus far, the scripter has the tools to:
41
MOOSE User Guide
A couple of weeks should prove sufficient to become comfortable with these classes.
The ability to create a series of mission actions using these methods indicates a good
grasp of the basics.
42
MOOSE User Guide
Core.EVENT
Events are realtime feedback events form the game. The Event Handler class presents a
second method to initiate mission actions after mission start, using the specific DCS
recorded events, as described in the following graphic:
DCS writes these “events” to a table as they occur in real time as the mission progresses,
including the event’s who, what and where. Shot, Hit, Takeoff, Land, Dead, Crash and
Ejection are often used in scoring mechanisms; and the DCS mission debrief gets all its
data from these event tables. The EVENT class methods, unlike SCHEDULER, monitor only
the events that occur during the mission; and the EVENT function outputs a result when
the event “fires”.
Be aware that in a MOOSE script a UNIT:EVENT gets handled for that unit only! Similarly,
GROUP:EVENT gets handled only for all the units in that group. For all objects of other
classes, like SET’s, the subscribed events get handled for all units within the mission!
43
MOOSE User Guide
Core.Coordinate
One of the more complex classes, COORDINATE, introduces fine positioning control on
the game map and empowers the scripter to calculate distances and bearings between
points derived from zones and units, as well as obtaining weather and surface type,
providing waypoints to groups, displacing (translating) coordinates, converting to various
in-game readable formats like Bullseye, MGRS, LL DMS and putting smoke, explosions and
markers on the ground or air. COORDINATE provides fine positioning like placing units,
and it has randomization features, useful for random positioning of spawns using:
SpawnFromVec2()
Coordinate.
COORDINATE is a MOOSE class that has methods not only to describe points in space, but
also to manipulate the Vec point, get its attributes, get the characteristics of its physical
environment and do things like emit smoke. A coordinate can be expressed in map terms
like lat/long, MGRS, etc, in addition to x, y and z; and coordinates are directly obtainable
from the cursor position shown on the bottom bar of the mission map.
Wrapper.Controllable
The CONTROLLABLE class is almost redundant to the GROUP and UNIT wrappers. An AI
Controller for a Unit or Group is the interface to controlling that group or unit. This is where
you send commands. A good description of how these commands work is this Hoggit link
https://wiki.hoggitworld.com/view/Mission_Editor:_AI_Tasking . In MOOSE, the Unit or
Group inherits the controller so you never need to define the controller as a separate
thing. In pure SSE lua scripting you have to find the group, then getController() on it
before issuing the command.
So far, this User Guide has been devoted to equipping a DCS mission designer to do
basic scripting with MOOSE. The mission environment functional classes, however,
44
MOOSE User Guide
typically consist of “off the shelf” entire libraries of code that the scripter can configure for
a mission by customizing a few simple parameters. These classes can enhance a mission
so profoundly that mission designers completely unfamiliar with Lua or MOOSE frequently
attempt to employ them, leading to much confusion, frustration, help demands and
anger management sessions. Using A2A_Dispatcher, with RAT, CARGO, AIRBOSS, RANGE
and MISSILE TRAINER/FOX, a scripter can make a great training map, but lacking basic
scripting skills, s/he will have no idea how to make a table of group objects, save it to
disk, spawn from tables, randomize events of large spawns, randomize their positions and
so on.
Final Summary
By following this User Guide to this point, a novice scripter should be able to read and
understand most of the syntax in a MOOSE script, successfully used the SCHEDULER class,
have written a few new scripts for his own missions and done a bit of troubleshooting.
Script repair and log analysis skills probably still need considerable honing, however.
Completing the following challenges permits one to claim the title of MOOSE Scripter 3rd
Class:
● Written at least one simple function and reused it with at least one argument and
a return value
● Constructed and referenced data from a table
● Used the SET:ForEachGroup() iterator
● Employed the SPAWN:OnSpawnGroup() function
● Used a “for loop”
● Referenced, without prompting, the MOOSE docs and the SSE API on Hoggit
● Found, understood and fixed script errors
● Used the DCS.log to identify a script error
● Obtained help to correct a mal-functioning script
● Created custom debugging tools and methods
● Abandoned a scripting objective beyond his ken
● Collected and saved script snippets
● Have a more complex mission scenario to script
These competencies indicate one can write MOOSE scripts in DCS, regardless of one’s
scripting insecurities, so go boldly forth.
Reading other people’s work forms the path, now, for improving scripting skills. For
example, read the code of Ciribob’s CTLD script and try to discern how it works. CTLD is
somewhat long, very mature and complex; but it is structured and documented nicely for
self-education purposes. Of course, CTLD was written with MiST environment, but that
should present no difficulties at this stage, since many of the functions and methods will
require researching the reference documents.
These following activities will serve to further enhance a mission designer’s MOOSE
scripting skills:
45
MOOSE User Guide
Checkpoint reached
➢ Learn the CORE MOOSE classes (201)
➢ Learn the Remaining CORE classes (301)
We sincerely hope you MOOSE scripters have found this User Guide
useful and are encouraged to get out there and script!
46
MOOSE User Guide
Advanced MOOSE
The tallest hurdle is getting started. There is not much else other than working through
individual scripting problems or maths or design of scripts. You won’t ever feel that
confident, but you get faster at scripting and the things you improve on, are learning
what you can and cannot do with the SSE, as well as how easy it is to break DCS.
You can:
• Develop for Moose
• Setup the Lua Debugger
• Contribute to helping
• Go outside of the Mission environment and explore
To build repositories of your own and build Moose from the dynamic loader, follow this
guide
https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Beta_Test_Guide.html
47
MOOSE User Guide
The simulator has an archaic, creaky engine. Its original designers have become largely
unavailable and studying the engine leads to black holes and frustration. Shocking as it
may seem to millennials, who rely heavily on YouTube videos and Google search,
experienced people remain gold mines of information.
DCS has multiple Lua environments, each totally isolated from the others and having their
own distinct scope. The Lua environment of interest to MOOSE scripters is the “Mission”
environment. The other key Lua environment is the “server” environment, which runs on
the DCS server and has a different, documented API available in the DCS install under
the folder “API”. These two environments are isolated from each other for security
reasons.
Circa 2015, Ciribob found that both environments share global flags. This discovery led to
such scripts as Simple Slot Block, which uses Mission environment information (groups and
flags) and executes a net function in the net environment: onPlayerTryChangeSlot().
Meanwhile, SLMOD by Speed and Grimes used a Lua TCP connection to bridge the two
environments. “Web-comfortable” designers, Java and Python folks, developed
interesting web applications relying on exports to the cloud. Within ED the DCS ‘mission’
guys sat isolated and confined, whilst the SSE accumulated bugs.
One CAN run MOOSE (or any Lua) in the Net environment in the Saved
Games\DCS\Scripts\Hooks area by creating a GameGUI hook for the script. MOOSE is
not meant for server side execution. Any modifications, however, need a full restart of the
game engine; so, working in the Net environment proves problematic.
One can, however, increase the scope of Lua by stopping DCS from sanitizing these
libraries in the Scripts\MissionScripting.lua file by commenting out these lines like so:
-- sanitizeModule('os')
-- sanitizeModule('io')
-- sanitizeModule('lfs')
This procedure does not affect multiplayer server integrity checks (IC).
These edits do allow access to the file system and make available functions that can
wreak havoc, if assisted by a careless PC user who downloads and executes someone’s
48
MOOSE User Guide
mission without reading its scripts, a pretty dumb chain of events; but it does constitute a
risk. Due diligence executing downloaded files all but eliminates this risk.
File; \mission. This is a large, complex table created by the mission editor and read at
runtime. It loads the units and groups, time/day and coalitions, countries, weather,
triggers and such into the game engine. DCS reads the mission file only once at mission
runtime; therefore, it cannot be manipulated thereafter. Access its table output by asking
for ‘env.mission’.
49
MOOSE User Guide
The most unread but most-owned book (not the Holy Bible), the DCS User Manual in the
‘Doc’ folder of your DCS installation, has a useful description of the warehouse system.
We recommend periodic re-visiting the DCS User Manual, as every rereading seems to
reveal something new and wonderful!
Folder; \|10\DEFAULT. This folder stores mission scripts, among other things. Beware that
the DCS ME manages this folder and can delete user files, sound files for instance,
unreferenced in the mission.
File; \Track and Track Data folders populate from a saved track file, a recording of the
mission run in Multiplayer. These files hold all the recorded pilot keypresses, cockpit
switching and controller inputs. Mission events record sequentially and save into the
mission.trk. A ‘.trk’ is merely a .miz with track data and plays only in the forward direction.
Unfortunately, DCS records track data inaccurately and playback may not reflect actual
mission events, unlike what Tacview has achieved.
File; \Scripts folder contains some legacy Lua files on weather and earth GPS. Illogically,
mission scripts reside in \l10n\DEFAULT.
Cockpit Setup Folders. Using “Prepare mission” from the Mission Editor will create, for now,
KA50 and A-10C customised cockpit folders in the .miz file, saved as the device name,
e.g. “ABRIS”. These configurations overwrite the player experience.
User Folders. The user can create custom folders in the mission file and fill them with any
desired files. Custom folders added to the .miz zip will remain largely untouched by the
mission editor. The ME checks these user folders for extraneous media files declared in the
triggers and loads them into the mission. As previously stated, at mission save the ME
removes sound files manually added to the .miz, and not referenced in a trigger action.
File Precedence. The two areas that store configuration settings are the \Saved
Games\DCS\ folders and the DCS main installation directory. Settings in the DCS
installation folder pass in a hierarchical fashion into the game environment. First comes
the Game’s default settings, then the player configured changes from Saved
Games\DCS, and finally, the mission zip (.miz), which overwrites all client settings. That is
how a client’s settings and views get overwritten when joining a server.
The mission.miz\mission file, is a huge table containing the instruction set for building the
game environment. Its structure can prove confusing, but the mission file table plays such
an important role in DCS scripting that it merits its own section in this User Guide. This table
50
MOOSE User Guide
contains the definitions for all the mission entity, country and coalition attributes; and a
script can access this table during mission execution.
Any table in Lua can nest inside another table, and the mission table resembles a Russian
doll of nesting. Everything from the start of the mission resides in this table and its most
important element is the mission table’s GROUP structure. Why? Because any GROUP or
UNIT data requested by a script resides in this portion of the table. Spawning a group
using the SSE, without MOOSE or MiST, requires that the script exactly duplicate the
mission file table’s arcane group structure. The table also provides clues regarding
available group information obtainable during mission execution! The mission table
follows this structure for each group:
Mission["coalition"]["blue"]["country"][28]["ship"]["group"]
The red or blue coalition comes first; then the country, of which there are many,
enumerated by a number; then the group types of air, ground, ship etc. Under the
‘group’ comes its group number and all the group data from the ME. Getting a unit’s ‘x’
coordinate requires drilling down to:
Mission["coalition"]["blue"]["country"][28]["ship"]["group"][1]["units"][1]["x"]
51
MOOSE User Guide
Limitations
Recall that DCS reads the mission.miz file only once, at runtime, and copies it to a
Windows\temp directory. The DCS game engine continuously updates the data in the
Windows\temp copied table as the mission progresses after runtime; but a script can not,
during the mission, access the original mission.miz file. If, during the mission, a group
receives a new order from, say, Combined Arms, the original .miz table orders cannot be
recalled to discover the group’s primary tasking. One can obtain, however, the group’s
current location, health and ammo status from the continuously updated Windows\temp
table. The same goes for elements like client slots, as new slots cannot be created after
runtime. Nor can one move warehouses. Warehouse current inventory can be altered,
but its original stores and attributes cannot be accessed after runtime. Ships can
circumvent this limitation due to their special coded properties.
52
MOOSE User Guide
● Player slots have access to certain functions that Client slots do not have; and the
Player slot is designed for Single Player, where these functions operate in a
different environment, e.g. gates and cockpit arguments.
53
MOOSE User Guide
Following the A-10C release, around the release date of DCS World in 2012, a couple of
chaps called Speed and Grimes created a similar and now very mature higher level
scripting framework called MiST, which is the father of MOOSE, and which greatly
simplified DCS scripting. MiST and MOOSE have the same aims, but MOOSE is more
comprehensive and expands functionality with complete modules. Some functions in
MiST and MOOSE, for example spawning units, use completely different approaches. MiST
still follows the requirement for a table for a spawn, whereas MOOSE uses a late-
activated group to eliminate the table requirement. MOOSE has more features and has
incorporated many of the MiST functions into its own framework. Fortunately, the two
environments can happily cohabitate the same mission without noticeable impact.
Scroll forward to circa 2014. The author of MOOSE, Sven “FlightControl” found himself with
a LOT of time on his hands and a very big vision. He sat down and wrote the early forms
of MOOSE in an Object Oriented protocol. The Object Oriented development method
uses a set of hierarchical objects in their own ordered classes that can interface with
each other in a modular fashion. In early 2015 FlightControl released MOOSE to the DCS
user community with scant fanfare. Development has continued, nevertheless, and by
2020, MOOSE had a large and busy Discord server with over 1397 members, more than
doubling year on year, huge documentation and video libraries, several code
contributors/champions, and currently sits at over 147 thousand lines of code and
embedded documentation. The 265 page main thread on the ED forums has over
435,000 views. More DCS mission developers are discovering the wonderful world of
MOOSE every day.
54
MOOSE User Guide
"When you have eliminated all which is impossible, then whatever remains, however
improbable, must be the truth." —Sherlock Holmes
T/S: Shorthand for trouble shooting, the act of working out the cause and fix of an issue
Symptoms: Symptoms are apparent displays of the presence of a specific problem. They
are not the problem itself. There is the problem, and the problems symptoms.
Probable cause: The nominal direct line reason for the issue.
Root cause: The original start of the trail of causes leading to the final symptom.
RCA: Root Cause Analysis – The process of chasing the data and of 5 whys to understand
the original start of the problem.
Intermittent issues: Non reproduceable issues, issues that do not occur when the same set
of data is passed as an input. Intermittency is the T/S’s nemesis.
Timing issues: Timing issues are a specific type of intermittent issue that relies on
something called a race-condition. Race conditions are when two or more events that
are ‘time based’ may complete in a different order causing something unexpected. A
good example of issues that are caused by race conditions are testing for GROUP’s that
are alive just after they are spawned. Example: The code execution moves from the
Spawning of a Group and continues. The Lua responsible for Spawning the Group
performs the lower level C code to the simulation, makes prechecks (surface, validity)
and brings the groups model into the game. The Group’s arrival triggers a birth event. The
birth event is handled by Moose database. Moose adds the Group to the Database. If a
GROUP:FindByName() occurs on that group before it exists in the world, even though the
Spawn was called first, then FindByName will return nil as the object doesn’t exist (at the
time) Also happens with destroyed groups, the lag between the group being removed
from the database and the act of it “dying”.
Simplification/Isolation
When a MOOSE script produces unexpected results, remove all the code, except for
suspect sections and essential functions, and run the stripped-down script in a test mission
consisting of only essential mission entities. Different sections of the script can be
efficiently tested to isolate the misbehaving code; and the test script can be quickly
modified and re-tested until it yields the expected results. Isolation is a form of Bracketing;
however it is following the principal of simplifying the issue to the barest form to remove
any complexity. Reproduce in the simplest of forms and you have less complexity to
muddle through.
Reproduction
Reproducing the problem is the second step to effective troubleshooting, but many
people do not know why it is required. When you can cause the problem on demand,
you know the steps to make something happen and have “captured” the issue. You can
then institute one of the common T/S techniques like bracketing and change the inputs.
Providing the miz file is part of the steps to reproduce. Reproduction should be done
locally on the tester’s workstation. Without reproduction, the problem is not transportable.
If the problem is not transportable, the Root cause could still be something specific to the
55
MOOSE User Guide
local environment or process. E.g. a person has a broken script. They provide the mission
to someone to check. The mission runs fine (WFM – works for me). This shows the problem
was either a) not isolated effectively b) not reproduced effectively. c) a problem of
environment (scripts in the profile, installation issues, PEBKAC)
Bracketing/BlackBox
Bracketing or Black Box troubleshooting is simply ruling things out until you are left with the
only possibility. Anyone can do Black Box troubleshooting because you have no
requirement to know anything under the hood. You just take the inputs and rearrange
them so that on each execution of the reproduction steps you rule out a possible cause.
The opposite, White Box T/S, assumes you understand all the code and processes
absolutely. In most cases this is never available. DCS will forever be a Black Box scenario!
E.g. My table has 18 groups. The script intermittently errors as it goes through the table.
One of the objects is the cause. Bracketing would run the script with objects 1-9, see if the
problem reproduces. If so, we have eliminated objects 9-18 and we can use objects 1-8
to test against. The same technique works by removing processes from a multi process
operation or segments or any other large amount of inputs.
Be Data Driven
Forming conclusions about an issue should never be made on a gut instinct, even a small
conclusion can close off an avenue of investigation. All avenues remain open until the
data presented shows that it is not. Chasing the data means to follow where the
evidence (data) points to, and specifically you can use this to see what executed, to
validate each step of the way. Debugging comments is a way of being Data Driven. You
put in a debug comment and see it executes and you know that the code processed
this normally.
56
MOOSE User Guide
Appendix D - Snippets
Over time, MOOSE script writers began saving bits of scripts now called snippets, as a
time-saving convenience. These script snippets perform a specific mission action and can
be judiciously pasted into a developing script to avoid constantly reinventing the wheel.
We recommend this practice for all MOOSE scripters. A few examples follow:
-----------------------------------------------------------------
-- ASSERT LOADFILE: dynamically loads a script file from a hard drive folder.
---------------------------------------------------------------
---------------------------------------------------------------
-----------------------------------
---------------------------------------------------------------
CFile = "mySaveGame.txt"
if file_exists(CFile) then
dofile(CFile)--execute the Lua inside the file
env.info("Campaign: Saved Campaign exists, loading from file...")
trigger.action.outText("Loading existing Campaign data from file...",4)
else
57
MOOSE User Guide
---------------------------------------------------------------
---------------------------------------------------------------
function checkblue(ABname)
local tmp = coalition.getAirbases(2)
for i=1,#tmp do
if tmp[i]:getName() == ABname then
return true
end
end
end
---------------------------------------------------------------
function AliveSet(set) --thanks to CraigOwen for his help on this, he put me on the
right path and it's down to him we stuck with it! #CommunityWin :)
local count = 0
set:ForEachClient(
function (setclient)
if setclient:IsAlive() then
count=count+1
end
end)
return count
end
---------------------------------------------------------------
-- Get the first alive group from a SPAWN and RTB it if not already
---------------------------------------------------------------
local _Num = 5
local _Table={"Spawn1", "Spawn2", "Spawn3",}
local _Zones={ZoneA, ZoneB, ZoneC,}
Spawns = {} --empty table for multiple spawns
for i=1,_Num do
local tempRandGroup = math.random(1,_Table.getn(_Table))
local tempGrpAlias = "Spawn-" .. i
Spawns[i] = SPAWN:NewWithAlias(_Table[tempRandGroup],
tempGrpAlias):InitRandomizeZones(_Zones)
Spawns[i]:Spawn()
end
---------------------------------------------------------------
-- Spawn from Static unit via Scheduler and go to random place in Zone when spawned
---------------------------------------------------------------
SCHEDULER:New( nil,function()
MESSAGE:New("Checking for new units",10):ToAll()
trigger.action.outSound("micclick.ogg")
Something = trigger.misc.getUserFlag(1)
end, {}, 5, 600, 0.2 )
---------------------------------------------------------------
59
MOOSE User Guide
This little beauty sets off a ground explosion at a random location in an ME zone ( “Boom
Zone” ) while the Player is in the zone. It starts immediately (time 0) when an ME trigger
action starts the script and explosions occur on average every 4 seconds with a 50% time
variation.
---------------------------------------------------------------
function Bang(plane)
Outer = 500
Inner = 250
Zstart = -500
Ystart = -100
ExploStrength = math.random(1,50)
PlanePos = POINT_VEC3:NewFromVec3(tgt:GetVec3())
RngPos = POINT_VEC3:NewFromVec3( PlanePos:GetRandomVec3InRadius(Outer, Inner) )
AdjustedPos = RngPos:AddX(0):AddY(Ystart):AddZ(Zstart)
AdjustedPos:Explosion(ExploStrength)
if Outer ~= 0 then
Outer = Outer - 10
end
if Inner ~= 0 then
Inner = Inner - 5
end
if Zstart ~= 0 then
Zstart = Zstart + 10
end
if Ystart ~= 0 then
Ystart = Ystart +5
end
end, {}, 0, 1, 1)
end
--------------------------------------------------------------
BaseAltitude = 4500 --in metres the lowest point of the barrage, or base altitude
above ground
FlakHeight = 1500 --in metres, the total height of the Flak that it can fill above the
base altitude. eg. 4500 and 1500 gives a volume of 4500-6000m
60
MOOSE User Guide
Periodicity = .7 --a value measured in seconds for which each flak burst will occur.
Randomness = .7 --a number between 0 and 1 that can change the Periodicity above. A
value of 0 makes it observe the periodicity exactly. A value of 1 will randomise it up
to double the time
Strength = 50 --the strength of the explosion. It's a bit difficult to explain but is
about double the number of "lbs" of explosive
MEflag = 10 --the flag you are using to trigger flak to start and stop
MEflagNo = 1 -- the flag number of the flag you want to start the flak. When the flag
is this number it starts, (until the flag is not that number, checked every Period
Bursts = 6 --the number of flak bursts per Period. Represents individual guns. Note,
combined settings will have a performance effect
flakzone = ZONE:New("flak") --The zone where the flak will occupy. Zone name is "flak"
function Flak()
local alt = math.random(1,FlakHeight)
local rngpos = flakzone:GetRandomPointVec3()
local AdjustedPos = rngpos:AddX(0):AddY(BaseAltitude + alt):AddZ(0)
AdjustedPos:Explosion(Strength)
end
else
--nothing
end
end, {}, 0, Periodicity, Randomness)
---------------------------------------------------------
function s(table)
local Result = routines.utils.oneLineSerialize(table)
MESSAGE:New(Result,10):ToAll()
env.info(Result)
return Result
end
---------------------------------------------------------
61
MOOSE User Guide
SU25TSETClient:ForEachClient(function (MooseClient)
exists = CheckClientExistsInSet(MooseClient:GetName(),clientName)
end)
CheckClientExistsInSet = function (client,searchString)
if client == searchString then
return true
else
return false
end
end
--------------------------------------------------------------
DeleteLanding = EVENTHANDLER:New()
DeleteLanding:HandleEvent( EVENTS.Land )
function DeleteLanding:OnEventLand( EventData )
ThisGroup = GROUP:FindByName(EventData.IniGroupName)
GroupUnit = ThisGroup:GetDCSUnit(1)
FirstUnit = UNIT:Find(GroupUnit)
if FirstUnit:GetPlayerName() then
PlayerName = FirstUnit:GetPlayerName()
env.info(PlayerName .. " has landed")
else
env.info("Not a player landed, deleting")
ScheduleDelete(ThisGroup)-- custom schedule to delete a group
end
end
--------------------------------------------------------------
-- Match a string
end
------------------------------
function getAB(airbaseName)
local TmpAB = AIRBASE:FindByName(airbaseName)
62
MOOSE User Guide
if TmpAB:GetCoalition() == 1 then
return "Red"
elseif TmpAB:GetCoalition() == 2 then
return "Blue"
elseif TmpAB:GetCoalition() == 0 then
return "Neutral"
else return "Not an Airbase"
end
end
MySpawn = SPAWN:New("bKobCAS1"):InitLimit(1,0):SpawnScheduled(500,.2)
MySpawn:SpawnScheduleStop()
SCHEDULER:New( nil,
function()
if getAB("Batumi") == "Red" then
MySpawn:SpawnScheduleStart()
else
MySpawn:SpawnScheduleStop()
end
--------------
-- TABLE SAVE
-- http://lua-users.org/wiki/SaveTableToFile
return string.format("%q", s)
end
63
MOOSE User Guide
64
MOOSE User Guide
end
file:write( "}" )
file:close()
end
function table.val_to_str ( v )
if "string" == type( v ) then
v = string.gsub( v, "\n", "\\n" )
if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
return "'" .. v .. "'"
end
return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
else
return "table" == type( v ) and table.tostring( v ) or
tostring( v )
end
end
function table.key_to_str ( k )
if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
return k
else
return "[" .. table.val_to_str( k ) .. "]"
end
end
function table.tostring( tbl )
local result, done = {}, {}
65
MOOSE User Guide
----------------------------------
function IntegratedbasicSerialize(s)
if s == nil then
return "\"\""
else
if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function')
or (type(s) == 'table') or (type(s) == 'userdata') ) then
return tostring(s)
elseif type(s) == 'string' then
return string.format('%q', s)
end
end
end
-- imported slmod.serializeWithCycles (Speed)
function IntegratedserializeWithCycles(name, value, saved)
local basicSerialize = function (o)
if type(o) == "number" then
return tostring(o)
elseif type(o) == "boolean" then
return tostring(o)
else -- assume it is a string
return IntegratedbasicSerialize(o)
end
end
local t_str = {}
saved = saved or {} -- initial value
if ((type(value) == 'string') or (type(value) == 'number') or (type(value) ==
'table') or (type(value) == 'boolean')) then
table.insert(t_str, name .. " = ")
if type(value) == "number" or type(value) == "string" or type(value) ==
"boolean" then
table.insert(t_str, basicSerialize(value) .. "\n")
else
66
MOOSE User Guide
end
--------------------------------------------
function getNearest()
local ab = world.getAirbases()
nearestPair = {dist = 1000000} -- assign large value to start so that almost anything
will be smaller than it
for i = 1, #ab do
local coa = (ab[i]):getCoalition()
local pos = (ab[i]):getPoint()
if coa ~= 0 then -- not neutral
for j = 1, #ab do
local oCoa = Airbase.getCoalition(ab[j])
--TO DO MUST also exclude Ships.
if i ~= j and (coa ~= oCoa and oCoa ~= 0 ) then -- not the same airbase
and not the same coalition and not neutral
local dist = mist.utils.get2DDist(pos, (ab[j]):getPoint()) -- whatever
you wanted to use to get the distance between the two points
if dist < nearestPair.dist then -- check against the shortest distance
between opposing side airbases
nearestPair = {dist = dist, bases = {i, j}} -- newly defined entry
end
end
end
end
end
67
MOOSE User Guide
-----------------------------
-- Execute a restart batch file based on real server time, from inside mission
REBOOT=false
Reboot1 = 2350
Reboot2 = 0700
ServerTime = os.date("%H:%M")
NumberTime = os.date("%H%M")
env.info(os.date("The server's local time is " .. ServerTime))
MESSAGE:New("The Server's local time is " .. ServerTime):ToAll()
MESSAGE:New("The Server will reboot at 23:30 in " .. tonumber(NumberTime)-0010
.. "mins."):ToAll()
if tonumber(NumberTime) == 0010 then
MESSAGE:New("The Server is rebooting"):ToAll()
--os.execute[[C:\\Users\thebg\\Desktop\\restart.bat]]
REBOOT=true
end
------------------------------
EventHandler = EVENTHANDLER:New()
EventHandler:HandleEvent( EVENTS.Dead )
function EventHandler:OnEventDead( EventData )
-- POTI EAST
if EventData.IniUnitName == 74645955 then --Scenery has a IniUnitName. You can find it
by blowing it up first and catching the ID
MESSAGE:New("Poti East Bridge destroyed",10):ToAll()
flag1 = 1
trigger.action.outSound("radio1.ogg")
elseif EventData.IniUnitName == 74645956 then
MESSAGE:New("Poti East Bridge destroyed",10):ToAll()
flag1 = 1
trigger.action.outSound("radio1.ogg")
end
68
MOOSE User Guide
---------------------------
---------------------------------
local ZoneList1 = {
ZONE:New( "ZONE1" ),
ZONE:New( "ZONE2" ),
ZONE:New( "ZONE3" ),
ZONE:New( "ZONE4" ),
ZONE:New( "ZONE5" ),
}
function ReRouteR( VehicleGroup )
local ZoneNumber = math.random( 1, #ZoneList1 )
local FromCoord = VehicleGroup:GetCoordinate() -- Core.Point#COORDINATE
local FromWP = FromCoord:WaypointGround()
local ZoneTo = ZoneList1[ ZoneNumber ] -- Core.Zone#ZONE
local ToCoord = ZoneTo:GetCoordinate()
local ToWP = ToCoord:WaypointGround( 50, "On Road" )
local TaskReRoute = VehicleGroup:TaskFunction( "ReRouteR" )
VehicleGroup:SetTaskWaypoint( ToWP, TaskReRoute )
VehicleGroup:Route( { FromWP, ToWP }, 4 )
end
Rtanks = SPAWN
:New("rtanks")
:InitLimit(3,0)
Rtanks:OnSpawnGroup(function( SpawnGroupR )ReRouteR(SpawnGroupR)end)
69
MOOSE User Guide
:SpawnScheduled(100,0)
70