mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.1.2
More persistence
This commit is contained in:
parent
7ad32b89c4
commit
78ff9a10e0
Binary file not shown.
Binary file not shown.
@ -451,7 +451,7 @@ function FARPZones.saveData()
|
||||
-- iterate all farp data and put them into a container each
|
||||
for theZone, theFARP in pairs(FARPZones.allFARPZones) do
|
||||
fName = theZone.name
|
||||
trigger.action.outText("frpZ persistence: processing FARP <" .. fName .. ">", 30)
|
||||
--trigger.action.outText("frpZ persistence: processing FARP <" .. fName .. ">", 30)
|
||||
local fData = {}
|
||||
fData.owner = theFARP.owner
|
||||
fData.defenderData = dcsCommon.clone(theFARP.defenderData)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
rndFlags = {}
|
||||
rndFlags.version = "1.3.2"
|
||||
rndFlags.version = "1.4.0"
|
||||
rndFlags.verbose = false
|
||||
rndFlags.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -30,6 +30,8 @@ rndFlags.requiredLibs = {
|
||||
- added 'rndDone!' flag
|
||||
- rndMethod defaults to "inc"
|
||||
1.3.2 - moved flagArrayFromString to dcsCommon
|
||||
- minor clean-up
|
||||
1.4.0 - persistence
|
||||
|
||||
--]]
|
||||
|
||||
@ -39,60 +41,16 @@ function rndFlags.addRNDZone(aZone)
|
||||
table.insert(rndFlags.rndGen, aZone)
|
||||
end
|
||||
|
||||
function rndFlags.getRNDByName(aName)
|
||||
for idx, theRND in pairs(rndFlags.rndGen) do
|
||||
if theRND.name == aName then return theRND end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function rndFlags.flagArrayFromString(inString)
|
||||
return dcsCommon.flagArrayFromString(inString, rndFlags.verbose)
|
||||
--[[--
|
||||
if string.len(inString) < 1 then
|
||||
trigger.action.outText("+++RND: empty flags", 30)
|
||||
return {}
|
||||
end
|
||||
if rndFlags.verbose then
|
||||
trigger.action.outText("+++RND: processing <" .. inString .. ">", 30)
|
||||
end
|
||||
|
||||
local flags = {}
|
||||
local rawElements = dcsCommon.splitString(inString, ",")
|
||||
-- go over all elements
|
||||
for idx, anElement in pairs(rawElements) do
|
||||
if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then
|
||||
-- interpret this as a range
|
||||
local theRange = dcsCommon.splitString(anElement, "-")
|
||||
local lowerBound = theRange[1]
|
||||
lowerBound = tonumber(lowerBound)
|
||||
local upperBound = theRange[2]
|
||||
upperBound = tonumber(upperBound)
|
||||
if lowerBound and upperBound then
|
||||
-- swap if wrong order
|
||||
if lowerBound > upperBound then
|
||||
local temp = upperBound
|
||||
upperBound = lowerBound
|
||||
lowerBound = temp
|
||||
end
|
||||
-- now add add numbers to flags
|
||||
for f=lowerBound, upperBound do
|
||||
table.insert(flags, f)
|
||||
|
||||
end
|
||||
else
|
||||
-- bounds illegal
|
||||
trigger.action.outText("+++RND: ignored range <" .. anElement .. "> (range)", 30)
|
||||
end
|
||||
else
|
||||
-- single number
|
||||
f = dcsCommon.trim(anElement) -- DML flag upgrade: accept strings tonumber(anElement)
|
||||
if f then
|
||||
table.insert(flags, f)
|
||||
|
||||
else
|
||||
trigger.action.outText("+++RND: ignored element <" .. anElement .. "> (single)", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
if rndFlags.verbose then
|
||||
trigger.action.outText("+++RND: <" .. #flags .. "> flags total", 30)
|
||||
end
|
||||
return flags
|
||||
--]]--
|
||||
end
|
||||
|
||||
--
|
||||
@ -214,7 +172,6 @@ function rndFlags.fire(theZone)
|
||||
if rndFlags.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++RND: RND " .. theZone.name .. " ran out of flags. Will fire 'done' instead ", 30)
|
||||
end
|
||||
|
||||
if theZone.doneFlag then
|
||||
cfxZones.pollFlag(theZone.doneFlag, theZone.rndMethod, theZone)
|
||||
end
|
||||
@ -281,14 +238,66 @@ end
|
||||
function rndFlags.startCycle()
|
||||
for idx, theZone in pairs(rndFlags.rndGen) do
|
||||
if theZone.onStart then
|
||||
if rndFlags.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++RND: starting " .. theZone.name, 30)
|
||||
if theZone.isStarted then
|
||||
-- suppressed by persistence
|
||||
else
|
||||
if rndFlags.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++RND: starting " .. theZone.name, 30)
|
||||
end
|
||||
rndFlags.fire(theZone)
|
||||
end
|
||||
rndFlags.fire(theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Load / Save data
|
||||
--
|
||||
function rndFlags.saveData()
|
||||
local theData = {}
|
||||
local allRND = {}
|
||||
for idx, theRND in pairs(rndFlags.rndGen) do
|
||||
local theName = theRND.name
|
||||
local rndData = {}
|
||||
-- save data for this RND
|
||||
rndData.myFlags = dcsCommon.clone(theRND.myFlags)
|
||||
allRND[theName] = rndData
|
||||
end
|
||||
theData.allRND = allRND
|
||||
|
||||
return theData
|
||||
end
|
||||
|
||||
function rndFlags.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("rndFlags")
|
||||
if not theData then
|
||||
if rndFlags.verbose then
|
||||
trigger.action.outText("+++RND Persistence: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local allRND = theData.allRND
|
||||
if not allRND then
|
||||
if rndFlags.verbose then
|
||||
trigger.action.outText("+++RND Persistence - no data, skipping", 30)
|
||||
end
|
||||
return
|
||||
end -- no data, no proccing
|
||||
|
||||
for theName, rData in pairs(allRND) do
|
||||
local theRND = rndFlags.getRNDByName(theName)
|
||||
if theRND then
|
||||
-- get current myFlags
|
||||
local myFlags = dcsCommon.clone(rData.myFlags)
|
||||
theRND.myFlags = myFlags
|
||||
theRND.isStarted = true -- we are initted, NO ON START
|
||||
else
|
||||
trigger.action.outText("+++RND persistecne: can't synch RND <" .. theName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- start module and read config
|
||||
@ -312,7 +321,7 @@ end
|
||||
|
||||
function rndFlags.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
if not dcsCommon then
|
||||
trigger.action.outText("RNDFlags requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
@ -345,6 +354,15 @@ function rndFlags.start()
|
||||
rndFlags.addRNDZone(aZone) -- remember it so we can smoke it
|
||||
end
|
||||
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = rndFlags.saveData
|
||||
persistence.registerModule("rndFlags", callbacks)
|
||||
-- now load my data
|
||||
rndFlags.loadData()
|
||||
end
|
||||
|
||||
-- start cycle
|
||||
timer.scheduleFunction(rndFlags.startCycle, {}, timer.getTime() + 0.25)
|
||||
|
||||
@ -361,4 +379,3 @@ if not rndFlags.start() then
|
||||
rndFlags = nil
|
||||
end
|
||||
|
||||
-- TODO: move flags to RND!, rename RND to RND!, deprecate flags!
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxGroundTroops = {}
|
||||
cfxGroundTroops.version = "1.7.6"
|
||||
cfxGroundTroops.version = "1.7.7"
|
||||
cfxGroundTroops.ups = 1
|
||||
cfxGroundTroops.verbose = false
|
||||
cfxGroundTroops.requiredLibs = {
|
||||
@ -61,6 +61,7 @@ cfxGroundTroops.deployedTroops = {}
|
||||
-- 1.7.4 - verbose flag, warnings suppressed
|
||||
-- 1.7.5 - some troop.group hardening with isExist()
|
||||
-- 1.7.6 - fixed switchToOffroad
|
||||
-- 1.7.7 - no longer case sensitive for orders
|
||||
|
||||
|
||||
-- an entry into the deployed troop has the following attributes
|
||||
@ -580,17 +581,17 @@ function cfxGroundTroops.updateTroops(troop)
|
||||
-- their order is removed, and the 'real' orders
|
||||
-- are revealed. For now, do nothing
|
||||
cfxGroundTroops.updateWait(troop)
|
||||
|
||||
--REMEMBER: LOWER CASE ONLY!
|
||||
elseif troop.orders == "guard" then
|
||||
cfxGroundTroops.updateGuards(troop)
|
||||
|
||||
elseif troop.orders == "attackOwnedZone" then
|
||||
elseif troop.orders == "attackownedzone" then
|
||||
cfxGroundTroops.updateZoneAttackers(troop)
|
||||
|
||||
elseif troop.orders == "laze" then
|
||||
cfxGroundTroops.updateLaze(troop)
|
||||
|
||||
elseif troop.orders == "attackZone" then
|
||||
elseif troop.orders == "attackzone" then
|
||||
cfxGroundTroops.updateAttackers(troop)
|
||||
|
||||
else
|
||||
@ -752,7 +753,7 @@ function cfxGroundTroops.updateSingleScheduled(params)
|
||||
|
||||
-- check max speed of group. if < 0.1 then note and increase
|
||||
-- speedWarning. if not, reset speed warning
|
||||
if troops.orders == "attackOwnedZone" and dcsCommon.getGroupMaxSpeed(troops.group) < 0.1 then
|
||||
if troops.orders == "attackownedzone" and dcsCommon.getGroupMaxSpeed(troops.group) < 0.1 then
|
||||
if not troops.speedWarning then troops.speedWarning = 0 end
|
||||
troops.speedWarning = troops.speedWarning + 1
|
||||
else
|
||||
@ -918,7 +919,7 @@ function cfxGroundTroops.getTroopReport(theSide, ignoreInfantry)
|
||||
if troop.side == theSide and troop.group:isExist() then
|
||||
local unitNum = troop.group:getSize()
|
||||
report = report .. "\n" .. troop.name .. " (".. unitNum .."): <" .. troop.orders .. ">"
|
||||
if troop.orders == "attackOwnedZone" then
|
||||
if troop.orders == "attackownedzone" then
|
||||
if troop.destination then
|
||||
report = report .. " move towards " .. troop.destination.name
|
||||
else
|
||||
@ -944,19 +945,16 @@ function cfxGroundTroops.createGroundTroops(inGroup, range, orders)
|
||||
local newTroops = {}
|
||||
if not orders then
|
||||
orders = "guard"
|
||||
--trigger.action.outText("+++ adding ground troops <".. inGroup:getName() ..">with default orders", 30)
|
||||
else
|
||||
--trigger.action.outText("+++ adding ground troops <".. inGroup:getName() ..">with orders " .. orders, 30)
|
||||
end
|
||||
if orders:lower() == "lase" then
|
||||
orders = "laze" -- we use WRONG spelling here, cause we're cool
|
||||
orders = "laze" -- we use WRONG spelling here, cause we're cool. yeah, right.
|
||||
end
|
||||
newTroops.insideDestination = false
|
||||
newTroops.unscheduleCount = 0 -- will count up as we aren't scheduled
|
||||
newTroops.speedWarning = 0
|
||||
newTroops.isOffroad = false -- if true, we switched to direct orders, not roads, after standstill
|
||||
newTroops.group = inGroup
|
||||
newTroops.orders = orders
|
||||
newTroops.orders = orders:lower()
|
||||
newTroops.coalition = inGroup:getCoalition()
|
||||
newTroops.side = newTroops.coalition -- because we'e been using both.
|
||||
newTroops.name = inGroup:getName()
|
||||
@ -972,7 +970,8 @@ function cfxGroundTroops.addGroundTroopsToPool(troops) -- troops MUST be a table
|
||||
trigger.action.outText("+++ adding ground troops with unsupported troop signature", 30)
|
||||
return
|
||||
end
|
||||
|
||||
if not troops.orders then troops.orders = "guard" end
|
||||
troops.orders = troops.orders:lower()
|
||||
troops.reschedule = true -- in case we use scheduled update
|
||||
-- we now add to internal array. this is worked on by all
|
||||
-- update meths, on scheduled upadtes, it is only used to
|
||||
|
||||
@ -146,8 +146,6 @@ end
|
||||
|
||||
function cfxGroups.start()
|
||||
cfxGroups.fetchAllGroupsFromDCS() -- read all groups from mission.
|
||||
-- cfxGroups.showAllGroups()
|
||||
|
||||
trigger.action.outText("cfxGroups version " .. cfxGroups.version .. " started", 30)
|
||||
return true
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "2.1.0"
|
||||
cfxHeloTroops.version = "2.2.0"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -18,6 +18,10 @@ cfxHeloTroops.pickupRange = 100 -- meters
|
||||
-- -- check spawner legality by types
|
||||
-- -- updated types to include 2.7.6 additions to infantry
|
||||
-- -- updated types to include stinger/manpads
|
||||
-- 2.2.0 -- minor maintenance (dcsCommon)
|
||||
-- -- (re?) connected readConfigZone (wtf?)
|
||||
-- -- persistence support
|
||||
-- -- made legalTroops entrirely optional and defer to dcsComon else
|
||||
|
||||
--
|
||||
-- cfxHeloTroops -- a module to pick up and drop infantry. Can be used with any helo,
|
||||
@ -40,10 +44,15 @@ cfxHeloTroops.requiredLibs = {
|
||||
|
||||
cfxHeloTroops.unitConfigs = {} -- all configs are stored by unit's name
|
||||
cfxHeloTroops.myEvents = {3, 4, 5} -- 3- takeoff, 4 - land, 5 - crash
|
||||
cfxHeloTroops.legalTroops = {"Soldier AK", "Infantry AK", "Infantry AK ver2", "Infantry AK ver3", "Infantry AK Ins", "Soldier M249", "Soldier M4 GRG", "Soldier M4", "Soldier RPG", "Paratrooper AKS-74", "Paratrooper RPG-16", "Stinger comm dsr", "Stinger comm", "Soldier stinger", "SA-18 Igla-S comm", "SA-18 Igla-S manpad", "Igla manpad INS", "SA-18 Igla comm", "SA-18 Igla manpad",}
|
||||
|
||||
-- legalTroops now optional, else check against dcsCommon.typeIsInfantry
|
||||
--cfxHeloTroops.legalTroops = {"Soldier AK", "Infantry AK", "Infantry AK ver2", "Infantry AK ver3", "Infantry AK Ins", "Soldier M249", "Soldier M4 GRG", "Soldier M4", "Soldier RPG", "Paratrooper AKS-74", "Paratrooper RPG-16", "Stinger comm dsr", "Stinger comm", "Soldier stinger", "SA-18 Igla-S comm", "SA-18 Igla-S manpad", "Igla manpad INS", "SA-18 Igla comm", "SA-18 Igla manpad",}
|
||||
|
||||
cfxHeloTroops.troopWeight = 100 -- kg average weight per trooper
|
||||
|
||||
-- persistence support
|
||||
cfxHeloTroops.deployedTroops = {}
|
||||
|
||||
function cfxHeloTroops.resetConfig(conf)
|
||||
conf.autoDrop = cfxHeloTroops.autoDrop --if true, will drop troops on-board upon touchdown
|
||||
conf.autoPickup = cfxHeloTroops.autoPickup -- if true will load nearest troops upon touchdown
|
||||
@ -113,7 +122,7 @@ function cfxHeloTroops.preProcessor(event)
|
||||
-- make sure it has an initiator
|
||||
if not event.initiator then return false end -- no initiator
|
||||
local theUnit = event.initiator
|
||||
if not cfxPlayer.isPlayerUnit(theUnit) then return false end -- not a player unit
|
||||
if not dcsCommon.isPlayerUnit(theUnit) then return false end -- not a player unit
|
||||
local cat = theUnit:getCategory()
|
||||
if cat ~= Group.Category.HELICOPTER then return false end
|
||||
|
||||
@ -414,9 +423,16 @@ function cfxHeloTroops.addGroundMenu(conf)
|
||||
local typeArray = dcsCommon.splitString(theTypes, ',')
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
local allLegal = true
|
||||
-- check agianst default (dcsCommon) or own definition (if exists)
|
||||
for idy, aType in pairs(typeArray) do
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then
|
||||
allLegal = false
|
||||
if cfxHeloTroops.legalTroops then
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then
|
||||
allLegal = false
|
||||
end
|
||||
else
|
||||
if not dcsCommon.typeIsInfantry(aType) then
|
||||
allLegal = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if allLegal then
|
||||
@ -503,9 +519,16 @@ function cfxHeloTroops.filterTroopsByType(unitsToLoad)
|
||||
local pass = true
|
||||
for iT, sT in pairs(aT) do
|
||||
-- check if this is a valid type
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, sT) then
|
||||
pass = false
|
||||
break
|
||||
if cfxHeloTroops.legalTroops then
|
||||
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, sT) then
|
||||
pass = false
|
||||
break
|
||||
end
|
||||
else
|
||||
if not dcsCommon.typeIsInfantry(sT) then
|
||||
pass = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if pass then
|
||||
@ -604,13 +627,21 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m ratius around choppa
|
||||
--local theCoalition = theUnit:getCountry() -- make it choppers country
|
||||
local theCoalition = theUnit:getGroup():getCoalition() -- make it choppers COALITION
|
||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
theCoalition,
|
||||
conf.troopsOnBoard.name, -- dcsCommon.uuid("Assault"), -- maybe use config name as loaded from the group
|
||||
chopperZone,
|
||||
unitTypes,
|
||||
conf.dropFormation,
|
||||
90)
|
||||
-- persistence management
|
||||
local troopData = {}
|
||||
troopData.groupData = theData
|
||||
troopData.orders = orders -- always set
|
||||
troopData.side = theCoalition
|
||||
troopData.range = range
|
||||
cfxHeloTroops.deployedTroops[theData.name] = troopData
|
||||
|
||||
local troop = cfxGroundTroops.createGroundTroops(theGroup, range, orders) -- use default range and orders
|
||||
-- instead of scheduling tasking in one second, we add to
|
||||
-- ground troops pool, and the troop pool manager will assign some enemies
|
||||
@ -663,8 +694,13 @@ function cfxHeloTroops.doLoadGroup(args)
|
||||
-- TODO: ensure compatibility with CSAR module
|
||||
group:destroy()
|
||||
|
||||
-- now immediately run a GC so this group is removed
|
||||
-- from any save data
|
||||
cfxHeloTroops.GC()
|
||||
|
||||
-- say so
|
||||
trigger.action.outTextForGroup(conf.id, "Team '".. conf.troopsOnBoard.name .."' aboard, ready to go!", 30)
|
||||
|
||||
-- reset menu
|
||||
cfxHeloTroops.removeComms(conf.unit)
|
||||
cfxHeloTroops.setCommsMenu(conf.unit)
|
||||
@ -748,6 +784,32 @@ function cfxHeloTroops.playerChangeEvent(evType, description, player, data)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Regular GC and housekeeping
|
||||
--
|
||||
function cfxHeloTroops.GC()
|
||||
-- GC run. remove all my dead remembered troops
|
||||
local filteredAttackers = {}
|
||||
local before = #cfxHeloTroops.deployedTroops
|
||||
for gName, gData in pairs (cfxHeloTroops.deployedTroops) do
|
||||
-- all we need to do is get the group of that name
|
||||
-- and if it still returns units we are fine
|
||||
local gameGroup = Group.getByName(gName)
|
||||
if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then
|
||||
filteredAttackers[gName] = gData
|
||||
end
|
||||
end
|
||||
cfxHeloTroops.deployedTroops = filteredAttackers
|
||||
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("helo troops GC ran: before <" .. before .. ">, after <" .. #cfxHeloTroops.deployedTroops .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function cfxHeloTroops.houseKeeping()
|
||||
timer.scheduleFunction(cfxHeloTroops.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes
|
||||
cfxHeloTroops.GC()
|
||||
end
|
||||
|
||||
--
|
||||
-- read config zone
|
||||
@ -757,7 +819,7 @@ function cfxHeloTroops.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("heloTroopsConfig")
|
||||
if not theZone then
|
||||
trigger.action.outText("+++heloT: no config zone!", 30)
|
||||
return
|
||||
theZone = cfxZones.createSimpleZone("heloTroopsConfig")
|
||||
end
|
||||
|
||||
cfxHeloTroops.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
@ -781,6 +843,60 @@ function cfxHeloTroops.readConfigZone()
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- Load / Save data
|
||||
--
|
||||
function cfxHeloTroops.saveData()
|
||||
local theData = {}
|
||||
local allTroopData = {}
|
||||
-- run a GC pre-emptively
|
||||
cfxHeloTroops.GC()
|
||||
-- now simply iterate and save all deployed troops
|
||||
for gName, gData in pairs(cfxHeloTroops.deployedTroops) do
|
||||
local sData = dcsCommon.clone(gData)
|
||||
dcsCommon.synchGroupData(sData.groupData)
|
||||
allTroopData[gName] = sData
|
||||
end
|
||||
theData.troops = allTroopData
|
||||
return theData
|
||||
end
|
||||
|
||||
function cfxHeloTroops.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("cfxHeloTroops")
|
||||
if not theData then
|
||||
if cfxHeloTroops.verbose then
|
||||
trigger.action.outText("+++heloT: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- simply spawn all troops that we have carried around and
|
||||
-- were still alive when we saved. Troops that were picked
|
||||
-- up by helos never made it to the save file
|
||||
local allTroopData = theData.troops
|
||||
for gName, gdTroop in pairs (allTroopData) do
|
||||
local gData = gdTroop.groupData
|
||||
local orders = gdTroop.orders
|
||||
local side = gdTroop.side
|
||||
local range = gdTroop.range
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
|
||||
-- now spawn, but first
|
||||
-- add to my own deployed queue so we can save later
|
||||
local gdClone = dcsCommon.clone(gdTroop)
|
||||
cfxHeloTroops.deployedTroops[gName] = gdClone
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
-- post-proccing for cfxGroundTroops
|
||||
|
||||
-- add to groundTroops
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders)
|
||||
cfxGroundTroops.addGroundTroopsToPool(newTroops)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Start
|
||||
--
|
||||
@ -792,6 +908,10 @@ function cfxHeloTroops.start()
|
||||
end
|
||||
|
||||
-- read config zone
|
||||
cfxHeloTroops.readConfigZone()
|
||||
|
||||
-- start housekeeping
|
||||
cfxHeloTroops.houseKeeping()
|
||||
|
||||
-- install callbacks for helo-relevant events
|
||||
dcsCommon.addEventHandler(cfxHeloTroops.somethingHappened, cfxHeloTroops.preProcessor, cfxHeloTroops.postProcessor)
|
||||
@ -807,6 +927,18 @@ function cfxHeloTroops.start()
|
||||
|
||||
cfxPlayer.addMonitor(cfxHeloTroops.playerChangeEvent)
|
||||
trigger.action.outText("cf/x Helo Troops v" .. cfxHeloTroops.version .. " started", 30)
|
||||
|
||||
-- now load all save data and populate map with troops that
|
||||
-- we deployed last save.
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cfxHeloTroops.saveData
|
||||
persistence.registerModule("cfxHeloTroops", callbacks)
|
||||
-- now load my data
|
||||
cfxHeloTroops.loadData()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxMX = {}
|
||||
cfxMX.version = "1.2.1"
|
||||
cfxMX.version = "1.2.2"
|
||||
cfxMX.verbose = false
|
||||
--[[--
|
||||
Mission data decoder. Access to ME-built mission structures
|
||||
@ -18,7 +18,9 @@ cfxMX.verbose = false
|
||||
- added references for allFixed, allHelo, allGround, allSea, allStatic
|
||||
1.2.1 - added countryByName
|
||||
- added linkByName
|
||||
|
||||
1.2.2 - fixed ctry bug in countryByName
|
||||
- playerGroupByName
|
||||
- playerUnitByName
|
||||
--]]--
|
||||
cfxMX.groupNamesByID = {}
|
||||
cfxMX.groupIDbyName = {}
|
||||
@ -29,7 +31,10 @@ cfxMX.allFixedByName = {}
|
||||
cfxMX.allHeloByName = {}
|
||||
cfxMX.allGroundByName = {}
|
||||
cfxMX.allSeaByName = {}
|
||||
cfxMX.allStaticByName ={}
|
||||
cfxMX.allStaticByName = {}
|
||||
|
||||
cfxMX.playerGroupByName = {} -- returns data only if a player is in group
|
||||
cfxMX.playerUnitByName = {} -- returns data only if this is a player unit
|
||||
|
||||
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
|
||||
if not fetchOriginal then fetchOriginal = false end
|
||||
@ -145,14 +150,11 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
|
||||
theStatic.groupId = group_data.groupId
|
||||
-- copy linked unit data
|
||||
theStatic.linkUnit = linkUnit
|
||||
|
||||
end
|
||||
return theStatic, category, countryID, groupName
|
||||
|
||||
end -- if name match
|
||||
end -- for all units
|
||||
end -- has groups
|
||||
|
||||
end -- is a static
|
||||
end --if has category data
|
||||
end --if plane, helo etc... category
|
||||
@ -207,7 +209,8 @@ function cfxMX.createCrossReferences()
|
||||
cfxMX.groupNamesByID[aID] = aName
|
||||
cfxMX.groupIDbyName[aName] = aID
|
||||
cfxMX.groupDataByName[aName] = group_data
|
||||
cfxMX.countryByName[aName] = cntry_id
|
||||
cfxMX.countryByName[aName] = countryID -- !!! was cntry_id
|
||||
|
||||
-- now make the type-specific xrefs
|
||||
if obj_type_name == "helicopter" then
|
||||
cfxMX.allHeloByName[aName] = group_data
|
||||
@ -223,7 +226,18 @@ function cfxMX.createCrossReferences()
|
||||
-- should be impossible, but still
|
||||
trigger.action.outText("+++MX: <" .. obj_type_name .. "> unknown type for <" .. aName .. ">", 30)
|
||||
end
|
||||
end
|
||||
-- now iterate all units in this group
|
||||
-- for player into
|
||||
for unit_num, unit_data in pairs(group_data.units) do
|
||||
if unit_data.skill then
|
||||
if unit_data.skill == "Client" or unit_data.skill == "Player" then
|
||||
-- player unit
|
||||
cfxMX.playerUnitByName[unit_data.name] = unit_data
|
||||
cfxMX.playerGroupByName[aName] = group_data -- inefficient, but works
|
||||
end -- if unit skill client
|
||||
end -- if has skill
|
||||
end -- for all units
|
||||
end -- for all groups
|
||||
end --if has category data
|
||||
end --if plane, helo etc... category
|
||||
end --for all objects in country
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxOwnedZones = {}
|
||||
cfxOwnedZones.version = "1.2.0"
|
||||
cfxOwnedZones.version = "1.2.1"
|
||||
cfxOwnedZones.verbose = false
|
||||
cfxOwnedZones.announcer = true
|
||||
cfxOwnedZones.name = "cfxOwnedZones"
|
||||
@ -45,6 +45,7 @@ cfxOwnedZones.name = "cfxOwnedZones"
|
||||
1.2.0 - support for persistence
|
||||
- conq+1 --> conquered!
|
||||
- no cfxGroundTroop bug (no delay)
|
||||
1.2.1 - fix in load to correctly re-establish all attackers for subsequent save
|
||||
|
||||
--]]--
|
||||
cfxOwnedZones.requiredLibs = {
|
||||
@ -797,6 +798,7 @@ end
|
||||
|
||||
function cfxOwnedZones.GC()
|
||||
-- GC run. remove all my dead remembered troops
|
||||
local before = #cfxOwnedZones.spawnedAttackers
|
||||
local filteredAttackers = {}
|
||||
for gName, gData in pairs (cfxOwnedZones.spawnedAttackers) do
|
||||
-- all we need to do is get the group of that name
|
||||
@ -807,6 +809,9 @@ function cfxOwnedZones.GC()
|
||||
end
|
||||
end
|
||||
cfxOwnedZones.spawnedAttackers = filteredAttackers
|
||||
if cfxOwnedZones.verbose then
|
||||
trigger.action.outText("owned zones GC ran: before <" .. before .. ">, after <" .. #cfxOwnedZones.spawnedAttackers .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function cfxOwnedZones.update()
|
||||
@ -967,7 +972,7 @@ function cfxOwnedZones.loadData()
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
-- add to my own attacker queue so we can save later
|
||||
local dClone = dcsCommon.clone(gData)
|
||||
local dClone = dcsCommon.clone(gdTroop)
|
||||
cfxOwnedZones.spawnedAttackers[gName] = dClone
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
if cfxGroundTroops then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxPlayerScore = {}
|
||||
cfxPlayerScore.version = "1.3.1"
|
||||
cfxPlayerScore.version = "1.4.0"
|
||||
cfxPlayerScore.badSound = "Death BRASS.wav"
|
||||
cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
|
||||
cfxPlayerScore.announcer = true
|
||||
@ -23,12 +23,15 @@ cfxPlayerScore.announcer = true
|
||||
number that is given under OBJECT ID when
|
||||
using assign as...
|
||||
1.3.1 - isStaticObject() to better detect buildings after Match 22 patch
|
||||
1.3.2 - corrected ground default score
|
||||
- removed dependency to cfxPlayer
|
||||
1.4.0 - persistence support
|
||||
- better unit-->static switch support for generic type kill
|
||||
|
||||
|
||||
--]]--
|
||||
cfxPlayerScore.requiredLibs = {
|
||||
"dcsCommon", -- this is doing score keeping
|
||||
"cfxPlayer", -- player events, comms
|
||||
"cfxZones", -- zones for config
|
||||
}
|
||||
cfxPlayerScore.playerScore = {} -- init to empty
|
||||
@ -46,14 +49,15 @@ cfxPlayerScore.typeScore = {}
|
||||
--
|
||||
cfxPlayerScore.aircraft = 50
|
||||
cfxPlayerScore.helo = 40
|
||||
cfxPlayer.ground = 10
|
||||
cfxPlayerScore.ground = 10
|
||||
cfxPlayerScore.ship = 80
|
||||
cfxPlayerScore.train = 5
|
||||
|
||||
|
||||
function cfxPlayerScore.cat2BaseScore(inCat)
|
||||
if inCat == 0 then return cfxPlayerScore.aircraft end -- airplane
|
||||
if inCat == 1 then return cfxPlayerScore.helo end -- helo
|
||||
if inCat == 2 then return cfxPlayer.ground end -- ground
|
||||
if inCat == 2 then return cfxPlayerScore.ground end -- ground
|
||||
if inCat == 3 then return cfxPlayerScore.ship end -- ship
|
||||
if inCat == 4 then return cfxPlayerScore.train end -- train
|
||||
|
||||
@ -70,6 +74,9 @@ function cfxPlayerScore.object2score(inVictim) -- does not have group
|
||||
inName = tostring(inName)
|
||||
end
|
||||
|
||||
-- now, since 2.7x DCS turns units into static objects for
|
||||
-- cooking off, so first thing we need to do is do a name check
|
||||
|
||||
local objectScore = cfxPlayerScore.typeScore[inName]
|
||||
if not objectScore then
|
||||
-- try the type desc
|
||||
@ -80,6 +87,20 @@ function cfxPlayerScore.object2score(inVictim) -- does not have group
|
||||
if type(objectScore) == "string" then
|
||||
objectScore = tonumber(objectScore)
|
||||
end
|
||||
|
||||
if objectScore then return objectScore end
|
||||
|
||||
-- we now try and get the general type of the killed object
|
||||
local desc = inVictim:getDesc() -- Object.getDesc(inVictim)
|
||||
local attributes = desc.attributes
|
||||
if attributes then
|
||||
if attributes["Vehicles"] or attributes["Ground vehicles"] or attributes["Ground Units"] then return cfxPlayerScore.ground end
|
||||
if attributes["Helicopters"] then return cfxPlayerScore.helo end
|
||||
if attributes["Planes"] then return cfxPlayerScore.aircraft end
|
||||
if attributes["Ships"] then return cfxPlayerScore.ship end
|
||||
-- trains can't be detected
|
||||
end
|
||||
|
||||
if not objectScore then return 0 end
|
||||
return objectScore
|
||||
end
|
||||
@ -224,7 +245,7 @@ function cfxPlayerScore.preProcessor(theEvent)
|
||||
return false
|
||||
end
|
||||
|
||||
local wasPlayer = cfxPlayer.isPlayerUnit(killer)
|
||||
local wasPlayer = dcsCommon.isPlayerUnit(killer)
|
||||
return wasPlayer
|
||||
end
|
||||
return false
|
||||
@ -245,7 +266,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
-- we are only getting called when and if
|
||||
-- a kill occured and killer was a player
|
||||
-- and target exists
|
||||
|
||||
-- trigger.action.outText("KILL EVENT", 30)
|
||||
local killer = theEvent.initiator
|
||||
local killerName = killer:getPlayerName()
|
||||
if not killerName then killerName = "<nil>" end
|
||||
@ -255,13 +276,14 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
local victim = theEvent.target
|
||||
|
||||
-- was it a player kill?
|
||||
local pk = cfxPlayer.isPlayerUnit(victim)
|
||||
local pk = dcsCommon.isPlayerUnit(victim)
|
||||
|
||||
-- was it a scenery object?
|
||||
local wasBuilding = dcsCommon.isSceneryObject(victim)
|
||||
if wasBuilding then
|
||||
-- these objects have no coalition; we simply award the score if
|
||||
-- it exists in look-up table.
|
||||
--trigger.action.outText("KILL SCENERY", 30)
|
||||
local staticScore = cfxPlayerScore.object2score(victim)
|
||||
if staticScore > 0 then
|
||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||
@ -286,6 +308,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
local staticName = victim:getName() -- on statics, this returns
|
||||
-- name as entered in TOP LINE
|
||||
local staticScore = cfxPlayerScore.object2score(victim)
|
||||
-- trigger.action.outText("KILL STATIC with score " .. staticScore, 30)
|
||||
if staticScore > 0 then
|
||||
-- this was a named static, return the score - unless our own
|
||||
if fraternicide then
|
||||
@ -295,6 +318,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||
end
|
||||
staticScore = scoreMod * staticScore
|
||||
cfxPlayerScore.logKillForPlayer(killerName, victim)
|
||||
cfxPlayerScore.awardScoreTo(killSide, staticScore, killerName)
|
||||
else
|
||||
-- no score, no mentions
|
||||
@ -368,7 +392,7 @@ function cfxPlayerScore.readConfigZone(theZone)
|
||||
-- default scores
|
||||
cfxPlayerScore.aircraft = cfxZones.getNumberFromZoneProperty(theZone, "aircraft", 50)
|
||||
cfxPlayerScore.helo = cfxZones.getNumberFromZoneProperty(theZone, "helo", 40)
|
||||
cfxPlayer.ground = cfxZones.getNumberFromZoneProperty(theZone, "ground", 10)
|
||||
cfxPlayerScore.ground = cfxZones.getNumberFromZoneProperty(theZone, "ground", 10)
|
||||
cfxPlayerScore.ship = cfxZones.getNumberFromZoneProperty(theZone, "ship", 80)
|
||||
cfxPlayerScore.train = cfxZones.getNumberFromZoneProperty(theZone, "train", 5)
|
||||
|
||||
@ -382,6 +406,36 @@ function cfxPlayerScore.readConfigZone(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- load / save
|
||||
--
|
||||
function cfxPlayerScore.saveData()
|
||||
local theData = {}
|
||||
local theScore = dcsCommon.clone(cfxPlayerScore.playerScore)
|
||||
theData.theScore = theScore
|
||||
return theData
|
||||
end
|
||||
|
||||
function cfxPlayerScore.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("cfxPlayerScore")
|
||||
if not theData then
|
||||
if cfxPlayerScore.verbose then
|
||||
trigger.action.outText("+++playerscore: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local theScore = theData.theScore
|
||||
cfxPlayerScore.playerScore = theScore
|
||||
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- start
|
||||
--
|
||||
|
||||
function cfxPlayerScore.start()
|
||||
if not dcsCommon.libCheck("cfx Player Score",
|
||||
cfxPlayerScore.requiredLibs)
|
||||
@ -413,6 +467,18 @@ function cfxPlayerScore.start()
|
||||
dcsCommon.addEventHandler(cfxPlayerScore.killDetected,
|
||||
cfxPlayerScore.preProcessor,
|
||||
cfxPlayerScore.postProcessor)
|
||||
|
||||
-- now load all save data and populate map with troops that
|
||||
-- we deployed last save.
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cfxPlayerScore.saveData
|
||||
persistence.registerModule("cfxPlayerScore", callbacks)
|
||||
-- now load my data
|
||||
cfxPlayerScore.loadData()
|
||||
end
|
||||
|
||||
trigger.action.outText("cfxPlayerScore v" .. cfxPlayerScore.version .. " started", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxSSBClient = {}
|
||||
cfxSSBClient.version = "2.0.3"
|
||||
cfxSSBClient.version = "2.1.0"
|
||||
cfxSSBClient.verbose = false
|
||||
cfxSSBClient.singleUse = false -- set to true to block crashed planes
|
||||
-- NOTE: singleUse (true) requires SSB to disable immediate respawn after kick
|
||||
@ -40,6 +40,9 @@ Version History
|
||||
- added verbosity
|
||||
2.0.3 - getPlayerName nil-trap on cloned player planes guard
|
||||
in onEvent
|
||||
2.1.0 - slotState
|
||||
- persistence
|
||||
|
||||
|
||||
WHAT IT IS
|
||||
SSB Client is a small script that forms the client-side counterpart to
|
||||
@ -88,6 +91,7 @@ cfxSSBClient.playerGroups = {}
|
||||
cfxSSBClient.closedAirfields = {} -- list that closes airfields for any aircrafts
|
||||
cfxSSBClient.playerPlanes = {} -- names of units that a player is flying
|
||||
cfxSSBClient.crashedGroups = {} -- names of groups to block after crash of their player-flown plane
|
||||
cfxSSBClient.slotState = {} -- keeps a record of which slot has which value. For persistence and debugging
|
||||
|
||||
|
||||
function cfxSSBClient.closeAirfieldNamed(name)
|
||||
@ -191,6 +195,7 @@ function cfxSSBClient.setSlotAccessForGroup(theGroup)
|
||||
end
|
||||
-- set the ssb flag for this group so the server can see it
|
||||
trigger.action.setUserFlag(theName, blockState)
|
||||
cfxSSBClient.slotState[theName] = blockState
|
||||
if cfxSSBClient.verbose then
|
||||
trigger.action.outText("+++SSB: group ".. theName .. ": " .. comment, 30)
|
||||
end
|
||||
@ -312,7 +317,7 @@ function cfxSSBClient:onEvent(event)
|
||||
|
||||
-- block this slot.
|
||||
trigger.action.setUserFlag(gName, cfxSSBClient.disabledFlagValue)
|
||||
|
||||
cfxSSBClient.slotState[gName] = cfxSSBClient.disabledFlagValue
|
||||
-- remember this plane to not re-enable if
|
||||
-- airfield changes hands later
|
||||
cfxSSBClient.crashedGroups[gName] = thePilot -- set to crash pilot
|
||||
@ -425,6 +430,48 @@ function cfxSSBClient.readConfigZone()
|
||||
cfxSSBClient.disabledFlagValue = cfxZones.getNumberFromZoneProperty(theZone, "disabledFlagValue", cfxSSBClient.enabledFlagValue + 100)
|
||||
end
|
||||
|
||||
--
|
||||
-- load / save
|
||||
--
|
||||
function cfxSSBClient.saveData()
|
||||
local theData = {}
|
||||
local states = dcsCommon.clone(cfxSSBClient.slotState)
|
||||
local crashed = dcsCommon.clone(cfxSSBClient.crashedGroups)
|
||||
theData.states = states
|
||||
theData.crashed = crashed
|
||||
return theData
|
||||
end
|
||||
|
||||
function cfxSSBClient.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("cfxSSBClient")
|
||||
if not theData then
|
||||
if cfxSSBClient.verbose then
|
||||
trigger.action.outText("+++cfxSSB: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
cfxSSBClient.slotState = theData.states
|
||||
if not cfxSSBClient.slotState then
|
||||
trigger.action.outText("SSBClient: nil slot state on load", 30)
|
||||
cfxSSBClient.slotState = {}
|
||||
end
|
||||
for slot, state in pairs (cfxSSBClient.slotState) do
|
||||
trigger.action.setUserFlag(slot, state)
|
||||
if state > 0 and cfxSSBClient.verbose then
|
||||
trigger.action.outText("SSB: blocked <" .. slot .. "> on load", 30)
|
||||
end
|
||||
end
|
||||
|
||||
cfxSSBClient.crashedGroups = theData.crashed
|
||||
if not cfxSSBClient.crashedGroups then
|
||||
cfxSSBClient.crashedGroups = {}
|
||||
trigger.action.outText("SSBClient: nil crashers on load", 30)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- start
|
||||
--
|
||||
@ -455,6 +502,16 @@ function cfxSSBClient.start()
|
||||
-- now turn on ssb
|
||||
trigger.action.setUserFlag("SSB",100)
|
||||
|
||||
-- persistence: load states
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cfxSSBClient.saveData
|
||||
persistence.registerModule("cfxSSBClient", callbacks)
|
||||
-- now load my data
|
||||
cfxSSBClient.loadData()
|
||||
end
|
||||
|
||||
-- say hi!
|
||||
trigger.action.outText("cfxSSBClient v".. cfxSSBClient.version .. " running, SBB enabled", 30)
|
||||
|
||||
|
||||
@ -1,15 +1,21 @@
|
||||
cfxSpawnZones = {}
|
||||
cfxSpawnZones.version = "1.6.0"
|
||||
cfxSpawnZones.version = "1.7.0"
|
||||
cfxSpawnZones.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
-- pretty stupid to check for this since we
|
||||
-- need common to invoke the check, but anyway
|
||||
"cfxZones", -- Zones, of course
|
||||
"cfxCommander", -- to make troops do stuff
|
||||
"cfxGroundTroops", -- generic data module for weight
|
||||
"cfxGroundTroops", -- for ordering then around
|
||||
}
|
||||
cfxSpawnZones.ups = 1
|
||||
cfxSpawnZones.verbose = false
|
||||
|
||||
-- persistence: all groups we ever spawned.
|
||||
-- is regularly GC'd
|
||||
|
||||
cfxSpawnZones.spawnedGroups = {}
|
||||
|
||||
--
|
||||
-- Zones that conform with this requirements spawn toops automatically
|
||||
-- *** DOES NOT EXTEND ZONES *** LINKED OWNER via masterOwner ***
|
||||
@ -54,6 +60,7 @@ cfxSpawnZones.verbose = false
|
||||
-- 1.5.2 - activate?, pause? flag
|
||||
-- 1.5.3 - spawn?, spawnUnits? flags
|
||||
-- 1.6.0 - trackwith interface for group tracker
|
||||
-- 1.7.0 - persistence support
|
||||
--
|
||||
-- new version requires cfxGroundTroops, where they are
|
||||
--
|
||||
@ -203,12 +210,14 @@ function cfxSpawnZones.createSpawner(inZone)
|
||||
theSpawner.formation = "circle_out"
|
||||
theSpawner.formation = cfxZones.getStringFromZoneProperty(inZone, "formation", "circle_out")
|
||||
theSpawner.paused = cfxZones.getBoolFromZoneProperty(inZone, "paused", false)
|
||||
theSpawner.orders = cfxZones.getStringFromZoneProperty(inZone, "orders", "guard")
|
||||
theSpawner.orders = cfxZones.getStringFromZoneProperty(inZone, "orders", "guard"):lower()
|
||||
--theSpawner.orders = cfxZones.getZoneProperty(inZone, "orders")
|
||||
-- used to assign special orders, default is 'guard', use "laze" to make them laze targets. can be 'wait-' which may auto-convert to 'guard' after pick-up by helo, to be handled outside.
|
||||
-- use "train" to tell them to HOLD WEAPONS, don't move and don't participate in loop, so we have in effect target dummies
|
||||
-- can also use order 'dummy' or 'dummies' to switch to train
|
||||
if theSpawner.orders:lower() == "dummy" or theSpawner.orders:lower() == "dummies" then theSpawner.orders = "train" end
|
||||
if theSpawner.orders:lower() == "training" then theSpawner.orders = "train" end
|
||||
|
||||
|
||||
theSpawner.range = cfxZones.getNumberFromZoneProperty(inZone, "range", 300) -- if we have a range, for example enemy detection for Lasing or engage range
|
||||
theSpawner.maxSpawns = cfxZones.getNumberFromZoneProperty(inZone, "maxSpawns", -1) -- if there is a limit on how many troops can spawn. -1 = endless spawns
|
||||
@ -238,6 +247,7 @@ end
|
||||
|
||||
function cfxSpawnZones.getSpawnerForZoneNamed(aName)
|
||||
local aZone = cfxZones.getZoneByName(aName)
|
||||
if not aZone then return nil end
|
||||
return cfxSpawnZones.getSpawnerForZone(aZone)
|
||||
end
|
||||
|
||||
@ -331,7 +341,7 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
local theCoalition = coalition.getCountryCoalition(theCountry)
|
||||
-- trigger.action.outText("+++ spawn: coal <" .. theCoalition .. "> from country <" .. theCountry .. ">", 30)
|
||||
|
||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
theCoalition,
|
||||
aSpawner.baseName .. "-" .. aSpawner.count, -- must be unique
|
||||
aSpawner.zone,
|
||||
@ -340,15 +350,26 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
aSpawner.heading)
|
||||
aSpawner.theSpawn = theGroup
|
||||
aSpawner.count = aSpawner.count + 1
|
||||
-- we may also want to add this to auto ground troops pool
|
||||
-- we not only want to, we absolutely need to in order
|
||||
-- to make this work.
|
||||
|
||||
-- isnert into collector for persistence
|
||||
local troopData = {}
|
||||
troopData.groupData = theData
|
||||
troopData.orders = aSpawner.orders -- always set
|
||||
troopData.side = theCoalition
|
||||
troopData.target = aSpawner.target -- can be nil!
|
||||
troopData.tracker = theZone.trackWith -- taken from ZONE!!, can be nil
|
||||
troopData.range = aSpawner.range
|
||||
cfxSpawnZones.spawnedGroups[theData.name] = troopData
|
||||
|
||||
if aSpawner.orders and (
|
||||
aSpawner.orders:lower() == "training" or
|
||||
aSpawner.orders:lower() == "train" )
|
||||
then
|
||||
-- make them ROE "HOLD"
|
||||
-- remember to do this in persistence as well!
|
||||
-- they aren't fed to cfxGroundTroops.
|
||||
-- we should update groundTroops to simply
|
||||
-- drop those with 'train' or 'training'
|
||||
cfxCommander.scheduleOptionForGroup(
|
||||
theGroup,
|
||||
AI.Option.Ground.id.ROE,
|
||||
@ -434,6 +455,23 @@ end
|
||||
--
|
||||
-- U P D A T E
|
||||
--
|
||||
function cfxSpawnZones.GC()
|
||||
-- GC run. remove all my dead remembered troops
|
||||
local filteredAttackers = {}
|
||||
local before = #cfxSpawnZones.spawnedGroups
|
||||
for gName, gData in pairs (cfxSpawnZones.spawnedGroups) do
|
||||
-- all we need to do is get the group of that name
|
||||
-- and if it still returns units we are fine
|
||||
local gameGroup = Group.getByName(gName)
|
||||
if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then
|
||||
filteredAttackers[gName] = gData
|
||||
end
|
||||
end
|
||||
cfxSpawnZones.spawnedGroups = filteredAttackers
|
||||
if cfxSpawnZones.verbose then
|
||||
trigger.action.outText("spawn zones GC ran: before <" .. before .. ">, after <" .. #cfxSpawnZones.spawnedGroups .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSpawnZones.update()
|
||||
cfxSpawnZones.updateSchedule = timer.scheduleFunction(cfxSpawnZones.update, {}, timer.getTime() + 1/cfxSpawnZones.ups)
|
||||
@ -445,9 +483,9 @@ function cfxSpawnZones.update()
|
||||
local group = spawner.theSpawn
|
||||
if group:isExist() then
|
||||
-- see how many members of this group are still alive
|
||||
local liveUnits = dcsCommon.getLiveGroupUnits(group)
|
||||
local liveUnits = group:getSize() --dcsCommon.getLiveGroupUnits(group)
|
||||
-- spawn is still alive, will not spawn
|
||||
if #liveUnits > 1 then
|
||||
if liveUnits > 1 then
|
||||
-- we may want to check if this member is still inside
|
||||
-- of spawn location. currently we don't do that
|
||||
needsSpawn = false
|
||||
@ -526,6 +564,158 @@ function cfxSpawnZones.update()
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSpawnZones.houseKeeping()
|
||||
timer.scheduleFunction(cfxSpawnZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes
|
||||
cfxSpawnZones.GC()
|
||||
end
|
||||
|
||||
--
|
||||
-- LOAD/SAVE
|
||||
--
|
||||
function cfxSpawnZones.saveData()
|
||||
local theData = {}
|
||||
local allSpawnerData = {}
|
||||
-- now iterate all spawners and collect their data
|
||||
for theZone, theSpawner in pairs(cfxSpawnZones.allSpawners) do
|
||||
local zName = theZone.name
|
||||
local spawnData = {}
|
||||
if theSpawner.spawn and theSpawner.spawn:isExist() then
|
||||
spawnData.spawn = theSpawner.spawn:getName()
|
||||
end
|
||||
spawnData.count = theSpawner.count
|
||||
spawnData.paused = theSpawner.paused
|
||||
spawnData.cdStarted = theSpawner.cdStarted
|
||||
spawnData.cdTimer = theSpawner.cdTimer - timer.getTime() -- what remains of the cooldown time
|
||||
|
||||
allSpawnerData[zName] = spawnData
|
||||
end
|
||||
|
||||
-- run a GC
|
||||
cfxSpawnZones.GC()
|
||||
-- now collect all living groups
|
||||
-- no longer required to check if group is lively
|
||||
local allLivingTroopData = {}
|
||||
for gName, gData in pairs(cfxSpawnZones.spawnedGroups) do
|
||||
local sData = dcsCommon.clone(gData)
|
||||
dcsCommon.synchGroupData(sData.groupData)
|
||||
allLivingTroopData[gName] = sData
|
||||
end
|
||||
|
||||
theData.spawnerData = allSpawnerData
|
||||
theData.troopData = allLivingTroopData
|
||||
return theData
|
||||
end
|
||||
|
||||
function cfxSpawnZones.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("cfxSpawnZones")
|
||||
if not theData then
|
||||
if cfxSpawnZones.verbose then
|
||||
trigger.action.outText("+++spwn: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- we begin by re-spawning all spawned groups so that the
|
||||
-- spwners can then later link to them
|
||||
local allTroopData = theData.troopData
|
||||
for gName, gdTroop in pairs (allTroopData) do
|
||||
local gData = gdTroop.groupData
|
||||
local orders = gdTroop.orders
|
||||
local target = gdTroop.target
|
||||
local tracker = gdTroop.tracker
|
||||
local side = gdTroop.side
|
||||
local range = gdTroop.range
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
|
||||
-- now spawn, but first
|
||||
-- add to my own attacker queue so we can save later
|
||||
local gdClone = dcsCommon.clone(gdTroop)
|
||||
cfxSpawnZones.spawnedGroups[gName] = gdClone
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
-- post-proccing for 'train' orders
|
||||
if orders and (orders == "train" ) then
|
||||
-- make them ROE "HOLD"
|
||||
cfxCommander.scheduleOptionForGroup(
|
||||
theGroup,
|
||||
AI.Option.Ground.id.ROE,
|
||||
AI.Option.Ground.val.ROE.WEAPON_HOLD,
|
||||
1.0)
|
||||
else
|
||||
-- add to groundTroops
|
||||
local newTroops = cfxGroundTroops.createGroundTroops(theGroup, range, orders)
|
||||
cfxGroundTroops.addGroundTroopsToPool(newTroops)
|
||||
-- engage a target zone
|
||||
if target then
|
||||
local destZone = cfxZones.getZoneByName(target)
|
||||
if destZone then
|
||||
newTroops.destination = destZone
|
||||
cfxGroundTroops.makeTroopsEngageZone(newTroops)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- post-proccing for trackwith [may not be needed when we]
|
||||
-- have persistence in the tracking module. that module
|
||||
-- simply schedules re-connecting after one second
|
||||
end
|
||||
|
||||
-- now set up all spawners with save data
|
||||
local allSpawnerData = theData.spawnerData
|
||||
for zName, sData in pairs (allSpawnerData) do
|
||||
local theZone = cfxZones.getZoneByName(zName)
|
||||
if theZone then
|
||||
local theSpawner = cfxSpawnZones.getSpawnerForZone(theZone)
|
||||
if theSpawner then
|
||||
theSpawner.inited = true -- inited by persistence
|
||||
theSpawner.count = sData.count
|
||||
theSpawner.paused = sData.paused
|
||||
theSpawner.cdStarted = sData.cdStarted
|
||||
if theSpawner.cdStarted then
|
||||
theSpawner.cdTimer = timer.getTime() + sData.cdTimer
|
||||
else
|
||||
theSpawner.cdTimer = -1
|
||||
end
|
||||
if sData.spawn then
|
||||
local theGroup = Group.getByName(sData.spawn)
|
||||
if theGroup then
|
||||
theSpawner.spawn = theGroup
|
||||
else
|
||||
trigger.action.outText("+++spwn (persistence): can't re-connect spawner <" .. zName .. "> with group <" .. sData.spawn .. ">, skipping", 30)
|
||||
end
|
||||
end
|
||||
else
|
||||
trigger.action.outText("+++spwn (persistence): can't find spawner for zone <" .. zName .. ">, skipping", 30)
|
||||
end
|
||||
else
|
||||
trigger.action.outText("+++spwn (persistence): can't find zone <" .. zName .. "> for spawner, skipping", 30)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- START
|
||||
--
|
||||
function cfxSpawnZones.initialSpawnCheck(aSpawner)
|
||||
if not aSpawner.paused
|
||||
and cfxSpawnZones.verifySpawnOwnership(aSpawner)
|
||||
and aSpawner.maxSpawns ~= 0
|
||||
and not aSpawner.inited
|
||||
then
|
||||
cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
-- update spawn count and make sure we haven't spawned the one and only
|
||||
if aSpawner.maxSpawns > 0 then
|
||||
aSpawner.maxSpawns = aSpawner.maxSpawns - 1
|
||||
end
|
||||
if aSpawner.maxSpawns == 0 then
|
||||
aSpawner.paused = true
|
||||
trigger.action.outText("+++ maxspawn -- turning off zone " .. aSpawner.zone.name, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSpawnZones.start()
|
||||
if not dcsCommon.libCheck("cfx Spawn Zones",
|
||||
cfxSpawnZones.requiredLibs) then
|
||||
@ -540,22 +730,29 @@ function cfxSpawnZones.start()
|
||||
for k, aZone in pairs(attrZones) do
|
||||
local aSpawner = cfxSpawnZones.createSpawner(aZone)
|
||||
cfxSpawnZones.addSpawner(aSpawner)
|
||||
if not aSpawner.paused and cfxSpawnZones.verifySpawnOwnership(aSpawner) and aSpawner.maxSpawns ~= 0 then
|
||||
cfxSpawnZones.spawnWithSpawner(aSpawner)
|
||||
-- update spawn count and make sure we haven't spawned the one and only
|
||||
if aSpawner.maxSpawns > 0 then
|
||||
aSpawner.maxSpawns = aSpawner.maxSpawns - 1
|
||||
end
|
||||
if aSpawner.maxSpawns == 0 then
|
||||
aSpawner.paused = true
|
||||
trigger.action.outText("+++ maxspawn -- turning off zone " .. aSpawner.zone.name, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- we now do persistence
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cfxSpawnZones.saveData
|
||||
persistence.registerModule("cfxSpawnZones", callbacks)
|
||||
-- now load my data
|
||||
cfxSpawnZones.loadData()
|
||||
end
|
||||
|
||||
-- we now spawn if not taken care of by load / save
|
||||
for theZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do
|
||||
cfxSpawnZones.initialSpawnCheck(aSpawner)
|
||||
end
|
||||
|
||||
-- and start the regular update calls
|
||||
cfxSpawnZones.update()
|
||||
|
||||
-- start housekeeping
|
||||
cfxSpawnZones.houseKeeping()
|
||||
|
||||
trigger.action.outText("cfx Spawn Zones v" .. cfxSpawnZones.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
changer = {}
|
||||
changer.version = "1.0.2"
|
||||
changer.version = "1.0.3"
|
||||
changer.verbose = false
|
||||
changer.ups = 1
|
||||
changer.requiredLibs = {
|
||||
@ -12,6 +12,7 @@ changer.changers = {}
|
||||
1.0.0 - Initial version
|
||||
1.0.1 - Better guards in config to avoid <none> Zone getter warning
|
||||
1.0.2 - on/off: verbosity
|
||||
1.0.3 - NOT on/off
|
||||
|
||||
Transmogrify an incoming signal to an output signal
|
||||
- not
|
||||
@ -111,6 +112,12 @@ function changer.createChangerWithZone(theZone)
|
||||
if cfxZones.hasProperty(theZone, "changeOn/Off?") then
|
||||
theZone.changerOnOff = cfxZones.getStringFromZoneProperty(theZone, "changeOn/Off?", "*<none>", 1)
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "NOT On/Off?") then
|
||||
theZone.changerOnOffINV = cfxZones.getStringFromZoneProperty(theZone, "NOT On/Off?", "*<none>", 1)
|
||||
end
|
||||
if cfxZones.hasProperty(theZone, "NOT changeOn/Off?") then
|
||||
theZone.changerOnOffINV = cfxZones.getStringFromZoneProperty(theZone, "NOT changeOn/Off?", "*<none>", 1)
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@ -207,6 +214,10 @@ function changer.update()
|
||||
end
|
||||
|
||||
-- do processing if not paused
|
||||
if aZone.changerOnOff and aZone.changerOnOffINV then
|
||||
trigger.action.outText("+++chgr: WARNING - zone <" .. aZone.name .. "> has conflicting change On/off inputs, disregating inverted input (NOT changeOn/off?)", 30)
|
||||
end
|
||||
|
||||
if not aZone.changerPaused then
|
||||
if aZone.changerOnOff then
|
||||
if cfxZones.getFlagValue(aZone.changerOnOff, aZone) > 0 then
|
||||
@ -216,6 +227,14 @@ function changer.update()
|
||||
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed.", 30)
|
||||
end
|
||||
end
|
||||
elseif aZone.changerOnOffINV then
|
||||
if cfxZones.getFlagValue(aZone.changerOnOffINV, aZone) == 0 then
|
||||
changer.process(aZone)
|
||||
else
|
||||
if changer.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++chgr: " .. aZone.name .. " gate closed.", 30)
|
||||
end
|
||||
end
|
||||
else
|
||||
changer.process(aZone)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "1.4.9"
|
||||
cloneZones.version = "1.5.0"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -17,6 +17,9 @@ cloneZones.callbacks = {}
|
||||
cloneZones.unitXlate = {}
|
||||
cloneZones.groupXlate = {} -- used to translate original groupID to cloned. only holds last spawned group id
|
||||
cloneZones.uniqueCounter = 9200000 -- we start group numbering here
|
||||
|
||||
cloneZones.allClones = {} -- all clones spawned, regularly GC'd
|
||||
cloneZones.allCObjects = {} -- all clones objects
|
||||
--[[--
|
||||
Clones Groups from ME mission data
|
||||
Copyright (c) 2022 by Christian Franz and cf/x AG
|
||||
@ -53,6 +56,7 @@ cloneZones.uniqueCounter = 9200000 -- we start group numbering here
|
||||
1.4.8 - added 'wipe?' synonym
|
||||
1.4.9 - onRoad option
|
||||
- rndHeading option
|
||||
1.5.0 - persistence
|
||||
|
||||
|
||||
--]]--
|
||||
@ -289,7 +293,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
theZone.onRoad = cfxZones.getBoolFromZoneProperty(theZone, "onRoad", false)
|
||||
|
||||
if theZone.rndLoc and theZone.verbose then
|
||||
trigger.action.outText("+++ rndloc on for " .. theZone.name, 30)
|
||||
--trigger.action.outText("+++ rndloc on for " .. theZone.name, 30)
|
||||
end
|
||||
|
||||
-- we end with clear plate
|
||||
@ -767,6 +771,12 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
-- now spawn all raw data
|
||||
for idx, rawData in pairs (dataToSpawn) do
|
||||
-- now spawn and save to clones
|
||||
-- first norm and clone data for later save
|
||||
rawData.cty = rawData.CZctry
|
||||
rawData.cat = rawData.CZtheCat
|
||||
local theData = dcsCommon.clone(rawData)
|
||||
cloneZones.allClones[rawData.name] = theData
|
||||
|
||||
local theGroup = coalition.addGroup(rawData.CZctry, rawData.CZtheCat, rawData)
|
||||
table.insert(spawnedGroups, theGroup)
|
||||
|
||||
@ -888,20 +898,25 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
end
|
||||
|
||||
local isCargo = rawData.canCargo
|
||||
rawData.cty = ctry
|
||||
-- save for persistence
|
||||
local theData = dcsCommon.clone(rawData)
|
||||
cfxZones.allCObjects[rawData.name] = theData
|
||||
|
||||
local theStatic = coalition.addStaticObject(ctry, rawData)
|
||||
local newStaticID = tonumber(theStatic:getID())
|
||||
table.insert(spawnedStatics, theStatic)
|
||||
-- we don't mix groups with units, so no lookup tables for
|
||||
-- statics
|
||||
if newStaticID == rawData.CZTargetID then
|
||||
-- trigger.action.outText("Static ID OK: " .. newStaticID .. " for " .. rawData.name, 30)
|
||||
|
||||
else
|
||||
trigger.action.outText("Static ID mismatch: " .. newStaticID .. " vs (target) " .. rawData.CZTargetID .. " for " .. rawData.name, 30)
|
||||
end
|
||||
cloneZones.unitXlate[origID] = newStaticID -- same as units
|
||||
|
||||
cloneZones.invokeCallbacks(theZone, "did spawn static", theStatic)
|
||||
--]]--
|
||||
|
||||
if cloneZones.verbose or spawnZone.verbose then
|
||||
trigger.action.outText("Static spawn: spawned " .. aStaticName, 30)
|
||||
end
|
||||
@ -1058,7 +1073,7 @@ function cloneZones.update()
|
||||
if aZone.deSpawnFlag then
|
||||
local currTriggerVal = cfxZones.getFlagValue(aZone.deSpawnFlag, aZone) -- trigger.misc.getUserFlag(aZone.deSpawnFlag)
|
||||
if currTriggerVal ~= aZone.lastDeSpawnValue then
|
||||
if cloneZones.verbose then
|
||||
if cloneZones.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++clnZ: DEspawn triggered for <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
cloneZones.despawnAll(aZone)
|
||||
@ -1103,15 +1118,249 @@ function cloneZones.onStart()
|
||||
--trigger.action.outText("+++clnZ: Enter atStart", 30)
|
||||
for idx, theZone in pairs(cloneZones.cloners) do
|
||||
if theZone.onStart then
|
||||
if cloneZones.verbose then
|
||||
trigger.action.outText("+++clnZ: atStart will spawn for <"..theZone.name .. ">", 30)
|
||||
if theZone.isStarted then
|
||||
if cloneZones.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++clnz: onStart pre-empted for <" .. theZone.name .. "> by persistence", 30)
|
||||
end
|
||||
else
|
||||
if cloneZones.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++clnZ: onStart spawing for <"..theZone.name .. ">", 30)
|
||||
end
|
||||
cloneZones.spawnWithCloner(theZone)
|
||||
end
|
||||
cloneZones.spawnWithCloner(theZone)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Regular GC and housekeeping
|
||||
--
|
||||
function cloneZones.GC()
|
||||
-- GC run. remove all my dead remembered troops
|
||||
local filteredAttackers = {}
|
||||
for gName, gData in pairs (cloneZones.allClones) do
|
||||
-- all we need to do is get the group of that name
|
||||
-- and if it still returns units we are fine
|
||||
local gameGroup = Group.getByName(gName)
|
||||
if gameGroup and gameGroup:isExist() and gameGroup:getSize() > 0 then
|
||||
-- we now filter for categories. we currently only let
|
||||
-- ground units pass
|
||||
-- better make this configurabele by option later
|
||||
if gData.cat == 0 and false then -- block aircraft
|
||||
elseif gData.cat == 1 and false then -- block helos
|
||||
elseif gData.cat == 2 and false then -- block ground
|
||||
elseif gData.cat == 3 and false then -- block ship
|
||||
elseif gData.cat == 4 and false then -- block trains
|
||||
else
|
||||
-- not filtered, persist
|
||||
filteredAttackers[gName] = gData
|
||||
end
|
||||
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
|
||||
-- and if it still returns units we are fine
|
||||
local theObject = StaticObject.getByName(gName)
|
||||
if theObject and theObject:isExist() then
|
||||
filteredAttackers[gName] = gData
|
||||
if theObject:getLife() < 1 then
|
||||
gData.dead = true
|
||||
end
|
||||
end
|
||||
end
|
||||
cloneZones.allCObjects = filteredAttackers
|
||||
end
|
||||
|
||||
function cloneZones.houseKeeping()
|
||||
timer.scheduleFunction(cloneZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes
|
||||
cloneZones.GC()
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- LOAD / SAVE
|
||||
--
|
||||
function cloneZones.synchGroupMXData(theData)
|
||||
-- we iterate the group's units one by one and update them
|
||||
local newUnits = {}
|
||||
local allUnits = theData.units
|
||||
for idx, unitData in pairs(allUnits) do
|
||||
local uName = unitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
unitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
unitData.x = pos.x
|
||||
unitData.y = pos.z -- (!!)
|
||||
-- add aircraft handling here (alt, speed etc)
|
||||
-- perhaps even curtail route
|
||||
table.insert(newUnits, unitData)
|
||||
end
|
||||
end
|
||||
theData.units = newUnits
|
||||
end
|
||||
|
||||
function cloneZones.synchMXObjData(theData)
|
||||
local oName = theData.name
|
||||
local theObject = StaticObject.getByName(oName)
|
||||
theData.heading = dcsCommon.getUnitHeading(theObject)
|
||||
pos = theObject:getPoint()
|
||||
theData.x = pos.x
|
||||
theData.y = pos.z -- (!!)
|
||||
theData.isDead = theObject:getLife() < 1
|
||||
theData.dead = theData.isDead
|
||||
end
|
||||
|
||||
function cloneZones.saveData()
|
||||
local theData = {}
|
||||
local allCloneData = {}
|
||||
local allSOData = {}
|
||||
-- run a GC pre-emptively
|
||||
cloneZones.GC()
|
||||
|
||||
-- now simply iterate and save all deployed clones
|
||||
for gName, gData in pairs(cloneZones.allClones) do
|
||||
local sData = dcsCommon.clone(gData)
|
||||
cloneZones.synchGroupMXData(sData)
|
||||
allCloneData[gName] = sData
|
||||
end
|
||||
|
||||
-- now simply iterate and save all deployed clones
|
||||
for gName, gData in pairs(cloneZones.allCObjects) do
|
||||
local sData = dcsCommon.clone(gData)
|
||||
cloneZones.synchMXObjData(sData)
|
||||
allSOData[gName] = sData
|
||||
end
|
||||
|
||||
-- now save all cloner stati
|
||||
local cloners = {}
|
||||
for idx, theCloner in pairs(cloneZones.cloners) do
|
||||
local cData = {}
|
||||
local cName = theCloner.name
|
||||
-- mySpawns: all groups i'm curently observing for empty!
|
||||
-- myStatics: dto for objects
|
||||
local mySpawns = {}
|
||||
for idx, aGroup in pairs(theCloner.mySpawns) do
|
||||
if aGroup and aGroup:isExist() and aGroup:getSize() > 0 then
|
||||
table.insert(mySpawns, aGroup:getName())
|
||||
end
|
||||
end
|
||||
cData.mySpawns = mySpawns
|
||||
local myStatics = {}
|
||||
for idx, aStatic in pairs(theCloner.myStatics) do
|
||||
table.insert(myStatics, aStatic:getName())
|
||||
end
|
||||
cData.myStatics = myStatics
|
||||
cData.isStarted = theCloner.isStarted -- to prevent onStart
|
||||
cloners[cName] = cData
|
||||
end
|
||||
|
||||
|
||||
-- save globals
|
||||
theData.cuid = cloneZones.uniqueCounter -- replace whatever is larger
|
||||
theData.uuid = dcsCommon.simpleUUID -- replace whatever is larger
|
||||
|
||||
-- save to struct and pass back
|
||||
theData.clones = allCloneData
|
||||
theData.objects = allSOData
|
||||
theData.cloneZones = cloners
|
||||
return theData
|
||||
end
|
||||
|
||||
function cloneZones.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("cloneZones")
|
||||
if not theData then
|
||||
if cloneZones.verbose then
|
||||
trigger.action.outText("+++clnZ: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- spawn all units
|
||||
local allClones = theData.clones
|
||||
for gName, gData in pairs (allClones) do
|
||||
local cty = gData.cty
|
||||
local cat = gData.cat
|
||||
|
||||
-- now spawn, but first
|
||||
-- add to my own deployed queue so we can save later
|
||||
local gdClone = dcsCommon.clone(gData)
|
||||
cloneZones.allClones[gName] = gdClone
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
end
|
||||
|
||||
-- spawn all static objects
|
||||
local allObjects = theData.objects
|
||||
for oName, oData in pairs(allObjects) do
|
||||
local newStatic = dcsCommon.clone(oData)
|
||||
-- add link info if it exists
|
||||
newStatic.linkUnit = cfxMX.linkByName[oName]
|
||||
if newStatic.linkUnit and unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: linked static <" .. oName .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
||||
end
|
||||
local cty = staticData.cty
|
||||
-- local cat = staticData.cat
|
||||
-- spawn new one, replacing same.named old, dead if required
|
||||
gStatic = coalition.addStaticObject(cty, newStatic)
|
||||
|
||||
-- processing for cargoManager
|
||||
if oData.canCargo then
|
||||
if cfxCargoManager then
|
||||
cfxCargoManager.addCargo(gStatic)
|
||||
end
|
||||
end
|
||||
|
||||
-- add the original data block to be remembered
|
||||
-- for next save
|
||||
cloneZones.allCObjects[oName] = oData
|
||||
end
|
||||
|
||||
-- now update all spawners and reconnect them with their spawns
|
||||
local allCloners = theData.cloneZones
|
||||
for cName, cData in pairs(allCloners) do
|
||||
local theCloner = cloneZones.getCloneZoneByName(cName)
|
||||
if theCloner then
|
||||
theCloner.isStarted = true -- ALWAYS TRUE WHEN WE COME HERE! cData.isStarted
|
||||
local mySpawns = {}
|
||||
for idx, aName in pairs(cData.mySpawns) do
|
||||
local theGroup = Group.getByName(aName)
|
||||
if theGroup then
|
||||
table.insert(mySpawns, theGroup)
|
||||
else
|
||||
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with clone group <".. aName .. ">", 30)
|
||||
end
|
||||
end
|
||||
theCloner.mySpawns = mySpawns
|
||||
|
||||
local myStatics = {}
|
||||
for idx, aName in pairs(cData.myStatics) do
|
||||
local theStatic = StaticObject.getByName(aName)
|
||||
if theStatic then
|
||||
table.insert(myStatics, theStatic)
|
||||
else
|
||||
trigger.action.outText("+++clnZ - persistence: can't reconnect cloner <" .. cName .. "> with static <".. aName .. ">", 30)
|
||||
end
|
||||
end
|
||||
theCloner.myStatics = myStatics
|
||||
else
|
||||
trigger.action.outText("+++clnZ - persistence: cannot synch cloner <" .. cName .. ">, does not exist", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- finally, synch uid and uuid
|
||||
if theData.cuid and theData.cuid > cloneZones.uniqueCounter then
|
||||
cloneZones.uniqueCounter = theData.cuid
|
||||
end
|
||||
if theData.uuiD and theData.uuid > dcsCommon.simpleUUID then
|
||||
dcsCommon.simpleUUID = theData.uuid
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
-- START
|
||||
--
|
||||
@ -1155,16 +1404,28 @@ function cloneZones.start()
|
||||
cloneZones.addCloneZone(aZone) -- remember it so we can smoke it
|
||||
end
|
||||
|
||||
-- run through onStart, but leave at least a few
|
||||
-- cycles to go through object removal so statics
|
||||
-- can spawn on ground. onStart is being deprecated, the
|
||||
-- raiseFlag module covers this since the first time
|
||||
-- raiseFlag is run is t0 + 0.5s
|
||||
-- update all cloners and spawned clones from file
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cloneZones.saveData
|
||||
persistence.registerModule("cloneZones", callbacks)
|
||||
-- now load my data
|
||||
cloneZones.loadData()
|
||||
end
|
||||
|
||||
-- schedule onStart, and leave at least a few
|
||||
-- cycles to go through object removal
|
||||
-- persistencey has loaded isStarted if a cloner was
|
||||
-- already started
|
||||
timer.scheduleFunction(cloneZones.onStart, {}, timer.getTime() + 0.1)
|
||||
|
||||
-- start update
|
||||
cloneZones.update()
|
||||
|
||||
-- start housekeeping
|
||||
cloneZones.houseKeeping()
|
||||
|
||||
trigger.action.outText("cfx Clone Zones v" .. cloneZones.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "2.7.0"
|
||||
dcsCommon.version = "2.7.1"
|
||||
--[[-- VERSION HISTORY
|
||||
2.2.6 - compassPositionOfARelativeToB
|
||||
- clockPositionOfARelativeToB
|
||||
@ -89,6 +89,9 @@ dcsCommon.version = "2.7.0"
|
||||
- new getSceneryObjectInZoneByName()
|
||||
2.7.0 - new synchGroupData()
|
||||
clone, topClone and copyArray now all nil-trap
|
||||
2.7.1 - new isPlayerUnit() -- moved from cfxPlayer
|
||||
new getAllExistingPlayerUnitsRaw - from cfxPlayer
|
||||
new typeIsInfantry()
|
||||
|
||||
--]]--
|
||||
|
||||
@ -103,6 +106,7 @@ dcsCommon.version = "2.7.0"
|
||||
-- globals
|
||||
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
||||
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P"} -- Ka-50 and Gazelle can't carry troops
|
||||
dcsCommon.coalitionSides = {0, 1, 2}
|
||||
|
||||
-- verify that a module is loaded. obviously not required
|
||||
-- for dcsCommon, but all higher-order modules
|
||||
@ -2248,6 +2252,28 @@ function dcsCommon.isTroopCarrier(theUnit)
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.isPlayerUnit(theUnit)
|
||||
-- new patch. simply check if getPlayerName returns something
|
||||
if not theUnit then return false end
|
||||
if not theUnit.getPlayerName then return false end -- map/static object
|
||||
local pName = theUnit:getPlayerName()
|
||||
if pName then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
local apu = {}
|
||||
for idx, theSide in pairs(dcsCommon.coalitionSides) do
|
||||
local thePlayers = coalition.getPlayers(theSide)
|
||||
for idy, theUnit in pairs (thePlayers) do
|
||||
if theUnit and theUnit:isExist() then
|
||||
table.insert(apu, theUnit)
|
||||
end
|
||||
end
|
||||
end
|
||||
return apu
|
||||
end
|
||||
|
||||
function dcsCommon.getUnitAlt(theUnit)
|
||||
if not theUnit then return 0 end
|
||||
if not theUnit:isExist() then return 0 end
|
||||
@ -2335,10 +2361,7 @@ function dcsCommon.getUnitHeadingDegrees(theUnit)
|
||||
return heading * 57.2958 -- 180 / math.pi
|
||||
end
|
||||
|
||||
function dcsCommon.unitIsInfantry(theUnit)
|
||||
if not theUnit then return false end
|
||||
if not theUnit:isExist() then return end
|
||||
local theType = theUnit:getTypeName()
|
||||
function dcsCommon.typeIsInfantry(theType)
|
||||
local isInfantry =
|
||||
dcsCommon.containsString(theType, "infantry", false) or
|
||||
dcsCommon.containsString(theType, "paratrooper", false) or
|
||||
@ -2349,6 +2372,23 @@ function dcsCommon.unitIsInfantry(theUnit)
|
||||
return isInfantry
|
||||
end
|
||||
|
||||
function dcsCommon.unitIsInfantry(theUnit)
|
||||
if not theUnit then return false end
|
||||
if not theUnit:isExist() then return end
|
||||
local theType = theUnit:getTypeName()
|
||||
--[[--
|
||||
local isInfantry =
|
||||
dcsCommon.containsString(theType, "infantry", false) or
|
||||
dcsCommon.containsString(theType, "paratrooper", false) or
|
||||
dcsCommon.containsString(theType, "stinger", false) or
|
||||
dcsCommon.containsString(theType, "manpad", false) or
|
||||
dcsCommon.containsString(theType, "soldier", false) or
|
||||
dcsCommon.containsString(theType, "SA-18 Igla", false)
|
||||
return isInfantry
|
||||
--]]--
|
||||
return dcsCommon.typeIsInfantry(theType)
|
||||
end
|
||||
|
||||
function dcsCommon.coalition2county(inCoalition)
|
||||
-- simply return UN troops for 0 neutral,
|
||||
-- joint red for 1 red
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
groupTracker = {}
|
||||
groupTracker.version = "1.1.3"
|
||||
groupTracker.version = "1.1.4"
|
||||
groupTracker.verbose = false
|
||||
groupTracker.ups = 1
|
||||
groupTracker.requiredLibs = {
|
||||
@ -20,6 +20,12 @@ groupTracker.trackers = {}
|
||||
1.1.3 - spellings
|
||||
- addGroupToTrackerNamed bug removed accessing tracker
|
||||
- new removeGroupNamedFromTrackerNamed()
|
||||
1.1.4 - destroy? input
|
||||
- allGone! output
|
||||
- triggerMethod
|
||||
- method
|
||||
- isDead optimization
|
||||
|
||||
--]]--
|
||||
|
||||
function groupTracker.addTracker(theZone)
|
||||
@ -87,7 +93,7 @@ function groupTracker.addGroupToTrackerNamed(theGroup, trackerName)
|
||||
return
|
||||
end
|
||||
|
||||
if not theGroup:isExist() then
|
||||
if not Group.isExist(theGroup) then
|
||||
trigger.action.outText("+++gTrk: group does not exist when adding to tracker <" .. trackerName .. ">", 30)
|
||||
return
|
||||
end
|
||||
@ -147,6 +153,17 @@ function groupTracker.createTrackerWithZone(theZone)
|
||||
theZone.trackedGroups = {}
|
||||
|
||||
|
||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
if cfxZones.hasProperty(theZone, "trackerMethod") then
|
||||
theZone.trackerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerMethod", "inc")
|
||||
end
|
||||
|
||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
||||
|
||||
if cfxZones.hasProperty(theZone, "trackerTriggerMethod") then
|
||||
theZone.trackerTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "trackerTriggerMethod", "change")
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "numGroups") then
|
||||
theZone.tNumGroups = cfxZones.getStringFromZoneProperty(theZone, "numGroups", "*<none>")
|
||||
-- we may need to zero this flag
|
||||
@ -181,6 +198,16 @@ function groupTracker.createTrackerWithZone(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "destroy?") then
|
||||
theZone.destroyFlag = cfxZones.getStringFromZoneProperty(theZone, "destroy?", "*<none>")
|
||||
theZone.lastDestroyValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "allGone!") then
|
||||
theZone.allGoneFlag = cfxZones.getStringFromZoneProperty(theZone, "allGone!", "<None>") -- note string on number default
|
||||
end
|
||||
theZone.lastGroupCount = 0
|
||||
|
||||
if theZone.verbose or groupTracker.verbose then
|
||||
trigger.action.outText("gTrck: processed <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
@ -189,12 +216,23 @@ end
|
||||
--
|
||||
-- update
|
||||
--
|
||||
function groupTracker.destroyAllInZone(theZone)
|
||||
for idx, theGroup in pairs(theZone.trackedGroups) do
|
||||
if Group.isExist(theGroup) then
|
||||
theGroup:destroy()
|
||||
end
|
||||
end
|
||||
-- we keep all groups in trackedGroups so we
|
||||
-- generate a host of destroy events when we run through
|
||||
-- checkGroups next
|
||||
end
|
||||
|
||||
function groupTracker.checkGroups(theZone)
|
||||
local filteredGroups = {}
|
||||
for idx, theGroup in pairs(theZone.trackedGroups) do
|
||||
-- see if this group can be transferred
|
||||
local isDead = false
|
||||
if theGroup:isExist() then
|
||||
--[[ if theGroup.isExist and theGroup:isExist() then
|
||||
local allUnits = theGroup:getUnits()
|
||||
isDead = true
|
||||
for idy, aUnit in pairs(allUnits) do
|
||||
@ -203,6 +241,8 @@ function groupTracker.checkGroups(theZone)
|
||||
break
|
||||
end
|
||||
end
|
||||
--]]--
|
||||
if Group.isExist(theGroup) and theGroup:getSize() > 0 then
|
||||
else
|
||||
isDead = true -- no longer exists
|
||||
end
|
||||
@ -238,7 +278,22 @@ function groupTracker.update()
|
||||
timer.scheduleFunction(groupTracker.update, {}, timer.getTime() + 1/groupTracker.ups)
|
||||
|
||||
for idx, theZone in pairs(groupTracker.trackers) do
|
||||
|
||||
if theZone.destroyFlag and cfxZones.testZoneFlag(theZone, theZone.destroyFlag, theZone.trackerTriggerMethod, "lastDestroyValue") then
|
||||
groupTracker.destroyAllInZone(theZone)
|
||||
if groupTracker.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++gTrk: destroying all groups tracked with <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
groupTracker.checkGroups(theZone)
|
||||
|
||||
-- see if we need to bang on empty!
|
||||
local currCount = #theZone.trackedGroups
|
||||
if theZone.allGoneFlag and currCount == 0 and currCount ~= theZone.lastGroupCount then
|
||||
cfxZones.pollFlag(aZone.allGoneFlag, aZone.trackerMethod, aZone)
|
||||
end
|
||||
theZone.lastGroupCount = currCount
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
limitedAirframes = {}
|
||||
limitedAirframes.version = "1.4.0"
|
||||
limitedAirframes.version = "1.5.0"
|
||||
limitedAirframes.verbose = false
|
||||
limitedAirframes.enabled = true -- can be turned off
|
||||
limitedAirframes.userCanToggle = true -- F10 menu?
|
||||
@ -18,7 +18,7 @@ limitedAirframes.requiredLibs = {
|
||||
-- pretty stupid to check for this since we
|
||||
-- need common to invoke the check, but anyway
|
||||
"cfxZones", -- Zones, of course for safe landings
|
||||
"cfxPlayer",
|
||||
-- "cfxPlayer", no longer needed
|
||||
}
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -48,7 +48,8 @@ limitedAirframes.requiredLibs = {
|
||||
- 1.4.0 - DML integration, verbosity, clean-up, QoL improvements
|
||||
redSafe, blueSafe with attribute, backward compatible
|
||||
currRed
|
||||
|
||||
- 1.4.1 - removed dependency to cfxPlayer
|
||||
- 1.5.0 - persistence support
|
||||
|
||||
--]]--
|
||||
|
||||
@ -123,6 +124,7 @@ function limitedAirframes.readConfigZone()
|
||||
end
|
||||
-- remember me
|
||||
limitedAirframes.config = theZone
|
||||
limitedAirframes.name = "limitedAirframes" -- so we can call cfxZones with ourself as param
|
||||
|
||||
limitedAirframes.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||
|
||||
@ -131,22 +133,22 @@ function limitedAirframes.readConfigZone()
|
||||
end
|
||||
|
||||
-- ok, for each property, load it if it exists
|
||||
if cfxZones.hasProperty(theZone, "enabled") then
|
||||
-- if cfxZones.hasProperty(theZone, "enabled") then
|
||||
limitedAirframes.enabled = cfxZones.getBoolFromZoneProperty(theZone, "enabled", true)
|
||||
end
|
||||
-- end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "userCanToggle") then
|
||||
-- if cfxZones.hasProperty(theZone, "userCanToggle") then
|
||||
limitedAirframes.userCanToggle = cfxZones.getBoolFromZoneProperty(theZone, "userCanToggle", true)
|
||||
end
|
||||
-- end
|
||||
|
||||
|
||||
if cfxZones.hasProperty(theZone, "maxRed") then
|
||||
-- if cfxZones.hasProperty(theZone, "maxRed") then
|
||||
limitedAirframes.maxRed = cfxZones.getNumberFromZoneProperty(theZone, "maxRed", -1)
|
||||
end
|
||||
-- end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "maxBlue") then
|
||||
-- if cfxZones.hasProperty(theZone, "maxBlue") then
|
||||
limitedAirframes.maxBlue = cfxZones.getNumberFromZoneProperty(theZone, "maxBlue", -1)
|
||||
end
|
||||
-- end
|
||||
|
||||
limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "#red", "*none")
|
||||
limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "#blue", "*none")
|
||||
@ -318,7 +320,7 @@ function limitedAirframes.preProcessor(event)
|
||||
end
|
||||
|
||||
|
||||
if not cfxPlayer.isPlayerUnit(theUnit) then
|
||||
if not dcsCommon.isPlayerUnit(theUnit) then
|
||||
-- not a player unit. Events 5 and 6 have been
|
||||
-- handled before, so we can safely ignore
|
||||
return false
|
||||
@ -828,6 +830,37 @@ function limitedAirframes.pilotsRescued(theCoalition, success, numRescued, notes
|
||||
trigger.action.outSoundForCoalition(theCoalition, limitedAirframes.warningSound)--"Quest Snare 3.wav")
|
||||
end
|
||||
|
||||
--
|
||||
-- Load / Save
|
||||
--
|
||||
function limitedAirframes.saveData()
|
||||
local theData = {}
|
||||
theData.currRed = limitedAirframes.currRed
|
||||
theData.currBlue = limitedAirframes.currBlue
|
||||
return theData
|
||||
end
|
||||
|
||||
function limitedAirframes.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("limitedAirframes")
|
||||
if not theData then
|
||||
if limitedAirframes.verbose then
|
||||
trigger.action.outText("+++limA: no save date received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if theData.currRed then
|
||||
limitedAirframes.currRed = theData.currRed
|
||||
end
|
||||
|
||||
if theData.currBlue then
|
||||
limitedAirframes.currBlue = theData.currBlue
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- START
|
||||
--
|
||||
@ -885,7 +918,7 @@ function limitedAirframes.start()
|
||||
limitedAirframes.currBlue = limitedAirframes.maxBlue
|
||||
|
||||
-- collect active player unit names
|
||||
local allPlayerUnits = cfxPlayer.getAllExistingPlayerUnitsRaw()
|
||||
local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
for i=1, #allPlayerUnits do
|
||||
local aUnit = allPlayerUnits[i]
|
||||
limitedAirframes.addPlayerUnit(aUnit)
|
||||
@ -906,6 +939,16 @@ function limitedAirframes.start()
|
||||
trigger.action.outText("+++limA: NO CSAR integration", 30)
|
||||
end
|
||||
|
||||
-- persistence: load states
|
||||
if persistence then
|
||||
-- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = limitedAirframes.saveData
|
||||
persistence.registerModule("limitedAirframes", callbacks)
|
||||
-- now load my data
|
||||
limitedAirframes.loadData()
|
||||
end
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("cf/x Limited Airframes v" .. limitedAirframes.version .. " started: R:".. limitedAirframes.maxRed .. "/B:" .. limitedAirframes.maxBlue, 30)
|
||||
return true
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
persistence = {}
|
||||
persistence.version = "1.0.1"
|
||||
persistence.version = "1.0.2"
|
||||
persistence.ups = 1 -- once every 1 seconds
|
||||
persistence.verbose = false
|
||||
persistence.active = false
|
||||
@ -21,6 +21,7 @@ persistence.requiredLibs = {
|
||||
- spelling
|
||||
- cfxZones interface
|
||||
- always output save location
|
||||
1.0.2 - QoL when verbosity is on
|
||||
|
||||
|
||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||
@ -304,9 +305,9 @@ function persistence.missionStartDataLoad()
|
||||
-- we are done for now. modules check in
|
||||
-- after persistence and load their own data
|
||||
-- when they detect that there is data to load
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: basic import complete.", 30)
|
||||
end
|
||||
|
||||
trigger.action.outText("+++persistence: successfully read mission save data", 30)
|
||||
|
||||
end
|
||||
|
||||
--
|
||||
@ -345,6 +346,10 @@ function persistence.saveMissionData()
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: gathered data from <" .. moduleName .. ">", 30)
|
||||
end
|
||||
else
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: NO DATA gathered data from <" .. moduleName .. ">, module returned NIL", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -88,43 +88,6 @@ function radioMenu.createRadioMenuWithZone(theZone)
|
||||
if theZone.menuVisible then
|
||||
radioMenu.installMenu(theZone)
|
||||
end
|
||||
--[[--
|
||||
-- now do the two options
|
||||
local menuA = cfxZones.getStringFromZoneProperty(theZone, "itemA", "<no A submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuA = missionCommands.addCommand(menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"})
|
||||
else
|
||||
theZone.menuA = missionCommands.addCommandForCoalition(theZone.coalition, menuA, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "A"})
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemB") then
|
||||
local menuB = cfxZones.getStringFromZoneProperty(theZone, "itemB", "<no B submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuB = missionCommands.addCommand(menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"})
|
||||
else
|
||||
theZone.menuB = missionCommands.addCommandForCoalition(theZone.coalition, menuB, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "B"})
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemC") then
|
||||
local menuC = cfxZones.getStringFromZoneProperty(theZone, "itemC", "<no C submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuC = missionCommands.addCommand(menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"})
|
||||
else
|
||||
theZone.menuC = missionCommands.addCommandForCoalition(theZone.coalition, menuC, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "C"})
|
||||
end
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "itemD") then
|
||||
local menuD = cfxZones.getStringFromZoneProperty(theZone, "itemD", "<no D submenu>")
|
||||
if theZone.coalition == 0 then
|
||||
theZone.menuD = missionCommands.addCommand(menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"})
|
||||
else
|
||||
theZone.menuD = missionCommands.addCommandForCoalition(theZone.coalition, menuD, theZone.rootMenu, radioMenu.redirectMenuX, {theZone, "D"})
|
||||
end
|
||||
end
|
||||
|
||||
--]]--
|
||||
|
||||
-- get the triggers & methods here
|
||||
theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
|
||||
18
modules/sixthSense.lua
Normal file
18
modules/sixthSense.lua
Normal file
@ -0,0 +1,18 @@
|
||||
sixthSense = {}
|
||||
sixthSense.version = "1.0.0"
|
||||
-- sniff out dead events and log them
|
||||
function sixthSense:onEvent(event)
|
||||
if event.id == 8 then -- S_EVENT_DEAD
|
||||
if event.initiator then
|
||||
local theObject = event.initiator
|
||||
trigger.action.outText("DEAD event: " .. theObject:getName(), 30)
|
||||
|
||||
else
|
||||
trigger.action.outText("DEAD event, no initiator", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- add event handler
|
||||
world.addEventHandler(sixthSense)
|
||||
trigger.action.outText("sixthSense v" .. sixthSense.version .. " loaded.", 30)
|
||||
@ -1289,4 +1289,5 @@ end
|
||||
-q d.e.f returns string "asdasda..."
|
||||
-q sada reuturs <nil>
|
||||
|
||||
- xref: which zones/attributes reference a flag, g.g. '-xref go'
|
||||
--]]--
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
unitPersistence = {}
|
||||
unitPersistence.version = '1.0.1'
|
||||
unitPersistence.version = '1.1.1'
|
||||
unitPersistence.verbose = false
|
||||
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
|
||||
unitPersistence.requiredLibs = {
|
||||
@ -15,6 +15,11 @@ unitPersistence.requiredLibs = {
|
||||
- handles linked static objects
|
||||
- does no longer mess with heliports
|
||||
- update statics once a minute, not second
|
||||
1.0.2 - fixes coalition bug for static objects
|
||||
1.1.0 - added air and sea units - for filtering destroyed units
|
||||
1.1.1 - fixed static link (again)
|
||||
- fixed air spawn (fixed wing)
|
||||
|
||||
|
||||
REQUIRES PERSISTENCE AND MX
|
||||
|
||||
@ -23,6 +28,10 @@ unitPersistence.requiredLibs = {
|
||||
--]]--
|
||||
unitPersistence.groundTroops = {} -- local buffered copy that we
|
||||
-- maintain from save to save
|
||||
unitPersistence.fixedWing = {}
|
||||
unitPersistence.rotorWing = {}
|
||||
unitPersistence.ships = {}
|
||||
|
||||
unitPersistence.statics = {} -- locally unpacked and buffered static objects
|
||||
|
||||
--
|
||||
@ -49,18 +58,24 @@ function unitPersistence.saveData()
|
||||
local uName = theUnitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
-- got a live one!
|
||||
gotALiveOne = true
|
||||
-- update x and y and heading
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
-- ground units do not use alt
|
||||
if gUnit:isActive() then
|
||||
theUnitData.isDead = gUnit:getLife() < 1
|
||||
if not theUnitData.isDead then
|
||||
-- got a live one!
|
||||
gotALiveOne = true
|
||||
-- update x, y and heading
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
end
|
||||
else
|
||||
gotALiveOne = true -- not yet activated
|
||||
end
|
||||
else
|
||||
theUnitData.isDead = true
|
||||
end -- is alive and exists?
|
||||
end -- unit not dead
|
||||
end -- unit maybe not dead
|
||||
end -- iterate units in group
|
||||
groupData.isDead = not gotALiveOne
|
||||
end -- if group is not dead
|
||||
@ -69,6 +84,136 @@ function unitPersistence.saveData()
|
||||
end
|
||||
end
|
||||
|
||||
-- aircraft
|
||||
for groupName, groupData in pairs(unitPersistence.fixedWing) do
|
||||
-- we update this record live and save it to file
|
||||
if not groupData.isDead then
|
||||
local gotALiveOne = false
|
||||
local allUnits = groupData.units
|
||||
for idx, theUnitData in pairs(allUnits) do
|
||||
if not theUnitData.isDead then
|
||||
local uName = theUnitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
-- update x and y and heading if active and alive
|
||||
if gUnit:isActive() then -- only overwrite if active
|
||||
theUnitData.isDead = gUnit:getLife() < 1
|
||||
if not theUnitData.isDead then
|
||||
gotALiveOne = true -- this group is still alive
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
theUnitData.alt = pos.y
|
||||
theUnitData.alt_type = "BARO"
|
||||
theUnitData.speed = dcsCommon.vMag(gUnit:getVelocity())
|
||||
-- we now could get fancy and do some proccing of the
|
||||
-- waypoints and make the one it's nearest to its
|
||||
-- current waypoint, curtailing all others, but that
|
||||
-- may easily mess with waypoint actions, so we don't
|
||||
end
|
||||
else
|
||||
gotALiveOne = true -- has not yet been activated, live
|
||||
end
|
||||
else
|
||||
theUnitData.isDead = true
|
||||
-- trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
|
||||
end -- is alive and exists?
|
||||
end -- unit maybe not dead
|
||||
end -- iterate units in group
|
||||
groupData.isDead = not gotALiveOne
|
||||
end -- if group is not dead
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: save - processed air group <" .. groupName .. ">.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- helos
|
||||
for groupName, groupData in pairs(unitPersistence.rotorWing) do
|
||||
-- we update this record live and save it to file
|
||||
if not groupData.isDead then
|
||||
local gotALiveOne = false
|
||||
local allUnits = groupData.units
|
||||
for idx, theUnitData in pairs(allUnits) do
|
||||
if not theUnitData.isDead then
|
||||
local uName = theUnitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
-- update x and y and heading if active and alive
|
||||
if gUnit:isActive() then -- only overwrite if active
|
||||
theUnitData.isDead = gUnit:getLife() < 1
|
||||
if not theUnitData.isDead then
|
||||
gotALiveOne = true -- this group is still alive
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
theUnitData.alt = pos.y
|
||||
theUnitData.alt_type = "BARO"
|
||||
theUnitData.speed = dcsCommon.vMag(gUnit:getVelocity())
|
||||
-- we now could get fancy and do some proccing of the
|
||||
-- waypoints and make the one it's nearest to its
|
||||
-- current waypoint, curtailing all others, but that
|
||||
-- may easily mess with waypoint actions, so we don't
|
||||
end
|
||||
else
|
||||
gotALiveOne = true -- has not yet been activated, live
|
||||
end
|
||||
else
|
||||
theUnitData.isDead = true
|
||||
trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
|
||||
end -- is alive and exists?
|
||||
end -- unit maybe not dead
|
||||
end -- iterate units in group
|
||||
groupData.isDead = not gotALiveOne
|
||||
end -- if group is not dead
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: save - processed helo group <" .. groupName .. ">.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- ships
|
||||
for groupName, groupData in pairs(unitPersistence.ships) do
|
||||
-- we update this record live and save it to file
|
||||
if not groupData.isDead then
|
||||
local gotALiveOne = false
|
||||
local allUnits = groupData.units
|
||||
for idx, theUnitData in pairs(allUnits) do
|
||||
if not theUnitData.isDead then
|
||||
local uName = theUnitData.name
|
||||
local gUnit = Unit.getByName(uName)
|
||||
if gUnit and gUnit:isExist() then
|
||||
-- update x and y and heading if active and alive
|
||||
if gUnit:isActive() then -- only overwrite if active
|
||||
theUnitData.isDead = gUnit:getLife() < 1
|
||||
if not theUnitData.isDead then
|
||||
gotALiveOne = true -- this group is still alive
|
||||
theUnitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||
pos = gUnit:getPoint()
|
||||
theUnitData.x = pos.x
|
||||
theUnitData.y = pos.z -- (!!)
|
||||
-- we only filter dead ships and don't mess with others
|
||||
-- during load, so we are doing this solely for possible
|
||||
-- later expansions
|
||||
end
|
||||
else
|
||||
gotALiveOne = true -- has not yet been activated, live
|
||||
end
|
||||
else
|
||||
theUnitData.isDead = true
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence - unit <" .. uName .. "> of group <" .. groupName .. "> is dead or non-existant", 30)
|
||||
end
|
||||
end -- is alive and exists?
|
||||
end -- unit maybe not dead
|
||||
end -- iterate units in group
|
||||
groupData.isDead = not gotALiveOne
|
||||
end -- if group is not dead
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("unitPersistence: save - processed ship group <" .. groupName .. ">.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- process all static objects placed with ME
|
||||
for oName, oData in pairs(unitPersistence.statics) do
|
||||
if not oData.isDead or oData.lateActivation then
|
||||
@ -96,6 +241,10 @@ function unitPersistence.saveData()
|
||||
|
||||
theData.version = unitPersistence.version
|
||||
theData.ground = unitPersistence.groundTroops
|
||||
theData.fixedWing = unitPersistence.fixedWing
|
||||
theData.rotorWing = unitPersistence.rotorWing
|
||||
theData.ships = unitPersistence.ships
|
||||
|
||||
theData.statics = unitPersistence.statics
|
||||
return theData
|
||||
end
|
||||
@ -103,6 +252,13 @@ end
|
||||
--
|
||||
-- Load Mission Data
|
||||
--
|
||||
function unitPersistence.delayedSpawn(args)
|
||||
local cat = args.cat
|
||||
local cty = args.cty
|
||||
local newGroup = args.newGroup
|
||||
local theGroup = coalition.addGroup(cty, cat, newGroup)
|
||||
end
|
||||
|
||||
function unitPersistence.loadMission()
|
||||
local theData = persistence.getSavedDataForModule("unitPersistence")
|
||||
if not theData then
|
||||
@ -137,7 +293,6 @@ function unitPersistence.loadMission()
|
||||
-- filter all dead groups
|
||||
if theUnitData.isDead then
|
||||
-- skip it
|
||||
|
||||
else
|
||||
-- add it to new group
|
||||
table.insert(newUnits, theUnitData)
|
||||
@ -145,29 +300,129 @@ function unitPersistence.loadMission()
|
||||
end
|
||||
-- replace old unit setup with new
|
||||
newGroup.units = newUnits
|
||||
|
||||
local cty = groupData.cty
|
||||
local cat = groupData.cat
|
||||
|
||||
-- destroy the old group
|
||||
--theGroup:destroy() -- will be replaced
|
||||
|
||||
-- spawn new one
|
||||
-- spawn new one, replaces old one
|
||||
theGroup = coalition.addGroup(cty, cat, newGroup)
|
||||
if not theGroup then
|
||||
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">")
|
||||
end
|
||||
if unitPersistence.verbose then
|
||||
-- trigger.action.outText("+++unitPersistence: updated group <" .. groupName .. "> of cat <" .. cat .. "> for cty <" .. cty .. ">", 30)
|
||||
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
unitPersistence.groundTroops = theData.ground
|
||||
else
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: no ground unit data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if theData.fixedWing then
|
||||
for groupName, groupData in pairs(theData.fixedWing) do
|
||||
--trigger.action.outText("+++ start loading group <" .. groupName .. ">", 30)
|
||||
local theGroup = Group.getByName(groupName)
|
||||
if not theGroup then
|
||||
mismatchWarning = true
|
||||
elseif groupData.isDead then
|
||||
theGroup:destroy()
|
||||
elseif groupData.isPlayer then
|
||||
-- skip it
|
||||
else
|
||||
local newGroup = dcsCommon.clone(groupData)
|
||||
local newUnits = {}
|
||||
for idx, theUnitData in pairs(groupData.units) do
|
||||
-- filter all dead groups
|
||||
if theUnitData.isDead then
|
||||
-- skip it
|
||||
else
|
||||
-- add it to new group
|
||||
table.insert(newUnits, theUnitData)
|
||||
end
|
||||
end
|
||||
-- replace old unit setup with (delayed) new
|
||||
newGroup.units = newUnits
|
||||
local cty = groupData.cty
|
||||
local cat = groupData.cat
|
||||
|
||||
-- spawn new one, replaces old one
|
||||
theGroup:destroy()
|
||||
local args = {}
|
||||
args.cty = cty
|
||||
args.cat = cat
|
||||
args.newGroup = newGroup
|
||||
-- since DCS can't replace a group directly (none will appear), we introduce a brief interval for things to settle
|
||||
timer.scheduleFunction(unitPersistence.delayedSpawn, args, timer.getTime()+0.5)
|
||||
|
||||
end
|
||||
end
|
||||
unitPersistence.fixedWing = theData.fixedWing
|
||||
else
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: no aircraft (fixed wing) unit data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if theData.rotorWing then
|
||||
for groupName, groupData in pairs(theData.rotorWing) do
|
||||
local theGroup = Group.getByName(groupName)
|
||||
if not theGroup then
|
||||
mismatchWarning = true
|
||||
elseif groupData.isDead then
|
||||
theGroup:destroy()
|
||||
elseif groupData.isPlayer then
|
||||
-- skip it
|
||||
else
|
||||
local newGroup = dcsCommon.clone(groupData)
|
||||
local newUnits = {}
|
||||
for idx, theUnitData in pairs(groupData.units) do
|
||||
-- filter all dead groups
|
||||
if theUnitData.isDead then
|
||||
-- skip it
|
||||
else
|
||||
-- add it to new group
|
||||
table.insert(newUnits, theUnitData)
|
||||
end
|
||||
end
|
||||
-- replace old unit setup with new
|
||||
newGroup.units = newUnits
|
||||
local cty = groupData.cty
|
||||
local cat = groupData.cat
|
||||
|
||||
-- spawn new one, replaces old one
|
||||
theGroup = coalition.addGroup(cty, cat, newGroup)
|
||||
if not theGroup then
|
||||
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
unitPersistence.rotorWing = theData.rotorWing
|
||||
else
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: no rotor wing unit data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if theData.ships then
|
||||
for groupName, groupData in pairs(theData.ships) do
|
||||
local theGroup = Group.getByName(groupName)
|
||||
if not theGroup then
|
||||
mismatchWarning = true
|
||||
elseif groupData.isDead then
|
||||
-- when entire group is destroyed, we will also
|
||||
-- destroy group. Else all survive
|
||||
-- we currently don't dick around with carrieres unless they are dead
|
||||
theGroup:destroy()
|
||||
else
|
||||
-- do nothing
|
||||
end
|
||||
end
|
||||
unitPersistence.ships = theData.ships
|
||||
else
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: no rotor wing unit data.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
-- and now the same for static objects
|
||||
if theData.statics then
|
||||
for name, staticData in pairs(theData.statics) do
|
||||
@ -177,8 +432,6 @@ function unitPersistence.loadMission()
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: static <" .. name .. "> is late activate, no update", 30)
|
||||
end
|
||||
--elseif not theStatic then
|
||||
-- mismatchWarning = true
|
||||
elseif staticData.category == "Heliports" then
|
||||
-- FARPS are static objects that HATE to be
|
||||
-- messed with, so we don't
|
||||
@ -188,7 +441,7 @@ function unitPersistence.loadMission()
|
||||
else
|
||||
local newStatic = dcsCommon.clone(staticData)
|
||||
-- add link info if it exists
|
||||
newStatic.linkUnit = cfxMX.linkByName[name]
|
||||
newStatic.linkUnit = cfxMX.linkByName[staticData.groupName]
|
||||
if newStatic.linkUnit and unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: linked static <" .. name .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
||||
end
|
||||
@ -197,15 +450,16 @@ function unitPersistence.loadMission()
|
||||
-- spawn new one, replacing same.named old, dead if required
|
||||
gStatic = coalition.addStaticObject(cty, newStatic)
|
||||
if not gStatic then
|
||||
trigger.action.outText("+++ failed to add modified static <" .. name .. ">")
|
||||
trigger.action.outText("+++ failed to add modified static <" .. name .. ">", 30)
|
||||
end
|
||||
if unitPersistence.verbose then
|
||||
local note = ""
|
||||
if newStatic.dead then note = " (dead)" end
|
||||
-- trigger.action.outText("+++unitPersistence: updated static <" .. name .. "> for cty <" .. cty .. ">" .. note, 30)
|
||||
trigger.action.outText("+++unitPersistence: updated static <" .. name .. "> for cty <" .. cty .. ">" .. note, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
unitPersistence.statics = theData.statics
|
||||
end
|
||||
|
||||
if mismatchWarning then
|
||||
@ -268,6 +522,7 @@ function unitPersistence.start()
|
||||
-- create a local copy of the entire groundForces data that
|
||||
-- we maintain internally. It's fixed, and we work on our
|
||||
-- own copy for speed
|
||||
unitPersistence.groundTroops = {}
|
||||
for gname, data in pairs(cfxMX.allGroundByName) do
|
||||
local gd = dcsCommon.clone(data) -- copy the record
|
||||
gd.isDead = false -- init new field to alive
|
||||
@ -275,14 +530,71 @@ function unitPersistence.start()
|
||||
gd.cat = cfxMX.catText2ID("vehicle")
|
||||
local gGroup = Group.getByName(gname)
|
||||
if not gGroup then
|
||||
trigger.action.outText("+++warning: group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
trigger.action.outText("+++warning: ground group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
local firstUnit = gGroup:getUnit(1)
|
||||
gd.cty = firstUnit:getCountry()
|
||||
gd.cty = cfxMX.countryByName[gname]
|
||||
unitPersistence.groundTroops[gname] = gd
|
||||
end
|
||||
end
|
||||
|
||||
-- now add all aircraft
|
||||
unitPersistence.fixedWing = {}
|
||||
for gname, data in pairs(cfxMX.allFixedByName) do
|
||||
local gd = dcsCommon.clone(data) -- copy the record
|
||||
gd.isDead = false -- init new field to alive
|
||||
gd.isPlayer = (cfxMX.playerGroupByName[gname] ~= nil)
|
||||
-- coalition and country
|
||||
gd.cat = cfxMX.catText2ID("plane") -- 0
|
||||
gd.cty = cfxMX.countryByName[gname]
|
||||
local gGroup = Group.getByName(gname)
|
||||
if gd.isPlayer then
|
||||
-- skip
|
||||
elseif not gGroup then
|
||||
trigger.action.outText("+++warning: fixed-wing group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
unitPersistence.fixedWing[gname] = gd
|
||||
end
|
||||
end
|
||||
|
||||
-- and helicopters
|
||||
unitPersistence.rotorWing = {}
|
||||
for gname, data in pairs(cfxMX.allHeloByName) do
|
||||
local gd = dcsCommon.clone(data) -- copy the record
|
||||
gd.isDead = false -- init new field to alive
|
||||
gd.isPlayer = (cfxMX.playerGroupByName[gname] ~= nil)
|
||||
-- coalition and country
|
||||
gd.cat = cfxMX.catText2ID("helicopter") -- 1
|
||||
gd.cty = cfxMX.countryByName[gname]
|
||||
local gGroup = Group.getByName(gname)
|
||||
if gd.isPlayer then
|
||||
-- skip
|
||||
elseif not gGroup then
|
||||
trigger.action.outText("+++warning: helo group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
unitPersistence.rotorWing[gname] = gd
|
||||
end
|
||||
end
|
||||
|
||||
-- finally ships
|
||||
-- we only do ships to remove them when they are dead because
|
||||
-- messing with ships can give problems: aircraft carriers.
|
||||
unitPersistence.ships = {}
|
||||
for gname, data in pairs(cfxMX.allSeaByName) do
|
||||
local gd = dcsCommon.clone(data) -- copy the record
|
||||
gd.isDead = false -- init new field to alive
|
||||
-- coalition and country
|
||||
gd.cat = cfxMX.catText2ID("ship") -- 3
|
||||
gd.cty = cfxMX.countryByName[gname]
|
||||
local gGroup = Group.getByName(gname)
|
||||
if gd.isPlayer then
|
||||
-- skip
|
||||
elseif not gGroup then
|
||||
trigger.action.outText("+++warning: ship group <" .. gname .. "> does not exist in-game!?", 30)
|
||||
else
|
||||
unitPersistence.ships[gname] = gd
|
||||
end
|
||||
end
|
||||
|
||||
-- make local copies of all static MX objects
|
||||
-- that we also maintain internally, and convert them to game
|
||||
-- spawnable objects
|
||||
@ -294,19 +606,18 @@ function unitPersistence.start()
|
||||
local theStatic = dcsCommon.clone(staticData)
|
||||
theStatic.isDead = false
|
||||
theStatic.groupId = mxData.groupId
|
||||
theStatic.groupName = name -- save top-level name
|
||||
theStatic.cat = cfxMX.catText2ID("static")
|
||||
theStatic.cty = cfxMX.countryByName[name]
|
||||
--trigger.action.outText("Processed MX static group <" .. name .. ">, object <" .. name .. "> with cty <" .. theStatic.cty .. ">",30)
|
||||
local gameOb = StaticObject.getByName(theStatic.name)
|
||||
if not gameOb then
|
||||
if unitPersistence.verbose then
|
||||
trigger.action.outText("+++unitPersistence: static object <" .. theStatic.name .. "> has late activation", 30)
|
||||
end
|
||||
theStatic.lateActivation = true
|
||||
else
|
||||
--theStatic.cty = gameOb:getCountry()
|
||||
--unitPersistence.statics[theStatic.name] = theStatic
|
||||
end
|
||||
unitPersistence.statics[theStatic.name] = theStatic
|
||||
unitPersistence.statics[theStatic.name] = theStatic -- HERE WE CHANGE FROM GROUP NAME TO STATIC NAME!!!
|
||||
end
|
||||
end
|
||||
|
||||
@ -328,6 +639,11 @@ if not unitPersistence.start() then
|
||||
unitPersistence = nil
|
||||
end
|
||||
--[[--
|
||||
ToDo: linked statics and linked units on restore
|
||||
- waypoint analysis for aircraft so
|
||||
- whan they have take off as Inital WP and they are moving
|
||||
then we change the first WP to 'turning point'
|
||||
- waypoint analysis to match waypoint to position. very difficult if waypoints describe a circle.
|
||||
- group analysis for carriers to be able to process groups that do
|
||||
not contain carriers
|
||||
|
||||
--]]--
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user