Version 0.985

Quick Ref
Clone Zones Reference Resolve
Clone Relations Demo
This commit is contained in:
Christian Franz 2022-02-24 16:55:32 +01:00
parent d69ce05d80
commit f1aa9cabf1
22 changed files with 885 additions and 138 deletions

Binary file not shown.

BIN
Doc/DML Quick Reference.pdf Normal file

Binary file not shown.

View File

@ -178,13 +178,8 @@ function FARPZones.createFARPFromZone(aZone)
return theFarp
end
--[[--
function FARPZones.drawFarp(theFarp)
local theZone = theFarp.zone
local theOwner = theFarp.owner
FARPZones.drawZoneInMap(theZone, theOwner)
end
--]]--
function FARPZones.drawFARPCircleInMap(theFarp)
if not theFarp then return end
@ -430,6 +425,7 @@ function FARPZones.readConfig()
FARPZones.spinUpDelay = cfxZones.getNumberFromZoneProperty(theZone, "spinUpDelay", 30)
if FARPZones.verbose then
trigger.action.outText("***frpZ: read config", 30)
end

View File

@ -1,5 +1,5 @@
cfxArtilleryZones = {}
cfxArtilleryZones.version = "2.0.1"
cfxArtilleryZones.version = "2.0.2"
cfxArtilleryZones.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
@ -24,7 +24,8 @@ cfxArtilleryZones.verbose = false
- att transition time to zone info mark
- made compatible with linked zones
- added silent attribute
- added transition time to arty command chatter
- added transition time to arty command chatter
2.0.2 - boom?, arty? synonyms
Artillery Target Zones *** EXTENDS ZONES ***
Target Zones for artillery. Can determine which zones are in range and visible and then handle artillery barrage to this zone
@ -121,13 +122,18 @@ function cfxArtilleryZones.processArtilleryZone(aZone)
aZone.addMark = cfxZones.getBoolFromZoneProperty(aZone, "addMark", true) -- note: defaults to true
aZone.shellVariance = cfxZones.getNumberFromZoneProperty(aZone, "shellVariance", 0.2) -- strength of explosion can vary by +/- this amount
if cfxZones.hasProperty(aZone, "f?") then
aZone.triggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
end
--[[--
if cfxZones.hasProperty(aZone, "triggerFlag") then
aZone.triggerFlag = cfxZones.getStringFromZoneProperty(aZone, "triggerFlag", "none")
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "triggerFlag", "none")
end
if aZone.triggerFlag then
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.triggerFlag) -- save last value
--]]--
if cfxZones.hasProperty(aZone, "artillery?") then
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "artillery?", "none")
end
if aZone.artyTriggerFlag then
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.artyTriggerFlag) -- save last value
end
aZone.cooldown =cfxZones.getNumberFromZoneProperty(aZone, "cooldown", 120) -- seconds
aZone.baseAccuracy = cfxZones.getNumberFromZoneProperty(aZone, "baseAccuracy", aZone.radius) -- meters from center radius shell impact
@ -380,8 +386,8 @@ function cfxArtilleryZones.update()
-- iterate all zones to see if a trigger has changed
for idx, aZone in pairs(cfxArtilleryZones.artilleryZones) do
if aZone.triggerFlag then
local currTriggerVal = trigger.misc.getUserFlag(aZone.triggerFlag)
if aZone.artyTriggerFlag then
local currTriggerVal = trigger.misc.getUserFlag(aZone.artyTriggerFlag)
if currTriggerVal ~= aZone.lastTriggerValue
then
-- a triggered release!

View File

@ -1,5 +1,5 @@
cfxCargoReceiver = {}
cfxCargoReceiver.version = "1.1.0"
cfxCargoReceiver.version = "1.2.0"
cfxCargoReceiver.ups = 1 -- once a second
cfxCargoReceiver.maxDirectionRange = 500 -- in m. distance when cargo manager starts talking to pilots who are carrying that cargo
cfxCargoReceiver.requiredLibs = {
@ -13,7 +13,9 @@ cfxCargoReceiver.requiredLibs = {
- 1.0.0 initial vbersion
- 1.1.0 added flag manipulation options
no negative agl on announcement
silent attribute
silent attribute
- 1.2.0 method
f!, cargoReceived!
CargoReceiver is a zone enhancement you use to be automatically
@ -67,6 +69,17 @@ function cfxCargoReceiver.processReceiverZone(aZone) -- process attribute and ad
if cfxZones.hasProperty(aZone, "f-1") then
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
end
-- new method support
aZone.method = cfxZones.getStringFromZoneProperty(aZone, "method", "inc")
if cfxZones.hasProperty(aZone, "f!") then
aZone.outReceiveFlag = cfxZones.getNumberFromZoneProperty(aZone, "f!", -1)
end
if cfxZones.hasProperty(aZone, "cargoReceived!") then
aZone.outReceiveFlag = cfxZones.getNumberFromZoneProperty(aZone, "cargoReceived!", -1)
end
end
function cfxCargoReceiver.addReceiverZone(aZone)
@ -132,6 +145,9 @@ function cfxCargoReceiver.cargoEvent(event, object, name)
trigger.action.setUserFlag(aZone.decreaseFlag, val)
end
if aZone.outReceiveFlag then
cfxZones.pollFlag(aZone.outReceiveFlag, aZone.method)
end
--trigger.action.outText("+++rcv: " .. name .. " delivered in zone " .. aZone.name, 30)
--trigger.action.outSound("Quest Snare 3.wav")

View File

@ -1,5 +1,5 @@
cfxMX = {}
cfxMX.version = "1.0.1"
cfxMX.version = "1.1.0"
--[[--
Mission data decoder. Access to ME-built mission structures
@ -8,9 +8,17 @@ cfxMX.version = "1.0.1"
Version History
1.0.0 - initial version
1.0.1 - getStaticFromDCSbyName()
1.1.0 - getStaticFromDCSbyName also copies groupID when not fetching orig
- on start up collects a cross reference table of all
original group id
- add linkUnit for statics
--]]--
cfxMX.groupNamesByID = {}
cfxMX.groupIDbyName = {}
cfxMX.groupDataByName = {}
function cfxMX.getGroupFromDCSbyName(aName, fetchOriginal)
if not fetchOriginal then fetchOriginal = false end
@ -105,6 +113,15 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
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 static in group!
for group_num, group_data in pairs(obj_type_data.group) do
-- 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
if linkUnit then
--trigger.action.outText("MX: found missing link to " .. linkUnit .. " in " .. group_data.name, 30)
end
end
if group_data and group_data.units and type(group_data.units) == 'table'
then --make sure - again - that this is a valid group
for unit_num, unit_data in pairs(group_data.units) do -- iterate units
@ -113,6 +130,11 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
local theStatic = unit_data
if not fetchOriginal then
theStatic = dcsCommon.clone(unit_data)
-- copy group ID from group above
theStatic.groupId = group_data.groupId
-- copy linked unit data
theStatic.linkUnit = linkUnit
end
return theStatic, category, countryID, groupName
@ -132,6 +154,51 @@ function cfxMX.getStaticFromDCSbyName(aName, fetchOriginal)
return nil, "<none>", "<none>", "<no group name>"
end
function cfxMX.createCrossReference()
for coa_name_miz, coa_data in pairs(env.mission.coalition) do -- iterate all coalitions
local coa_name = coa_name_miz
if string.lower(coa_name_miz) == 'neutrals' then -- remove 's' at neutralS
coa_name = 'neutral'
end
-- directly convert coalition into number for easier access later
local coaNum = 0
if coa_name == "red" then coaNum = 1 end
if coa_name == "blue" then coaNum = 2 end
if type(coa_data) == 'table' then -- coalition = {bullseye, nav_points, name, county},
-- with county being an array
if coa_data.country then -- make sure there a country table for this coalition
for cntry_id, cntry_data in pairs(coa_data.country) do -- iterate all countries for this
-- per country = {id, name, vehicle, helicopter, plane, ship, static}
local countryName = string.lower(cntry_data.name)
local countryID = cntry_data.id
if type(cntry_data) == 'table' then -- filter strings .id and .name
for obj_type_name, obj_type_data in pairs(cntry_data) do
if obj_type_name == "helicopter" or
obj_type_name == "ship" or
obj_type_name == "plane" or
obj_type_name == "vehicle" or
obj_type_name == "static"
then -- (so it's not id or 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!
for group_num, group_data in pairs(obj_type_data.group) do
local aName = group_data.name
local aID = group_data.groupId
cfxMX.groupNamesByID[aID] = aName
cfxMX.groupIDbyName[aName] = aID
cfxMX.groupDataByName[aName] = group_data
end
end --if has category data
end --if plane, helo etc... category
end --for all objects in country
end --if has country data
end --for all countries in coalition
end --if coalition has country table
end -- if there is coalition data
end --for all coalitions in mission
end
function cfxMX.catText2ID(inText)
local outCat = 0 -- airplane
local c = inText:lower()
@ -144,3 +211,11 @@ function cfxMX.catText2ID(inText)
return outCat
end
function cfxMX.start()
cfxMX.createCrossReference()
trigger.action.outText("cfxMX: "..#cfxMX.groupNamesByID .. " groups processed successfully", 30)
end
-- start
cfxMX.start()

View File

@ -1,5 +1,5 @@
cfxObjectDestructDetector = {}
cfxObjectDestructDetector.version = "1.0.1"
cfxObjectDestructDetector.version = "1.1.0"
cfxObjectDestructDetector.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
@ -9,6 +9,8 @@ cfxObjectDestructDetector.verbose = false
VERSION HISTORY
1.0.0 initial version, based on parashoo, arty zones
1.0.1 fixed bug: trigger.MISC.getUserFlag()
1.1.0 added support for method, f! and destroyed!
Detect when an object with OBJECT ID as assigned in ME dies
*** EXTENDS ZONES
@ -69,6 +71,16 @@ function cfxObjectDestructDetector.processObjectDestructZone(aZone)
if cfxZones.hasProperty(aZone, "f-1") then
aZone.decreaseFlag = cfxZones.getStringFromZoneProperty(aZone, "f-1", "999")
end
-- new method support
aZone.method = cfxZones.getStringFromZoneProperty(aZone, "method", "flip")
if cfxZones.hasProperty(aZone, "f!") then
aZone.outDestroyFlag = cfxZones.getNumberFromZoneProperty(aZone, "f!", -1)
end
if cfxZones.hasProperty(aZone, "destroyed!") then
aZone.outDestroyFlag = cfxZones.getNumberFromZoneProperty(aZone, "destroyed!", -1)
end
end
--
-- MAIN DETECTOR
@ -98,6 +110,11 @@ function cfxObjectDestructDetector:onEvent(event)
trigger.action.setUserFlag(aZone.decreaseFlag, val)
end
-- support for banging
if aZone.outDestroyFlag then
cfxZones.pollFlag(aZone.outDestroyFlag, aZone.method)
end
-- invoke callbacks
cfxObjectDestructDetector.invokeCallbacksFor(aZone)
if cfxObjectDestructDetector.verbose then

View File

@ -1,5 +1,5 @@
cfxObjectSpawnZones = {}
cfxObjectSpawnZones.version = "1.1.4"
cfxObjectSpawnZones.version = "1.1.5"
cfxObjectSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we
@ -23,6 +23,7 @@ cfxObjectSpawnZones.ups = 1
-- - added possibility to autoUnlink
-- 1.1.3 - ME-triggered flag via f? and triggerFlag
-- 1.1.4 - activate?, pause? attributes
-- 1.1.5 - spawn?, spawnObjects? synonyms
-- Object spawn zones have the following major uses:
-- - dynamically spawn cargo
@ -85,6 +86,16 @@ function cfxObjectSpawnZones.createSpawner(inZone)
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
end
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, "spawnObjects?") then
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawnObjects?", "none")
theSpawner.lastTriggerValue = trigger.misc.getUserFlag(theSpawner.triggerFlag)
end
if cfxZones.hasProperty(inZone, "activate?") then
theSpawner.activateFlag = cfxZones.getStringFromZoneProperty(inZone, "activate?", "none")
theSpawner.lastActivateValue = trigger.misc.getUserFlag(theSpawner.activateFlag)

View File

@ -1,5 +1,5 @@
cfxSmokeZone = {}
cfxSmokeZone.version = "1.0.3"
cfxSmokeZone.version = "1.0.4"
cfxSmokeZone.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
@ -12,7 +12,10 @@ cfxSmokeZone.requiredLibs = {
1.0.3 - added paused attribute
- added f? attribute --> onFlag
- broke out startSmoke
1.0.4 - startSmoke? synonym
- alphanum flag upgrade
- random color support
SMOKE ZONES *** EXTENDS ZONES ***
keeps 'eternal' smoke up for any zone that has the
'smoke' attribute
@ -34,6 +37,9 @@ function cfxSmokeZone.processSmokeZone(aZone)
if rawVal == "white" or rawVal == "2" then theColor = 2 end
if rawVal == "orange" or rawVal == "3" then theColor = 3 end
if rawVal == "blue" or rawVal == "4" then theColor = 4 end
if rawVal == "?" or rawVal == "random" or rawVal == "rnd" then
theColor = dcsCommon.smallRandom(5) - 1
end
aZone.smokeColor = theColor
aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "altitude", 1)
@ -46,8 +52,12 @@ function cfxSmokeZone.processSmokeZone(aZone)
aZone.onFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
end
if cfxZones.hasProperty(aZone, "startSmoke?") then
aZone.onFlag = cfxZones.getStringFromZoneProperty(aZone, "startSmoke?", "none")
end
if aZone.onFlag then
aZone.onFlagVal = trigger.misc.getUserFlag(aZone.onFlag) -- save last value
aZone.onFlagVal = cfxZones.getFlagValue(aZone.onFlag, aZone) -- save last value
end
end
@ -67,7 +77,7 @@ function cfxSmokeZone.addSmokeZoneWithColor(aZone, aColor, anAltitude, paused, o
if onFlag then
aZone.onFlag = onFlag
aZone.onFlagVal = trigger.misc.getUserFlag(onFlag)
aZone.onFlagVal = cfxZones.getFlagValue(aZone.onFlag, aZone) -- trigger.misc.getUserFlag(onFlag)
end
cfxSmokeZone.addSmokeZone(aZone) -- add to update loop
@ -123,7 +133,7 @@ function cfxSmokeZone.checkFlags()
for idx, aZone in pairs(cfxSmokeZone.smokeZones) do
if aZone.paused and aZone.onFlagVal then
-- see if this changed
local currTriggerVal = trigger.misc.getUserFlag(aZone.onFlag)
local currTriggerVal = cfxZones.getFlagValue(aZone.onFlag, aZone) -- trigger.misc.getUserFlag(aZone.onFlag)
if currTriggerVal ~= aZone.onFlagVal then
-- yupp, trigger start
cfxSmokeZone.startSmoke(aZone)

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {}
cfxSpawnZones.version = "1.5.2"
cfxSpawnZones.version = "1.5.3"
cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we
@ -52,6 +52,7 @@ cfxSpawnZones.verbose = false
-- 1.5.1 - relaxed baseName and default to dcsCommon.uuid()
-- - verbose
-- 1.5.2 - activate?, pause? flag
-- 1.5.3 - spawn?, spawnUnits? flags
--
-- new version requires cfxGroundTroops, where they are
--
@ -140,6 +141,17 @@ function cfxSpawnZones.createSpawner(inZone)
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")
theSpawner.lastActivateValue = trigger.misc.getUserFlag(theSpawner.activateFlag)

View File

@ -6,7 +6,7 @@
--
cfxZones = {}
cfxZones.version = "2.5.5"
cfxZones.version = "2.5.6"
--[[-- VERSION HISTORY
- 2.2.4 - getCoalitionFromZoneProperty
- getStringFromZoneProperty
@ -50,6 +50,10 @@ cfxZones.version = "2.5.5"
- extractPropertyFromDCS trims key and property
- 2.5.5 - pollFlag() centralized for banging
- allStaticsInZone
- 2.5.6 - flag accessor setFlagValue(), getFlagValue
- pollFlag supports theZone as final parameter
- randomDelayFromPositiveRange
- isMEFlag
--]]--
cfxZones.verbose = false
@ -197,6 +201,7 @@ function cfxZones.readFromDCS(clearfirst)
-- add to my table
cfxZones.zones[upperName] = newZone -- WARNING: UPPER ZONE!!!
--trigger.action.outText("znd: procced " .. newZone.name .. " with radius " .. newZone.radius, 30)
else
if cfxZones.verbose then
trigger.action.outText("cf/x zones: malformed zone #" .. i .. " dropped", 10)
@ -467,11 +472,13 @@ end;
function cfxZones.isPointInsideZone(thePoint, theZone)
local p = {x=thePoint.x, y = 0, z = thePoint.z} -- zones have no altitude
if (theZone.isCircle) then
local zp = cfxZones.getPoint(theZone)
local d = dcsCommon.dist(p, theZone.point)
return d < theZone.radius
end
if (theZone.isPoly) then
--trigger.action.outText("zne: isPointInside: " .. theZone.name .. " is Polyzone!", 30)
return (cfxZones.isPointInsidePoly(p, theZone.poly))
end
@ -550,6 +557,7 @@ end
--
function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be code
-- warning: does not check for exiting!
--trigger.action.outText("Zone " .. theZone.name .. " radius " .. theZone.radius, 30)
local inZones = {}
local coals = {0, 1, 2} -- all coalitions
for idx, coa in pairs(coals) do
@ -601,9 +609,12 @@ function cfxZones.isGroupPartiallyInZone(aGroup, aZone)
for uk, aUnit in pairs (allUnits) do
if aUnit:isExist() and aUnit:getLife() > 1 then
local p = aUnit:getPoint()
if cfxZones.isPointInsideZone(p, aZone) then
local inzone, percent, dist = cfxZones.pointInZone(p, aZone)
if inzone then -- cfxZones.isPointInsideZone(p, aZone) then
--trigger.action.outText("zne: YAY <" .. aUnit:getName() .. "> IS IN " .. aZone.name, 30)
return true
end
--trigger.action.outText("zne: <" .. aUnit:getName() .. "> not in " .. aZone.name .. ", dist = " .. dist .. ", rad = ", aZone.radius, 30)
end
end
return false
@ -1048,7 +1059,7 @@ end
--
-- Flag Pulling
--
function cfxZones.pollFlag(theFlag, method)
function cfxZones.pollFlag(theFlag, method, theZone)
if cfxZones.verbose then
trigger.action.outText("+++zones: polling flag " .. theFlag .. " with " .. method, 30)
end
@ -1085,6 +1096,68 @@ function cfxZones.pollFlag(theFlag, method)
end
end
function cfxZones.setFlagValue(theFlag, theValue, theZone)
local zoneName = "<dummy>"
if not theZone then
trigger.action.outText("+++Zne: no zone on setFlagValue")
else
zoneName = theZone.name -- for flag wildcards
end
if type(theFlag) == "number" then
-- straight set, ME flag
trigger.action.setUserFlag(theFlag, theValue)
return
end
-- we assume it's a string now
theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces
local nFlag = tonumber(theFlag)
if nFlag then
trigger.action.setUserFlag(theFlag, theValue)
return
end
-- now do wildcard processing. we have alphanumeric
if dcsCommon.stringStartsWith(theFlag, "*") then
theFlag = zoneName .. theFlag
end
trigger.action.setUserFlag(theFlag, theValue)
end
function cfxZones.getFlagValue(theFlag, theZone)
local zoneName = "<dummy>"
if not theZone then
trigger.action.outText("+++Zne: no zone on getFlagValue")
else
zoneName = theZone.name -- for flag wildcards
end
if type(theFlag) == "number" then
-- straight get, ME flag
return trigger.misc.getUserFlag(theFlag)
end
-- we assume it's a string now
theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces
local nFlag = tonumber(theFlag)
if nFlag then
return trigger.misc.getUserFlag(theFlag)
end
-- now do wildcard processing. we have alphanumeric
if dcsCommon.stringStartsWith(theFlag, "*") then
theFlag = zoneName .. theFlag
end
return trigger.misc.getUserFlag(theFlag)
end
function cfxZones.isMEFlag(inFlag)
return true
-- returns true if inFlag is a pure positive number
-- inFlag = dcsCommon.trim(inFlag)
-- return dcsCommon.stringIsPositiveNumber(inFlag)
end
--
-- PROPERTY PROCESSING
@ -1167,6 +1240,19 @@ function cfxZones.getMinMaxFromZoneProperty(theZone, theProperty)
end
function cfxZones.randomDelayFromPositiveRange(minVal, maxVal)
if not maxVal then return minVal end
if not minVal then return maxVal end
local delay = maxVal
if minVal > 0 and minVal < delay then
-- we want a randomized from time from minTime .. delay
local varPart = delay - minVal + 1
varPart = dcsCommon.smallRandom(varPart) - 1
delay = minVal + varPart
end
return delay
end
function cfxZones.getPositiveRangeFromZoneProperty(theZone, theProperty, default)
-- reads property as string, and interprets as range 'a-b'.
-- if not a range but single number, returns both for upper and lower

View File

@ -1,5 +1,5 @@
cloneZones = {}
cloneZones.version = "1.1.1"
cloneZones.version = "1.2.0"
cloneZones.verbose = false
cloneZones.requiredLibs = {
"dcsCommon", -- always
@ -7,7 +7,10 @@ cloneZones.requiredLibs = {
"cfxMX",
}
cloneZones.cloners = {}
cloneZones.callbacks = {}
cloneZones.unitXlate = {}
cloneZones.groupXlate = {} -- used to translate original groupID to cloned. only holds last spawned group id
cloneZones.uniqueCounter = 9200000 -- we start group numbering here
--[[--
Clones Groups from ME mission data
Copyright (c) 2022 by Christian Franz and cf/x AG
@ -18,6 +21,16 @@ cloneZones.cloners = {}
1.1.0 - support for static objects
- despawn? attribute
1.1.1 - despawnAll: isExist guard
- map in? to f?
1.2.0 - Lua API integration: callbacks
- groupXlate struct
- unitXlate struct
- resolveReferences
- getGroupsInZone rewritten for data
- static resolve
- linkUnit resolve
- clone? synonym
- empty! and method attributes
--]]--
@ -38,18 +51,82 @@ function cloneZones.getCloneZoneByName(aName)
return nil
end
--
-- callbacks
--
function cloneZones.addCallback(theCallback)
if not theCallback then return end
table.insert(cloneZones.callbacks, theCallback)
end
-- reasons for callback
-- "will despawn group" - args is the group about to be despawned
-- "did spawn group" -- args is group that was spawned
-- "will despawn static"
-- "did spawn static"
-- "spawned" -- completed spawn cycle. args contains .groups and .statics spawned
-- "empty" -- all spawns have been killed, args is empty
-- "wiped" -- preWipe executed
-- "<none" -- something went wrong
function cloneZones.invokeCallbacks(theZone, reason, args)
if not theZone then return end
if not reason then reason = "<none>" end
if not args then args = {} end
-- invoke anyone who wants to know that a group
-- of people was rescued.
for idx, cb in pairs(cloneZones.callbacks) do
cb(theZone, reason, args)
end
end
-- group translation orig id
--
-- reading zones
--
function cloneZones.partOfGroupDataInZone(theZone, theUnits)
local zP = cfxZones.getPoint(theZone)
zP.y = 0
for idx, aUnit in pairs(theUnits) do
local uP = {}
uP.x = aUnit.x
uP.y = 0
uP.z = aUnit.y -- !! y-z
local dist = dcsCommon.dist(uP, zP)
if dist <= theZone.radius then return true end
end
return false
end
function cloneZones.allGroupsInZoneByData(theZone)
local theGroupsInZone = {}
local radius = theZone.radius
for groupName, groupData in pairs(cfxMX.groupDataByName) do
if groupData.units then
if cloneZones.partOfGroupDataInZone(theZone, groupData.units) then
theGroup = Group.getByName(groupName)
table.insert(theGroupsInZone, theGroup)
end
end
end
return theGroupsInZone
end
function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
local localZones = cfxZones.allGroupsInZone(theZone)
if cloneZones.verbose then
trigger.action.outText("+++clnZ: new cloner " .. theZone.name, 30)
end
local localZones = cloneZones.allGroupsInZoneByData(theZone)
local localObjects = cfxZones.allStaticsInZone(theZone)
theZone.cloner = true -- this is a cloner zoner
theZone.mySpawns = {}
theZone.myStatics = {}
--theZone.groupVectors = {}
theZone.origin = cfxZones.getPoint(theZone) -- save reference point for all groupVectors
-- source tells us which template to use. it can be the following:
@ -72,8 +149,15 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
if gName then
table.insert(theZone.cloneNames, gName)
table.insert(theZone.mySpawns, aGroup) -- collect them for initial despawn
-- now get group data and save a lookup for
-- resolving internal references
local rawData, cat, ctry = cfxMX.getGroupFromDCSbyName(gName)
local origID = rawData.groupId
-- cloneZones.templateGroups[gName] = origID
-- cloneZones.templateGroupsReverse[origID] = gName
end
end
for idx, aStatic in pairs (localObjects) do
local sName = aStatic:getName()
if sName then
@ -111,6 +195,12 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.lastSpawnValue = trigger.misc.getUserFlag(theZone.spawnFlag) -- save last value
end
-- synonyms
if cfxZones.hasProperty(theZone, "clone?") then
theZone.spawnFlag = cfxZones.getStringFromZoneProperty(theZone, "clone?", "none")
theZone.lastSpawnValue = trigger.misc.getUserFlag(theZone.spawnFlag) -- save last value
end
-- deSpawn?
if cfxZones.hasProperty(theZone, "deSpawn?") then
theZone.deSpawnFlag = cfxZones.getStringFromZoneProperty(theZone, "deSpawn?", "none")
@ -127,16 +217,19 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
theZone.emptyFlag = cfxZones.getNumberFromZoneProperty(theZone, "empty+1", "<None>") -- note string on number default
end
if cfxZones.hasProperty(theZone, "empty!") then
theZone.emptyBangFlag = cfxZones.getNumberFromZoneProperty(theZone, "empty!", "<None>") -- note string on number default
end
theZone.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
if cfxZones.hasProperty(theZone, "masterOwner") then
theZone.masterOwner = cfxZones.getStringFromZoneProperty(theZone, "masterOwner", "<none>")
end
--cloneZones.spawnWithCloner(theZone)
theZone.turn = cfxZones.getNumberFromZoneProperty(theZone, "turn", 0)
-- make sure we spawn at least once
-- bad idea, since we may want to simply create a template
-- if not theZone.spawnFlag then theZone.onStart = true end
-- we end with clear plate
end
--
@ -148,7 +241,10 @@ function cloneZones.despawnAll(theZone)
trigger.action.outText("wiping <" .. theZone.name .. ">", 30)
end
for idx, aGroup in pairs(theZone.mySpawns) do
--trigger.action.outText("++clnZ: despawn all " .. aGroup.name, 30)
if aGroup:isExist() then
cloneZones.invokeCallbacks(theZone, "will despawn group", aGroup)
Group.destroy(aGroup)
end
end
@ -156,7 +252,10 @@ function cloneZones.despawnAll(theZone)
-- warning! may be mismatch because we are looking at groups
-- not objects. let's see
if aStatic:isExist() then
trigger.action.outText("Destroying static <" .. aStatic:getName() .. ">", 30)
if cloneZones.verbose then
trigger.action.outText("Destroying static <" .. aStatic:getName() .. ">", 30)
end
cloneZones.invokeCallbacks(theZone, "will despawn static", aStatic)
Object.destroy(aStatic) -- we don't aStatio:destroy() to find out what it is
end
end
@ -165,7 +264,7 @@ function cloneZones.despawnAll(theZone)
end
function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWaypoints)
--trigger.action.outText("Update loc - zone delta: [" .. zoneDelta.x .. "," .. zoneDelta.z .. "]", 30)
-- remember that zoneDelta's [z] modifies theData's y!!
theData.x = theData.x + zoneDelta.x
theData.y = theData.y + zoneDelta.z -- !!!
@ -178,8 +277,6 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp
-- first waypoint, but only all others if asked
-- to
local theRoute = theData.route
-- TODO: vehicles can have 'spans' - may need to program for
-- those as well. we currently only go for points
if theRoute then
local thePoints = theRoute.points
if thePoints and #thePoints > 0 then
@ -227,8 +324,13 @@ function cloneZones.updateLocationsInGroupData(theData, zoneDelta, adjustAllWayp
end
end
end
function cloneZones.uniqueID()
local uid = cloneZones.uniqueCounter
cloneZones.uniqueCounter = cloneZones.uniqueCounter + 1
return uid
end
function cloneZones.uniqueNameGroupData(theData)
function cloneZones.uniqueNameGroupData(theData)
theData.name = dcsCommon.uuid(theData.name)
local units = theData.units
for idx, aUnit in pairs(units) do
@ -236,6 +338,21 @@ function cloneZones.uniqueNameGroupData(theData)
end
end
function cloneZones.uniqueIDGroupData(theData)
theData.groupId = cloneZones.uniqueID()
end
function cloneZones.uniqueIDUnitData(theData)
if not theData then return end
if not theData.units then return end
local units = theData.units
for idx, aUnit in pairs(units) do
aUnit.CZorigID = aUnit.unitId
aUnit.unitId = cloneZones.uniqueID()
aUnit.CZTargetID = aUnit.unitId
end
end
function cloneZones.resolveOwnership(spawnZone, ctry)
if not spawnZone.masterOwner then return ctry end
@ -254,9 +371,151 @@ function cloneZones.resolveOwnership(spawnZone, ctry)
return ctry
end
--
-- resolve external group references
--
function cloneZones.resolveGroupID(gID, rawData, dataTable, reason)
local resolvedID = gID
local myOName = rawData.CZorigName
local groupName = cfxMX.groupNamesByID[gID]
--trigger.action.outText("Resolve for <" .. myOName .. "> the external ID: " .. gID .. " --> " .. groupName .. " for <" .. reason.. "> task", 30)
-- first, check if this an internal reference, i.e. inside the same
-- zone template
for idx, otherData in pairs(dataTable) do
-- look in own data table
if otherData.CZorigName == groupName then
-- using cfxMX for clarity only (name access)
resolvedID = otherData.CZTargetID
--trigger.action.outText("resolved (internally) " .. gID .. " to " .. resolvedID, 30)
return resolvedID
end
end
-- now check if we have spawned this before
local lastClone = cloneZones.groupXlate[gID]
if lastClone then
resolvedID = lastClone
--trigger.action.outText("resolved (EXT) " .. gID .. " to " .. resolvedID, 30)
return resolvedID
end
-- if we get here, reference is not to a cloned item
--trigger.action.outText("resolved " .. gID .. " to " .. resolvedID, 30)
return resolvedID
end
function cloneZones.resolveUnitID(uID, rawData, dataTable, reason)
-- also resolves statics as they share ID with units
local resolvedID = uID
--trigger.action.outText("Resolve reference to unitId <" .. uID .. "> for <" .. reason.. "> task", 30)
-- first, check if this an internal reference, i.e. inside the same
-- zone template
for idx, otherData in pairs(dataTable) do
-- iterate all units
for idy, aUnit in pairs(otherData.units) do
if aUnit.CZorigID == uID then
resolvedID = aUnit.CZTargetID
--trigger.action.outText("resolved (internally) " .. uID .. " to " .. resolvedID, 30)
return resolvedID
end
end
end
-- now check if we have spawned this before
local lastClone = cloneZones.unitXlate[uID]
if lastClone then
resolvedID = lastClone
--trigger.action.outText("resolved (U-EXT) " .. uID .. " to " .. resolvedID, 30)
return resolvedID
end
-- if we get here, reference is not to a cloned item
--trigger.action.outText("resolved G-" .. uID .. " to " .. resolvedID, 30)
return resolvedID
end
function cloneZones.resolveStaticLinkUnit(uID)
local resolvedID = uID
local lastClone = cloneZones.unitXlate[uID]
if lastClone then
resolvedID = lastClone
--trigger.action.outText("resolved (U-EXT) " .. uID .. " to " .. resolvedID, 30)
return resolvedID
end
return resolvedID
end
function cloneZones.resolveWPReferences(rawData, theZone, dataTable)
-- check to see if we really need data table, as we have theZone
-- perform a check of route for group or unit references
if not rawData then return end
local myOName = rawData.CZorigName
if rawData.route and rawData.route.points then
local points = rawData.route.points
for idx, aPoint in pairs(points) do
-- check if there is a link unit here and resolve
if aPoint.linkUnit then
local gID = aPoint.linkUnit
local resolvedID = cloneZones.resolveUnitID(gID, rawData, dataTable, "linkUnit")
aPoint.linkUnit = resolvedID
--trigger.action.outText("resolved link unit to "..resolvedID .. " for " .. rawData.name, 30)
end
-- iterate all tasks assigned to point
local task = aPoint.task
if task and task.params and task.params.tasks then
local tasks = task.params.tasks
for idy, taskData in pairs(tasks) do
-- resolve group references in TASKS
if taskData.id and taskData.params and taskData.params.groupId
then
-- we resolve group reference
local gID = taskData.params.groupId
local resolvedID = cloneZones.resolveGroupID(gID, rawData, dataTable, taskData.id)
taskData.params.groupId = resolvedID
end
-- resolve unit references in TASKS
if taskData.id and taskData.params and taskData.params.unitId
then
-- we don't look for keywords, we simply resolve
local uID = taskData.params.unitId
local resolvedID = cloneZones.resolveUnitID(uID, rawData, dataTable, taskData.id)
taskData.params.unitId = resolvedID
end
-- resolve unit references in ACTIONS
if taskData.params and taskData.params.action and
taskData.params.action.params and taskData.params.action.params.unitId then
local uID = taskData.params.action.params.unitId
local resolvedID = cloneZones.resolveUnitID(uID, rawData, dataTable, "Action")
taskData.params.action.params.unitId = resolvedID
end
end
end
end
end
end
function cloneZones.resolveReferences(theZone, dataTable)
-- when an action refers to another group, we check if
-- the group referred to is also a clone, and update
-- the reference to the newest incardnation
for idx, rawData in pairs(dataTable) do
-- resolve references in waypoints
cloneZones.resolveWPReferences(rawData, theZone, dataTable)
end
end
function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
--trigger.action.outText("ENTER: Spawn with template " .. theZone.name .. " for spawnZone " .. spawnZone.name, 30)
-- theZone is the zoner with the template
-- theZone is the cloner with the template
-- spawnZone is the spawner with settings
--if not spawnZone then spawnZone = theZone end
local newCenter = cfxZones.getPoint(spawnZone)
@ -265,17 +524,23 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
local spawnedGroups = {}
local spawnedStatics = {}
local dataToSpawn = {}
for idx, aGroupName in pairs(theZone.cloneNames) do
local rawData, cat, ctry = cfxMX.getGroupFromDCSbyName(aGroupName)
if rawData.name == aGroupName then
else
rawData.CZorigName = rawData.name -- save original group name
local origID = rawData.groupId -- save original group ID
rawData.CZorigID = origID
cloneZones.uniqueIDGroupData(rawData) -- assign unique ID we know
cloneZones.uniqueIDUnitData(rawData) -- assign unique ID for units -- saves old unitId as CZorigID
rawData.CZTargetID = rawData.groupId -- save
if rawData.name ~= aGroupName then
trigger.action.outText("Clone: FAILED name check", 30)
end
-- now use raw data to spawn and see if it works outabox
local theCat = cfxMX.catText2ID(cat)
rawData.CZtheCat = theCat -- save cat
-- update their position if not spawning to exact same location
cloneZones.updateLocationsInGroupData(rawData, zoneDelta, spawnZone.moveRoute)
@ -286,29 +551,76 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
-- make sure unit and group names are unique
cloneZones.uniqueNameGroupData(rawData)
-- see waht country we spawn for
-- see what country we spawn for
ctry = cloneZones.resolveOwnership(spawnZone, ctry)
local theGroup = coalition.addGroup(ctry, theCat, rawData)
rawData.CZctry = ctry -- save ctry
table.insert(dataToSpawn, rawData)
end
-- now resolve references to other cloned units for all raw data
-- we must do this BEFORE we spawn
cloneZones.resolveReferences(theZone, dataToSpawn)
-- now spawn all raw data
for idx, rawData in pairs (dataToSpawn) do
-- now spawn and save to clones
local theGroup = coalition.addGroup(rawData.CZctry, rawData.CZtheCat, rawData)
table.insert(spawnedGroups, theGroup)
--trigger.action.outText("spawned group " .. rawData.name .. "consisting of", 30)
-- update groupXlate table
local newGroupID = theGroup:getID() -- new ID assigned by DCS
local origID = rawData.CZorigID -- before we materialized
cloneZones.groupXlate[origID] = newGroupID
-- now also save all units for references
-- and verify assigned vs target ID
for idx, aUnit in pairs(rawData.units) do
-- access the proposed name
local uName = aUnit.name
local gUnit = Unit.getByName(uName)
if gUnit then
-- unit exists. compare planned and assigned ID
local uID = tonumber(gUnit:getID())
if uID == aUnit.CZTargetID then
--trigger.action.outText("unit " .. uName .. "#"..uID, 30)
-- all good
else
trigger.action.outText("clnZ: post-clone verification failed for unit <" .. uName .. ">: ÎD mismatch: " .. uID .. " -- " .. aUnit.CZTargetID, 30)
end
cloneZones.unitXlate[aUnit.CZorigID] = uID
else
trigger.action.outText("clnZ: post-clone verifiaction failed for unit <" .. uName .. ">: not found", 30)
end
end
-- check if our assigned ID matches the handed out by
-- DCS
if newGroupID == rawData.CZTargetID then
-- we are good
else
trigger.action.outText("clnZ: MISMATCH " .. rawData.name .. " target ID " .. rawData.CZTargetID .. " does not match " .. newGroupID, 30)
end
cloneZones.invokeCallbacks(theZone, "did spawn group", theGroup)
end
-- static spawns
for idx, aStaticName in pairs(theZone.staticNames) do
local rawData, cat, ctry, parent = cfxMX.getStaticFromDCSbyName(aStaticName)
local rawData, cat, ctry, parent = cfxMX.getStaticFromDCSbyName(aStaticName) -- returns a UNIT data block
if not rawData then
trigger.action.outText("Static Clone: no such group <"..aStaticName .. ">", 30)
trigger.action.outText("Static Clone: no such group <"..aStaticName .. ">", 30)
elseif rawData.name == aStaticName then
trigger.action.outText("Static Clone: suxess!!! <".. aStaticName ..">", 30)
-- all good
else
trigger.action.outText("Static Clone: FAILED name check for <" .. aStaticName .. ">", 30)
end
local origID = rawData.unitId -- save original unit ID
rawData.CZorigID = origID
-- now use raw data to spawn and see if it works outabox
local theCat = cfxMX.catText2ID(cat) -- will be "static"
--local theCat = cfxMX.catText2ID(cat) -- will be "static"
-- move origin
rawData.x = rawData.x + zoneDelta.x
@ -317,20 +629,56 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
-- apply turning
dcsCommon.rotateUnitData(rawData, spawnZone.turn, newCenter.x, newCenter.z)
-- make sure static name is unique
-- cloneZones.uniqueNameGroupData(rawData)
-- make sure static name is unique and remember original
rawData.name = dcsCommon.uuid(rawData.name)
rawData.unitID = nil -- simply forget, will be newly issued
rawData.unitId = cloneZones.uniqueID()
rawData.CZTargetID = rawData.unitId
-- see waht country we spawn for
-- see what country we spawn for
ctry = cloneZones.resolveOwnership(spawnZone, ctry)
-- handle linkUnit if provided
if rawData.linkUnit then
--trigger.action.outText("has link to " .. rawData.linkUnit, 30)
local lU = cloneZones.resolveStaticLinkUnit(rawData.linkUnit)
--trigger.action.outText("resolved to " .. lU, 30)
rawData.linkUnit = lU
if not rawData.offsets then
rawData.offsets = {}
rawData.offsets.angle = 0
rawData.offsets.x = 0
rawData.offsets.y = 0
--trigger.action.outText("clnZ: link required offset for " .. rawData.name, 30)
end
rawData.offsets.y = rawData.offsets.y - zoneDelta.z
rawData.offsets.x = rawData.offsets.x - zoneDelta.x
rawData.offsets.angle = rawData.offsets.angle + spawnZone.turn
rawData.linkOffset = true
-- trigger.action.outText("zone deltas are " .. zoneDelta.x .. ", " .. zoneDelta.y, 30)
end
local theStatic = coalition.addStaticObject(ctry, rawData)
local newStaticID = tonumber(theStatic:getID())
table.insert(spawnedStatics, theStatic)
-- we don't mix groups with units, so no lookup tables for
-- statics
if newStaticID == rawData.CZTargetID then
-- trigger.action.outText("Static ID OK: " .. newStaticID .. " for " .. rawData.name, 30)
else
trigger.action.outText("Static ID mismatch: " .. newStaticID .. " vs (target) " .. rawData.CZTargetID .. " for " .. rawData.name, 30)
end
cloneZones.unitXlate[origID] = newStaticID -- same as units
cloneZones.invokeCallbacks(theZone, "did spawn static", theStatic)
--]]--
trigger.action.outText("Static spawn: spawned " .. aStaticName, 30)
if cloneZones.verbose then
trigger.action.outText("Static spawn: spawned " .. aStaticName, 30)
end
end
local args = {}
args.groups = spawnedGroups
args.statics = spawnedStatics
cloneZones.invokeCallbacks(theZone, "spawned", args)
return spawnedGroups, spawnedStatics
end
@ -381,6 +729,7 @@ function cloneZones.spawnWithCloner(theZone)
-- pre-Wipe?
if theZone.preWipe then
cloneZones.despawnAll(theZone)
cloneZones.invokeCallbacks(theZone, "wiped", {})
end
@ -454,6 +803,7 @@ function cloneZones.hasLiveUnits(theZone)
return false
end
-- old code, deprecated
function cloneZones.pollFlag(flagNum, method)
-- we currently ignore method
local num = trigger.misc.getUserFlag(flagNum)
@ -491,17 +841,24 @@ function cloneZones.update()
end
end
-- see if we are empty and should signal
if aZone.emptyFlag and aZone.hasClones then
if cloneZones.countLiveUnits(aZone) < 1 then
-- we are depleted. poll flag once, then remember we have
-- polled
-- 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)
aZone.hasClones = false
end
if aZone.emptyBangFlag then
cfxZones.pollFlag(aZone.emptyBangFlag, aZone.method)
end
-- invoke callbacks
cloneZones.invokeCallbacks(aZone, "empty", {})
-- prevent isEmpty next pass
aZone.hasClones = false
end
end
end
@ -571,8 +928,25 @@ function cloneZones.start()
return true
end
-- let's go!
if not cloneZones.start() then
trigger.action.outText("cf/x Clone Zones aborted: missing libraries", 30)
cloneZones = nil
end
end
--[[-- callback testing
czcb = {}
function czcb.callback(theZone, reason, args)
trigger.action.outText("clone CB: " .. theZone.name .. " with " .. reason, 30)
end
cloneZones.addCallback(czcb.callback)
--]]--
--[[--
to resolve tasks
- AFAC
- FAC Assign group
- set freq for unit
--]]--

View File

@ -15,6 +15,7 @@ countDown.requiredLibs = {
1.1.0 - Lua interface: callbacks
- corrected verbose (erroneously always suppressed)
- triggerFlag --> triggerCountFlag
1.1.1 - corrected bug in invokeCallback
--]]--
@ -56,7 +57,7 @@ function countDown.invokeCallbacks(theZone, val, tminus, zero, belowZero, loopin
-- invoke anyone who wants to know that a group
-- of people was rescued.
for idx, cb in pairs(csarManager.csarCompleteCB) do
for idx, cb in pairs(countDown.callbacks) do
cb(theZone, val, tminus, zero, belowZero, looping)
end
end
@ -94,7 +95,7 @@ function countDown.createCountDownWithZone(theZone)
end
if theZone.triggerCountFlag then
theZone.lastTriggerValue = trigger.misc.getUserFlag(theZone.triggerCountFlag) -- save last value
theZone.lastCountTriggerValue = trigger.misc.getUserFlag(theZone.triggerCountFlag) -- save last value
end
-- zero! bang
@ -184,13 +185,13 @@ function countDown.update()
-- make sure to re-start before reading time limit
if aZone.triggerCountFlag then
local currTriggerVal = trigger.misc.getUserFlag(aZone.triggerCountFlag)
if currTriggerVal ~= aZone.lastTriggerValue
if currTriggerVal ~= aZone.lastCountTriggerValue
then
if countDown.verbose then
trigger.action.outText("+++cntD: triggered on in?", 30)
end
countDown.isTriggered(aZone)
aZone.lastTriggerValue = trigger.misc.getUserFlag(aZone.triggerCountFlag) -- save last value
aZone.lastCountTriggerValue = trigger.misc.getUserFlag(aZone.triggerCountFlag) -- save last value
end
end
end

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "2.5.4"
dcsCommon.version = "2.5.5"
--[[-- VERSION HISTORY
2.2.6 - compassPositionOfARelativeToB
- clockPositionOfARelativeToB
@ -62,6 +62,9 @@ dcsCommon.version = "2.5.4"
- removed forced error in failed pickRandom
2.5.4 - rotateUnitData()
- randomBetween()
2.5.5 - stringStartsWithDigit()
- stringStartsWithLetter()
- stringIsPositiveNumber()
--]]--
@ -1700,7 +1703,31 @@ dcsCommon.version = "2.5.4"
return trimmedArray
end
-- same as cfxZones, this is the commonly used, may need to remove from zones
function dcsCommon.stringIsPositiveNumber(theString)
-- only full integer positive numbers supported
if not theString then return false end
-- if theString == "" then return false end
for i = 1, #theString do
local c = theString:sub(i,i)
if c < "0" or c > "9" then return false end
end
return true
end
function dcsCommon.stringStartsWithDigit(theString)
if #theString < 1 then return false end
local c = string.sub(theString, 1, 1)
return c >= "0" and c <= "9"
end
function dcsCommon.stringStartsWithLetter(theString)
if #theString < 1 then return false end
local c = string.sub(theString, 1, 1)
if c >= "a" and c <= "z" then return true end
if c >= "A" and c <= "Z" then return true end
return false
end
function dcsCommon.stringStartsWith(theString, thePrefix)
return theString:find(thePrefix) == 1
end
@ -2109,8 +2136,8 @@ end
function dcsCommon.init()
cbID = 0
dcsCommon.uuIdent = 0
if (dcsCommon.verbose) then
trigger.action.outText("dcsCommon v" .. dcsCommon.version .. " loaded successfully", 10)
if (dcsCommon.verbose) or true then
trigger.action.outText("dcsCommon v" .. dcsCommon.version .. " loaded", 10)
end
end

View File

@ -1,5 +1,5 @@
delayFlag = {}
delayFlag.version = "1.0.2"
delayFlag.version = "1.0.4"
delayFlag.verbose = false
delayFlag.requiredLibs = {
"dcsCommon", -- always
@ -20,7 +20,9 @@ delayFlag.flags = {}
- using cfxZones for polling
- removed pollFlag
1.0.3 - bug fix for config zone name
- removed message attribute, moved to own module
- removed message attribute, moved to own module
- triggerFlag --> triggerDelayFlag
1.0.4 - startDelay
--]]--
@ -55,15 +57,19 @@ function delayFlag.createTimerWithZone(theZone)
-- trigger flag
if cfxZones.hasProperty(theZone, "f?") then
theZone.triggerFlag = cfxZones.getStringFromZoneProperty(theZone, "f?", "none")
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "f?", "none")
end
if cfxZones.hasProperty(theZone, "in?") then
theZone.triggerFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none")
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none")
end
if theZone.triggerFlag then
theZone.lastTriggerValue = trigger.misc.getUserFlag(theZone.triggerFlag) -- save last value
if cfxZones.hasProperty(theZone, "startDelay?") then
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "startDelay?", "none")
end
if theZone.triggerDelayFlag then
theZone.lastDelayTriggerValue = trigger.misc.getUserFlag(theZone.triggerDelayFlag) -- save last value
end
@ -79,10 +85,6 @@ function delayFlag.createTimerWithZone(theZone)
theZone.onStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", false)
end
-- message
if cfxZones.hasProperty(theZone, "message") then
theZone.myMessage = cfxZones.getStringZoneProperty(theZone, "message", "<none>")
end
-- init
theZone.running = false
@ -128,9 +130,9 @@ function delayFlag.update()
for idx, aZone in pairs(delayFlag.flags) do
-- make sure to re-start before reading time limit
if aZone.triggerFlag then
local currTriggerVal = trigger.misc.getUserFlag(aZone.triggerFlag)
if currTriggerVal ~= aZone.lastTriggerValue
if aZone.triggerDelayFlag then
local currTriggerVal = trigger.misc.getUserFlag(aZone.triggerDelayFlag)
if currTriggerVal ~= aZone.lastDelayTriggerValue
then
if delayFlag.verbose then
if aZone.running then
@ -140,7 +142,7 @@ function delayFlag.update()
end
end
delayFlag.startDelay(aZone) -- we restart even if running
aZone.lastTriggerValue = currTriggerVal
aZone.lastDelayTriggerValue = currTriggerVal
end
end
@ -152,9 +154,9 @@ function delayFlag.update()
-- poll flag
cfxZones.pollFlag(aZone.outFlag, aZone.method)
-- say message
if aZone.myMessage then
trigger.action.outText(aZone.myMessage, 30)
end
--if aZone.myMessage then
-- trigger.action.outText(aZone.myMessage, 30)
--end
end
end

View File

@ -1,5 +1,5 @@
messenger = {}
messenger.version = "1.0.0"
messenger.version = "1.0.1"
messenger.verbose = false
messenger.requiredLibs = {
"dcsCommon", -- always
@ -9,6 +9,8 @@ messenger.messengers = {}
--[[--
Version History
1.0.0 - initial version
1.0.1 - messageOut? synonym
- spelling types in about
--]]--
@ -53,6 +55,10 @@ function messenger.createMessengerDownWithZone(theZone)
theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none")
end
if cfxZones.hasProperty(theZone, "messageOut?") then
theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "messageOut?", "none")
end
if theZone.triggerMessagerFlag then
theZone.lastMessageTriggerValue = trigger.misc.getUserFlag(theZone.triggerMessagerFlag) -- save last value
end
@ -125,10 +131,10 @@ end
function messenger.start()
-- lib check
if not dcsCommon.libCheck then
trigger.action.outText("cfx Count Down requires dcsCommon", 30)
trigger.action.outText("cfx Messenger requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx Count Down", messenger.requiredLibs) then
if not dcsCommon.libCheck("cfx Messenger", messenger.requiredLibs) then
return false
end

View File

@ -1,5 +1,5 @@
pulseFlags = {}
pulseFlags.version = "1.0.2"
pulseFlags.version = "1.0.3"
pulseFlags.verbose = false
pulseFlags.requiredLibs = {
"dcsCommon", -- always
@ -14,6 +14,10 @@ pulseFlags.requiredLibs = {
- 1.0.0 Initial version
- 1.0.1 pause behavior debugged
- 1.0.2 zero pulse optional initial pulse suppress
- 1.0.3 pollFlag switched to cfxZones
uses randomDelayFromPositiveRange
flag! now is string
WARNING: still needs full alphaNum flag upgrade
--]]--
@ -28,7 +32,7 @@ end
--
function pulseFlags.createPulseWithZone(theZone)
theZone.flag = cfxZones.getNumberFromZoneProperty(theZone, "flag!", -1) -- the flag to pulse
theZone.flag = cfxZones.getStringFromZoneProperty(theZone, "flag!", -1) -- the flag to pulse
-- time can be number, or number-number range
theZone.minTime, theZone.time = cfxZones.getPositiveRangeFromZoneProperty(theZone, "time", 1)
@ -68,42 +72,6 @@ end
--
-- update
--
function pulseFlags.pollFlag(theFlag, method)
if pulseFlags.verbose then
trigger.action.outText("+++PulF: polling flag " .. theFlag .. " with " .. method, 30)
end
method = method:lower()
local currVal = trigger.misc.getUserFlag(theFlag)
if method == "inc" or method == "f+1" then
trigger.action.setUserFlag(theFlag, currVal + 1)
elseif method == "dec" or method == "f-1" then
trigger.action.setUserFlag(theFlag, currVal - 1)
elseif method == "off" or method == "f=0" then
trigger.action.setUserFlag(theFlag, 0)
elseif method == "flip" or method == "xor" then
if currVal ~= 0 then
trigger.action.setUserFlag(theFlag, 0)
else
trigger.action.setUserFlag(theFlag, 1)
end
else
if method ~= "on" and method ~= "f=1" then
trigger.action.outText("+++PulF: unknown method <" .. method .. "> - using 'on'", 30)
end
-- default: on.
trigger.action.setUserFlag(theFlag, 1)
end
local newVal = trigger.misc.getUserFlag(theFlag)
if pulseFlags.verbose then
trigger.action.outText("+++PulF: flag <" .. theFlag .. "> changed from " .. currVal .. " to " .. newVal, 30)
end
end
function pulseFlags.doPulse(args)
@ -118,7 +86,7 @@ function pulseFlags.doPulse(args)
-- do a poll on flags
-- first, we only do an initial pulse if zeroPulse is set
if theZone.hasPulsed or theZone.zeroPulse then
pulseFlags.pollFlag(theZone.flag, theZone.method)
cfxZones.pollFlag(theZone.flag, theZone.method, theZone)
-- decrease count
if theZone.pulses > 0 then
@ -149,6 +117,7 @@ function pulseFlags.doPulse(args)
theZone.hasPulsed = true -- we are past initial pulse
-- if we get here, schedule next pulse
--[[--
local delay = theZone.time
if theZone.minTime > 0 and theZone.minTime < delay then
-- we want a randomized from time from minTime .. delay
@ -156,6 +125,8 @@ function pulseFlags.doPulse(args)
varPart = dcsCommon.smallRandom(varPart) - 1
delay = theZone.minTime + varPart
end
--]]--
local delay = cfxZones.randomDelayFromPositiveRange(theZone.minTime, theZone.time)
--trigger.action.outText("***PulF: pulse <" .. theZone.name .. "> scheduled in ".. delay .."!", 30)

137
modules/raiseFlag.lua Normal file
View File

@ -0,0 +1,137 @@
raiseFlag = {}
raiseFlag.version = "1.0.0"
raiseFlag.verbose = false
raiseFlag.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
}
raiseFlag.flags = {}
--[[--
Raise A Flag module -- (c) 2022 by Christian Franz and cf/x AG
Version History
1.0.0 - initial release
--]]--
function raiseFlag.addRaiseFlag(theZone)
table.insert(raiseFlag.flags, theZone)
end
function raiseFlag.getRaiseFlagByName(aName)
for idx, aZone in pairs(raiseFlag.flags) do
if aName == aZone.name then return aZone end
end
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: no raiseFlag with name <" .. aName ..">", 30)
end
return nil
end
--
-- read attributes
--
function raiseFlag.createRaiseFlagWithZone(theZone)
-- get flag from faiseFlag itself
theZone.raiseFlag = cfxZones.getStringFromZoneProperty(theZone, "raiseFlag", "<none>") -- the flag to raise
theZone.flagValue = cfxZones.getNumberFromZoneProperty(theZone, "value", 1) -- value to set to
theZone.minAfterTime, theZone.maxAfterTime = cfxZones.getPositiveRangeFromZoneProperty(theZone, "afterTime", -1)
if cfxZones.hasProperty(theZone, "stopFlag?") then
theZone.triggerStopFlag = cfxZones.getStringFromZoneProperty(theZone, "stopFlag?", "none")
theZone.lastTriggerStopValue = cfxZones.getFlagValue(theZone.triggerStopFlag, theZone) -- save last value
end
theZone.scheduleID = nil
theZone.raiseStopped = false
-- now simply schedule for invocation
local args = {}
args.theZone = theZone
if theZone.minAfterTime < 1 then
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + 0.5)
else
local delay = cfxZones.randomDelayFromPositiveRange(theZone.minAfterTime, theZone.maxAfterTime)
timer.scheduleFunction(raiseFlag.triggered, args, timer.getTime() + delay)
end
end
function raiseFlag.triggered(args)
local theZone = args.theZone
if theZone.raiseStopped then return end
-- if we get here, we aren't stopped and do the flag pull
cfxZones.setFlagValue(theZone.raiseFlag, theZone.flagValue, theZone)
end
--
-- update
--
function raiseFlag.update()
-- call me in a second to poll triggers
timer.scheduleFunction(raiseFlag.update, {}, timer.getTime() + 1)
for idx, aZone in pairs(raiseFlag.flags) do
-- make sure to re-start before reading time limit
if aZone.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
--
-- config & go!
--
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
end
raiseFlag.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
if raiseFlag.verbose then
trigger.action.outText("+++rFlg: read config", 30)
end
end
function raiseFlag.start()
-- lib check
if not dcsCommon.libCheck then
trigger.action.outText("cfx raise flag requires dcsCommon", 30)
return false
end
if not dcsCommon.libCheck("cfx Count Down", raiseFlag.requiredLibs) then
return false
end
-- read config
raiseFlag.readConfigZone()
-- process cloner Zones
local attrZones = cfxZones.getZonesWithAttributeNamed("raiseFlag")
for k, aZone in pairs(attrZones) do
raiseFlag.createRaiseFlagWithZone(aZone) -- process attributes
raiseFlag.addRaiseFlag(aZone) -- add to list
end
-- start update
raiseFlag.update()
trigger.action.outText("cfx raiseFlag v" .. raiseFlag.version .. " started.", 30)
return true
end
-- let's go!
if not raiseFlag.start() then
trigger.action.outText("cfx Raise Flag aborted: missing libraries", 30)
raiseFlag = nil
end

Binary file not shown.

Binary file not shown.