Delayed action -passing unit (Full Version)

All Forums >> [New Releases from Matrix Games] >> Command: Modern Operations series >> Mods and Scenarios >> Lua Legion



Message


vettim89 -> Delayed action -passing unit (3/25/2021 1:52:36 AM)

So I am trying to set up an event where a unit enters an area and this action triggers a delay. After the delay the unit will then activate/execute a new mission. This is a repeatable action so in the future a different unit may trigger the same event

I tried a very clunky method where the first action created an new event named after the unit that activated the initial trigger. After the delay is where I have the problem because I have to pass the units name/guid to the new event. My thought I would just pass the event name into the action as the unit name (because the even name and unit name are the same) Hit a roadblock here as I cannot figure out how to do that.

Is there A. a way to pass the event name into the action or b. another way to store this unit's name for future use

I though about using a key value. Can you make a key value a table? I could then store the unit name in the key value 'table' and use another key value as the counter. Just thinking out loud.

Or gasp, is this a place I could use a condition?




jkgarner -> RE: Delayed action -passing unit (3/25/2021 11:17:08 AM)

Hi, There are two way to solve this issue. By passing the information forward as parameters along the chain of events, or by packing the unit and side names into the event name.

Method one:
Write functions for each event that takes the unit identifying data as parameters, fall them something meaningful (e.g. foo1, foo2)

The script for the first event will use UnitX to get the unit identifying data and pass it to foo1.

foo1, can put the info into the script generated for event 2 as follows:

local script = ""
script = script.."foo2('"..side-name.."', '"..unit_name.."')"

The drawback to this method is that both functions must be visible, which means they must belong to a namespace (or object) that is loaded by the scenario. The best way to do this is through the use of scenario attachments. if this is too much for you right now, there is the second method.

Method 2:
If you pack data into the event name you can then parse the event name and get the information you need. The only trouble you will have is to guarantee uniqueness of the event name across the system. One way to do this is also pack something else unique, like the time the event was triggered.


Here is a function that packs data to use as an example: (you will need to modify it for your data)
StepKeyValueName =
function(side_name, unit_name, day)
	local separator = 'XXX'
	local stepKeyValueName = "StepValue"..separator..
	                         side_name..separator..
							 unit_name..separator..
							 "D"..day
	local retVal = RemoveSpecialChars(stepKeyValueName)
    return retVal
end

The justification and implementaion of RemoveSpecialChars can be found at the thread:
https://www.matrixgames.com/forums/tm.asp?m=4977343

The matching parse function is
ParseStepEventName =
function(stepEventName)
	local side = nil
	local unit = nil
	local day = nil
	local step = nil
	local suffix = nil
	if (stepEventName ~= nil and type(stepEventName) == 'string') then
		local separator = 'XXX'
		local data = LeMay.splitString(stepEventName,separator)
		if (#data > 5) then			
			side = data[2]
			unit = data[3]
			day = tonumber(string.sub(data[4], 2))
			step = tonumber(string.sub(data[5], 2))
			suffix = data[6]
		end
	end
	return side, unit, day, step, suffix
end


So in the first event, use UnitX function to get the unit data. Then pack it into a local vaariable eName and use that variable to set the event name when generating your event.

You will need to add the following to the beginning of your script:
local script = ""
script = script.."SpecialCharCodes = {".."\r\n"
script = script.."	[1]={char='`',   pat='%`', code='_btick_'},".."\r\n"
script = script.."	[2]={char='~',   pat='%~', code='_tilde_'},".."\r\n"
script = script.."	[3]={char='!',   pat='%!', code='_bang_'},".."\r\n"
script = script.."	[4]={char='@',   pat='%@', code='_at_'},".."\r\n"
script = script.."	[5]={char='#',   pat='%#', code='_hash_'},".."\r\n"
script = script.."	[6]={char='$',   pat='%$', code='_dol_'},".."\r\n"
script = script.."	[7]={char='%',   pat='%%', code='_pct_'},".."\r\n"
script = script.."	[8]={char='^',   pat='%^', code='_hat_'},".."\r\n"
script = script.."	[9]={char='&',   pat='%&', code='_amp_'},".."\r\n"
script = script.."	[10]={char='*',  pat='%*', code='_splat_'},".."\r\n"
script = script.."	[11]={char='(',  pat='%(', code='_op_'},".."\r\n"
script = script.."	[12]={char=')',  pat='%)', code='_cp_'},".."\r\n"
script = script.."	[13]={char='+',  pat='%+', code='_plus_'},".."\r\n"
script = script.."	[14]={char='=',  pat='%=', code='_eq_'},".."\r\n"
script = script.."	[15]={char='{',  pat='%{', code='_ocb_'},".."\r\n"
script = script.."	[16]={char='}',  pat='%}', code='_ccb_'},".."\r\n"
script = script.."	[17]={char='|',  pat='%|', code='_pipe_'},".."\r\n"
script = script.."	[18]={char='[',  pat='%[', code='_osb_'},".."\r\n"
script = script.."	[19]={char=']',  pat='%]', code='_csb_'},".."\r\n"
script = script.."	[20]={char='\\', pat='%\\', code='_bslash_'},".."\r\n"
script = script.."	[21]={char=':',  pat='%:', code='_colon_'},".."\r\n"
script = script.."	[22]={char='\"',  pat='%\"', code='_quote_'},".."\r\n"
script = script.."	[23]={char=';',  pat='%;', code='_scolon_'},".."\r\n"
script = script.."	[24]={char='\'', pat='%\'', code='_tick_'},".."\r\n"
script = script.."	[25]={char='<',  pat='%<', code='_lt_'},".."\r\n"
script = script.."	[26]={char='>',  pat='%>', code='_gt_'},".."\r\n"
script = script.."	[27]={char='?',  pat='%?', code='_ques_'},".."\r\n"
script = script.."	[28]={char=',',  pat='%,', code='_comma_'},".."\r\n"
script = script.."	[29]={char='.',  pat='%.', code='_stop_'},".."\r\n"
script = script.."	[30]={char='/',  pat='%/', code='_slash_'},".."\r\n"
script = script.."	[31]={char=' ',  pat='%s', code='_sp_'},".."\r\n"
script = script.."}".."\r\n"
script = script.."\r\n"












as well as

script = script.."ReplaceSpecialChars =".."\r\n"
script = script.."function(inputStr)".."\r\n"
script = script.."    local retval = inputStr".."\r\n"
script = script.."	local i = #LeMay.SpecialCharCodes -1".."\r\n"
script = script.."	while i > 0 do".."\r\n"
script = script.."		local cData = LeMay.SpecialCharCodes[i]".."\r\n"
script = script.."        local rpl = cData.char".."\r\n"
script = script.."        if (rpl == '%') then rpl = '%%' end".."\r\n"
script = script.."        if (rpl ~= ' ') then ".."\r\n"
script = script.."            retval= string.gsub(retval, cData.code, rpl) ".."\r\n"
script = script.."        end".."\r\n"
script = script.."		i = i - 1".."\r\n"
script = script.."	end".."\r\n"
script = script.."    --handle spaces!".."\r\n"
script = script.."    local temp = ''".."\r\n"
script = script.."    local strdata = LeMay.splitString(retval, '_sp_')".."\r\n"
script = script.."    local j = 0".."\r\n"
script = script.."    while j < #strdata do".."\r\n"
script = script.."        j=j+1".."\r\n"
script = script.."        if (temp ~= '') then ".."\r\n"
script = script.."            temp = temp..' '".."\r\n"
script = script.."        end".."\r\n"
script = script.."        temp = temp..strdata[j]".."\r\n"
script = script.."    end".."\r\n"
script = script.."    retval = temp".."\r\n"
script = script.."	return retval".."\r\n"
script = script.."end".."\r\n"



After you modify the above function to pack the name and parse the name to suit, you will have to put the parse function in the script (similar to what you see above)

In the script, use EventX to access the current event, and get its name, then use the parse function to extract the unit identifying information.

I have used both methods successfully to pass information to later events in a chain of events.

Good luck





vettim89 -> RE: Delayed action -passing unit (3/25/2021 3:00:03 PM)

I think my head just exploded[&o][&o][&o]




Gunner98 -> RE: Delayed action -passing unit (3/25/2021 3:30:34 PM)

Ok that looks like a slick way but my head exploded as well.

Here is how I do it - clunky and old fashioned - yes. But it works

I set up a non-playable side which I use for a bunch of things (I call mine Marker) but in this case I go to that side. Go to the other side of the world and set up a box of RPs.

Add a Geo-marker - make it non-AutoDetectable, name it what you want. You can get the GUID or just use a distinct name

When the triggering event happens use a Teleport action to put the geo-marker into the box

use a unit remains in area trigger to set your delay

You can use this same geo marker at various delays for your trigger and/or you can teleport more geo-markers into the same box

Been using this method since my first scenario well before Lua was around and still use it today (check NF#43 Red Devils)




KnightHawk75 -> RE: Delayed action -passing unit (3/25/2021 5:27:51 PM)

quote:

ORIGINAL: vettim89

So I am trying to set up an event where a unit enters an area and this action triggers a delay. After the delay the unit will then activate/execute a new mission. This is a repeatable action so in the future a different unit may trigger the same event

I tried a very clunky method where the first action created an new event named after the unit that activated the initial trigger. After the delay is where I have the problem because I have to pass the units name/guid to the new event. My thought I would just pass the event name into the action as the unit name (because the even name and unit name are the same) Hit a roadblock here as I cannot figure out how to do that.

Is there A. a way to pass the event name into the action or b. another way to store this unit's name for future use

I though about using a key value. Can you make a key value a table? I could then store the unit name in the key value 'table' and use another key value as the counter. Just thinking out loud.

Or gasp, is this a place I could use a condition?

When you delay the action by creating the new event for that unit that will happen in the future just add like 8 digit random code to the end of the name after say a special delimiter like say "||keycode here". Have a global table that tracks these names as keys and any needed associated data.
In the associated action get the name of the running event triggering it via ScenEdit_EventX, parse the keycode out, grab the data in the global table for that key if it exists.

This is what jkgarner was explaining I think more or less, I think I'm just repeating it in maybe more layman terms, at least in so far as I've done it, I use the keycode method cause I'm usually referring more than one piece of data or things I already have stored, but the packing solution he mentions works too.
Example:
--Startup\OnLoad Script 
MyGlobalTableName = {}; -- create a table to hold event keycode names.

--random 8 char alphanumeric generator
function GetRandomSuffix()
  local a = math.random(65,90); --generate random number from 65-90  {utf caps}
  local b = math.random(97,122); --generate random number from 97-122 {utf lower}
  local c = math.random(1000,99999)--generate random number from 1000-99999
  local d = math.random(65,90);--generate random number from 65-90  {utf caps}
  local e = math.random(97,122); --generate random number from 97-122 {utf lower}
  return '(' .. string.char(a,b) .. tostring(c) .. string.char(d,e) .. ')' 
end
Then in the action that is triggered that creates the future event it would be something like.
local keycode = GetRandomSuffix();
MyGlobalTableName[keycode] = {unitguid=guidofunitXhere,someOtherValueYouMightNeed=1234} -add data you need to the key
--...do whatever you are doing.. but when you create the event attach the keycode.
ScenEdit_SetEvent ("My Delayed Event Name ||" ..keycode ,{otherParamsHere})
--... do whatever you are doing
So say your eventname was "My Delayed Event Name ||xy1234ab"
Now can just use a simple parse to grab the end using string.match(eventnamehere, ".*%|%|(.*)")) in your action.
local ex = ScenEdit_EventX();
local keycode = string.match(ex.details.description, ".*%|%|(.*)")) --match at capture\return everything after ||
local guidOfUnit;
if keycode ~= nil then 
   guidOfUnit = MyGlobalTableName[keycode].unitguid
else
  print("could not parse keycode.")
end
if guidOfUnit ~= nil then
 --...whatever you really want to do with regard to that unit\guid here.
 --SceneEdit_AssignUnitToMission(guidOfUnit,"SomeMissionNameorGuidHere")
else
  print("could not obtain unit guid.")
end
As Gunner mentioned there may be other ways if you're really trying to avoid lua to avoid it in some cases.




jkgarner -> RE: Delayed action -passing unit (3/25/2021 11:24:41 PM)

Well, KnightHawk75, that was not quite what I had in mind, however using the KeyValues is another, potentially simpler way to do it, as well.

When you generate your second Event give it a unique name, using the time and unit name and side name from UnitX. Just concatenate them together. Use the Unique event name as the key for Storing the guid as a KeyValue

The following code shows how this works, while creating the next event.

local unit = UnitX()
if unit ~= nil then
	local unit_guid = unit.guid
	local time = ScenEdit_CurrentTime()
	local offSet = 62135596801 --number of seconds from 01-01-0001 to 01-01-1970
	local newTime = (time + offSet + seconds)*10000000
	local eTime  = string.format("%18.0f",newTime)
	local eName = 'EventForXXX'..side_name...'XXX'...unit_name...'XXX'...time
	ScenEdit_SDetKeyValue(eName, unit_guid)
	ScenEdit_SetEvent(eName, {mode='add', isRepeatable=false, isActive=ture})
	ScenEdit_SetTrigger({name=eName, Mode='add', type='Time', Time=eTime})
	ScenEdit_SetEventTrigger(eName, {mode = 'add', name = eName})
	local script = ""	
	script = script.."local e = ScenEdit_EventX()".."\r\n"
	script = script.."if e != nil then".."\r\n"
	script = script.."	local eName = e.name".."\r\n"
	script = script.."	local unit_guid = ScenEndit_GetValue(eName)".."\r\n"
	script = script.."	if unit_guid ~= '' then".."\r\n"
	script = script.."		local myUnit = ScenEdit_GetUnit({guid=unitguid})".."\r\n"
	script = script.."		--do what you want with the unit here...".."\r\n"
	script = script.."	end".."\r\n"
	script = script.."end".."\r\n"	
	ScenEdit_SetAction(eName,{mode='add', type='LuaScript', ScriptText=script})
	ScenEdit_SetEventAction(eName, {mode = 'add', name = eName})
end



The script added to the action will generate the following code in the action:
local e = ScenEdit_EventX()
if e != nil then
	local eName = e.name
	local unit_guid = ScenEndit_GetValue(eName)
	if unit_guid ~= '' then
		local myUnit = ScenEdit_GetUnit({guid=unitguid})
		--do what you want with the unit here...
	end
end

Have I lost you yet?

The idea is to put the guid of the unit, while you have access to it, in the Key_Value store, using the name of the event that you are about to create (the second event). That way when the next event executes, you simply get the name of the event, and use that as the key value to look up the guid. With the guid, you get the unit, and do what you want with it.


What gets tricky is that the code above MUST execute as the action of an event (the first event)
So you have to pass all that in to an action as a string, so that when it is placed in the action, the code at the top is the result.

The following code will do that:

local script = ""
script = script.."local unit = UnitX()".."\r\n"
script = script.."if unit ~= nil then".."\r\n"
script = script.."	local unit_guid = unit.guid".."\r\n"
script = script.."	local time = ScenEdit_CurrentTime()".."\r\n"
script = script.."	local offSet = 62135596801 --number of seconds from 01-01-0001 to 01-01-1970".."\r\n"
script = script.."	local newTime = (time + offSet + seconds)*10000000".."\r\n"
script = script.."	local eTime  = string.format(\"%18.0f\",newTime)".."\r\n"
script = script.."	local eName = 'EventForXXX'..side_name...'XXX'...unit_name...'XXX'...time".."\r\n"
script = script.."	ScenEdit_SDetKeyValue(eName, unit_guid)".."\r\n"
script = script.."	ScenEdit_SetEvent(eName, {mode='add', isRepeatable=false, isActive=ture})".."\r\n"
script = script.."	ScenEdit_SetTrigger({name=eName, Mode='add', type='Time', Time=eTime})".."\r\n"
script = script.."	ScenEdit_SetEventTrigger(eName, {mode = 'add', name = eName})".."\r\n"
script = script.."	local script = \"\"".."\r\n"	
script = script.."	script = script..\"local e = ScenEdit_EventX()\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"if e != nil then\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"	local eName = e.name\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"	local unit_guid = ScenEndit_GetValue(eName)\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"	if unit_guid ~= '' then\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"		local myUnit = ScenEdit_GetUnit({guid=unitguid})\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"		--do what you want with the unit here...\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"	end\"..\"\r\n\"".."\r\n"
script = script.."	script = script..\"end\"..\"\r\n\"".."\r\n"	
script = script.."	ScenEdit_SetAction(eName,{mode='add', type='LuaScript', ScriptText=script})".."\r\n"
script = script.."	ScenEdit_SetEventAction(eName, {mode = 'add', name = eName})".."\r\n"
script = script.."end".."\r\n"


This method works without having to have libraries of functions or namespaces pre-defined.
The double parsing gets tricky.





jkgarner -> RE: Delayed action -passing unit (3/26/2021 12:14:37 PM)

KnightHawk75 suggested a solution to your problem that involved creating a global object to hold data by keys. This works, but the global object can also hold functions, so if you go with creating a global object, you could write the event handler code and place it there. In my first post above, I was suggesting using the Key Value Store (SetKeyValue and GetKeyValue) in a way similar to KnightHawk75's global object. The draw back to my suggested method is you can not store functions there, just data.

So let's discuss writing event handler code. (AN ASIDE: The example code for Event Handling is atrocious!)

When I am writing my event handling code, I keep it simple by writing a function that is part of a global namespace, and simply call that function. The advantage to this is simplicity in the event generation code, and the ability to test the function.

For example:

If I have an event that triggers when an enemy unit enters area Yumptiyummp, I create a function called EnemyUnitEntersYumptiYump(unit_guid).

The code in the action is as follows:
local unit = UnitX()
if unit ~= nil then
    EnemyUnitEntersYumptyYump( unit.guid )
end
This in NOT what I pass the Action. If I just put quotes around the code and call it good it will look a mess in the action. Further, if I had used quotes in the code, it would not work.

I will wrap the code in quotes, converting it to a string, line by line. This has the advantage of preserving the code's shape in the action, which makes it easier to examine, and test. Further it preserves the code's shape so in truth, once you understand the process, it makes it easier to read.

The first thing I do is define the script variable ahead of the block that will be scripted:
local script = ""
Then I put the following in front of each line:
script = script.."
Then at the end of each line, I put:
".."\r\n"

So the code above will be converted to the following code to generate the script string (for passing into my action):
local script = ""
script = script.."local unit = UnitX()".."\r\n"
script = script.."if unit ~= nil then".."\r\n"
script = script.."    EnemyUnitEntersYumptyYump( unit.guid )".."\r\n"
script = script.."end".."\r\n"

This code did not have any double quote characters or escaped characters, so there was not need to do any further escaping. You can check the correctness of you stringification of the code by printing the script variable. The result should be identical to the starting code. If you did it correctly, you can copy the output of the print into the input area of the console and execute it.

Now, if the original code had double quotes, or escaped characters (like '\r\n') then you must escape double quotes lest the string get terminated too soon, and you must also escape the back slashes of the escaped characters to preserve them as escaped characters in the final string. If you examine the former example, you can note that I did not properly escape the escaped characters in the example. This become obvious when you print the script string. I have corrected the former example below so that you can see how escaping escape characters and double quotes work:
local script = ""
script = script.."local unit = UnitX()".."\r\n"
script = script.."if unit ~= nil then".."\r\n"
script = script.."	local unit_guid = unit.guid".."\r\n"
script = script.."	local time = ScenEdit_CurrentTime()".."\r\n"
script = script.."	local offSet = 62135596801 --number of seconds from 01-01-0001 to 01-01-1970".."\r\n"
script = script.."	local newTime = (time + offSet + seconds)*10000000".."\r\n"
script = script.."	local eTime  = string.format(\"%18.0f\",newTime)".."\r\n"
script = script.."	local eName = 'EventForXXX'..side_name..'XXX'..unit_name..'XXX'..time".."\r\n"
script = script.."	ScenEdit_SDetKeyValue(eName, unit_guid)".."\r\n"
script = script.."	ScenEdit_SetEvent(eName, {mode='add', isRepeatable=false, isActive=ture})".."\r\n"
script = script.."	ScenEdit_SetTrigger({name=eName, Mode='add', type='Time', Time=eTime})".."\r\n"
script = script.."	ScenEdit_SetEventTrigger(eName, {mode = 'add', name = eName})".."\r\n"
script = script.."	local script = \"\"".."\r\n"	
script = script.."	script = script..\"local e = ScenEdit_EventX()\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"if e != nil then\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"	local eName = e.name\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"	local unit_guid = ScenEndit_GetValue(eName)\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"	if unit_guid ~= '' then\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"		local myUnit = ScenEdit_GetUnit({guid=unitguid})\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"		--do what you want with the unit here...\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"	end\"..\"\\r\\n\"".."\r\n"
script = script.."	script = script..\"end\"..\"\\r\\n\"".."\r\n"	
script = script.."	ScenEdit_SetAction(eName,{mode='add', type='LuaScript', ScriptText=script})".."\r\n"
script = script.."	ScenEdit_SetEventAction(eName, {mode = 'add', name = eName})".."\r\n"
script = script.."end".."\r\n"
print(script)


In all cases, you can verify the conversion of code to a string by printing the string. The result of the print statement should be exactly the original code, and should be executable. Copy it from the output area, and paste it into the input area, and test it.

I went a bit off topic there with explaining how to wrap code in strings. Most of the complexity in event handling code that I see comes from wrapping the code in strings. The way to minimize this complexity is to minimize the amount of code that gets wrapped, and this is why I write event handler functions and place them in a global object.

If you avoid global namespaces or global objects, then you will see a lot of complicated event code, because of the conversion of the event code to strings when setting up the actions. Creating a global namespace to hold functions that are available throughout the scenario (from all the events) really allows you to simplify the event code. But it brings with it its own complexities.

If you have further questions, I am willing to answer them. I have found KNightHawk75 willing and capable. So ask away. Good Luck.




Page: [1]

Valid CSS!




Forum Software © ASPPlayground.NET Advanced Edition 2.4.5 ANSI
1.59375