Matrix Games Forums

Forums  Register  Login  Photo Gallery  Member List  Search  Calendars  FAQ 

My Profile  Inbox  Address Book  My Subscription  My Forums  Log Out

NRT with FOC in Regular time trigger

View related threads: (in this forum | in all forums)

Logged in as: Guest
Users viewing this topic: none
  Printable Version
All Forums >> [New Releases from Matrix Games] >> Command: Modern Operations series >> Mods and Scenarios >> Lua Legion >> NRT with FOC in Regular time trigger Page: [1]
Message << Older Topic   Newer Topic >>
NRT with FOC in Regular time trigger - 7/25/2021 1:16:57 PM   


Posts: 436
Joined: 10/10/2019
From: Netherlands
Status: offline
Good afternoon,
Most likly an impossible Question and might seem unlogic but gonna try it. So sorry if it is.
I initiated a Opfor Non-Real-Time (NRT) track based on a message with PCS. Around the track a made an circle RP's based on time reported and speed. Made it AutoDetectable.
I made several event with a time (hourly) trigger and a new radius of that circle again based on speed. A Further-On-Circle.
Got help on the forum to make that happen, works nice.

local newRadius = 12
local unit = ScenEdit_GetUnit({guid='50PVO4-0HMAFCPT5PHO5'})
local circle = World_GetCircleFromPoint({latitude = unit.latitude, longitude = unit.longitude, numpoints = 16, Radius = newRadius})

for each,point in ipairs(circle) do
rp = ScenEdit_GetReferencePoint({side='Blue', name='FocC '..each})
ScenEdit_SetReferencePoint({side=rp.side, guid=rp.guid, latitude=point.latitude, longitude=point.longitude})

I was wondering it something similar is possible with the "regular time" trigger. Is there a way that the newRadius Value changes every time the event is triggered.

Hope the question makes a little bit of sense.
With regards Gert-Jan

Post #: 1
RE: NRT with FOC in Regular time trigger - 7/26/2021 4:21:27 PM   


Posts: 1199
Joined: 11/15/2018
Status: offline
missing a 'local' for var rp - btw

I was wondering it something similar is possible with the "regular time" trigger.
Is there a way that the newRadius Value changes every time the event is triggered.

Sure. There are various ways to do it, but basically just pull the value you want from a global var.

I've Attached an example that increases the radius every minute until such time as you tell it to stop or remove the unit via a remove function (id doesn't handle destroyed as is but could with few tweaks). It's just an extension of your original sample file from the other thread, i did the following additions:
-On scene load a test "PCS" Unit is created at a given test location as autodetectable on side smugglers.
-The rp radius created an set to a starting point of 10nm and drawn on side blue.
-An event is enabled to check for disabling autodetection of the unit at the next one min mark.
-A second later it is detected by blue, on the next minute change it's set back to undetectable and that event disables itself.
-On every minute mark, provided the system is still flagged active the radius is increase by a sample increment of 1nm, and redrawn.
-Upon the first Hour mark passing the the rp's are deleted and removed and the event that check for things a disabled.
-Upon every key change state is saved\updated so that it's restored on a save.

This shows:
Use of global vars and functions quasi organized into gParel namespace.
Changes to most of the behavior can be done with tweaking the global vars at the top of the OnSceneLoad action.
Use of saving important vars to keystore\save file and restoring them on startup if they exist or using the defaults if they don't.
~95% of the code is in the OnStartup lua action.
~5% of the code is in the 4 or so new event actions, they basically just call globals.
A create action is provided but not used in the sample.

Obviously adjust as needed for your speed calc or other needs but should provide a concept to work off, and if you don't need incrementing can just tweak the event to not increment before calling gParel.UpdatePCSUnitRadius(). The times\timers values used where just to show off it all working in a quick manner.

build 1147.29

Attachment (1)

(in reply to Parel803)
Post #: 2
RE: NRT with FOC in Regular time trigger - 7/27/2021 8:41:19 AM   


Posts: 436
Joined: 10/10/2019
From: Netherlands
Status: offline
Thanks again KnightHawk for you're time and help.
This gonna take some timme for me to read and try to understand :-)
best regards Gert-Jan

Corrections on my script are always appriciated. try to keep learning.

< Message edited by Parel803 -- 7/27/2021 9:27:09 AM >

(in reply to KnightHawk75)
Post #: 3
RE: NRT with FOC in Regular time trigger - 7/29/2021 12:20:31 PM   


Posts: 436
Joined: 10/10/2019
From: Netherlands
Status: offline
I played and struggled with it. Deleted the auto ID stuff and added a RT (Real Time) track.
Next to try is to delete the NRT track and FOC when the TRMP is withinn the circle or when the RT track is detected and identified.

Still struggling a bit with the Global functions load and the trigger: scen loaded. Have to try more.

Not fully understand the UpdateAutodetectedContactNames function?

If you see how stuff can be done better or errors in it, always glad to hear.

with regards GJ

Attachment (1)

(in reply to Parel803)
Post #: 4
RE: NRT with FOC in Regular time trigger - 7/30/2021 9:12:14 AM   


Posts: 1199
Joined: 11/15/2018
Status: offline

Not fully understand the UpdateAutodetectedContactNames function?

UpdateAutodetectedContactNames .. that's left over related to the prior changes from your other thread you know the one were you reported the bug, this action is related to that workaround. I point that out less for you lol, and more for anyone else reading this just so they know it's not exactly related to the changes made to scene related to this thread. Also not a function per say, just the name of an action, it's important we keep the two distinctions since there are indeed functions involved.

So the action name "UpdateAutodetectedContactNames" is part of an event called "RenameAutodetectedContactsEveryMinute", who's trigger is\was to run every one minute, it relates to the AutoDetect(AIS)on action\event such that it's meant to handle certain things after that runs. What does that mean, that means every 1 minute what's inside "UpdateAutodetectedContactNames" will attempt to run and do changes if needed, those changes will only matter to things\units that AutoDetect(AIS)on set's to be autodetectable.

Now let's walk though the code line by line in my narrator voice in the comments, some this is going to be boring and probably to repetitive but I don't know what part you might not be following, so I will try to assume little. Due to forum formatting there may be annoying unintended extra space added as I attempt to break up the code sections because if I don't it'll also mangle the formatting. Now go get yourself a beer first we're gonna be here awhile. So here goes....
local gKH={}; --this defines a local scoped table (empty at the moment) called gKH. 
-- locally scoped variable are removed as soon as this action is over\finished. ie each minute all the code runs everything here we be re-created
-- so to speak. if you removed the 'local' here then all this stuff would 'stick around' in memory but still actually be re-written every 1 minute.
-- I actually original didn't have it local and didn't even have all this code inside the action instead in a startup routine and global in scope. 
-- BUT people get easily confused so because it didn't matter much for this example I threw everything into the every minute action and make it local.
-- What's important you remember is local = goes away when my event or the current scope is over. 'no'-local means stay in memory till CMO restarts
-- or till explicitly removed, ie restarting a scene\new scene doesn't get rid of global. 
-- You want to use globals where it makes sense to do so (ie for functions or var that need to stick around for re-use), otherwise keep it local.
gKH.SideGuidCache={}; -- this sets up another table called SideGuidCache inside the gKH table. 
-- While this may look like a global definition it's not it's inside whatever gKH is which is local.
-- everything inside gKH goes bye-bye when the event action is done running.
-- It will be populated later with [guid]=name data as quick lookup table when we want to convert side-guids to the actual side's name without
-- having to call VP_GetSide or VP_GetSides and running a for-each to look for matching up names.
-- TLDR: It's used to maximize performance, and reduce complexity believe it or not.
--This creates a function called buildSideGuidCache inside the gKH table. it takes a parameter called sides
--It populates a guid to name lookup table.
function gKH.buildSideGuidCache(sides)
    --run though the list of sides submitted as the sides parameter, code assumes it will be a table of sides.
    --each side is expected to contain a side wrapper; or at least something that has both .guid and .name fields.  
    for i=1,#sides do  -- for up to the number of sides in the table do the following 
         --Set the table entry by key in gKH.SideGuidCache['someguidhere'] to equal the sides name property.
         gKH.SideGuidCache[sides[i].guid] = sides[i].name; 
    end --end marker related to the 'for' statement
end --end marker related to the function statement\definition.

So let's move on to the next set of code, it defines a function called processSides stored inside the gKH table.
It take one parameter named unitType. You can see from the code inside it expects this unitType parameter to be a string, and it enforces that restriction. The purpose of the function is basically to run through ALL sides that exist in the scene, grab all units of type 'Ship' from each of those sides and then check each unit for autodetectable and if each unit also has been detected by anyone yet. If so then the it calls a contact renaming function. Think of it as the master and the other functions as helpers. As I mention in comments at the end of the code this is sledge hammer approach to the problem, written fast as an example work around to the issue described in the original thread.
function gKH.processSides(unitType) 
  -- The following if statement line says besides everything else first do what is inside (parentheses) before everything.
  -- In Lua parens surrounding something indicate order, and processing logic sometimes very much depends on order.
  -- For example if I did not have unitType==nil in paraens then type(unitType) would actually run first during execution
  -- and if unitType was nil it may throw an error. But because I know the order is right to left and that short circuiting will
  -- happen I can avoid that by making sure it checks for nil first and then short circuits.  "short circuiting" you say, WTF does that mean.  
  -- It just means the Lua interpreter is smart, it knows this is an "or" and it's checking for anything being "true" here 
  -- if the first thing it evaluates is true then nothing else that follows is going to change that so it's actually skipped.
  -- for example in the line below if unitType is actually nil then the part about type(unitType) ~="string" is not even processed.
  -- it jumps right to the 'then' part which is what we want because we don't want and error generated by type checking 'nil'.
  -- Ok back to actually explaining this..
  -- If unitType is nil, or if unitType is not a string then print to the lua log a message and then return nil from the function.
  -- a 'return' anywhere in your code stops the function right there and then, return nil and just return are the same thing.
  if (unitType ==nil) or type(unitType) ~= "string" then print("processSides(): unitType invalid. Aborting."); return; end
-- The following line creates 2 local variables called retval and sides, these values are set to whatever returns back from
  -- a pcall to the CMO API call VP_GetSides, a function that takes no parameters cause it just gets a list of all sides.
  -- First you're wondering WTF is a pcall(), and why does KH seem to use it alot in his code?  Glad you asked.
  -- There are times where you're going to call a function and an error could happen including and 'interactive' that will
  -- blow up your script and stop it from executing, VP_GetSides actually it's one of this is a bad example and pure habit
  -- So what Lua offers is something called pcall(), it stands for Protected Call. Protected in the sense that you tell it
  -- what function to run for you and any parameters and it will do it for you in a special context, it will then return back to you
  -- at least two things as return values, first is true|false .. either the call succeeded or it did not.
  -- the second is what the actual function returned if it did succeed, OR if it failed it will return the error message if any. 
  -- SOOO in the following line after it executes successfully then 'retval' will be true, and 'sides' will be a table of sides.
  -- but if it fails 'retval' will be false and 'sides' will either be nil, or a string containing some error text.
  local retval,sides = pcall(VP_GetSides);
  if (retval ==true) and sides ~=nil then   -- IF the call was successful and the sides table isn't nil we should proceed.
    gKH.buildSideGuidCache(sides);  --CALL already talked about function above, submit 'sides' table as the parameter for it.
                               -- it's called here once at the start so that if sides are actually added to the scene over time 
                               -- then it's always up to date, and since this is all local there is no saving the data between runs. 
    local ulist; -- this sets up a variable called ulist to hold as you might guess a list of units. It's worth noting 
               -- the variable is defined outside the For statement that is coming, that was intentional.
               -- HOWEVER there is a non-breaking bug of sorts later in the line 'local ulist = sides[i]:unitsBy(unitType);'
               -- the 'local' is not intended to be there but it works anyway for.. reasons.
               -- That bug doesn't actually end up mattering much but in some other case it could.
               -- the purpose of defining ulist outside of the for was to save tiny amount of performance re-allocating during a for loop.
               -- or if you were doing something else where you wanted ulist to be available outside the for loop scope.
               -- Below we're going to pretend the line local ulist = sides[i]... actually says ulist = sides[i]... 
    -- This for-loop runs though all the sides. for each side that has units of a certain type it will check if the unit
    -- is auto detectable, and if it a contact on some side, if it is autodetectable and a contact on someside it will call
    -- another function to handled changing the name of that contact to the real name of the unit.  
    -- remember all this is to work around a little bug where units that were already contacts on a side and get switched to
    -- autodetectable don't get their contact names updated to reflect level 4 detection/classification.

    -- So let's begin by looping through all sides, the sides table consists of sides[a numberstarting at 1] = {full side wrapper here}
    for i=1,#sides do  -- for each side do the following
        -- Remember what I said above we're pretending local doesn't exist here but if it did this would say redefine\setup a local var called ulist.
        -- Assign the var ulist a value that is returned by unitsBy("SomeType") call on the sides wrapper.
        -- a ':' is method\function reference on a object\wrapper I'm gonna save explaining ":" vs "." for another time 
        -- but while they can be interchangeable note that for CMO purposes the ":" will mean a instance method\function that belongs to a wrapper.
        -- and generally operates upon that specific wrapper instance only, SomeUnit:launch() means call launch on that unit as the context. 
        -- Here we're calling SomeSideWrapper:unitsBy("Ship"), you can see the documentation for :unitsBy() details but it's awesome!
        -- It was enhanced at my request to even take further specific filtering which took a load of pain off Lua users if they know to use it.
        -- and performance gains too when needing to get a list of units filtered by something fairly specific instead of having to 
        -- do it all yourself.  But here we really do just want ALL ships, so we don't use the optional subtype filtering.
        local ulist = sides[i]:unitsBy(unitType);
        if (ulist ~=nil) and #ulist >0 then  -- Says if our ulist is not nil and has a count greater than 0, then continue.  
            --print("processing: " .. sides[i].name .. " unitcount:" .. tostring(#ulist)); --this was for debugging purposes.
            local u;  -- Create a local variable inside the scope of the for loop above. it will be used inside the for-loop to come.
                      -- it does not exist outside this for-loop scope, only downward so to speak. 
            for j=1,#ulist do  --ANOTHER for-loop to run though the unit list, which is a list of units guids and names.
              -- Now we're back to our friend Mr. pcall(), in a sample that can indeed fail cause a unit could in theory no longer exist. 
              -- Here we have pcall call ScenEdit_GetUnit for us, and we tell it pass {guid=ulist[j].guid} as the parameter to the function.
              -- Notice we're reusing the 'retval' variable since it already exists way above, and is no longer being used for anything.  
              -- Here 'retval' and 'u' will get assigned true and the unit wrapper object or false and error msg string on failure, respectively.
              retval,u = pcall(ScenEdit_GetUnit,{guid=ulist[j].guid})
              -- Remember I mentioned about parentheses and controlling order of evaluation here we use it again multiple times
              -- from inner to outer. first (retval == true) is evaluated. then (FirstResult and u~=nil) then 
              -- (SecondResult and u.autodetectable ==true) and finally ThirdResult and u.ascontact ~=nil
              -- Such that if retval is false we stop, then if u is nil we stop, then if u.autodetectable is false we stop
              -- Then and only then does it bother checking if u.ascontact is nil.
              -- It's the long but needed way of saying getting unit succeeded and unit exists and 
              -- the unit is presently set to autodetectable and it is a contact on at least 1 side then continue.  
              if (((retval == true) and u~=nil) and u.autodetectable ==true) and u.ascontact ~=nil then 
                -- Call the function to do the name change, pass this unit's name as 1st parameter, pass the units ascontact table as 2nd.
              end  -- ends the 'if' statement no 'else' needed unless debugging.
              retval,u = false,nil;  -- multiple assignment, clear both variables before the next iteration set retval to false and u to nil.
                         -- it's functionally no different then retval=false; u=nil;
            end --ends the ulist related for-loop statement.
        end  --ends the 'if' related to the nil and count checking on, no need for 'else' unless debugging. 
        ulist = nil; -- sets ulist to nil\nothing we want it nil at the start of every iteration in case the pcall fails.
                     -- if we did not do it (and 'ulist=sides[i]...' existed without the local in front of it like we are pretending) 
                     -- then 1 success followed by one failure would produce a case where ulist was not nil, but instead still had values
                     -- of the last iteration of the loop. The fact I left 'local ulist=sides[i]...' in there by mistake, 
                     -- actually prevents that from happening regardless, in this case anyway.  
    end  --ends the sides related for-loop statement
  end  -- ends the 'if' related to check if sides exist. normally I would have 'else' just above this to print an error if VP_GetSides failed.
end  -- closes the function definition.

On to the next one, this sets up a function called setContactNamesToUnitName, it's used above. It takes two parameters, the first is a name, it's what name will be assigned to a sides's contact object (btw I use 'object' and 'wrapper' often interchangeably). The second parameter is table that contains a units .ascontact table. The function basically just handles the details of making the changes to each contact that exists inside the table that is provided.
There is no technical reason all of this code couldn't have technically been included in the process function as yet another for-loop, but pays to break things into smaller chunks of defined functions of work.
Before we begin it's helpful to know that unitwrapper .ascontact returns a table that has a format that looks like the following (stripped to just show 1 entry):
{ [1] = { guid = '4FH7PU-0HMAIQ71NIM27', side = '50PVO4-0HM6IJ7M6SLSG', name = 'SKUNK #16' } }
That is [1] is the numeric key\index, the value is a table, that table contains a .guid key, a .side key and a name key.
They represent a contact guid (relative to the side), the guid of the side the contact guid is from, and the current name that side has assigned to the contact.
function gKH.setContactNamesToUnitName(uname,ascontact)
  -- We've been though this a couple times now but next line basically says 
  -- IF uname is not nil, and ascontact is not nil and the internal count of ascontact table is greater than 0
  -- then proceed. #table name means get the tables internal count.
  -- HOWEVER # only works accurately when all 'keys' for a table are ALL sequential numbers\integers. 
  -- Somewhere on the Lua forum: post#3 
  -- I have a post that talks about this as well as pairs vs ipairs from a learning\teaching perspective. 
  -- moving on... actually while we're here talking about parameter sanity checking i should mention we 
  -- we could have\should have checked if ascontact is actually of type 'table' and uname of type 'string'.
  -- I tend to not bother doing that on non-user facing code where the chance of them being the wrong type vs being nil is umm about nil.
  if ((uname~=nil) and ascontact ~=nil) and #ascontact > 0 then
    local retval,cw;  -- Define two local variables that exist inside the function and are used in the upcoming for.
    for i=1,#ascontact do  --Loop though the table that was submitted to this function.
      -- Here is our good friend pcall() working for us again to wrap the ScenEdit_GetContact call.
      -- Why bother you say shouldn't the contact always exist if it was in the list, so it should never fail right?
      -- That is true like 99.9% of the time, and 99.999999% if the game is paused. But it's a multithreaded app with lots going on
      -- it is possible that a contact\unit whatever doesn't exist a millisecond or two after it previously did.
      -- It's mainly just habit, and I consider it good form when you don't want the error to stop execution of chain of functions.
      -- Basically I default to using it unless I have reasons not too - and there are reasons, including lazy days, but I digress.  
      --   Now back to the the call, it calls ScenEdit_GetContact for us and it requires a side name, and the guid we want to grab on that side.
      -- The thing is it's one of the few functions that does not take a guid instead of a name, and all we have in the table is the side's guid.
      -- If only we had a up to date quick lookup table to convert a side-guid to a side-name without having to call vp_getsides and 
      -- loop though looking for a match every time this gets call.  OH WAIT... we created that already for just this sort of use!
      -- We put gKH.SideGuidCache to use and insert the value of entry for the sideguid we do have which will result in the textual name.
      retval,cw = pcall(ScenEdit_GetContact,{side=gKH.SideGuidCache[ascontact[i].side],guid=ascontact[i].guid})
      -- Here we do the usual check again to make sure the call was successful and the return value wasn't nil.
      -- technically this works fine, but I should have put parens around retval == true ie ideally it should be (retval==true) and cw...
      if retval ==true and cw ~=nil then
        print("changing " .. .. " to " .. uname .. " on side ".. ascontact[i].side); --this prints the success to the log for the hell of it.
        -- ^if we wanted it to print the textual side name and not the guid change ascontact[i].side to = uname;  -- This actually makes the change. is the contact wrapper field for the descriptive name. 
      end  --ends the retval\cw checking 'if'
    end  --ends the for-loop related to checking each contact entry in ascontacts table.
  end  --ends the 'if' at the top that was checking the parameters and the table count.
end  -- ends the function definition. 
gKH.processSides("Ship");  -- CALLS gKH.processSides function with "Ship" as the unit type to actually run all this code.

Why the functions and not one large chunk of code that just does it all. Cause that's a mess to follow,manage, and maintain. Breaking things it into smaller manageable pieces is almost always the way to go where you can anyway. It also often makes things become somewhat re-usable even if that's not the upfront intent or much of a consideration when you're writing it. It sure makes copy\pasta\change easier in the future.
Suppose you wanted it applied to only Aircraft? Well just change "Ship" to Aircraft, or "Ship" to "Weapons", and if you wanted it to apply to everything you could just call it once for each type of unit. Now that I think about it there is a undocumented feature that shouldn't be relied on but I like that it's there, it's that if you feed it explicitly a nil unittype, not just wrong one, ie :unitsBy(nil), it will return everything. So in theory you could actually process every unit on all sides in the scene if you did gKH.processSides(nil), but our parameter checking actually stops you from doing that, but that's an easy change to remove it or make it convert say "ALL" to nil.

Parting thought about gKH being defined as local... you see originally for you I actually had everything global and in an OnSceneLoaded triggered action. All that was in this action was the last line gKH.processSides("Ship");.
BUT because I didn't want to complicate the sample scene more than technically needed. You see because everything involved in the above doesn't actually need to be global to work, that it's only being called once a minute, and that it is not performance heavy (in the simple scene) at least I copied everything back into this action and just stuck a 'local' in front of gKH. Which is just my own personal namespace preference for my stuff. I mention this because you will almost always see my uses of gKH in the global context. What it actually highlights is the flexibility in Lua if you store your stuff inside a table. Tables in Lua aren't just for 'data' storing some strings or numbers. They can are a building block for all sorts of things you might want to do and can be manipulated to work in all sorts of creative ways. That said you don't have to know beyond the basics for use with Lua in CMO. Eh back to my point which was because I stored everything inside gKH, I was able to change the whole thing to run non-globally by changing 1 line of code from 'gKH={};' to 'local gKH={};' and copy\paste\delete from one action to another. I didn't have to change anything else. Now in retrospect had I known I was going to come back to this same sample again to add stuff that did have to have a SceneLoaded trigger and OnSceneLoaded action and have globals for all the other changes made for THIS thread topic, than I would have left just everything as global and in the startup action I originally had but removed (lol and then added back for this thread's changes). Confused yet? lol

In fact since that exists now in the sample in post #2, you could if you wanted cut everything out of UpdateAutodetectedContactNames except for the last line, and paste it into the top or the bottom of the "OnSceneLoaded" action, and just change 'local gKH={};' to 'gKH={};'. Everything would work just the time, only technically a little fast because all the code above except for the last line global run at startup and not have to be compiled every 1 minute. Nothing, in theory, should break, in fact I encourage you to try the exercise cause that's really how it should be.

If fact to take it step further you could then if you wanted remove gKH={}; line, and change everywhere there is a gKH to a gParel (since gParel exists in the changes I made for this thread in the OnSceneLoaded action) then everything would be under one namespace\table called gParel.

Ok that was alot of typing, in fact it was probably way to much omg, hopefully there aren't too many typos. I hope it helped you more than confused you, or if not someone else who happens upon the thread in understanding something they did not before (though they might have to load the scenes to have full context). If there is any line of code, or any concept you still don't understand\follow or want clarity about just keep asking. little by little it'll add up. If I've confused the hell out of you about something (probably because Too much info) tell me.

I'll look at your update scene and see if I can see anything that stands out lua wise.

Edit: fixed some typos..there are probably more but sleep calls.

< Message edited by KnightHawk75 -- 7/30/2021 10:36:27 AM >

(in reply to Parel803)
Post #: 5
RE: NRT with FOC in Regular time trigger - 7/30/2021 12:34:18 PM   


Posts: 436
Joined: 10/10/2019
From: Netherlands
Status: offline
wow on all the text you take the time to write. I'm gonna read this evening. Thanks again.
LOL on the confusion between the two, my brain sometimes has some small smeltdown incidents.

Best regards Gert-Jan

but I don't know what part you might not be following, so I will try to assume little. You are correct :-)

< Message edited by Parel803 -- 7/30/2021 12:37:40 PM >

(in reply to KnightHawk75)
Post #: 6
RE: NRT with FOC in Regular time trigger - 7/30/2021 3:25:08 PM   


Posts: 1199
Joined: 11/15/2018
Status: offline

ORIGINAL: Parel803

wow on all the text you take the time to write. I'm gonna read this evening. Thanks again.
LOL on the confusion between the two, my brain sometimes has some small smeltdown incidents.

Best regards Gert-Jan

but I don't know what part you might not be following, so I will try to assume little. You are correct :-)

I looked at the attachment in post #4.
"FocGlobalFunctionLoad" event must be marked "repeatable" so that it loads on every sceneload, this is very important.

Trigger "15 min Repeat" is set to 30 seconds... I assume this is just during\for testing purposes I left it untouched.

I noticed there is no default value for:
gParel.SceneGlobals.PCSMessageUnitAutodetected = 0;
I noticed it gets set inside gParel.DeletePCSMessageUnit(uguid), and used nowhere else. Did I do that by mistake (probably?) or are you working on adding the autodetection stuff and this is just work in progress? Anyway I removed it in the attached scene.
gParel.SceneGlobals.PCSMessageRadius = 0; --typo in original that was meant to be 10 same as default.
I set to:
gParel.SceneGlobals.PCSMessageRadius = 16; -- the new default you have.

There was a line in gParel.UpdatePCSUnitRadius(rpside,rpbasename,newRadius,modeflag)
during mode=3 (deleting) it will fail because I screwed up and never changed the name part to rpbasename.
I know this was my mistake not yours, I just never noticed because I never changed the rpbasename global var like you did from the original.
rp = ScenEdit_GetReferencePoint({side=rpside, name='FocC '..i});
it should read:
rp = ScenEdit_GetReferencePoint({side=rpside, name=rpbasename..i});

In the action named "Initiate NRT tack and initial circle" there is a line:
gParel.GetExistingSavedValues(); --this should be at the end of GlobalFunctionsForFoc which runs first, imho.
Now if you really don't want the stored values loaded on startup I guess you don't have too, but I see no harm in it.
I personally think it makes more sense to do that upon Scene Loaded, it removes any ambiguity about the values being loaded or not at any point in the scene, and allows them to be used before the 15second delay. Which they aren't yet by anything so I guess it makes no difference. It's moved back in the attached version.

Action: "Initiate RT tracks + WPs"

tip: It's not technically wrong but in CMO Lua generally you don't want to start variable names with uppercase letters, because things starting with uppercase (when not inside some other namespace), are kinda of reserved for Lua itself and CMO api functions or CMO special vars like UnitX for example, there are more vars that can get added by QuickBattles that are undocumented too. But try to follow the no first letter uppercase rule at least for globals, locals matter less. So even though technically nothing here conflicted I would rename them to lowercase unless the lower case version conflict with a lua function name already, and they don't.
The line:
table.insert(FocMLA,{latitude=13.848, longitude=116.594,description='Leg2',presetThrottle='cruis'});
Is missing an 'e' in cruise
I renamed the vars in the version attached and added the 'e'. ;)

In the same action I'm annotating what's going on:
local Unit = ScenEdit_AddUnit({side=Side,type='Ship',name=Name,dbid=gParel.SceneGlobals.PCSMessageUnitDBID,
latitude='N14.34.00',longitude='E115.20.00',heading=Heading,speed=Speed }); --create unit store resulting unit it var Unit

local UC = ScenEdit_GetUnit({side=Side, name=Name}) -- now go get the unit we just created again?
local FocMLA = {}; -- create table
table.insert(FocMLA,{latitude=13.848, longitude=116.594,description='Leg2',presetThrottle='cruis'}); --insert in pos1
table.insert(FocMLA,{latitude=12.711, longitude=116.310,description='Leg3',presetThrottle='cruise'});--insert in pos2
table.insert(FocMLA,{latitude=11.464, longitude=115.895,description='Leg4',presetThrottle='creep'}); --insert in pos3
UC.course = FocMLA; -- replace course table with our own.
I would maybe suggest this and included the change in the attached:
local unit = ScenEdit_AddUnit({side=Side,type='Ship',name=Name,dbid=gParel.SceneGlobals.PCSMessageUnitDBID,
latitude='N14.34.00',longitude='E115.20.00',heading=Heading,speed=Speed }); --create unit store resulting unit it var unit

-- I tweaked the creation of the table.
-- There is no functional difference in the result vs the table.insert method, what you had was _perfectly fine_,
-- just showing another way without without having to call table.insert when are initially making a small table.
local FocMLA = {
[1]={latitude=13.848, longitude=116.594,description='Leg2',presetThrottle='cruise'},
[2]={latitude=12.711, longitude=116.310,description='Leg3',presetThrottle='cruise'},
[3]={latitude=11.464, longitude=115.895,description='Leg4',presetThrottle='creep'}
unit.course = FocMLA; --note unit already holds the created unit wrapper, there is not need to re-get it again into uc.

Also I added an event, trigger, and code related to trapping if the unit in question get's destroyed, if it does then gParel.DeletePCSMessageUnit(uguid,true) get's called to clean up the RP's and update state. I enhanced that function with a second parameter called destroyedFlag, and logic inside the function to handle cases where the unit no longer exists, even though it's accessible via UnitX for a second more. This enhancement lets someone still call the function manually, but the event triggered code to call this function to just clean up state, while letting the game naturally finish destroying the unit.

gParel.UpdatePCSUnitRadius() - I enhanced this for handling cases where mode 3 (ie delete mode) and the unit doesn't exist anymore, basically we dummy up a fake lat and lon for 'u' so the exist for-loop still runs 16 times.

See new event "NrtRed_CheckForDestroyed"
See new trigger "Any_NrtRedShip_Destroyed"
See new action "HandleDestroyedNrtUnit"
The contents of that action are:
local unitguid = ScenEdit_UnitX().guid
if (unitguid ~=nil and gParel.SceneGlobals.PCSMessageUnitGuid ~=nil) and unitguid == gParel.SceneGlobals.PCSMessageUnitGuid then
    gParel.DeletePCSMessageUnit(unitguid,true); --removes all the rpcs and resets active state, basically everything back to starting state.
  -- the new 'true' parameter tells the function not to actually delete the unit, as the game finish do that itself.

I tested that this works.. by inserting a b-2 and droping a nuke on the ship 25 seconds into the scene. Boom. No more ship and the RP's all get removed as you would expect.

I also enhanced 1 line of gParel.GetAndCheckKeyVal() I just reversed the logic such that there is no longer an 'else', nothing really changed except 1 less line of code.
Introduced SceneGlobals.PCSMessageRunOnce and related PCSMessageRunOnce storage key. The thing is using PCSMessageActive to determine if things had ever run before wouldn't work in some edge cases, basically bebecause upon the unit being destroyed the storage keys are cleared and The SceneGlobals.PCSMessageActive global is reset to 0. You can now use SceneGlobals.PCSMessageRunOnce to indicate when not to create the unit due to having been done 'ever' before.
(I made the changes needed) This is more resilient in that the PCSMessageRunOnce key once set is never cleared. Unless you unhide the new Blue special action to manually clear it - this is provide so that during development if you end up saving a copy that has it set you can just press a button and re-save and reload with it then cleared. attached.

Attachment (1)

< Message edited by KnightHawk75 -- 7/30/2021 3:29:46 PM >

(in reply to Parel803)
Post #: 7
RE: NRT with FOC in Regular time trigger - 7/31/2021 7:43:52 AM   


Posts: 436
Joined: 10/10/2019
From: Netherlands
Status: offline
KnightHawk, thanks again.
I'm half way your previous post. It is nice to understand a little more for was is written in all the Lua files. We stay with our boat in the current marina due to the weather so that gives me a bit more time reading it all. You're text (and time) is highly appreciated.
I have the feeling that I understand most of what is written, not yet able to write it myself but small steps.
best regards GJ

(in reply to KnightHawk75)
Post #: 8
Page:   [1]
All Forums >> [New Releases from Matrix Games] >> Command: Modern Operations series >> Mods and Scenarios >> Lua Legion >> NRT with FOC in Regular time trigger Page: [1]
Jump to:

New Messages No New Messages
Hot Topic w/ New Messages Hot Topic w/o New Messages
Locked w/ New Messages Locked w/o New Messages
 Post New Thread
 Reply to Message
 Post New Poll
 Submit Vote
 Delete My Own Post
 Delete My Own Thread
 Rate Posts

Forum Software © ASPPlayground.NET Advanced Edition 2.4.5 ANSI