Jump to content

Tutorial: Introduction to Lua scripting


Recommended Posts

There is communication but its simply not as extensive as it was. Essentially the guy who wrote the scripting engine made documentation and added suggestions rather quickly. After he left ED it wasn't quite the same. So feature requests now go through normal channels instead of direct communication with the guy.

The right man in the wrong place makes all the difference in the world.

Current Projects:  Grayflag ServerScripting Wiki

Useful Links: Mission Scripting Tools MIST-(GitHub) MIST-(Thread)

 SLMOD, Wiki wishlist, Mission Editing Wiki!, Mission Building Forum

Link to comment
Share on other sites

Before I get into making something that might already exist, do we have a comprehensive, general-use, tool with tableShow -like features but instead of this kind of data:

 

        [env][mission][failures] = table: 000000007F3C6BE0 {
           [env][mission][failures][pp_damage_EmergMaxTempr] = table: 000000007F3C6C30 {
               [env][mission][failures][pp_damage_EmergMaxTempr][hh] = 0,
               [env][mission][failures][pp_damage_EmergMaxTempr][prob] = 100,
               [env][mission][failures][pp_damage_EmergMaxTempr][enable] = false,
               [env][mission][failures][pp_damage_EmergMaxTempr][mmint] = 1,
               [env][mission][failures][pp_damage_EmergMaxTempr][id] = pp_damage_EmergMaxTempr,
               [env][mission][failures][pp_damage_EmergMaxTempr][mm] = 0,
               },
           [env][mission][failures][FWD_TANK_LEAK] = table: 00000000CCD75CF0 {
               [env][mission][failures][FWD_TANK_LEAK][hh] = 0,
               [env][mission][failures][FWD_TANK_LEAK][prob] = 100,
               [env][mission][failures][FWD_TANK_LEAK][enable] = false,
               [env][mission][failures][FWD_TANK_LEAK][mmint] = 1,
               [env][mission][failures][FWD_TANK_LEAK][id] = FWD_TANK_LEAK,
               [env][mission][failures][FWD_TANK_LEAK][mm] = 0,
               },
           [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE] = table: 00000000CCD75D40 {
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][hh] = 0,
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][prob] = 100,
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][enable] = false,
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][mmint] = 1,
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][id] = CTRL_RUDDER_ROD_MAJOR_DAMAGE,
               [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE][mm] = 0,
               },

 

We can get only API-level summary:

 

        [env][mission][failures] {
           [env][mission][failures][pp_damage_EmergMaxTempr]
           [env][mission][failures][FWD_TANK_LEAK]
           [env][mission][failures][CTRL_RUDDER_ROD_MAJOR_DAMAGE]
               [env][mission][failures][xx][hh],
               [env][mission][failures][xx][prob],
               [env][mission][failures][xx][enable],
               [env][mission][failures][xx][mmint],
               [env][mission][failures][xx][id],
               [env][mission][failures][xx][mm],
               },

 

Basically I'm looking for something that dumps a table and returns only it's structure and possible values, not enumerate every single value entry. Grimes, since you made the online API reference, I'm wondering if used a tool to distill that info from tableShow or went through the dump by hand?

Link to comment
Share on other sites

  • 4 months later...
The previous stages worked fine

Thank you to help me, i need to understand :thumbup:;)

 

Found it, this one was easy to miss. ;)

 

The second line has a line break. Because of the tiny text window, the visual representation is the same with or without it, but if you copy the code to Notepad (or Notepad++, or any other editor), it'll become visible:

 

-- This one is okay
env.warning("Some output via env.warning")

-- And this one causes the error
env.warning("some other
output")

Link to comment
Share on other sites

Hey ok

 

Hi Yurgon I think of having understood,in fact when I seized the text in the window of script of ME in the second line instead of letting in in the keyboard, I would have of to continue to write the line.

Isn'it?

 

Thank you now is good in the file dcs.log so i can continue to practice your tuto ;)

Thank you very much :thumbup: ;)

Link to comment
Share on other sites

  • 5 months later...

I am new to DCS Lua scripting and I wonder how to properly set up an aircraft group in Lua. It is very easy to define a group in the mission editor, but I do not know how to express a group in a Lua code properly.

I have already seen an example for defining a ground group, but now I want to define an aircraft group with certain waypoint, loadouts etc.

So e.g. when I want to define the loadout then I do not know what to write after the equals-sign:

["payload"] =

Currently I could only make the following code work for a simple aircraft spawn, but it still does not have all the important informations the aircraft would need:

 

-- see https://forums.eagle.ru/showthread.php?t=174318

-- Initialization
if (initialized == nil) then
   initialized = true
   
end

function test()
   local iX, iY = -260000, 700000 
   local groupData = {
       ["visible"] = false,
       ["taskSelected"] = true,
       ["route"] = 
       {
       }, -- end of ["route"]
       ["groupId"] = 2,
       ["tasks"] = 
       {
       }, -- end of ["tasks"]
       ["hidden"] = false,        
       ["units"] = 
       {
           [1] = 
           {
               ["type"] = "Su-33", --other airplane types are stored in ...\DCSWorld\Scripts\Database\planes
               
               ["x"] = iX,
               ["y"] = iY,
                    ["alt"] = 7000, -- altitude in meters
                    ["speed"] = 300,
               ["unitId"] = 2,
               ["skill"] = "Excellent",
               ["name"] = "Russian Air Unit1"                
           }, -- end of [1]
       }, -- end of ["units"]
       ["name"] = "Russian Air Group1",
       ["start_time"] = 0,
       ["task"] = "Intercept",
   } -- end of [1]
       
   coalition.addGroup(country.id.RUSSIA, Group.Category.AIRPLANE, groupData)  
end
test();

It would be great if we could simply export an existing group from the mission editor as a Lua code. Then it would be much more easier for me to learn the Lua expressions.

 

 

Btw. I am trying to use this Lua script to spawn a group at a random location within a certain area. I know that you can also simulate random spawns in the mission editor using triggers and flags, but that method is simply to ponderous for complex mission designs.


Edited by Tiramisu
Link to comment
Share on other sites

It would be great if we could simply export an existing group from the mission editor as a Lua code. Then it would be much more easier for me to learn the Lua expressions.

 

That might actually work. :)

 

I don't actually know the answer to your question.

 

But if there's one thing I know about Lua, it's that everything is a table.

 

So my take on this would be to place a unit via Mission Editor, equip it with some loadout, and then try to get the properties of this as a Lua-object (getUnit("myID")) by traversing it with table-functions. I haven't written any Lua in years, but as I recall it, there should be table iterators like "for" or "foreach" or "each" or "while" or something of that kind. That should give you a way to progress into the object until you reach the payload, and then you know how to set up your own units just via Lua. :thumbup:

Link to comment
Share on other sites

Now I have tried to follow your suggestion by using the methods Group.getByName and getUnit. With these methods it seems like I could get the data structure of the payload.

 

 

So first I created an airplane group called "Premade Group" with the tools of the mission editor and I gave it a proper payload with some missiles like the R-27. Then I used this Lua code to print the payload table in the log:

 

 

function DCSPrint( Message )    
   env.info( Message, 20)
   --trigger.action.outText( Message, 20)
end

function DisplayTableStructure( Data, iHierarchy )    
   local sLineSpace = ""
   for i = 1,iHierarchy do
       sLineSpace = sLineSpace .. "\t"
   end
   if( type(Data) ~= "table" ) then
       DCSPrint( "##### No table available! #####" )
       return;
   end
   for key, value in pairs( Data ) do
       local sType = type( value )
       if( sType == "table" ) then
           if( type(key) == "string" ) then key = "\"" .. key .. "\"" end
           DCSPrint( sLineSpace .. "[" .. key .. "]" .. " = {" )
           DisplayTableStructure( value, iHierarchy + 1 )
           DCSPrint( sLineSpace .. "}," )
       else
           if( type(key) == "string" ) then key = "\"" .. key .. "\"" end
           if( type(value) == "string" ) then value = "\"" .. value .. "\"" end
           DCSPrint( sLineSpace .. "[" .. key .. "]" .. " = " .. value .. ",")
       end
   end
end

function GetPremadeAmmoLoadout()    
   local PremadeGroup = Group.getByName("Premade Group")
   local iUnitIndex = 1
   local PremadeUnit = PremadeGroup:getUnit( iUnitIndex )
   local AmmoLoadout =  PremadeUnit:getAmmo()
   --print the desired table structure in the DCS log file:
   DCSPrint( "" )
   DCSPrint( "local payload = {" )
   DisplayTableStructure( AmmoLoadout, 1)    
   DCSPrint( "}" )
   --note: PremadeGroup and PremadeUnit also have a table structure, but it only contains their ID
end
GetPremadeAmmoLoadout();

--[[    
   get the results in the DCS log file and delete the unnessecary informations in each line with the regular expression
   "(\d)+\-(\d)+\-(\d)+ (\d)+\:(\d)+\:(\d)+\.(\d)+ INFO    SCRIPTING: "     
]]

 

Based on the resulting payload information for the SU-33 I have created this group:

 

local payload = {
   [1] = {
       ["count"] = 150,
       ["desc"] = {
           ["life"] = 2,
           ["warhead"] = {
               ["explosiveMass"] = 0.39,
               ["type"] = 1,
               ["caliber"] = 30,
               ["mass"] = 0.39,
           },
           ["_origin"] = "",
           ["category"] = 0,
           ["displayName"] = "30mm HE",
           ["typeName"] = "weapons.shells.GSH301_30_HE",
           ["box"] = {
               ["min"] = {
                   ["y"] = -0.12504199147224,
                   ["x"] = -6.61008644104,
                   ["z"] = -0.12113920599222,
               },
               ["max"] = {
                   ["y"] = 0.12504191696644,
                   ["x"] = 2.2344591617584,
                   ["z"] = 0.12113922089338,
               },
           },
       },
   },
   [2] = {
       ["count"] = 2,
       ["desc"] = {
           ["box"] = {
               ["min"] = {
                   ["y"] = -0.27390250563622,
                   ["x"] = -1.0582795143127,
                   ["z"] = -0.19778195023537,
               },
               ["max"] = {
                   ["y"] = 0.10370192676783,
                   ["x"] = 1.8849183320999,
                   ["z"] = 0.19778576493263,
               },
           },
           ["rangeMaxAltMin"] = 4000,
           ["fuseDist"] = 5,
           ["category"] = 1,
           ["guidance"] = 2,
           ["Nmax"] = 45,
           ["rangeMin"] = 300,
           ["altMax"] = 20000,
           ["RCS"] = 0.026200000196695,
           ["displayName"] = "R-73",
           ["altMin"] = -1,
           ["life"] = 2,
           ["missileCategory"] = 1,
           ["warhead"] = {
               ["explosiveMass"] = 13.60000038147,
               ["type"] = 1,
               ["caliber"] = 170,
               ["mass"] = 13.60000038147,
           },
           ["rangeMaxAltMax"] = 12000,
           ["typeName"] = "R-73",
           ["_origin"] = "",
       },
   },
   [3] = {
       ["count"] = 2,
       ["desc"] = {
           ["box"] = {
               ["min"] = {
                   ["y"] = -0.4617146551609,
                   ["x"] = -1.9782328605652,
                   ["z"] = -0.3530935049057,
               },
               ["max"] = {
                   ["y"] = 0.23811063170433,
                   ["x"] = 2.5149307250977,
                   ["z"] = 0.35310152173042,
               },
           },
           ["rangeMaxAltMin"] = 20000,
           ["fuseDist"] = 11,
           ["category"] = 1,
           ["guidance"] = 2,
           ["Nmax"] = 24,
           ["rangeMin"] = 700,
           ["altMax"] = 25000,
           ["RCS"] = 0.061999998986721,
           ["displayName"] = "R-27ET",
           ["altMin"] = 1,
           ["life"] = 2,
           ["missileCategory"] = 1,
           ["warhead"] = {
               ["explosiveMass"] = 39,
               ["type"] = 1,
               ["caliber"] = 260,
               ["mass"] = 39,
           },
           ["rangeMaxAltMax"] = 54000,
           ["typeName"] = "R-27ET",
           ["_origin"] = "",
       },
   },
   [4] = {
       ["count"] = 6,
       ["desc"] = {
           ["box"] = {
               ["min"] = {
                   ["y"] = -0.4617146551609,
                   ["x"] = -1.2691594362259,
                   ["z"] = -0.3530935049057,
               },
               ["max"] = {
                   ["y"] = 0.23811063170433,
                   ["x"] = 2.8275010585785,
                   ["z"] = 0.35310152173042,
               },
           },
           ["rangeMaxAltMin"] = 14000,
           ["fuseDist"] = 11,
           ["category"] = 1,
           ["guidance"] = 4,
           ["Nmax"] = 24,
           ["rangeMin"] = 500,
           ["altMax"] = 27000,
           ["RCS"] = 0.061999998986721,
           ["displayName"] = "R-27ER",
           ["altMin"] = 1,
           ["life"] = 2,
           ["missileCategory"] = 1,
           ["warhead"] = {
               ["explosiveMass"] = 39,
               ["type"] = 1,
               ["caliber"] = 265,
               ["mass"] = 39,
           },
           ["rangeMaxAltMax"] = 60000,
           ["typeName"] = "R-27ER",
           ["_origin"] = "",
       },
   },
}


local route = { 
   points = { 
       [1] = { 
         type = "TURNING_POINT", 
         airdromeId = Airbase.ID, 
         helipadId = Airbase.ID, 
         action = "FLY_OVER_POINT", 
         x = 1000, 
         y = 1000, 
         alt = -1000, 
         alt_type = "RADIO",
         speed = 200, 
         speed_locked = false, 
         --ETA = Time, 
         --ETA_locked = boolean, 
         name = "Some Waypoint name", 
         --task = Task 
       },            
   }            
}



function InitializeGroup()
   local iX, iY = -260000, 700000 
   local groupData = {
       ["visible"] = false,
       ["taskSelected"] = true,
       ["route"] = route,
       ["groupId"] = 2,
       ["tasks"] = 
       {
       }, -- end of ["tasks"]
       ["hidden"] = false,        
       ["units"] = 
       {
           [1] = 
           {
               ["type"] = "Su-33", -- other airplane types are stored in ...\DCSWorld\Scripts\Database\planes                
               ["x"] = iX,
               ["y"] = iY,
               ["alt"] = 7000, -- altitude in meters
               ["alt_type"] =  "RADIO", -- "BARO" or "RADIO" for Above sea level or above ground level
               ["speed"] = 300,
               ["unitId"] = 2,                
               ["name"] = "Russian Air Unit1",                     
               ["payload"] = payload,
                    ["skill"] = "Good",    -- can be "Excellent", "High", "Good", "Average", "Random", "Player"
                    ["heading"] = 3.141, -- in radians
                    ["callsign"] = 100, 
           }, -- end of [1]
       }, -- end of ["units"]
       ["name"] = "Russian Air Group1",
       ["start_time"] = 0,
       ["task"] = "Intercept",
   } -- end of [1]
       
   coalition.addGroup(country.id.RUSSIA, Group.Category.AIRPLANE, groupData)  
end
InitializeGroup();

 

However, in the simulation I do not see any outer weapons on the spawned SU-33. So the payload is apparently empty. There are no error messages and the airplane itself spawned, but surely there must be something missing.

I hope someone can find a solution for my problems. :(

Link to comment
Share on other sites

Ok, I will try MOOSE. It seems to be very helpful for most of my purposes, although it would be even better, if I was able to control all the details of the random spawns myself.

 

Anyway, thanks for your help, guys!

 

 

I think once you gain some familiarity with MOOSE you wll be amazed at the control you can impose on random, and any other, spawn process. The folks who hang out at the MOOSE Discord channel are most generous with their assistance.

Link to comment
Share on other sites

What details would those be, exactly?

 

 

If I got it right, then MOOSE only allows you to randomly spawn groups in certain airports or in predefined trigger zones. I.e. you have to set those circular zones manually, so your airplanes can spawn randomly in those static areas.

I know you can achieve a lot using this method, but in complex missions setting all those static zones and naming them would become somehow cumbersome.

 

Instead I would prefer to compute the random spawn locations and the random waypoints dynamically depending on the current situation. That would also allow me to prorgam the AI behaviour a little bit and create more strategic depth in the missions.

 

A very simple example for a dynamic spawning behaviour is spawning an enemy unit around an airplane on a random heading between 90° and 140° on a random distance between 20nm and 30nm. If I wanted to imitate such a spawning behaviour using only the MOOSE in predefined trigger zones, then it would be very hard.

Also if MOOSE is only using uniform probability distributions, then I could not use custom distributions like the Gaussian one.

 

So it would be really nice, if I could design missions using almost only Lua codes. It would be much more versatile. Still I will give MOOSE a try and maybe I will change my mind.

Cheers! :)

Link to comment
Share on other sites

A very simple example for a dynamic spawning behaviour is spawning an enemy unit around an airplane on a random heading between 90° and 140° on a random distance between 20nm and 30nm. If I wanted to imitate such a spawning behaviour using only the MOOSE in predefined trigger zones, then it would be very hard.

Also if MOOSE is only using uniform probability distributions, then I could not use custom distributions like the Gaussian one.

local spawn=SPAWN:New("Spawn Template")
local rho=UTILS.RandomGaussian(UTILS.NMToMeters(25), UTILS.NMToMeters(2), UTILS.NMToMeters(20), UTILS.NMToMeters(30))
local theta=UTILS.RandomGaussian(120, 5, 90, 140)
local zone=ZONE_UNIT:New("Zone Unit", UNIT:FindByName("Spawn Relative"), 100, {rho=rho, theta=theta, relative_to_unit=true})
spawn:SpawnInZone(zone)


Edited by funkyfranky

A warrior's mission is to foster the success of others.

i9-12900K | MSI RTX 3080Ti Suprim X | 128 GB Ram 3200 MHz DDR-4 | MSI MPG Edge Z690 | Samung EVO 980 Pro SSD | Virpil Stick, Throttle and Collective | MFG Crosswind | HP Reverb G2

RAT - On the Range - Rescue Helo - Recovery Tanker - Warehouse - Airboss

Link to comment
Share on other sites

Ok, that kind of impresses me. I guess I should ask my further questions in the MOOSE threads then. :D

 

Edit: So I have tried your code and it works well. Something I noticed is that I had to load my own Lua file in the "Do Script"-trigger-option again each time I edited it with Notepad++. That seems like a small issue in the mission editor, which does not load changed Lua files automatically. Other than that MOOSE worked perfectly for me. So thx!

 

Btw. I wonder if it is possible to set the settings of the DDIs, weapons, flairs and the radar of the player's airplane before the mission start. It would be nice for training missions, that are frequently repeated. For example in bombing training missions I do not want to set the counter measures and bomb settings myself every time I start that mission.


Edited by Tiramisu
Link to comment
Share on other sites

Edit: So I have tried your code and it works well.

Great, and sorry for just giving you the plain code above. I was about to go to bed and just had a few minutes :)

A warrior's mission is to foster the success of others.

i9-12900K | MSI RTX 3080Ti Suprim X | 128 GB Ram 3200 MHz DDR-4 | MSI MPG Edge Z690 | Samung EVO 980 Pro SSD | Virpil Stick, Throttle and Collective | MFG Crosswind | HP Reverb G2

RAT - On the Range - Rescue Helo - Recovery Tanker - Warehouse - Airboss

Link to comment
Share on other sites

  • 5 months later...

dabbling

 

I've trying to get ti grips with scripting...

 

 

so I have a mission with a group called "tanks" consisting of two tanks (tank 01 and tank 02)

 

 

This code works (taken from an example)

 

myUnit = Group.getByName("tanks"):getUnit(2)

 

 

 

but this code does not (error message says 'param self missing')

 

myGroup = Group.getByName("tanks")

myUnit = myGroup.getUnit(2)

 

 

 

 

 

can anyone please explain where i'm going wrong (other than being out of my depth tee hee)

Link to comment
Share on other sites

lol. You forgot the colon in the 2nd example.

 

 

myGroup = Group.getByName("tanks")

myUnit = myGroup:getUnit(2)

 

 

thanks tiramisu, worked a treat.

 

 

It's a tough job, but do you have an explanation for the difference of using a period in the first statement and a colon in the second?

Link to comment
Share on other sites

It is quite easy. In Lua a colon automatically inserts the class itself into the first argument of the function.

So myGroup:getUnit(2) should be the same as myGroup.getUnit( myGroup, 2) if I am not mistaken. In this case imagine myGroup.getUnit as a function with actually two arguments, which needs the object in the first and the number in the second argument.

Link to comment
Share on other sites

  • 2 weeks later...

Is there any method for the player to send coordinates to a Lua script during a running mission?

 

What I have already tried is to get the coordinates of the waypoints of a unit with the _DATABASE method and then change its waypoints with the Combined Arms module. Sadly the waypoint data from _DATABASE is not updated, so it only contains informations of the preset waypoint coordinates set in the mission editor.

I was wondering if a script could read the coordinates of markers, that can be placed by the player in the F-10 map.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...