LUA - Determining if an Open Borders agreement in place?

@LeeS - thanks for the suggestion about PlayerDoTurn. Have made the adjustments, even borrowed heavily from your UnitSpawnHandler mod (why this isn't used by every mod on the planet is a mystery to me...). Using this approach is much more efficient and seems to be more trustworthy during game play.

For the most part everything is set up well, but... :help:

When the PlayerDoTurn function fires, it looks thru all a player's units, and if it finds an airfield, it looks at the TI for that plot. If it's not an airfield TI, it fires the "DismantleAirfield" function.

Dismantle Airfield basically loops through all units at that plot, and if their 'GetTransportUnit' is equal to the Airfield unit, it attempts to relocate them to a city with spare air capacity.

In testing, I have an airfield with 8 units that is being dismantled. Unfortunately, for some reason only half the units make the jump. The other half come back from the 'unit = plot:GetUnit(i)' statement as 'nil'

Not sure if it's relevant, but if I look at the list of units using IGE (mouse hovering over the plot) - on the list of 8 air units shown before the function executes - every other unit makes the jump (e.g. #1, 3, 5, and 7).

Here are some screenshots to hopefully better explain the before/after function executes:

upload_2018-12-21_7-18-30.jpeg upload_2018-12-21_7-18-47.jpeg upload_2018-12-21_7-19-2.jpeg upload_2018-12-21_7-19-15.jpeg upload_2018-12-21_7-19-30.jpeg


I'm just confused as how any unit could come out of the 'local pPlotUnit = pAirfieldPlot:GetUnit( i )' statement as nil?


Spoiler Actual Code :


Code:
function DismantleAirfield(pPlayer, pUnit, pAirfieldPlot)                                    -- Properly relocates aircraft, then deletes airfield unit
    local iUnitCount = pAirfieldPlot:GetNumUnits() or 0                                        -- How many units (of any kind) are there?
    local iRelocatedCount = 0
    local iUnrelocatedCount = 0
    local iAirfieldX = pUnit:GetX()
    local iAirfieldY = pUnit:GetY()


    if iUnitCount > 0 then                                                                    -- If there are more than 0 units, then (should always be true, due to airfield unit)
        dPrint("DismantleAirfield.  Units to process: " .. iUnitCount)
        
        for i = 0, iUnitCount - 1, 1 do                                                        -- Loop thru all units on this plot
            local pPlotUnit = pAirfieldPlot:GetUnit( i )                                    -- Get the Unit pointer for unit 'i'           

            dPrint("================ i: " .. i)       

            if pPlotUnit then
                dPrint("Unit: " .. pPlotUnit:GetName() .. " (" ..pPlotUnit:GetID() .. ")" )
                if pPlotUnit:GetTransportUnit() == pUnit then                                    -- If the pPlotUnit is cargo of pUnit, then...  (unit belongs to the airfield unit)                           
                    dPrint("Unit was stationed at this " .. pUnit:GetName())               
                    local pRelocationCity = FindEmptyCity(pPlayer)                                -- Find a city with spare air unit capacity
                    if pRelocationCity then                                                        -- If there is such a city, then
                        local iOldX = pPlotUnit:GetX()                                            -- Get old X location for debug
                        local iOldY = pPlotUnit:GetY()                                            -- Get old X location for debug
                        local x = pRelocationCity:GetX()                                        -- Get new city X location for jumping
                        local y = pRelocationCity:GetY()                                        -- Get new city Y location for jumping
                        pPlotUnit:SetXY(x,y)                                                    -- Physically relocate the unit to the new city's X/Y
                        iRelocatedCount = iRelocatedCount + 1
                        dPrint("Relocating Unit " .. pPlotUnit:GetName() .. " to " .. pRelocationCity:GetName() .. ".   Old Location: " .. iOldX .. " / " .. iOldY .. ",   New Location: " .. x .. " / " .. y)                   
                    else
                        iUnrelocatedCount = iUnrelocatedCount + 1
                    end                                                                        -- Otherwise, skip this unit (which will ultimately be deleted)                   
                end
            else
                print("PlotUnit nil")
            end
        end
    end
    local sHeader = pPlayer:GetCivilizationShortDescription() .. " dismantled a " .. pUnit:GetName()
    local sMessage = pPlayer:GetCivilizationShortDescription() .. " has dismantled a " .. pUnit:GetName() .. ", located at Tile  " .. pUnit:GetX() .. " / " .. pUnit:GetY() .. "."
    
    if iRelocatedCount > 0 then
        sMessage = sMessage .. "  They managed to relocate " .. iRelocatedCount .. " aircraft."
    end

    if iUnrelocatedCount > 0 then
        sMessage = sMessage .. "  Unfortunately, " .. iUnrelocatedCount .. " aircraft could not be relocated and were lost."
    end
    
    AddAirfieldNotification(2, pPlayer, sHeader, sMessage, pUnit:GetUnitType(), iAirfieldX, iAirfieldY)

    pUnit:Kill()                                                                            -- Remove the Airfield unit from the map, along with all unrelocated planes
end

 
Last edited:
You can't move the units in the loop that checks the units - because that alters the number of units on the plot.

5 units, 2 and 3 are planes

1 is this a plane? no …
2 is this a plane? yes … so jump the plane, but that now makes the old unit 3, unit 2 etc
3 … but we are now looking at the new unit 3 (which is the old unit 4), so we never process the old unit 3

You need TWO loops
Loop the units on the plot, if they are planes, store their ID in an array
Now loop the array and move the planes
 
D'oh! :wallbash:

I stared at this a couple of hours yesterday, much like a pig stares at a wrist watch...

And you glance at it for a couple of seconds.... :worship:

Much appreciate the advice! Send me a paypal link and I'll show this appreciation in a more tangible way! :xmas:
 
And you glance at it for a couple of seconds.... :worship:

Having posted a similar answer at least three times before on these boards … it's a common SNAFU ;)
 
Thanks! My SNAFU was common? Seems appropriate, as SNAFUs are common in my mods... :crazyeye:

One (hopefully last!) question - I originally had the Airfield units with 2 movement points, but the Immobile element set true. It was the best looking alternative in the standard UnitPanel UI. This allowed the player to delete the unit if they wanted to get rid of it. Of course it deleted any cargo units, and I was OK with that. The problem I had is that I was never able to find an event I could hook in to so I could subsequently remove the terrain improvement.

CanSaveUnit didn't fire, nor did UnitKilledInCombat.

Ultimately, I set movement to 0 - which prevents users from removing an airfield without using a Worker to plant a new improvement on top of it. I think I'm OK with that, but was curious if there's an event out there that pops when you delete a unit using the DEL key?
 
UnitPrekill

But it is a booger to use since it usually fires at least twice for the same unit-removal from the game. Once actually before the unit is removed, and once after the unit is actually removed. It fires for every unit removal from the game, for whatever reason, and also as I recall fires for unit upgrades. It fires when a barb captures a worker, for example, showing the worker as the 'removed' unit.
 
Thanks, @LeeS - I didn't see that event in the Modiki, but I keep forgetting to refer to WHoward's spreadsheet.

Looking at it now, does it allow dealing with the actual unit instance (in the columns with these entries: int:iUnit OR UnitTypes:eUnit) ?
 
You have to use the "iUnit" value to find the unit's object. The "iUnit" value will be the UnitID# for that specific unit.

"iUnitType" will be the ID# for the type of unit from the Units XML-table, so refers to UNIT_SETTLER, UNIT_WARRIOR, etc.
Code:
function NearProconsulKiller(iOwner, iUnit, iUnitType, iX, iY, bDelay, iKiller)
	local pOwnerPlayer = Players[iOwner]
	local pUnitKilled = pOwnerPlayer:GetUnitByID(iUnit)
	--etc
end
  1. "bDelay" will be true before the unit is actually removed, and "iKiller" will be the PlayerID of the player who 'killed' the unit when "bDelay" is true.
  2. When the owner of the unit deletes it or uses it to found a city, etc., 'iKiller' will be the ID# of the player who "used" the unit. As I recall in these cases the event only fires once for that unit-removal.
  3. "bDelay" will be false after the unit is actually removed, and "iKiller" will be '-1' when "bDelay" is false.
 
Back
Top Bottom