Version 1.4.5

Cloners for HeloTrooper
This commit is contained in:
Christian Franz 2023-10-06 14:53:18 +02:00
parent d1d4af63a0
commit 28830f1378
10 changed files with 308 additions and 213 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
airfield = {}
airfield.version = "1.0.0"
airfield.version = "1.1.0"
airfield.requiredLibs = {
"dcsCommon",
"cfxZones",
@ -15,15 +15,21 @@ airfield.farps = false
Version History
1.0.0 - initial release
1.1.0 - added 'fixed' attribute
- added 'farps' attribute to individual zones
- allow zone.local farps designation
- always checks farp cap events
- added verbosity
--]]--
--
-- setting up airfield
--
function airfield.createAirFieldFromZone(theZone)
theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
local filterCat = 0
if airfield.farps then filterCat = {0, 1} end -- bases and farps
if (airfield.farps or theZone.farps) then filterCat = {0, 1} end -- bases and farps
local p = theZone:getPoint()
local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
theZone.airfield = theBase
@ -72,12 +78,26 @@ function airfield.createAirFieldFromZone(theZone)
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
end
-- if fixed attribute, we switch to that color and keep it fixed.
-- can be overridden by either makeXX or autoCap.
if theZone:hasProperty("fixed") then
local theFixed = theZone:getCoalitionFromZoneProperty("fixed")
local theAirfield = theZone.airfield
airfield.assumeControl(theZone) -- turn off capturable
theAirfield:setCoalition(theFixed)
theZone.owner = theFixed
end
-- index by name, and warn if duplicate associate
if airfield.myAirfields[theZone.afName] then
trigger.action.outText("+++airF: WARNING - zone <" .. theZone:getName() .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30)
else
airfield.myAirfields[theZone.afName] = theZone
end
if theZone.verbose or airfield.verbose then
trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30)
end
end
function airfield.assumeControl(theZone)
@ -137,14 +157,14 @@ function airfield:onEvent(event)
local desc = theBase:getDesc()
local bName = theBase:getName()
local cat = desc.category -- never get cat directly!
if cat == 1 then
--[[-- if cat == 1 then
if not airfield.farps then
if airfield.verbose then
trigger.action.outText("+++airF: ignored cap event for FARP <" .. bName .. ">", 30)
end
return
end
end
end --]]
if airfield.verbose then
trigger.action.outText("+++airF: cap event for <" .. bName .. ">, cat = (" .. cat .. ")", 30)
end

View File

@ -1,9 +1,10 @@
cfxHeloTroops = {}
cfxHeloTroops.version = "2.4.1"
cfxHeloTroops.version = "3.0.0"
cfxHeloTroops.verbose = false
cfxHeloTroops.autoDrop = true
cfxHeloTroops.autoPickup = false
cfxHeloTroops.pickupRange = 100 -- meters
cfxHeloTroops.requestRange = 500 -- meters
--
--[[--
VERSION HISTORY
@ -32,7 +33,11 @@ cfxHeloTroops.pickupRange = 100 -- meters
- removed restriction to only apply to helicopters in anticipation of the C-130 Hercules appearing in the game
2.4.1 - new actionSound attribute, sound plays to group whenever
troops have boarded or disembarked
3.0.0 - added requestable cloner support
- harmonized spawning invocations across cloners and spawners
- dmlZones
- requestRange attribute
--]]--
--
-- cfxHeloTroops -- a module to pick up and drop infantry.
@ -158,13 +163,11 @@ function cfxHeloTroops.heloLanded(theUnit)
cfxHeloTroops.setCommsMenu(conf.unit)
end
--
--
-- Helo took off
--
--
function cfxHeloTroops.heloDeparted(theUnit)
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return end
@ -206,17 +209,14 @@ function cfxHeloTroops.cleanHelo(theUnit)
end
end
end
conf.troopsOnBoard = {}
end
function cfxHeloTroops.heloCrashed(theUnit)
if not dcsCommon.isTroopCarrier(theUnit, cfxHeloTroops.troopCarriers) then return
end
-- clean up
cfxHeloTroops.cleanHelo(theUnit)
end
--
@ -252,7 +252,6 @@ function cfxHeloTroops.removeComms(theUnit)
conf.id = id
conf.unit = theUnit
cfxHeloTroops.removeCommsFromConfig(conf)
end
@ -371,16 +370,15 @@ function cfxHeloTroops.addGroundMenu(conf)
return
end
-- case 2A: no troops aboard, and spawners in range
-- that are requestable
-- case 2A: no troops aboard, and requestable spawners/cloners in range
local p = conf.unit:getPosition().p
local mySide = conf.unit:getCoalition()
if cfxSpawnZones then
-- only if SpawnZones is implemented
local availableSpawnersRaw = cfxSpawnZones.getRequestableSpawnersInRange(p, 500, mySide)
-- DONE: requestable spawners must check for troop compatibility
local availableSpawners = {}
-- collect available spawn zones
local availableSpawners = {}
if cfxSpawnZones then -- only if SpawnZones is implemented
local availableSpawnersRaw = cfxSpawnZones.getRequestableSpawnersInRange(p, cfxHeloTroops.requestRange, mySide)
for idx, aSpawner in pairs(availableSpawnersRaw) do
-- filter all spawners that spawn "illegal" troops
local theTypes = aSpawner.types
@ -403,26 +401,55 @@ function cfxHeloTroops.addGroundMenu(conf)
table.insert(availableSpawners, aSpawner)
end
end
local numSpawners = #availableSpawners
if numSpawners > 5 then numSpawners = 5 end
while numSpawners > 0 do
-- for each spawner in range, create a
-- spawn menu item
local spawner = availableSpawners[numSpawners]
local theName = spawner.baseName
local comm = "Request <" .. theName .. "> troops for transport" -- .. math.floor(aTeam.dist) .. "m away"
local theCommand = missionCommands.addCommandForGroup(
conf.id,
comm,
conf.myMainMenu,
cfxHeloTroops.redirectSpawnGroup,
{conf, spawner}
)
table.insert(conf.myCommands, theCommand)
numSpawners = numSpawners - 1
end
end
-- collect available clone zones
if cloneZones then
local availableSpawnersRaw = cloneZones.getRequestableClonersInRange(p, cfxHeloTroops.requestRange, mySide)
for idx, aSpawner in pairs(availableSpawnersRaw) do
-- filter all spawners that spawn "illegal" troops or have none
local theTypes = aSpawner.allTypes
local allLegal = true
local numTypes = dcsCommon.getSizeOfTable(theTypes)
if numTypes > 0 then
for aType, cnt in pairs(theTypes) do
if cfxHeloTroops.legalTroops then
if not dcsCommon.arrayContainsString(cfxHeloTroops.legalTroops, aType) then
allLegal = false
end
else
if not dcsCommon.typeIsInfantry(aType) then
allLegal = false
end
end
end
else
allegal = false
end
if allLegal then
table.insert(availableSpawners, aSpawner)
end
end
end
local numSpawners = #availableSpawners
if numSpawners > 5 then numSpawners = 5 end
while numSpawners > 0 do
-- for each spawner in range, create a
-- spawn menu item
local spawner = availableSpawners[numSpawners]
local theName = spawner.baseName
local comm = "Request <" .. theName .. "> troops for transport" -- .. math.floor(aTeam.dist) .. "m away"
local theCommand = missionCommands.addCommandForGroup(
conf.id,
comm,
conf.myMainMenu,
cfxHeloTroops.redirectSpawnGroup,
{conf, spawner}
)
table.insert(conf.myCommands, theCommand)
numSpawners = numSpawners - 1
end
-- case 2B: no troops aboard. see if there are troops around
@ -440,7 +467,6 @@ function cfxHeloTroops.addGroundMenu(conf)
-- now limit the options to the five closest legal groups
local numUnits = #unitsToLoad
if numUnits > 5 then numUnits = 5 end
if numUnits < 1 then
local theCommand = missionCommands.addCommandForGroup(
conf.id,
@ -469,8 +495,6 @@ function cfxHeloTroops.addGroundMenu(conf)
)
table.insert(conf.myCommands, theCommand)
end
end
function cfxHeloTroops.filterTroopsByType(unitsToLoad)
@ -546,7 +570,6 @@ end
--
-- Deploying Troops
--
function cfxHeloTroops.redirectDeployTroops(args)
timer.scheduleFunction(cfxHeloTroops.doDeployTroops, args, timer.getTime() + 0.1)
end
@ -626,8 +649,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> revoke 'wait' orders, proceed with <".. orders .. ">", 30)
end
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m ratius around choppa
--local theCoalition = theUnit:getCountry() -- make it choppers country
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa
local theCoalition = theUnit:getGroup():getCoalition() -- make it choppers COALITION
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
theCoalition,
@ -649,7 +671,7 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
troop.destination = dest -- transfer target zone for attackzone oders
cfxGroundTroops.addGroundTroopsToPool(troop) -- will schedule move orders
trigger.action.outTextForGroup(conf.id, "<" .. theGroup:getName() .. "> have deployed to the ground with orders " .. orders .. "!", 30)
trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound) -- "Quest Snare 3.wav")
trigger.action.outSoundForGroup(conf.id, cfxHeloTroops.actionSound)
-- see if this is tracked by a tracker, and pass them back so
-- they can un-limbo
if groupTracker then
@ -665,7 +687,6 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
end
end
--
-- Loading Troops
--
@ -755,7 +776,7 @@ end
function cfxHeloTroops.doSpawnGroup(args)
local conf = args[1]
local theSpawner = args[2]
-- NOTE: theSpawner can be of type cfxSpawnZone !!!OR!!! cfxCloneZones
-- make sure cooldown on spawner has timed out, else
-- notify that you have to wait
local now = timer.getTime()
@ -765,13 +786,13 @@ function cfxHeloTroops.doSpawnGroup(args)
return
end
cfxSpawnZones.spawnWithSpawner(theSpawner)
--cfxSpawnZones.spawnWithSpawner(theSpawner) -- old code
theSpawner.spawnWithSpawner(theSpawner) -- can be both spawner and cloner
trigger.action.outTextForGroup(conf.id, "Deploying <" .. theSpawner.baseName .. "> now...", 30)
-- reset all comms so we can include new troops
-- into load menu
timer.scheduleFunction(cfxHeloTroops.delayedCommsResetForUnit, {conf.unit, "ignore"}, now + 1.0)
end
--
@ -859,14 +880,13 @@ function cfxHeloTroops.readConfigZone()
-- note: must match exactly!!!!
local theZone = cfxZones.getZoneByName("heloTroopsConfig")
if not theZone then
trigger.action.outText("+++heloT: no config zone!", 30)
theZone = cfxZones.createSimpleZone("heloTroopsConfig")
end
cfxHeloTroops.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
cfxHeloTroops.verbose = theZone:getBoolFromZoneProperty("verbose", false)
if cfxZones.hasProperty(theZone, "legalTroops") then
local theTypesString = cfxZones.getStringFromZoneProperty(theZone, "legalTroops", "")
if theZone:hasProperty("legalTroops") then
local theTypesString = theZone:getStringFromZoneProperty("legalTroops", "")
local unitTypes = dcsCommon.splitString(aSpawner.types, ",")
if #unitTypes < 1 then
unitTypes = {"Soldier AK", "Infantry AK", "Infantry AK ver2", "Infantry AK ver3", "Infantry AK Ins", "Soldier M249", "Soldier M4 GRG", "Soldier M4", "Soldier RPG", "Paratrooper AKS-74", "Paratrooper RPG-16", "Stinger comm dsr", "Stinger comm", "Soldier stinger", "SA-18 Igla-S comm", "SA-18 Igla-S manpad", "Igla manpad INS", "SA-18 Igla comm", "SA-18 Igla manpad",} -- default
@ -876,18 +896,19 @@ function cfxHeloTroops.readConfigZone()
cfxHeloTroops.legalTroops = unitTypes
end
cfxHeloTroops.troopWeight = cfxZones.getNumberFromZoneProperty(theZone, "troopWeight", 100) -- kg average weight per trooper
cfxHeloTroops.troopWeight = theZone:getNumberFromZoneProperty("troopWeight", 100) -- kg average weight per trooper
cfxHeloTroops.autoDrop = cfxZones.getBoolFromZoneProperty(theZone, "autoDrop", false)
cfxHeloTroops.autoPickup = cfxZones.getBoolFromZoneProperty(theZone, "autoPickup", false)
cfxHeloTroops.pickupRange = cfxZones.getNumberFromZoneProperty(theZone, "pickupRange", 100)
cfxHeloTroops.combatDropScore = cfxZones.getNumberFromZoneProperty(theZone, "combatDropScore", 200)
cfxHeloTroops.autoDrop = theZone:getBoolFromZoneProperty("autoDrop", false)
cfxHeloTroops.autoPickup = theZone:getBoolFromZoneProperty("autoPickup", false)
cfxHeloTroops.pickupRange = theZone:getNumberFromZoneProperty("pickupRange", 100)
cfxHeloTroops.combatDropScore = theZone:getNumberFromZoneProperty( "combatDropScore", 200)
cfxHeloTroops.actionSound = cfxZones.getStringFromZoneProperty(theZone, "actionSound", "Quest Snare 3.wav")
cfxHeloTroops.actionSound = theZone:getStringFromZoneProperty("actionSound", "Quest Snare 3.wav")
cfxHeloTroops.requestRange = theZone:getNumberFromZoneProperty("requestRange", 500)
-- add own troop carriers
if cfxZones.hasProperty(theZone, "troopCarriers") then
local tc = cfxZones.getStringFromZoneProperty(theZone, "troopCarriers", "UH-1D")
if theZone:hasProperty("troopCarriers") then
local tc = theZone:getStringFromZoneProperty("troopCarriers", "UH-1D")
tc = dcsCommon.splitString(tc, ",")
cfxHeloTroops.troopCarriers = dcsCommon.trimArray(tc)
end

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {}
cfxSpawnZones.version = "1.7.5"
cfxSpawnZones.version = "2.0.0"
cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we
@ -68,27 +68,10 @@ cfxSpawnZones.spawnedGroups = {}
1.7.4 - wait-attackZone fixes
1.7.5 - improved verbosity on spawning
- getRequestableSpawnersInRange() ignores height for distance
- types - type strings, comma separated
see here: https://github.com/mrSkortch/DCS-miscScripts/tree/master/ObjectDB
- country - defaults to 2 (usa) -- see here https://wiki.hoggitworld.com/view/DCS_enum_country
some important: 0 = Russia, 2 = US, 82 = UN neutral
country is converted to coalition and then assigned to
Joint Task Force <side> upon spawn
- formation - default is circle_out; other formations are
- line - left lo right (west-east) facing north
- line_V - vertical line, facing north
- chevron - west-east, point growing to north
- scattered, random
- circle, circle_forward (all fact north)
- circle-in (all face in)
- circle-out (all face out)
- grid, square, rect arrayed in optimal grid
- 2deep, 2cols two columns, deep
- 2wide 2 columns wide (2 deep)
2.0.0 - dmlZones
- moved "types" to spawner
- baseName defaults to zone name, as it is safe for naming
- spawnWithSpawner direct link in spawner to spawnZones
--]]--
cfxSpawnZones.allSpawners = {}
@ -115,103 +98,101 @@ function cfxSpawnZones.createSpawner(inZone)
local theSpawner = {}
theSpawner.zone = inZone
theSpawner.name = inZone.name
theSpawner.spawnWithSpawner = cfxSpawnZones.spawnWithSpawner
-- interface to groupTracker
-- WARNING: attaches to ZONE, not spawner object
if cfxZones.hasProperty(inZone, "trackWith:") then
inZone.trackWith = cfxZones.getStringFromZoneProperty(inZone, "trackWith:", "<None>")
if inZone:hasProperty("trackWith:") then
inZone.trackWith = inZone:getStringFromZoneProperty("trackWith:", "<None>")
end
-- interface to delicates
if cfxZones.hasProperty(inZone, "useDelicates") then
theSpawner.delicateName = dcsCommon.trim(cfxZones.getStringFromZoneProperty(inZone, "useDelicates", "<none>"))
if inZone:hasProperty("useDelicates") then
theSpawner.delicateName = dcsCommon.trim(inZone:getStringFromZoneProperty("useDelicates", "<none>"))
if theSpawner.delicateName == "*" then theSpawner.delicateName = inZone.name end
end
-- connect with ME if a trigger flag is given
if cfxZones.hasProperty(inZone, "f?") then
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "f?", "none")
if inZone:hasProperty("f?") then
theSpawner.triggerFlag = inZone:getStringFromZoneProperty("f?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
elseif inZone:hasProperty("spawn?") then
theSpawner.triggerFlag = inZone:getStringFromZoneProperty("spawn?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
elseif inZone:hasProperty("spawnUnits?") then
theSpawner.triggerFlag = inZone:getStringFromZoneProperty( "spawnObject?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
end
-- synonyms spawn? and spawnObject?
if cfxZones.hasProperty(inZone, "spawn?") then
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawn?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
end
if cfxZones.hasProperty(inZone, "spawnUnits?") then
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawnObject?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
end
if cfxZones.hasProperty(inZone, "activate?") then
theSpawner.activateFlag = cfxZones.getStringFromZoneProperty(inZone, "activate?", "none")
if inZone:hasProperty("activate?") then
theSpawner.activateFlag = inZone:getStringFromZoneProperty( "activate?", "none")
theSpawner.lastActivateValue = trigger.misc.getUserFlag(theSpawner.activateFlag)
end
if cfxZones.hasProperty(inZone, "pause?") then
theSpawner.pauseFlag = cfxZones.getStringFromZoneProperty(inZone, "pause?", "none")
if inZone:hasProperty("pause?") then
theSpawner.pauseFlag = inZone:getStringFromZoneProperty("pause?", "none")
theSpawner.lastPauseValue = trigger.misc.getUserFlag(theSpawner.pauseFlag)
end
theSpawner.types = cfxZones.getZoneProperty(inZone, "types")
--theSpawner.owner = cfxZones.getCoalitionFromZoneProperty(inZone, "owner", 0)
-- synthesize types * typeMult
local n = cfxZones.getNumberFromZoneProperty(inZone, "typeMult", 1)
local repeater = ""
if n < 1 then n = 1 end
while n > 1 do
repeater = repeater .. "," .. theSpawner.types
n = n - 1
if inZone:hasProperty("types") then
theSpawner.types = inZone:getStringFromZoneProperty("types", "Soldier M4")
else
theSpawner.types = inZone:getStringFromZoneProperty("spawner", "Soldier M4")
end
-- synthesize types * typeMult
if inZone:hasProperty("typeMult") then
local n = inZone:getNumberFromZoneProperty("typeMult", 1)
local repeater = ""
if n < 1 then n = 1 end
while n > 1 do
repeater = repeater .. "," .. theSpawner.types
n = n - 1
end
theSpawner.types = theSpawner.types .. repeater
end
theSpawner.types = theSpawner.types .. repeater
theSpawner.country = cfxZones.getNumberFromZoneProperty(inZone, "country", 0) -- coalition2county(theSpawner.owner)
theSpawner.masterZoneName = cfxZones.getStringFromZoneProperty(inZone, "masterOwner", "")
if theSpawner.masterZoneName == "" then theSpawner.masterZoneName = nil end
theSpawner.country = inZone:getNumberFromZoneProperty("country", 0)
if inZone:hasProperty("masterOwner") then
theSpawner.masterZoneName = inZone:getStringFromZoneProperty("masterOwner", "")
if theSpawner.masterZoneName == "" then theSpawner.masterZoneName = nil end
end
theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)
--theSpawner.baseName = cfxZones.getZoneProperty(inZone, "baseName")
theSpawner.baseName = cfxZones.getStringFromZoneProperty(inZone, "baseName", dcsCommon.uuid("SpwnDflt"))
-- theSpawner.baseName = inZone:getStringFromZoneProperty("baseName", dcsCommon.uuid("SpwnDflt"))
theSpawner.baseName = inZone:getStringFromZoneProperty("baseName", "*")
theSpawner.baseName = dcsCommon.trim(theSpawner.baseName)
if theSpawner.baseName == "*" then
theSpawner.baseName = inZone.name -- convenience shortcut
end
theSpawner.cooldown = cfxZones.getNumberFromZoneProperty(inZone, "cooldown", 60)
theSpawner.autoRemove = cfxZones.getBoolFromZoneProperty(inZone, "autoRemove", false)
theSpawner.lastSpawnTimeStamp = -10000 -- just init so it will always work
theSpawner.heading = cfxZones.getNumberFromZoneProperty(inZone, "heading", 0)
--trigger.action.outText("+++spwn: zone " .. inZone.name .. " owner " .. theSpawner.owner " --> ctry " .. theSpawner.country, 30)
theSpawner.cooldown = inZone:getNumberFromZoneProperty("cooldown", 60)
theSpawner.autoRemove = inZone:getBoolFromZoneProperty("autoRemove", false)
theSpawner.lastSpawnTimeStamp = -10000 -- init so it will always work
theSpawner.heading = inZone:getNumberFromZoneProperty("heading", 0)
theSpawner.cdTimer = 0 -- used for cooldown. if timer.getTime < this value, don't spawn
theSpawner.cdStarted = false -- used to initiate cooldown when theSpawn disappears
theSpawner.count = 1 -- used to create names, and count how many groups created
theSpawner.theSpawn = nil -- link to last spawned group
theSpawner.formation = "circle_out"
theSpawner.formation = cfxZones.getStringFromZoneProperty(inZone, "formation", "circle_out")
theSpawner.paused = cfxZones.getBoolFromZoneProperty(inZone, "paused", false)
theSpawner.formation = inZone:getStringFromZoneProperty("formation", "circle_out")
theSpawner.paused = inZone:getBoolFromZoneProperty("paused", false)
-- orders are always converted to all lower case
theSpawner.orders = cfxZones.getStringFromZoneProperty(inZone, "orders", "guard"):lower()
theSpawner.orders = inZone:getStringFromZoneProperty("orders", "guard"):lower()
-- used to assign orders, default is 'guard', use "laze" to make them laze targets. can be 'wait-' which may auto-convert to 'guard' after pick-up by helo, to be handled outside.
-- use "train" to tell them to HOLD WEAPONS, don't move and don't participate in loop, so we have in effect target dummies
-- can also use order 'dummy' or 'dummies' to switch to train
if theSpawner.orders:lower() == "dummy" or theSpawner.orders:lower() == "dummies" then theSpawner.orders = "train" end
if theSpawner.orders:lower() == "training" then theSpawner.orders = "train" end
theSpawner.range = cfxZones.getNumberFromZoneProperty(inZone, "range", 300) -- if we have a range, for example enemy detection for Lasing or engage range
theSpawner.maxSpawns = cfxZones.getNumberFromZoneProperty(inZone, "maxSpawns", -1) -- if there is a limit on how many troops can spawn. -1 = endless spawns
theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false)
theSpawner.range = inZone:getNumberFromZoneProperty("range", 300) -- if we have a range, for example enemy detection for Lasing or engage range
theSpawner.maxSpawns = inZone:getNumberFromZoneProperty("maxSpawns", -1) -- if there is a limit on how many troops can spawn. -1 = endless spawns
theSpawner.requestable = inZone:getBoolFromZoneProperty( "requestable", false)
if theSpawner.requestable then
theSpawner.paused = true
if inZone.verbose or cfxSpawnZones.verbose then
trigger.action.outText("+++spwn: spawner <" .. inZone.name .. "> paused: requestable enabled", 30)
end
end
if cfxZones.hasProperty(inZone, "target") then
theSpawner.target = cfxZones.getStringFromZoneProperty(inZone, "target", "")
if inZone:hasProperty("target") then
theSpawner.target = inZone:getStringFromZoneProperty("target", "")
if theSpawner.target == "" then -- this is the defaut case
theSpawner.target = nil
end

View File

@ -1,5 +1,5 @@
cloneZones = {}
cloneZones.version = "1.8.2"
cloneZones.version = "1.9.0"
cloneZones.verbose = false
cloneZones.requiredLibs = {
"dcsCommon", -- always
@ -100,8 +100,12 @@ cloneZones.respawnOnGroupID = true
- upgraded config zone parsing
1.8.1 - clone zone definition now supports quads
1.8.2 - on pre-wipe, delay respawn by 0.5s to avoid 'dropping' statics
1.9.0 - minor clean-up for synonyms
- spawnWithSpawner alias for HeloTroops etc requestable SPAWN
- requestable attribute
- cooldown attribute
- cloner collects all types used
- groupScheme attribute
--]]--
--
@ -194,7 +198,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
if cloneZones.verbose or theZone.verbose then
trigger.action.outText("+++clnZ: new cloner <" .. theZone.name ..">", 30)
end
theZone.spawnWithSpawner = cloneZones.spawnWithSpawner
theZone.myUniqueCounter = cloneZones.lclUniqueCounter -- init local counter
local localZones = cloneZones.allGroupsInZoneByData(theZone)
@ -226,11 +230,11 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.source = theZone:getStringFromZoneProperty("source", "<none>")
if theZone.source == "<none>" then theZone.source = nil end
end
theZone.allTypes = {} -- names of all types
if not theZone.source then
theZone.cloneNames = {} -- names of the groups. only present in template spawners
theZone.staticNames = {} -- names of all statics. only present in templates
theZone.staticNames = {} -- names of all statics. only present in templates
for idx, aGroup in pairs(localZones) do
local gName = aGroup:getName()
if gName then
@ -239,10 +243,19 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
-- now get group data and save a lookup for
-- resolving internal references
local rawData, cat, ctry = cfxMX.getGroupFromDCSbyName(gName)
-- iterate all units and save their individual types
for idy, aUnit in pairs(rawData.units) do
local theType = aUnit.type
-- trigger.action.outText("proccing type <" .. theType .. ">", 30)
if not theZone.allTypes[theType] then
theZone.allTypes[theType] = 1 -- first one
else
theZone.allTypes[theType] = theZone.allTypes[theType] + 1 -- increment
end
end
local origID = rawData.groupId
end
end
for idx, aStatic in pairs (localObjects) do
local sName = aStatic:getName()
if sName then
@ -277,17 +290,11 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
-- f? and spawn? and other synonyms map to the same
if theZone:hasProperty("f?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("f?", "none")
end
if theZone:hasProperty("in?") then
elseif theZone:hasProperty("in?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("in?", "none")
end
if theZone:hasProperty("spawn?") then
elseif theZone:hasProperty("spawn?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("spawn?", "none")
end
if theZone:hasProperty("clone?") then
elseif theZone:hasProperty("clone?") then
theZone.spawnFlag = theZone:getStringFromZoneProperty("clone?", "none")
end
@ -298,13 +305,9 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
-- deSpawn?
if theZone:hasProperty("deSpawn?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deSpawn?", "none")
end
if theZone:hasProperty("deClone?") then
elseif theZone:hasProperty("deClone?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deClone?", "none")
end
if theZone:hasProperty("wipe?") then
elseif theZone:hasProperty("wipe?") then
theZone.deSpawnFlag = theZone:getStringFromZoneProperty("wipe?", "none")
end
@ -312,6 +315,9 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.lastDeSpawnValue = theZone:getFlagValue(theZone.deSpawnFlag)
end
theZone.cooldown = theZone:getNumberFromZoneProperty("cooldown", -1) -- anything > 0 activates cd
theZone.lastSpawnTimeStamp = -10000
theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false)
theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false)
@ -354,6 +360,15 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
end
end
-- interface to requestable, must be unsourced!
if theZone:hasProperty("requestable") then
theZone.requestable = theZone:getBoolFromZoneProperty( "requestable", false)
theZone.baseName = theZone.name -- backward compatibility with HeloTroops
if theZone.source then
trigger.action.outText("WARNING: cloner <" .. theZone.name .. "> has 'source' attribute and is marked 'requestable' - this can result in unrequestable clones", 30)
end
end
-- randomized locations on spawn
theZone.rndLoc = theZone:getBoolFromZoneProperty("randomizedLoc", false)
if theZone:hasProperty("rndLoc") then
@ -380,9 +395,14 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.nameScheme = theZone:getStringFromZoneProperty( "nameScheme", "<o>-<uid>") -- default to [<original name> "-" <uuid>]
end
if theZone:hasProperty("groupScheme") then
theZone.groupScheme = theZone:getStringFromZoneProperty("groupScheme", "<o>-<uid>")
end
if theZone.identical and theZone.nameScheme then
trigger.action.outText("+++clnZ: WARNING - clone zone <" .. theZone.name .. "> has both IDENTICAL and NAMESCHEME attributes. nameScheme is ignored.", 30)
theZone.nameScheme = nil
trigger.action.outText("+++clnZ: WARNING - clone zone <" .. theZone.name .. "> has both IDENTICAL and NAMESCHEME/GROUPSCHEME attributes. nameScheme is ignored.", 30)
theZone.nameScheme = nil
theZone.groupScheme = nil
end
-- we end with clear plate
end
@ -629,14 +649,19 @@ end
function cloneZones.uniqueNameGroupData(theData, theCloneZone, sourceName)
if not sourceName then sourceName = theCloneZone.name end
theData.name = dcsCommon.uuid(theData.name)
if not theCloneZone.groupScheme then
theData.name = dcsCommon.uuid(theData.name)
else
theData.name = cloneZones.nameFromSchema(theCloneZone.groupScheme, theData.name, theCloneZone, sourceName, 1)
end
local schema = theCloneZone.nameScheme
local units = theData.units
local iterCount = 1
local newName = "none"
local allNames = {} -- enforce unique names inside group
for idx, aUnit in pairs(units) do
if theCloneZone and theCloneZone.nameScheme then
local schema = theCloneZone.nameScheme
newName, iterCount = cloneZones.nameFromSchema(schema, aUnit.name, theCloneZone, sourceName, iterCount)
-- make sure that this name is has not been generated yet
@ -1490,6 +1515,16 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
return spawnedGroups, spawnedStatics
end
-- retro-fit for helo troops and others to provide 'requestable' support
function cloneZones.spawnWithSpawner(theZone)
-- analog to cfxSpawnZones.spawnWithSpawner(theSpawner)
-- glue code for helo troops and other modules
-- we may want to check if cloner isn't emtpy first
cloneZones.spawnWithCloner(theZone)
end
function cloneZones.spawnWithCloner(theZone)
if not theZone then
trigger.action.outText("+++clnZ: nil zone on spawnWithCloner", 30)
@ -1500,11 +1535,24 @@ function cloneZones.spawnWithCloner(theZone)
return
end
-- see if we are on cooldown. If so, exit
if theZone.cooldown > 0 then
local now = timer.getTime()
if now < theZone.lastSpawnTimeStamp + theZone.cooldown then
if theZone.verbose or cloneZones.verbose then
trigger.action.outText("+++clnZ: cloner <" .. theZone.name .. "> still on cool-down, no clone cycle", 30)
end
return
else
theZone.lastSpawnTimeStamp = now
end
end
-- force spawn with this spawner
local templateZone = theZone
if theZone.source then
-- we use a different zone for templates
-- souce can be a comma separated list
-- source can be a comma separated list
local templateName = theZone.source
if dcsCommon.containsString(templateName, ",") then
local allNames = templateName
@ -1615,18 +1663,8 @@ function cloneZones.hasLiveUnits(theZone)
if theZone.mySpawns then
for idx, aGroup in pairs(theZone.mySpawns) do
if aGroup:isExist() then
-- an easier/faster method would be to invoke
-- aGroup:getSize()
local uNum = aGroup:getSize()
if uNum > 0 then return true end
--[[
local allUnits = aGroup:getUnits()
for idy, aUnit in pairs(allUnits) do
if aUnit:isExist() and aUnit:getLife() >= 1 then
return true
end
end
--]]--
end
end
end
@ -1642,6 +1680,49 @@ function cloneZones.hasLiveUnits(theZone)
return false
end
function cloneZones.resolveOwningCoalition(theZone)
if not theZone.masterOwner then return theZone.owner end
local masterZone = cfxZones.getZoneByName(theZone.masterOwner)
if not masterZone then
trigger.action.outText("+++clnZ: cloner " .. theZone.name .. " could not find master owner <" .. theZone.masterOwner .. ">", 30)
return theZone.owner
end
return masterZone.owner
end
function cloneZones.getRequestableClonersInRange(aPoint, aRange, aSide)
if not aSide then aSide = 0 end
if not aRange then aRange = 200 end
if not aPoint then return {} end
local theSpawners = {}
for idx, aZone in pairs(cloneZones.cloners) do
-- iterate all zones and collect those that match
local hasMatch = true
local delta = dcsCommon.distFlat(aPoint, aZone:getPoint())
if delta > aRange then hasMatch = false end
if aSide ~= 0 then
-- check if side is correct for owned zone
local resolved = cloneZones.resolveOwningCoalition(aZone)
--if resolved ~= 0 and resolved ~= aSide then
if resolved == 0 or resolved ~= aSide then
-- failed ownership test. must match and not be zero
hasMatch = false
end
end
if not aZone.requestable then
hasMatch = false
end
if hasMatch then
table.insert(theSpawners, aZone)
end
end
return theSpawners
end
--
-- UPDATE
--
@ -1672,14 +1753,7 @@ function cloneZones.update()
-- empty handling
local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones
if isEmpty then
-- see if we need to bang a flag
--[[--
if aZone.emptyFlag then
--cloneZones.pollFlag(aZone.emptyFlag)
cfxZones.pollFlag(aZone.emptyFlag, 'inc', aZone)
end
--]]--
-- see if we need to bang a flag
if aZone.emptyBangFlag then
aZone:pollFlag(aZone.emptyBangFlag, aZone.cloneMethod)
if cloneZones.verbose then
@ -2049,8 +2123,6 @@ end
- FAC Assign group
- set freq for unit
-- fixedName: immutable name, no safe renaming. always use ID and name without changing it. very special use case.
nameTest - optional safety / debug feature that will name-test each unit that is about to be spawned for replacement. Maybe auto turn on when verbose is set?
identical - make a clone of template and do not touch name nor id. will fully replace
make example where transport can be different plane types but have same name
--]]--

View File

@ -15,6 +15,10 @@ raiseFlag.flags = {}
1.1.0 - DML update
1.2.0 - Watchflag update
1.2.1 - support for 'inc', 'dec', 'flip'
2.0.0 - dmlZones
- full method support
- full DML upgrade
- method attribute (synonym to 'value'
--]]--
function raiseFlag.addRaiseFlag(theZone)
@ -43,22 +47,28 @@ function raiseFlag.createRaiseFlagWithZone(theZone)
theZone.raiseFlag = cfxZones.getStringFromZoneProperty(theZone, "raiseFlag!", "<none>") -- the flag to raise
end
theZone.flagValue = cfxZones.getStringFromZoneProperty(theZone, "value", "inc") -- value to set to. default is command 'inc'
-- pre-method DML raiseFlag is now upgraded to method.
-- flagValue now carries the method
if theZone:hasProperty("value") then -- backward compatibility
theZone.flagValue = theZone:getStringFromZoneProperty("value", "inc") -- value to set to. default is command 'inc'
else
theZone.flagValue = theZone:getStringFromZoneProperty("method", "inc")
end
theZone.flagValue = theZone.flagValue:lower()
theZone.minAfterTime, theZone.maxAfterTime = cfxZones.getPositiveRangeFromZoneProperty(theZone, "afterTime", -1)
theZone.minAfterTime, theZone.maxAfterTime = theZone:getPositiveRangeFromZoneProperty("afterTime", -1)
-- method for triggering
-- watchflag:
-- triggerMethod
theZone.raiseTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
if cfxZones.hasProperty(theZone, "raiseTriggerMethod") then
theZone.raiseTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "raiseTriggerMethod", "change")
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
if theZone:hasProperty("raiseTriggerMethod") then
theZone.raiseTriggerMethod = theZone:getStringFromZoneProperty("raiseTriggerMethod", "change")
end
if cfxZones.hasProperty(theZone, "stopFlag?") then
theZone.triggerStopFlag = cfxZones.getStringFromZoneProperty(theZone, "stopFlag?", "none")
theZone.lastTriggerStopValue = cfxZones.getFlagValue(theZone.triggerStopFlag, theZone) -- save last value
if theZone:hasProperty("stopFlag?") then
theZone.triggerStopFlag = theZone:getStringFromZoneProperty( "stopFlag?", "none")
theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopFlag) -- save last value
end
theZone.scheduleID = nil
@ -80,6 +90,12 @@ function raiseFlag.triggered(args)
if theZone.raiseStopped then return end
-- if we get here, we aren't stopped and do the flag pull
local command = theZone.flagValue
theZone:pollFlag(theZone.raiseFlag, command)
if raiseFlag.verbose or theZone.verbose then
trigger.action.outText("+++rFlg - raising <" .. theZone.raiseFlag .. "> with method '" .. command .. "'" ,30)
end
--[[--
command = dcsCommon.trim(command)
if command == "inc" or command == "dec" or command == "flip" then
cfxZones.pollFlag(theZone.raiseFlag, command, theZone)
@ -92,6 +108,7 @@ function raiseFlag.triggered(args)
trigger.action.outText("+++rFlg - raising <" .. theZone.raiseFlag .. "> to value: " .. theZone.flagValue ,30)
end
end
--]]--
end
--
@ -103,20 +120,10 @@ function raiseFlag.update()
for idx, aZone in pairs(raiseFlag.flags) do
-- make sure to re-start before reading time limit
if cfxZones.testZoneFlag(aZone, aZone.triggerStopFlag, aZone.raiseTriggerMethod, "lastTriggerStopValue") then
if aZone:testZoneFlag(aZone.triggerStopFlag, aZone.raiseTriggerMethod, "lastTriggerStopValue") then
theZone.raiseStopped = true -- we are done, no flag!
end
-- old code
--[[--
if aZone.triggerStopFlag then
local currTriggerVal = cfxZones.getFlagValue(aZone.triggerStopFlag, theZone)
if currTriggerVal ~= aZone.lastTriggerStopValue
then
theZone.raiseStopped = true -- we are done, no flag!
end
end
--]]--
end
end
@ -127,13 +134,10 @@ end
function raiseFlag.readConfigZone()
local theZone = cfxZones.getZoneByName("raiseFlagConfig")
if not theZone then
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: NO config zone!", 30)
end
return
theZone = cfxZones.createSimpleZone("raiseFlagConfig")
end
raiseFlag.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
raiseFlag.verbose = theZone.verbose
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: read config", 30)
@ -178,6 +182,3 @@ if not raiseFlag.start() then
trigger.action.outText("cfx Raise Flag aborted: missing libraries", 30)
raiseFlag = nil
end
-- add rnd(a,b) support to value
-- better: if value is a range, make it a random. problem: negative values are legal, so we need formula

View File

@ -1,5 +1,5 @@
stopGap = {}
stopGap.version = "1.0.8"
stopGap.version = "1.0.9"
stopGap.verbose = false
stopGap.ssbEnabled = true
stopGap.ignoreMe = "-sg"

Binary file not shown.