mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.11
cloneZone onRoad, more persistence
This commit is contained in:
parent
66686acbf5
commit
7ad32b89c4
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
FARPZones = {}
|
FARPZones = {}
|
||||||
FARPZones.version = "1.1.0"
|
FARPZones.version = "1.2.0"
|
||||||
FARPZones.verbose = false
|
FARPZones.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
@ -11,6 +11,9 @@ FARPZones.verbose = false
|
|||||||
- rFormation attribute added
|
- rFormation attribute added
|
||||||
- verbose flag
|
- verbose flag
|
||||||
- verbose cleanup ("FZ: something happened")
|
- verbose cleanup ("FZ: something happened")
|
||||||
|
1.2.0 - persistence
|
||||||
|
- handles contested state
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -75,7 +78,7 @@ FARPZones.spinUpDelay = 30 -- seconds until FARP becomes operational after captu
|
|||||||
|
|
||||||
|
|
||||||
FARPZones.allFARPZones = {}
|
FARPZones.allFARPZones = {}
|
||||||
FARPZones.startingUp = false
|
FARPZones.startingUp = false -- not needed / read anywhere
|
||||||
|
|
||||||
-- FARP ZONE ACCESS
|
-- FARP ZONE ACCESS
|
||||||
function FARPZones.addFARPZone(aFARP)
|
function FARPZones.addFARPZone(aFARP)
|
||||||
@ -90,6 +93,15 @@ function FARPZones.getFARPForZone(aZone)
|
|||||||
return FARPZones.allFARPZones[aZone]
|
return FARPZones.allFARPZones[aZone]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function FARPZones.getFARPZoneByName(aName)
|
||||||
|
for aZone, aFarp in pairs(FARPZones.allFARPZones) do
|
||||||
|
if aZone.name == aName then return aFarp end
|
||||||
|
-- we assume zone.name == farp.name
|
||||||
|
end
|
||||||
|
trigger.action.outText("Unable to find FARP <" .. aName .. ">", 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function FARPZones.getFARPZoneForFARP(aFarp)
|
function FARPZones.getFARPZoneForFARP(aFarp)
|
||||||
-- find the first FARP zone that associates with
|
-- find the first FARP zone that associates with
|
||||||
-- aFARP (an airField)
|
-- aFARP (an airField)
|
||||||
@ -123,9 +135,9 @@ function FARPZones.createFARPFromZone(aZone)
|
|||||||
if #mapFarps == 0 then
|
if #mapFarps == 0 then
|
||||||
trigger.action.outText("***Farp Zones: no FARP found for zone " .. aZone.name, 30)
|
trigger.action.outText("***Farp Zones: no FARP found for zone " .. aZone.name, 30)
|
||||||
else
|
else
|
||||||
for idx, aFarp in pairs(mapFarps) do
|
--for idx, aFarp in pairs(mapFarps) do
|
||||||
-- trigger.action.outText("Associated FARP " .. aFarp:getName() .. " with FARP Zone " .. aZone.name, 30)
|
-- trigger.action.outText("Associated FARP " .. aFarp:getName() .. " with FARP Zone " .. aZone.name, 30)
|
||||||
end
|
--end
|
||||||
|
|
||||||
theFarp.mainFarp = theFarp.myFarps[1]
|
theFarp.mainFarp = theFarp.myFarps[1]
|
||||||
theFarp.point = theFarp.mainFarp:getPoint() -- this is FARP, not zone!!!
|
theFarp.point = theFarp.mainFarp:getPoint() -- this is FARP, not zone!!!
|
||||||
@ -317,7 +329,7 @@ function FARPZones.produceVehicles(theFarp)
|
|||||||
local theCoalition = theFarp.owner
|
local theCoalition = theFarp.owner
|
||||||
|
|
||||||
if theTypes ~= "none" then
|
if theTypes ~= "none" then
|
||||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
theCoalition,
|
theCoalition,
|
||||||
theFarp.name .. "-D" .. theFarp.count, -- must be unique
|
theFarp.name .. "-D" .. theFarp.count, -- must be unique
|
||||||
theFarp.defZone,
|
theFarp.defZone,
|
||||||
@ -326,10 +338,11 @@ function FARPZones.produceVehicles(theFarp)
|
|||||||
theFarp.defHeading)
|
theFarp.defHeading)
|
||||||
-- we do not add these troops to ground troop management
|
-- we do not add these troops to ground troop management
|
||||||
theFarp.defenders = theGroup -- but we retain a handle just in case
|
theFarp.defenders = theGroup -- but we retain a handle just in case
|
||||||
|
theFarp.defenderData = theData
|
||||||
end
|
end
|
||||||
|
|
||||||
unitTypes = FARPZones.resourceTypes
|
unitTypes = FARPZones.resourceTypes
|
||||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
theCoalition,
|
theCoalition,
|
||||||
theFarp.name .. "-R" .. theFarp.count, -- must be unique
|
theFarp.name .. "-R" .. theFarp.count, -- must be unique
|
||||||
theFarp.resZone,
|
theFarp.resZone,
|
||||||
@ -337,7 +350,7 @@ function FARPZones.produceVehicles(theFarp)
|
|||||||
"line_v",
|
"line_v",
|
||||||
theFarp.resHeading)
|
theFarp.resHeading)
|
||||||
theFarp.resources = theGroup
|
theFarp.resources = theGroup
|
||||||
|
theFarp.resourceData = theData
|
||||||
-- update unique counter
|
-- update unique counter
|
||||||
theFarp.count = theFarp.count + 1
|
theFarp.count = theFarp.count + 1
|
||||||
end
|
end
|
||||||
@ -382,6 +395,23 @@ function FARPZones.somethingHappened(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local newOwner = aFarp:getCoalition()
|
local newOwner = aFarp:getCoalition()
|
||||||
|
-- now, because we can load from file, we may get a notice
|
||||||
|
-- that a newly loaded state disagrees with new game state
|
||||||
|
-- if so, we simply wink and exit
|
||||||
|
if newOwner == zonedFarp.owner then
|
||||||
|
trigger.action.outText("FARP <" .. zonedFarp.name .. "> aligned with persisted data", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- let's ignore the owner = 3 (contested). Usually does not
|
||||||
|
-- happen with an event, but let's be prepared
|
||||||
|
if newOwner == 3 then
|
||||||
|
if FARPZones.verbose then
|
||||||
|
trigger.action.outText("FARP <" .. zonedFarp.name .. "> has become contested", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local blueRed = "Red"
|
local blueRed = "Red"
|
||||||
if newOwner == 2 then blueRed = "Blue" end
|
if newOwner == 2 then blueRed = "Blue" end
|
||||||
trigger.action.outText("FARP " .. zonedFarp.zone.name .. " captured by " .. blueRed .."!", 30)
|
trigger.action.outText("FARP " .. zonedFarp.zone.name .. " captured by " .. blueRed .."!", 30)
|
||||||
@ -408,6 +438,80 @@ function FARPZones.somethingHappened(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- LOAD / SAVE
|
||||||
|
--
|
||||||
|
function FARPZones.saveData()
|
||||||
|
local theData = {}
|
||||||
|
if FARPZones.verbose then
|
||||||
|
trigger.action.outText("+++frpZ: enter saveData", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
local farps = {}
|
||||||
|
-- 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)
|
||||||
|
local fData = {}
|
||||||
|
fData.owner = theFARP.owner
|
||||||
|
fData.defenderData = dcsCommon.clone(theFARP.defenderData)
|
||||||
|
fData.resourceData = dcsCommon.clone(theFARP.resourceData)
|
||||||
|
dcsCommon.synchGroupData(fData.defenderData)
|
||||||
|
if fData.defenderData and #fData.defenderData.units<1 then
|
||||||
|
fData.defenderData = nil
|
||||||
|
end
|
||||||
|
dcsCommon.synchGroupData(fData.resourceData)
|
||||||
|
if fData.resourceData and #fData.resourceData.units<1 then
|
||||||
|
fData.resourceData = nil
|
||||||
|
end
|
||||||
|
farps[fName] = fData
|
||||||
|
end
|
||||||
|
|
||||||
|
theData.farps = farps
|
||||||
|
return theData
|
||||||
|
end
|
||||||
|
|
||||||
|
function FARPZones.loadMission()
|
||||||
|
local theData = persistence.getSavedDataForModule("FARPZones")
|
||||||
|
if not theData then
|
||||||
|
if FARPZones.verbose then
|
||||||
|
trigger.action.outText("frpZ: no save date received, skipping.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local farps = theData.farps
|
||||||
|
if farps then
|
||||||
|
for fName, fData in pairs(farps) do
|
||||||
|
local theFARP = FARPZones.getFARPZoneByName(fName)
|
||||||
|
if theFARP then
|
||||||
|
theFARP.owner = fData.owner
|
||||||
|
theFARP.zone.owner = fData.owner
|
||||||
|
theFARP.defenderData = dcsCommon.clone(fData.defenderData)
|
||||||
|
local groupData = fData.defenderData
|
||||||
|
if groupData and #groupData.units > 0 then
|
||||||
|
local cty = groupData.cty
|
||||||
|
local cat = groupData.cat
|
||||||
|
theFARP.defenders = coalition.addGroup(cty, cat, groupData)
|
||||||
|
end
|
||||||
|
|
||||||
|
groupData = fData.resourceData
|
||||||
|
if groupData and #groupData.units > 0 then
|
||||||
|
local cty = groupData.cty
|
||||||
|
local cat = groupData.cat
|
||||||
|
theFARP.resources = coalition.addGroup(cty, cat, groupData)
|
||||||
|
end
|
||||||
|
FARPZones.drawFARPCircleInMap(theFARP) -- mark in map
|
||||||
|
if (not theFARP.defenders) and (not theFARP.resources) then
|
||||||
|
-- we instigate a resource and defender drop
|
||||||
|
FARPZones.produceVehicles(theFARP)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("frpZ: persistence: FARP <" .. fName .. "> no longer exists in mission, skipping", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Start
|
-- Start
|
||||||
@ -439,7 +543,7 @@ function FARPZones.start()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
FARPZones.startingUp = true
|
FARPZones.startingUp = true -- not needed / read anywhere
|
||||||
|
|
||||||
-- read config zone
|
-- read config zone
|
||||||
FARPZones.readConfig()
|
FARPZones.readConfig()
|
||||||
@ -449,19 +553,41 @@ function FARPZones.start()
|
|||||||
FARPZones.preProcessor,
|
FARPZones.preProcessor,
|
||||||
FARPZones.postProcessor)
|
FARPZones.postProcessor)
|
||||||
|
|
||||||
|
-- set up persistence BEFORE we read zones, so weh know the
|
||||||
|
-- score during init phase
|
||||||
|
local hasSaveData = false
|
||||||
|
if persistence then
|
||||||
|
-- sign up for persistence
|
||||||
|
callbacks = {}
|
||||||
|
callbacks.persistData = FARPZones.saveData
|
||||||
|
persistence.registerModule("FARPZones", callbacks)
|
||||||
|
hasSaveData = persistence.hasData
|
||||||
|
end
|
||||||
|
|
||||||
-- collect all FARP Zones
|
-- collect all FARP Zones
|
||||||
local theZones = cfxZones.getZonesWithAttributeNamed("FARP")
|
local theZones = cfxZones.getZonesWithAttributeNamed("FARP")
|
||||||
for k, aZone in pairs(theZones) do
|
for k, aZone in pairs(theZones) do
|
||||||
local aFARP = FARPZones.createFARPFromZone(aZone) -- read attributes from DCS
|
local aFARP = FARPZones.createFARPFromZone(aZone) -- read attributes from DCS
|
||||||
FARPZones.addFARPZone(aFARP) -- add to managed zones
|
FARPZones.addFARPZone(aFARP) -- add to managed zones
|
||||||
FARPZones.drawFARPCircleInMap(aFARP) -- mark in map
|
-- moved FARPZones.drawFARPCircleInMap(aFARP) -- mark in map
|
||||||
FARPZones.produceVehicles(aFARP) -- allocate initial vehicles
|
-- moved FARPZones.produceVehicles(aFARP) -- allocate initial vehicles
|
||||||
if FARPZones.verbose then
|
if FARPZones.verbose then
|
||||||
trigger.action.outText("processed FARP <" .. aZone.name .. "> now owned by " .. aZone.owner, 30)
|
trigger.action.outText("processed FARP <" .. aZone.name .. "> now owned by " .. aZone.owner, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
FARPZones.startingUp = false
|
-- now produce all vehicles - whether from
|
||||||
|
-- save, or clean from start
|
||||||
|
if hasSaveData then
|
||||||
|
FARPZones.loadMission()
|
||||||
|
else
|
||||||
|
for idx, aFARP in pairs (FARPZones.allFARPZones) do
|
||||||
|
FARPZones.drawFARPCircleInMap(aFARP) -- mark in map
|
||||||
|
FARPZones.produceVehicles(aFARP) -- allocate initial vehicles
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FARPZones.startingUp = false -- not needed / read anywhere
|
||||||
|
|
||||||
trigger.action.outText("cf/x FARP Zones v" .. FARPZones.version .. " started", 30)
|
trigger.action.outText("cf/x FARP Zones v" .. FARPZones.version .. " started", 30)
|
||||||
return true
|
return true
|
||||||
@ -480,4 +606,6 @@ Improvements:
|
|||||||
|
|
||||||
make hidden farps only appear for owning side
|
make hidden farps only appear for owning side
|
||||||
|
|
||||||
|
make farps repair their service vehicles after a time, or simply refresh them every x minutes, to make the algo simpler
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -297,6 +297,10 @@ function cfxArtilleryZones.doFireAt(aZone, maxDistFromCenter)
|
|||||||
cfxArtilleryZones.invokeCallbacksFor('fire', aZone, {})
|
cfxArtilleryZones.invokeCallbacksFor('fire', aZone, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- API main entry call for firing at zone
|
||||||
|
-- invokes doFireAt()
|
||||||
|
--
|
||||||
function cfxArtilleryZones.simFireAtZone(aZone, aGroup, dist)
|
function cfxArtilleryZones.simFireAtZone(aZone, aGroup, dist)
|
||||||
|
|
||||||
if not dist then dist = aZone.spotRange end
|
if not dist then dist = aZone.spotRange end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxMX = {}
|
cfxMX = {}
|
||||||
cfxMX.version = "1.2.0"
|
cfxMX.version = "1.2.1"
|
||||||
cfxMX.verbose = false
|
cfxMX.verbose = false
|
||||||
--[[--
|
--[[--
|
||||||
Mission data decoder. Access to ME-built mission structures
|
Mission data decoder. Access to ME-built mission structures
|
||||||
@ -16,14 +16,15 @@ cfxMX.verbose = false
|
|||||||
1.2.0 - added group name reference table
|
1.2.0 - added group name reference table
|
||||||
- added group type reference
|
- added group type reference
|
||||||
- added references for allFixed, allHelo, allGround, allSea, allStatic
|
- added references for allFixed, allHelo, allGround, allSea, allStatic
|
||||||
|
1.2.1 - added countryByName
|
||||||
|
- added linkByName
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxMX.groupNamesByID = {}
|
cfxMX.groupNamesByID = {}
|
||||||
cfxMX.groupIDbyName = {}
|
cfxMX.groupIDbyName = {}
|
||||||
cfxMX.groupDataByName = {}
|
cfxMX.groupDataByName = {}
|
||||||
|
cfxMX.countryByName ={}
|
||||||
|
cfxMX.linkByName = {}
|
||||||
cfxMX.allFixedByName = {}
|
cfxMX.allFixedByName = {}
|
||||||
cfxMX.allHeloByName = {}
|
cfxMX.allHeloByName = {}
|
||||||
cfxMX.allGroundByName = {}
|
cfxMX.allGroundByName = {}
|
||||||
@ -193,11 +194,20 @@ function cfxMX.createCrossReferences()
|
|||||||
local category = obj_type_name
|
local category = obj_type_name
|
||||||
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's at least one group!
|
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's at least one group!
|
||||||
for group_num, group_data in pairs(obj_type_data.group) do
|
for group_num, group_data in pairs(obj_type_data.group) do
|
||||||
|
|
||||||
local aName = group_data.name
|
local aName = group_data.name
|
||||||
local aID = group_data.groupId
|
local aID = group_data.groupId
|
||||||
|
-- get linkUnit info if it exists
|
||||||
|
local linkUnit = nil
|
||||||
|
if group_data and group_data.route and group_data.route and group_data.route.points[1] then
|
||||||
|
linkUnit = group_data.route.points[1].linkUnit
|
||||||
|
cfxMX.linkByName[aName] = linkUnit
|
||||||
|
end
|
||||||
|
|
||||||
cfxMX.groupNamesByID[aID] = aName
|
cfxMX.groupNamesByID[aID] = aName
|
||||||
cfxMX.groupIDbyName[aName] = aID
|
cfxMX.groupIDbyName[aName] = aID
|
||||||
cfxMX.groupDataByName[aName] = group_data
|
cfxMX.groupDataByName[aName] = group_data
|
||||||
|
cfxMX.countryByName[aName] = cntry_id
|
||||||
-- now make the type-specific xrefs
|
-- now make the type-specific xrefs
|
||||||
if obj_type_name == "helicopter" then
|
if obj_type_name == "helicopter" then
|
||||||
cfxMX.allHeloByName[aName] = group_data
|
cfxMX.allHeloByName[aName] = group_data
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
cfxObjectDestructDetector = {}
|
cfxObjectDestructDetector = {}
|
||||||
cfxObjectDestructDetector.version = "1.2.0"
|
cfxObjectDestructDetector.version = "1.3.0"
|
||||||
|
cfxObjectDestructDetector.verbose = false
|
||||||
cfxObjectDestructDetector.requiredLibs = {
|
cfxObjectDestructDetector.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
cfxObjectDestructDetector.verbose = false
|
|
||||||
--[[--
|
--[[--
|
||||||
VERSION HISTORY
|
VERSION HISTORY
|
||||||
1.0.0 initial version, based on parashoo, arty zones
|
1.0.0 initial version, based on parashoo, arty zones
|
||||||
1.0.1 fixed bug: trigger.MISC.getUserFlag()
|
1.0.1 fixed bug: trigger.MISC.getUserFlag()
|
||||||
1.1.0 added support for method, f! and destroyed!
|
1.1.0 added support for method, f! and destroyed!
|
||||||
1.2.0 DML / Watchflag support
|
1.2.0 DML / Watchflag support
|
||||||
|
1.3.0 Persistence support
|
||||||
|
|
||||||
|
|
||||||
Detect when an object with OBJECT ID as assigned in ME dies
|
Detect when an object with OBJECT ID as assigned in ME dies
|
||||||
*** EXTENDS ZONES
|
*** EXTENDS ZONES
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
cfxObjectDestructDetector.objectZones = {}
|
cfxObjectDestructDetector.objectZones = {}
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -41,6 +43,14 @@ function cfxObjectDestructDetector.addObjectDetectZone(aZone)
|
|||||||
table.insert(cfxObjectDestructDetector.objectZones, aZone)
|
table.insert(cfxObjectDestructDetector.objectZones, aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxObjectDestructDetector.getObjectDetectZoneByName(aName)
|
||||||
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
||||||
|
if aZone.name == aName then return aZone end
|
||||||
|
end
|
||||||
|
-- add landHeight to this zone
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- processing of zones
|
-- processing of zones
|
||||||
--
|
--
|
||||||
@ -48,6 +58,10 @@ function cfxObjectDestructDetector.processObjectDestructZone(aZone)
|
|||||||
aZone.name = cfxZones.getStringFromZoneProperty(aZone, "NAME", aZone.name)
|
aZone.name = cfxZones.getStringFromZoneProperty(aZone, "NAME", aZone.name)
|
||||||
-- aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "coalition", 0)
|
-- aZone.coalition = cfxZones.getCoalitionFromZoneProperty(aZone, "coalition", 0)
|
||||||
aZone.ID = cfxZones.getNumberFromZoneProperty(aZone, "OBJECT ID", 1) -- THIS!
|
aZone.ID = cfxZones.getNumberFromZoneProperty(aZone, "OBJECT ID", 1) -- THIS!
|
||||||
|
-- persistence interface
|
||||||
|
aZone.isDestroyed = false
|
||||||
|
|
||||||
|
--[[-- old code, to be decom'd --]]--
|
||||||
if cfxZones.hasProperty(aZone, "setFlag") then
|
if cfxZones.hasProperty(aZone, "setFlag") then
|
||||||
aZone.setFlag = cfxZones.getStringFromZoneProperty(aZone, "setFlag", "999")
|
aZone.setFlag = cfxZones.getStringFromZoneProperty(aZone, "setFlag", "999")
|
||||||
end
|
end
|
||||||
@ -73,16 +87,16 @@ function cfxObjectDestructDetector.processObjectDestructZone(aZone)
|
|||||||
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
|
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- new method support
|
-- DML method support
|
||||||
aZone.oddMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "flip")
|
aZone.oddMethod = cfxZones.getStringFromZoneProperty(aZone, "method", "inc")
|
||||||
if cfxZones.hasProperty(aZone, "oddMethod") then
|
if cfxZones.hasProperty(aZone, "oddMethod") then
|
||||||
aZone.oddMethod = cfxZones.getStringFromZoneProperty(aZone, "oddMethod", "flip")
|
aZone.oddMethod = cfxZones.getStringFromZoneProperty(aZone, "oddMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(aZone, "f!") then
|
-- we now always have that property
|
||||||
aZone.outDestroyFlag = cfxZones.getStringFromZoneProperty(aZone, "f!", "*none")
|
aZone.outDestroyFlag = cfxZones.getStringFromZoneProperty(aZone, "f!", "*none")
|
||||||
end
|
|
||||||
if cfxZones.hasProperty(aZone, "destroyed!") then
|
if cfxZones.hasProperty(aZone, "destroyed!") then
|
||||||
aZone.outDestroyFlag = cfxZones.getStringFromZoneProperty(aZone, "destroyed!", "*none")
|
aZone.outDestroyFlag = cfxZones.getStringFromZoneProperty(aZone, "destroyed!", "*none")
|
||||||
end
|
end
|
||||||
@ -102,7 +116,7 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
if not id then return end
|
if not id then return end
|
||||||
|
|
||||||
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
||||||
if aZone.ID == id then
|
if (not aZone.isDestroyed) and aZone.ID == id then
|
||||||
-- flag manipulation
|
-- flag manipulation
|
||||||
-- OLD FLAG SUPPORT, SOON TO BE REMOVED
|
-- OLD FLAG SUPPORT, SOON TO BE REMOVED
|
||||||
if aZone.setFlag then
|
if aZone.setFlag then
|
||||||
@ -128,7 +142,7 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
|
|
||||||
-- invoke callbacks
|
-- invoke callbacks
|
||||||
cfxObjectDestructDetector.invokeCallbacksFor(aZone)
|
cfxObjectDestructDetector.invokeCallbacksFor(aZone)
|
||||||
if cfxObjectDestructDetector.verbose then
|
if aZone.verbose or cfxObjectDestructDetector.verbose then
|
||||||
trigger.action.outText("OBJECT KILL: " .. id, 30)
|
trigger.action.outText("OBJECT KILL: " .. id, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -136,6 +150,9 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
-- for better performance since it cant
|
-- for better performance since it cant
|
||||||
-- die twice
|
-- die twice
|
||||||
|
|
||||||
|
-- save state for persistence
|
||||||
|
aZone.isDestroyed = true
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -143,8 +160,85 @@ function cfxObjectDestructDetector:onEvent(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
-- add event handler
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- persistence: save and load data
|
||||||
|
--
|
||||||
|
function cfxObjectDestructDetector.saveData() -- invoked by persistence
|
||||||
|
local theData = {}
|
||||||
|
local zoneInfo = {}
|
||||||
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
||||||
|
-- save all pertinent info. in our case, it's just
|
||||||
|
-- the isDestroyed and flag info info
|
||||||
|
info = {}
|
||||||
|
info.isDestroyed = aZone.isDestroyed
|
||||||
|
info.outDestroyVal = cfxZones.getFlagValue(aZone.outDestroyFlag, aZone)
|
||||||
|
zoneInfo[aZone.name] = info
|
||||||
|
end
|
||||||
|
-- expasion proof: assign as own field
|
||||||
|
theData.zoneInfo = zoneInfo
|
||||||
|
return theData
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxObjectDestructDetector.loadMission()
|
||||||
|
if cfxObjectDestructDetector.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence - loading data", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
local theData = persistence.getSavedDataForModule("cfxObjectDestructDetector")
|
||||||
|
if not theData then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- iterate the data, and fail graciously if
|
||||||
|
-- we can't find a zone. it's probably beed edited out
|
||||||
|
local zoneInfo = theData.zoneInfo
|
||||||
|
if not zoneInfo then return end
|
||||||
|
if cfxObjectDestructDetector.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence - processing data", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
for zName, info in pairs (zoneInfo) do
|
||||||
|
local theZone = cfxObjectDestructDetector.getObjectDetectZoneByName(zName)
|
||||||
|
if theZone then
|
||||||
|
theZone.isDestroyed = info.isDestroyed
|
||||||
|
cfxZones.setFlagValue(theZone.outDestroyFlag, info.outDestroyVal, theZone)
|
||||||
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence setting flag <" .. theZone.outDestroyFlag .. "> to <" .. info.outDestroyVal .. ">",30)
|
||||||
|
end
|
||||||
|
local theName = tostring(theZone.ID)
|
||||||
|
if info.isDestroyed then
|
||||||
|
-- We now get the scenery object in that zone
|
||||||
|
-- and remove it
|
||||||
|
-- note that dcsCommon methods use DCS zones, not cfx
|
||||||
|
local theObject = dcsCommon.getSceneryObjectInZoneByName(theName, theZone.dcsZone)
|
||||||
|
if theObject then
|
||||||
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence removing dead scenery object <" .. theName .. ">",30)
|
||||||
|
end
|
||||||
|
theObject:destroy()
|
||||||
|
else
|
||||||
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence - can't find scenery objects <" .. theName .. ">, skipped destruction",30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence - scenery objects <" .. theName .. "> is healthy",30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++oDDet: persistence - can't find detector <" .. zName .. "> on load. skipping", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if cfxObjectDestructDetector.verbose then
|
||||||
|
trigger.action.outText("+++oDDet: persistence - processing complete", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- start
|
||||||
|
--
|
||||||
|
|
||||||
function cfxObjectDestructDetector.start()
|
function cfxObjectDestructDetector.start()
|
||||||
if not dcsCommon.libCheck("cfx Object Destruct Detector",
|
if not dcsCommon.libCheck("cfx Object Destruct Detector",
|
||||||
@ -152,20 +246,38 @@ function cfxObjectDestructDetector.start()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- collect all zones with 'smoke' attribute
|
-- collect all zones with 'OBJECT id' attribute
|
||||||
-- collect all spawn zones
|
-- collect all spawn zones
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("OBJECT ID")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("OBJECT ID")
|
||||||
|
|
||||||
-- now create a spawner for all, add them to the spawner updater, and spawn for all zones that are not
|
|
||||||
-- paused
|
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
cfxObjectDestructDetector.processObjectDestructZone(aZone) -- process attribute and add to zone properties (extend zone)
|
cfxObjectDestructDetector.processObjectDestructZone(aZone) -- process attribute and add to zone properties (extend zone)
|
||||||
cfxObjectDestructDetector.addObjectDetectZone(aZone) -- remember it so we can smoke it
|
cfxObjectDestructDetector.addObjectDetectZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add myself as event handler
|
-- add myself as event handler
|
||||||
world.addEventHandler(cfxObjectDestructDetector)
|
world.addEventHandler(cfxObjectDestructDetector)
|
||||||
|
|
||||||
|
-- persistence: see if we have any data to process
|
||||||
|
-- for all our zones, and sign up for data saving
|
||||||
|
if persistence and persistence.active then
|
||||||
|
-- sign up for saves
|
||||||
|
callbacks = {}
|
||||||
|
callbacks.persistData = cfxObjectDestructDetector.saveData
|
||||||
|
persistence.registerModule("cfxObjectDestructDetector", callbacks)
|
||||||
|
|
||||||
|
if persistence.hasData then
|
||||||
|
cfxObjectDestructDetector.loadMission()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if cfxObjectDestructDetector.verbose then
|
||||||
|
trigger.action.outText("no persistence for cfxObjectDestructDetector", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- say hi
|
-- say hi
|
||||||
trigger.action.outText("cfx Object Destruct Zones v" .. cfxObjectDestructDetector.version .. " started.", 30)
|
trigger.action.outText("cfx Object Destruct Zones v" .. cfxObjectDestructDetector.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
cfxOwnedZones = {}
|
cfxOwnedZones = {}
|
||||||
cfxOwnedZones.version = "1.1.2"
|
cfxOwnedZones.version = "1.2.0"
|
||||||
cfxOwnedZones.verbose = false
|
cfxOwnedZones.verbose = false
|
||||||
cfxOwnedZones.announcer = true
|
cfxOwnedZones.announcer = true
|
||||||
|
cfxOwnedZones.name = "cfxOwnedZones"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
|
|
||||||
1.0.3 - added getNearestFriendlyZone
|
1.0.3 - added getNearestFriendlyZone
|
||||||
@ -21,7 +22,7 @@ cfxOwnedZones.announcer = true
|
|||||||
- support of 'none' type string to indicate no attackers/defenders
|
- support of 'none' type string to indicate no attackers/defenders
|
||||||
- updated property access
|
- updated property access
|
||||||
- module check
|
- module check
|
||||||
- cfxOwnedTroop.usesDefenders(aZone)
|
- cfxOwnedZones.usesDefenders(aZone)
|
||||||
- verifyZone
|
- verifyZone
|
||||||
1.0.8 - repairDefenders trims types to allow blanks in
|
1.0.8 - repairDefenders trims types to allow blanks in
|
||||||
type separator
|
type separator
|
||||||
@ -41,6 +42,9 @@ cfxOwnedZones.announcer = true
|
|||||||
- announcer
|
- announcer
|
||||||
1.1.1 - conq+1 flag
|
1.1.1 - conq+1 flag
|
||||||
1.1.2 - corrected type bug in zoneConquered
|
1.1.2 - corrected type bug in zoneConquered
|
||||||
|
1.2.0 - support for persistence
|
||||||
|
- conq+1 --> conquered!
|
||||||
|
- no cfxGroundTroop bug (no delay)
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxOwnedZones.requiredLibs = {
|
cfxOwnedZones.requiredLibs = {
|
||||||
@ -60,6 +64,10 @@ cfxOwnedZones.attackingTime = 300 -- 300 seconds until new attackers are produce
|
|||||||
cfxOwnedZones.shockTime = 200 -- 200 -- 'shocked' period of inactivity
|
cfxOwnedZones.shockTime = 200 -- 200 -- 'shocked' period of inactivity
|
||||||
cfxOwnedZones.repairTime = 200 -- 200 -- time until we raplace one lost unit, also repairs all other units to 100%
|
cfxOwnedZones.repairTime = 200 -- 200 -- time until we raplace one lost unit, also repairs all other units to 100%
|
||||||
|
|
||||||
|
-- persistence: all attackers we ever sent out.
|
||||||
|
-- is regularly verified and cut to size
|
||||||
|
cfxOwnedZones.spawnedAttackers = {}
|
||||||
|
|
||||||
-- owned zones is a module that managers 'conquerable' zones and keeps a
|
-- owned zones is a module that managers 'conquerable' zones and keeps a
|
||||||
-- record of who owns the zone
|
-- record of who owns the zone
|
||||||
-- based on some simple rules that are regularly checked
|
-- based on some simple rules that are regularly checked
|
||||||
@ -166,6 +174,12 @@ function cfxOwnedZones.drawZoneInMap(aZone)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxOwnedZones.getOwnedZoneByName(zName)
|
||||||
|
for zKey, theZone in pairs (cfxOwnedZones.zones) do
|
||||||
|
if theZone.name == zName then return theZone end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.addOwnedZone(aZone)
|
function cfxOwnedZones.addOwnedZone(aZone)
|
||||||
local owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0) -- is already readm read it again
|
local owner = cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0) -- is already readm read it again
|
||||||
@ -203,8 +217,9 @@ function cfxOwnedZones.addOwnedZone(aZone)
|
|||||||
local paused = cfxZones.getBoolFromZoneProperty(aZone, "paused", false)
|
local paused = cfxZones.getBoolFromZoneProperty(aZone, "paused", false)
|
||||||
aZone.paused = paused
|
aZone.paused = paused
|
||||||
|
|
||||||
|
aZone.conqueredFlag = cfxZones.getStringFromZoneProperty(aZone, "conquered!", "*<cfxnone>")
|
||||||
if cfxZones.hasProperty(aZone, "conq+1") then
|
if cfxZones.hasProperty(aZone, "conq+1") then
|
||||||
cfxOwnedZones.conqueredFlag = cfxZones.getNumberFromZoneProperty(theZone, "conq+1", -1)
|
aZone.conqueredFlag = cfxZones.getStringFromZoneProperty(aZone, "conq+1", "*<cfxnone>")
|
||||||
end
|
end
|
||||||
|
|
||||||
aZone.unbeatable = cfxZones.getBoolFromZoneProperty(aZone, "unbeatable", false)
|
aZone.unbeatable = cfxZones.getBoolFromZoneProperty(aZone, "unbeatable", false)
|
||||||
@ -218,7 +233,7 @@ end
|
|||||||
function cfxOwnedZones.verifyZone(aZone)
|
function cfxOwnedZones.verifyZone(aZone)
|
||||||
-- do some sanity checks
|
-- do some sanity checks
|
||||||
if not cfxGroundTroops and (aZone.attackersRED ~= "none" or aZone.attackersBLUE ~= "none") then
|
if not cfxGroundTroops and (aZone.attackersRED ~= "none" or aZone.attackersBLUE ~= "none") then
|
||||||
trigger.action.outText("+++owdZ: " .. aZone.name .. " attackers need cfxGroundTroops to function")
|
trigger.action.outText("+++owdZ: " .. aZone.name .. " attackers need cfxGroundTroops to function", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -375,14 +390,14 @@ function cfxOwnedZones.spawnAttackTroops(theTypes, aZone, aCoalition, aFormation
|
|||||||
|
|
||||||
local spawnZone = cfxZones.createSimpleZone("attkSpawnZone", spawnPoint, aZone.attackRadius)
|
local spawnZone = cfxZones.createSimpleZone("attkSpawnZone", spawnPoint, aZone.attackRadius)
|
||||||
|
|
||||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
aCoalition, -- theCountry,
|
aCoalition, -- theCountry,
|
||||||
aZone.name .. " (A) " .. dcsCommon.numberUUID(), -- must be unique
|
aZone.name .. " (A) " .. dcsCommon.numberUUID(), -- must be unique
|
||||||
spawnZone,
|
spawnZone,
|
||||||
unitTypes,
|
unitTypes,
|
||||||
aFormation, -- outward facing
|
aFormation, -- outward facing
|
||||||
0)
|
0)
|
||||||
return theGroup
|
return theGroup, theData
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.spawnDefensiveTroops(theTypes, aZone, aCoalition, aFormation)
|
function cfxOwnedZones.spawnDefensiveTroops(theTypes, aZone, aCoalition, aFormation)
|
||||||
@ -400,13 +415,13 @@ function cfxOwnedZones.spawnDefensiveTroops(theTypes, aZone, aCoalition, aFormat
|
|||||||
|
|
||||||
--local theCountry = dcsCommon.coalition2county(aCoalition)
|
--local theCountry = dcsCommon.coalition2county(aCoalition)
|
||||||
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
||||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
aCoalition, --theCountry,
|
aCoalition, --theCountry,
|
||||||
aZone.name .. " (D) " .. dcsCommon.numberUUID(), -- must be unique
|
aZone.name .. " (D) " .. dcsCommon.numberUUID(), -- must be unique
|
||||||
spawnZone, unitTypes,
|
spawnZone, unitTypes,
|
||||||
aFormation, -- outward facing
|
aFormation, -- outward facing
|
||||||
0)
|
0)
|
||||||
return theGroup
|
return theGroup, theData
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
-- U P D A T E
|
-- U P D A T E
|
||||||
@ -432,7 +447,13 @@ function cfxOwnedZones.sendOutAttackers(aZone)
|
|||||||
|
|
||||||
if attackers == "none" then return end
|
if attackers == "none" then return end
|
||||||
|
|
||||||
local theGroup = cfxOwnedZones.spawnAttackTroops(attackers, aZone, aZone.owner, aZone.attackFormation)
|
local theGroup, theData = cfxOwnedZones.spawnAttackTroops(attackers, aZone, aZone.owner, aZone.attackFormation)
|
||||||
|
|
||||||
|
local troopData = {}
|
||||||
|
troopData.groupData = theData
|
||||||
|
troopData.orders = "attackOwnedZone" -- lazy coding!
|
||||||
|
troopData.side = aZone.owner
|
||||||
|
cfxOwnedZones.spawnedAttackers[theData.name] = troopData
|
||||||
|
|
||||||
-- submit them to ground troops handler as zoneseekers
|
-- submit them to ground troops handler as zoneseekers
|
||||||
-- and our groundTroops module will handle the rest
|
-- and our groundTroops module will handle the rest
|
||||||
@ -496,10 +517,11 @@ function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- increase conq flag
|
-- increase conq flag
|
||||||
if aZone.conqueredFlag then
|
-- if aZone.conqueredFlag then
|
||||||
local lastVal = trigger.misc.getUserFlag(aZone.conqueredFlag)
|
-- local lastVal = trigger.misc.getUserFlag(aZone.conqueredFlag)
|
||||||
trigger.action.setUserFlag(aZone.conqueredFlag, lastVal + 1)
|
-- trigger.action.setUserFlag(aZone.conqueredFlag, lastVal + 1)
|
||||||
end
|
cfxZones.pollFlag(aZone.conqueredFlag, "inc", aZone)
|
||||||
|
-- end
|
||||||
-- invoke callbacks now
|
-- invoke callbacks now
|
||||||
cfxOwnedZones.invokeConqueredCallbacks(aZone, theSide, formerOwner)
|
cfxOwnedZones.invokeConqueredCallbacks(aZone, theSide, formerOwner)
|
||||||
|
|
||||||
@ -567,7 +589,7 @@ function cfxOwnedZones.repairDefenders(aZone)
|
|||||||
-- now livingTypes holds the full array of units we need to spawn
|
-- now livingTypes holds the full array of units we need to spawn
|
||||||
local theCountry = dcsCommon.getACountryForCoalition(aZone.owner)
|
local theCountry = dcsCommon.getACountryForCoalition(aZone.owner)
|
||||||
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
local spawnZone = cfxZones.createSimpleZone("spawnZone", aZone.point, aZone.spawnRadius)
|
||||||
local theGroup = cfxZones.createGroundUnitsInZoneForCoalition (
|
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||||
aZone.owner, -- was wrongly: theCountry
|
aZone.owner, -- was wrongly: theCountry
|
||||||
aZone.name .. dcsCommon.numberUUID(), -- must be unique
|
aZone.name .. dcsCommon.numberUUID(), -- must be unique
|
||||||
spawnZone,
|
spawnZone,
|
||||||
@ -599,15 +621,17 @@ function cfxOwnedZones.spawnDefenders(aZone)
|
|||||||
-- if 'none', simply exit
|
-- if 'none', simply exit
|
||||||
if defenders == "none" then return end
|
if defenders == "none" then return end
|
||||||
|
|
||||||
local theGroup = cfxOwnedZones.spawnDefensiveTroops(defenders, aZone, aZone.owner, aZone.formation)
|
local theGroup, theData = cfxOwnedZones.spawnDefensiveTroops(defenders, aZone, aZone.owner, aZone.formation)
|
||||||
-- the troops reamin, so no orders to move, no handing off to ground troop manager
|
-- the troops reamin, so no orders to move, no handing off to ground troop manager
|
||||||
aZone.defenders = theGroup;
|
aZone.defenders = theGroup
|
||||||
|
aZone.defenderData = theData -- used for persistence
|
||||||
if theGroup then
|
if theGroup then
|
||||||
aZone.defenderMax = theGroup:getInitialSize() -- so we can determine if some units were destroyed
|
--aZone.defenderMax = theGroup:getInitialSize() -- so we can determine if some units were destroyed
|
||||||
aZone.lastDefenders = aZone.defenderMax -- if this is larger than current number, someone bit the dust
|
aZone.lastDefenders = theGroup:getInitialSize() --- aZone.defenderMax -- if this is larger than current number, someone bit the dust
|
||||||
--trigger.action.outText("+++ spawned defenders for ".. aZone.name, 30)
|
--trigger.action.outText("+++ spawned defenders for ".. aZone.name, 30)
|
||||||
else
|
else
|
||||||
trigger.action.outText("+++owdZ: WARNING: spawned no defenders for ".. aZone.name, 30)
|
trigger.action.outText("+++owdZ: WARNING: spawned no defenders for ".. aZone.name, 30)
|
||||||
|
aZone.defenderData = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -661,6 +685,10 @@ function cfxOwnedZones.updateZone(aZone)
|
|||||||
-- we have defenders
|
-- we have defenders
|
||||||
if aZone.defenders:isExist() then
|
if aZone.defenders:isExist() then
|
||||||
-- isee if group was damaged
|
-- isee if group was damaged
|
||||||
|
if not aZone.lastDefenders then
|
||||||
|
-- fresh group, probably from persistence, needs init
|
||||||
|
aZone.lastDefenders = -1
|
||||||
|
end
|
||||||
if aZone.defenders:getSize() < aZone.lastDefenders then
|
if aZone.defenders:getSize() < aZone.lastDefenders then
|
||||||
-- yes, at least one unit destroyed
|
-- yes, at least one unit destroyed
|
||||||
aZone.timeStamp = timer.getTime()
|
aZone.timeStamp = timer.getTime()
|
||||||
@ -671,6 +699,8 @@ function cfxOwnedZones.updateZone(aZone)
|
|||||||
aZone.state = "shocked"
|
aZone.state = "shocked"
|
||||||
|
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
aZone.lastDefenders = aZone.defenders:getSize()
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -725,17 +755,19 @@ function cfxOwnedZones.updateZone(aZone)
|
|||||||
-- we are currently rebuilding defenders unit by unit
|
-- we are currently rebuilding defenders unit by unit
|
||||||
if timer.getTime() > aZone.timeStamp + cfxOwnedZones.repairTime then
|
if timer.getTime() > aZone.timeStamp + cfxOwnedZones.repairTime then
|
||||||
aZone.timeStamp = timer.getTime()
|
aZone.timeStamp = timer.getTime()
|
||||||
|
-- wait's up, repair one defender, then check if full strength
|
||||||
cfxOwnedZones.repairDefenders(aZone)
|
cfxOwnedZones.repairDefenders(aZone)
|
||||||
if aZone.defenders:getSize() >= aZone.defenderMax then
|
-- see if we are full strenght and if so go to attack, else set timer to reair the next unit
|
||||||
--
|
if aZone.defenders and aZone.defenders:isExist() and aZone.defenders:getSize() >= aZone.defenders:getInitialSize() then
|
||||||
-- we are at max size, time to produce some attackers
|
-- we are at max size, time to produce some attackers
|
||||||
|
-- progress to next state
|
||||||
nextState = "attacking"
|
nextState = "attacking"
|
||||||
aZone.timeStamp = timer.getTime()
|
aZone.timeStamp = timer.getTime()
|
||||||
if cfxOwnedZones.verbose then
|
if cfxOwnedZones.verbose then
|
||||||
trigger.action.outText("+++owdZ: State " .. aZone.state .. " to " .. nextState .. " for " .. aZone.name, 30)
|
trigger.action.outText("+++owdZ: State " .. aZone.state .. " to " .. nextState .. " for " .. aZone.name, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- see if we are full strenght and if so go to attack, else set timer to reair the next unit
|
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif aZone.state == "shocked" then
|
elseif aZone.state == "shocked" then
|
||||||
@ -763,6 +795,20 @@ function cfxOwnedZones.updateZone(aZone)
|
|||||||
aZone.state = nextState
|
aZone.state = nextState
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxOwnedZones.GC()
|
||||||
|
-- GC run. remove all my dead remembered troops
|
||||||
|
local filteredAttackers = {}
|
||||||
|
for gName, gData in pairs (cfxOwnedZones.spawnedAttackers) 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
|
||||||
|
cfxOwnedZones.spawnedAttackers = filteredAttackers
|
||||||
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.update()
|
function cfxOwnedZones.update()
|
||||||
cfxOwnedZones.updateSchedule = timer.scheduleFunction(cfxOwnedZones.update, {}, timer.getTime() + 1/cfxOwnedZones.ups)
|
cfxOwnedZones.updateSchedule = timer.scheduleFunction(cfxOwnedZones.update, {}, timer.getTime() + 1/cfxOwnedZones.ups)
|
||||||
|
|
||||||
@ -792,6 +838,11 @@ function cfxOwnedZones.update()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxOwnedZones.houseKeeping()
|
||||||
|
timer.scheduleFunction(cfxOwnedZones.houseKeeping, {}, timer.getTime() + 5 * 60) -- every 5 minutes
|
||||||
|
cfxOwnedZones.GC()
|
||||||
|
end
|
||||||
|
|
||||||
function cfxOwnedZones.sideOwnsAll(theSide)
|
function cfxOwnedZones.sideOwnsAll(theSide)
|
||||||
for key, aZone in pairs(cfxOwnedZones.zones) do
|
for key, aZone in pairs(cfxOwnedZones.zones) do
|
||||||
if aZone.owner ~= theSide then
|
if aZone.owner ~= theSide then
|
||||||
@ -810,19 +861,150 @@ function cfxOwnedZones.hasOwnedZones()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- load / save data
|
||||||
|
--
|
||||||
|
|
||||||
|
function cfxOwnedZones.saveData()
|
||||||
|
-- this is called from persistence when it's time to
|
||||||
|
-- save data. returns a table with all my data
|
||||||
|
local theData = {}
|
||||||
|
local allZoneData = {}
|
||||||
|
-- iterate all my zones and create data
|
||||||
|
for idx, theZone in pairs(cfxOwnedZones.zones) do
|
||||||
|
local zoneData = {}
|
||||||
|
if theZone.defenderData then
|
||||||
|
zoneData.defenderData = dcsCommon.clone(theZone.defenderData)
|
||||||
|
dcsCommon.synchGroupData(zoneData.defenderData)
|
||||||
|
end
|
||||||
|
zoneData.conquered = cfxZones.getFlagValue(theZone.conqueredFlag, theZone)
|
||||||
|
zoneData.owner = theZone.owner
|
||||||
|
zoneData.state = theZone.state -- will prevent immediate spawn
|
||||||
|
-- since new zones are spawned with 'init'
|
||||||
|
allZoneData[theZone.name] = zoneData
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now iterate all attack groups that we have spawned and that
|
||||||
|
-- (maybe) are still alive
|
||||||
|
cfxOwnedZones.GC() -- start with a GC run to remove all dead
|
||||||
|
local livingAttackers = {}
|
||||||
|
for gName, gData in pairs (cfxOwnedZones.spawnedAttackers) do
|
||||||
|
-- all we need to do is get the group of that name
|
||||||
|
-- and if it still returns units we are fine
|
||||||
|
-- spawnedAttackers is a [groupName] table with {.groupData, .orders, .side}
|
||||||
|
local gameGroup = Group.getByName(gName)
|
||||||
|
if gameGroup and gameGroup:isExist() then
|
||||||
|
if gameGroup:getSize() > 0 then
|
||||||
|
local sData = dcsCommon.clone(gData)
|
||||||
|
dcsCommon.synchGroupData(sData.groupData)
|
||||||
|
livingAttackers[gName] = sData
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now write the info for the flags that we output for #red, etc
|
||||||
|
local flagInfo = {}
|
||||||
|
flagInfo.neutral = cfxZones.getFlagValue(cfxOwnedZones.neutralTriggerFlag, cfxOwnedZones)
|
||||||
|
flagInfo.red = cfxZones.getFlagValue(cfxOwnedZones.redTriggerFlag, cfxOwnedZones)
|
||||||
|
flagInfo.blue = cfxZones.getFlagValue(cfxOwnedZones.blueTriggerFlag, cfxOwnedZones)
|
||||||
|
-- assemble the data
|
||||||
|
theData.zoneData = allZoneData
|
||||||
|
theData.attackers = livingAttackers
|
||||||
|
theData.flagInfo = flagInfo
|
||||||
|
|
||||||
|
-- return it
|
||||||
|
return theData
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxOwnedZones.loadData()
|
||||||
|
-- remember to draw in map with new owner
|
||||||
|
if not persistence then return end
|
||||||
|
local theData = persistence.getSavedDataForModule("cfxOwnedZones")
|
||||||
|
if not theData then
|
||||||
|
if cfxOwnedZones.verbose then
|
||||||
|
trigger.action.outText("owdZ: no save date received, skipping.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- theData contains the following tables:
|
||||||
|
-- zoneData: per-zone data
|
||||||
|
-- flagInfo: module-global flags
|
||||||
|
-- attackers: all spawned attackers that we feed to groundTroops
|
||||||
|
local allZoneData = theData.zoneData
|
||||||
|
for zName, zData in pairs(allZoneData) do
|
||||||
|
-- access zone
|
||||||
|
local theZone = cfxOwnedZones.getOwnedZoneByName(zName)
|
||||||
|
if theZone then
|
||||||
|
if zData.defenderData then
|
||||||
|
if theZone.defenders and theZone.defenders:isExist() then
|
||||||
|
-- should not happen, but so be it
|
||||||
|
theZone.defenders:destroy()
|
||||||
|
end
|
||||||
|
local gData = zData.defenderData
|
||||||
|
local cty = gData.cty
|
||||||
|
local cat = gData.cat
|
||||||
|
theZone.defenders = coalition.addGroup(cty, cat, gData)
|
||||||
|
theZone.defenderData = zData.defenderData
|
||||||
|
end
|
||||||
|
theZone.owner = zData.owner
|
||||||
|
theZone.state = zData.state
|
||||||
|
cfxZones.setFlagValue(theZone.conqueredFlag, zData.conquered, theZone)
|
||||||
|
-- update mark in map
|
||||||
|
cfxOwnedZones.drawZoneInMap(theZone)
|
||||||
|
else
|
||||||
|
trigger.action.outText("owdZ: load - data mismatch: cannot find zone <" .. zName .. ">, skipping zone.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now process all attackers
|
||||||
|
local allAttackers = theData.attackers
|
||||||
|
for gName, gdTroop in pairs(allAttackers) do
|
||||||
|
-- table is {.groupData, .orders, .side}
|
||||||
|
local gData = gdTroop.groupData
|
||||||
|
local orders = gdTroop.orders
|
||||||
|
local side = gdTroop.side
|
||||||
|
local cty = gData.cty
|
||||||
|
local cat = gData.cat
|
||||||
|
-- add to my own attacker queue so we can save later
|
||||||
|
local dClone = dcsCommon.clone(gData)
|
||||||
|
cfxOwnedZones.spawnedAttackers[gName] = dClone
|
||||||
|
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||||
|
if cfxGroundTroops then
|
||||||
|
local troops = cfxGroundTroops.createGroundTroops(theGroup)
|
||||||
|
troops.orders = orders
|
||||||
|
troops.side = side
|
||||||
|
cfxGroundTroops.addGroundTroopsToPool(troops) -- hand off to ground troops
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now process module global flags
|
||||||
|
local flagInfo = theData.flagInfo
|
||||||
|
if flagInfo then
|
||||||
|
cfxZones.setFlagValue(cfxOwnedZones.neutralTriggerFlag, flagInfo.neutral, cfxOwnedZones)
|
||||||
|
cfxZones.setFlagValue(cfxOwnedZones.redTriggerFlag, flagInfo.red, cfxOwnedZones)
|
||||||
|
cfxZones.setFlagValue(cfxOwnedZones.blueTriggerFlag, flagInfo.blue, cfxOwnedZones)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
function cfxOwnedZones.readConfigZone(theZone)
|
function cfxOwnedZones.readConfigZone(theZone)
|
||||||
|
if not theZone then theZone = cfxZones.createSimpleZone("ownedZonesConfig") end
|
||||||
|
|
||||||
|
cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones
|
||||||
cfxOwnedZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxOwnedZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
cfxOwnedZones.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
cfxOwnedZones.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "r!") then
|
-- if cfxZones.hasProperty(theZone, "r!") then
|
||||||
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r!", "<none>")
|
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r!", "*<cfxnone>")
|
||||||
end
|
-- end
|
||||||
if cfxZones.hasProperty(theZone, "b!") then
|
-- if cfxZones.hasProperty(theZone, "b!") then
|
||||||
cfxOwnedZones.blueTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b!", "<none>")
|
cfxOwnedZones.blueTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b!", "*<cfxnone>")
|
||||||
end
|
-- end
|
||||||
if cfxZones.hasProperty(theZone, "n!") then
|
-- if cfxZones.hasProperty(theZone, "n!") then
|
||||||
cfxOwnedZones.neutralTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n!", "<none>")
|
cfxOwnedZones.neutralTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n!", "*<cfxnone>")
|
||||||
end
|
-- end
|
||||||
cfxOwnedZones.defendingTime = cfxZones.getNumberFromZoneProperty(theZone, "defendingTime", 100)
|
cfxOwnedZones.defendingTime = cfxZones.getNumberFromZoneProperty(theZone, "defendingTime", 100)
|
||||||
cfxOwnedZones.attackingTime = cfxZones.getNumberFromZoneProperty(theZone, "attackingTime", 300)
|
cfxOwnedZones.attackingTime = cfxZones.getNumberFromZoneProperty(theZone, "attackingTime", 300)
|
||||||
cfxOwnedZones.shockTime = cfxZones.getNumberFromZoneProperty(theZone, "shockTime", 200)
|
cfxOwnedZones.shockTime = cfxZones.getNumberFromZoneProperty(theZone, "shockTime", 200)
|
||||||
@ -838,11 +1020,8 @@ function cfxOwnedZones.init()
|
|||||||
|
|
||||||
-- read my config zone
|
-- read my config zone
|
||||||
local theZone = cfxZones.getZoneByName("ownedZonesConfig")
|
local theZone = cfxZones.getZoneByName("ownedZonesConfig")
|
||||||
if not theZone then
|
|
||||||
trigger.action.outText("+++ownZ: no config", 30)
|
|
||||||
else
|
|
||||||
cfxOwnedZones.readConfigZone(theZone)
|
cfxOwnedZones.readConfigZone(theZone)
|
||||||
end
|
|
||||||
|
|
||||||
-- collect all owned zones by their 'owner' property
|
-- collect all owned zones by their 'owner' property
|
||||||
-- start the process
|
-- start the process
|
||||||
@ -854,9 +1033,21 @@ function cfxOwnedZones.init()
|
|||||||
cfxOwnedZones.addOwnedZone(aZone)
|
cfxOwnedZones.addOwnedZone(aZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if persistence then
|
||||||
|
-- sign up for persistence
|
||||||
|
callbacks = {}
|
||||||
|
callbacks.persistData = cfxOwnedZones.saveData
|
||||||
|
persistence.registerModule("cfxOwnedZones", callbacks)
|
||||||
|
-- now load my data
|
||||||
|
cfxOwnedZones.loadData()
|
||||||
|
end
|
||||||
|
|
||||||
initialized = true
|
initialized = true
|
||||||
cfxOwnedZones.updateSchedule = timer.scheduleFunction(cfxOwnedZones.update, {}, timer.getTime() + 1/cfxOwnedZones.ups)
|
cfxOwnedZones.updateSchedule = timer.scheduleFunction(cfxOwnedZones.update, {}, timer.getTime() + 1/cfxOwnedZones.ups)
|
||||||
|
|
||||||
|
-- start housekeeping
|
||||||
|
cfxOwnedZones.houseKeeping()
|
||||||
|
|
||||||
trigger.action.outText("cx/x owned zones v".. cfxOwnedZones.version .. " started", 30)
|
trigger.action.outText("cx/x owned zones v".. cfxOwnedZones.version .. " started", 30)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxReconMode = {}
|
cfxReconMode = {}
|
||||||
cfxReconMode.version = "2.1.0"
|
cfxReconMode.version = "2.1.1"
|
||||||
cfxReconMode.verbose = false -- set to true for debug info
|
cfxReconMode.verbose = false -- set to true for debug info
|
||||||
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
||||||
|
|
||||||
@ -77,6 +77,8 @@ VERSION HISTORY
|
|||||||
2.1.0 - processZoneMessage uses group's position, not zone
|
2.1.0 - processZoneMessage uses group's position, not zone
|
||||||
- silent attribute for priority targets
|
- silent attribute for priority targets
|
||||||
- activate / deactivate by flags
|
- activate / deactivate by flags
|
||||||
|
2.1.1 - Lat Lon and MGRS also give Elevation
|
||||||
|
- cfxReconMode.reportTime
|
||||||
|
|
||||||
cfxReconMode is a script that allows units to perform reconnaissance
|
cfxReconMode is a script that allows units to perform reconnaissance
|
||||||
missions and, after detecting units, marks them on the map with
|
missions and, after detecting units, marks them on the map with
|
||||||
@ -421,13 +423,14 @@ function cfxReconMode.getLocation(theGroup)
|
|||||||
local msg = ""
|
local msg = ""
|
||||||
local theUnit = theGroup:getUnit(1)
|
local theUnit = theGroup:getUnit(1)
|
||||||
local currPoint = theUnit:getPoint()
|
local currPoint = theUnit:getPoint()
|
||||||
|
local ele = math.floor(land.getHeight({x = currPoint.x, y = currPoint.z}))
|
||||||
if cfxReconMode.mgrs then
|
if cfxReconMode.mgrs then
|
||||||
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
||||||
msg = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
msg = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing .. " Ele " .. ele .."m"
|
||||||
else
|
else
|
||||||
local lat, lon, alt = coord.LOtoLL(currPoint)
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
||||||
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
msg = "Lat " .. lat .. " Lon " .. lon
|
msg = "Lat " .. lat .. " Lon " .. lon .. " Ele " .. ele .."m"
|
||||||
end
|
end
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
@ -516,7 +519,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
-- say something
|
-- say something
|
||||||
if not silent and cfxReconMode.announcer then
|
if not silent and cfxReconMode.announcer then
|
||||||
local msg = cfxReconMode.generateSALT(theScout, theGroup)
|
local msg = cfxReconMode.generateSALT(theScout, theGroup)
|
||||||
trigger.action.outTextForCoalition(mySide, msg, 30)
|
trigger.action.outTextForCoalition(mySide, msg, cfxReconMode.reportTime)
|
||||||
-- trigger.action.outTextForCoalition(mySide, theScout:getName() .. " reports new ground contact " .. theGroup:getName(), 30)
|
-- trigger.action.outTextForCoalition(mySide, theScout:getName() .. " reports new ground contact " .. theGroup:getName(), 30)
|
||||||
if cfxReconMode.verbose then
|
if cfxReconMode.verbose then
|
||||||
trigger.action.outText("+++rcn: announced for side " .. mySide, 30)
|
trigger.action.outText("+++rcn: announced for side " .. mySide, 30)
|
||||||
@ -549,7 +552,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
-- AND EVEN WHEN SILENT!!!
|
-- AND EVEN WHEN SILENT!!!
|
||||||
local msg = zInfo.prioMessage
|
local msg = zInfo.prioMessage
|
||||||
msg = cfxReconMode.processZoneMessage(msg, zInfo.theZone, theGroup)
|
msg = cfxReconMode.processZoneMessage(msg, zInfo.theZone, theGroup)
|
||||||
trigger.action.outTextForCoalition(mySide, msg, 30)
|
trigger.action.outTextForCoalition(mySide, msg, cfxReconMode.reportTime)
|
||||||
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
||||||
trigger.action.outText("+++rcn: prio message sent for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
trigger.action.outText("+++rcn: prio message sent for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
||||||
end
|
end
|
||||||
@ -965,6 +968,7 @@ function cfxReconMode.readConfigZone()
|
|||||||
cfxReconMode.greyScouts = cfxZones.getBoolFromZoneProperty(theZone, "greyScouts", false)
|
cfxReconMode.greyScouts = cfxZones.getBoolFromZoneProperty(theZone, "greyScouts", false)
|
||||||
cfxReconMode.playerOnlyRecon = cfxZones.getBoolFromZoneProperty(theZone, "playerOnlyRecon", false)
|
cfxReconMode.playerOnlyRecon = cfxZones.getBoolFromZoneProperty(theZone, "playerOnlyRecon", false)
|
||||||
cfxReconMode.reportNumbers = cfxZones.getBoolFromZoneProperty(theZone, "reportNumbers", true)
|
cfxReconMode.reportNumbers = cfxZones.getBoolFromZoneProperty(theZone, "reportNumbers", true)
|
||||||
|
cfxReconMode.reportTime = cfxZones.getNumberFromZoneProperty(theZone, "reportTime", 30)
|
||||||
|
|
||||||
cfxReconMode.detectionMinRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMinRange", 3000)
|
cfxReconMode.detectionMinRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMinRange", 3000)
|
||||||
cfxReconMode.detectionMaxRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMaxRange", 12000)
|
cfxReconMode.detectionMaxRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMaxRange", 12000)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "2.8.4"
|
cfxZones.version = "2.8.5"
|
||||||
|
|
||||||
-- cf/x zone management module
|
-- cf/x zone management module
|
||||||
-- reads dcs zones and makes them accessible and mutable
|
-- reads dcs zones and makes them accessible and mutable
|
||||||
@ -89,6 +89,9 @@ cfxZones.version = "2.8.4"
|
|||||||
- changed extractPropertyFromDCS() to also match attributes with blanks like "the Attr" to "theAttr"
|
- changed extractPropertyFromDCS() to also match attributes with blanks like "the Attr" to "theAttr"
|
||||||
- new expandFlagName()
|
- new expandFlagName()
|
||||||
- 2.8.4 - fixed bug in setFlagValue()
|
- 2.8.4 - fixed bug in setFlagValue()
|
||||||
|
- 2.8.5 - createGroundUnitsInZoneForCoalition() now always passes back a copy of the group data
|
||||||
|
- data also contains cty = country and cat = category for easy spawn
|
||||||
|
- getFlagValue additional zone name guards
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxZones.verbose = false
|
cfxZones.verbose = false
|
||||||
@ -1047,8 +1050,17 @@ function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName,
|
|||||||
-- first we need to translate the coalition to a legal
|
-- first we need to translate the coalition to a legal
|
||||||
-- country. we use UN for neutral, cjtf for red and blue
|
-- country. we use UN for neutral, cjtf for red and blue
|
||||||
local theSideCJTF = dcsCommon.coalition2county(theCoalition)
|
local theSideCJTF = dcsCommon.coalition2county(theCoalition)
|
||||||
return coalition.addGroup(theSideCJTF, Group.Category.GROUND, theGroup)
|
-- store cty and cat for later access. DCS doesn't need it, but we may
|
||||||
|
|
||||||
|
theGroup.cty = theSideCJTF
|
||||||
|
theGroup.cat = Group.Category.GROUND
|
||||||
|
|
||||||
|
-- create a copy of the group data for
|
||||||
|
-- later reference
|
||||||
|
local groupDataCopy = dcsCommon.clone(theGroup)
|
||||||
|
|
||||||
|
local newGroup = coalition.addGroup(theSideCJTF, Group.Category.GROUND, theGroup)
|
||||||
|
return newGroup, groupDataCopy
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parsing zone names. The first part of the name until the first blank " "
|
-- parsing zone names. The first part of the name until the first blank " "
|
||||||
@ -1311,8 +1323,8 @@ end
|
|||||||
|
|
||||||
function cfxZones.getFlagValue(theFlag, theZone)
|
function cfxZones.getFlagValue(theFlag, theZone)
|
||||||
local zoneName = "<dummy>"
|
local zoneName = "<dummy>"
|
||||||
if not theZone then
|
if not theZone or not theZone.name then
|
||||||
trigger.action.outText("+++Zne: no zone on getFlagValue", 30)
|
trigger.action.outText("+++Zne: no zone or zone name on getFlagValue")
|
||||||
else
|
else
|
||||||
zoneName = theZone.name -- for flag wildcards
|
zoneName = theZone.name -- for flag wildcards
|
||||||
end
|
end
|
||||||
|
|||||||
@ -258,7 +258,7 @@ function changer.start()
|
|||||||
-- read config
|
-- read config
|
||||||
changer.readConfigZone()
|
changer.readConfigZone()
|
||||||
|
|
||||||
-- process cloner Zones
|
-- process changer Zones
|
||||||
local attrZones = cfxZones.getZonesWithAttributeNamed("change?")
|
local attrZones = cfxZones.getZonesWithAttributeNamed("change?")
|
||||||
for k, aZone in pairs(attrZones) do
|
for k, aZone in pairs(attrZones) do
|
||||||
changer.createChangerWithZone(aZone) -- process attributes
|
changer.createChangerWithZone(aZone) -- process attributes
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
cloneZones = {}
|
cloneZones = {}
|
||||||
cloneZones.version = "1.4.8"
|
cloneZones.version = "1.4.9"
|
||||||
cloneZones.verbose = false
|
cloneZones.verbose = false
|
||||||
cloneZones.requiredLibs = {
|
cloneZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
"cfxMX",
|
"cfxMX",
|
||||||
}
|
}
|
||||||
|
cloneZones.minSep = 10 -- minimal separation for onRoad auto-pos
|
||||||
|
cloneZones.maxIter = 100 -- maximum number of attempts to resolve
|
||||||
|
-- a too-close separation
|
||||||
|
|
||||||
-- groupTracker is OPTIONAL! and required only with trackWith attribute
|
-- groupTracker is OPTIONAL! and required only with trackWith attribute
|
||||||
|
|
||||||
cloneZones.cloners = {}
|
cloneZones.cloners = {}
|
||||||
@ -47,6 +51,8 @@
|
|||||||
1.4.6 - removed some verbosity for spawned aircraft with airfields on their routes
|
1.4.6 - removed some verbosity for spawned aircraft with airfields on their routes
|
||||||
1.4.7 - DML watchflag and DML Flag polish, method-->cloneMethod
|
1.4.7 - DML watchflag and DML Flag polish, method-->cloneMethod
|
||||||
1.4.8 - added 'wipe?' synonym
|
1.4.8 - added 'wipe?' synonym
|
||||||
|
1.4.9 - onRoad option
|
||||||
|
- rndHeading option
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -278,6 +284,10 @@
|
|||||||
if cfxZones.hasProperty(theZone, "rndLoc") then
|
if cfxZones.hasProperty(theZone, "rndLoc") then
|
||||||
theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "rndLoc", false)
|
theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "rndLoc", false)
|
||||||
end
|
end
|
||||||
|
theZone.rndHeading = cfxZones.getBoolFromZoneProperty(theZone, "rndHeading", false)
|
||||||
|
|
||||||
|
theZone.onRoad = cfxZones.getBoolFromZoneProperty(theZone, "onRoad", false)
|
||||||
|
|
||||||
if theZone.rndLoc and theZone.verbose then
|
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
|
end
|
||||||
@ -695,6 +705,49 @@
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if spawnZone.rndHeading then
|
||||||
|
local units = rawData.units
|
||||||
|
for idx, aUnit in pairs(units) do
|
||||||
|
local phi = 6.2831 * math.random() -- that's 2Pi, folx
|
||||||
|
aUnit.heading = phi
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply onRoad option if selected
|
||||||
|
if spawnZone.onRoad then
|
||||||
|
local units = rawData.units
|
||||||
|
local iterCount = 0
|
||||||
|
local otherLocs = {} -- resolved locs
|
||||||
|
for idx, aUnit in pairs(units) do
|
||||||
|
local cx = aUnit.x
|
||||||
|
local cy = aUnit.y
|
||||||
|
-- we now iterate until there is enough separation or too many iters
|
||||||
|
local tooClose
|
||||||
|
local np, nx, ny
|
||||||
|
repeat
|
||||||
|
nx, ny = land.getClosestPointOnRoads("roads", cx, cy)
|
||||||
|
-- compare this with all other locs
|
||||||
|
np = {x=nx, y=ny}
|
||||||
|
tooClose = false
|
||||||
|
for idc, op in pairs(otherLocs) do
|
||||||
|
local d = dcsCommon.dist(np, op)
|
||||||
|
if d < cloneZones.minSep then
|
||||||
|
tooClose = true
|
||||||
|
cx = cx + cloneZones.minSep
|
||||||
|
cy = cy + cloneZones.minSep
|
||||||
|
iterCount = iterCount + 1
|
||||||
|
-- trigger.action.outText("d fail for <" .. aUnit.name.. ">: d= <" .. d .. ">, iters = <" .. iterCount .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
until (iterCount > cloneZones.maxIter) or (not tooClose)
|
||||||
|
-- trigger.action.outText("separation iters for <" .. aUnit.name.. ">:<" .. iterCount .. ">", 30)
|
||||||
|
table.insert(otherLocs, np)
|
||||||
|
aUnit.x = nx
|
||||||
|
aUnit.y = ny
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- apply turning
|
-- apply turning
|
||||||
dcsCommon.rotateGroupData(rawData, spawnZone.turn, newCenter.x, newCenter.z)
|
dcsCommon.rotateGroupData(rawData, spawnZone.turn, newCenter.x, newCenter.z)
|
||||||
|
|
||||||
@ -790,6 +843,19 @@
|
|||||||
rawData.y = rawData.y + dy
|
rawData.y = rawData.y + dy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if spawnZone.rndHeading then
|
||||||
|
local phi = 6.2831 * math.random() -- that's 2Pi, folx
|
||||||
|
rawData.heading = phi
|
||||||
|
end
|
||||||
|
|
||||||
|
if spawnZone.onRoad then
|
||||||
|
local cx = rawData.x
|
||||||
|
local cy = rawData.y
|
||||||
|
local nx, ny = land.getClosestPointOnRoads("roads", cx, cy)
|
||||||
|
rawData.x = nx
|
||||||
|
rawData.y = ny
|
||||||
|
end
|
||||||
|
|
||||||
-- apply turning
|
-- apply turning
|
||||||
dcsCommon.rotateUnitData(rawData, spawnZone.turn, newCenter.x, newCenter.z)
|
dcsCommon.rotateUnitData(rawData, spawnZone.turn, newCenter.x, newCenter.z)
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.6.8"
|
dcsCommon.version = "2.7.0"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
2.2.6 - compassPositionOfARelativeToB
|
||||||
- clockPositionOfARelativeToB
|
- clockPositionOfARelativeToB
|
||||||
@ -85,6 +85,11 @@ dcsCommon.version = "2.6.8"
|
|||||||
2.6.7 - new menu2text()
|
2.6.7 - new menu2text()
|
||||||
2.6.8 - new getMissionName()
|
2.6.8 - new getMissionName()
|
||||||
- new flagArrayFromString()
|
- new flagArrayFromString()
|
||||||
|
2.6.9 - new getSceneryObjectsInZone()
|
||||||
|
- new getSceneryObjectInZoneByName()
|
||||||
|
2.7.0 - new synchGroupData()
|
||||||
|
clone, topClone and copyArray now all nil-trap
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
@ -456,6 +461,7 @@ dcsCommon.version = "2.6.8"
|
|||||||
return dcsCommon.dist(point1, point2)
|
return dcsCommon.dist(point1, point2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- distance between points
|
-- distance between points
|
||||||
function dcsCommon.dist(point1, point2) -- returns distance between two points
|
function dcsCommon.dist(point1, point2) -- returns distance between two points
|
||||||
-- supports xyz and xy notations
|
-- supports xyz and xy notations
|
||||||
@ -882,6 +888,7 @@ dcsCommon.version = "2.6.8"
|
|||||||
-- topClone is a shallow clone of orig, only top level is iterated,
|
-- topClone is a shallow clone of orig, only top level is iterated,
|
||||||
-- all values are ref-copied
|
-- all values are ref-copied
|
||||||
function dcsCommon.topClone(orig)
|
function dcsCommon.topClone(orig)
|
||||||
|
if not orig then return nil end
|
||||||
local orig_type = type(orig)
|
local orig_type = type(orig)
|
||||||
local copy
|
local copy
|
||||||
if orig_type == 'table' then
|
if orig_type == 'table' then
|
||||||
@ -898,6 +905,7 @@ dcsCommon.version = "2.6.8"
|
|||||||
-- clone is a recursive clone which will also clone
|
-- clone is a recursive clone which will also clone
|
||||||
-- deeper levels, as used in units
|
-- deeper levels, as used in units
|
||||||
function dcsCommon.clone(orig)
|
function dcsCommon.clone(orig)
|
||||||
|
if not orig then return nil end
|
||||||
local orig_type = type(orig)
|
local orig_type = type(orig)
|
||||||
local copy
|
local copy
|
||||||
if orig_type == 'table' then
|
if orig_type == 'table' then
|
||||||
@ -913,6 +921,8 @@ dcsCommon.version = "2.6.8"
|
|||||||
end
|
end
|
||||||
|
|
||||||
function dcsCommon.copyArray(inArray)
|
function dcsCommon.copyArray(inArray)
|
||||||
|
if not inArray then return nil end
|
||||||
|
|
||||||
-- warning: this is a ref copy!
|
-- warning: this is a ref copy!
|
||||||
local theCopy = {}
|
local theCopy = {}
|
||||||
for idx, element in pairs(inArray) do
|
for idx, element in pairs(inArray) do
|
||||||
@ -1687,6 +1697,28 @@ dcsCommon.version = "2.6.8"
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.synchGroupData(inGroupData) -- update group data block by
|
||||||
|
-- comparing it to spawned group and update units by x, y, heding and isExist
|
||||||
|
-- modifies inGroupData!
|
||||||
|
if not inGroupData then return end
|
||||||
|
-- groupdata from game, NOT MX DATA!
|
||||||
|
-- we synch the units and their coords
|
||||||
|
local livingUnits = {}
|
||||||
|
for idx, unitData in pairs(inGroupData.units) do
|
||||||
|
local theUnit = Unit.getByName(unitData.name)
|
||||||
|
if theUnit and theUnit:isExist() and theUnit:getLife()>1 then
|
||||||
|
-- update x and y and heading
|
||||||
|
local pos = theUnit:getPoint()
|
||||||
|
unitData.unitId = theUnit:getID()
|
||||||
|
unitData.x = pos.x
|
||||||
|
unitData.y = pos.z -- !!!!
|
||||||
|
unitData.heading = dcsCommon.getUnitHeading(gUnit)
|
||||||
|
table.insert(livingUnits, unitData)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
inGroupData.units = livingUnits
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- M I S C M E T H O D S
|
-- M I S C M E T H O D S
|
||||||
@ -2440,6 +2472,42 @@ function dcsCommon.flagArrayFromString(inString, verbose)
|
|||||||
end
|
end
|
||||||
return flags
|
return flags
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.objectHandler(theObject, theCollector)
|
||||||
|
table.insert(theCollector, theObject)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function dcsCommon.getSceneryObjectsInZone(theZone) -- DCS ZONE!!!
|
||||||
|
local aCat = 5 -- scenery
|
||||||
|
-- WARNING: WE ARE USING DCS ZONES, NOT CFX!!!
|
||||||
|
local p = {x=theZone.x, y=0, z=theZone.y}
|
||||||
|
local lp = {x = p.x, y = p.z}
|
||||||
|
p.y = land.getHeight(lp)
|
||||||
|
local collector = {}
|
||||||
|
|
||||||
|
-- now build the search argument
|
||||||
|
local args = {
|
||||||
|
id = world.VolumeType.SPHERE,
|
||||||
|
params = {
|
||||||
|
point = p,
|
||||||
|
radius = theZone.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- now call search
|
||||||
|
world.searchObjects(aCat, args, dcsCommon.objectHandler, collector)
|
||||||
|
return collector
|
||||||
|
end
|
||||||
|
|
||||||
|
function dcsCommon.getSceneryObjectInZoneByName(theName, theZone) -- DCS ZONE!!!
|
||||||
|
local allObs = dcsCommon.getSceneryObjectsInZone(theZone)
|
||||||
|
for idx, anObject in pairs(allObs) do
|
||||||
|
if tostring(anObject:getName()) == theName then return anObject end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- INIT
|
-- INIT
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
persistence = {}
|
persistence = {}
|
||||||
persistence.version = "1.0.0"
|
persistence.version = "1.0.1"
|
||||||
persistence.ups = 1 -- once every 1 seconds
|
persistence.ups = 1 -- once every 1 seconds
|
||||||
persistence.verbose = false
|
persistence.verbose = false
|
||||||
persistence.active = false
|
persistence.active = false
|
||||||
@ -7,7 +7,7 @@ persistence.saveFileName = nil -- "mission data.txt"
|
|||||||
persistence.sharedDir = nil -- not yet implemented
|
persistence.sharedDir = nil -- not yet implemented
|
||||||
persistence.missionDir = nil -- set at start
|
persistence.missionDir = nil -- set at start
|
||||||
persistence.saveDir = nil -- set at start
|
persistence.saveDir = nil -- set at start
|
||||||
|
persistence.name = "persistence" -- for cfxZones
|
||||||
persistence.missionData = {} -- loaded from file
|
persistence.missionData = {} -- loaded from file
|
||||||
persistence.requiredLibs = {
|
persistence.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -16,6 +16,12 @@ persistence.requiredLibs = {
|
|||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
1.0.0 - initial version
|
||||||
|
1.0.1 - when available, module sets flag "cfxPersistence" to 1
|
||||||
|
- when data availabe, cfxPersistenceHasData is set to 1
|
||||||
|
- spelling
|
||||||
|
- cfxZones interface
|
||||||
|
- always output save location
|
||||||
|
|
||||||
|
|
||||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||||
PROVIDES STANDALONE/HOSTED SERVER COMPATIOBILITY
|
PROVIDES STANDALONE/HOSTED SERVER COMPATIOBILITY
|
||||||
@ -42,7 +48,7 @@ function persistence.registerModule(name, callbacks)
|
|||||||
-- and must be the one given when you retrieve it later
|
-- and must be the one given when you retrieve it later
|
||||||
persistence.callbacks[name] = callbacks
|
persistence.callbacks[name] = callbacks
|
||||||
if persistence.verbose then
|
if persistence.verbose then
|
||||||
trigger.action.outText("+++persistence: module <" .. name .. "> registred itself", 30)
|
trigger.action.outText("+++persistence: module <" .. name .. "> registered itself", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -120,7 +126,10 @@ end
|
|||||||
--
|
--
|
||||||
function persistence.saveText(theString, fileName, shared, append)
|
function persistence.saveText(theString, fileName, shared, append)
|
||||||
if not persistence.active then return false end
|
if not persistence.active then return false end
|
||||||
if not fileName then return false end
|
if not fileName then
|
||||||
|
trigger.action.outText("+++persistence: saveText without fileName")
|
||||||
|
return false
|
||||||
|
end
|
||||||
if not shared then shared = flase end
|
if not shared then shared = flase end
|
||||||
if not theString then theString = "" end
|
if not theString then theString = "" end
|
||||||
|
|
||||||
@ -140,6 +149,7 @@ function persistence.saveText(theString, fileName, shared, append)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not theFile then
|
if not theFile then
|
||||||
|
trigger.action.outText("+++persistence: saveText - unable to open " .. path, 30)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -283,6 +293,7 @@ function persistence.missionStartDataLoad()
|
|||||||
-- can init from by data
|
-- can init from by data
|
||||||
persistence.missionData = theData
|
persistence.missionData = theData
|
||||||
persistence.hasData = true
|
persistence.hasData = true
|
||||||
|
trigger.action.setUserFlag("cfxPersistenceHasData", 1)
|
||||||
|
|
||||||
-- init my flags from last save
|
-- init my flags from last save
|
||||||
local theFlags = theData["persistence.flagData"]
|
local theFlags = theData["persistence.flagData"]
|
||||||
@ -359,9 +370,9 @@ function persistence.doSaveMission()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if persistence.verbose then
|
-- if persistence.verbose then
|
||||||
trigger.action.outText("+++persistence: mission saved", 30)
|
trigger.action.outText("+++persistence: mission saved to\n" .. persistence.missionDir .. persistence.saveFileName, 30)
|
||||||
end
|
-- end
|
||||||
end
|
end
|
||||||
|
|
||||||
function persistence.noteCleanRestart()
|
function persistence.noteCleanRestart()
|
||||||
@ -546,6 +557,7 @@ function persistence.start()
|
|||||||
persistence.missionDir = missionDir
|
persistence.missionDir = missionDir
|
||||||
|
|
||||||
persistence.active = true -- we can load and save data
|
persistence.active = true -- we can load and save data
|
||||||
|
trigger.action.setUserFlag("cfxPersistence", 1)
|
||||||
persistence.hasData = false -- we do not have save data
|
persistence.hasData = false -- we do not have save data
|
||||||
|
|
||||||
-- from here on we can read and write files in the missionDir
|
-- from here on we can read and write files in the missionDir
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
-- theDebugger
|
-- theDebugger
|
||||||
debugger = {}
|
debugger = {}
|
||||||
debugger.version = "1.1.1"
|
debugger.version = "1.1.2"
|
||||||
debugDemon = {}
|
debugDemon = {}
|
||||||
debugDemon.version = "1.1.1"
|
debugDemon.version = "1.1.2"
|
||||||
|
|
||||||
debugger.verbose = false
|
debugger.verbose = false
|
||||||
debugger.ups = 4 -- every 0.25 second
|
debugger.ups = 4 -- every 0.25 second
|
||||||
@ -22,6 +22,7 @@ debugger.log = ""
|
|||||||
- persistence of logs
|
- persistence of logs
|
||||||
- save <name>
|
- save <name>
|
||||||
1.1.1 - warning when trying to set a flag to a non-int
|
1.1.1 - warning when trying to set a flag to a non-int
|
||||||
|
1.1.2 - remove command
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -478,11 +479,6 @@ if not debugger.start() then
|
|||||||
debugger = nil
|
debugger = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[--
|
|
||||||
debug on and off. globally, not per zone
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- DEBUG DEMON
|
-- DEBUG DEMON
|
||||||
@ -625,15 +621,6 @@ end
|
|||||||
--
|
--
|
||||||
-- Helpers
|
-- Helpers
|
||||||
--
|
--
|
||||||
--[[--
|
|
||||||
function debugDemon.isObserving(flagName)
|
|
||||||
-- for now, we simply scan out own
|
|
||||||
for idx, aName in pairs(debugDemon.observer.flagArray) do
|
|
||||||
if aName == flagName then return true end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
function debugDemon.createObserver(aName)
|
function debugDemon.createObserver(aName)
|
||||||
local observer = cfxZones.createSimpleZone(aName)
|
local observer = cfxZones.createSimpleZone(aName)
|
||||||
@ -667,6 +654,7 @@ debugger.outText("*** debugger: commands are:" ..
|
|||||||
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
"\n\n " .. debugDemon.markOfDemon .. "snap [<observername>] -- create new snapshot of flags" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
"\n " .. debugDemon.markOfDemon .. "compare -- compare snapshot flag values with current" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
"\n " .. debugDemon.markOfDemon .. "note <your note> -- add <your note> to the text log" ..
|
||||||
|
"\n\n " .. debugDemon.markOfDemon .. "remove <group/unit/object name> -- remove named item from mission" ..
|
||||||
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
"\n\n " .. debugDemon.markOfDemon .. "start -- starts debugger" ..
|
||||||
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
"\n " .. debugDemon.markOfDemon .. "stop -- stop debugger" ..
|
||||||
|
|
||||||
@ -1154,6 +1142,40 @@ function debugDemon.processSaveCommand(args, event)
|
|||||||
debugger.saveLog(aName)
|
debugger.saveLog(aName)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function debugDemon.processRemoveCommand(args, event)
|
||||||
|
-- remove a group, unit or object
|
||||||
|
-- try group first
|
||||||
|
local aName = event.remainder
|
||||||
|
if not aName or aName:len() < 1 then
|
||||||
|
debugger.outText("*** remove: no remove target", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
aName = dcsCommon.trim(aName)
|
||||||
|
local theGroup = Group.getByName(aName)
|
||||||
|
if theGroup and theGroup:isExist() then
|
||||||
|
theGroup:destroy()
|
||||||
|
debugger.outText("*** remove: removed group <" .. aName .. ">", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local theUnit = Unit.getByName(aName)
|
||||||
|
if theUnit and theUnit:isExist() then
|
||||||
|
theUnit:destroy()
|
||||||
|
debugger.outText("*** remove: removed unit <" .. aName .. ">", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local theStatic = StaticObject.getByName(aName)
|
||||||
|
if theStatic and theStatic:isExist() then
|
||||||
|
theStatic:destroy()
|
||||||
|
debugger.outText("*** remove: removed static object <" .. aName .. ">", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
debugger.outText("*** remove: did not find anything called <" .. aName .. "> to remove", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- init and start
|
-- init and start
|
||||||
--
|
--
|
||||||
@ -1223,6 +1245,7 @@ function debugDemon.init()
|
|||||||
debugDemon.addCommndProcessor("?", debugDemon.processHelpCommand)
|
debugDemon.addCommndProcessor("?", debugDemon.processHelpCommand)
|
||||||
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
debugDemon.addCommndProcessor("help", debugDemon.processHelpCommand)
|
||||||
|
|
||||||
|
debugDemon.addCommndProcessor("remove", debugDemon.processRemoveCommand)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -1259,6 +1282,11 @@ end
|
|||||||
- inspect objects, dumping category, life, if it's tasking, latLon, alt, speed, direction
|
- inspect objects, dumping category, life, if it's tasking, latLon, alt, speed, direction
|
||||||
|
|
||||||
- exec files. save all commands and then run them from script
|
- exec files. save all commands and then run them from script
|
||||||
- remove units via delete and explode
|
|
||||||
|
- query objects: -q persistence.active returns boolean, true
|
||||||
|
-q x.y returns table, 12 elements
|
||||||
|
-q a.b.x returns number 12
|
||||||
|
-q d.e.f returns string "asdasda..."
|
||||||
|
-q sada reuturs <nil>
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
unitPersistence = {}
|
unitPersistence = {}
|
||||||
unitPersistence.version = '1.0.0'
|
unitPersistence.version = '1.0.1'
|
||||||
unitPersistence.verbose = false
|
unitPersistence.verbose = false
|
||||||
|
unitPersistence.updateTime = 60 -- seconds. Once every minute check statics
|
||||||
unitPersistence.requiredLibs = {
|
unitPersistence.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -10,6 +11,10 @@ unitPersistence.requiredLibs = {
|
|||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 - initial version
|
1.0.0 - initial version
|
||||||
|
1.0.1 - handles late activation
|
||||||
|
- handles linked static objects
|
||||||
|
- does no longer mess with heliports
|
||||||
|
- update statics once a minute, not second
|
||||||
|
|
||||||
REQUIRES PERSISTENCE AND MX
|
REQUIRES PERSISTENCE AND MX
|
||||||
|
|
||||||
@ -66,7 +71,7 @@ function unitPersistence.saveData()
|
|||||||
|
|
||||||
-- process all static objects placed with ME
|
-- process all static objects placed with ME
|
||||||
for oName, oData in pairs(unitPersistence.statics) do
|
for oName, oData in pairs(unitPersistence.statics) do
|
||||||
if not oData.isDead then
|
if not oData.isDead or oData.lateActivation then
|
||||||
-- fetch the object and see if it's still alive
|
-- fetch the object and see if it's still alive
|
||||||
local theObject = StaticObject.getByName(oName)
|
local theObject = StaticObject.getByName(oName)
|
||||||
if theObject and theObject:isExist() then
|
if theObject and theObject:isExist() then
|
||||||
@ -75,17 +80,16 @@ function unitPersistence.saveData()
|
|||||||
oData.x = pos.x
|
oData.x = pos.x
|
||||||
oData.y = pos.z -- (!!)
|
oData.y = pos.z -- (!!)
|
||||||
oData.isDead = theObject:getLife() < 1
|
oData.isDead = theObject:getLife() < 1
|
||||||
-- trigger.action.outText("deadcheck: " .. oName .. " has health=" .. theObject:getLife(), 30)
|
|
||||||
oData.dead = oData.isDead
|
oData.dead = oData.isDead
|
||||||
else
|
else
|
||||||
oData.isDead = true
|
oData.isDead = true
|
||||||
oData.dead = true
|
oData.dead = true
|
||||||
-- trigger.action.outText("deadcheck: " .. oName .. " certified dead", 30)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if unitPersistence.verbose then
|
if unitPersistence.verbose then
|
||||||
local note = "(ok)"
|
local note = "(ok)"
|
||||||
if oData.isDead then note = "(dead)" end
|
if oData.isDead then note = "(dead)" end
|
||||||
|
if oData.lateActivation then note = "(late active)" end
|
||||||
trigger.action.outText("unitPersistence: save - processed group <" .. oName .. ">. " .. note, 30)
|
trigger.action.outText("unitPersistence: save - processed group <" .. oName .. ">. " .. note, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -154,7 +158,7 @@ function unitPersistence.loadMission()
|
|||||||
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">")
|
trigger.action.outText("+++ failed to add modified group <" .. groupName .. ">")
|
||||||
end
|
end
|
||||||
if unitPersistence.verbose then
|
if unitPersistence.verbose then
|
||||||
trigger.action.outText("+++unitPersistence: updated group <" .. groupName .. "> of cat <" .. cat .. "> for cty <" .. cty .. ">", 30)
|
-- trigger.action.outText("+++unitPersistence: updated group <" .. groupName .. "> of cat <" .. cat .. "> for cty <" .. cty .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -167,11 +171,27 @@ function unitPersistence.loadMission()
|
|||||||
-- and now the same for static objects
|
-- and now the same for static objects
|
||||||
if theData.statics then
|
if theData.statics then
|
||||||
for name, staticData in pairs(theData.statics) do
|
for name, staticData in pairs(theData.statics) do
|
||||||
local theStatic = StaticObject.getByName(name)
|
--local theStatic = StaticObject.getByName(name)
|
||||||
if not theStatic then
|
if staticData.lateActivation then
|
||||||
mismatchWarning = true
|
-- this one will not be in the game now, skip
|
||||||
|
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
|
||||||
|
if unitPersistence.verbose then
|
||||||
|
trigger.action.outText("+++unitPersistence: static <" .. name .. "> is Heliport, no update", 30)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
local newStatic = dcsCommon.clone(staticData)
|
local newStatic = dcsCommon.clone(staticData)
|
||||||
|
-- add link info if it exists
|
||||||
|
newStatic.linkUnit = cfxMX.linkByName[name]
|
||||||
|
if newStatic.linkUnit and unitPersistence.verbose then
|
||||||
|
trigger.action.outText("+++unitPersistence: linked static <" .. name .. "> to unit <" .. newStatic.linkUnit .. ">", 30)
|
||||||
|
end
|
||||||
local cty = staticData.cty
|
local cty = staticData.cty
|
||||||
local cat = staticData.cat
|
local cat = staticData.cat
|
||||||
-- spawn new one, replacing same.named old, dead if required
|
-- spawn new one, replacing same.named old, dead if required
|
||||||
@ -182,7 +202,7 @@ function unitPersistence.loadMission()
|
|||||||
if unitPersistence.verbose then
|
if unitPersistence.verbose then
|
||||||
local note = ""
|
local note = ""
|
||||||
if newStatic.dead then note = " (dead)" end
|
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
|
end
|
||||||
end
|
end
|
||||||
@ -197,6 +217,31 @@ function unitPersistence.loadMission()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Update
|
||||||
|
--
|
||||||
|
function unitPersistence.update()
|
||||||
|
-- we check every minute
|
||||||
|
timer.scheduleFunction(unitPersistence.update, {}, timer.getTime() + unitPersistence.updateTime)
|
||||||
|
-- do a quick scan for all late activated static objects and if they
|
||||||
|
-- are suddently visible, remove their late activate state
|
||||||
|
--for groupName, groupdata in pairs(unitPersistence.groundTroops) do
|
||||||
|
-- currently not needed
|
||||||
|
--end
|
||||||
|
|
||||||
|
for objName, objData in pairs(unitPersistence.statics) do
|
||||||
|
if objData.lateActivation then
|
||||||
|
local theStatic = StaticObject.getByName(objData.name)
|
||||||
|
if theStatic then
|
||||||
|
objData.lateActivation = false
|
||||||
|
if unitPersistence.verbose then
|
||||||
|
trigger.action.outText("+++unitPersistence: <" .. objData.name .. "> has activated", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Start
|
-- Start
|
||||||
--
|
--
|
||||||
@ -250,13 +295,18 @@ function unitPersistence.start()
|
|||||||
theStatic.isDead = false
|
theStatic.isDead = false
|
||||||
theStatic.groupId = mxData.groupId
|
theStatic.groupId = mxData.groupId
|
||||||
theStatic.cat = cfxMX.catText2ID("static")
|
theStatic.cat = cfxMX.catText2ID("static")
|
||||||
|
theStatic.cty = cfxMX.countryByName[name]
|
||||||
local gameOb = StaticObject.getByName(theStatic.name)
|
local gameOb = StaticObject.getByName(theStatic.name)
|
||||||
if not gameOb then
|
if not gameOb then
|
||||||
trigger.action.outText("+++warning: static object <" .. theStatic.name .. "> does not exist in-game!?", 30)
|
if unitPersistence.verbose then
|
||||||
else
|
trigger.action.outText("+++unitPersistence: static object <" .. theStatic.name .. "> has late activation", 30)
|
||||||
theStatic.cty = gameOb:getCountry()
|
|
||||||
unitPersistence.statics[theStatic.name] = theStatic
|
|
||||||
end
|
end
|
||||||
|
theStatic.lateActivation = true
|
||||||
|
else
|
||||||
|
--theStatic.cty = gameOb:getCountry()
|
||||||
|
--unitPersistence.statics[theStatic.name] = theStatic
|
||||||
|
end
|
||||||
|
unitPersistence.statics[theStatic.name] = theStatic
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -265,6 +315,9 @@ function unitPersistence.start()
|
|||||||
unitPersistence.loadMission()
|
unitPersistence.loadMission()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- start update
|
||||||
|
unitPersistence.update()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -274,3 +327,7 @@ if not unitPersistence.start() then
|
|||||||
end
|
end
|
||||||
unitPersistence = nil
|
unitPersistence = nil
|
||||||
end
|
end
|
||||||
|
--[[--
|
||||||
|
ToDo: linked statics and linked units on restore
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user