Assigning Units Based On Side Posture

All discussions & material related to Command's Lua interface

Moderators: angster, RoryAndersonCDT, michaelm75au, MOD_Command

Post Reply
butch4343
Posts: 327
Joined: Thu Mar 26, 2015 2:09 pm

Assigning Units Based On Side Posture

Post by butch4343 »

Hi folks I wonder if you might be able to help me figure something out, apologies for the long post but its worth explaining what am trying to achieve.
I have a scenario where there are 5 sides, NATO (Always Players Side), WP, Libyia, Syria , Algeria, so I have the scenario set to randomly on each load , NATO and the WP will always be hostile to each other, Libyia , Syria, Algeria declare for NATO, in which case the units deploy to the players side, neutral where they are removed from the scenario or they are allied to the WP where they transfer to the WP side.

So any airbases that were Libyian ect are now on the WP side.

As the scenario progresses there are reinforcements for the WP side that are Soviet forces, (Think 6th Guards Aviation Regiment ect deploying from far east to the med), in the case of further air reinforcements, I want to know if theres a way I can script CMO to check a side posture and allocate the forces dependent on that, so for example

local a = Side Posture Libyia to WP
local b = Side Posture Algeria to WP
local c = Side Posture Syria to WP
if a= F then
ScenEdit_HostUnitToParent({HostedUnitNameOrID='Aircraft #1 ',SelectedHostNameOrID='Benghazi'}
ScenEdit_AssignUnitToMission('Aircraft', 'Benghazi Air Intercept Mission XXX')
This would mean that if WP and Lib are friendly that aircraft will transfer to the Benghazi
elseif b= F Then
ScenEdit_HostUnitToParent({HostedUnitNameOrID='Aircraft #1 ',SelectedHostNameOrID='Sidi Ahmed Airbasei'})
ScenEdit_AssignUnitToMission('Aircraft', 'Sidi Ahmed Airbasei XXX')

elseif c = F Then
ScenEdit_HostUnitToParent({HostedUnitNameOrID='Aircraft #1 ',SelectedHostNameOrID='Latika'})
ScenEdit_AssignUnitToMission('Aircraft', 'Latika XXX')

end
And so on and so forth, so in effect the WP reinforcements can only transfer closer to allied countries, but because of the random nature of the allies in each run through I dont know which airfields will be open to the WP side.
So the script above is written very high level as I think what I want to achieve not in LUA language, so my question is...
Would a similar script work?
And how do I define what local a/b/c is? Is it a case of writing sometihng like

ScenEdit_GetSidePosture('Libyia', 'WP')

That according to the documentation will return F, H, N, A

Oh and my last question is will this mean that it runs through the list of countries Libyia, Syria, Algeria in a set order?

My knowledge is limited in LUA , but I am trying to learn so any explanation needs to be at the most base level lol

Kind Regards All

Butch
butch4343
Posts: 327
Joined: Thu Mar 26, 2015 2:09 pm

RE: Assigning Units Based On Side Posture

Post by butch4343 »

I have atempted the following

a =ScenEdit_GetSidePosture ({"Libyia", "WP"})
b = ScenEdit_GetSidePosture ("Syria", "WP")
if
a=('F') then
ScenEdit_AssignUnitToMission('Black', 'Al Jufra AB')
else
b=('F') then
ScenEdit_AssignUnitToMission('Black', 'Aleppo')
End

But I get an error on

ERROR: [string "Console"]:4: 'then' expected near '='
KnightHawk75
Posts: 1850
Joined: Thu Nov 15, 2018 7:24 pm

RE: Assigning Units Based On Side Posture

Post by KnightHawk75 »

You get the error probably because you need to do == vs = ; also the params look wrong in the first call.
== means compare (same)
~= means compare (not same)
= means assign.

Here is an example, and a utility function from my library, that I think will help you out and answer the questions you asked - if not then keep asking and I can explain more.

Code: Select all

gKH={}; --my personal global namespace prefix should use your own, suggest gXXXX where X are ascii chars.
 gKH.Util={}; -- sub namespaces that contains all my utility functions, though only one is included in this sample.
 gKH.SceneGlobals={};  -- sub namespace that usually hold all my scene-specific vars and constants. 
 -- Function: GetGlobalPostureData(paramTable)
 -- Params: paramTable is optional, if provided instead of returning a table it will populate the one you send it.
 -- Returns: a table of all side relations to each other indexed by string key side1_to_side2.

Code: Select all

function gKH.Util:GetGlobalPostureData(paramTable)
     local pTbl={}; pTbl["entrycount"] = 0;pTbl["sidecount"] = 0;
     local s = VP_GetSides();
 -- ipairs goes in numberical order if table is indexed with only sequencial number (otherwise it stops at first break in series);
 -- pairs goes in unspecified order but will work over everything no matter if # based indexes or not;
     for k,v in ipairs(s) do  --for each side
         for i,j in ipairs(s) do -- for each side again.. 
             if i ~= k then --skip if same side otherwise do this.
                 pTbl[v.name .. "_to_" .. j.name]= {posture = ScenEdit_GetSidePosture(v.name,j.name)};
                 pTbl["entrycount"] = pTbl["entrycount"] + 1;
             end
         end
                 pTbl["sidecount"] = pTbl["sidecount"] + 1;
     end
     if (paramTable ~=nil) and type(paramTable) == "Table" then
         paramTable = pTbl; return; --assumes incoming table is byref.
     end
     return pTbl;
 end
Using the global function:

Code: Select all

-- Now access the function to build the full list and then store globally, 
 -- it only needs to be updated if a posture change event happens assuming you do it after you random posture assignments
 gKH.SceneGlobals.PostureData = gKH.Util:GetGlobalPostureData()
 -- Now lets say the following runs in some event later... or really for that matter even in your setup scripts after the above line ran.
 local pd = gKH.SceneGlobals.PostureData; create shortcut ref to it so we're not typing as much. 
 --print(pd);
 if pd["Russia_to_Civilian"].posture == 'F' then
  --do something here ..
 elseif pd["Azerbaijan_to_Civilian"].posture == 'H' then 
  --do other stuff here
 elseif pd["Downed Pilots_to_Azerbaijan"].posture == 'F' then 
 -- do other stuff here
 elseif pd["Downed Pilots_to_Azerbaijan"].posture == 'H' and pd["Civilian_to_Russia"].posture == 'H' then 
 -- do other stuff here
 else
  --- default else case here if you need.
 end
So if you had 4 sides, it would generate a table with 12 entries one for every side vs every other-side except for vs itself. Now in your case those would be "Libya_to_WP","Libya_to_WP","Syria_to_WP",etc. So long as your side name does not start with a number or a whacky character it's should work for just about any scene you throw at it.

Stop reading here if the above solves your issue and it's all you need or care about.

Now for a mini ipairs vs pairs explanation\tutorial:
Consider the following three tables and functions to loop through them.

Code: Select all

local tbl1 = {[1]={someid=1,name="a"},[2]={someid=2,name="b"},[3]={someid=3,name="c"}}
 local tbl2 = {[1]={someid=1,name="a"},[3]={someid=2,name="b"},[4]={someid=3,name="c"}}
 local tbl3 = {["apples"]={someid=1,name="a"},["blueberries"]={someid=2,name="b"},["carrots"]={someid=3,name="c"}}
 print("--- using ipairs ---");
 -- This will print "3" and then each entry as expected and ALWAYS in order. 
     print(#tbl1); --get the built in count property of the table.
     for k,v in ipairs(tbl1) do
       print(v);
     end
 -- This will print "1" even though there are 3 entries, but because they are not numerically contiguous it's not correct.
 -- it will also only print the first entry for the same reason, it stops at the first break in the series. 
     print(#tbl2);
     for k,v in ipairs(tbl2) do
       print(v);
     end
 -- This will print "0" even though there are 3 entries, but because they are not numeric.
 -- it will print no entries at all because none are numeric indexes.
     print(#tbl3);
     for k,v in ipairs(tbl3) do
       print(v);
     end
Now rerun the same test with pairs.

Code: Select all

print("--- using pairs ---");
 -- This will print "3" and then each entry as expected but in ANY order, run it a 10 times and you'll probably see some out of order. 
     print(#tbl1); 
     for k,v in pairs(tbl1) do
       print(v);
     end
 -- This will print "1" even though there are 3 entries, but because they are not numerically contiguous it's not correct.
 -- it will print every entry however as expected but again, in any order. 
     print(#tbl2);
     for k,v in pairs(tbl2) do
       print(v);
     end
 -- This will print "0" even though there are 3 entries, but because they are not numeric.
 -- it will print every entry however as expected but again, in any order. 
     print(#tbl3);
     for k,v in pairs(tbl3) do
       print(v);
     end
General "pairs" rules/advice:
- If order absolutely matters to you and the table is ipairs compatible (most cmo returned tables are) then you must use ipairs to ensure you process in 1..2..3..etc order.
- If order doesn't mater to you and the table is ipairs compatible, use either, though pairs is marginally (~10%) faster in vanilla lua 5.3 in cmo.
- If the keys for the tables contain numerical breaks or are strings, you must use pairs.
- If you want the count of a table who's keys are numeric and sequential #tablename will be correct 99.x% of the time, that other .1% you're not likely to run across dealing with cmo tables or probably even your own.
- If you want the count of entries in a table who uses strings as keys or has breaks in the #'s well... it depends, in general you must loop though using pairs and build your own count. In the case of your own tables this would also usually be true, unless you're doing some more advanced lua things to maintain the internal count, a whole other topic.
- Off the exact topic of pairs - but if max speed is critical and you have the totalsize\count already either via #table or another way, the fastest method will always be direct indexing, and not marginally, but like ~50'ish % faster (generic lua 5.3 anyway). Personally I tend to avoid it unless I'm doing beefy stuff where the difference is significant, and it definitely is sometimes, in favor of pairs and ipairs. So if your beefy code is running once on load, or once and hour or once every 15 minutes, don't give too much concern, if it's running every 5min or less pay more attention, and if it's running every 15seconds or less optimize it when dealing with larger tables as in the latter a couple ms here, a couple ms there add up.

For example in the far above GetGlobalPostureData function I use ipairs over pairs for example, because i absolutely have to, if I'm going to be using the indexes as the comparing values for "the same check" for first side and next side. But I could have got the count via #s and done the follow for absolute speed.

Code: Select all

...
     for i=1,#s do  --for each side
         for j=1,#s do -- for each side again.. with new counter 
             if i ~= j then --skip if same side otherwise do this.
                 pTbl[s[i].name .. "_to_" .. s[j].name]= {posture = ScenEdit_GetSidePosture(s[i].name,s[j].name)};
  ...
 
I didn't though cause chances are there are only a few sides so I didn't bother as the pairs method is easier to follow to my eyes and it's not something that should be needed to be called often. I also could have used pairs if I really wanted, but then to be safe I would of had to do string compares (more expensive) on the name to check if it was the same side instead reusing the index var which of I already had (and had them in order via ipairs or the count as above), basically I could have ..but it would have been less efficient for that specific need.

Yeah I know, you just wanted a simple answer and I give a mini tutorial cause I'm in the mood, but hopefully it's here some day when someone asks (or searches in ipairs ) or asks "when should I use ipairs\pairs or index counter and what are some (I didn't remotely cover all) the differences?", then I can link back here to this post. Any as always hope it helps more than it bores.

Appendix: basic speed test you can past into your console and just run:

Code: Select all

 local data = {} --test table.
 for i = 1,10000 do -- increase upward as you like this should be enough to show difference.
    data[#data + 1] = 1 --populate our data.
 end
 
 --speed tests do 100 iterations of running through the above sized table for each type.
 local start = os.clock() -- Start time
 for iteration = 1,100 do -- Iterate 100 times
    for i,v in ipairs(data) do local v1 = v; end -- ipairs
 end
 print("ipairs Result: " .. os.clock() - start .. " secs") -- Print duration
 
 local start = os.clock() -- Start time
 for iteration = 1,100 do -- Iterate 100 times
    for i,v in pairs(data) do local v1 = v; end -- pairs
 end
 print("pairs Result: " .. os.clock() - start .. " secs") -- Print duration
 
 local start = os.clock() -- Start time
 for iteration = 1,100 do -- Iterate 100 times
    for i = 1,#data do local v = data[i]; end  --direct index
 end
 print("direct counter Result: " .. os.clock() - start .." secs") -- Print duration
 
butch4343
Posts: 327
Joined: Thu Mar 26, 2015 2:09 pm

RE: Assigning Units Based On Side Posture

Post by butch4343 »

Knighthawk,

Mate thanks so much for this and your kind offer to come back and drill you further on this, I am going to sit down and look at each section and gen up on the phrases and terms thats used in it mate.

Thanks your help is appreciated :-)[:)]
Parel803
Posts: 941
Joined: Thu Oct 10, 2019 3:39 pm
Location: Netherlands

RE: Assigning Units Based On Side Posture

Post by Parel803 »

missed this one, but is now on the reading list. Thx
regards GJ
Post Reply

Return to “Lua Legion”