Version 2.3.5

WHpersistence
This commit is contained in:
Christian Franz 2024-10-24 08:37:44 +02:00
parent a70fadc550
commit 0c1eb53a89
13 changed files with 450 additions and 286 deletions

Binary file not shown.

Binary file not shown.

96
modules/WHpersistence.lua Normal file
View File

@ -0,0 +1,96 @@
WHpersistence = {}
WHpersistence.version = "1.0.0"
WHpersistence.requiredLibs = {
"dcsCommon",
"cfxZones",
"persistence",
}
--
-- load / save (game data)
--
function WHpersistence.saveData()
local theData = {}
local theWH = {}
-- generate all WH data from all my airfields
local allMyBase = world:getAirbases()
for idx, theBase in pairs(allMyBase) do
local name = theBase:getName()
local WH = theBase:getWarehouse()
local inv = WH:getInventory()
theWH[name] = inv
end
theData.theWH = theWH
return theData, WHpersistence.sharedData -- second val currently nil
end
function WHpersistence.loadData()
if not persistence then return end
local shared = nil
local theData = persistence.getSavedDataForModule("WHpersistence")
if (not theData) or not (theData.theWH) then
if WHpersistence.verbose then
trigger.action.outText("+++WHp: no save date received, skipping.", 30)
end
return
end
-- set up all warehouses from data loaded
for name, inv in pairs(theData.theWH) do
trigger.action.outText("+++restoring <" .. name .. ">", 30)
local theBase = Airbase.getByName(name)
if theBase then
local theWH = theBase:getWarehouse()
if theWH then
-- we go through weapon, liquids and aircraft
for idx, liq in pairs(inv.liquids) do
theWH:setLiquidAmount(idx, liq)
trigger.action.outText(name .. ": Liq <" .. idx .. "> : <" .. liq .. ">", 30)
end
for ref, num in pairs(inv.weapon) do
theWH:setItem(ref, num)
end
for ref, num in pairs(inv.aircraft) do
theWH:setItem(ref, num)
end
else
trigger.action.outText(name .. ": no warehouse")
end
else
trigger.action.outText(name .. ": no airbase")
end
end
end
--
-- config
--
function WHpersistence.readConfigZone()
local theZone = cfxZones.getZoneByName("WHpersistenceConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("WHpersistenceConfig")
end
WHpersistence.verbose = theZone.verbose
end
--
-- GO
--
function WHpersistence.start()
if not dcsCommon.libCheck then
trigger.action.outText("cfx WHpersistence requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx Raise Flag", WHpersistence.requiredLibs)then return false end
WHpersistence.readConfigZone()
if persistence then
callbacks = {}
callbacks.persistData = WHpersistence.saveData
persistence.registerModule("WHpersistence", callbacks)
-- now load my data
WHpersistence.loadData()
end
trigger.action.outText("cfx WHpersistence v" .. WHpersistence.version .. " started.", 30)
return true
end
if not WHpersistence.start() then
trigger.action.outText("cfx WHpersistence aborted: missing libraries", 30)
WHpersistence = nil
end

View File

@ -1,5 +1,5 @@
airfield = {} airfield = {}
airfield.version = "2.2.0" airfield.version = "2.3.0"
airfield.requiredLibs = { airfield.requiredLibs = {
"dcsCommon", "dcsCommon",
"cfxZones", "cfxZones",
@ -22,6 +22,12 @@ airfield.allAirfields = {} -- inexed by af name, db entries: base, cat
2.1.0 - added support for makeNeutral? 2.1.0 - added support for makeNeutral?
2.1.1 - bug fixing for DCS 2.9x airfield retrofit 2.1.1 - bug fixing for DCS 2.9x airfield retrofit
2.2.0 - dmlZone:getCoalition() / masterowner adaptation for owner 2.2.0 - dmlZone:getCoalition() / masterowner adaptation for owner
2.3.0 - increased verbosity on persistence
- airfield delayed release after data load
- locked owner on start
- release after start
- redrawing airfields when releasing
- cleanup
--]]-- --]]--
-- init all airfields DB -- init all airfields DB
@ -31,7 +37,6 @@ function airfield.collectAll()
local dropped = 0 local dropped = 0
for idx, aBase in pairs(allBases) do for idx, aBase in pairs(allBases) do
local entry = {} local entry = {}
--local cat = Airbase.getCategory(aBase) -- DCS 2.9 hardened
-- ho! dcs 2.9.x retrofit screwed with Airfield.getCategory. -- ho! dcs 2.9.x retrofit screwed with Airfield.getCategory.
local cat = aBase:getDesc().category local cat = aBase:getDesc().category
-- cats: 0 = airfield, 1 = farp, 2 = ship -- cats: 0 = airfield, 1 = farp, 2 = ship
@ -44,20 +49,18 @@ function airfield.collectAll()
count = count + 1 count = count + 1
else else
dropped = dropped + 1 dropped = dropped + 1
-- trigger.action.outText("***dropped airbase <" .. aBase:getName() .. ">, cat = <" .. cat .. ">", 30)
end end
end end
if airfield.verbose then if airfield.verbose then
trigger.action.outText("+++airF: init - count = <" .. count .. ">, dropped = <" .. dropped .. ">", 30) trigger.action.outText("+++airF: init - count = <" .. count .. ">, dropped = <" .. dropped .. ">", 30)
end end
end end
-- --
-- setting up airfield -- setting up airfield
-- --
airfield.collector = {}
function airfield.createAirFieldFromZone(theZone) function airfield.createAirFieldFromZone(theZone)
theZone.farps = theZone:getBoolFromZoneProperty("farps", false) theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
local filterCat = 0 local filterCat = 0
if (theZone.farps) then filterCat = {0, 1} end -- bases and farps if (theZone.farps) then filterCat = {0, 1} end -- bases and farps
local p = theZone:getPoint() local p = theZone:getPoint()
@ -65,17 +68,15 @@ function airfield.createAirFieldFromZone(theZone)
theZone.airfield = theBase theZone.airfield = theBase
theZone.afName = theBase:getName() theZone.afName = theBase:getName()
-- set zone's owner -- set zone's owner
theZone.owner = theBase:getCoalition() theZone.owner = theBase:getCoalition()
theZone.mismatchCount = airfield.gracePeriod theZone.mismatchCount = airfield.gracePeriod
if theZone.verbose or airfield.verbose then if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30) trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30)
end end
-- methods
theZone.method = theZone:getStringFromZoneProperty("method", "inc") theZone.method = theZone:getStringFromZoneProperty("method", "inc")
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
if theZone:hasProperty("red!") then if theZone:hasProperty("red!") then
theZone.redCap = theZone:getStringFromZoneProperty("red!") theZone.redCap = theZone:getStringFromZoneProperty("red!")
end end
@ -92,23 +93,20 @@ function airfield.createAirFieldFromZone(theZone)
theZone.makeBlue = theZone:getStringFromZoneProperty("makeBlue?", "<none>") theZone.makeBlue = theZone:getStringFromZoneProperty("makeBlue?", "<none>")
theZone.lastMakeBlue = trigger.misc.getUserFlag(theZone.makeBlue) theZone.lastMakeBlue = trigger.misc.getUserFlag(theZone.makeBlue)
end end
if theZone:hasProperty("makeNeutral?") then if theZone:hasProperty("makeNeutral?") then
theZone.makeNeutral = theZone:getStringFromZoneProperty("makeNeutral?", "<none>") theZone.makeNeutral = theZone:getStringFromZoneProperty("makeNeutral?", "<none>")
theZone.lastMakeNeutral = trigger.misc.getUserFlag(theZone.makeNeutral) theZone.lastMakeNeutral = trigger.misc.getUserFlag(theZone.makeNeutral)
end end
if theZone:hasProperty("autoCap?") then if theZone:hasProperty("autoCap?") then
theZone.autoCap = theZone:getStringFromZoneProperty("autoCap?", "<none>") theZone.autoCap = theZone:getStringFromZoneProperty("autoCap?", "<none>")
theZone.lastAutoCap = trigger.misc.getUserFlag(theZone.autoCap) theZone.lastAutoCap = trigger.misc.getUserFlag(theZone.autoCap)
end end
theZone.directControl = theZone:getBoolFromZoneProperty("directControl", false) theZone.directControl = theZone:getBoolFromZoneProperty("directControl", false)
if theZone.directControl then if theZone.directControl then
airfield.assumeControl(theZone) airfield.assumeControl(theZone)
end end
-- if fixed attribute, we switch to that color and keep it fixed. -- if fixed attribute, we switch to that color and keep it fixed.
-- can be overridden by either makeXX or autoCap. -- can be overridden by either makeXX or autoCap.
if theZone:hasProperty("fixed") then if theZone:hasProperty("fixed") then
@ -144,6 +142,14 @@ function airfield.createAirFieldFromZone(theZone)
airfield.showAirfield(theZone) airfield.showAirfield(theZone)
theBase:autoCapture(false) -- lock down owner to avoid contested at beginning
-- set up collector to free ownership later
if theZone:hasProperty("fixed") then
airfield.collector[theBase] = false -- autocap off after delay
else
airfield.collector[theBase] = true
end
-- now mark this zone as handled -- now mark this zone as handled
local entry = airfield.allAirfields[theZone.afName] local entry = airfield.allAirfields[theZone.afName]
if not entry then if not entry then
@ -179,7 +185,6 @@ function airfield.showAirfield(theZone)
theZone.ownerMark = nil theZone.ownerMark = nil
end end
if not theZone.show then return end -- we don't show in map if not theZone.show then return end -- we don't show in map
local lineColor = theZone.redLine -- {1.0, 0, 0, 1.0} -- red local lineColor = theZone.redLine -- {1.0, 0, 0, 1.0} -- red
local fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red local fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red
local owner = theZone:getCoalition() -- .owner local owner = theZone:getCoalition() -- .owner
@ -190,10 +195,7 @@ function airfield.showAirfield(theZone)
lineColor = theZone.neutralLine -- {0.8, 0.8, 0.8, 1.0} lineColor = theZone.neutralLine -- {0.8, 0.8, 0.8, 1.0}
fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2} fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
end end
theZone.ownerMark = airfield.markAirfieldOnMap(theZone.airfield, lineColor, fillColor) theZone.ownerMark = airfield.markAirfieldOnMap(theZone.airfield, lineColor, fillColor)
end end
function airfield.assumeControl(theZone) function airfield.assumeControl(theZone)
@ -211,7 +213,6 @@ function airfield.relinquishControl(theZone)
end end
theBase:autoCapture(true) -- turn off autocap theBase:autoCapture(true) -- turn off autocap
end end
-- --
-- event handling -- event handling
-- --
@ -252,7 +253,6 @@ function airfield.airfieldCaptured(theBase)
if theZone.verbose or airfield.verbose then if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: capturing <" .. bName .. "> for zone <" .. theZone.name .. ">", 30) trigger.action.outText("+++airF: capturing <" .. bName .. "> for zone <" .. theZone.name .. ">", 30)
end end
local newCoa = theBase:getCoalition() local newCoa = theBase:getCoalition()
theZone.owner = newCoa theZone.owner = newCoa
@ -272,7 +272,6 @@ function airfield.airfieldCaptured(theBase)
if theZone.blueCap and newCoa == 2 then if theZone.blueCap and newCoa == 2 then
theZone:pollFlag(theZone.blueCap, theZone.method) theZone:pollFlag(theZone.blueCap, theZone.method)
end end
end end
function airfield:onEvent(event) function airfield:onEvent(event)
@ -347,7 +346,6 @@ function airfield.update()
end end
theZone.owner = 0 theZone.owner = 0
end end
if theZone.autoCap and theZone:testZoneFlag(theZone.autoCap, theZone.triggerMethod, "lastAutoCap") then if theZone.autoCap and theZone:testZoneFlag(theZone.autoCap, theZone.triggerMethod, "lastAutoCap") then
if theAirfield:autoCaptureIsOn() then if theAirfield:autoCaptureIsOn() then
@ -386,16 +384,12 @@ function airfield.GC()
trigger.action.outText("+++airF: corrected ownership after grace period", 30) trigger.action.outText("+++airF: corrected ownership after grace period", 30)
end end
end end
end end
end end
end end
-- --
-- LOAD / SAVE -- LOAD / SAVE
-- --
function airfield.saveData() function airfield.saveData()
local theData = {} local theData = {}
local allAF = {} local allAF = {}
@ -411,6 +405,21 @@ function airfield.saveData()
return theData return theData
end end
function airfield.releaseFields(releaseMe)
for theAF, rel in pairs(releaseMe) do
theAF:autoCapture(rel)
if airfield.verbose then
trigger.action.outText("+++airF: releasing AF <" .. theAF:getName() .. "> to saved cap state <" .. dcsCommon.bool2Text(rel) .. ">", 30)
end
end
for name, theZone in pairs(airfield.myAirfields) do
airfield.showAirfield(theZone)
if airfield.verbose or theZone.verbose then
trigger.action.outText("+++airF: redrawing <" .. theZone.name .. ">", 30)
end
end
end
function airfield.loadData() function airfield.loadData()
if not persistence then return end if not persistence then return end
local theData = persistence.getSavedDataForModule("airfield") local theData = persistence.getSavedDataForModule("airfield")
@ -418,6 +427,7 @@ function airfield.loadData()
if airfield.verbose then if airfield.verbose then
trigger.action.outText("+++airF persistence: no save data received, skipping.", 30) trigger.action.outText("+++airF persistence: no save data received, skipping.", 30)
end end
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
return return
end end
@ -426,9 +436,12 @@ function airfield.loadData()
if airfield.verbose then if airfield.verbose then
trigger.action.outText("+++airF persistence: no airfield data, skipping", 30) trigger.action.outText("+++airF persistence: no airfield data, skipping", 30)
end end
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
return return
end end
airfield.collector = {} -- overwrite existing
for theName, AFData in pairs(allAF) do for theName, AFData in pairs(allAF) do
local theZone = airfield.myAirfields[theName] local theZone = airfield.myAirfields[theName]
if theZone then if theZone then
@ -438,20 +451,22 @@ function airfield.loadData()
theAirfield:autoCapture(false) theAirfield:autoCapture(false)
theAirfield:setCoalition(AFData.owner) theAirfield:setCoalition(AFData.owner)
theZone.owner = AFData.owner theZone.owner = AFData.owner
if airfield.verbose or theZone.verbose then
trigger.action.outText("+++airF: setting AF Zone <" .. theZone.name .. ">, owner from file to <" .. theZone.owner .. ">", 30)
end
-- set ownedBy# -- set ownedBy#
if theZone.ownedBy then if theZone.ownedBy then
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner) trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
end end
-- set owning mode: autocap or direct -- set owning mode: autocap or direct
theAirfield:autoCapture(AFData.autocapActive) --theAirfield:autoCapture(AFData.autocapActive)
airfield.collector[theAirfield] = AFData.autocapActive
else else
trigger.action.outText("+++airF persistence: cannot synch airfield <" .. theName .. ">, skipping", 40) trigger.action.outText("+++airF persistence: cannot synch airfield <" .. theName .. ">, skipping", 40)
end end
end end
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
end end
-- --
-- start up -- start up
-- --
@ -460,9 +475,7 @@ function airfield.readConfig()
if not theZone then if not theZone then
theZone = cfxZones.createSimpleZone("airfieldConfig") theZone = cfxZones.createSimpleZone("airfieldConfig")
end end
airfield.verbose = theZone.verbose airfield.verbose = theZone.verbose
-- airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
-- colors for line and fill -- colors for line and fill
airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0}) airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
airfield.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", {1.0, 0, 0, 0.2}) airfield.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", {1.0, 0, 0, 0.2})
@ -470,7 +483,6 @@ function airfield.readConfig()
airfield.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2}) airfield.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2})
airfield.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0}) airfield.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0})
airfield.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2}) airfield.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2})
airfield.showAll = theZone:getBoolFromZoneProperty("show", false) airfield.showAll = theZone:getBoolFromZoneProperty("show", false)
end end
@ -485,13 +497,10 @@ end
function airfield.start() function airfield.start()
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs) if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
then return false end then return false end
-- set up DB -- set up DB
airfield.collectAll() airfield.collectAll()
-- read config -- read config
airfield.readConfig() airfield.readConfig()
-- read bases -- read bases
local abZones = cfxZones.zonesWithProperty("airfield") local abZones = cfxZones.zonesWithProperty("airfield")
for idx, aZone in pairs(abZones) do for idx, aZone in pairs(abZones) do
@ -512,6 +521,8 @@ function airfield.start()
persistence.registerModule("airfield", callbacks) persistence.registerModule("airfield", callbacks)
-- now load my data -- now load my data
airfield.loadData() airfield.loadData()
else
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2) -- release airfields when not loaded from storage
end end
-- start update in 1 second -- start update in 1 second

View File

@ -1,5 +1,5 @@
autoCSAR = {} autoCSAR = {}
autoCSAR.version = "2.2.0" autoCSAR.version = "2.2.1"
autoCSAR.requiredLibs = { autoCSAR.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
"cfxZones", -- Zones, of course "cfxZones", -- Zones, of course
@ -19,7 +19,8 @@ autoCSAR.trackedEjects = {} -- we start tracking on eject
2.1.0 - persistence support 2.1.0 - persistence support
2.2.0 - new noExploit option in config 2.2.0 - new noExploit option in config
- no csar mission if pilot lands too close to airbase or farp - no csar mission if pilot lands too close to airbase or farp
and noExploit is on and noExploit is on
2.2.1 - DCS hardening for isExist
--]]-- --]]--
autoCSAR.forbidden = {} -- indexed by name, contains point autoCSAR.forbidden = {} -- indexed by name, contains point
autoCSAR.killDist = 2100 -- meters from center of forbidden autoCSAR.killDist = 2100 -- meters from center of forbidden
@ -173,7 +174,9 @@ function autoCSAR:onEvent(event)
local coa = event.initiator:getCoalition() local coa = event.initiator:getCoalition()
-- see if pilot has ejector seat and prepare to connect one with the other -- see if pilot has ejector seat and prepare to connect one with the other
local info = nil local info = nil
if event.target and event.target:isExist() then if event.target
and event.target.isExist
and event.target:isExist() then -- DCS hardening
info = {} info = {}
info.coa = coa info.coa = coa
info.seat = event.target info.seat = event.target

View File

@ -1,5 +1,5 @@
cloneZones = {} cloneZones = {}
cloneZones.version = "2.4.0" cloneZones.version = "2.5.0"
cloneZones.verbose = false cloneZones.verbose = false
cloneZones.requiredLibs = { cloneZones.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
@ -57,6 +57,8 @@ cloneZones.respawnOnGroupID = true
path) with wiper module path) with wiper module
- using "wipe?" will now create a warning - using "wipe?" will now create a warning
2.4.0 - reworked masterOwner to fit with dmlZone 2.4.0 - reworked masterOwner to fit with dmlZone
2.5.0 - re-establish spawn zone in persistence to provide
empty! detection through saves (missed hasClones)
--]]-- --]]--
-- --
@ -90,7 +92,6 @@ function cloneZones.invokeCallbacks(theZone, reason, args)
if not theZone then return end if not theZone then return end
if not reason then reason = "<none>" end if not reason then reason = "<none>" end
if not args then args = {} end if not args then args = {} end
-- invoke anyone who wants to know that a group -- invoke anyone who wants to know that a group
-- of people was rescued. -- of people was rescued.
for idx, cb in pairs(cloneZones.callbacks) do for idx, cb in pairs(cloneZones.callbacks) do
@ -105,7 +106,6 @@ function cloneZones.partOfGroupDataInZone(theZone, theUnits)
local zP = cfxZones.getPoint(theZone) local zP = cfxZones.getPoint(theZone)
zP = theZone:getDCSOrigin() -- don't use getPoint now. zP = theZone:getDCSOrigin() -- don't use getPoint now.
zP.y = 0 zP.y = 0
for idx, aUnit in pairs(theUnits) do for idx, aUnit in pairs(theUnits) do
local uP = {} local uP = {}
uP.x = aUnit.x uP.x = aUnit.x
@ -197,7 +197,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
table.insert(theZone.myStatics, aStatic) table.insert(theZone.myStatics, aStatic)
end end
end end
cloneZones.despawnAll(theZone) cloneZones.despawnAll(theZone)
if (#theZone.cloneNames + #theZone.staticNames) < 1 then if (#theZone.cloneNames + #theZone.staticNames) < 1 then
if cloneZones.verbose then if cloneZones.verbose then
@ -210,18 +209,12 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
trigger.action.outText(theZone.name .. " clone template saved", 30) trigger.action.outText(theZone.name .. " clone template saved", 30)
end end
end end
-- declutter
theZone.declutter = theZone:getBoolFromZoneProperty("declutter", false) theZone.declutter = theZone:getBoolFromZoneProperty("declutter", false)
-- watchflags
theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
if theZone:hasProperty("cloneTriggerMethod") then if theZone:hasProperty("cloneTriggerMethod") then
theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("cloneTriggerMethod", "change") theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("cloneTriggerMethod", "change")
end end
-- f? and spawn? and other synonyms map to the same
if theZone:hasProperty("f?") then if theZone:hasProperty("f?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("f?", "none") theZone.spawnFlag = theZone:getStringFromZoneProperty("f?", "none")
elseif theZone:hasProperty("in?") then elseif theZone:hasProperty("in?") then
@ -231,12 +224,10 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
elseif theZone:hasProperty("clone?") then elseif theZone:hasProperty("clone?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("clone?", "none") theZone.spawnFlag = theZone:getStringFromZoneProperty("clone?", "none")
end end
if theZone.spawnFlag then if theZone.spawnFlag then
theZone.lastSpawnValue = theZone:getFlagValue(theZone.spawnFlag) theZone.lastSpawnValue = theZone:getFlagValue(theZone.spawnFlag)
end end
-- deSpawn?
if theZone:hasProperty("deSpawn?") then if theZone:hasProperty("deSpawn?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deSpawn?", "none") theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deSpawn?", "none")
elseif theZone:hasProperty("deClone?") then elseif theZone:hasProperty("deClone?") then
@ -259,7 +250,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false) theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false) theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false) theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false)
if theZone:hasProperty("empty!") then if theZone:hasProperty("empty!") then
theZone.emptyBangFlag = theZone:getStringFromZoneProperty("empty!", "<None>") -- note string on number default theZone.emptyBangFlag = theZone:getStringFromZoneProperty("empty!", "<None>") -- note string on number default
end end
@ -267,30 +258,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.cloneMethod = theZone:getStringFromZoneProperty("cloneMethod", "inc") theZone.cloneMethod = theZone:getStringFromZoneProperty("cloneMethod", "inc")
if theZone:hasProperty("method") then if theZone:hasProperty("method") then
theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default
end end
--[[--
if theZone:hasProperty("masterOwner") then
theZone.masterOwner = theZone:getStringFromZoneProperty( "masterOwner", "*")
theZone.masterOwner = dcsCommon.trim(theZone.masterOwner)
if theZone.masterOwner == "*" then
theZone.masterOwner = theZone.name
if theZone.verbose then
trigger.action.outText("+++clnZ: masterOwner for <" .. theZone.name .. "> set successfully to to itself, currently owned by faction <" .. theZone.owner .. ">", 30)
end
end
if theZone.verbose or cloneZones.verbose then
trigger.action.outText("+++clnZ: ownership of <" .. theZone.name .. "> tied to zone <" .. theZone.masterOwner .. ">", 30)
end
-- check that the zone exists in DCS
local theMaster = cfxZones.getZoneByName(theZone.masterOwner)
if not theMaster then
trigger.action.outText("clnZ: WARNING: cloner's <" .. theZone.name .. "> master owner named <" .. theZone.masterOwner .. "> does not exist!", 30)
end
theZone.masterOwner = theMaster
end
--]]--
theZone.turn = theZone:getNumberFromZoneProperty("turn", 0) theZone.turn = theZone:getNumberFromZoneProperty("turn", 0)
-- interface to groupTracker -- interface to groupTracker
@ -329,7 +297,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.inBuiltup = theZone:getNumberFromZoneProperty("inBuiltup", 10) -- 10 meter radius must be free -- small houses theZone.inBuiltup = theZone:getNumberFromZoneProperty("inBuiltup", 10) -- 10 meter radius must be free -- small houses
end end
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false) theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false) theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false) theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false)
@ -338,15 +305,12 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.identical = theZone:getBoolFromZoneProperty("identical", false) theZone.identical = theZone:getBoolFromZoneProperty("identical", false)
if theZone.identical == false then theZone.identical = nil end if theZone.identical == false then theZone.identical = nil end
end end
if theZone:hasProperty("nameScheme") then if theZone:hasProperty("nameScheme") then
theZone.nameScheme = theZone:getStringFromZoneProperty( "nameScheme", "<o>-<uid>") -- default to [<original name> "-" <uuid>] theZone.nameScheme = theZone:getStringFromZoneProperty( "nameScheme", "<o>-<uid>") -- default to [<original name> "-" <uuid>]
end end
if theZone:hasProperty("groupScheme") then if theZone:hasProperty("groupScheme") then
theZone.groupScheme = theZone:getStringFromZoneProperty("groupScheme", "<o>-<uid>") theZone.groupScheme = theZone:getStringFromZoneProperty("groupScheme", "<o>-<uid>")
end end
if theZone.identical and theZone.nameScheme then if theZone.identical and theZone.nameScheme then
trigger.action.outText("+++clnZ: WARNING - clone zone <" .. theZone.name .. "> has both IDENTICAL and NAMESCHEME/GROUPSCHEME attributes. nameScheme is ignored.", 30) trigger.action.outText("+++clnZ: WARNING - clone zone <" .. theZone.name .. "> has both IDENTICAL and NAMESCHEME/GROUPSCHEME attributes. nameScheme is ignored.", 30)
theZone.nameScheme = nil theZone.nameScheme = nil
@ -539,7 +503,6 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp
-- now process departing slot if given -- now process departing slot if given
if departFromAerodrome then if departFromAerodrome then
-- we may need alt from land to add here, maybe later -- we may need alt from land to add here, maybe later
-- now process parking slots, and choose closest slot -- now process parking slots, and choose closest slot
-- per unit's location -- per unit's location
if fromParking then if fromParking then
@ -658,11 +621,9 @@ function cloneZones.uniqueNameStaticData(theData, theCloneZone, sourcename)
if theCloneZone and theCloneZone.nameScheme then if theCloneZone and theCloneZone.nameScheme then
local schema = theCloneZone.nameScheme local schema = theCloneZone.nameScheme
newName, iterCount = cloneZones.nameFromSchema(schema, theData.name, theCloneZone, sourceName, iterCount) newName, iterCount = cloneZones.nameFromSchema(schema, theData.name, theCloneZone, sourceName, iterCount)
if theCloneZone.verbose then if theCloneZone.verbose then
trigger.action.outText("clnZ: zone <" .. theCloneZone.name .. "> static schema <" .. schema .. ">: <" .. theData.name .. "> --> <" .. newName .. ">", 30) trigger.action.outText("clnZ: zone <" .. theCloneZone.name .. "> static schema <" .. schema .. ">: <" .. theData.name .. "> --> <" .. newName .. ">", 30)
end end
theData.name = newName -- dcsCommon.uuid(theData.name) theData.name = newName -- dcsCommon.uuid(theData.name)
else else
-- default naming scheme: <name>-<uuid> -- default naming scheme: <name>-<uuid>
@ -697,19 +658,6 @@ end
function cloneZones.resolveOwnership(spawnZone, ctry) function cloneZones.resolveOwnership(spawnZone, ctry)
if not spawnZone.masterOwner then return ctry end -- old code if not spawnZone.masterOwner then return ctry end -- old code
--[[--
local masterZone = cfxZones.getZoneByName(spawnZone.masterOwner)
if not masterZone then
trigger.action.outText("+++clnZ: cloner " .. spawnZone.name .. " could not find master owner <" .. spawnZone.masterOwner .. ">", 30)
return ctry
end
if not masterZone.owner then
return ctry
end
--]]--
-- ctry = dcsCommon.getACountryForCoalition(masterZone.owner)
ctry = dcsCommon.getACountryForCoalition(spawnZone:getCoalition()) ctry = dcsCommon.getACountryForCoalition(spawnZone:getCoalition())
return ctry return ctry
end end
@ -1249,6 +1197,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
-- clone for persistence -- clone for persistence
local theData = dcsCommon.clone(rawData) local theData = dcsCommon.clone(rawData)
-- remember the zone that spawned this particular group
theData.CZspawner = spawnZone.name
cloneZones.allClones[rawData.name] = theData cloneZones.allClones[rawData.name] = theData
if cloneZones.verbose or spawnZone.verbose then if cloneZones.verbose or spawnZone.verbose then
@ -1273,7 +1223,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
info.timeLimit = now + timeLimit info.timeLimit = now + timeLimit
info.cloneZone = spawnZone info.cloneZone = spawnZone
table.insert(cloneZones.despawnPlan, info) table.insert(cloneZones.despawnPlan, info)
-- trigger.action.outText("+++clne: scheduled auto-despawn for <" .. info.name .. "> in <" .. timeLimit .. "> secs", 30)
end end
-- turn off AI if disabled -- turn off AI if disabled
@ -1446,6 +1395,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
rawData.cty = ctry rawData.cty = ctry
-- save for persistence -- save for persistence
local theData = dcsCommon.clone(rawData) local theData = dcsCommon.clone(rawData)
theData.CZspawner = spawnZone.name -- remember spawner
cloneZones.allCObjects[rawData.name] = theData cloneZones.allCObjects[rawData.name] = theData
if cloneZones.verbose or spawnZone.verbose then if cloneZones.verbose or spawnZone.verbose then
@ -1466,7 +1416,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
info.timeLimit = now + timeLimit info.timeLimit = now + timeLimit
info.cloneZone = spawnZone info.cloneZone = spawnZone
table.insert(cloneZones.despawnPlan, info) table.insert(cloneZones.despawnPlan, info)
-- trigger.action.outText("+++clne: scheduled auto-despawn for OBJECT <" .. info.name .. "> in <" .. timeLimit .. "> secs", 30)
end end
-- we don't mix groups with units, so no lookup tables for -- we don't mix groups with units, so no lookup tables for
-- statics -- statics
@ -1528,9 +1477,7 @@ end
function cloneZones.spawnWithSpawner(theZone) function cloneZones.spawnWithSpawner(theZone)
-- analog to cfxSpawnZones.spawnWithSpawner(theSpawner) -- analog to cfxSpawnZones.spawnWithSpawner(theSpawner)
-- glue code for helo troops and other modules -- glue code for helo troops and other modules
-- we may want to check if cloner isn't emtpy first -- we may want to check if cloner isn't emtpy first
cloneZones.spawnWithCloner(theZone) cloneZones.spawnWithCloner(theZone)
end end
@ -1702,15 +1649,6 @@ end
function cloneZones.resolveOwningCoalition(theZone) function cloneZones.resolveOwningCoalition(theZone)
return theZone:getCoalition() return theZone:getCoalition()
--[[--
if not theZone.masterOwner then return theZone.owner end
local masterZone = cfxZones.getZoneByName(theZone.masterOwner)
if not masterZone then
trigger.action.outText("+++clnZ: cloner " .. theZone.name .. " could not find master owner <" .. theZone.masterOwner .. ">", 30)
return theZone.owner
end
return masterZone.owner
--]]--
end end
function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide) function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
@ -1821,7 +1759,6 @@ function cloneZones.update()
local filtered = {} local filtered = {}
for idx, theInfo in pairs(cloneZones.despawnPlan) do for idx, theInfo in pairs(cloneZones.despawnPlan) do
if theInfo.timeLimit < now then if theInfo.timeLimit < now then
-- trigger.action.outText("+++clne: auto-despawning <" .. theInfo.name .. ">", 30)
if theInfo.isObject then if theInfo.isObject then
-- dealloc static object -- dealloc static object
local theObject = theInfo.theGroup local theObject = theInfo.theGroup
@ -1857,7 +1794,6 @@ function cloneZones.doOnStart()
end end
end end
end end
-- --
-- Regular GC and housekeeping -- Regular GC and housekeeping
-- --
@ -1884,7 +1820,6 @@ function cloneZones.GC()
end end
end end
cloneZones.allClones = filteredAttackers cloneZones.allClones = filteredAttackers
filteredAttackers = {} filteredAttackers = {}
for gName, gData in pairs (cloneZones.allCObjects) do for gName, gData in pairs (cloneZones.allCObjects) do
-- all we need to do is get the group of that name -- all we need to do is get the group of that name
@ -1904,8 +1839,6 @@ function cloneZones.houseKeeping()
timer.scheduleFunction(cloneZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes timer.scheduleFunction(cloneZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes
cloneZones.GC() cloneZones.GC()
end end
-- --
-- LOAD / SAVE -- LOAD / SAVE
-- --
@ -1969,7 +1902,7 @@ function cloneZones.saveData()
cData.myUniqueCounter = theCloner.myUniqueCounter cData.myUniqueCounter = theCloner.myUniqueCounter
cData.oSize = theCloner.oSize cData.oSize = theCloner.oSize
cData.lastSize = theCloner.lastSize cData.lastSize = theCloner.lastSize
-- mySpawns: all groups i'm curently observing for empty! -- mySpawns: all groups I'm curently observing for empty!
-- myStatics: dto for objects -- myStatics: dto for objects
local mySpawns = {} local mySpawns = {}
for idx, aGroup in pairs(theCloner.mySpawns) do for idx, aGroup in pairs(theCloner.mySpawns) do
@ -2071,6 +2004,7 @@ function cloneZones.loadData()
local theGroup = Group.getByName(aName) local theGroup = Group.getByName(aName)
if theGroup then if theGroup then
table.insert(mySpawns, theGroup) table.insert(mySpawns, theGroup)
theCloner.hasClones = true -- was missing!
else else
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with clone group <".. aName .. ">", 30) trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with clone group <".. aName .. ">", 30)
end end
@ -2082,6 +2016,7 @@ function cloneZones.loadData()
local theStatic = StaticObject.getByName(aName) local theStatic = StaticObject.getByName(aName)
if theStatic then if theStatic then
table.insert(myStatics, theStatic) table.insert(myStatics, theStatic)
theCloner.hasClones = true -- was missing!S
else else
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with static <".. aName .. ">", 30) trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with static <".. aName .. ">", 30)
end end
@ -2115,24 +2050,16 @@ function cloneZones.readConfigZone()
end end
theZone = cfxZones.createSimpleZone("cloneZonesConfig") theZone = cfxZones.createSimpleZone("cloneZonesConfig")
end end
if theZone:hasProperty("uniqueCount") then if theZone:hasProperty("uniqueCount") then
cloneZones.uniqueCounter = theZone:getNumberFromZoneProperty("uniqueCount", cloneZone.uniqueCounter) cloneZones.uniqueCounter = theZone:getNumberFromZoneProperty("uniqueCount", cloneZone.uniqueCounter)
end end
if theZone:hasProperty("localCount") then if theZone:hasProperty("localCount") then
cloneZones.lclUniqueCounter = theZone:getNumberFromZoneProperty("localCount", cloneZone.lclUniqueCounter) cloneZones.lclUniqueCounter = theZone:getNumberFromZoneProperty("localCount", cloneZone.lclUniqueCounter)
end end
if theZone:hasProperty("globalCount") then if theZone:hasProperty("globalCount") then
cloneZones.globalCounter = theZone:getNumberFromZoneProperty("globalCount", cloneZone.globalCounter) cloneZones.globalCounter = theZone:getNumberFromZoneProperty("globalCount", cloneZone.globalCounter)
end end
cloneZones.verbose = theZone.verbose
cloneZones.verbose = theZone:getBoolFromZoneProperty("verbose", false)
if cloneZones.verbose then
trigger.action.outText("+++clnZ: read config", 30)
end
end end
function cloneZones.start() function cloneZones.start()
@ -2201,4 +2128,5 @@ end
make example where transport can be different plane types but have same name make example where transport can be different plane types but have same name
support 'orders' to complete replace routes, and pass to groundCommander like spawner. only for ground troops support 'orders' to complete replace routes, and pass to groundCommander like spawner. only for ground troops
maxCycles - maximum number of clone cycles. emulate with countdown?
--]]-- --]]--

View File

@ -1,5 +1,5 @@
cfxHeloTroops = {} cfxHeloTroops = {}
cfxHeloTroops.version = "3.1.3" cfxHeloTroops.version = "3.1.5"
cfxHeloTroops.verbose = false cfxHeloTroops.verbose = false
cfxHeloTroops.autoDrop = true cfxHeloTroops.autoDrop = true
cfxHeloTroops.autoPickup = false cfxHeloTroops.autoPickup = false
@ -23,7 +23,8 @@ cfxHeloTroops.requestRange = 500 -- meters
3.1.3 - decycled structures (destination zone) on save 3.1.3 - decycled structures (destination zone) on save
- upcycled structures (destination) on load - upcycled structures (destination) on load
- loadSound and disembarkSound - loadSound and disembarkSound
3.1.4 - guarding destination access in save
3.1.5 - more guarding of destination access
--]]-- --]]--
@ -926,9 +927,14 @@ function cfxHeloTroops.saveData()
for gName, gData in pairs(cfxHeloTroops.deployedTroops) do for gName, gData in pairs(cfxHeloTroops.deployedTroops) do
local sData = dcsCommon.clone(gData) local sData = dcsCommon.clone(gData)
dcsCommon.synchGroupData(sData.groupData) dcsCommon.synchGroupData(sData.groupData)
if sData.destination then if sData.destination then
net.log("cfxHeloTroops: decycling troop 'destination' for <" .. sData.destination:getName() .. ">") if type(sData.destination) == "table" and (sData.destination.name) then
sData.destination = sData.destination:getName() net.log("cfxHeloTroops: decycling troop 'destination' for <" .. sData.destination.name .. ">")
sData.destination = sData.destination.name
else
sData.destination = nil
net.log("cfxHeloTroops: decycling deployed troops 'destination' nilling for safety")
end
end end
allTroopData[gName] = sData allTroopData[gName] = sData
end end

View File

@ -1,6 +1,6 @@
impostors={} impostors={}
impostors.version = "1.1.0" impostors.version = "1.2.0"
impostors.verbose = false impostors.verbose = false
impostors.ups = 1 impostors.ups = 1
impostors.requiredLibs = { impostors.requiredLibs = {
@ -21,7 +21,7 @@ impostors.uniqueCounter = 8200000 -- clones start at 9200000
1.1.0 - filtered dead units during spawns 1.1.0 - filtered dead units during spawns
cleanup cleanup
some performance boost for mx lookup some performance boost for mx lookup
1.2.0 - filters dead groups entirely
LIMITATIONS: LIMITATIONS:
must be on ground (or would be very silly must be on ground (or would be very silly
does not work with any units deployed on ships does not work with any units deployed on ships
@ -296,7 +296,7 @@ function impostors.spawnGroupsFromImpostor(theZone)
local deadUnits = {} -- collect all dead units for immediate delete local deadUnits = {} -- collect all dead units for immediate delete
-- after spawning -- after spawning
local filtered = {}
for idx, groupName in pairs(theZone.groupNames) do for idx, groupName in pairs(theZone.groupNames) do
-- get my group data from MX based on my name -- get my group data from MX based on my name
-- we get from MX so we get all path and order info -- we get from MX so we get all path and order info
@ -306,67 +306,75 @@ function impostors.spawnGroupsFromImpostor(theZone)
local cat = cfxMX.groupCatByName[groupName] local cat = cfxMX.groupCatByName[groupName]
local ctry = cfxMX.countryByName[groupName] local ctry = cfxMX.countryByName[groupName]
local impostorGroup = theZone.myImpostors[groupName] local impostorGroup = theZone.myImpostors[groupName]
local relinkZones = {} if impostorGroup then
-- now iterate all units in that group, and remove their impostors table.insert(filtered, groupName)
for idy, theUnit in pairs(rawData.units) do local relinkZones = {}
if theUnit and theUnit.name then -- now iterate all units in that group, and remove their impostors
local impName = impostorGroup[theUnit.name] for idy, theUnit in pairs(rawData.units) do
if not impName then if theUnit and theUnit.name then
if theZone.verbose then local impName = impostorGroup[theUnit.name]
trigger.action.outText("group <" .. groupName .. ">: no impostor for <" .. theUnit.name .. ">", 30) if not impName then
end if theZone.verbose then
else trigger.action.outText("group <" .. groupName .. ">: no impostor for <" .. theUnit.name .. ">", 30)
local impStat = StaticObject.getByName(impName) end
if impStat and impStat:isExist() and impStat:getLife() > 1 then
-- still alive. read x, y and heading
local sp = impStat:getPoint()
theUnit.x = sp.x
theUnit.y = sp.z -- !!!
theUnit.heading = dcsCommon.getUnitHeading(impStat) -- should also work for statics
-- should automatically handle ["livery_id"]
relinkZones[theUnit.name] = cfxZones.zonesLinkedToUnit(impStat)
else else
-- dead local impStat = StaticObject.getByName(impName)
table.insert(deadUnits, theUnit.name) if impStat and impStat:isExist() and impStat:getLife() > 1 then
end -- still alive. read x, y and heading
-- destroy imp local sp = impStat:getPoint()
if impStat and impStat:isExist() then theUnit.x = sp.x
impStat:destroy() theUnit.y = sp.z -- !!!
theUnit.heading = dcsCommon.getUnitHeading(impStat) -- should also work for statics
-- should automatically handle ["livery_id"]
relinkZones[theUnit.name] = cfxZones.zonesLinkedToUnit(impStat)
else
-- dead
table.insert(deadUnits, theUnit.name)
end
-- destroy imp
if impStat and impStat:isExist() then
impStat:destroy()
end
end end
end end
end end
end
-- destroy impostor info
-- destroy impostor info theZone.myImpostors[groupName] = nil
theZone.myImpostors[groupName] = nil theZone.impostor = false -- is this good?
theZone.impostor = false
-- now create the group
-- now create the group if theZone.blinkTime <= 0 then
if theZone.blinkTime <= 0 then -- immediate spawn
-- immediate spawn --local newGroup = coalition.addGroup(ctry, cfxMX.catText2ID(cat), rawData)
--local newGroup = coalition.addGroup(ctry, cfxMX.catText2ID(cat), rawData) local newGroup = coalition.addGroup(ctry, cat, rawData)
local newGroup = coalition.addGroup(ctry, cat, rawData) impostors.relinkZonesForGroup(relinkZones, newGroup)
impostors.relinkZonesForGroup(relinkZones, newGroup) if theZone.trackWith and groupTracker.addGroupToTrackerNamed then
if theZone.trackWith and groupTracker.addGroupToTrackerNamed then -- add these groups to the group tracker
-- add these groups to the group tracker if theZone.verbose or impostors.verbose then
if theZone.verbose or impostors.verbose then trigger.action.outText("+++ipst: attempting to add group <" .. newGroup:getName() .. "> to tracker <" .. theZone.trackWith .. ">", 30)
trigger.action.outText("+++ipst: attempting to add group <" .. newGroup:getName() .. "> to tracker <" .. theZone.trackWith .. ">", 30) end
end groupTracker.addGroupToTrackerNamed(newGroup, theZone.trackWith)
groupTracker.addGroupToTrackerNamed(newGroup, theZone.trackWith) end
else
-- scheduled spawn
theZone.blinkCount = theZone.blinkCount + 1 -- so healthcheck avoids false positives
local args = {}
args.ctry = ctry
args.cat = cat -- cfxMX.catText2ID(cat)
args.rawData = rawData
args.theZone = theZone
args.relinkZones = relinkZones
timer.scheduleFunction(impostors.delayedSpawn, args, timer.getTime() + theZone.blinkTime)
end end
else else
-- scheduled spawn if theZone.verbose or impostors.verbose then
theZone.blinkCount = theZone.blinkCount + 1 -- so healthcheck avoids false positives trigger.action.outText("No impostor group named <" .. groupName .. "> any more, skipped.", 30)
local args = {} end
args.ctry = ctry -- theZone.myImpostors[groupName] = nil
args.cat = cat -- cfxMX.catText2ID(cat)
args.rawData = rawData
args.theZone = theZone
args.relinkZones = relinkZones
timer.scheduleFunction(impostors.delayedSpawn, args, timer.getTime() + theZone.blinkTime)
end end
end end
theZone.groupNames = filtered -- filter out non-existing
-- now remove all dead units -- now remove all dead units
if theZone.blinkTime <= 0 then if theZone.blinkTime <= 0 then
for idx, unitName in pairs(deadUnits) do for idx, unitName in pairs(deadUnits) do

View File

@ -1,5 +1,5 @@
noGap = {} noGap = {}
noGap.version = "1.0.1" noGap.version = "1.3.1"
noGap.verbose = false noGap.verbose = false
noGap.ignoreMe = "-ng" -- ignore altogether noGap.ignoreMe = "-ng" -- ignore altogether
@ -20,11 +20,7 @@ noGap.requiredLibs = {
works on unit-level (stop-Gap works on group level) works on unit-level (stop-Gap works on group level)
Advantage: multiple-ship player groups look better, less code Advantage: multiple-ship player groups look better, less code
Disadvantage: incompatibe with SSB/slotBlock Disadvantage: incompatibe with SSB/slotBlock
What it does:
Replace all player units with static aircraft until the first time
that a player slots into that plane. Static is then replaced with live player unit.
DOES NOT SUPPORT SHIP-BASED AIRCRAFT DOES NOT SUPPORT SHIP-BASED AIRCRAFT
For multiplayer, NoGapGUI must run on the server (only server) For multiplayer, NoGapGUI must run on the server (only server)
@ -38,12 +34,18 @@ noGap.requiredLibs = {
Version History Version History
1.0.0 - Initial version 1.0.0 - Initial version
1.0.1 - added "from runway" 1.0.1 - added "from runway"
1.3.1 - in line with stopGap 1.3.0
- allNeutral attribute
- DCS dynamic player spawn compatibility
- shallow water also qualifies as carrier based
- noParking
- kickTheDead
- refreshInterval, reinforced guards
--]]-- --]]--
noGap.standInUnits = {} -- static replacement, if filled; indexed by name noGap.standInUnits = {} -- static replacement, if filled; indexed by name
noGap.liveUnits = {} -- live in-game units, checked regularly noGap.liveUnits = {} -- live in-game units, checked regularly
noGap.allPlayerUnits = {} -- for update check to get server notification noGap.allPlayerUnits = {} -- for update check to get server notification, excludes dynamically spawned units
noGap.noGapZones = {} -- DML only noGap.noGapZones = {} -- DML only
function noGap.staticMXFromUnitMX(theGroup, theUnit) function noGap.staticMXFromUnitMX(theGroup, theUnit)
@ -57,6 +59,12 @@ function noGap.staticMXFromUnitMX(theGroup, theUnit)
theStatic.type = theUnit.type theStatic.type = theUnit.type
theStatic.name = theUnit.name -- same as ME unit theStatic.name = theUnit.name -- same as ME unit
theStatic.cty = cfxMX.countryByName[theGroup.name] theStatic.cty = cfxMX.countryByName[theGroup.name]
theStatic.payload = theUnit.payload -- not supported (yet) by DCS
theStatic.onboard_num = theUnit.onboard_num -- not supported
-- DML only: allNeutral
if noGap.allNeutral then
theStatic.cty = dcsCommon.getACountryForCoalition(0)
end
return theStatic return theStatic
end end
@ -82,10 +90,20 @@ function noGap.isGroundStart(theGroup)
if action == "Turning Point" then return false end if action == "Turning Point" then return false end
if action == "Landing" then return false end if action == "Landing" then return false end
if action == "From Runway" then return false end if action == "From Runway" then return false end
if noGap.noParking then
local loAct = string.lower(action)
if loAct == "from parking area" or
loAct == "from parking area hot" then
if noGap.verbose then
trigger.action.outText("StopG: Player Group <" .. theGroup.name .. "> NOPARKING: [" .. action .. "] must be skipped.", 30)
end
return false
end
end
-- aircraft is on the ground - but is it in water (carrier)? -- aircraft is on the ground - but is it in water (carrier)?
local u1 = theGroup.units[1] local u1 = theGroup.units[1]
local sType = land.getSurfaceType(u1) -- has fields x and y local sType = land.getSurfaceType(u1) -- has fields x and y
if sType == 3 then return false end if sType == 3 or sType == 2 then return false end
if noGap.verbose then if noGap.verbose then
trigger.action.outText("noG: Player Group <" .. theGroup.name .. "> GROUND BASED: " .. action .. ", land type " .. sType, 30) trigger.action.outText("noG: Player Group <" .. theGroup.name .. "> GROUND BASED: " .. action .. ", land type " .. sType, 30)
end end
@ -107,7 +125,7 @@ function noGap.ignoreMXUnit(theUnit) -- DML-only
return false return false
end end
function noGap.createStandInForMXData(group, theUnit) -- group, theUnit are MX data blocks function noGap.createStandInForMXData(group, theUnit) -- WARNING: group and theUnit are MX data blocks
local sgMatch = theUnit.name:sub(-#noGap.ignoreMe) == noGap.ignoreMe or group.name:sub(-#noGap.ignoreMe) == noGap.ignoreMe local sgMatch = theUnit.name:sub(-#noGap.ignoreMe) == noGap.ignoreMe or group.name:sub(-#noGap.ignoreMe) == noGap.ignoreMe
local spMatch = theUnit.name:sub(-#noGap.spIgnore) == noGap.spIgnore or group.name:sub(-#noGap.spIgnore) == noGap.spIgnore local spMatch = theUnit.name:sub(-#noGap.spIgnore) == noGap.spIgnore or group.name:sub(-#noGap.spIgnore) == noGap.spIgnore
local zoneIgnore = noGap.ignoreMXUnit(theUnit) local zoneIgnore = noGap.ignoreMXUnit(theUnit)
@ -139,7 +157,6 @@ function noGap.createStandInForMXData(group, theUnit) -- group, theUnit are MX d
end end
end end
end end
end end
function noGap.fillGaps() function noGap.fillGaps()
@ -175,6 +192,7 @@ function noGap.turnOff()
StaticObject.destroy(standIn) StaticObject.destroy(standIn)
end end
noGap.standInUnits = {} noGap.standInUnits = {}
noGap.running = false
end end
function noGap.turnOn() function noGap.turnOn()
@ -183,6 +201,23 @@ function noGap.turnOn()
end end
-- populate all empty (non-taken) slots with stand-ins -- populate all empty (non-taken) slots with stand-ins
noGap.fillGaps() noGap.fillGaps()
noGap.running = true
end
function noGap.refreshAll() -- restore all statics
if noGap.refreshInterval > 0 then
-- re-schedule invocation
timer.scheduleFunction(noGap.refreshAll, {}, timer.getTime() + noGap.refreshInterval)
if not noGap.enabled then return end
if noGap.running then
noGap.turnOff() -- kill all statics
-- turn back on in half a second
timer.scheduleFunction(noGap.turnOn, {}, timer.getTime() + 0.5)
end
if stopGap.verbose then
noGap.action.outText("+++noG: refreshing all static", 30)
end
end
end end
-- --
@ -193,11 +228,18 @@ function noGap:onEvent(event)
if not event.id then return end if not event.id then return end
if not event.initiator then return end if not event.initiator then return end
local theUnit = event.initiator local theUnit = event.initiator
if (not theUnit.getPlayerName) or (not theUnit:getPlayerName()) then
return
end -- no player unit.
if cfxMX.isDynamicPlayer(theUnit) then
if noGap.verbose then
trigger.action.outText("+++noG: unit <" .. theUnit:getName() .. "> controlled by <" .. theUnit:getPlayerName() .. "> is dynamically spawned, ignoring.", 30)
end
return
end -- ignore all dynamically spawned aircraft
if event.id == 15 then -- we act on player unit birth if event.id == 15 then -- we act on player unit birth
if (not theUnit.getPlayerName) or (not theUnit:getPlayerName()) then
return
end -- no player unit.
local uName = theUnit:getName() local uName = theUnit:getName()
if noGap.standInUnits[uName] then if noGap.standInUnits[uName] then
@ -208,11 +250,35 @@ function noGap:onEvent(event)
trigger.action.outText("+++noG: removed static for <" ..uName .. ">, player inbound", 30) trigger.action.outText("+++noG: removed static for <" ..uName .. ">, player inbound", 30)
end end
end end
noGap.liveUnits[uName] = theUnit noGap.liveUnits[uName] = theUnit -- dynamic never show up here
-- reset noGapGUI flag, it has done its job. Unit is live -- reset noGapGUI flag, it has done its job. Unit is live
-- we can reset it for next iteration -- we can reset it for next iteration
trigger.action.setUserFlag("NG"..uName, 0) trigger.action.setUserFlag("NG"..uName, 0)
end end
if id == 6 then -- eject, ignore for now
end
if (id == 9) or (id == 30) or (id == 5) then -- dead, lost, crash
local pName = theUnit:getPlayerName()
timer.scheduleFunction(noGap.kickplayer, pName, timer.getTime() + 1)
end
end
noGap.kicks = {}
function noGap.kickplayer(args)
if not noGap.kickTheDead then return end
local pName = args
for i,slot in pairs(net.get_player_list()) do
local nn = net.get_name(slot)
if nn == pName then
if noGap.kicks[nn] then
if timer.getTime() < noGap.kicks[nn] then return end
end
net.force_player_slot(slot, 0, '')
noGap.kicks[nn] = timer.getTime() + 5 -- avoid too many kicks in 5 seconds
end
end
end end
-- --
@ -312,7 +378,7 @@ function noGap.update()
end end
-- --
-- read stopGapZone (DML only) -- read noGap Zone (DML only)
-- --
function noGap.createNoGapZone(theZone) function noGap.createNoGapZone(theZone)
local ng = theZone:getBoolFromZoneProperty("noGap", true) local ng = theZone:getBoolFromZoneProperty("noGap", true)
@ -331,8 +397,11 @@ noGap.name = "noGapConfig" -- cfxZones compatibility here
function noGap.readConfigZone(theZone) function noGap.readConfigZone(theZone)
-- currently nothing to do -- currently nothing to do
noGap.verbose = theZone.verbose noGap.verbose = theZone.verbose
noGap.ssbEnabled = theZone:getBoolFromZoneProperty("ssb", true)
noGap.enabled = theZone:getBoolFromZoneProperty("onStart", true) noGap.enabled = theZone:getBoolFromZoneProperty("onStart", true)
noGap.timeOut = theZone:getNumberFromZoneProperty("timeOut", 0) -- default to off noGap.timeOut = theZone:getNumberFromZoneProperty("timeOut", 0) -- default to off
noGap.noParking = theZone:getBoolFromZoneProperty("noParking", false)
if theZone:hasProperty("on?") then if theZone:hasProperty("on?") then
noGap.turnOnFlag = theZone:getStringFromZoneProperty("on?", "*<none>") noGap.turnOnFlag = theZone:getStringFromZoneProperty("on?", "*<none>")
noGap.lastTurnOnFlag = trigger.misc.getUserFlag(noGap.turnOnFlag) noGap.lastTurnOnFlag = trigger.misc.getUserFlag(noGap.turnOnFlag)
@ -350,6 +419,10 @@ function noGap.readConfigZone(theZone)
trigger.action.outText("+++noG: turned off", 30) trigger.action.outText("+++noG: turned off", 30)
end end
end end
noGap.refreshInterval = theZone:getNumberFromZoneProperty("refresh", -1) -- default: no refresh
noGap.kickTheDead = theZone:getBoolFromZoneProperty("kickDead", true)
noGap.allNeutral = theZone:getBoolFromZoneProperty("allNeutral", false)
end end
-- --
@ -394,6 +467,11 @@ function noGap.start()
-- start update in 1 second -- start update in 1 second
timer.scheduleFunction(noGap.update, {}, timer.getTime() + 1) timer.scheduleFunction(noGap.update, {}, timer.getTime() + 1)
-- start refresh cycle if refresh (>0)
if noGap.refreshInterval > 0 then
timer.scheduleFunction(noGap.refreshAll, {}, timer.getTime() + noGap.refreshInterval)
end
-- say hi! -- say hi!
local mp = " (SP - <" .. sgDetect .. ">)" local mp = " (SP - <" .. sgDetect .. ">)"
if sgDetect > 0 then mp = " -- MP GUI Detected (" .. sgDetect .. ")!" end if sgDetect > 0 then mp = " -- MP GUI Detected (" .. sgDetect .. ")!" end

View File

@ -1,5 +1,5 @@
radioMenu = {} radioMenu = {}
radioMenu.version = "4.0.1" radioMenu.version = "4.0.2"
radioMenu.verbose = false radioMenu.verbose = false
radioMenu.ups = 1 radioMenu.ups = 1
radioMenu.requiredLibs = { radioMenu.requiredLibs = {
@ -19,7 +19,8 @@ radioMenu.lateGroups = {} -- dict by ID
detect cyclic references detect cyclic references
4.0.0 - added infrastructure for dynamic players (DCS 2.9.6) 4.0.0 - added infrastructure for dynamic players (DCS 2.9.6)
- added dynamic player support - added dynamic player support
4.0.1 - MX no longer optional, so ask for it 4.0.1 - MX no longer optional, so ask for it
4.0.2 - ackSnd now also broadcasts to all if no group
--]]-- --]]--
function radioMenu.addRadioMenu(theZone) function radioMenu.addRadioMenu(theZone)
@ -770,7 +771,11 @@ function radioMenu.doMenuX(args)
radioMenu.radioOutMsg(ack, gid, theZone) radioMenu.radioOutMsg(ack, gid, theZone)
end end
if ackSnd then if ackSnd then
trigger.action.outSoundForGroup(gid, ackSnd) if (not gid) or gid == 0 then
trigger.action.outSound(ackSnd)
else
trigger.action.outSoundForGroup(gid, ackSnd)
end
end end
end end

View File

@ -1,107 +1,144 @@
raiseFlag = {} raiseFlag = {}
raiseFlag.version = "2.0.0" raiseFlag.version = "3.0.0"
raiseFlag.verbose = false
raiseFlag.requiredLibs = { raiseFlag.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
"cfxZones", -- Zones, of course "cfxZones", -- Zones, of course
} }
raiseFlag.flags = {} raiseFlag.flags = {} -- my minions
--[[-- --[[--
Raise A Flag module -- (c) 2022-23 by Christian Franz and cf/x AG (c) 2022-23 by Christian Franz and cf/x AG
Version History Version History
1.0.0 - initial release 3.0.0 - switched to polling
1.0.1 - synonym "raiseFlag!" - remains# attribute
1.1.0 - DML update - support for persistence
1.2.0 - Watchflag update - zone-individual verbosity
1.2.1 - support for 'inc', 'dec', 'flip' - code cleanup, removed deprecated attributes
2.0.0 - dmlZones
- full method support
- full DML upgrade
- method attribute (synonym to 'value')
--]]-- --]]--
function raiseFlag.addRaiseFlag(theZone) function raiseFlag.addRaiseFlag(theZone)
table.insert(raiseFlag.flags, theZone) raiseFlag.flags[theZone.name] = theZone
end end
function raiseFlag.getRaiseFlagByName(aName)
for idx, aZone in pairs(raiseFlag.flags) do
if aName == aZone.name then return aZone end
end
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: no raiseFlag with name <" .. aName ..">", 30)
end
return nil
end
-- --
-- read attributes -- read attributes
-- --
function raiseFlag.createRaiseFlagWithZone(theZone) function raiseFlag.createRaiseFlagWithZone(theZone)
-- get flag from faiseFlag itself theZone.raiseFlag = theZone:getStringFromZoneProperty("raiseFlag!", "<none>") -- the flag to raise
if theZone:hasProperty("raiseFlag") then theZone.flagValue = theZone:getStringFromZoneProperty("method", "inc")
theZone.raiseFlag = theZone:getStringFromZoneProperty("raiseFlag", "<none>") -- the flag to raise
else
theZone.raiseFlag = theZone:getStringFromZoneProperty("raiseFlag!", "<none>") -- the flag to raise
end
-- pre-method DML raiseFlag is now upgraded to method.
-- flagValue now carries the method
if theZone:hasProperty("value") then -- backward compatibility
theZone.flagValue = theZone:getStringFromZoneProperty("value", "inc") -- value to set to. default is command 'inc'
else
theZone.flagValue = theZone:getStringFromZoneProperty("method", "inc")
end
theZone.flagValue = theZone.flagValue:lower() theZone.flagValue = theZone.flagValue:lower()
theZone.minAfterTime, theZone.maxAfterTime = theZone:getPositiveRangeFromZoneProperty("afterTime", -1) theZone.minAfterTime, theZone.maxAfterTime = theZone:getPositiveRangeFromZoneProperty("afterTime", -1)
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change") theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
if theZone:hasProperty("raiseTriggerMethod") then if theZone:hasProperty("raiseTriggerMethod") then
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty("raiseTriggerMethod", "change") theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty("raiseTriggerMethod", "change")
end end
if theZone:hasProperty("stopFlag?") then if theZone:hasProperty("stopFlag?") then
theZone.triggerStopFlag = theZone:getStringFromZoneProperty( "stopFlag?", "none") theZone.triggerStopFlag = theZone:getStringFromZoneProperty( "stopFlag?", "none")
theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopFlag) -- save last value theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopFlag) -- save last value
end end
theZone.scheduleID = nil if theZone:hasProperty("timeLeft#") then
theZone.timeLeft = theZone:getStringFromZoneProperty("timeLeft#", "none")
end
theZone.raiseStopped = false theZone.raiseStopped = false
-- now simply schedule for invocation -- now simply schedule for invocation
local args = {}
args.theZone = theZone
if theZone.minAfterTime < 1 then if theZone.minAfterTime < 1 then
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + 0.5) theZone.deadLine = -1 -- will always trigger
else else
local delay = cfxZones.randomDelayFromPositiveRange(theZone.minAfterTime, theZone.maxAfterTime) local delay = cfxZones.randomDelayFromPositiveRange(theZone.minAfterTime, theZone.maxAfterTime)
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + delay) theZone.deadLine = timer.getTime() + delay
end end
end end
function raiseFlag.triggered(args) function raiseFlag.triggered(theZone)
local theZone = args.theZone if theZone.raiseStopped then return end -- should be filtered
if theZone.raiseStopped then return end
-- if we get here, we aren't stopped and do the flag pull
local command = theZone.flagValue local command = theZone.flagValue
theZone:pollFlag(theZone.raiseFlag, command) theZone:pollFlag(theZone.raiseFlag, command)
if raiseFlag.verbose or theZone.verbose then if raiseFlag.verbose or theZone.verbose then
trigger.action.outText("+++rFlg - raising <" .. theZone.raiseFlag .. "> with method '" .. command .. "'" ,30) trigger.action.outText("+++rFlg - raising <" .. theZone.raiseFlag .. "> with method '" .. command .. "'" .. " for zone <" .. theZone.name .. ">" ,30)
end end
end end
-- --
-- update -- update
-- --
function raiseFlag.update() function raiseFlag.update(firstUpdate)
-- call me in a second to poll triggers local now = timer.getTime()
timer.scheduleFunction(raiseFlag.update, {}, timer.getTime() + 1) timer.scheduleFunction(raiseFlag.update, false, now + 1) -- we always beat on .5 offset!
for idx, aZone in pairs(raiseFlag.flags) do local filtered = {}
-- make sure to re-start before reading time limit for zName, theZone in pairs(raiseFlag.flags) do
if aZone:testZoneFlag(aZone.triggerStopFlag, aZone.raiseTriggerMethod, "lastTriggerStopValue") then -- see if this timer has run out
theZone.raiseStopped = true -- we are done, no flag! if theZone.deadLine < now then
if theZone.verbose then
trigger.action.outText("+++rFlg: will raise flag <" .. theZone.name .. ">", 30)
end
raiseFlag.triggered(theZone)
if theZone.timeLeft then theZone:setFlagValue(theZone.timeLeft, 0) end
theZone.raiseStopped = true -- will filter
elseif theZone.timeLeft then
local rem = theZone.deadLine - now
theZone:setFlagValue(theZone.timeLeft, rem)
if theZone.verbose then
trigger.action.outText("+++rFlg: time left on <" .. theZone.name .. ">:" .. math.floor(rem) .. " secs", 30)
end
end end
if theZone:testZoneFlag(theZone.triggerStopFlag, theZone.raiseTriggerMethod, "lastTriggerStopValue") then
theZone.raiseStopped = true -- filter
end
if not theZone.raiseStopped then
filtered[theZone.name] = theZone
end
end end
raiseFlag.flags = filtered
end
--
-- load / save (game data)
--
function raiseFlag.saveData()
local theData = {}
local theFlags = {}
local now = timer.getTime()
for name, theZone in pairs (raiseFlag.flags) do
-- note: we only process existing!
theFlags[name] = theZone.deadLine - now
end
-- save current deadlines
theData.theFlags = theFlags
return theData, raiseFlag.sharedData -- second val currently nil
end end
function raiseFlag.loadData()
if not persistence then return end
local shared = nil
local theData = persistence.getSavedDataForModule("raiseFlag")
if (not theData) then
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: no save date received, skipping.", 30)
end
return
end
local theFlags = theData.theFlags
-- filter and reset timers
local filtered = {}
local now = timer.getTime()
for name, deadLine in pairs(theFlags) do
local theZone = raiseFlag.flags[name]
if theZone then
theZone.deadLine = now + deadLine
filtered[name] = theZone
if theZone.verbose then
trigger.action.outText("+++rFlg: (persistence) reset zone <" .. name .. "> to <" .. theZone.deadLine .. "> (<" .. deadLine .. ">)", 30)
end
else
trigger.action.outText("+++rFlag: (persistence) filtered <" .. name .. ">, does not exist in miz.", 30)
end
end
for name, theZone in pairs(raiseFlag.flags) do
if not filtered[name] and theZone.verbose then
trigger.action.outText("+++rFlg: (persistence) filtered spent/non-saved <" .. name .. ">.", 30)
end
end
raiseFlag.flags = filtered
end
-- --
-- config & go! -- config & go!
-- --
@ -117,39 +154,30 @@ function raiseFlag.readConfigZone()
end end
function raiseFlag.start() function raiseFlag.start()
-- lib check
if not dcsCommon.libCheck then if not dcsCommon.libCheck then
trigger.action.outText("cfx raise flag requires dcsCommon", 30) trigger.action.outText("cfx raise flag requires dcsCommon", 30)
return false return false
end end
if not dcsCommon.libCheck("cfx Raise Flag", raiseFlag.requiredLibs) then if not dcsCommon.libCheck("cfx Raise Flag", raiseFlag.requiredLibs)then return false end
return false
end
-- read config
raiseFlag.readConfigZone() raiseFlag.readConfigZone()
-- process raise Flags Zones
local attrZones = cfxZones.getZonesWithAttributeNamed("raiseFlag")
for k, aZone in pairs(attrZones) do
raiseFlag.createRaiseFlagWithZone(aZone) -- process attributes
raiseFlag.addRaiseFlag(aZone) -- add to list
end
-- try synonym
attrZones = cfxZones.getZonesWithAttributeNamed("raiseFlag!") attrZones = cfxZones.getZonesWithAttributeNamed("raiseFlag!")
for k, aZone in pairs(attrZones) do for k, aZone in pairs(attrZones) do
raiseFlag.createRaiseFlagWithZone(aZone) -- process attributes raiseFlag.createRaiseFlagWithZone(aZone) -- process attributes
raiseFlag.addRaiseFlag(aZone) -- add to list raiseFlag.addRaiseFlag(aZone) -- add to list
end end
if persistence then
-- start update callbacks = {}
raiseFlag.update() callbacks.persistData = raiseFlag.saveData
persistence.registerModule("raiseFlag", callbacks)
-- now load my data
raiseFlag.loadData()
end
-- start update at 0.5 secs mark
timer.scheduleFunction(raiseFlag.update, true, timer.getTime() + 0.5)
trigger.action.outText("cfx raiseFlag v" .. raiseFlag.version .. " started.", 30) trigger.action.outText("cfx raiseFlag v" .. raiseFlag.version .. " started.", 30)
return true return true
end end
-- let's go!
if not raiseFlag.start() then if not raiseFlag.start() then
trigger.action.outText("cfx Raise Flag aborted: missing libraries", 30) trigger.action.outText("cfx Raise Flag aborted: missing libraries", 30)
raiseFlag = nil raiseFlag = nil

View File

@ -1,5 +1,5 @@
slotty = {} slotty = {}
slotty.version = "1.1.0" slotty.version = "1.2.0"
--[[-- --[[--
Single-player slot blocking and slot blocking fallback Single-player slot blocking and slot blocking fallback
for multiplayer when SSB is not installed on server. for multiplayer when SSB is not installed on server.
@ -12,7 +12,7 @@ slotty.version = "1.1.0"
Version history Version history
1.0.0 - Initial version 1.0.0 - Initial version
1.1.0 - "noSlotty" global disable flag, anti-mirror SSB flag 1.1.0 - "noSlotty" global disable flag, anti-mirror SSB flag
1.2.0 - better (delayed) nilling for cfxSSB ocupied clients to 1.2.0 - better (delayed) nilling for cfxSSB occupied clients to
- avoid a race condition - avoid a race condition
--]]-- --]]--

View File

@ -58,7 +58,7 @@ stopGap.requiredLibs = {
required required
1.3.0 - carriers in shallow waters also no longer handled as viable 1.3.0 - carriers in shallow waters also no longer handled as viable
- noParking option - noParking option
1.3.1 - stronger guards on refresh
--]]-- --]]--
stopGap.standInGroups = {} -- idx by name, if set has a static stopGap.standInGroups = {} -- idx by name, if set has a static
@ -239,6 +239,7 @@ function stopGap.refreshAll() -- restore all statics
if stopGap.refreshInterval > 0 then if stopGap.refreshInterval > 0 then
-- re-schedule invocation -- re-schedule invocation
timer.scheduleFunction(stopGap.refreshAll, {}, timer.getTime() + stopGap.refreshInterval) timer.scheduleFunction(stopGap.refreshAll, {}, timer.getTime() + stopGap.refreshInterval)
if not stopGap.enabled then return end -- why do we have enabled and running?
if stopGap.running then if stopGap.running then
stopGap.turnOff() -- kill all statics stopGap.turnOff() -- kill all statics
-- turn back on in half a second -- turn back on in half a second