mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.3.5
WHpersistence
This commit is contained in:
parent
a70fadc550
commit
0c1eb53a89
Binary file not shown.
Binary file not shown.
96
modules/WHpersistence.lua
Normal file
96
modules/WHpersistence.lua
Normal 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
|
||||
@ -1,5 +1,5 @@
|
||||
airfield = {}
|
||||
airfield.version = "2.2.0"
|
||||
airfield.version = "2.3.0"
|
||||
airfield.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
@ -22,6 +22,12 @@ airfield.allAirfields = {} -- inexed by af name, db entries: base, cat
|
||||
2.1.0 - added support for makeNeutral?
|
||||
2.1.1 - bug fixing for DCS 2.9x airfield retrofit
|
||||
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
|
||||
@ -31,7 +37,6 @@ function airfield.collectAll()
|
||||
local dropped = 0
|
||||
for idx, aBase in pairs(allBases) do
|
||||
local entry = {}
|
||||
--local cat = Airbase.getCategory(aBase) -- DCS 2.9 hardened
|
||||
-- ho! dcs 2.9.x retrofit screwed with Airfield.getCategory.
|
||||
local cat = aBase:getDesc().category
|
||||
-- cats: 0 = airfield, 1 = farp, 2 = ship
|
||||
@ -44,20 +49,18 @@ function airfield.collectAll()
|
||||
count = count + 1
|
||||
else
|
||||
dropped = dropped + 1
|
||||
-- trigger.action.outText("***dropped airbase <" .. aBase:getName() .. ">, cat = <" .. cat .. ">", 30)
|
||||
end
|
||||
end
|
||||
if airfield.verbose then
|
||||
trigger.action.outText("+++airF: init - count = <" .. count .. ">, dropped = <" .. dropped .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- setting up airfield
|
||||
--
|
||||
airfield.collector = {}
|
||||
function airfield.createAirFieldFromZone(theZone)
|
||||
theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||
|
||||
local filterCat = 0
|
||||
if (theZone.farps) then filterCat = {0, 1} end -- bases and farps
|
||||
local p = theZone:getPoint()
|
||||
@ -65,17 +68,15 @@ function airfield.createAirFieldFromZone(theZone)
|
||||
theZone.airfield = theBase
|
||||
theZone.afName = theBase:getName()
|
||||
|
||||
-- set zone's owner
|
||||
-- set zone's owner
|
||||
theZone.owner = theBase:getCoalition()
|
||||
theZone.mismatchCount = airfield.gracePeriod
|
||||
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)
|
||||
end
|
||||
|
||||
-- methods
|
||||
theZone.method = theZone:getStringFromZoneProperty("method", "inc")
|
||||
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
|
||||
if theZone:hasProperty("red!") then
|
||||
theZone.redCap = theZone:getStringFromZoneProperty("red!")
|
||||
end
|
||||
@ -92,19 +93,16 @@ function airfield.createAirFieldFromZone(theZone)
|
||||
theZone.makeBlue = theZone:getStringFromZoneProperty("makeBlue?", "<none>")
|
||||
theZone.lastMakeBlue = trigger.misc.getUserFlag(theZone.makeBlue)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("makeNeutral?") then
|
||||
theZone.makeNeutral = theZone:getStringFromZoneProperty("makeNeutral?", "<none>")
|
||||
theZone.lastMakeNeutral = trigger.misc.getUserFlag(theZone.makeNeutral)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("autoCap?") then
|
||||
theZone.autoCap = theZone:getStringFromZoneProperty("autoCap?", "<none>")
|
||||
theZone.lastAutoCap = trigger.misc.getUserFlag(theZone.autoCap)
|
||||
end
|
||||
|
||||
theZone.directControl = theZone:getBoolFromZoneProperty("directControl", false)
|
||||
|
||||
if theZone.directControl then
|
||||
airfield.assumeControl(theZone)
|
||||
end
|
||||
@ -144,6 +142,14 @@ function airfield.createAirFieldFromZone(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
|
||||
local entry = airfield.allAirfields[theZone.afName]
|
||||
if not entry then
|
||||
@ -179,7 +185,6 @@ function airfield.showAirfield(theZone)
|
||||
theZone.ownerMark = nil
|
||||
end
|
||||
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 fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red
|
||||
local owner = theZone:getCoalition() -- .owner
|
||||
@ -190,10 +195,7 @@ function airfield.showAirfield(theZone)
|
||||
lineColor = theZone.neutralLine -- {0.8, 0.8, 0.8, 1.0}
|
||||
fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
|
||||
end
|
||||
|
||||
theZone.ownerMark = airfield.markAirfieldOnMap(theZone.airfield, lineColor, fillColor)
|
||||
|
||||
|
||||
end
|
||||
|
||||
function airfield.assumeControl(theZone)
|
||||
@ -211,7 +213,6 @@ function airfield.relinquishControl(theZone)
|
||||
end
|
||||
theBase:autoCapture(true) -- turn off autocap
|
||||
end
|
||||
|
||||
--
|
||||
-- event handling
|
||||
--
|
||||
@ -252,7 +253,6 @@ function airfield.airfieldCaptured(theBase)
|
||||
if theZone.verbose or airfield.verbose then
|
||||
trigger.action.outText("+++airF: capturing <" .. bName .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
local newCoa = theBase:getCoalition()
|
||||
theZone.owner = newCoa
|
||||
|
||||
@ -272,7 +272,6 @@ function airfield.airfieldCaptured(theBase)
|
||||
if theZone.blueCap and newCoa == 2 then
|
||||
theZone:pollFlag(theZone.blueCap, theZone.method)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function airfield:onEvent(event)
|
||||
@ -348,7 +347,6 @@ function airfield.update()
|
||||
theZone.owner = 0
|
||||
end
|
||||
|
||||
|
||||
if theZone.autoCap and theZone:testZoneFlag(theZone.autoCap, theZone.triggerMethod, "lastAutoCap") then
|
||||
if theAirfield:autoCaptureIsOn() then
|
||||
-- do nothing
|
||||
@ -386,16 +384,12 @@ function airfield.GC()
|
||||
trigger.action.outText("+++airF: corrected ownership after grace period", 30)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- LOAD / SAVE
|
||||
--
|
||||
|
||||
function airfield.saveData()
|
||||
local theData = {}
|
||||
local allAF = {}
|
||||
@ -411,6 +405,21 @@ function airfield.saveData()
|
||||
return theData
|
||||
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()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("airfield")
|
||||
@ -418,6 +427,7 @@ function airfield.loadData()
|
||||
if airfield.verbose then
|
||||
trigger.action.outText("+++airF persistence: no save data received, skipping.", 30)
|
||||
end
|
||||
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
|
||||
return
|
||||
end
|
||||
|
||||
@ -426,9 +436,12 @@ function airfield.loadData()
|
||||
if airfield.verbose then
|
||||
trigger.action.outText("+++airF persistence: no airfield data, skipping", 30)
|
||||
end
|
||||
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
|
||||
return
|
||||
end
|
||||
|
||||
airfield.collector = {} -- overwrite existing
|
||||
|
||||
for theName, AFData in pairs(allAF) do
|
||||
local theZone = airfield.myAirfields[theName]
|
||||
if theZone then
|
||||
@ -438,20 +451,22 @@ function airfield.loadData()
|
||||
theAirfield:autoCapture(false)
|
||||
theAirfield:setCoalition(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#
|
||||
if theZone.ownedBy then
|
||||
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
|
||||
end
|
||||
-- set owning mode: autocap or direct
|
||||
theAirfield:autoCapture(AFData.autocapActive)
|
||||
|
||||
--theAirfield:autoCapture(AFData.autocapActive)
|
||||
airfield.collector[theAirfield] = AFData.autocapActive
|
||||
else
|
||||
trigger.action.outText("+++airF persistence: cannot synch airfield <" .. theName .. ">, skipping", 40)
|
||||
end
|
||||
end
|
||||
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- start up
|
||||
--
|
||||
@ -461,8 +476,6 @@ function airfield.readConfig()
|
||||
theZone = cfxZones.createSimpleZone("airfieldConfig")
|
||||
end
|
||||
airfield.verbose = theZone.verbose
|
||||
-- airfield.farps = theZone:getBoolFromZoneProperty("farps", false)
|
||||
|
||||
-- colors for line and fill
|
||||
airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
|
||||
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.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.showAll = theZone:getBoolFromZoneProperty("show", false)
|
||||
end
|
||||
|
||||
@ -485,13 +497,10 @@ end
|
||||
function airfield.start()
|
||||
if not dcsCommon.libCheck("cfx airfield", airfield.requiredLibs)
|
||||
then return false end
|
||||
|
||||
-- set up DB
|
||||
airfield.collectAll()
|
||||
|
||||
-- read config
|
||||
airfield.readConfig()
|
||||
|
||||
-- read bases
|
||||
local abZones = cfxZones.zonesWithProperty("airfield")
|
||||
for idx, aZone in pairs(abZones) do
|
||||
@ -512,6 +521,8 @@ function airfield.start()
|
||||
persistence.registerModule("airfield", callbacks)
|
||||
-- now load my data
|
||||
airfield.loadData()
|
||||
else
|
||||
timer.scheduleFunction(airfield.releaseFields, airfield.collector, timer.getTime() + 2) -- release airfields when not loaded from storage
|
||||
end
|
||||
|
||||
-- start update in 1 second
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
autoCSAR = {}
|
||||
autoCSAR.version = "2.2.0"
|
||||
autoCSAR.version = "2.2.1"
|
||||
autoCSAR.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -20,6 +20,7 @@ autoCSAR.trackedEjects = {} -- we start tracking on eject
|
||||
2.2.0 - new noExploit option in config
|
||||
- no csar mission if pilot lands too close to airbase or farp
|
||||
and noExploit is on
|
||||
2.2.1 - DCS hardening for isExist
|
||||
--]]--
|
||||
autoCSAR.forbidden = {} -- indexed by name, contains point
|
||||
autoCSAR.killDist = 2100 -- meters from center of forbidden
|
||||
@ -173,7 +174,9 @@ function autoCSAR:onEvent(event)
|
||||
local coa = event.initiator:getCoalition()
|
||||
-- see if pilot has ejector seat and prepare to connect one with the other
|
||||
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.coa = coa
|
||||
info.seat = event.target
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "2.4.0"
|
||||
cloneZones.version = "2.5.0"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -57,6 +57,8 @@ cloneZones.respawnOnGroupID = true
|
||||
path) with wiper module
|
||||
- using "wipe?" will now create a warning
|
||||
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 reason then reason = "<none>" end
|
||||
if not args then args = {} end
|
||||
|
||||
-- invoke anyone who wants to know that a group
|
||||
-- of people was rescued.
|
||||
for idx, cb in pairs(cloneZones.callbacks) do
|
||||
@ -105,7 +106,6 @@ function cloneZones.partOfGroupDataInZone(theZone, theUnits)
|
||||
local zP = cfxZones.getPoint(theZone)
|
||||
zP = theZone:getDCSOrigin() -- don't use getPoint now.
|
||||
zP.y = 0
|
||||
|
||||
for idx, aUnit in pairs(theUnits) do
|
||||
local uP = {}
|
||||
uP.x = aUnit.x
|
||||
@ -197,7 +197,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
table.insert(theZone.myStatics, aStatic)
|
||||
end
|
||||
end
|
||||
|
||||
cloneZones.despawnAll(theZone)
|
||||
if (#theZone.cloneNames + #theZone.staticNames) < 1 then
|
||||
if cloneZones.verbose then
|
||||
@ -210,18 +209,12 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
trigger.action.outText(theZone.name .. " clone template saved", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- declutter
|
||||
theZone.declutter = theZone:getBoolFromZoneProperty("declutter", false)
|
||||
|
||||
-- watchflags
|
||||
theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
|
||||
if theZone:hasProperty("cloneTriggerMethod") then
|
||||
theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("cloneTriggerMethod", "change")
|
||||
end
|
||||
|
||||
-- f? and spawn? and other synonyms map to the same
|
||||
if theZone:hasProperty("f?") then
|
||||
theZone.spawnFlag = theZone:getStringFromZoneProperty("f?", "none")
|
||||
elseif theZone:hasProperty("in?") then
|
||||
@ -231,12 +224,10 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
elseif theZone:hasProperty("clone?") then
|
||||
theZone.spawnFlag = theZone:getStringFromZoneProperty("clone?", "none")
|
||||
end
|
||||
|
||||
if theZone.spawnFlag then
|
||||
theZone.lastSpawnValue = theZone:getFlagValue(theZone.spawnFlag)
|
||||
end
|
||||
|
||||
-- deSpawn?
|
||||
if theZone:hasProperty("deSpawn?") then
|
||||
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deSpawn?", "none")
|
||||
elseif theZone:hasProperty("deClone?") then
|
||||
@ -268,29 +259,6 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
if theZone:hasProperty("method") then
|
||||
theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default
|
||||
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)
|
||||
|
||||
-- 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
|
||||
end
|
||||
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
|
||||
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||
theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false)
|
||||
|
||||
@ -338,15 +305,12 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
theZone.identical = theZone:getBoolFromZoneProperty("identical", false)
|
||||
if theZone.identical == false then theZone.identical = nil end
|
||||
end
|
||||
|
||||
if theZone:hasProperty("nameScheme") then
|
||||
theZone.nameScheme = theZone:getStringFromZoneProperty( "nameScheme", "<o>-<uid>") -- default to [<original name> "-" <uuid>]
|
||||
end
|
||||
|
||||
if theZone:hasProperty("groupScheme") then
|
||||
theZone.groupScheme = theZone:getStringFromZoneProperty("groupScheme", "<o>-<uid>")
|
||||
end
|
||||
|
||||
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)
|
||||
theZone.nameScheme = nil
|
||||
@ -539,7 +503,6 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp
|
||||
-- now process departing slot if given
|
||||
if departFromAerodrome then
|
||||
-- we may need alt from land to add here, maybe later
|
||||
|
||||
-- now process parking slots, and choose closest slot
|
||||
-- per unit's location
|
||||
if fromParking then
|
||||
@ -658,11 +621,9 @@ function cloneZones.uniqueNameStaticData(theData, theCloneZone, sourcename)
|
||||
if theCloneZone and theCloneZone.nameScheme then
|
||||
local schema = theCloneZone.nameScheme
|
||||
newName, iterCount = cloneZones.nameFromSchema(schema, theData.name, theCloneZone, sourceName, iterCount)
|
||||
|
||||
if theCloneZone.verbose then
|
||||
trigger.action.outText("clnZ: zone <" .. theCloneZone.name .. "> static schema <" .. schema .. ">: <" .. theData.name .. "> --> <" .. newName .. ">", 30)
|
||||
end
|
||||
|
||||
theData.name = newName -- dcsCommon.uuid(theData.name)
|
||||
else
|
||||
-- default naming scheme: <name>-<uuid>
|
||||
@ -697,19 +658,6 @@ end
|
||||
|
||||
function cloneZones.resolveOwnership(spawnZone, ctry)
|
||||
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())
|
||||
return ctry
|
||||
end
|
||||
@ -1249,6 +1197,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
|
||||
-- clone for persistence
|
||||
local theData = dcsCommon.clone(rawData)
|
||||
-- remember the zone that spawned this particular group
|
||||
theData.CZspawner = spawnZone.name
|
||||
cloneZones.allClones[rawData.name] = theData
|
||||
|
||||
if cloneZones.verbose or spawnZone.verbose then
|
||||
@ -1273,7 +1223,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
info.timeLimit = now + timeLimit
|
||||
info.cloneZone = spawnZone
|
||||
table.insert(cloneZones.despawnPlan, info)
|
||||
-- trigger.action.outText("+++clne: scheduled auto-despawn for <" .. info.name .. "> in <" .. timeLimit .. "> secs", 30)
|
||||
end
|
||||
|
||||
-- turn off AI if disabled
|
||||
@ -1446,6 +1395,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
rawData.cty = ctry
|
||||
-- save for persistence
|
||||
local theData = dcsCommon.clone(rawData)
|
||||
theData.CZspawner = spawnZone.name -- remember spawner
|
||||
cloneZones.allCObjects[rawData.name] = theData
|
||||
|
||||
if cloneZones.verbose or spawnZone.verbose then
|
||||
@ -1466,7 +1416,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
info.timeLimit = now + timeLimit
|
||||
info.cloneZone = spawnZone
|
||||
table.insert(cloneZones.despawnPlan, info)
|
||||
-- trigger.action.outText("+++clne: scheduled auto-despawn for OBJECT <" .. info.name .. "> in <" .. timeLimit .. "> secs", 30)
|
||||
end
|
||||
-- we don't mix groups with units, so no lookup tables for
|
||||
-- statics
|
||||
@ -1528,9 +1477,7 @@ end
|
||||
function cloneZones.spawnWithSpawner(theZone)
|
||||
-- analog to cfxSpawnZones.spawnWithSpawner(theSpawner)
|
||||
-- glue code for helo troops and other modules
|
||||
|
||||
-- we may want to check if cloner isn't emtpy first
|
||||
|
||||
cloneZones.spawnWithCloner(theZone)
|
||||
end
|
||||
|
||||
@ -1702,15 +1649,6 @@ end
|
||||
|
||||
function cloneZones.resolveOwningCoalition(theZone)
|
||||
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
|
||||
|
||||
function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
|
||||
@ -1821,7 +1759,6 @@ function cloneZones.update()
|
||||
local filtered = {}
|
||||
for idx, theInfo in pairs(cloneZones.despawnPlan) do
|
||||
if theInfo.timeLimit < now then
|
||||
-- trigger.action.outText("+++clne: auto-despawning <" .. theInfo.name .. ">", 30)
|
||||
if theInfo.isObject then
|
||||
-- dealloc static object
|
||||
local theObject = theInfo.theGroup
|
||||
@ -1857,7 +1794,6 @@ function cloneZones.doOnStart()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Regular GC and housekeeping
|
||||
--
|
||||
@ -1884,7 +1820,6 @@ function cloneZones.GC()
|
||||
end
|
||||
end
|
||||
cloneZones.allClones = filteredAttackers
|
||||
|
||||
filteredAttackers = {}
|
||||
for gName, gData in pairs (cloneZones.allCObjects) do
|
||||
-- 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
|
||||
cloneZones.GC()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- LOAD / SAVE
|
||||
--
|
||||
@ -1969,7 +1902,7 @@ function cloneZones.saveData()
|
||||
cData.myUniqueCounter = theCloner.myUniqueCounter
|
||||
cData.oSize = theCloner.oSize
|
||||
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
|
||||
local mySpawns = {}
|
||||
for idx, aGroup in pairs(theCloner.mySpawns) do
|
||||
@ -2071,6 +2004,7 @@ function cloneZones.loadData()
|
||||
local theGroup = Group.getByName(aName)
|
||||
if theGroup then
|
||||
table.insert(mySpawns, theGroup)
|
||||
theCloner.hasClones = true -- was missing!
|
||||
else
|
||||
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with clone group <".. aName .. ">", 30)
|
||||
end
|
||||
@ -2082,6 +2016,7 @@ function cloneZones.loadData()
|
||||
local theStatic = StaticObject.getByName(aName)
|
||||
if theStatic then
|
||||
table.insert(myStatics, theStatic)
|
||||
theCloner.hasClones = true -- was missing!S
|
||||
else
|
||||
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with static <".. aName .. ">", 30)
|
||||
end
|
||||
@ -2115,24 +2050,16 @@ function cloneZones.readConfigZone()
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("cloneZonesConfig")
|
||||
end
|
||||
|
||||
if theZone:hasProperty("uniqueCount") then
|
||||
cloneZones.uniqueCounter = theZone:getNumberFromZoneProperty("uniqueCount", cloneZone.uniqueCounter)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("localCount") then
|
||||
cloneZones.lclUniqueCounter = theZone:getNumberFromZoneProperty("localCount", cloneZone.lclUniqueCounter)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("globalCount") then
|
||||
cloneZones.globalCounter = theZone:getNumberFromZoneProperty("globalCount", cloneZone.globalCounter)
|
||||
end
|
||||
|
||||
cloneZones.verbose = theZone:getBoolFromZoneProperty("verbose", false)
|
||||
|
||||
if cloneZones.verbose then
|
||||
trigger.action.outText("+++clnZ: read config", 30)
|
||||
end
|
||||
cloneZones.verbose = theZone.verbose
|
||||
end
|
||||
|
||||
function cloneZones.start()
|
||||
@ -2201,4 +2128,5 @@ end
|
||||
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
|
||||
maxCycles - maximum number of clone cycles. emulate with countdown?
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "3.1.3"
|
||||
cfxHeloTroops.version = "3.1.5"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -23,7 +23,8 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
3.1.3 - decycled structures (destination zone) on save
|
||||
- upcycled structures (destination) on load
|
||||
- loadSound and disembarkSound
|
||||
|
||||
3.1.4 - guarding destination access in save
|
||||
3.1.5 - more guarding of destination access
|
||||
--]]--
|
||||
|
||||
|
||||
@ -927,8 +928,13 @@ function cfxHeloTroops.saveData()
|
||||
local sData = dcsCommon.clone(gData)
|
||||
dcsCommon.synchGroupData(sData.groupData)
|
||||
if sData.destination then
|
||||
net.log("cfxHeloTroops: decycling troop 'destination' for <" .. sData.destination:getName() .. ">")
|
||||
sData.destination = sData.destination:getName()
|
||||
if type(sData.destination) == "table" and (sData.destination.name) then
|
||||
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
|
||||
allTroopData[gName] = sData
|
||||
end
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
impostors={}
|
||||
|
||||
impostors.version = "1.1.0"
|
||||
impostors.version = "1.2.0"
|
||||
impostors.verbose = false
|
||||
impostors.ups = 1
|
||||
impostors.requiredLibs = {
|
||||
@ -21,7 +21,7 @@ impostors.uniqueCounter = 8200000 -- clones start at 9200000
|
||||
1.1.0 - filtered dead units during spawns
|
||||
cleanup
|
||||
some performance boost for mx lookup
|
||||
|
||||
1.2.0 - filters dead groups entirely
|
||||
LIMITATIONS:
|
||||
must be on ground (or would be very silly
|
||||
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
|
||||
-- after spawning
|
||||
|
||||
local filtered = {}
|
||||
for idx, groupName in pairs(theZone.groupNames) do
|
||||
-- get my group data from MX based on my name
|
||||
-- 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 ctry = cfxMX.countryByName[groupName]
|
||||
local impostorGroup = theZone.myImpostors[groupName]
|
||||
local relinkZones = {}
|
||||
-- now iterate all units in that group, and remove their impostors
|
||||
for idy, theUnit in pairs(rawData.units) do
|
||||
if theUnit and theUnit.name then
|
||||
local impName = impostorGroup[theUnit.name]
|
||||
if not impName then
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("group <" .. groupName .. ">: no impostor for <" .. theUnit.name .. ">", 30)
|
||||
end
|
||||
else
|
||||
local impStat = StaticObject.getByName(impName)
|
||||
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)
|
||||
if impostorGroup then
|
||||
table.insert(filtered, groupName)
|
||||
local relinkZones = {}
|
||||
-- now iterate all units in that group, and remove their impostors
|
||||
for idy, theUnit in pairs(rawData.units) do
|
||||
if theUnit and theUnit.name then
|
||||
local impName = impostorGroup[theUnit.name]
|
||||
if not impName then
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("group <" .. groupName .. ">: no impostor for <" .. theUnit.name .. ">", 30)
|
||||
end
|
||||
else
|
||||
-- dead
|
||||
table.insert(deadUnits, theUnit.name)
|
||||
end
|
||||
-- destroy imp
|
||||
if impStat and impStat:isExist() then
|
||||
impStat:destroy()
|
||||
local impStat = StaticObject.getByName(impName)
|
||||
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
|
||||
-- dead
|
||||
table.insert(deadUnits, theUnit.name)
|
||||
end
|
||||
-- destroy imp
|
||||
if impStat and impStat:isExist() then
|
||||
impStat:destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- destroy impostor info
|
||||
theZone.myImpostors[groupName] = nil
|
||||
theZone.impostor = false
|
||||
-- destroy impostor info
|
||||
theZone.myImpostors[groupName] = nil
|
||||
theZone.impostor = false -- is this good?
|
||||
|
||||
-- now create the group
|
||||
if theZone.blinkTime <= 0 then
|
||||
-- immediate spawn
|
||||
--local newGroup = coalition.addGroup(ctry, cfxMX.catText2ID(cat), rawData)
|
||||
local newGroup = coalition.addGroup(ctry, cat, rawData)
|
||||
impostors.relinkZonesForGroup(relinkZones, newGroup)
|
||||
if theZone.trackWith and groupTracker.addGroupToTrackerNamed then
|
||||
-- add these groups to the group tracker
|
||||
if theZone.verbose or impostors.verbose then
|
||||
trigger.action.outText("+++ipst: attempting to add group <" .. newGroup:getName() .. "> to tracker <" .. theZone.trackWith .. ">", 30)
|
||||
-- now create the group
|
||||
if theZone.blinkTime <= 0 then
|
||||
-- immediate spawn
|
||||
--local newGroup = coalition.addGroup(ctry, cfxMX.catText2ID(cat), rawData)
|
||||
local newGroup = coalition.addGroup(ctry, cat, rawData)
|
||||
impostors.relinkZonesForGroup(relinkZones, newGroup)
|
||||
if theZone.trackWith and groupTracker.addGroupToTrackerNamed then
|
||||
-- add these groups to the group tracker
|
||||
if theZone.verbose or impostors.verbose then
|
||||
trigger.action.outText("+++ipst: attempting to add group <" .. newGroup:getName() .. "> to tracker <" .. theZone.trackWith .. ">", 30)
|
||||
end
|
||||
groupTracker.addGroupToTrackerNamed(newGroup, theZone.trackWith)
|
||||
end
|
||||
groupTracker.addGroupToTrackerNamed(newGroup, theZone.trackWith)
|
||||
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
|
||||
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)
|
||||
if theZone.verbose or impostors.verbose then
|
||||
trigger.action.outText("No impostor group named <" .. groupName .. "> any more, skipped.", 30)
|
||||
end
|
||||
-- theZone.myImpostors[groupName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
theZone.groupNames = filtered -- filter out non-existing
|
||||
-- now remove all dead units
|
||||
if theZone.blinkTime <= 0 then
|
||||
for idx, unitName in pairs(deadUnits) do
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
noGap = {}
|
||||
noGap.version = "1.0.1"
|
||||
noGap.version = "1.3.1"
|
||||
|
||||
noGap.verbose = false
|
||||
noGap.ignoreMe = "-ng" -- ignore altogether
|
||||
@ -21,10 +21,6 @@ noGap.requiredLibs = {
|
||||
Advantage: multiple-ship player groups look better, less code
|
||||
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
|
||||
|
||||
For multiplayer, NoGapGUI must run on the server (only server)
|
||||
@ -38,12 +34,18 @@ noGap.requiredLibs = {
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
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.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
|
||||
|
||||
function noGap.staticMXFromUnitMX(theGroup, theUnit)
|
||||
@ -57,6 +59,12 @@ function noGap.staticMXFromUnitMX(theGroup, theUnit)
|
||||
theStatic.type = theUnit.type
|
||||
theStatic.name = theUnit.name -- same as ME unit
|
||||
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
|
||||
end
|
||||
|
||||
@ -82,10 +90,20 @@ function noGap.isGroundStart(theGroup)
|
||||
if action == "Turning Point" then return false end
|
||||
if action == "Landing" 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)?
|
||||
local u1 = theGroup.units[1]
|
||||
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
|
||||
trigger.action.outText("noG: Player Group <" .. theGroup.name .. "> GROUND BASED: " .. action .. ", land type " .. sType, 30)
|
||||
end
|
||||
@ -107,7 +125,7 @@ function noGap.ignoreMXUnit(theUnit) -- DML-only
|
||||
return false
|
||||
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 spMatch = theUnit.name:sub(-#noGap.spIgnore) == noGap.spIgnore or group.name:sub(-#noGap.spIgnore) == noGap.spIgnore
|
||||
local zoneIgnore = noGap.ignoreMXUnit(theUnit)
|
||||
@ -139,7 +157,6 @@ function noGap.createStandInForMXData(group, theUnit) -- group, theUnit are MX d
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function noGap.fillGaps()
|
||||
@ -175,6 +192,7 @@ function noGap.turnOff()
|
||||
StaticObject.destroy(standIn)
|
||||
end
|
||||
noGap.standInUnits = {}
|
||||
noGap.running = false
|
||||
end
|
||||
|
||||
function noGap.turnOn()
|
||||
@ -183,6 +201,23 @@ function noGap.turnOn()
|
||||
end
|
||||
-- populate all empty (non-taken) slots with stand-ins
|
||||
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
|
||||
|
||||
--
|
||||
@ -193,11 +228,18 @@ function noGap:onEvent(event)
|
||||
if not event.id then return end
|
||||
if not event.initiator then return end
|
||||
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 (not theUnit.getPlayerName) or (not theUnit:getPlayerName()) then
|
||||
return
|
||||
end -- no player unit.
|
||||
|
||||
local uName = theUnit:getName()
|
||||
|
||||
if noGap.standInUnits[uName] then
|
||||
@ -208,11 +250,35 @@ function noGap:onEvent(event)
|
||||
trigger.action.outText("+++noG: removed static for <" ..uName .. ">, player inbound", 30)
|
||||
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
|
||||
-- we can reset it for next iteration
|
||||
trigger.action.setUserFlag("NG"..uName, 0)
|
||||
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
|
||||
|
||||
--
|
||||
@ -312,7 +378,7 @@ function noGap.update()
|
||||
end
|
||||
|
||||
--
|
||||
-- read stopGapZone (DML only)
|
||||
-- read noGap Zone (DML only)
|
||||
--
|
||||
function noGap.createNoGapZone(theZone)
|
||||
local ng = theZone:getBoolFromZoneProperty("noGap", true)
|
||||
@ -331,8 +397,11 @@ noGap.name = "noGapConfig" -- cfxZones compatibility here
|
||||
function noGap.readConfigZone(theZone)
|
||||
-- currently nothing to do
|
||||
noGap.verbose = theZone.verbose
|
||||
noGap.ssbEnabled = theZone:getBoolFromZoneProperty("ssb", true)
|
||||
noGap.enabled = theZone:getBoolFromZoneProperty("onStart", true)
|
||||
noGap.timeOut = theZone:getNumberFromZoneProperty("timeOut", 0) -- default to off
|
||||
noGap.noParking = theZone:getBoolFromZoneProperty("noParking", false)
|
||||
|
||||
if theZone:hasProperty("on?") then
|
||||
noGap.turnOnFlag = theZone:getStringFromZoneProperty("on?", "*<none>")
|
||||
noGap.lastTurnOnFlag = trigger.misc.getUserFlag(noGap.turnOnFlag)
|
||||
@ -350,6 +419,10 @@ function noGap.readConfigZone(theZone)
|
||||
trigger.action.outText("+++noG: turned off", 30)
|
||||
end
|
||||
end
|
||||
|
||||
noGap.refreshInterval = theZone:getNumberFromZoneProperty("refresh", -1) -- default: no refresh
|
||||
noGap.kickTheDead = theZone:getBoolFromZoneProperty("kickDead", true)
|
||||
noGap.allNeutral = theZone:getBoolFromZoneProperty("allNeutral", false)
|
||||
end
|
||||
|
||||
--
|
||||
@ -394,6 +467,11 @@ function noGap.start()
|
||||
-- start update in 1 second
|
||||
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!
|
||||
local mp = " (SP - <" .. sgDetect .. ">)"
|
||||
if sgDetect > 0 then mp = " -- MP GUI Detected (" .. sgDetect .. ")!" end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
radioMenu = {}
|
||||
radioMenu.version = "4.0.1"
|
||||
radioMenu.version = "4.0.2"
|
||||
radioMenu.verbose = false
|
||||
radioMenu.ups = 1
|
||||
radioMenu.requiredLibs = {
|
||||
@ -20,6 +20,7 @@ radioMenu.lateGroups = {} -- dict by ID
|
||||
4.0.0 - added infrastructure for dynamic players (DCS 2.9.6)
|
||||
- added dynamic player support
|
||||
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)
|
||||
@ -770,7 +771,11 @@ function radioMenu.doMenuX(args)
|
||||
radioMenu.radioOutMsg(ack, gid, theZone)
|
||||
end
|
||||
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
|
||||
|
||||
|
||||
@ -1,107 +1,144 @@
|
||||
raiseFlag = {}
|
||||
raiseFlag.version = "2.0.0"
|
||||
raiseFlag.verbose = false
|
||||
raiseFlag.version = "3.0.0"
|
||||
raiseFlag.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"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
|
||||
1.0.0 - initial release
|
||||
1.0.1 - synonym "raiseFlag!"
|
||||
1.1.0 - DML update
|
||||
1.2.0 - Watchflag update
|
||||
1.2.1 - support for 'inc', 'dec', 'flip'
|
||||
2.0.0 - dmlZones
|
||||
- full method support
|
||||
- full DML upgrade
|
||||
- method attribute (synonym to 'value')
|
||||
3.0.0 - switched to polling
|
||||
- remains# attribute
|
||||
- support for persistence
|
||||
- zone-individual verbosity
|
||||
- code cleanup, removed deprecated attributes
|
||||
--]]--
|
||||
|
||||
function raiseFlag.addRaiseFlag(theZone)
|
||||
table.insert(raiseFlag.flags, theZone)
|
||||
raiseFlag.flags[theZone.name] = theZone
|
||||
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
|
||||
--
|
||||
function raiseFlag.createRaiseFlagWithZone(theZone)
|
||||
-- get flag from faiseFlag itself
|
||||
if theZone:hasProperty("raiseFlag") then
|
||||
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.raiseFlag = theZone:getStringFromZoneProperty("raiseFlag!", "<none>") -- the flag to raise
|
||||
theZone.flagValue = theZone:getStringFromZoneProperty("method", "inc")
|
||||
theZone.flagValue = theZone.flagValue:lower()
|
||||
theZone.minAfterTime, theZone.maxAfterTime = theZone:getPositiveRangeFromZoneProperty("afterTime", -1)
|
||||
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
if theZone:hasProperty("raiseTriggerMethod") then
|
||||
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty("raiseTriggerMethod", "change")
|
||||
end
|
||||
|
||||
if theZone:hasProperty("stopFlag?") then
|
||||
theZone.triggerStopFlag = theZone:getStringFromZoneProperty( "stopFlag?", "none")
|
||||
theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopFlag) -- save last value
|
||||
end
|
||||
theZone.scheduleID = nil
|
||||
if theZone:hasProperty("timeLeft#") then
|
||||
theZone.timeLeft = theZone:getStringFromZoneProperty("timeLeft#", "none")
|
||||
end
|
||||
theZone.raiseStopped = false
|
||||
|
||||
-- now simply schedule for invocation
|
||||
local args = {}
|
||||
args.theZone = theZone
|
||||
if theZone.minAfterTime < 1 then
|
||||
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + 0.5)
|
||||
theZone.deadLine = -1 -- will always trigger
|
||||
else
|
||||
local delay = cfxZones.randomDelayFromPositiveRange(theZone.minAfterTime, theZone.maxAfterTime)
|
||||
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + delay)
|
||||
theZone.deadLine = timer.getTime() + delay
|
||||
end
|
||||
end
|
||||
|
||||
function raiseFlag.triggered(args)
|
||||
local theZone = args.theZone
|
||||
if theZone.raiseStopped then return end
|
||||
-- if we get here, we aren't stopped and do the flag pull
|
||||
function raiseFlag.triggered(theZone)
|
||||
if theZone.raiseStopped then return end -- should be filtered
|
||||
local command = theZone.flagValue
|
||||
theZone:pollFlag(theZone.raiseFlag, command)
|
||||
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
|
||||
|
||||
--
|
||||
-- update
|
||||
--
|
||||
function raiseFlag.update()
|
||||
-- call me in a second to poll triggers
|
||||
timer.scheduleFunction(raiseFlag.update, {}, timer.getTime() + 1)
|
||||
for idx, aZone in pairs(raiseFlag.flags) do
|
||||
-- make sure to re-start before reading time limit
|
||||
if aZone:testZoneFlag(aZone.triggerStopFlag, aZone.raiseTriggerMethod, "lastTriggerStopValue") then
|
||||
theZone.raiseStopped = true -- we are done, no flag!
|
||||
function raiseFlag.update(firstUpdate)
|
||||
local now = timer.getTime()
|
||||
timer.scheduleFunction(raiseFlag.update, false, now + 1) -- we always beat on .5 offset!
|
||||
local filtered = {}
|
||||
for zName, theZone in pairs(raiseFlag.flags) do
|
||||
-- see if this timer has run out
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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!
|
||||
--
|
||||
@ -117,39 +154,30 @@ function raiseFlag.readConfigZone()
|
||||
end
|
||||
|
||||
function raiseFlag.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx raise flag requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx Raise Flag", raiseFlag.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
if not dcsCommon.libCheck("cfx Raise Flag", raiseFlag.requiredLibs)then return false end
|
||||
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!")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
raiseFlag.createRaiseFlagWithZone(aZone) -- process attributes
|
||||
raiseFlag.addRaiseFlag(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update
|
||||
raiseFlag.update()
|
||||
|
||||
if persistence then
|
||||
callbacks = {}
|
||||
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)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's go!
|
||||
if not raiseFlag.start() then
|
||||
trigger.action.outText("cfx Raise Flag aborted: missing libraries", 30)
|
||||
raiseFlag = nil
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
slotty = {}
|
||||
slotty.version = "1.1.0"
|
||||
slotty.version = "1.2.0"
|
||||
--[[--
|
||||
Single-player slot blocking and slot blocking fallback
|
||||
for multiplayer when SSB is not installed on server.
|
||||
@ -12,7 +12,7 @@ slotty.version = "1.1.0"
|
||||
Version history
|
||||
1.0.0 - Initial version
|
||||
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
|
||||
|
||||
--]]--
|
||||
|
||||
@ -58,7 +58,7 @@ stopGap.requiredLibs = {
|
||||
required
|
||||
1.3.0 - carriers in shallow waters also no longer handled as viable
|
||||
- noParking option
|
||||
|
||||
1.3.1 - stronger guards on refresh
|
||||
--]]--
|
||||
|
||||
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
|
||||
-- re-schedule invocation
|
||||
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
|
||||
stopGap.turnOff() -- kill all statics
|
||||
-- turn back on in half a second
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user