Lua script working - almost

All discussions & material related to Command's Lua interface

Moderators: michaelm75au, angster, RoryAndersonCDT, MOD_Command

Post Reply
User avatar
vettim89
Posts: 3668
Joined: Fri Jul 13, 2007 11:38 pm
Location: Toledo, Ohio

Lua script working - almost

Post by vettim89 »

You ever have one of those problems where you can solve everything except for one minor detail?

Code: Select all

local count=tonumber(ScenEdit_GetKeyValue('SURFACE1'))
local test, unit
local s=ScenEdit_SelectedUnits()
for i=1,#s.units do
local g=s.units[i].guid
local ship=ScenEdit_GetUnit({side='USSR', guid=g})
if ship.type~='Ship' then
ScenEdit_MsgBox('This action is only available for surface ships or groups', 1)
else 
test=true
end
end
if #s.units>1 then
uname=s.name
else
uname=s.units[1].name
end
if test==true then
TeleportSub('SURFACE 1', uname)
count=count-1
end
if count==0 then
ScenEdit_MsgBox('You have now used this Special Action the maximum allowed times', 1)
ScenEdit_SetSpecialAction({ActionNameOrID='Teleport SURFACE 1', mode='update', isactive=false})
else
local message='You may only use this action ('..count..') more times'
ScenEdit_MsgBox(message, 1)
ScenEdit_SetKeyValue('SURFACE1', count)
end
So this is a special action where the player selects a surface ship or group of surface ships (we'll get to that) and it teleports to the ship or group to a random location near a reference point. I have a similar special action for submarines that is working perfectly. If I select only one ship, this special action works perfectly. If I select a group or ships, the error pop-up appears but the special action still moves the group. So it both errors and doesn't error simultaneously. Do you think changing the for loop to a while loop would work instead.

local test = false
while test ~= true do

Would that work?
"We have met the enemy and they are ours" - Commodore O.H. Perry
KnightHawk75
Posts: 1850
Joined: Thu Nov 15, 2018 7:24 pm

Re: Lua script working - almost

Post by KnightHawk75 »

I rewrote this a little using a utility function, such that I think it works more as you intended now.
What don't know is what you're teleport function looks like, so I just have a dummy one in this example, difference is mine doesn't take a unit name it expects the full wrapper already which is provided too it. I'm not sure where your problem exactly was, but you weren't checking for cases of group + grouptype for your validation, and I think some of the logic flow needed tweaking.

Anyway... The following should work I think exactly how you intend it, allow only ships or groups of surface ships (if others included ask use to try again after deselecting the invalid selection), move each one, deduct usage, message user accordingly.

Code: Select all

local gKH = {}; gKH.Util = {}; -- just setup for the example adjust names and local/global as needed


--  Utility function to return all the unit and or group wrappers currently selected, optionally to not return groups themselves 
--  but all expanded members unit wrappers instead. 
---@param expandGroups boolean --defaults to true, controls if group wrappers are returned or all their members unit wrappers are returned.
---@return table -- empty table on error or no selections otherwise a table of unit wrappers indexed by guid.
function gKH.Util.GetSelectedUnitsAllMemberUnits(expandGroups);
    local fn = "gKH.Util.GetSelectedUnitsMemberUnits() ";
    if expandGroups == nil then expandGroups=true; end
    local unitTable = {};
    local su=ScenEdit_SelectedUnits();
    if ((su~=nil) and su.units ~=nil) and #su.units > 0 then 
        local retval,u = false,nil
        for i=1,#su.units do
            retval,u = pcall(ScenEdit_GetUnit,{guid=su.units[i].guid});
            if ((retval == true) and u~=nil) and u.type ~='Group' then  --are we valid and not a group?
                unitTable[u.guid] = u; --store unit
            elseif ((retval == true) and u~=nil) and u.type =='Group' and expandGroups ==true then  --are we valid and also a group?
                g = u.group.unitlist; --simply array of unit guids of members.
                for l,m in pairs(g) do
                        retval,u = pcall(ScenEdit_GetUnit,{guid=m}); --get the unit.
                        if ((retval == true) and u ~= nil) and u.type ~='Group' then -- are we valid and not also another group?
                            unitTable[u.guid] = u; --store unit
                        else 
                            print(fn.. 'Group inside Group not supported. Skipping this unit because group with-in group?? guid: ' ..tostring(m));
                        end
                    retval=false;u=nil;
                end
            elseif ((retval == true) and u~=nil) and u.type =='Group' and expandGroups ==false then  --are we valid and also a group?
                unitTable[u.guid] = u; --store the unit-group wrapper only.
            else
                print(fn.. 'Could not obtain the object for one of selected units with guid: ' ..tostring(v.guid));
            end
            u=nil;retval=false;
        end
        return unitTable;  --we're done
    else
        print(fn.. 'No units are currently selected.')
    end
    return {};
end


-- my teleport version provides that you don't feed it the name of the unit but whole wrapper.
-- you didn't provide code for that function so I have no visibility into yours this is dummy just to simulate it.
local function TeleportSub(somestring,unitwrapper)
    --...actual stuff here ... check for elevation depth etc for subs vs ships..vs desired depth 
    --...etc etc
    if unitwrapper ~=nil then  
    -- if wrapper is unit then it will move unit to location. setting each specifically to a location.
    -- if a group wrapper you're moving the lead and the other member units are moved as well relative to leads location.
        unitwrapper.latitude=1.0; --test data
        unitwrapper.longitude=1.0; --test data
        print('Moving' .. tostring(unitwrapper.name) .. ' to lat:'.. unitwrapper.latitude .. '  lon:' ..unitwrapper.longitude);
    else
        print('TeleportSub(): unitwrapper not supplied, failed to move unit .');
    end
-- ..other stuff
end



--Your general logic code, didn't need to be function but you know how I roll. ;)

local function DoTheThing()
    local count=tonumber(ScenEdit_GetKeyValue('SURFACE1'))
    local uwTable = gKH.Util.GetSelectedUnitsAllMemberUnits(false); 
    -- use true if you want individual units instread of group wrapper being included in uwTable.
    -- the difference will be with groups included you might say teleport a group and it's members locations stay relative to the lead.
    -- vs with true, if you did the same things each unit would need to be assigned a specific location.

    --validation
    for _,unitwrap in pairs(uwTable) do
        if unitwrap.type~='Ship' then
            if unitwrap.type ~='Group' or ((unitwrap.type=='Group') and unitwrap.group.type ~='SurfaceGroup') then
                ScenEdit_MsgBox('This action is only available for surface ships or groups. selected unit '..tostring(unitwrap.name) ..' violates this, try again with it unselected.', 0)
                return false; --exit don't charge the user if any ships found, indicate a failure.
            end
        end
    end
    --attempt run through list doing teleportation if count is valid.
    if count > 0 then  -- 
        local ranAtLeastOnce = false;
        for _,unitwrap in pairs(uwTable) do
            TeleportSub('SURFACE 1', unitwrap)  --call teleport per unit, pass unitwrapper since we already have it.
            ranAtLeastOnce = true;
        end
        if ranAtLeastOnce then count=count - 1 end; -- deplete account only if there was at least one unit teleported we can't check #uwTable cause it's not indexed by number so this instead.
    end

    --bookkeeping
    if count < 1 then
        ScenEdit_MsgBox('You have now, or already used this Special Action the maximum allowed times', 0);
        ScenEdit_SetSpecialAction({ActionNameOrID='Teleport SURFACE 1', mode='update', isactive=false});
        ScenEdit_SetKeyValue('SURFACE1', count);
    else
        ScenEdit_MsgBox('You may only use this action (' ..count.. ') more times', 0);
        ScenEdit_SetKeyValue('SURFACE1', count);
    end
    return true; 
end

--ScenEdit_SetKeyValue('SURFACE1', "2"); --for testing.
DoTheThing();
Seems to work fine in build 1147.44
User avatar
vettim89
Posts: 3668
Joined: Fri Jul 13, 2007 11:38 pm
Location: Toledo, Ohio

Re: Lua script working - almost

Post by vettim89 »

Mr. Knighthawk, Sir,

I understand this code for the most part. I can see I was trying to shortcut the Lua logic with my simple code. I was wondering if you see any problem passing the key for the key value and the RP for the teleport anchor into the Function DoTheTHing?

BTW here is my Teleport code. It is base on a RP so the player can see approximately where the unit will end up

Code: Select all

function TeleportSub(rp, sub)
local r=ScenEdit_GetReferencePoint({side='USSR', name=rp})
local dist=math.random(1,50)
local bear=math.random(1,360)
local point=World_GetPointFromBearing({latitude=r.latitude, longitude=r.longitude, distance=dist, bearing=bear})
ScenEdit_SetUnit({side='USSR', name=sub, longitude=point.longitude, latitude=point.latitude})
end
"We have met the enemy and they are ours" - Commodore O.H. Perry
KnightHawk75
Posts: 1850
Joined: Thu Nov 15, 2018 7:24 pm

Re: Lua script working - almost

Post by KnightHawk75 »

vettim89 wrote: Tue Jun 07, 2022 1:45 am Mr. Knighthawk, Sir,

I understand this code for the most part. I can see I was trying to shortcut the Lua logic with my simple code. I was wondering if you see any problem passing the key for the key value and the RP for the teleport anchor into the Function DoTheTHing?
I don't see a problem, but yes you could make dothething take the parameters, such that you can better reuse it all.
So in total... using your TeleportSub version it might look like this:

Code: Select all

local gKH = {}; gKH.Util = {}; -- just setup for the example adjust names and local/global as needed


--  Utility function to return all the unit and or group wrappers currently selected, optionally to not return groups themselves 
--  but all expanded members unit wrappers instead. 
---@param expandGroups boolean --defaults to true, controls if group wrappers are returned or all their members unit wrappers are returned.
---@return table -- empty table on error or no selections otherwise a table of unit wrappers indexed by guid.
function gKH.Util.GetSelectedUnitsAllMemberUnits(expandGroups);
    local fn = "gKH.Util.GetSelectedUnitsMemberUnits() ";
    if expandGroups == nil then expandGroups=true; end
    local unitTable = {};
    local su=ScenEdit_SelectedUnits();
    if ((su~=nil) and su.units ~=nil) and #su.units > 0 then 
        local retval,u = false,nil
        for i=1,#su.units do
            retval,u = pcall(ScenEdit_GetUnit,{guid=su.units[i].guid});
            if ((retval == true) and u~=nil) and u.type ~='Group' then  --are we valid and not a group?
                unitTable[u.guid] = u; --store unit
            elseif ((retval == true) and u~=nil) and u.type =='Group' and expandGroups ==true then  --are we valid and also a group?
                g = u.group.unitlist; --simply array of unit guids of members.
                for l,m in pairs(g) do
                        retval,u = pcall(ScenEdit_GetUnit,{guid=m}); --get the unit.
                        if ((retval == true) and u ~= nil) and u.type ~='Group' then -- are we valid and not also another group?
                            unitTable[u.guid] = u; --store unit
                        else 
                            print(fn.. 'Group inside Group not supported. Skipping this unit because group with-in group?? guid: ' ..tostring(m));
                        end
                    retval=false;u=nil;
                end
            elseif ((retval == true) and u~=nil) and u.type =='Group' and expandGroups ==false then  --are we valid and also a group?
                unitTable[u.guid] = u; --store the unit-group wrapper only.
            else
                print(fn.. 'Could not obtain the object for one of selected units with guid: ' ..tostring(v.guid));
            end
            u=nil;retval=false;
        end
        return unitTable;  --we're done
    else
        print(fn.. 'No units are currently selected.')
    end
    return {};
end

-- Your updated TeleportSub() --

function TeleportSub(rp, sub)
        if rp ~=nil and sub ~=nil then
		local dist=math.random(1,50);
		local bear=math.random(1,360);
		local point=World_GetPointFromBearing({latitude=rp.latitude, longitude=rp.longitude, distance=dist, bearing=bear});
		sub.longitude=point.longitude; sub.latitude=point.latitude;
	else
		print('TeleportSub(): rp  or unit wrapper missing or nil, function call aborted.');
	end
end



--Your general logic code, didn't need to be function but you know how I roll. ;)

local function DoTheThing(rpwrapper,rpname)
    local count=tonumber(ScenEdit_GetKeyValue(keystorevalue))
    local uwTable = gKH.Util.GetSelectedUnitsAllMemberUnits(false); 
    -- use true if you want individual units instread of group wrapper being included in uwTable.
    -- the difference will be with groups included you might say teleport a group and it's members locations stay relative to the lead.
    -- vs with true, if you did the same things each unit would need to be assigned a specific location.

    --validation
    for _,unitwrap in pairs(uwTable) do
        if unitwrap.type~='Ship' then
            if unitwrap.type ~='Group' or ((unitwrap.type=='Group') and unitwrap.group.type ~='SurfaceGroup') then
                ScenEdit_MsgBox('This action is only available for surface ships or groups. selected unit '..tostring(unitwrap.name) ..' violates this, try again with it unselected.', 0)
                return false; --exit don't charge the user if any non-ships found, indicate a failure.
            end
        end
    end
    --attempt run through list doing teleportation if count is valid.
    if count > 0 then  -- 
        local ranAtLeastOnce = false;
        for _,unitwrap in pairs(uwTable) do
            TeleportSub(rpwrapper, unitwrap)  --call teleport per unit, pass rp and unitwrapper since we already have it.
            ranAtLeastOnce = true;
        end
        if ranAtLeastOnce then count=count - 1 end; -- deplete account only if there was at least one unit teleported we can't check #uwTable cause it's not indexed by number so this instead.
    end

    --bookkeeping
    if count < 1 then
        ScenEdit_MsgBox('You have now, or already used this Special Action the maximum allowed times', 0);
        ScenEdit_SetSpecialAction({ActionNameOrID='Teleport SURFACE 1', mode='update', isactive=false});
        ScenEdit_SetKeyValue(keystorevalue, count);
    else
        ScenEdit_MsgBox('You may only use this action (' ..count.. ') more times', 0);
        ScenEdit_SetKeyValue(keystorevalue, count);
    end
    return true; 
end

--ScenEdit_SetKeyValue('SURFACE1', "2"); --for testing.
--DoTheThing('SURFACE99',ScenEdit_GetReferencePoint({side='SomeOtherSide', name='SURFACE 1234'}));
DoTheThing('SURFACE1',ScenEdit_GetReferencePoint({side='USSR', name='SURFACE 1'}));

Post Reply

Return to “Lua Legion”