mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.4.4
Removed show-stopping bug in removeMark(), now stops smoke, better drones.
This commit is contained in:
parent
dff5faa06e
commit
2f80033077
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
FARPZones = {}
|
||||
FARPZones.version = "2.3.0"
|
||||
FARPZones.version = "2.4.0"
|
||||
FARPZones.verbose = false
|
||||
--[[--
|
||||
Version History
|
||||
@ -28,6 +28,7 @@ FARPZones.verbose = false
|
||||
2.2.0 - changing a FARP's owner invokes SSBClient if it is loaded
|
||||
2.3.0 - new attributes redCap!, blueCap! captured! and farpMethod
|
||||
- send out signals
|
||||
2.4.0 - work-around for crashing DCS bug in trigger.action.removeMark
|
||||
|
||||
--]]--
|
||||
|
||||
@ -564,6 +565,7 @@ function FARPZones.loadMission()
|
||||
|
||||
local farps = theData.farps
|
||||
if farps then
|
||||
local delay = timer.getTime() + 0.2
|
||||
for fName, fData in pairs(farps) do
|
||||
local theFARP = FARPZones.getFARPZoneByName(fName)
|
||||
if theFARP then
|
||||
@ -574,8 +576,10 @@ function FARPZones.loadMission()
|
||||
theFARP.defenderData = dcsCommon.clone(fData.defenderData)
|
||||
|
||||
FARPZones.produceVehicles(theFARP) -- do full defender and resource cycle
|
||||
FARPZones.drawFARPCircleInMap(theFARP) -- mark in map
|
||||
|
||||
-- stagger drawing the map in time to
|
||||
-- prevent removeMark crashing the thread
|
||||
timer.scheduleFunction(FARPZones.drawFARPCircleInMap, theFARP, delay) -- mark in map
|
||||
delay = delay + 0.2
|
||||
else
|
||||
trigger.action.outText("frpZ: persistence: FARP <" .. fName .. "> no longer exists in mission, skipping", 30)
|
||||
end
|
||||
|
||||
@ -9,7 +9,7 @@ rndFlags.requiredLibs = {
|
||||
Random Flags: DML module to select flags at random
|
||||
and then change them
|
||||
|
||||
Copyright 2022 by Christian Franz and cf/x
|
||||
Copyright 2022-2025 by Christian Franz and cf/x
|
||||
|
||||
Version History
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.5.2"
|
||||
cfxZones.version = "4.5.3"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -9,39 +9,15 @@ cfxZones.version = "4.5.2"
|
||||
--
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
||||
- 4.1.1 - evalRemainder() updates
|
||||
- 4.1.2 - hash property missing warning
|
||||
- 4.2.0 - new createRandomPointInPopulatedZone()
|
||||
- 4.3.0 - boolean supports maybe, random, rnd, ?
|
||||
- small optimization for randomInRange()
|
||||
- randomDelayFromPositiveRange also allows 0
|
||||
- 4.3.1 - new drawText() for zones
|
||||
- dmlZone:getClosestZone() bridge
|
||||
- 4.3.2 - new getListFromZoneProperty()
|
||||
- 4.3.3 - hardened calculateZoneBounds
|
||||
- 4.3.4 - rewrote zone bounds for poly zones
|
||||
- 4.3.5 - hardened getStringFromZoneProperty against number value returns (WebEd bug)
|
||||
- 4.3.6 - tiny optimization in isPointInsideQuad
|
||||
- moving zone - hardening code for static objects
|
||||
- moving zones - now deriving dx, dy,uHeading from dcsCommon xref for linked zones
|
||||
- 4.3.7 - corrected bug in processDynamicValues for lookup table
|
||||
- 4.4.0 - dmlZone:getCoalition()
|
||||
- dmlZone:getTypeName()
|
||||
- dmlZone supports masterOwner by default
|
||||
- dmlZone:getCoalition() dereferences masterOwner once
|
||||
-4.4.1 - better verbosity for error in doPollFlag()
|
||||
-4.4.2 - twn support for wildcards <twn: > and <loc:>
|
||||
-4.4.3 - property name is trimmed (double check)
|
||||
-4.4.4 - createGroundUnitsInZoneForCoalition supports drivable
|
||||
-4.4.5 - corrected startMovingZones() for linked zones via ME's LINKZONE drop-down
|
||||
-4.4.6 - corrected pattern bug in processDynamicAB()
|
||||
-4.5.0 - corrected bug in getBoolFromZoneProperty for default = false and "rnd"
|
||||
- rnd in bool can have = xxx param for percentage
|
||||
- getSmokeColorNumberFromZoneProperty()
|
||||
-4.5.1 - moved processSimpleZoneDynamics to common
|
||||
-4.5.2 - NEW getAllZoneProperties()
|
||||
- guard agains DCS radius stored as sting (WTF, ED?)
|
||||
-4.5.3 - added name for all smoke meths
|
||||
- getSmokeColorStringFromZoneProperty() supports "?"
|
||||
- getSmokeColorNumberFromZoneProperty() supports "?"
|
||||
--]]--
|
||||
|
||||
--
|
||||
@ -71,7 +47,6 @@ end
|
||||
-- dmlZone:getTypeName() -- returns "dmlZone"
|
||||
-- dmlZone:getCoalition -- returns owner
|
||||
|
||||
|
||||
--
|
||||
-- CLASSIC INTERFACE
|
||||
--
|
||||
@ -92,16 +67,14 @@ function cfxZones.readFromDCS(clearfirst)
|
||||
trigger.action.outText("cf/x zones: no env.triggers defined", 10)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
if not env.mission.triggers.zones then
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("cf/x zones: no zones defined", 10)
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
-- we only retrieve the data we need. At this point it is name, location and radius
|
||||
-- we only retrieve the data that we need. At this point it is name, location and radius
|
||||
-- and put this in our own little structure. we also convert to all upper case name for index
|
||||
-- and assume that the name may also carry meaning, e.g. 'LZ:' defines a landing zone
|
||||
-- so we can quickly create other sets from this
|
||||
@ -133,16 +106,8 @@ function cfxZones.readFromDCS(clearfirst)
|
||||
else
|
||||
newZone.properties = {}
|
||||
end -- WARNING: REF COPY. May need to clone
|
||||
--[[--
|
||||
trigger.action.outText("zone <> properties:trimmed", 30)
|
||||
local msg = "["
|
||||
for idx, val in pairs(newZone.properties) do
|
||||
msg = msg .. "<" .. val.key .. ">:<" .. dcsCommon.trim(val.key) .. ">, "
|
||||
end
|
||||
trigger.action.outText(msg, 30)
|
||||
--]]--
|
||||
local upperName = newZone.name:upper()
|
||||
|
||||
local upperName = newZone.name:upper()
|
||||
-- location as 'point'
|
||||
-- WARNING: zones locs are 2D (x,y) pairs, while y in DCS is altitude.
|
||||
-- so we need to change (x,y) into (x, 0, z). Since Zones have no
|
||||
@ -160,20 +125,17 @@ function cfxZones.readFromDCS(clearfirst)
|
||||
newZone.point = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)
|
||||
newZone.dcsOrigin = dcsCommon.createPoint(dcsZone.x, 0, dcsZone.y)
|
||||
end
|
||||
|
||||
-- start type processing. if zone.type exists, we have a mission
|
||||
-- created with 2.7 or above, else earlier
|
||||
local zoneType = 0
|
||||
if (dcsZone.type) then
|
||||
zoneType = dcsZone.type
|
||||
end
|
||||
|
||||
if zoneType == 0 then
|
||||
-- circular zone
|
||||
newZone.isCircle = true
|
||||
newZone.radius = tonumber(dcsZone.radius)
|
||||
newZone.maxRadius = newZone.radius -- same for circular
|
||||
|
||||
elseif zoneType == 2 then
|
||||
-- polyZone
|
||||
newZone.isPoly = true
|
||||
@ -190,7 +152,6 @@ function cfxZones.readFromDCS(clearfirst)
|
||||
-- in later versions, this was corrected
|
||||
verts = dcsZone.vertices -- see if this is ever called
|
||||
end
|
||||
|
||||
for v=1, #verts do
|
||||
local dcsPoint = verts[v]
|
||||
local polyPoint = cfxZones.createPointFromDCSPoint(dcsPoint) -- (x, y) --> (x, 0, y-->z)
|
||||
@ -201,32 +162,22 @@ function cfxZones.readFromDCS(clearfirst)
|
||||
if dist > newZone.maxRadius then newZone.maxRadius = dist end
|
||||
end
|
||||
else
|
||||
|
||||
trigger.action.outText("cf/x zones: malformed zone #" .. i .. " unknown type " .. zoneType, 10)
|
||||
end
|
||||
|
||||
|
||||
-- calculate bounds
|
||||
cfxZones.calculateZoneBounds(newZone)
|
||||
|
||||
-- add to my table
|
||||
cfxZones.zones[upperName] = newZone -- WARNING: UPPER ZONE!!!
|
||||
|
||||
else
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("cf/x zones: malformed zone #" .. i .. " dropped", 10)
|
||||
end
|
||||
if cfxZones.verbose then trigger.action.outText("cf/x zones: malformed zone #" .. i .. " dropped", 10) end
|
||||
end -- else var not a table
|
||||
|
||||
end -- for all zones kvp
|
||||
end -- readFromDCS
|
||||
|
||||
function cfxZones.calculateZoneBounds(theZone)
|
||||
if not (theZone) then return
|
||||
end
|
||||
|
||||
local bounds = theZone.bounds -- copy ref! -- DON'T BELIEVE THIS!
|
||||
|
||||
if theZone.isCircle then
|
||||
-- aabb are easy: center +/- radius
|
||||
local center = theZone.point
|
||||
@ -237,7 +188,6 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
bounds.ur = dcsCommon.createPoint(center.x + radius, 0, center.z - radius)
|
||||
bounds.ll = dcsCommon.createPoint(center.x - radius, 0, center.z + radius)
|
||||
bounds.lr = dcsCommon.createPoint(center.x + radius, 0, center.z + radius)
|
||||
|
||||
-- write back
|
||||
theZone.bounds = bounds
|
||||
elseif theZone.isPoly then
|
||||
@ -245,7 +195,6 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
-- create the four points
|
||||
local p = cfxZones.createPointFromPoint(poly[1])
|
||||
local pRad = dcsCommon.dist(theZone.point, poly[1]) -- rRad is radius for polygon from theZone.point
|
||||
|
||||
-- now iterate through all points and adjust bounds accordingly
|
||||
local lx, ly, mx, my = p.x, p.z, p.x, p.z
|
||||
for vtx=1, #poly do
|
||||
@ -257,7 +206,6 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
local dp = dcsCommon.dist(theZone.point, v)
|
||||
if dp > pRad then pRad = dp end -- find largst distance to vertex
|
||||
end
|
||||
|
||||
theZone.bounds.ul = dcsCommon.createPoint(lx, 0, my)
|
||||
theZone.bounds.ur = dcsCommon.createPoint(mx, 0, my)
|
||||
theZone.bounds.ll = dcsCommon.createPoint(lx, 0, ly)
|
||||
@ -270,7 +218,6 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
trigger.action.outText("cf/x zones: calc bounds: zone " .. theZone.name .. " has unknown type", 30)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function dmlZone:calculateZoneBounds()
|
||||
@ -308,7 +255,6 @@ function cfxZones.createPointFromDCSPoint(inPoint)
|
||||
return dcsCommon.createPoint(inPoint.x, 0, inPoint.y)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.createRandomPointInsideBounds(bounds)
|
||||
-- warning: bounds do not move woth zone! may have to be updated
|
||||
local x = math.random(bounds.ll.x, ur.x)
|
||||
@ -362,8 +308,7 @@ function cfxZones.createRandomPointInCircleZone(theZone, onEdge)
|
||||
if not theZone.isCircle then
|
||||
trigger.action.outText("+++Zones: warning - createRandomPointInCircleZone called for non-circle zone <" .. theZone.name .. ">", 30)
|
||||
return {x=theZone.point.x, y=0, z=theZone.point.z}
|
||||
end
|
||||
|
||||
end
|
||||
-- ok, let's first create a random percentage value for the new radius
|
||||
-- now lets get a random degree
|
||||
local degrees = math.random() * 2 * 3.14152 -- radiants.
|
||||
@ -391,11 +336,9 @@ function cfxZones.createRandomPointInPolyZone(theZone, onEdge)
|
||||
end
|
||||
-- force update of all points
|
||||
local p = cfxZones.getPoint(theZone)
|
||||
|
||||
-- point in convex poly: choose two different lines from that polygon
|
||||
local lineIdxA = dcsCommon.smallRandom(#theZone.poly)
|
||||
repeat lineIdxB = dcsCommon.smallRandom(#theZone.poly) until (lineIdxA ~= lineIdxB)
|
||||
|
||||
-- we now have two different lines. pick a random point on each.
|
||||
-- we use lerp to pick any point between a and b
|
||||
local a = theZone.poly[lineIdxA]
|
||||
@ -409,7 +352,6 @@ function cfxZones.createRandomPointInPolyZone(theZone, onEdge)
|
||||
local polyPoint = sourceA
|
||||
return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz
|
||||
end
|
||||
|
||||
-- now get point on second line
|
||||
a = theZone.poly[lineIdxB]
|
||||
lineIdxB = lineIdxB + 1 -- get next point in poly and wrap around
|
||||
@ -417,7 +359,6 @@ function cfxZones.createRandomPointInPolyZone(theZone, onEdge)
|
||||
b = theZone.poly[lineIdxB]
|
||||
randompercent = math.random()
|
||||
local sourceB = dcsCommon.vLerp (a, b, randompercent)
|
||||
|
||||
-- now take a random point on that line that entirely
|
||||
-- runs through the poly
|
||||
randompercent = math.random()
|
||||
@ -443,14 +384,9 @@ function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)
|
||||
local o = collector[1]
|
||||
local op = o:getPoint()
|
||||
d = dcsCommon.distFlat(op, p)
|
||||
-- trigger.action.outText("singleDist = " .. d, 30)
|
||||
if d > radius/2 then
|
||||
-- trigger.action.outText("good enough, will use", 30)
|
||||
return p, dx, dz
|
||||
end
|
||||
if d > radius/2 then return p, dx, dz end
|
||||
end
|
||||
cnt = cnt + 1
|
||||
-- trigger.action.outText(hits .. "hits --> failed try " .. cnt, 30)
|
||||
until cnt > maxTries
|
||||
return p, dx, dz
|
||||
end
|
||||
@ -489,15 +425,9 @@ function cfxZones.objectsInRange(pt, range)
|
||||
local op = anObject:getPoint()
|
||||
local dist = dcsCommon.dist(pt, op)
|
||||
if dist < range then
|
||||
-- local e = {
|
||||
-- dist = dist,
|
||||
-- o = anObject
|
||||
-- }
|
||||
-- table.insert(filtered, e)
|
||||
table.insert(filtered, anObject)
|
||||
end
|
||||
end
|
||||
|
||||
return #filtered, filtered
|
||||
end
|
||||
|
||||
@ -544,18 +474,14 @@ function cfxZones.createCircleZone(name, x, z, radius)
|
||||
newZone.isPoly = false
|
||||
newZone.poly = {}
|
||||
newZone.bounds = {}
|
||||
|
||||
newZone.name = name
|
||||
newZone.radius = radius
|
||||
newZone.point = dcsCommon.createPoint(x, 0, z)
|
||||
newZone.dcsOrigin = dcsCommon.createPoint(x, 0, z)
|
||||
|
||||
-- props
|
||||
newZone.properties = {}
|
||||
|
||||
-- calculate my bounds
|
||||
cfxZones.calculateZoneBounds(newZone)
|
||||
|
||||
return newZone
|
||||
end
|
||||
|
||||
@ -567,9 +493,7 @@ function cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
||||
if not location.x then location.x = 0 end
|
||||
if not location.z then location.z = 0 end
|
||||
if not location.y then location.y = 0 end
|
||||
|
||||
local newZone = cfxZones.createPolyZone(name, points, location)
|
||||
|
||||
if addToManaged then
|
||||
cfxZones.addZoneToManagedZones(newZone)
|
||||
end
|
||||
@ -617,7 +541,6 @@ function cfxZones.createPolyZone(name, poly, location) -- poly must be array of
|
||||
newZone.isPoly = true
|
||||
newZone.poly = {}
|
||||
newZone.bounds = {}
|
||||
|
||||
newZone.name = name
|
||||
newZone.radius = 0
|
||||
-- copy poly
|
||||
@ -625,10 +548,8 @@ function cfxZones.createPolyZone(name, poly, location) -- poly must be array of
|
||||
local theVertex = poly[v]
|
||||
newZone.poly[v] = cfxZones.createPointFromPoint(theVertex)
|
||||
end
|
||||
|
||||
-- properties
|
||||
newZone.properties = {}
|
||||
|
||||
cfxZones.calculateZoneBounds(newZone)
|
||||
return newZone
|
||||
end
|
||||
@ -638,7 +559,6 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns
|
||||
-- if entirelyInside is false, only the zone's center is guaranteed to be inside
|
||||
-- inZone.
|
||||
-- entirelyInside is not guaranteed for polyzones
|
||||
|
||||
if inZone.isCircle then
|
||||
local sourceRadius = inZone.radius
|
||||
if entirelyInside and targetRadius > sourceRadius then targetRadius = sourceRadius end
|
||||
@ -654,13 +574,11 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns
|
||||
-- construct new zone
|
||||
local newZone = cfxZones.createCircleZone(name, x, z, targetRadius)
|
||||
return newZone
|
||||
|
||||
elseif inZone.isPoly then
|
||||
local newPoint = cfxZones.createRandomPointInPolyZone(inZone)
|
||||
-- construct new zone
|
||||
local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius)
|
||||
return newZone
|
||||
|
||||
else
|
||||
-- zone type unknown
|
||||
trigger.action.outText("CreateZoneInZone: unknown zone type for inZone =" .. inZone.name , 10)
|
||||
@ -707,7 +625,6 @@ function cfxZones.isPointInsidePoly(thePoint, poly)
|
||||
end
|
||||
-- final test
|
||||
if cfxZones.isLeftXZ(poly[#poly], poly[1], thePoint) ~= mustMatch then return false end
|
||||
|
||||
return true
|
||||
end;
|
||||
|
||||
@ -720,12 +637,10 @@ function cfxZones.isPointInsideZone(thePoint, theZone, radiusIncrease)
|
||||
local d = dcsCommon.dist(p, theZone.point)
|
||||
return d < theZone.radius + radiusIncrease, d
|
||||
end
|
||||
|
||||
if (theZone.isPoly) then
|
||||
--trigger.action.outText("zne: isPointInside: " .. theZone.name .. " is Polyzone!", 30)
|
||||
return (cfxZones.isPointInsidePoly(p, theZone.poly)), 0 -- always returns delta 0
|
||||
end
|
||||
|
||||
trigger.action.outText("isPointInsideZone: Unknown zone type for " .. outerZone.name, 10)
|
||||
end
|
||||
|
||||
@ -747,14 +662,12 @@ function cfxZones.getZonesContainingPoint(thePoint, testZones) -- return array
|
||||
if not testZones then
|
||||
testZones = cfxZones.zones
|
||||
end
|
||||
|
||||
local containerZones = {}
|
||||
for tName, tData in pairs(testZones) do
|
||||
if cfxZones.isPointInsideZone(thePoint, tData) then
|
||||
table.insert(containerZones, tData)
|
||||
end
|
||||
end
|
||||
|
||||
return containerZones
|
||||
end
|
||||
|
||||
@ -762,13 +675,11 @@ function cfxZones.getFirstZoneContainingPoint(thePoint, testZones)
|
||||
if not testZones then
|
||||
testZones = cfxZones.zones
|
||||
end
|
||||
|
||||
for tName, tData in pairs(testZones) do
|
||||
if cfxZones.isPointInsideZone(thePoint, tData) then
|
||||
return tData
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -776,7 +687,6 @@ function cfxZones.getAllZonesInsideZone(superZone, testZones) -- returnes array!
|
||||
if not testZones then
|
||||
testZones = cfxZones.zones
|
||||
end
|
||||
|
||||
local containedZones = {}
|
||||
for zName, zData in pairs(testZones) do
|
||||
if cfxZones.isZoneInsideZone(zData, superZone) then
|
||||
@ -794,10 +704,8 @@ function dmlZone:getAllZonesInsideZone(testZones)
|
||||
return cfxZones.getAllZonesInsideZone(self, testZones)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.getZonesWithAttributeNamed(attributeName, testZones)
|
||||
if not testZones then testZones = cfxZones.zones end
|
||||
|
||||
local attributZones = {}
|
||||
for aName,aZone in pairs(testZones) do
|
||||
local attr = cfxZones.getZoneProperty(aZone, attributeName)
|
||||
@ -808,14 +716,11 @@ function cfxZones.getZonesWithAttributeNamed(attributeName, testZones)
|
||||
end
|
||||
return attributZones
|
||||
end
|
||||
|
||||
--
|
||||
-- zone volume management
|
||||
--
|
||||
|
||||
function cfxZones.getZoneVolume(theZone)
|
||||
if not theZone then return nil end
|
||||
|
||||
if (theZone.isCircle) then
|
||||
-- create a sphere volume
|
||||
local p = cfxZones.getPoint(theZone)
|
||||
@ -844,7 +749,6 @@ function cfxZones.getZoneVolume(theZone)
|
||||
upperRight.x = theZone.bounds.ur.x
|
||||
upperRight.z = theZone.bounds.ur.z
|
||||
upperRight.y = alt -- we go higher
|
||||
|
||||
-- construct volume
|
||||
local vol = {
|
||||
id = world.VolumeType.BOX,
|
||||
@ -863,7 +767,6 @@ function dmlZone:getZoneVolume()
|
||||
return cfxZones.getZoneVolume(self)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.declutterZone(theZone)
|
||||
if not theZone then return end
|
||||
local theVol = cfxZones.getZoneVolume(theZone)
|
||||
@ -874,7 +777,6 @@ function dmlZone:declutterZone()
|
||||
local theVol = cfxZones.getZoneVolume(self)
|
||||
world.removeJunk(theVol)
|
||||
end
|
||||
|
||||
--
|
||||
-- units / groups in zone
|
||||
--
|
||||
@ -941,7 +843,6 @@ function dmlZone:allStaticsInZone(useOrigin)
|
||||
return cfxZones.allStaticsInZone(self, useOrigin)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.groupsOfCoalitionPartiallyInZone(coal, theZone, categ) -- categ is optional
|
||||
local groupsInZone = {}
|
||||
local allGroups = coalition.getGroups(coal, categ)
|
||||
@ -992,11 +893,9 @@ end
|
||||
function dmlZone:isEntireGroupInZone(aGroup)
|
||||
return cfxZones.isEntireGroupInZone(aGroup, self)
|
||||
end
|
||||
|
||||
--
|
||||
-- Zone Manipulation
|
||||
--
|
||||
|
||||
function cfxZones.offsetZone(theZone, dx, dz)
|
||||
-- first, update center
|
||||
theZone.point.x = theZone.point.x + dx
|
||||
@ -1025,7 +924,6 @@ function dmlZone:offsetZone(dx, dz)
|
||||
cfxZones.offsetZone(self, dx, dz)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.moveZoneTo(theZone, x, z)
|
||||
local dx = x - theZone.point.x
|
||||
local dz = z - theZone.point.z
|
||||
@ -1046,7 +944,6 @@ function dmlZone:centerZoneOnUnit(theUnit)
|
||||
self:moveZoneTo(thePoint.x, thePoint.z)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.dumpZones(zoneTable)
|
||||
if not zoneTable then zoneTable = cfxZones.zones end
|
||||
|
||||
@ -1064,15 +961,12 @@ end
|
||||
function cfxZones.keysForTable(theTable)
|
||||
local keyset={}
|
||||
local n=0
|
||||
|
||||
for k,v in pairs(tab) do
|
||||
n=n+1
|
||||
keyset[n]=k
|
||||
end
|
||||
return keyset
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- return all zones that have a specific named property
|
||||
--
|
||||
@ -1091,7 +985,6 @@ function cfxZones.zonesWithProperty(propertyName, searchSet)
|
||||
end
|
||||
return theZones
|
||||
end
|
||||
|
||||
--
|
||||
-- return all zones from the zone table that begin with string prefix
|
||||
--
|
||||
@ -1107,7 +1000,6 @@ function cfxZones.zonesStartingWithName(prefix, searchSet)
|
||||
|
||||
return prefixZones
|
||||
end
|
||||
|
||||
--
|
||||
-- return all zones from the zone table that begin with the string or set of strings passed in prefix
|
||||
-- if you pass 'true' as second (optional) parameter, it will first look for all zones that begin
|
||||
@ -1116,7 +1008,6 @@ end
|
||||
function cfxZones.zonesStartingWith(prefix, searchSet, debugging)
|
||||
-- you can force zones by having their name start with "+"
|
||||
-- which will force them to return immediately if debugging is true for this call
|
||||
|
||||
if (debugging) then
|
||||
local debugZones = cfxZones.zonesStartingWithName("+", searchSet)
|
||||
if not (next(debugZones) == nil) then -- # operator only works on array elements
|
||||
@ -1124,7 +1015,6 @@ function cfxZones.zonesStartingWith(prefix, searchSet, debugging)
|
||||
return debugZones
|
||||
end
|
||||
end
|
||||
|
||||
if (type(prefix) == "string") then
|
||||
return cfxZones.zonesStartingWithName(prefix, searchSet)
|
||||
end
|
||||
@ -1139,7 +1029,6 @@ function cfxZones.zonesStartingWith(prefix, searchSet, debugging)
|
||||
allZones[zName] = zInfo -- will also replace doublets
|
||||
end
|
||||
end
|
||||
|
||||
return allZones
|
||||
end
|
||||
|
||||
@ -1158,13 +1047,11 @@ function cfxZones.getZonesContainingString(aString, searchSet)
|
||||
resultSet[zName] = zData
|
||||
end
|
||||
end
|
||||
|
||||
end;
|
||||
|
||||
-- filter zones by range to a point. returns indexed set
|
||||
function cfxZones.getZonesInRange(point, range, theZones)
|
||||
if not theZones then theZones = cfxZones.zones end
|
||||
|
||||
local inRangeSet = {}
|
||||
for zName, zData in pairs (theZones) do
|
||||
if dcsCommon.dist(point, zData.point) < range then
|
||||
@ -1216,43 +1103,41 @@ function cfxZones.getZoneByIndex(theZones, theIndex)
|
||||
end
|
||||
|
||||
-- place a smoke marker in center of zone, offset by dx, dy
|
||||
function cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)
|
||||
function cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt, name)
|
||||
if not alt then alt = 5 end
|
||||
local point = cfxZones.getPoint(theZone) --{} -- theZone.point
|
||||
point.x = point.x + dx -- getpoint updates and returns copy
|
||||
point.z = point.z + dz
|
||||
-- get height at point
|
||||
-- correct height at point
|
||||
point.y = land.getHeight({x = point.x, y = point.z}) + alt
|
||||
-- height-correct
|
||||
--local newPoint= {x = point.x, y = land.getHeight({x = point.x, y = point.z}) + 3, z= point.z}
|
||||
trigger.action.smoke(point, smokeColor)
|
||||
trigger.action.smoke(point, smokeColor, name)
|
||||
end
|
||||
|
||||
function dmlZone:markZoneWithSmoke(dx, dz, smokeColor, alt)
|
||||
cfxZones.markZoneWithSmoke(self, dx, dz, smokeColor, alt)
|
||||
function dmlZone:markZoneWithSmoke(dx, dz, smokeColor, alt, name)
|
||||
cfxZones.markZoneWithSmoke(self, dx, dz, smokeColor, alt, name)
|
||||
end
|
||||
|
||||
-- place a smoke marker in center of zone, offset by radius and degrees
|
||||
function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, alt)
|
||||
function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, alt, name)
|
||||
local rads = degrees * math.pi / 180
|
||||
local dx = radius * math.sin(rads)
|
||||
local dz = radius * math.cos(rads)
|
||||
cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt)
|
||||
cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt, name)
|
||||
end
|
||||
|
||||
function dmlZone:markZoneWithSmokePolar(radius, degrees, smokeColor, alt)
|
||||
cfxZones.markZoneWithSmokePolar(self, radius, degrees, smokeColor, alt)
|
||||
function dmlZone:markZoneWithSmokePolar(radius, degrees, smokeColor, alt, name)
|
||||
cfxZones.markZoneWithSmokePolar(self, radius, degrees, smokeColor, alt, name)
|
||||
end
|
||||
|
||||
-- place a smoke marker in center of zone, offset by radius and randomized degrees
|
||||
function cfxZones.markZoneWithSmokePolarRandom(theZone, radius, smokeColor)
|
||||
function cfxZones.markZoneWithSmokePolarRandom(theZone, radius, smokeColor, name)
|
||||
local degrees = math.random(360)
|
||||
cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor)
|
||||
cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, name)
|
||||
end
|
||||
|
||||
function dmlZone:markZoneWithSmokePolarRandom(radius, smokeColor)
|
||||
function dmlZone:markZoneWithSmokePolarRandom(radius, smokeColor, name)
|
||||
local degrees = math.random(360)
|
||||
self:markZoneWithSmokePolar(radius, degrees, smokeColor)
|
||||
self:markZoneWithSmokePolar(radius, degrees, smokeColor, name)
|
||||
end
|
||||
|
||||
function cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig)
|
||||
@ -1264,17 +1149,13 @@ function cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig)
|
||||
return false, 0, 0, nil
|
||||
end
|
||||
|
||||
|
||||
-- unitInZone returns true if theUnit is inside the zone
|
||||
-- the second value returned is the percentage of distance
|
||||
-- from center to rim, with 100% being entirely in center, 0 = outside
|
||||
-- the third value returned is the distance to center
|
||||
function cfxZones.pointInZone(thePoint, theZone, useOrig)
|
||||
|
||||
if not (theZone) then return false, 0, 0 end
|
||||
|
||||
local pflat = {x = thePoint.x, y = 0, z = thePoint.z}
|
||||
|
||||
local zpoint
|
||||
if useOrig then
|
||||
zpoint = cfxZones.getDCSOrigin(theZone)
|
||||
@ -1284,26 +1165,22 @@ function cfxZones.pointInZone(thePoint, theZone, useOrig)
|
||||
local ppoint = thePoint -- xyz
|
||||
local pflat = {x = ppoint.x, y = 0, z = ppoint.z}
|
||||
local dist = dcsCommon.dist(zpoint, pflat)
|
||||
|
||||
if theZone.isCircle then
|
||||
if theZone.radius <= 0 then
|
||||
return false, 0, 0
|
||||
end
|
||||
|
||||
local success = dist < theZone.radius
|
||||
local percentage = 0
|
||||
if (success) then
|
||||
percentage = 1 - dist / theZone.radius
|
||||
end
|
||||
return success, percentage, dist
|
||||
|
||||
elseif theZone.isPoly then
|
||||
local success = cfxZones.isPointInsidePoly(pflat, theZone.poly)
|
||||
return success, 0, dist
|
||||
else
|
||||
trigger.action.outText("pointInZone: Unknown zone type for " .. theZone.name, 10)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@ -1311,7 +1188,6 @@ function dmlZone:pointInZone(thePoint, useOrig)
|
||||
return cfxZones.pointInZone(thePoint, self, useOrig)
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.unitInZone(theUnit, theZone)
|
||||
if not (theUnit) then return false, 0, 0 end
|
||||
if not (theUnit:isExist()) then return false, 0, 0 end
|
||||
@ -1383,7 +1259,6 @@ end
|
||||
function cfxZones.growZone()
|
||||
-- circular zones simply increase radius
|
||||
-- poly zones: not defined
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -1394,7 +1269,6 @@ function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName,
|
||||
if not drivable then drivable = false end
|
||||
-- group name will be taken from zone name and prependend with "G_"
|
||||
local theGroup = dcsCommon.createGroundGroupWithUnits(groupName, theUnits, theZone.radius, nil, formation, nil, liveries)
|
||||
|
||||
theGroup.uncontrollable = false -- just for completeness
|
||||
if drivable then
|
||||
local units = theGroup.units
|
||||
@ -1402,34 +1276,26 @@ function cfxZones.createGroundUnitsInZoneForCoalition (theCoalition, groupName,
|
||||
theUnit.playerCanDrive = drivable
|
||||
end
|
||||
end
|
||||
|
||||
-- turn the entire formation to heading
|
||||
if (not heading) then heading = 0 end
|
||||
dcsCommon.rotateGroupData(theGroup, heading) -- currently, group is still at origin, no cx, cy
|
||||
|
||||
|
||||
-- now move the group to center of theZone
|
||||
dcsCommon.moveGroupDataTo(theGroup,
|
||||
theZone.point.x,
|
||||
theZone.point.z) -- watchit: Z!!!
|
||||
|
||||
-- create the group in the world and return it
|
||||
-- first we need to translate the coalition to a legal
|
||||
-- country. we use UN for neutral, cjtf for red and blue
|
||||
local theSideCJTF = dcsCommon.coalition2county(theCoalition)
|
||||
-- store cty and cat for later access. DCS doesn't need it, but we may
|
||||
|
||||
theGroup.cty = theSideCJTF
|
||||
theGroup.cat = Group.Category.GROUND
|
||||
|
||||
-- create a copy of the group data for
|
||||
-- later reference
|
||||
local groupDataCopy = dcsCommon.clone(theGroup)
|
||||
|
||||
local newGroup = coalition.addGroup(theSideCJTF, Group.Category.GROUND, theGroup)
|
||||
return newGroup, groupDataCopy
|
||||
end
|
||||
|
||||
--
|
||||
-- ===============
|
||||
-- FLAG PROCESSING
|
||||
@ -1456,7 +1322,6 @@ function cfxZones.pulseFlag(theFlag, method, theZone)
|
||||
end
|
||||
local newVal = 1
|
||||
cfxZones.setFlagValue(theFlag, newVal, theZone)
|
||||
|
||||
-- schedule second half of pulse
|
||||
timer.scheduleFunction(cfxZones.unPulseFlag, args, timer.getTime() + delay)
|
||||
end
|
||||
@ -1489,7 +1354,6 @@ function cfxZones.evalRemainder(remainder, theZone)
|
||||
remainder = string.sub(remainder, 2)
|
||||
remainder = dcsCommon.trim(remainder)
|
||||
end
|
||||
|
||||
if esc == "(" and last == ")" and string.len(remainder) > 2 then
|
||||
-- note: iisues with startswith("(") ???
|
||||
remainder = string.sub(remainder, 2, -2)
|
||||
@ -1519,7 +1383,6 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
if not theZone then
|
||||
trigger.action.outText("+++zones: nil theZone on pollFlag", 30)
|
||||
end
|
||||
|
||||
local mt = type(method)
|
||||
if mt == "number" then
|
||||
method = "#" .. method -- convert to immediate
|
||||
@ -1528,7 +1391,6 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
trigger.action.outText("+++zne: warning: zone <" .. theZone.name .. "> method type <" .. mt .. "> received. Ignoring", 30)
|
||||
return
|
||||
end
|
||||
|
||||
local val = nil
|
||||
method = method:lower()
|
||||
method = dcsCommon.trim(method)
|
||||
@ -1549,7 +1411,6 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
return
|
||||
else
|
||||
end
|
||||
|
||||
if dcsCommon.stringStartsWith(method, "#") then
|
||||
-- immediate value command. remove # and eval remainder
|
||||
local remainder = dcsCommon.removePrefix(method, "#")
|
||||
@ -1560,30 +1421,19 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local currVal = cfxZones.getFlagValue(theFlag, theZone)
|
||||
if method == "inc" or method == "f+1" then
|
||||
--trigger.action.setUserFlag(theFlag, currVal + 1)
|
||||
cfxZones.setFlagValue(theFlag, currVal+1, theZone)
|
||||
|
||||
elseif method == "dec" or method == "f-1" then
|
||||
-- trigger.action.setUserFlag(theFlag, currVal - 1)
|
||||
cfxZones.setFlagValue(theFlag, currVal-1, theZone)
|
||||
|
||||
elseif method == "off" or method == "f=0" then
|
||||
-- trigger.action.setUserFlag(theFlag, 0)
|
||||
cfxZones.setFlagValue(theFlag, 0, theZone)
|
||||
|
||||
elseif method == "flip" or method == "xor" then
|
||||
if currVal ~= 0 then
|
||||
-- trigger.action.setUserFlag(theFlag, 0)
|
||||
cfxZones.setFlagValue(theFlag, 0, theZone)
|
||||
|
||||
else
|
||||
--trigger.action.setUserFlag(theFlag, 1)
|
||||
cfxZones.setFlagValue(theFlag, 1, theZone)
|
||||
end
|
||||
|
||||
elseif dcsCommon.stringStartsWith(method, "pulse") then
|
||||
cfxZones.pulseFlag(theFlag, method, theZone)
|
||||
|
||||
@ -1595,22 +1445,18 @@ function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++zones: (poll) updating with '+' flag <" .. theFlag .. "> in <" .. theZone.name .. "> by <" .. adder .. "> to <" .. adder + currVal .. ">", 30)
|
||||
end
|
||||
|
||||
elseif dcsCommon.stringStartsWith(method, "-") then
|
||||
-- we subtract whatever is to the right
|
||||
local remainder = dcsCommon.removePrefix(method, "-")
|
||||
local adder = cfxZones.evalRemainder(remainder)
|
||||
cfxZones.setFlagValue(theFlag, currVal-adder, theZone)
|
||||
|
||||
else
|
||||
if method ~= "on" and method ~= "f=1" then
|
||||
trigger.action.outText("+++zones: unknown method <" .. method .. "> for flag <" .. theFlag .. "> in zone <" .. theZone.name .. "> - setting to 1", 30)
|
||||
end
|
||||
-- default: on.
|
||||
-- trigger.action.setUserFlag(theFlag, 1)
|
||||
cfxZones.setFlagValue(theFlag, 1, theZone)
|
||||
end
|
||||
|
||||
end
|
||||
if cfxZones.verbose then
|
||||
local newVal = cfxZones.getFlagValue(theFlag, theZone)
|
||||
trigger.action.outText("+++zones: flag <" .. theFlag .. "> changed from " .. currVal .. " to " .. newVal, 30)
|
||||
@ -1620,18 +1466,14 @@ end
|
||||
function cfxZones.pollFlag(theFlag, method, theZone)
|
||||
local allFlags = {}
|
||||
if dcsCommon.containsString(theFlag, ",") then
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("+++zones: will poll flag set <" .. theFlag .. "> with " .. method, 30)
|
||||
end
|
||||
if cfxZones.verbose then trigger.action.outText("+++zones: will poll flag set <" .. theFlag .. "> with " .. method, 30) end
|
||||
allFlags = dcsCommon.splitString(theFlag, ",")
|
||||
else
|
||||
table.insert(allFlags, theFlag)
|
||||
end
|
||||
|
||||
for idx, aFlag in pairs(allFlags) do
|
||||
aFlag = dcsCommon.trim(aFlag)
|
||||
-- note: mey require range preprocessing, but that's not
|
||||
-- a priority
|
||||
-- note: mey require range preprocessing, but that's not a priority
|
||||
cfxZones.doPollFlag(aFlag, method, theZone)
|
||||
end
|
||||
end
|
||||
@ -1646,19 +1488,16 @@ function cfxZones.expandFlagName(theFlag, theZone)
|
||||
if theZone then
|
||||
zoneName = theZone.name -- for flag wildcards
|
||||
end
|
||||
|
||||
if type(theFlag) == "number" then
|
||||
-- straight number, return
|
||||
return theFlag
|
||||
end
|
||||
|
||||
-- we assume it's a string now
|
||||
theFlag = dcsCommon.trim(theFlag) -- clear leading/trailing spaces
|
||||
local nFlag = tonumber(theFlag)
|
||||
if nFlag then -- a number, legal
|
||||
return theFlag
|
||||
end
|
||||
|
||||
-- now do wildcard processing. we have alphanumeric
|
||||
if dcsCommon.stringStartsWith(theFlag, "*") then
|
||||
theFlag = zoneName .. theFlag
|
||||
@ -1677,9 +1516,7 @@ end
|
||||
function cfxZones.setFlagValueMult(theFlag, theValue, theZone)
|
||||
local allFlags = {}
|
||||
if dcsCommon.containsString(theFlag, ",") then
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("+++zones: will multi-set flags <" .. theFlag .. "> to " .. theValue, 30)
|
||||
end
|
||||
if cfxZones.verbose then trigger.action.outText("+++zones: will multi-set flags <" .. theFlag .. "> to " .. theValue, 30)end
|
||||
allFlags = dcsCommon.splitString(theFlag, ",")
|
||||
else
|
||||
table.insert(allFlags, theFlag)
|
||||
@ -1687,8 +1524,7 @@ function cfxZones.setFlagValueMult(theFlag, theValue, theZone)
|
||||
|
||||
for idx, aFlag in pairs(allFlags) do
|
||||
aFlag = dcsCommon.trim(aFlag)
|
||||
-- note: mey require range preprocessing, but that's not
|
||||
-- a priority
|
||||
-- note: mey require range preprocessing, but that's not a priority
|
||||
cfxZones.doSetFlagValue(aFlag, theValue, theZone)
|
||||
end
|
||||
end
|
||||
@ -1700,7 +1536,6 @@ function cfxZones.doSetFlagValue(theFlag, theValue, theZone)
|
||||
else
|
||||
zoneName = theZone.name -- for flag wildcards
|
||||
end
|
||||
|
||||
if type(theFlag) == "number" then
|
||||
-- straight set, oldschool ME flag
|
||||
trigger.action.setUserFlag(theFlag, theValue)
|
||||
@ -1730,7 +1565,6 @@ function cfxZones.getFlagValue(theFlag, theZone)
|
||||
else
|
||||
zoneName = theZone.name -- for flag wildcards
|
||||
end
|
||||
|
||||
if type(theFlag) == "number" then
|
||||
-- straight get, ME flag
|
||||
return tonumber(trigger.misc.getUserFlag(theFlag))
|
||||
@ -1750,7 +1584,7 @@ function cfxZones.getFlagValue(theFlag, theZone)
|
||||
|
||||
-- now do wildcard processing. we have alphanumeric
|
||||
if dcsCommon.stringStartsWith(theFlag, "*") then
|
||||
theFlag = zoneName .. theFlag
|
||||
theFlag = zoneName .. theFlag
|
||||
end
|
||||
return tonumber(trigger.misc.getUserFlag(theFlag))
|
||||
end
|
||||
@ -2089,7 +1923,8 @@ function cfxZones.drawZone(theZone, lineColor, fillColor, markID)
|
||||
if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end
|
||||
if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.0} end
|
||||
if not markID then markID = dcsCommon.numberUUID() end
|
||||
|
||||
-- trigger.action.outText("drawZone <" .. theZone.name .. "> - L: r=" .. lineColor[1] .. ", g= " .. lineColor[2] .. ", b=" .. lineColor[3] .. ", a=" .. lineColor[4] .. ".", 30)
|
||||
-- trigger.action.outText("and fill F: r=" .. fillColor[1] .. ", g= " .. fillColor[2] .. ", b=" .. fillColor[3] .. ", a=" .. fillColor[4] .. ".", 30)
|
||||
if theZone.isCircle then
|
||||
trigger.action.circleToAll(-1, markID, theZone.point, theZone.radius, lineColor, fillColor, 1, true, "")
|
||||
else
|
||||
@ -2803,6 +2638,8 @@ function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, defa
|
||||
local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)
|
||||
s = s:lower()
|
||||
s = dcsCommon.trim(s)
|
||||
if s == "?" or s == "rnd" or s == "random" then s = tostring(dcsCommon.smallRandom(5) - 1) end
|
||||
|
||||
-- check numbers
|
||||
if (s == "0") then return "green" end
|
||||
if (s == "1") then return "red" end
|
||||
@ -2831,6 +2668,7 @@ function cfxZones.getSmokeColorNumberFromZoneProperty(theZone, theProperty, defa
|
||||
|
||||
s = s:lower()
|
||||
s = dcsCommon.trim(s)
|
||||
if s == "?" or s == "rnd" or s == "random" then s = tostring(dcsCommon.smallRandom(5) - 1) end
|
||||
-- check numbers
|
||||
local n = tonumber(s)
|
||||
if n then
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "2.5.2"
|
||||
cloneZones.version = "2.6.0"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -61,6 +61,8 @@ cloneZones.respawnOnGroupID = true
|
||||
empty! detection through saves (missed hasClones)
|
||||
2.5.1 - f? and in? put on notice for depreciation
|
||||
2.5.2 - removed bug when checking damaged! and no units cloned
|
||||
2.6.0 - maxCycles attribute
|
||||
- increased vorbosity during persistance:loadData
|
||||
--]]--
|
||||
|
||||
--
|
||||
@ -332,6 +334,14 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
if theZone:hasProperty("health#") then
|
||||
theZone.health = theZone:getStringFromZoneProperty("health#")
|
||||
end
|
||||
|
||||
-- maxCycles
|
||||
if theZone:hasProperty("maxCycles") then
|
||||
theZone.maxCycles = theZone:getNumberFromZoneProperty("maxCycles", 99999)
|
||||
end
|
||||
if theZone:hasProperty("maxCycle") then
|
||||
theZone.maxCycles = theZone:getNumberFromZoneProperty("maxCycle", 99999)
|
||||
end
|
||||
-- we end with clear plate
|
||||
theZone.lastSize = 0 -- no units here
|
||||
end
|
||||
@ -1506,6 +1516,18 @@ function cloneZones.spawnWithCloner(theZone)
|
||||
end
|
||||
end
|
||||
|
||||
-- see if we have spawned enough
|
||||
if theZone.maxCycles then
|
||||
if theZone.maxCycles > 0 then -- update and spawn
|
||||
theZone.maxCycles = theZone.maxCycles - 1
|
||||
else -- no spawn
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++clnZ: cloner <" .. theZone.name .. "> out of cycles. No clone cycle", 30)
|
||||
end
|
||||
return -- no spawning, out of cycles
|
||||
end
|
||||
end
|
||||
|
||||
-- force spawn with this spawner
|
||||
local templateZone = theZone
|
||||
if theZone.source then
|
||||
@ -1903,6 +1925,9 @@ function cloneZones.saveData()
|
||||
local cData = {}
|
||||
local cName = theCloner.name
|
||||
cData.myUniqueCounter = theCloner.myUniqueCounter
|
||||
if theCloner.maxCycles then
|
||||
cData.maxCycles = theCloner.maxCycles
|
||||
end
|
||||
cData.oSize = theCloner.oSize
|
||||
cData.lastSize = theCloner.lastSize
|
||||
-- mySpawns: all groups I'm curently observing for empty!
|
||||
@ -1946,7 +1971,8 @@ function cloneZones.loadData()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local gSpawn = 0
|
||||
local uSpawn = 0
|
||||
-- spawn all units
|
||||
local allClones = theData.clones
|
||||
for gName, gData in pairs (allClones) do
|
||||
@ -1958,12 +1984,15 @@ function cloneZones.loadData()
|
||||
local gdClone = dcsCommon.clone(gData)
|
||||
cloneZones.allClones[gName] = gdClone
|
||||
local theGroup = coalition.addGroup(cty, cat, gData)
|
||||
uSpawn = uSpawn + #theGroup:getUnits()
|
||||
gSpawn = gSpawn + 1
|
||||
-- turn off AI if disabled
|
||||
if not gData.useAI then
|
||||
cloneZones.turnOffAI({theGroup})
|
||||
end
|
||||
end
|
||||
|
||||
if cloneZones.verbose then trigger.action.outText("load: loaded <" .. gSpawn .. "> groups, <" .. uSpawn .. "> units total.", 30) end
|
||||
-- spawn all static objects
|
||||
local allObjects = theData.objects
|
||||
for oName, oData in pairs(allObjects) do
|
||||
@ -2001,6 +2030,7 @@ function cloneZones.loadData()
|
||||
end
|
||||
if cData.oSize then theCloner.oSize = cData.oSize end
|
||||
if cData.lastSize then theCloner.lastSize = cData.lastSize end
|
||||
if cData.maxCycles then theCloner.maxCycles = cData.maxCycles end
|
||||
|
||||
local mySpawns = {}
|
||||
for idx, aName in pairs(cData.mySpawns) do
|
||||
|
||||
@ -1,56 +1,16 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "4.2.1"
|
||||
csarManager.version = "4.3.0"
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
|
||||
- 2.3.0 - dmlZones
|
||||
- onRoad attribute for CSAR mission Zones
|
||||
- rndLoc support
|
||||
- triggerMethod support
|
||||
- 2.3.1 - addPrefix option
|
||||
- delay asynch OK (message only)
|
||||
- offset zone on randomized soldier
|
||||
- smokeDist
|
||||
- 2.3.2 - DCS 2.9 getCategory() fix
|
||||
- 3.0.0 - moved mission creation out of update loop into own
|
||||
- removed cfxPlayer dependency
|
||||
- new event manager
|
||||
- no longer single-proccing pilots
|
||||
- can also handle aircraft - isTroopCarrier
|
||||
- 3.1.0 - integration with scribe
|
||||
- expanded internal API: newMissionCB, invoked at addMission
|
||||
- expanded internal API: removedMissionCB
|
||||
- added *rnd as option for csarName (requires "names")
|
||||
- missions are sorted by distance
|
||||
- mission timeLimit range implemented
|
||||
- update handles time limit (pickup only)
|
||||
- inflight status reflects time limit but will not time out
|
||||
- pickupSound option
|
||||
- lostSound option
|
||||
- 3.1.1 - birth clears troopsOnBoard
|
||||
- 3.2.0 - inPopulated csar option
|
||||
- clearance csar attribute
|
||||
- maxTries csar attribute
|
||||
- 3.2.1 - comsRange attribute when mission times out
|
||||
- rescueTypes option in config
|
||||
3.2.2 - reset helicopter weight on birth
|
||||
- cleanup
|
||||
3.2.3 - hardening against *accidental* multi-unit player groups.
|
||||
3.2.4 - pass theZone with missionCreateCB when created from zone
|
||||
3.2.5 - smoke callbacks
|
||||
- useRanks option
|
||||
3.2.6 - inBuiltup analogon to cloner
|
||||
3.2.7 - createCSARForParachutist now supports optional coa (autoCSAR)
|
||||
3.3.0 - persistence support
|
||||
3.4.0 - global timeLimit option in config zone
|
||||
- fixes expiration bug when persisting data
|
||||
4.0.0 - support for mainMenu
|
||||
4.0.1 - increased verbosity
|
||||
- fix for Jul-11 2024 DCS bugs
|
||||
4.1.0 - support for DCS 2.9.6 dynamic spawns
|
||||
4.2.0 - automatically support twn if present
|
||||
4.2.1 - added Chinook to csar default set (via common)
|
||||
4.3.0 - pilot's smoke can now extinguish immediately
|
||||
- keepSmoke option
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||
INTEGRATES WITH LIMITED AIRFRAMES
|
||||
@ -549,6 +509,14 @@ function csarManager.heloLanded(theUnit)
|
||||
30)
|
||||
didPickup = true;
|
||||
|
||||
-- handle smoke
|
||||
if csarManager.keepSmoke then
|
||||
-- trigger.action.outText("keeping smokeName <" .. theMission.smokeName .. "> running", 30)
|
||||
else
|
||||
trigger.action.effectSmokeStop(theMission.smokeName)
|
||||
-- trigger.action.outText("smokeName <" .. theMission.smokeName .. "> removed", 30)
|
||||
end
|
||||
|
||||
local args = {}
|
||||
args.theName = theMission.name
|
||||
args.mySide = mySide
|
||||
@ -1186,18 +1154,19 @@ function csarManager.update() -- every second
|
||||
table.insert(csarMission.messagedUnits, uName) -- remember that we messaged them so we don't do again
|
||||
end
|
||||
|
||||
-- also pop smoke if not popped already, or more than 5 minutes ago
|
||||
if csarManager.useSmoke and (timer.getTime() - csarMission.lastSmokeTime) >= 5 * 60 then
|
||||
-- pop smoke if not popped already, or more than 5 minutes ago
|
||||
if csarManager.useSmoke and
|
||||
(timer.getTime() - csarMission.lastSmokeTime) >= 5 * 60 then
|
||||
if csarMission.lastSmokeTime < 0 then
|
||||
-- this is the first time that this mission pops smoke
|
||||
-- trigger.action.outText("***will invoke smoke cb", 30)
|
||||
csarManager.invokeSmokeCallbacks(csarMission, uName)
|
||||
else
|
||||
--trigger.action.outText("nope smoke's a dope", 30)
|
||||
end
|
||||
local smokePoint = dcsCommon.randomPointOnPerimeter(
|
||||
csarManager.smokeDist, csarMission.zone.point.x, csarMission.zone.point.z)
|
||||
dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor)
|
||||
csarMission.smokeName = dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor) -- returns unique smoke name
|
||||
trigger.action.outText("smokeName <" .. csarMission.smokeName .. "> started", 30)
|
||||
csarMission.lastSmokeTime = timer.getTime()
|
||||
end
|
||||
|
||||
@ -1236,6 +1205,13 @@ function csarManager.update() -- every second
|
||||
table.insert(conf.troopsOnBoard, csarMission)
|
||||
csarMission.group:destroy() -- will shut up radio as well
|
||||
csarMission.group = nil -- no more evacuees
|
||||
-- turn off smoke?
|
||||
if csarManager.keepSmoke then
|
||||
-- trigger.action.outText("keeping smokeName <" .. csarMission.smokeName .. "> running", 30)
|
||||
else
|
||||
trigger.action.effectSmokeStop(csarMission.smokeName)
|
||||
-- trigger.action.outText("smokeName <" .. csarMission.smokeName .. "> removed", 30)
|
||||
end
|
||||
needsGC = true -- need filtering missions
|
||||
|
||||
-- now handle weight using cargoSuper
|
||||
@ -1425,8 +1401,7 @@ function csarManager.readCSARZone(theZone)
|
||||
theZone.numCrew = 1
|
||||
theZone.csarMapMarker = nil
|
||||
if theZone:hasProperty("timeLimit") then
|
||||
local tmin, tmax = theZone:getPositiveRangeFromZoneProperty("timeLimit", 1)
|
||||
|
||||
local tmin, tmax = theZone:getPositiveRangeFromZoneProperty("timeLimit", 1) -- minutes
|
||||
theZone.timeLimit = {tmin, tmax}
|
||||
else
|
||||
theZone.timeLimit = nil
|
||||
@ -1572,6 +1547,7 @@ function csarManager.readConfigZone()
|
||||
csarManager.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||
|
||||
csarManager.useSmoke = theZone:getBoolFromZoneProperty("useSmoke", true)
|
||||
csarManager.keepSmoke = theZone:getBoolFromZoneProperty("keepSmoke", false)
|
||||
csarManager.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "blue")
|
||||
csarManager.smokeDist = theZone:getNumberFromZoneProperty("smokeDist", 30)
|
||||
csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor)
|
||||
@ -1807,4 +1783,5 @@ end
|
||||
player that they did not survive the transport
|
||||
|
||||
- randomize smoke color if smoke color has more than one entries
|
||||
- ELT freq higher bands?
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.2.0"
|
||||
dcsCommon.version = "3.2.1"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -39,6 +39,7 @@ dcsCommon.version = "3.2.0"
|
||||
- cfxZones links to processTimeLocWildCards
|
||||
- new processAtoBWildCards()
|
||||
- tons of new wildcards like <eleft>
|
||||
3.2.1 - markPointWithSmoke supports names and returns names
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -2650,14 +2651,16 @@ end
|
||||
end
|
||||
|
||||
|
||||
function dcsCommon.markPointWithSmoke(p, smokeColor)
|
||||
function dcsCommon.markPointWithSmoke(p, smokeColor, name)
|
||||
if not smokeColor then smokeColor = 0 end
|
||||
if not name then name = dcsCommon.uuid("dcsCsmoke") end
|
||||
local x = p.x
|
||||
local z = p.z -- do NOT change the point directly
|
||||
-- height-correct
|
||||
local y = land.getHeight({x = x, y = z})
|
||||
local newPoint= {x = x, y = y + 2, z = z}
|
||||
trigger.action.smoke(newPoint, smokeColor)
|
||||
trigger.action.smoke(newPoint, smokeColor, name)
|
||||
return name
|
||||
end
|
||||
|
||||
-- based on buzzer1977's idea, channel is number, eg in 74X, channel is 74, mode is "X"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxHeloTroops = {}
|
||||
cfxHeloTroops.version = "4.2.2"
|
||||
cfxHeloTroops.version = "4.2.3"
|
||||
cfxHeloTroops.verbose = false
|
||||
cfxHeloTroops.autoDrop = true
|
||||
cfxHeloTroops.autoPickup = false
|
||||
@ -22,6 +22,8 @@ cfxHeloTroops.requestRange = 500 -- meters
|
||||
4.2.1 - increased verbosity
|
||||
- also supports 'pickupRang" for reverse-compatibility with manual typo.
|
||||
4.2.2 - support for attachTo:
|
||||
4.2.3 - dropZone supports 'keepWait' attribute
|
||||
- dropZone supports 'setWait' attribute
|
||||
|
||||
--]]--
|
||||
cfxHeloTroops.minTime = 3 -- seconds beween tandings
|
||||
@ -48,6 +50,8 @@ function cfxHeloTroops.processDropZone(theZone)
|
||||
theZone.dropMethod = theZone:getStringFromZoneProperty("dropMethod", "inc")
|
||||
theZone.dropCoa = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||
theZone.autoDespawn = theZone:getNumberFromZoneProperty("autoDespawn", -1)
|
||||
theZone.keepWait = theZone:getBoolFromZoneProperty("keepWait", false)
|
||||
theZone.setWait = theZone:getBoolFromZoneProperty("setWait", false)
|
||||
end
|
||||
|
||||
--
|
||||
@ -704,20 +708,47 @@ function cfxHeloTroops.deployTroopsFromHelicopter(conf)
|
||||
local moveFormation = conf.troopsOnBoard.moveFormation
|
||||
local code = conf.troopsOnBoard.code
|
||||
local canDrive = conf.troopsOnBoard.canDrive
|
||||
local theCoalition = theUnit:getGroup():getCoalition() -- my coa
|
||||
|
||||
if not orders then orders = "guard" end
|
||||
orders = string.lower(orders)
|
||||
|
||||
-- order processing: if the orders were pre-pended with "wait-"
|
||||
-- we now remove that, so after dropping they do what their
|
||||
-- orders where AFTER being picked up
|
||||
-- order "wait" processing if not in special drop zone:
|
||||
-- if the orders were pre-pended with "wait-"
|
||||
-- remove that, so after dropping they do what their
|
||||
-- orders where AFTER removing "wait"
|
||||
-- or if setWait is true, add it
|
||||
local closestDropZone = cfxZones.getClosestZone(p, cfxHeloTroops.dropzones)
|
||||
if dcsCommon.stringStartsWith(orders, "wait-") then
|
||||
orders = dcsCommon.removePrefix(orders, "wait-")
|
||||
trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> revoke 'wait' orders, proceed with <".. orders .. ">", 30)
|
||||
local dropWait = false
|
||||
if closestDropZone and closestDropZone:pointInZone(p) then
|
||||
if closestDropZone.dropCoa == 0 or closestDropZone.dropCoa == theCoalition then
|
||||
if not closestDropZone.keepWait then dropWait = true end
|
||||
end
|
||||
end
|
||||
-- see if we are in a drop zone
|
||||
if dropWait then
|
||||
orders = dcsCommon.removePrefix(orders, "wait-")
|
||||
trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> revoke 'wait' orders, proceed with <".. orders .. ">", 30)
|
||||
else trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> keeping 'wait' orders (".. orders .. ")", 30) end
|
||||
end
|
||||
|
||||
if not dcsCommon.stringStartsWith(orders, "wait-") then
|
||||
local setWait = false
|
||||
if closestDropZone and closestDropZone:pointInZone(p) then
|
||||
if closestDropZone.dropCoa == 0 or closestDropZone.dropCoa == theCoalition then
|
||||
if not closestDropZone.setWait then setWait = true end
|
||||
end
|
||||
end
|
||||
-- see if we are in a drop zone
|
||||
if setWait then
|
||||
orders = "wait-" .. orders
|
||||
trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> added 'wait' orders: <".. orders .. ">", 30)
|
||||
else trigger.action.outTextForGroup(conf.id, "+++ <" .. conf.troopsOnBoard.name .. "> keeping orders (".. orders .. ")", 30) end
|
||||
end
|
||||
|
||||
local chopperZone = cfxZones.createSimpleZone("choppa", p, 12) -- 12 m radius around choppa
|
||||
local theCoalition = theUnit:getGroup():getCoalition() -- make it chopper's COALITION
|
||||
|
||||
local theGroup, theData = cfxZones.createGroundUnitsInZoneForCoalition (
|
||||
theCoalition,
|
||||
theName, -- group name, may be tracked
|
||||
@ -1173,4 +1204,5 @@ if not cfxHeloTroops.start() then
|
||||
end
|
||||
|
||||
|
||||
-- TODO: weight when loading troops
|
||||
-- TODO: weight when loading troops
|
||||
-- TODO: keepWait for dropzones: troops keep their "wait" orders when dropzone has that tag
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxOwnedZones = {}
|
||||
cfxOwnedZones.version = "2.4.1"
|
||||
cfxOwnedZones.version = "2.5.0"
|
||||
cfxOwnedZones.verbose = false
|
||||
cfxOwnedZones.announcer = true
|
||||
cfxOwnedZones.name = "cfxOwnedZones"
|
||||
@ -45,6 +45,8 @@ cfxOwnedZones.name = "cfxOwnedZones"
|
||||
2.3.1 - restored getNearestOwnedZoneToPoint
|
||||
2.4.0 - dmlZones masterOwner update
|
||||
2.4.1 - conquered flag now correctly guarded in loadData()
|
||||
2.5.0 - staggering zone drawing in time during loadData to avoid show-stopping DCS bug in trigger.action.removeMark()
|
||||
|
||||
|
||||
--]]--
|
||||
cfxOwnedZones.requiredLibs = {
|
||||
@ -125,11 +127,11 @@ function cfxOwnedZones.drawZoneInMap(aZone)
|
||||
end
|
||||
|
||||
if aZone.title then
|
||||
aZone.titleID = aZone:drawText(aZone.title, 18, lineColor, {0, 0, 0, 0})
|
||||
aZone.titleID = aZone:drawText(aZone.title, 18, lineColor, {0, 0, 0, 0}, nil)
|
||||
end
|
||||
|
||||
if aZone.hidden then return end
|
||||
aZone.markID = aZone:drawZone(lineColor, fillColor) -- markID
|
||||
aZone.markID = aZone:drawZone(lineColor, fillColor, nil) -- markID
|
||||
end
|
||||
|
||||
function cfxOwnedZones.getOwnedZoneByName(zName)
|
||||
@ -748,7 +750,7 @@ function cfxOwnedZones.loadData()
|
||||
local theData = persistence.getSavedDataForModule("cfxOwnedZones")
|
||||
if not theData then
|
||||
if cfxOwnedZones.verbose then
|
||||
trigger.action.outText("owdZ: no save date received, skipping.", 30)
|
||||
trigger.action.outText("owdZ: no save data received, skipping.", 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
@ -757,6 +759,9 @@ function cfxOwnedZones.loadData()
|
||||
-- flagInfo: module-global flags
|
||||
-- attackers: all spawned attackers that we feed to groundTroops
|
||||
local allZoneData = theData.zoneData
|
||||
local now = timer.getTime()
|
||||
local delay = now + 0.5
|
||||
|
||||
for zName, zData in pairs(allZoneData) do
|
||||
-- access zone
|
||||
local theZone = cfxOwnedZones.getOwnedZoneByName(zName)
|
||||
@ -766,7 +771,10 @@ function cfxOwnedZones.loadData()
|
||||
theZone:setFlagValue(theZone.conqueredFlag, zData.conquered)
|
||||
end
|
||||
-- update mark in map
|
||||
cfxOwnedZones.drawZoneInMap(theZone)
|
||||
-- does this impact performance?
|
||||
timer.scheduleFunction(cfxOwnedZones.drawZoneInMap, theZone, delay)
|
||||
delay = delay + 0.5
|
||||
-- cfxOwnedZones.drawZoneInMap(theZone)
|
||||
else
|
||||
trigger.action.outText("owdZ: load - data mismatch: cannot find zone <" .. zName .. ">, skipping zone.", 30)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
persistence = {}
|
||||
persistence.version = "3.0.2"
|
||||
persistence.version = "3.1.0"
|
||||
persistence.ups = 1 -- once every 1 seconds
|
||||
persistence.verbose = false
|
||||
persistence.active = false
|
||||
@ -24,6 +24,8 @@ persistence.requiredLibs = {
|
||||
code cleanup
|
||||
3.0.2 - more logging
|
||||
vardump to log possible
|
||||
3.1.0 - validFor attribute -- timed gc
|
||||
- persistence deallocs data after validFor seconds
|
||||
|
||||
PROVIDES LOAD/SAVE ABILITY TO MODULES
|
||||
PROVIDES STANDALONE/HOSTED SERVER COMPATIBILITY
|
||||
@ -74,12 +76,43 @@ end
|
||||
--
|
||||
-- registered modules call this to get their data
|
||||
--
|
||||
function persistence.count(theTable)
|
||||
if not theTable then return 0 end
|
||||
local c = 0
|
||||
for idx, val in pairs(theTable) do
|
||||
c = c + 1
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function persistence.filter(name) -- debugging
|
||||
--if true then return false end
|
||||
--if name == "WHpersistence" then return true end
|
||||
--if name == "unitPersistence" then return true end
|
||||
--if name == "cfxPlayerScore" then return true end
|
||||
--if name == "cfxSSBClient" then return true end
|
||||
--if name == "cfxSpawnZones" then return true end
|
||||
--if name == "cfxHeloTroops" then return true end
|
||||
--if name == "rndFlags" then return true end
|
||||
--if name == "cfxOwnedZones" then return true end
|
||||
--if name == "cloneZones" then return true end --*
|
||||
--if name == "cfxObjectDestructDetector" then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
function persistence.getSavedDataForModule(name, sharedDataName)
|
||||
-- if persistence.verbose then
|
||||
-- trigger.action.outText("+++persistence: enter load for <" .. name .. ">", 30)
|
||||
-- end
|
||||
if not persistence.active then return nil end
|
||||
if not persistence.hasData then return nil end
|
||||
if not persistence.missionData then return end
|
||||
if not persistence.missionData then return nil end
|
||||
if not sharedDataName then sharedDataName = nil end
|
||||
|
||||
if persistence.filter(name) then
|
||||
trigger.action.outText("FILTERED persistence for <" .. name .. ">", 30)
|
||||
return nil
|
||||
end
|
||||
if sharedDataName then
|
||||
-- we read from shared data and only revert to
|
||||
-- common if we find nothing
|
||||
@ -90,6 +123,7 @@ function persistence.getSavedDataForModule(name, sharedDataName)
|
||||
local theData = persistence.loadTable(shFile, true)
|
||||
if theData then
|
||||
if theData[name] then
|
||||
trigger.action.outText("+++persistence: returning SHARED for <" .. name .. ">", 30)
|
||||
return theData[name]
|
||||
end
|
||||
if persistence.verbose then
|
||||
@ -102,6 +136,9 @@ function persistence.getSavedDataForModule(name, sharedDataName)
|
||||
end
|
||||
end
|
||||
end
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: returning data (" .. persistence.count(persistence.missionData[name]) .. " records) for <" .. name .. ">", 30)
|
||||
end
|
||||
return persistence.missionData[name] -- simply get the modules data block
|
||||
end
|
||||
|
||||
@ -580,12 +617,25 @@ function persistence.readConfigZone()
|
||||
|
||||
persistence.saveNotification = theZone:getBoolFromZoneProperty("saveNotification", true)
|
||||
|
||||
persistence.validFor = theZone:getNumberFromZoneProperty("validFor", 5) -- GC after ... seconds
|
||||
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: read config", 30)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function persistence.GC()
|
||||
-- destroy loaded mission data
|
||||
if persistence.missionData then
|
||||
persistence.missionData = nil
|
||||
if persistence.verbose then
|
||||
trigger.action.outText("+++persistence: relinquished loaded data.", 30)
|
||||
end
|
||||
end
|
||||
persistence.hasData = false
|
||||
end
|
||||
|
||||
function persistence.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
@ -720,7 +770,7 @@ function persistence.start()
|
||||
|
||||
-- we now see if we can and need load data
|
||||
persistence.missionStartDataLoad()
|
||||
|
||||
timer.scheduleFunction(persistence.GC, nil, timer.getTime() + persistence.validFor) -- destroy loaded data after this interval
|
||||
-- and start updating
|
||||
persistence.update()
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
pulseFlags = {}
|
||||
pulseFlags.version = "2.0.3"
|
||||
pulseFlags.version = "2.0.4"
|
||||
pulseFlags.verbose = false
|
||||
pulseFlags.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -8,7 +8,7 @@ pulseFlags.requiredLibs = {
|
||||
--[[--
|
||||
Pulse Flags: DML module to regularly change a flag
|
||||
|
||||
Copyright 2022 by Christian Franz and cf/x
|
||||
Copyright 2022-2025 by Christian Franz and cf/x
|
||||
|
||||
Version History
|
||||
- 2.0.0 dmlZones / OOP
|
||||
@ -16,6 +16,7 @@ pulseFlags.requiredLibs = {
|
||||
- 2.0.1 activateZoneFlag now works correctly
|
||||
- 2.0.2 fixed scheduledTime bug while persisting
|
||||
- 2.0.3 now setting -1 (infinite) as pulses works correctly
|
||||
- 2.0.4 corrected typo when checking verbosity
|
||||
--]]--
|
||||
|
||||
pulseFlags.pulses = {}
|
||||
@ -69,7 +70,7 @@ function pulseFlags.createPulseWithZone(theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
if theZone.verbose or pulseFlag.verbose then
|
||||
if theZone.verbose or pulseFlags.verbose then
|
||||
trigger.action.outText("+++pulF: set pulses in <" .. theZone.name .. "> to <" .. theZone.pulses .. ">", 30)
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
reaper = {}
|
||||
reaper.version = "1.2.0"
|
||||
reaper.version = "1.3.0"
|
||||
reaper.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
@ -21,8 +21,9 @@ VERSION HISTORY
|
||||
- completely rewrote scanning method (performance)
|
||||
- added FAC task
|
||||
- split task generation from wp generation
|
||||
- updated reaper naming, uniqueNames attribute (undocumented)
|
||||
- updated reaper naming, uniqueN ames attribute (undocumented)
|
||||
1.2.0 - support twn when present
|
||||
1.3.0 - new invisible option for drone zone
|
||||
|
||||
--]]--
|
||||
|
||||
@ -78,6 +79,7 @@ function reaper.readReaperZone(theZone)
|
||||
theZone.cycle = theZone:getStringFromZoneProperty("cycle?", "<none>")
|
||||
theZone.cycleVal = theZone:getFlagValue(theZone.cycle)
|
||||
end
|
||||
theZone.invisible = theZone:getBoolFromZoneProperty("invisible", false)
|
||||
|
||||
theZone.hasSpawned = false
|
||||
|
||||
@ -149,7 +151,7 @@ function reaper.spawnForZone(theZone, ack)
|
||||
|
||||
-- now create and add waypoints to route
|
||||
gdata.route.points = {}
|
||||
local wp1 = reaper.createInitialWP(left, unit.alt, unit.speed)
|
||||
local wp1 = reaper.createInitialWP(left, unit.alt, unit.speed, theZone.invisible)
|
||||
gdata.route.points[1] = wp1
|
||||
local wp2 = dcsCommon.createSimpleRoutePointData(right, unit.alt, unit.speed)
|
||||
gdata.route.points[2] = wp2
|
||||
@ -195,75 +197,89 @@ function reaper.cleanUp(theZone)
|
||||
theZone.theSpot = nil
|
||||
end
|
||||
|
||||
function reaper.createReaperTask(alt, speed, target, theZone)
|
||||
local task = {
|
||||
function reaper.createReaperTask(alt, speed, target, theZone, invisible)
|
||||
if not invisible then invisible = false end
|
||||
local task = {
|
||||
["id"] = "ComboTask",
|
||||
["params"] = {
|
||||
["tasks"] = {
|
||||
[1] = {
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["id"] = "FAC",
|
||||
["number"] = 1,
|
||||
["params"] =
|
||||
{}, -- end of ["params"]
|
||||
["auto"] = false,
|
||||
["id"] = "WrappedAction",
|
||||
["name"] = "INV",
|
||||
["enabled"] = true,
|
||||
["params"] = {
|
||||
["action"] = {
|
||||
["id"] = "SetInvisible",
|
||||
["params"] = {
|
||||
["value"] = invisible,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [1]
|
||||
|
||||
[2] = {
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["id"] = "FAC",
|
||||
["number"] = 2,
|
||||
["params"] =
|
||||
{}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] = {
|
||||
["enabled"] = true,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["number"] = 3,
|
||||
["params"] = {
|
||||
["action"] = {
|
||||
["id"] = "EPLRS",
|
||||
["params"] = {
|
||||
["value"] = true,
|
||||
["groupId"] = 1,
|
||||
["groupId"] = 1, -- <- looks bad
|
||||
}, -- end of ["params"]
|
||||
}, -- end of ["action"]
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [2]
|
||||
[3] = {
|
||||
}, -- end of [3]
|
||||
[4] = {
|
||||
["enabled"] = true,
|
||||
["auto"] = false,
|
||||
["id"] = "Orbit",
|
||||
["number"] = 3,
|
||||
["number"] = 4,
|
||||
["params"] = {
|
||||
["altitude"] = alt,
|
||||
["pattern"] = "Race-Track",
|
||||
["speed"] = speed,
|
||||
}, -- end of ["params"]
|
||||
}, -- end of [3]
|
||||
}, -- end of [4]
|
||||
}, -- end of ["tasks"]
|
||||
}, -- end of ["params"]
|
||||
} -- end of ["task"]
|
||||
if theTarget and theZone then
|
||||
-- local gID = theTarget:getGroup():getID()
|
||||
local gID = theTarget:getID() -- NOTE: theTarget is a GROUP!!!!
|
||||
local task4 = {
|
||||
local task4 = { -- now task5 after we added invisibility
|
||||
["enabled"] = true,
|
||||
["auto"] = false,
|
||||
["id"] = "FAC_AttackGroup",
|
||||
["number"] = 4,
|
||||
["number"] = 5,
|
||||
["params"] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["number"] = 5,
|
||||
["designation"] = "No",
|
||||
["modulation"] = 0,
|
||||
["groupId"] = gID,
|
||||
-- ["callname"] = 1,
|
||||
-- ["datalink"] = true,
|
||||
["weaponType"] = 0, -- 9663676414,
|
||||
["frequency"] = theZone.freq, -- 133000000,
|
||||
}, -- end of ["params"]
|
||||
} -- end of [4]
|
||||
task.params.tasks[4] = task4
|
||||
} -- end of [5]
|
||||
task.params.tasks[5] = task4
|
||||
end
|
||||
return task
|
||||
end
|
||||
|
||||
function reaper.createInitialWP(p, alt, speed) -- warning: target must be a GROUP
|
||||
function reaper.createInitialWP(p, alt, speed, invisible) -- warning: target must be a GROUP
|
||||
if not invisible then invisible = false end
|
||||
local wp = {
|
||||
["alt"] = alt,
|
||||
["action"] = "Turning Point",
|
||||
@ -282,7 +298,7 @@ function reaper.createInitialWP(p, alt, speed) -- warning: target must be a GROU
|
||||
["formation_template"] = "",
|
||||
} -- end of wp
|
||||
|
||||
wp.task = reaper.createReaperTask(alt, speed) -- no zone, no target
|
||||
wp.task = reaper.createReaperTask(alt, speed, nil, nil, invisible) -- no zone, no target
|
||||
return wp
|
||||
end
|
||||
|
||||
@ -319,7 +335,7 @@ function reaper.setTarget(theZone, theTarget, cycled)
|
||||
|
||||
-- now make tracking the group the drone's task
|
||||
local theGroup = theTarget:getGroup()
|
||||
local theTask = reaper.createReaperTask(theZone.alt, theZone.speed, theGroup, theZone) -- create full FAC task with orbit and group engage
|
||||
local theTask = reaper.createReaperTask(theZone.alt, theZone.speed, theGroup, theZone, theZone.invisible) -- create full FAC task with orbit and group engage
|
||||
local theController = theZone.theUav:getController()
|
||||
if not theController then
|
||||
trigger.action.outText("+++Rpr: UAV has no controller, getting group")
|
||||
@ -927,4 +943,5 @@ end
|
||||
--[[--
|
||||
Idea: mobile launch vehicle, zone follows apc around. Can even be hauled along with hook
|
||||
|
||||
todo: make reaper invisible by attribute
|
||||
--]]--
|
||||
|
||||
@ -1,108 +1,74 @@
|
||||
cfxSmokeZone = {}
|
||||
cfxSmokeZone.version = "2.0.1"
|
||||
cfxSmokeZone.version = "3.0.0"
|
||||
cfxSmokeZone.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
2.0.0 - clean up
|
||||
2.0.1 - deprecating "f?"
|
||||
|
||||
Version History
|
||||
3.0.0 - now supports immediate smoke stop
|
||||
- supports persistence
|
||||
- code cleanup
|
||||
--]]--
|
||||
cfxSmokeZone.smokeZones = {}
|
||||
cfxSmokeZone.updateDelay = 5 * 60 -- every 5 minutes
|
||||
|
||||
function cfxSmokeZone.processSmokeZone(aZone)
|
||||
local rawVal = aZone:getStringFromZoneProperty("smoke", "green")
|
||||
rawVal = rawVal:lower()
|
||||
local theColor = 0
|
||||
if rawVal == "red" or rawVal == "1" then theColor = 1 end
|
||||
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.smokeColor = aZone:getSmokeColorNumberFromZoneProperty("smoke", "green")--theColor
|
||||
aZone.smokeAlt = aZone:getNumberFromZoneProperty("altitude", 1)
|
||||
aZone.smokeName = aZone.name .. "-s-" .. dcsCommon.numberUUID()
|
||||
if aZone:hasProperty("alt") then
|
||||
aZone.smokeAlt = aZone:getNumberFromZoneProperty("alt", 1)
|
||||
elseif aZone:hasProperty("agl") then
|
||||
aZone.smokeAlt = aZone:getNumberFromZoneProperty("agl", 1)
|
||||
end
|
||||
|
||||
-- paused
|
||||
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
||||
|
||||
if aZone:hasProperty("f?") then
|
||||
aZone.onFlag = aZone:getStringFromZoneProperty("f?", "*<none>")
|
||||
trigger.action.outText("+++smokeZones: WARNING: smoke zone <" .. aZone.name .. "> uses deprecated attribute 'f?' - use 'startSmoke?' instead.", 30)
|
||||
elseif aZone:hasProperty("startSmoke?") then
|
||||
aZone.onFlag = aZone:getStringFromZoneProperty("startSmoke?", "none")
|
||||
end
|
||||
|
||||
aZone.paused = aZone:getBoolFromZoneProperty("paused", false)
|
||||
aZone.onFlag = aZone:getStringFromZoneProperty("startSmoke?", "none")
|
||||
if aZone.onFlag then
|
||||
aZone.onFlagVal = aZone:getFlagValue(aZone.onFlag) -- save last value
|
||||
end
|
||||
|
||||
if aZone:hasProperty("stopSmoke?") then
|
||||
aZone.smkStopFlag = aZone:getStringFromZoneProperty("stopSmoke?", "<none>")
|
||||
aZone.smkLastStopFlag = aZone:getFlagValue(aZone.smkStopFlag)
|
||||
end
|
||||
|
||||
aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
|
||||
if aZone:hasProperty("smokeTriggerMethod") then
|
||||
aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "smokeTriggerMethod", "change")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function cfxSmokeZone.addSmokeZone(aZone)
|
||||
table.insert(cfxSmokeZone.smokeZones, aZone)
|
||||
end
|
||||
|
||||
function cfxSmokeZone.addSmokeZoneWithColor(aZone, aColor, anAltitude, paused, onFlag)
|
||||
if not aColor then aColor = 0 end -- default green
|
||||
if not anAltitude then anAltitude = 5 end
|
||||
if not aZone then return end
|
||||
if not paused then paused = false end
|
||||
|
||||
aZone.smokeColor = aColor
|
||||
aZone.smokeAlt = anAltitude
|
||||
aZone.paused = paused
|
||||
|
||||
if onFlag then
|
||||
aZone.onFlag = onFlag
|
||||
aZone.onFlagVal = cfxZones.getFlagValue(aZone.onFlag, aZone) -- trigger.misc.getUserFlag(onFlag)
|
||||
end
|
||||
|
||||
cfxSmokeZone.addSmokeZone(aZone) -- add to update loop
|
||||
if not paused then
|
||||
cfxSmokeZone.startSmoke(aZone)
|
||||
end
|
||||
|
||||
function cfxSmokeZone.getSmokeZoneNamed(aName)
|
||||
if not aName then return end
|
||||
local aName = string.upper(aName)
|
||||
for idx, theZone in pairs(cfxSmokeZone.smokeZones) do
|
||||
if theZone.name == aName then return theZone end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function cfxSmokeZone.startSmoke(aZone)
|
||||
if type(aZone) == "string" then
|
||||
aZone = cfxZones.getZoneByName(aZone)
|
||||
end
|
||||
if not aZone then return end
|
||||
if not aZone.smokeColor then return end
|
||||
-- remove old smoke if running
|
||||
if cfxSmokeZone.verbose or aZone.verbose then trigger.action.outText("+++smk: starting zone <" .. aZone.name .. "> smoke with name <" .. aZone.smokeName .. ">", 30) end
|
||||
trigger.action.effectSmokeStop(aZone.smokeName)
|
||||
aZone.paused = false
|
||||
cfxZones.markZoneWithSmoke(aZone, 0, 0, aZone.smokeColor, aZone.smokeAlt)
|
||||
aZone:markZoneWithSmoke(0, 0, aZone.smokeColor, aZone.smokeAlt, aZone.smokeName)
|
||||
end
|
||||
|
||||
function cfxSmokeZone.removeSmokeZone(aZone)
|
||||
if type(aZone) == "string" then
|
||||
aZone = cfxZones.getZoneByName(aZone)
|
||||
end
|
||||
function cfxSmokeZone.stopSmoke(aZone)
|
||||
if not aZone then return end
|
||||
|
||||
-- now create new table
|
||||
if cfxSmokeZone.verbose or aZone.verbose then trigger.action.outText("+++smk: ENDING zone <" .. aZone.name .. ">'s smoke with name <" .. aZone.smokeName .. ">", 30) end
|
||||
trigger.action.effectSmokeStop(aZone.smokeName)
|
||||
aZone.paused = true
|
||||
end
|
||||
|
||||
function cfxSmokeZone.removeSmokeZone(aZone)
|
||||
if not aZone then return end
|
||||
local filtered = {}
|
||||
for idx, theZone in pairs(cfxSmokeZone.smokeZones) do
|
||||
if theZone ~= aZone then
|
||||
@ -112,12 +78,9 @@ function cfxSmokeZone.removeSmokeZone(aZone)
|
||||
cfxSmokeZone.smokeZones = filtered
|
||||
end
|
||||
|
||||
|
||||
function cfxSmokeZone.update()
|
||||
-- call me in a couple of minutes to 'rekindle'
|
||||
-- 'rekindle' all smoke after 5 mins
|
||||
timer.scheduleFunction(cfxSmokeZone.update, {}, timer.getTime() + cfxSmokeZone.updateDelay)
|
||||
|
||||
-- re-smoke all zones after delay
|
||||
for idx, aZone in pairs(cfxSmokeZone.smokeZones) do
|
||||
if not aZone.paused and aZone.smokeColor then
|
||||
cfxSmokeZone.startSmoke(aZone)
|
||||
@ -125,52 +88,72 @@ function cfxSmokeZone.update()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function cfxSmokeZone.checkFlags()
|
||||
timer.scheduleFunction(cfxSmokeZone.checkFlags, {}, timer.getTime() + 1) -- every second
|
||||
for idx, aZone in pairs(cfxSmokeZone.smokeZones) do
|
||||
|
||||
if aZone.paused and aZone.onFlagVal then
|
||||
-- see if this changed
|
||||
if cfxZones.testZoneFlag(aZone, aZone.onFlag, aZone.smokeTriggerMethod, "onFlagVal") then
|
||||
cfxSmokeZone.startSmoke(aZone)
|
||||
end
|
||||
end
|
||||
|
||||
if aZone.smkStopFlag then
|
||||
if cfxZones.testZoneFlag(aZone, aZone.smkStopFlag, aZone.smokeTriggerMethod, "smkLastStopFlag") then
|
||||
aZone.paused = true -- will no longer re-smoke on update
|
||||
cfxSmokeZone.stopSmoke(aZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSmokeZone.start()
|
||||
if not dcsCommon.libCheck("cfx Smoke Zones", cfxSmokeZone.requiredLibs) then
|
||||
return false
|
||||
function cfxSmokeZone.saveData()
|
||||
local theData = {}
|
||||
for idx, theZone in pairs(cfxSmokeZone.smokeZones) do
|
||||
local entry = {}
|
||||
entry.paused = theZone.paused
|
||||
theData[theZone.name] = entry
|
||||
end
|
||||
-- save current log. simple clone
|
||||
return theData, cfxSmokeZone.sharedData -- second val only if shared
|
||||
end
|
||||
|
||||
function cfxSmokeZone.loadData()
|
||||
if not persistence then return end
|
||||
local theData = persistence.getSavedDataForModule("smokeZones", cfxSmokeZone.sharedData)
|
||||
if not theData then
|
||||
if cfxSmokeZone.verbose then trigger.action.outText("+++smk: no save date received, skipping.", 30) end
|
||||
return
|
||||
end
|
||||
|
||||
-- collect all zones with 'smoke' attribute
|
||||
for name, entry in pairs(theData) do
|
||||
local theZone = cfxSmokeZone.getSmokeZoneNamed(name)
|
||||
if theZone then
|
||||
theZone.paused = entry.paused
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function cfxSmokeZone.start()
|
||||
if not dcsCommon.libCheck("cfx Smoke Zones", cfxSmokeZone.requiredLibs) then return false end
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("smoke")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
cfxSmokeZone.processSmokeZone(aZone)
|
||||
cfxSmokeZone.addSmokeZone(aZone)
|
||||
end
|
||||
if persistence then -- sign up for persistence
|
||||
callbacks = {}
|
||||
callbacks.persistData = cfxSmokeZone.saveData
|
||||
persistence.registerModule("smokeZones", callbacks)
|
||||
-- now load my data
|
||||
persistence.loadData() -- will start with update
|
||||
end
|
||||
|
||||
-- start update loop
|
||||
-- start update and checkflag loops
|
||||
cfxSmokeZone.update() -- also starts all unpaused
|
||||
|
||||
-- start check loop in one second
|
||||
timer.scheduleFunction(cfxSmokeZone.checkFlags, {}, timer.getTime() + 1)
|
||||
|
||||
-- say hi
|
||||
|
||||
trigger.action.outText("cfx smoke zones v" .. cfxSmokeZone.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's go
|
||||
if not cfxSmokeZone.start() then
|
||||
trigger.action.outText("cf/x Smoke Zones aborted: missing libraries", 30)
|
||||
trigger.action.outText("cf/x Smoke Zones failed to start", 30)
|
||||
cfxSmokeZone = nil
|
||||
end
|
||||
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - colored smoke switcher.miz
Normal file
BIN
tutorial & demo missions/demo - colored smoke switcher.miz
Normal file
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user