diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index e333b19..22f9892 100644 Binary files a/Doc/DML Documentation.pdf and b/Doc/DML Documentation.pdf differ diff --git a/Doc/DML Quick Reference.pdf b/Doc/DML Quick Reference.pdf index d81b7a6..e8b80d2 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/cfxSmokeZones.lua b/modules/cfxSmokeZones.lua index 56fffc0..0832749 100644 --- a/modules/cfxSmokeZones.lua +++ b/modules/cfxSmokeZones.lua @@ -1,5 +1,5 @@ cfxSmokeZone = {} -cfxSmokeZone.version = "1.1.3" +cfxSmokeZone.version = "1.2.0" cfxSmokeZone.requiredLibs = { "dcsCommon", -- always "cfxZones", -- Zones, of course @@ -19,13 +19,14 @@ cfxSmokeZone.requiredLibs = { 1.1.1 - stopSmoke? input 1.1.2 - 'agl', 'alt' synonymous for altitude to keep in line with fireFX 1.1.3 - corrected smokeTriggerMethod in zone definition + 1.2.0 - first OOP guinea pig. --]]-- cfxSmokeZone.smokeZones = {} cfxSmokeZone.updateDelay = 5 * 60 -- every 5 minutes function cfxSmokeZone.processSmokeZone(aZone) - local rawVal = cfxZones.getStringFromZoneProperty(aZone, "smoke", "green") + local rawVal = aZone:getStringFromZoneProperty("smoke", "green") rawVal = rawVal:lower() local theColor = 0 if rawVal == "red" or rawVal == "1" then theColor = 1 end @@ -37,38 +38,38 @@ function cfxSmokeZone.processSmokeZone(aZone) end aZone.smokeColor = theColor - aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "altitude", 1) - if cfxZones.hasProperty(aZone, "alt") then - aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "alt", 1) - elseif cfxZones.hasProperty(aZone, "agl") then - aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "agl", 1) + aZone.smokeAlt = aZone:getNumberFromZoneProperty("altitude", 1) + 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 = cfxZones.getBoolFromZoneProperty(aZone, "paused", false) + aZone.paused = aZone:getBoolFromZoneProperty("paused", false) -- f? query flags - if cfxZones.hasProperty(aZone, "f?") then - aZone.onFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "*") - elseif cfxZones.hasProperty(aZone, "startSmoke?") then - aZone.onFlag = cfxZones.getStringFromZoneProperty(aZone, "startSmoke?", "none") + if aZone:hasProperty("f?") then + aZone.onFlag = aZone:getStringFromZoneProperty("f?", "*") + elseif aZone:hasProperty("startSmoke?") then + aZone.onFlag = aZone:getStringFromZoneProperty("startSmoke?", "none") end if aZone.onFlag then - aZone.onFlagVal = cfxZones.getFlagValue(aZone.onFlag, aZone) -- save last value + aZone.onFlagVal = aZone:getFlagValue(aZone.onFlag) -- save last value end - if cfxZones.hasProperty(aZone, "stopSmoke?") then - aZone.smkStopFlag = cfxZones.getStringFromZoneProperty(aZone, "stopSmoke?", "") - aZone.smkLastStopFlag = cfxZones.getFlagValue(aZone.smkStopFlag, aZone) + if aZone:hasProperty("stopSmoke?") then + aZone.smkStopFlag = aZone:getStringFromZoneProperty("stopSmoke?", "") + aZone.smkLastStopFlag = aZone:getFlagValue(aZone.smkStopFlag) end -- watchflags: -- triggerMethod - aZone.smokeTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change") + aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "triggerMethod", "change") - if cfxZones.hasProperty(aZone, "smokeTriggerMethod") then - aZone.smokeTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "smokeTriggerMethod", "change") + if aZone:hasProperty("smokeTriggerMethod") then + aZone.smokeTriggerMethod = aZone:getStringFromZoneProperty( "smokeTriggerMethod", "change") end end diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index 36f2b84..7619c46 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -1,5 +1,5 @@ cfxZones = {} -cfxZones.version = "3.1.3" +cfxZones.version = "4.0.0" -- cf/x zone management module -- reads dcs zones and makes them accessible and mutable @@ -137,8 +137,44 @@ cfxZones.version = "3.1.3" - new getZoneVolume() - offsetZone also updates zone bounds when moving zones - corrected bug in calculateZoneBounds() - +- 4.0.0 - dmlZone OOP API started + - code revision / refactoring + - moved createPoint and copxPoint to dcsCommon, added bridging code + - re-routed all createPoint() invocations to dcsCommon + - removed anyPlayerInZone() because of cfxPlayer dependency + - numberArrayFromString() moved to dcsCommon, bridged + - flagArrayFromString() moved to dcsCommon, bridged + - doPollFlag() can differentiate between number method and string method + to enable passing an immediate negative value + - getNumberFromZoneProperty() enforces number return even on default + - immediate method switched to preceeding '#', to resolve conflict witzh + negative numbers, backwards compatibility with old (dysfunctional) method --]]-- + +-- +-- ==================== +-- OOP dmlZone API HERE +-- ==================== +-- + +dmlZone = {} +function dmlZone:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + self.name = "dmlZone raw" + self.isCircle = false + self.isPoly = false + self.radius = 0 + self.poly = {} + self.bounds = {} + self.properties = {} + return o +end + +-- +-- CLASSIC INTERFACE +-- cfxZones.verbose = false cfxZones.caseSensitiveProperties = false -- set to true to make property names case sensitive cfxZones.ups = 1 -- updates per second. updates moving zones @@ -215,7 +251,7 @@ function cfxZones.readFromDCS(clearfirst) for i, dcsZone in pairs(env.mission.triggers.zones) do if type(dcsZone) == 'table' then -- hint taken from MIST: verify type when reading from dcs -- dcs data is like a box of chocolates... - local newZone = {} + local newZone = dmlZone:new(nil) -- WAS: {} -- OOP introduction July 2023 -- name, converted to upper is used only for indexing -- the original name remains untouched newZone.dcsZone = dcsZone @@ -245,11 +281,11 @@ function cfxZones.readFromDCS(clearfirst) -- calculate the zone's real position by accessing the unit's MX data -- as precached by dcsCommon local ux, uy = dcsCommon.getUnitStartPosByID(dcsZone.linkUnit) - newZone.point = cfxZones.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) - newZone.dcsOrigin = cfxZones.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) + newZone.point = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) + newZone.dcsOrigin = dcsCommon.createPoint(ux + dcsZone.x, 0, uy + dcsZone.y) else - newZone.point = cfxZones.createPoint(dcsZone.x, 0, dcsZone.y) - newZone.dcsOrigin = cfxZones.createPoint(dcsZone.x, 0, dcsZone.y) + 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 @@ -324,10 +360,10 @@ function cfxZones.calculateZoneBounds(theZone) local radius = theZone.radius -- dcs uses z+ is down on map -- upper left is center - radius - bounds.ul = cfxZones.createPoint(center.x - radius, 0, center.z - radius) - bounds.ur = cfxZones.createPoint(center.x + radius, 0, center.z - radius) - bounds.ll = cfxZones.createPoint(center.x - radius, 0, center.z + radius) - bounds.lr = cfxZones.createPoint(center.x + radius, 0, center.z + radius) + bounds.ul = dcsCommon.createPoint(center.x - radius, 0, center.z - radius) + 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) elseif theZone.isPoly then local poly = theZone.poly -- ref copy! @@ -370,15 +406,23 @@ function cfxZones.calculateZoneBounds(theZone) end -function cfxZones.createPoint(x, y, z) +function dmlZone:calculateZoneBounds() + cfxZones.calculateZoneBounds(self) +end + +function cfxZones.createPoint(x, y, z) -- bridge to dcsCommon, backward comp. + return dcsCommon.createPoint(x, y, z) +--[[-- local newPoint = {} newPoint.x = x newPoint.y = y newPoint.z = z - return newPoint + return newPoint --]]-- end -function cfxZones.copyPoint(inPoint) +function cfxZones.copyPoint(inPoint) -- bridge to dcsCommon, backward comp. + return dcsCommon.copyPoint(inPoint) +--[[-- local newPoint = {} newPoint.x = inPoint.x newPoint.y = inPoint.y @@ -388,16 +432,22 @@ function cfxZones.copyPoint(inPoint) else newPoint.z = inPoint.y end - return newPoint + return newPoint --]]-- end function cfxZones.createHeightCorrectedPoint(inPoint) -- this should be in dcsCommon - local cP = cfxZones.createPoint(inPoint.x, land.getHeight({x=inPoint.x, y=inPoint.z}),inPoint.z) + local cP = dcsCommon.createPoint(inPoint.x, land.getHeight({x=inPoint.x, y=inPoint.z}),inPoint.z) return cP end function cfxZones.getHeightCorrectedZonePoint(theZone) - return cfxZones.createHeightCorrectedPoint(theZone.point) + local thePoint = cfxZone.getPoint(theZone) + return cfxZones.createHeightCorrectedPoint(thePoint) +end + +function dmlZone:getHeightCorrectedZonePoint() + local thePoint = self:getPoint() + return dcsCommon.createPoint(thePoint.x, land.getHeight({x=thePoint.x, y=thePoint.z}),thePoint.z) end function cfxZones.createPointFromPoint(inPoint) @@ -405,7 +455,7 @@ function cfxZones.createPointFromPoint(inPoint) end function cfxZones.createPointFromDCSPoint(inPoint) - return cfxZones.createPoint(inPoint.x, 0, inPoint.y) + return dcsCommon.createPoint(inPoint.x, 0, inPoint.y) end @@ -413,7 +463,7 @@ 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) local z = math.random(bounds.ll.z, ur.z) - return cfxZones.createPoint(x, 0, z) + return dcsCommon.createPoint(x, 0, z) end function cfxZones.createRandomPointOnZoneBoundary(theZone) @@ -427,6 +477,10 @@ function cfxZones.createRandomPointOnZoneBoundary(theZone) end end +function dmlZone:createRandomPointOnZoneBoundary() + return cfxZones.createRandomPointOnZoneBoundary(self) +end + function cfxZones.createRandomPointInZone(theZone) if not theZone then return nil end if theZone.isPoly then @@ -438,11 +492,22 @@ function cfxZones.createRandomPointInZone(theZone) end end +function dmlZone:createRandomPointInZone() + local loc, dx, dy = cfxZones.createRandomPointInZone(self) + return loc, dx, dy +end + + function cfxZones.randomPointInZone(theZone) local loc, dx, dy = cfxZones.createRandomPointInZone(theZone) return loc, dx, dy end +function dmlZone:randomPointInZone() + local loc, dx, dy = cfxZones.createRandomPointInZone(self) + return loc, dx, dy +end + function cfxZones.createRandomPointInCircleZone(theZone, onEdge) if not theZone.isCircle then trigger.action.outText("+++Zones: warning - createRandomPointInCircleZone called for non-circle zone <" .. theZone.name .. ">", 30) @@ -464,10 +529,15 @@ function cfxZones.createRandomPointInCircleZone(theZone, onEdge) return {x=px, y=0, z = pz}, dx, dz -- returns loc and offsets to theZone.point end +function dmlZone:createRandomPointInCircleZone(theZone, onEdge) + local p, dx, dz = cfxZones.createRandomPointInCircleZone(self, onEdge) + return p, dx, dz +end + function cfxZones.createRandomPointInPolyZone(theZone, onEdge) if not theZone.isPoly then trigger.action.outText("+++Zones: warning - createRandomPointInPolyZone called for non-poly zone <" .. theZone.name .. ">", 30) - return cfxZones.createPoint(theZone.point.x, 0, theZone.point.z) + return dcsCommon.createPoint(theZone.point.x, 0, theZone.point.z) end -- force update of all points local p = cfxZones.getPoint(theZone) @@ -505,11 +575,21 @@ function cfxZones.createRandomPointInPolyZone(theZone, onEdge) return polyPoint, polyPoint.x - p.x, polyPoint.z - p.z -- return loc, dx, dz end +function dmlZone:createRandomPointInPolyZone(onEdge) + local p, dx, dz = cfxZones.createRandomPointInPolyZone(self, onEdge) + return p, dx, dz +end + function cfxZones.addZoneToManagedZones(theZone) local upperName = string.upper(theZone.name) -- newZone.name:upper() cfxZones.zones[upperName] = theZone end +function dmlZone:addZoneToManagedZones() + local upperName = string.upper(self.name) -- newZone.name:upper() + cfxZones.zones[upperName] = self +end + function cfxZones.createUniqueZoneName(inName, searchSet) if not inName then return nil end if not searchSet then searchSet = cfxZones.zones end @@ -538,7 +618,7 @@ function cfxZones.createSimpleZone(name, location, radius, addToManaged) end function cfxZones.createCircleZone(name, x, z, radius) - local newZone = {} + local newZone = dmlZone:new(nil) -- {} OOP compatibility newZone.isCircle = true newZone.isPoly = false newZone.poly = {} @@ -546,7 +626,7 @@ function cfxZones.createCircleZone(name, x, z, radius) newZone.name = name newZone.radius = radius - newZone.point = cfxZones.createPoint(x, 0, z) + newZone.point = dcsCommon.createPoint(x, 0, z) -- props newZone.properties = {} @@ -606,7 +686,7 @@ function cfxZones.createSimpleQuadZone(name, location, points, addToManaged) end function cfxZones.createPolyZone(name, poly) -- poly must be array of point type -local newZone = {} + local newZone = dmlZone:new(nil) -- {} OOP compatibility newZone.isCircle = false newZone.isPoly = true newZone.poly = {} @@ -743,11 +823,18 @@ function cfxZones.isPointInsideZone(thePoint, theZone, radiusIncrease) trigger.action.outText("isPointInsideZone: Unknown zone type for " .. outerZone.name, 10) end +function dmlZone:isPointInsideZone(thePoint, radiusIncrease) -- warning: param order! + return cfxZones.isPointInsideZone(thePoint, self, radiusIncrease) +end + -- isZoneInZone returns true if center of innerZone is inside outerZone function cfxZones.isZoneInsideZone(innerZone, outerZone) - return cfxZones.isPointInsideZone(innerZone.point, outerZone) + local p = cfxZones.getPoint(innerZone) + return cfxZones.isPointInsideZone(p, outerZone) +end - +function dmlZone:isZoneInsideZone(outerZone) + return cfxZones.isPointInsideZone(self:getPoint(), outerZone) end function cfxZones.getZonesContainingPoint(thePoint, testZones) -- return array @@ -797,6 +884,11 @@ function cfxZones.getAllZonesInsideZone(superZone, testZones) -- returnes array! return containedZones end +function dmlZone:getAllZonesInsideZone(testZones) + return cfxZones.getAllZonesInsideZone(self, testZones) +end + + function cfxZones.getZonesWithAttributeNamed(attributeName, testZones) if not testZones then testZones = cfxZones.zones end @@ -862,6 +954,11 @@ function cfxZones.getZoneVolume(theZone) end end +function dmlZone:getZoneVolume() + return cfxZones.getZoneVolume(self) +end + + function cfxZones.declutterZone(theZone) if not theZone then return end local theVol = cfxZones.getZoneVolume(theZone) @@ -871,6 +968,14 @@ function cfxZones.declutterZone(theZone) world.removeJunk(theVol) end +function dmlZone:declutterZone() + local theVol = cfxZones.getZoneVolume(self) + if self.verbose then + dcsCommon.dumpVar2Str("vol", theVol) + end + world.removeJunk(theVol) +end + -- -- units / groups in zone -- @@ -890,6 +995,10 @@ function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be return inZones end +function dmlZone:allGroupsInZone(categ) + return cfxZones.allGroupsInZone(self, categ) +end + function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must be code -- warning: does not check for exiting! --trigger.action.outText("Zone " .. theZone.name .. " radius " .. theZone.radius, 30) @@ -906,6 +1015,10 @@ function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must return inZones end +function dmlZone:allGroupNamesInZone(categ) + return cfxZones.allGroupNamesInZone(self, categ) +end + function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, must be code -- warning: does not check for exiting! local inZones = {} @@ -927,6 +1040,11 @@ function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, mus return inZones end +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) @@ -976,6 +1094,9 @@ function cfxZones.isEntireGroupInZone(aGroup, aZone) return true end +function dmlZone:isEntireGroupInZone(aGroup) + return cfxZones.isEntireGroupInZone(aGroup, self) +end -- -- Zone Manipulation @@ -1005,27 +1126,31 @@ function cfxZones.offsetZone(theZone, dx, dz) end +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 cfxZones.offsetZone(theZone, dx, dz) end; +function dmlZone:moveZoneTo(x, z) + cfxZones.moveZoneTo(self, x, z) +end + function cfxZones.centerZoneOnUnit(theZone, theUnit) local thePoint = theUnit:getPoint() cfxZones.moveZoneTo(theZone, thePoint.x, thePoint.z) end +function dmlZone:centerZoneOnUnit(theUnit) + local thePoint = theUnit:getPoint() + self:moveZoneTo(thePoint.x, thePoint.z) +end ---[[ --- no longer makes sense with poly zones -function cfxZones.isZoneEntirelyInsideZone(innerZone, outerZone) - if (innerZone.radius > outerZone.radius) then return false end -- cant fit inside - local d = dcsCommon.dist(innerZone.point, outerZone.point) - local reducedR = outerZone.radius - innerZone.radius - return d < reducedR -end; ---]] function cfxZones.dumpZones(zoneTable) if not zoneTable then zoneTable = cfxZones.zones end @@ -1038,13 +1163,13 @@ function cfxZones.dumpZones(zoneTable) trigger.action.outText("#".. i .. ": " .. zone.name .. " of type " .. myType, 10) end - trigger.action.outText("Zones END", 10) + trigger.action.outText("Zones end", 10) end - +--[[-- moved to dcsCommon function cfxZones.stringStartsWith(theString, thePrefix) return theString:find(thePrefix) == 1 end - +--]]-- function cfxZones.keysForTable(theTable) local keyset={} local n=0 @@ -1088,7 +1213,7 @@ function cfxZones.zonesStartingWithName(prefix, searchSet) prefix = prefix:upper() -- all zones have UPPERCASE NAMES! THEY SCREAM AT YOU for name, zone in pairs(searchSet) do -- trigger.action.outText("testing " .. name:upper() .. " starts with " .. prefix , 30) - if cfxZones.stringStartsWith(name:upper(), prefix) then + if dcsCommon.stringStartsWith(name:upper(), prefix) then prefixZones[name] = zone -- note: ref copy! --trigger.action.outText("zone with prefix <" .. prefix .. "> found: " .. name, 10) end @@ -1114,8 +1239,6 @@ function cfxZones.zonesStartingWith(prefix, searchSet, debugging) end end - --trigger.action.outText("#debugZones is <" .. #debugZones .. ">", 10) - if (type(prefix) == "string") then return cfxZones.zonesStartingWithName(prefix, searchSet) end @@ -1214,6 +1337,10 @@ function cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt) trigger.action.smoke(point, smokeColor) end +function dmlZone:markZoneWithSmoke(dx, dz, smokeColor, alt) + cfxZones.markZoneWithSmoke(self, dx, dz, smokeColor, alt) +end + -- place a smoke marker in center of zone, offset by radius and degrees function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, alt) local rads = degrees * math.pi / 180 @@ -1222,12 +1349,21 @@ function cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor, a cfxZones.markZoneWithSmoke(theZone, dx, dz, smokeColor, alt) end +function dmlZone:markZoneWithSmokePolar(radius, degrees, smokeColor, alt) + cfxZones.markZoneWithSmokePolar(self, radius, degrees, smokeColor, alt) +end + -- place a smoke marker in center of zone, offset by radius and randomized degrees function cfxZones.markZoneWithSmokePolarRandom(theZone, radius, smokeColor) local degrees = math.random(360) cfxZones.markZoneWithSmokePolar(theZone, radius, degrees, smokeColor) end +function dmlZone:markZoneWithSmokePolarRandom(radius, smokeColor) + local degrees = math.random(360) + self:markZoneWithSmokePolar(radius, degrees, smokeColor) +end + function cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig) if not zoneArray then zoneArray = cfxZones.zones end for idx, theZone in pairs(zoneArray) do @@ -1280,6 +1416,11 @@ function cfxZones.pointInZone(thePoint, theZone, useOrig) return false end +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 @@ -1287,7 +1428,15 @@ function cfxZones.unitInZone(theUnit, theZone) -- pointInZone does update local thePoint = theUnit:getPoint() return cfxZones.pointInZone(thePoint, theZone) - +end + +function dmlZone:unitInZone(theUnit) + if not (theUnit) then return false, 0, 0 end + if not (theUnit:isExist()) then return false, 0, 0 end + -- force zone update if it is linked to another zone + -- pointInZone does update + local thePoint = theUnit:getPoint() + return self:pointInZone(thePoint) end -- returns all units of the input set that are inside the zone @@ -1304,6 +1453,17 @@ function cfxZones.unitsInZone(theUnits, theZone) return zoneUnits end +function dmlZone:unitsInZone(theUnits) + if not theUnits then return {} end + local zoneUnits = {} + for index, aUnit in pairs(theUnits) do + if self:unitInZone(aUnit) then + table.insert(zoneUnits, aUnit) + end + end + return zoneUnits +end + function cfxZones.closestUnitToZoneCenter(theUnits, theZone) -- does not care if they really are in zone. call unitsInZone first -- if you need to have them filtered @@ -1311,15 +1471,24 @@ function cfxZones.closestUnitToZoneCenter(theUnits, theZone) if not theUnits then return nil end if #theUnits == 0 then return nil end local closestUnit = theUnits[1] + local zP = cfxZones.getPoint(theZone) + local smallestDist = math.huge for i=2, #theUnits do local aUnit = theUnits[i] - if dcsCommon.dist(theZone.point, closestUnit:getPoint()) > dcsCommon.dist(theZone.point, aUnit:getPoint()) then + local currDist = dcsCommon.dist(zP, aUnit:getPoint()) + if smallestDist > currDelta then closestUnit = aUnit + smallestDist = currDist end end return closestUnit end +function dmlZone:closestUnitToZoneCenter(theUnits) + return cfxZones.closestUnitToZoneCenter(theUnits, self) +end + +--[[ function cfxZones.anyPlayerInZone(theZone) -- returns first player it finds for pname, pinfo in pairs(cfxPlayer.playerDB) do local playerUnit = pinfo.unit @@ -1330,6 +1499,7 @@ function cfxZones.anyPlayerInZone(theZone) -- returns first player it finds return false, nil end +--]]-- -- grow zone function cfxZones.growZone() @@ -1405,6 +1575,10 @@ function cfxZones.pulseFlag(theFlag, method, theZone) timer.scheduleFunction(cfxZones.unPulseFlag, args, timer.getTime() + delay) end +function dmlZone:pulseFlag(theFlag, method) + cfxZones.pulseFlag(theFlag, method, self) +end + function cfxZones.unPulseFlag(args) local theZone = args.theZone local method = args.method @@ -1447,32 +1621,56 @@ function cfxZones.evalRemainder(remainder) return rNum end -function cfxZones.doPollFlag(theFlag, method, theZone) - if cfxZones.verbose then - trigger.action.outText("+++zones: polling flag " .. theFlag .. " with " .. method, 30) - end - +function cfxZones.doPollFlag(theFlag, method, theZone) -- no OOP equivalent + -- WARNING: + -- if method is a number string, it will be interpreted as follows: + -- positive number: set immediate + -- negative: decrement by amouint 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 + mt = "string" + elseif mt ~= "string" then + 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) - val = tonumber(method) - if dcsCommon.stringStartsWith(method, "+") or dcsCommon.stringStartsWith(method, "-") + val = tonumber(method) -- see if val can be directly converted + if dcsCommon.stringStartsWith(method, "+") or + dcsCommon.stringStartsWith(method, "-") then - -- skip this processing, a legal Lua val can start with "+" or "-" - -- but we interpret it as a method - else - if val then - cfxZones.setFlagValue(theFlag, val, theZone) - if cfxZones.verbose or theZone.verbose then - trigger.action.outText("+++zones: flag <" .. theFlag .. "> changed to #" .. val, 30) - end - return + -- skip this processing, a legal method can start with "+" or "-" + -- and we interpret it as a method to increase or decrease by amount + elseif (val ~= nil) then + -- provision to handle direct (positive) numbers (legacy support) + -- method can be converted to number but does not start with - or + + -- since all negative numbers start with '-' above guard will skip, positive will end up here + cfxZones.setFlagValue(theFlag, val, theZone) + if cfxZones.verbose or theZone.verbose then + trigger.action.outText("+++zones: flag <" .. theFlag .. "> changed to #" .. val, 30) end + return + else end - --trigger.action.outText("+++zones: polling " .. theZone.name .. " method " .. method .. " flag " .. theFlag, 30) + + if dcsCommon.stringStartsWith(method, "#") then + -- immediate value command. remove # and eval remainder + local remainder = dcsCommon.removePrefix(method, "#") + val = cfxZones.evalRemainder(remainder) -- always returens a number + cfxZones.setFlagValue(theFlag, val, theZone) + if theZone.verbose then + trigger.action.outText("+++zones: poll setting immediate <" .. theFlag .. "> in <" .. theZone.name .. "> to <" .. val .. ">", 30) + end + return + end + local currVal = cfxZones.getFlagValue(theFlag, theZone) if method == "inc" or method == "f+1" then --trigger.action.setUserFlag(theFlag, currVal + 1) @@ -1546,7 +1744,10 @@ function cfxZones.pollFlag(theFlag, method, theZone) -- a priority cfxZones.doPollFlag(aFlag, method, theZone) end - +end + +function dmlZone:pollFlag(theFlag, method) + cfxZones.pollFlag(theFlag, method, self) end function cfxZones.expandFlagName(theFlag, theZone) @@ -1628,6 +1829,11 @@ function cfxZones.setFlagValue(theFlag, theValue, theZone) trigger.action.setUserFlag(theFlag, theValue) end +function dmlZone:setFlagValue(theFlag, theValue) + cfxZones.setFlagValue(theFlag, theValue, self) +end + + function cfxZones.getFlagValue(theFlag, theZone) local zoneName = "" if not theZone or not theZone.name then @@ -1660,12 +1866,17 @@ function cfxZones.getFlagValue(theFlag, theZone) return tonumber(trigger.misc.getUserFlag(theFlag)) end +function dmlZone:getFlagValue(theFlag) + return cfxZones.getFlagValue(theFlag, self) +end +--[[-- function cfxZones.isMEFlag(inFlag) -- do NOT use me trigger.action.outText("+++zne: warning: deprecated isMEFlag", 30) return true -- returns true if inFlag is a pure positive number end +--]]-- function cfxZones.verifyMethod(theMethod, theZone) local lMethod = string.lower(theMethod) @@ -1728,6 +1939,10 @@ function cfxZones.verifyMethod(theMethod, theZone) return false end +function dmlZone:verifyMethod(theMethod) + return cfxZones.verifyMethod(theMethod, self) +end + -- method-based flag testing function cfxZones.evalFlagMethodImmediate(currVal, theMethod, theZone) -- immediate eval - does not look at last val. @@ -1816,6 +2031,11 @@ function cfxZones.evalFlagMethodImmediate(currVal, theMethod, theZone) return false end +function dmlZone:evalFlagMethodImmediate(currVal, theMethod, theZone) + return cfxZones.evalFlagMethodImmediate(currVal, theMethod, self) +end + + function cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone) -- return true/false based on theMethod's contraints -- simple constraints @@ -1914,7 +2134,7 @@ function cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone) return false end - +-- WARNING: testZoneFlag must also support non-dmlZone!!! function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName) -- returns two values: true/false method result, and curr value -- returns true if method constraints are met for flag theFlagName @@ -1965,113 +2185,21 @@ function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName) return testResult, currVal end -function cfxZones.numberArrayFromString(inString, default) - if not default then default = 0 end - if string.len(inString) < 1 then - trigger.action.outText("+++zne: empty numbers", 30) - return {default, } - end - if cfxZones.verbose then - trigger.action.outText("+++zne: processing <" .. inString .. ">", 30) - end - - local flags = {} - local rawElements = dcsCommon.splitString(inString, ",") - -- go over all elements - for idx, anElement in pairs(rawElements) do - anElement = dcsCommon.trim(anElement) - if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then - -- interpret this as a range - local theRange = dcsCommon.splitString(anElement, "-") - local lowerBound = theRange[1] - lowerBound = tonumber(lowerBound) - local upperBound = theRange[2] - upperBound = tonumber(upperBound) - if lowerBound and upperBound then - -- swap if wrong order - if lowerBound > upperBound then - local temp = upperBound - upperBound = lowerBound - lowerBound = temp - end - -- now add add numbers to flags - for f=lowerBound, upperBound do - table.insert(flags, tostring(f)) - end - else - -- bounds illegal - trigger.action.outText("+++zne: ignored range <" .. anElement .. "> (range)", 30) - end - else - -- single number - f = dcsCommon.trim(anElement) - f = tonumber(f) - if f then - table.insert(flags, f) - end - end - end - if #flags < 1 then flags = {default, } end - if cfxZones.verbose then - trigger.action.outText("+++zne: <" .. #flags .. "> flags total", 30) - end - return flags -end - -function cfxZones.flagArrayFromString(inString) --- original code from RND flag - if string.len(inString) < 1 then - trigger.action.outText("+++zne: empty flags", 30) - return {} - end - if cfxZones.verbose then - trigger.action.outText("+++zne: processing <" .. inString .. ">", 30) - end - - local flags = {} - local rawElements = dcsCommon.splitString(inString, ",") - -- go over all elements - for idx, anElement in pairs(rawElements) do - anElement = dcsCommon.trim(anElement) - if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then - -- interpret this as a range - local theRange = dcsCommon.splitString(anElement, "-") - local lowerBound = theRange[1] - lowerBound = tonumber(lowerBound) - local upperBound = theRange[2] - upperBound = tonumber(upperBound) - if lowerBound and upperBound then - -- swap if wrong order - if lowerBound > upperBound then - local temp = upperBound - upperBound = lowerBound - lowerBound = temp - end - -- now add add numbers to flags - for f=lowerBound, upperBound do - table.insert(flags, tostring(f)) - end - else - -- bounds illegal - trigger.action.outText("+++zne: ignored range <" .. anElement .. "> (range)", 30) - end - else - -- single number - f = dcsCommon.trim(anElement) -- DML flag upgrade: accept strings tonumber(anElement) - if f then - table.insert(flags, f) - - else - trigger.action.outText("+++zne: ignored element <" .. anElement .. "> (single)", 30) - end - end - end - if cfxZones.verbose then - trigger.action.outText("+++zne: <" .. #flags .. "> flags total", 30) - end - return flags +function dmlZone:testZoneFlag(theFlagName, theMethod, latchName) + local r, v = cfxZones.testZoneFlag(self, theFlagName, theMethod, latchName) + return r, v end +function cfxZones.numberArrayFromString(inString, default) -- bridge + return dcsCommon.numberArrayFromString(inString, default) +end + + +function cfxZones.flagArrayFromString(inString) -- dcsCommon bridge + return dcsCommon.flagArrayFromString(inString) +end + + -- -- =================== -- PROPERTY PROCESSING @@ -2102,6 +2230,10 @@ function cfxZones.getAllZoneProperties(theZone, caseInsensitive, numbersOnly) -- return props end +function dmlZone:getAllZoneProperties(caseInsensitive, numbersOnly) + return cfxZones.getAllZoneProperties(self, caseInsensitive, numbersOnly) +end + function cfxZones.extractPropertyFromDCS(theKey, theProperties) -- trim theKey = dcsCommon.trim(theKey) @@ -2148,10 +2280,34 @@ function cfxZones.getZoneProperty(cZone, theKey) return theVal end +function dmlZone:getZoneProperty(theKey) + if not theKey then + trigger.action.outText("+++zone: no property key in OOP getZoneProperty for zone " .. self.name, 30) + return nil + end + local props = self.properties + local theVal = cfxZones.extractPropertyFromDCS(theKey, props) + return theVal +end + + function cfxZones.getStringFromZoneProperty(theZone, theProperty, default) - if not default then default = "" end - local p = cfxZones.getZoneProperty(theZone, theProperty) +-- local p = cfxZones.getZoneProperty(theZone, theProperty) +-- OOP heavy duty test here + local p = theZone:getZoneProperty(theProperty) + if not p then return default end + if type(p) == "string" then + p = dcsCommon.trim(p) + if p == "" then p = default end + return p + end + return default -- warning. what if it was a number first? +end + +function dmlZone:getStringFromZoneProperty(theProperty, default) + if not default then default = "" end + local p = self:getZoneProperty(theProperty) if not p then return default end if type(p) == "string" then p = dcsCommon.trim(p) @@ -2164,12 +2320,16 @@ end function cfxZones.getMinMaxFromZoneProperty(theZone, theProperty) local p = cfxZones.getZoneProperty(theZone, theProperty) local theNumbers = dcsCommon.splitString(p, " ") - return tonumber(theNumbers[1]), tonumber(theNumbers[2]) - end -function cfxZones.randomInRange(minVal, maxVal) +function dmlZone:getMinMaxFromZoneProperty(theProperty) + local p = self:getZoneProperty(theProperty) + local theNumbers = dcsCommon.splitString(p, " ") + return tonumber(theNumbers[1]), tonumber(theNumbers[2]) +end + +function cfxZones.randomInRange(minVal, maxVal) -- should be moved to dcsCommon if maxVal < minVal then local t = minVal minVal = maxVal @@ -2178,7 +2338,7 @@ function cfxZones.randomInRange(minVal, maxVal) return cfxZones.randomDelayFromPositiveRange(minVal, maxVal) end -function cfxZones.randomDelayFromPositiveRange(minVal, maxVal) +function cfxZones.randomDelayFromPositiveRange(minVal, maxVal) -- should be moved to dcsCommon if not maxVal then return minVal end if not minVal then return maxVal end local delay = maxVal @@ -2230,6 +2390,12 @@ function cfxZones.getPositiveRangeFromZoneProperty(theZone, theProperty, default return lowerBound, upperBound end +function dmlZone:getPositiveRangeFromZoneProperty(theProperty, default, defaultmax) + local lo, up = cfxZones.getPositiveRangeFromZoneProperty(self, theProperty, default, defaultmax) + return lo, up +end + + function cfxZones.hasProperty(theZone, theProperty) if not theProperty then trigger.action.outText("+++zne: WARNING - hasProperty called with nil theProperty for zone <" .. theZone.name .. ">", 30) @@ -2276,6 +2442,51 @@ function cfxZones.hasProperty(theZone, theProperty) -- return foundIt ~= nil end +function dmlZone:hasProperty(theProperty) + if not theProperty then + trigger.action.outText("+++zne: WARNING - hasProperty called with nil theProperty for zone <" .. self.name .. ">", 30) + return false + end + local foundIt = self:getZoneProperty(theProperty) + if not foundIt then + -- check for possible forgotten or exchanged IO flags + if string.sub(theProperty, -1) == "?" then + local lessOp = theProperty:sub(1,-2) + if self:getZoneProperty(lessOp) ~= nil then + trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be missing a Query ('?') symbol", 30) + end + local lessPlus = lessOp .. "!" + if self:getZoneProperty(lessPlus) ~= nil then + trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be using '!' instead of '?' for input", 30) + end + return false + end + + if string.sub(theProperty, -1) == "!" then + local lessOp = theProperty:sub(1,-2) + if self:getZoneProperty(lessOp) ~= nil then + trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be missing a Bang! ('!') symbol", 30) + end + local lessPlus = lessOp .. "?" + if self:getZoneProperty(lessPlus) ~= nil then + trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be using '!' instead of '?' for input", 30) + end + return false + end + + if string.sub(theProperty, -1) == ":" then + local lessOp = theProperty:sub(1,-2) + if self:getZoneProperty(lessOp) ~= nil then + trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be missing a colon (':') at end", 30) + end + return false + end + + return false + end + return true +end + function cfxZones.getBoolFromZoneProperty(theZone, theProperty, defaultVal) if not defaultVal then defaultVal = false end if type(defaultVal) ~= "boolean" then @@ -2308,6 +2519,32 @@ function cfxZones.getBoolFromZoneProperty(theZone, theProperty, defaultVal) return theBool end +function dmlZone:getBoolFromZoneProperty(theProperty, defaultVal) + if not defaultVal then defaultVal = false end + if type(defaultVal) ~= "boolean" then + defaultVal = false + end + + local p = self:getZoneProperty(theProperty) + if not p then return defaultVal end + + -- make sure we compare so default always works when + -- answer isn't exactly the opposite + p = p:lower() + p = dcsCommon.trim(p) + if defaultVal == false then + -- only go true if exact match to yes or true + theBool = false + theBool = (p == 'true') or (p == 'yes') or p == "1" + return theBool + end + + local theBool = true + -- only go false if exactly no or false or "0" + theBool = (p ~= 'false') and (p ~= 'no') and (p ~= "0") + return theBool +end + function cfxZones.getCoalitionFromZoneProperty(theZone, theProperty, default) if not default then default = 0 end local p = cfxZones.getZoneProperty(theZone, theProperty) @@ -2335,12 +2572,59 @@ function cfxZones.getCoalitionFromZoneProperty(theZone, theProperty, default) return default end +function dmlZone:getCoalitionFromZoneProperty(theProperty, default) + if not default then default = 0 end + local p = self:getZoneProperty(theProperty) + if not p then return default end + if type(p) == "number" then -- can't currently really happen + if p == 1 then return 1 end + if p == 2 then return 2 end + return 0 + end + + if type(p) == "string" then + if p == "1" then return 1 end + if p == "2" then return 2 end + if p == "0" then return 0 end + + p = p:lower() + + if p == "red" then return 1 end + if p == "blue" then return 2 end + if p == "neutral" then return 0 end + if p == "all" then return 0 end + return default + end + + return default +end + function cfxZones.getNumberFromZoneProperty(theZone, theProperty, default) --TODO: trim string if not default then default = 0 end + default = tonumber(default) + if not default then default = 0 end -- enforce default numbner as well local p = cfxZones.getZoneProperty(theZone, theProperty) p = tonumber(p) - if not p then return default else return p end + if not p then p = default end +-- if theZone.verbose then +-- trigger.action.outText("+++zne: getNumberFromZoneProperty returns <" .. p .. "> for prop <" .. theProperty .. "> in zone <" .. theZone.name .. ">", 30) +-- end + return p +end + +function dmlZone:getNumberFromZoneProperty(theProperty, default) +--TODO: trim string + if not default then default = 0 end + default = tonumber(default) + if not default then default = 0 end -- enforce default numbner as well + local p = self:getZoneProperty(theProperty) + p = tonumber(p) + if not p then p = default end +-- if self.verbose then +-- trigger.action.outText("+++zne OOP: getNumberFromZoneProperty returns <" .. p .. "> for prop <" .. theProperty .. "> in zone <" .. self.name .. ">", 30) +-- end + return p end function cfxZones.getVectorFromZoneProperty(theZone, theProperty, minDims, defaultVal) @@ -2362,6 +2646,25 @@ function cfxZones.getVectorFromZoneProperty(theZone, theProperty, minDims, defau return nVec end +function dmlZone:getVectorFromZoneProperty(theProperty, minDims, defaultVal) + if not minDims then minDims = 0 end + if not defaultVal then defaultVal = 0 end + local s = self:getStringFromZoneProperty(theProperty, "") + local sVec = dcsCommon.splitString(s, ",") + local nVec = {} + for idx, numString in pairs (sVec) do + local n = tonumber(numString) + if not n then n = defaultVal end + table.insert(nVec, n) + end + -- make sure vector contains at least minDims values + while #nVec < minDims do + table.insert(nVec, defaultVal) + end + + return nVec +end + function cfxZones.getRGBVectorFromZoneProperty(theZone, theProperty, defaultVal) if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end @@ -2376,10 +2679,27 @@ function cfxZones.getRGBVectorFromZoneProperty(theZone, theProperty, defaultVal) if n < 0 then n = 0 end nVec[i] = n end - return nVec end +function dmlZone:getRGBVectorFromZoneProperty(theProperty, defaultVal) + if not defaultVal then defaultVal = {1.0, 1.0, 1.0} end + if #defaultVal ~=3 then defaultVal = {1.0, 1.0, 1.0} end + local s = self:getStringFromZoneProperty(theProperty, "") + local sVec = dcsCommon.splitString(s, ",") + local nVec = {} + for i = 1, 3 do + n = sVec[i] + if n then n = tonumber(n) end + if not n then n = defaultVal[i] end + if n > 1.0 then n = 1.0 end + if n < 0 then n = 0 end + nVec[i] = n + end + return nVec +end + + function cfxZones.getRGBAVectorFromZoneProperty(theZone, theProperty, defaultVal) if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end @@ -2405,6 +2725,31 @@ function cfxZones.getRGBAVectorFromZoneProperty(theZone, theProperty, defaultVal return nVec end +function dmlZone:getRGBAVectorFromZoneProperty(theProperty, defaultVal) + if not defaultVal then defaultVal = {1.0, 1.0, 1.0, 1.0} end + if #defaultVal ~=4 then defaultVal = {1.0, 1.0, 1.0, 1.0} end + local s = self:getStringFromZoneProperty(theProperty, "") + s = dcsCommon.trim(s) + if s:sub(1,1) == "#" then + -- it's probably a "#RRGGBBAA" format hex string + local hVec = dcsCommon.hexString2RGBA(s) + if hVec then return hVec end + end + + local sVec = dcsCommon.splitString(s, ",") + local nVec = {} + for i = 1, 4 do + n = sVec[i] + if n then n = tonumber(n) end + if not n then n = defaultVal[i] end + if n > 1.0 then n = 1.0 end + if n < 0 then n = 0 end + nVec[i] = n + end + + return nVec +end + function cfxZones.getRGBFromZoneProperty(theZone, theProperty, default) --if not default then default = {1.0, 1.0, 1.0} end -- white local rawRGB = cfxZones.getVectorFromZoneProperty(theZone, theProperty, 3, 1.0) @@ -2418,6 +2763,19 @@ function cfxZones.getRGBFromZoneProperty(theZone, theProperty, default) return retVal end +function dmlZone:getRGBFromZoneProperty(theProperty, default) + --if not default then default = {1.0, 1.0, 1.0} end -- white + local rawRGB = self:getVectorFromZoneProperty(theProperty, 3, 1.0) + local retVal = {} + for i = 1, 3 do + local cp = rawRGB[i] + if cp > 1.0 then cp = 1.0 end + if cp < 0 then cp = 0 end + retVal[i] = cp + end + return retVal +end + function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5 if not default then default = "red" end @@ -2440,6 +2798,27 @@ function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, defa return default end +function dmlZone:getSmokeColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5 + if not default then default = "red" end + local s = self:getStringFromZoneProperty(theProperty, default) + s = s:lower() + s = dcsCommon.trim(s) + -- check numbers + if (s == "0") then return "green" end + if (s == "1") then return "red" end + if (s == "2") then return "white" end + if (s == "3") then return "orange" end + if (s == "4") then return "blue" end + + if s == "green" or + s == "red" or + s == "white" or + s == "orange" or + s == "blue" then return s end + + return default +end + function cfxZones.getFlareColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5 if not default then default = "red" end local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default) @@ -2463,6 +2842,28 @@ function cfxZones.getFlareColorStringFromZoneProperty(theZone, theProperty, defa return default end +function dmlZone:getFlareColorStringFromZoneProperty(theProperty, default) -- smoke as 'red', 'green', or 1..5 + if not default then default = "red" end + local s = self:getStringFromZoneProperty(theProperty, default) + s = s:lower() + s = dcsCommon.trim(s) + -- check numbers + if (s == "rnd") then return "random" end + if (s == "0") then return "green" end + if (s == "1") then return "red" end + if (s == "2") then return "white" end + if (s == "3") then return "yellow" end + if (s == "-1") then return "random" end + + if s == "green" or + s == "red" or + s == "white" or + s == "yellow" or + s == "random" then + return s end + + return default +end -- -- Zone-based wildcard processing @@ -2476,6 +2877,11 @@ function cfxZones.processZoneStatics(inMsg, theZone) return inMsg end +function dmlZone:processZoneStatics(inMsg, theZone) + inMsg = inMsg:gsub("", self.name) + return inMsg +end + -- process , , , , function cfxZones.processSimpleZoneDynamics(inMsg, theZone, timeFormat, imperialUnits) if not inMsg then return "" end @@ -2767,6 +3173,21 @@ function cfxZones.getDCSOrigin(aZone) return o end +function dmlZone:getDCSOrigin() + local o = {} + if not self.dcsOrigin then + trigger.action.outText("dmlZone (OOP): no dcsOrigin defined for zone <" .. self.name .. ">", 30) + o.x = 0 + o.y = 0 + o.z = 0 + else + o.x = self.dcsOrigin.x + o.y = 0 + o.z = self.dcsOrigin.z + end + return o +end + function cfxZones.getLinkedUnit(theZone) if not theZone then return nil end if not theZone.linkedUnit then return nil end @@ -2774,6 +3195,12 @@ function cfxZones.getLinkedUnit(theZone) return theZone.linkedUnit end +function dmlZone:getLinkedUnit() + if not self.linkedUnit then return nil end + if not Unit.isExist(self.linkedUnit) then return nil end + return self.linkedUnit +end + function cfxZones.getPoint(aZone, getHeight) -- always works, even linked, returned point can be reused -- returned y (when using getHeight) is that of the land, else 0 if not getHeight then getHeight = false end @@ -2803,6 +3230,34 @@ function cfxZones.getPoint(aZone, getHeight) -- always works, even linked, retur return thePos end +function dmlZone:getPoint(getHeight) + if not getHeight then getHeight = false end + if self.linkedUnit then + local theUnit = self.linkedUnit + -- has a link. is link existing? + if Unit.isExist(theUnit) then + -- updates zone position + self:centerZoneOnUnit(theUnit) + local dx = self.dx + local dy = self.dy + if self.useHeading then + dx, dy = self:calcHeadingOffset(theUnit) + end + self:offsetZone(dx, dy) + end + end + local thePos = {} + thePos.x = self.point.x + thePos.z = self.point.z + if not getHeight then + thePos.y = 0 -- aZone.y + else + thePos.y = land.getHeight({x = thePos.x, y = thePos.z}) + end + + return thePos +end + function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!! theZone.linkedUnit = theUnit if not dx then dx = 0 end @@ -2826,6 +3281,28 @@ function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really --trigger.action.outText("Link setup: dx=<" .. dx .. ">, dy=<" .. dy .. "> unit original hdg = <" .. math.floor(57.2958 * unitHeading) .. ">", 30) end +function dmlZone:linkUnitToZone(theUnit, dx, dy) -- note: dy is really Z, don't get confused!!!! + self.linkedUnit = theUnit + if not dx then dx = 0 end + if not dy then dy = 0 end + self.dx = dx + self.dy = dy + self.rxy = math.sqrt(dx * dx + dy * dy) -- radius + local unitHeading = dcsCommon.getUnitHeading(theUnit) + local bearingOffset = math.atan2(dy, dx) -- rads + if bearingOffset < 0 then bearingOffset = bearingOffset + 2 * 3.141592 end + + local dPhi = bearingOffset - unitHeading + if dPhi < 0 then dPhi = dPhi + 2 * 3.141592 end + if (self.verbose and self.useHeading) then + trigger.action.outText("Zone <" .. self.name .. "> is at <" .. math.floor(57.2958 * dPhi) .. "> relative to unit heading", 30) + end + self.dPhi = dPhi -- constant delta between unit heading and + -- direction to zone + self.uHdg = unitHeading -- original unit heading to turn other + -- units if need be +end + function cfxZones.zonesLinkedToUnit(theUnit) -- returns all zones linked to this unit if not theUnit then return {} end local linkedZones = {} @@ -2856,6 +3333,17 @@ function cfxZones.calcHeadingOffset(aZone, theUnit) return dx, -dy -- note: dy is z coord!!!! end +function dmlZone:calcHeadingOffset(theUnit) + local unitHeading = dcsCommon.getUnitHeading(theUnit) + local zoneBearing = unitHeading + self.dPhi + if zoneBearing > 2 * 3.141592 then zoneBearing = zoneBearing - 2 * 3.141592 end + -- in DCS, positive x is north (wtf?) and positive z is east + local dy = (-self.rxy) * math.sin(zoneBearing) + local dx = self.rxy * math.cos(zoneBearing) + return dx, -dy -- note: dy is z coord!!!! +end + + function cfxZones.updateMovingZones() cfxZones.updateSchedule = timer.scheduleFunction(cfxZones.updateMovingZones, {}, timer.getTime() + 1/cfxZones.ups) -- simply scan all cfx zones for the linkName property, and if present @@ -2902,7 +3390,6 @@ function cfxZones.updateMovingZones() end function cfxZones.initLink(theZone) - theZone.linkBroken = true theZone.linkedUnit = nil theUnit = Unit.getByName(theZone.linkName) @@ -2931,6 +3418,35 @@ function cfxZones.initLink(theZone) end end +function dmlZone:initLink() + self.linkBroken = true + self.linkedUnit = nil + theUnit = Unit.getByName(self.linkName) + if theUnit then + + local dx = 0 + local dz = 0 + if self.useOffset or self.useHeading then + local A = self:getDCSOrigin() + local B = theUnit:getPoint() + local delta = dcsCommon.vSub(A,B) + dx = delta.x + dz = delta.z + end + self:linkUnitToZone(theUnit, dx, dz) -- also sets theZone.linkedUnit + + if self.verbose then + trigger.action.outText("Link established for zone <" .. self.name .. "> to unit <" .. self.linkName .. ">: dx=<" .. math.floor(dx) .. ">, dz=<" .. math.floor(dz) .. "> dist = <" .. math.floor(math.sqrt(dx * dx + dz * dz)) .. ">" , 30) + end + self.linkBroken = nil + + else + if self.verbose then + trigger.action.outText("Linked unit: no unit <" .. self.linkName .. "> to link <" .. self.name .. "> to", 30) + end + end +end + function cfxZones.startMovingZones() -- read all zones, and look for a property called 'linkedUnit' -- which will make them a linked zone if there is a unit that exists @@ -2969,6 +3485,7 @@ function cfxZones.startMovingZones() end + -- -- =========== -- INIT MODULE diff --git a/modules/cloneZone.lua b/modules/cloneZone.lua index 01b8f99..c320804 100644 --- a/modules/cloneZone.lua +++ b/modules/cloneZone.lua @@ -1,5 +1,5 @@ cloneZones = {} -cloneZones.version = "1.7.3" +cloneZones.version = "1.8.0" cloneZones.verbose = false cloneZones.requiredLibs = { "dcsCommon", -- always @@ -95,6 +95,9 @@ cloneZones.respawnOnGroupID = true - forcedRespawn passes zone instead of verbose 1.7.2 - onPerimeter attribute 1.7.3 - declutter option + 1.8.0 - OOP cfxZones + - removed "empty+1" as planned + - upgraded config zone parsing --]]-- @@ -155,7 +158,7 @@ end -- function cloneZones.partOfGroupDataInZone(theZone, theUnits) local zP = cfxZones.getPoint(theZone) - zP = cfxZones.getDCSOrigin(theZone) -- don't use getPoint now. + zP = theZone:getDCSOrigin() -- don't use getPoint now. zP.y = 0 for idx, aUnit in pairs(theUnits) do @@ -192,7 +195,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner" theZone.myUniqueCounter = cloneZones.lclUniqueCounter -- init local counter local localZones = cloneZones.allGroupsInZoneByData(theZone) - local localObjects = cfxZones.allStaticsInZone(theZone, true) -- true = use DCS origin, not moved zone + local localObjects = theZone:allStaticsInZone(true) -- true = use DCS origin, not moved zone if theZone.verbose then trigger.action.outText("+++clnZ: building cloner <" .. theZone.name .. "> TMPL: >>>", 30) for idx, theGroup in pairs (localZones) do @@ -208,7 +211,7 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner" theZone.myStatics = {} -- update: getPoint is bad if it's a moving zone. -- use getDCSOrigin instead - theZone.origin = cfxZones.getDCSOrigin(theZone) + theZone.origin = theZone:getDCSOrigin() -- source tells us which template to use. it can be the following: -- nothing (no attribute) - then we use whatever groups are in zone to @@ -216,8 +219,8 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner" -- name of another spawner that provides the template -- we can't simply use a group name as we lack the reference -- location for delta - if cfxZones.hasProperty(theZone, "source") then - theZone.source = cfxZones.getStringFromZoneProperty(theZone, "source", "") + if theZone:hasProperty("source") then + theZone.source = theZone:getStringFromZoneProperty("source", "") if theZone.source == "" then theZone.source = nil end end @@ -259,95 +262,96 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner" end -- declutter - theZone.declutter = cfxZones.getBoolFromZoneProperty(theZone, "declutter", false) + theZone.declutter = theZone:getBoolFromZoneProperty("declutter", false) -- watchflags - theZone.cloneTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") + theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") - if cfxZones.hasProperty(theZone, "cloneTriggerMethod") then - theZone.cloneTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "cloneTriggerMethod", "change") + if theZone:hasProperty("cloneTriggerMethod") then + theZone.cloneTriggerMethod = theZone:getStringFromZoneProperty("cloneTriggerMethod", "change") end -- f? and spawn? and other synonyms map to the same - if cfxZones.hasProperty(theZone, "f?") then - theZone.spawnFlag = cfxZones.getStringFromZoneProperty(theZone, "f?", "none") + if theZone:hasProperty("f?") then + theZone.spawnFlag = theZone:getStringFromZoneProperty("f?", "none") end - if cfxZones.hasProperty(theZone, "in?") then - theZone.spawnFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none") + if theZone:hasProperty("in?") then + theZone.spawnFlag = theZone:getStringFromZoneProperty("in?", "none") end - if cfxZones.hasProperty(theZone, "spawn?") then - theZone.spawnFlag = cfxZones.getStringFromZoneProperty(theZone, "spawn?", "none") + if theZone:hasProperty("spawn?") then + theZone.spawnFlag = theZone:getStringFromZoneProperty("spawn?", "none") end - if cfxZones.hasProperty(theZone, "clone?") then - theZone.spawnFlag = cfxZones.getStringFromZoneProperty(theZone, "clone?", "none") + if theZone:hasProperty("clone?") then + theZone.spawnFlag = theZone:getStringFromZoneProperty("clone?", "none") end if theZone.spawnFlag then - theZone.lastSpawnValue = cfxZones.getFlagValue(theZone.spawnFlag, theZone) + theZone.lastSpawnValue = theZone:getFlagValue(theZone.spawnFlag) end -- deSpawn? - if cfxZones.hasProperty(theZone, "deSpawn?") then - theZone.deSpawnFlag = cfxZones.getStringFromZoneProperty(theZone, "deSpawn?", "none") + if theZone:hasProperty("deSpawn?") then + theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deSpawn?", "none") end - if cfxZones.hasProperty(theZone, "deClone?") then - theZone.deSpawnFlag = cfxZones.getStringFromZoneProperty(theZone, "deClone?", "none") + if theZone:hasProperty("deClone?") then + theZone.deSpawnFlag = theZone:getStringFromZoneProperty( "deClone?", "none") end - if cfxZones.hasProperty(theZone, "wipe?") then - theZone.deSpawnFlag = cfxZones.getStringFromZoneProperty(theZone, "wipe?", "none") + if theZone:hasProperty("wipe?") then + theZone.deSpawnFlag = theZone:getStringFromZoneProperty("wipe?", "none") end if theZone.deSpawnFlag then - theZone.lastDeSpawnValue = cfxZones.getFlagValue(theZone.deSpawnFlag, theZone) + theZone.lastDeSpawnValue = theZone:getFlagValue(theZone.deSpawnFlag) end - theZone.onStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", false) + theZone.onStart = theZone:getBoolFromZoneProperty("onStart", false) - theZone.moveRoute = cfxZones.getBoolFromZoneProperty(theZone, "moveRoute", false) + theZone.moveRoute = theZone:getBoolFromZoneProperty("moveRoute", false) - theZone.preWipe = cfxZones.getBoolFromZoneProperty(theZone, "preWipe", false) + theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", false) -- to be deprecated - if cfxZones.hasProperty(theZone, "empty+1") then - theZone.emptyFlag = cfxZones.getStringFromZoneProperty(theZone, "empty+1", "") -- note string on number default + --[[-- + if theZone:hasProperty("empty+1") then + theZone.emptyFlag = theZone:getStringFromZoneProperty("empty+1", "") -- note string on number default + end + --]]-- + + if theZone:hasProperty("empty!") then + theZone.emptyBangFlag = theZone:getStringFromZoneProperty("empty!", "") -- note string on number default end - if cfxZones.hasProperty(theZone, "empty!") then - theZone.emptyBangFlag = cfxZones.getStringFromZoneProperty(theZone, "empty!", "") -- note string on number default + theZone.cloneMethod = theZone:getStringFromZoneProperty("cloneMethod", "inc") + if theZone:hasProperty("method") then + theZone.cloneMethod = theZone:getStringFromZoneProperty("method", "inc") -- note string on number default end - theZone.cloneMethod = cfxZones.getStringFromZoneProperty(theZone, "cloneMethod", "inc") - if cfxZones.hasProperty(theZone, "method") then - theZone.cloneMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") -- note string on number default - end - - if cfxZones.hasProperty(theZone, "masterOwner") then - theZone.masterOwner = cfxZones.getStringFromZoneProperty(theZone, "masterOwner", "*") + if theZone:hasProperty("masterOwner") then + theZone.masterOwner = theZone:getStringFromZoneProperty( "masterOwner", "*") theZone.masterOwner = dcsCommon.trim(theZone.masterOwner) if theZone.masterOwner == "*" then theZone.masterOwner = theZone.name if theZone.verbose then - trigger.action.outText("+++clnZ: masterOwner for <" .. theZone.name .. "> successfully to to itself, currently owned by faction <" .. theZone.owner .. ">", 30) + trigger.action.outText("+++clnZ: masterOwner for <" .. theZone.name .. "> set successfully to to itself, currently owned by faction <" .. theZone.owner .. ">", 30) end end end - theZone.turn = cfxZones.getNumberFromZoneProperty(theZone, "turn", 0) + theZone.turn = theZone:getNumberFromZoneProperty("turn", 0) -- interface to groupTracker - if cfxZones.hasProperty(theZone, "trackWith:") then - theZone.trackWith = cfxZones.getStringFromZoneProperty(theZone, "trackWith:", "") - --trigger.action.outText("trackwith: " .. theZone.trackWith, 30) + if theZone:hasProperty("trackWith:") then + theZone.trackWith = theZone:getStringFromZoneProperty( "trackWith:", "") end -- interface to delicates - if cfxZones.hasProperty(theZone, "useDelicates") then - theZone.delicateName = dcsCommon.trim(cfxZones.getStringFromZoneProperty(theZone, "useDelicates", "")) + if theZone:hasProperty("useDelicates") then + theZone.delicateName = dcsCommon.trim(theZone:getStringFromZoneProperty("useDelicates", "")) if theZone.delicateName == "*" then theZone.delicateName = theZone.name end if theZone.verbose then trigger.action.outText("+++clnZ: cloner <" .. theZone.name .."> hands off delicates to <" .. theZone.delicateName .. ">", 30) @@ -355,29 +359,29 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner" end -- randomized locations on spawn - theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "randomizedLoc", false) - if cfxZones.hasProperty(theZone, "rndLoc") then - theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "rndLoc", false) + theZone.rndLoc = theZone:getBoolFromZoneProperty("randomizedLoc", false) + if theZone:hasProperty("rndLoc") then + theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", false) end - theZone.centerOnly = cfxZones.getBoolFromZoneProperty(theZone, "centerOnly", false) - if cfxZones.hasProperty(theZone, "wholeGroups") then - theZone.centerOnly = cfxZones.getBoolFromZoneProperty(theZone, "wholeGroups", false) + theZone.centerOnly = theZone:getBoolFromZoneProperty("centerOnly", false) + if theZone:hasProperty("wholeGroups") then + theZone.centerOnly = theZone:getBoolFromZoneProperty( "wholeGroups", false) end - theZone.rndHeading = cfxZones.getBoolFromZoneProperty(theZone, "rndHeading", false) + theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false) - theZone.onRoad = cfxZones.getBoolFromZoneProperty(theZone, "onRoad", false) + theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false) - theZone.onPerimeter = cfxZones.getBoolFromZoneProperty(theZone, "onPerimeter", false) + theZone.onPerimeter = theZone:getBoolFromZoneProperty("onPerimeter", false) -- check for name scheme and / or identical - if cfxZones.hasProperty(theZone, "identical") then - theZone.identical = cfxZones.getBoolFromZoneProperty(theZone, "identical", false) + if theZone:hasProperty("identical") then + theZone.identical = theZone:getBoolFromZoneProperty("identical", false) if theZone.identical == false then theZone.identical = nil end end - if cfxZones.hasProperty(theZone, "nameScheme") then - theZone.nameScheme = cfxZones.getStringFromZoneProperty(theZone, "nameScheme", "-") -- default to [ "-" ] + if theZone:hasProperty("nameScheme") then + theZone.nameScheme = theZone:getStringFromZoneProperty( "nameScheme", "-") -- default to [ "-" ] end if theZone.identical and theZone.nameScheme then @@ -981,7 +985,6 @@ function cloneZones.validateSpawnUnitData(aUnit, theZone, unitNames) end end - -- check against static objects. local theStatic = StaticObject.getByName(aUnit.name) if theStatic and StaticObject.isExist(theStatic) then @@ -1073,9 +1076,9 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) trigger.action.outText("+++clnZ: spawning with template <" .. theZone.name .. "> for spawner <" .. spawnZone.name .. ">", 30) end -- theZone is the cloner with the TEMPLATE (source) - -- spawnZone is the spawner with SETTINGS and DESTINATION (target location) - local newCenter = cfxZones.getPoint(spawnZone) -- includes zone following updates - local oCenter = cfxZones.getDCSOrigin(theZone) -- get original coords on map for cloning offsets + -- spawnZone is the spawner with SETTINGS and DESTINATION (target location) where the clones are poofed into existence + local newCenter = spawnZone:getPoint() -- includes zone following updates + local oCenter = theZone:getDCSOrigin() -- get original coords on map for cloning offsets -- calculate zoneDelta, is added to all vectors local zoneDelta = dcsCommon.vSub(newCenter, theZone.origin) -- oCenter) --theZone.origin) @@ -1086,7 +1089,7 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) local theUnit = spawnZone.linkedUnit local currHeading = dcsCommon.getUnitHeading(theUnit) dHeading = currHeading - spawnZone.uHdg - rotCenter = cfxZones.getPoint(spawnZone) + rotCenter = spawnZone:getPoint() end local spawnedGroups = {} @@ -1114,10 +1117,6 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) local theCat = cfxMX.catText2ID(cat) rawData.CZtheCat = theCat -- save category - -- update their position if not spawning to exact same location - if cloneZones.verbose or theZone.verbose or spawnZone.verbose then - --trigger.action.outText("+++clnZ: tmpl delta x = <" .. math.floor(zoneDelta.x) .. ">, y = <" .. math.floor(zoneDelta.z) .. "> for tmpl <" .. theZone.name .. "> to cloner <" .. spawnZone.name .. ">", 30) - end -- update routes when not spawning same location cloneZones.updateLocationsInGroupData(rawData, zoneDelta, spawnZone.moveRoute, rotCenter, spawnZone.turn / 57.2958 + dHeading) @@ -1129,18 +1128,18 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) local loc, dx, dy if spawnZone.onPerimeter then - loc, dx, dy = cfxZones.createRandomPointOnZoneBoundary(spawnZone) + loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary() else - loc, dx, dy = cfxZones.createRandomPointInZone(spawnZone) -- also supports polygonal zones + loc, dx, dy = spawnZone:createRandomPointInZone() -- also supports polygonal zones end for idx, aUnit in pairs(units) do if not spawnZone.centerOnly then -- *every unit's displacement is randomized if spawnZone.onPerimeter then - loc, dx, dy = cfxZones.createRandomPointOnZoneBoundary(spawnZone) + loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary() else - loc, dx, dy = cfxZones.createRandomPointInZone(spawnZone) + loc, dx, dy = spawnZone:createRandomPointInZone() end aUnit.x = loc.x aUnit.y = loc.z @@ -1382,9 +1381,9 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone) local loc, dx, dy if spawnZone.onPerimeter then - loc, dx, dy = cfxZones.createRandomPointOnZoneBoundary(spawnZone) + loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary() else - loc, dx, dy = cfxZones.createRandomPointInZone(spawnZone) -- also supports polygonal zones + loc, dx, dy = spawnZone:createRandomPointInZone() -- also supports polygonal zones end rawData.x = rawData.x + dx -- might want to use loc rawData.y = rawData.y + dy -- directly @@ -1550,7 +1549,7 @@ function cloneZones.spawnWithCloner(theZone) -- declutter? if theZone.declutter then - cfxZones.declutterZone(theZone) + theZone:declutterZone() if theZone.verbose then trigger.action.outText("+++clnZ: cloner <" .. theZone.name .. "> declutter complete.", 30) end @@ -1641,7 +1640,7 @@ function cloneZones.update() for idx, aZone in pairs(cloneZones.cloners) do -- see if deSpawn was pulled. Must run before spawn if aZone.deSpawnFlag then - local currTriggerVal = cfxZones.getFlagValue(aZone.deSpawnFlag, aZone) -- trigger.misc.getUserFlag(aZone.deSpawnFlag) + local currTriggerVal = aZone:getFlagValue(aZone.deSpawnFlag) -- trigger.misc.getUserFlag(aZone.deSpawnFlag) if currTriggerVal ~= aZone.lastDeSpawnValue then if cloneZones.verbose or aZone.verbose then trigger.action.outText("+++clnZ: DEspawn triggered for <" .. aZone.name .. ">", 30) @@ -1652,7 +1651,7 @@ function cloneZones.update() end -- see if we got spawn? command - if cfxZones.testZoneFlag(aZone, aZone.spawnFlag, aZone.cloneTriggerMethod, "lastSpawnValue") then + if aZone:testZoneFlag(aZone.spawnFlag, aZone.cloneTriggerMethod, "lastSpawnValue") then if cloneZones.verbose then trigger.action.outText("+++clnZ: spawn triggered for <" .. aZone.name .. ">", 30) end @@ -1663,13 +1662,15 @@ function cloneZones.update() local isEmpty = cloneZones.countLiveUnits(aZone) < 1 and aZone.hasClones if isEmpty then -- see if we need to bang a flag + --[[-- if aZone.emptyFlag then --cloneZones.pollFlag(aZone.emptyFlag) cfxZones.pollFlag(aZone.emptyFlag, 'inc', aZone) end + --]]-- if aZone.emptyBangFlag then - cfxZones.pollFlag(aZone.emptyBangFlag, aZone.cloneMethod, aZone) + aZone:pollFlag(aZone.emptyBangFlag, aZone.cloneMethod) if cloneZones.verbose then trigger.action.outText("+++clnZ: bang! on " .. aZone.emptyBangFlag, 30) end @@ -1950,22 +1951,22 @@ function cloneZones.readConfigZone() if cloneZones.verbose then trigger.action.outText("+++clnZ: NO config zone!", 30) end - return + theZone = cfxZones.createSimpleZone("cloneZonesConfig") end - if cfxZones.hasProperty(theZone, "uniqueCount") then - cloneZones.uniqueCounter = cfxZones.getNumberFromZoneProperty(theZone, "uniqueCount", cloneZone.uniqueCounter) + if theZone:hasProperty("uniqueCount") then + cloneZones.uniqueCounter = theZone:getNumberFromZoneProperty("uniqueCount", cloneZone.uniqueCounter) end - if cfxZones.hasProperty(theZone, "localCount") then - cloneZones.lclUniqueCounter = cfxZones.getNumberFromZoneProperty(theZone, "localCount", cloneZone.lclUniqueCounter) + if theZone:hasProperty("localCount") then + cloneZones.lclUniqueCounter = theZone:getNumberFromZoneProperty("localCount", cloneZone.lclUniqueCounter) end - if cfxZones.hasProperty(theZone, "globalCount") then - cloneZones.globalCounter = cfxZones.getNumberFromZoneProperty(theZone, "globalCount", cloneZone.globalCounter) + if theZone:hasProperty("globalCount") then + cloneZones.globalCounter = theZone:getNumberFromZoneProperty("globalCount", cloneZone.globalCounter) end - cloneZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + cloneZones.verbose = theZone:getBoolFromZoneProperty("verbose", false) if cloneZones.verbose then trigger.action.outText("+++clnZ: read config", 30) diff --git a/modules/dcsCommon.lua b/modules/dcsCommon.lua index 0023f15..a79b33b 100644 --- a/modules/dcsCommon.lua +++ b/modules/dcsCommon.lua @@ -1,5 +1,5 @@ dcsCommon = {} -dcsCommon.version = "2.8.10" +dcsCommon.version = "2.9.0" --[[-- VERSION HISTORY 2.2.6 - compassPositionOfARelativeToB - clockPositionOfARelativeToB @@ -163,7 +163,11 @@ dcsCommon.version = "2.8.10" - new getPlayerUnit() - new getMapName() - new getMagDeclForPoint() +2.9.0 - createPoint() moved from cfxZones + - copyPoint() moved from cfxZones + - numberArrayFromString() moved from cfxZones + --]]-- -- dcsCommon is a library of common lua functions @@ -2520,9 +2524,7 @@ end --env.info("=== dcsCommon vardump END") end end - - - + function dcsCommon.numberUUID() dcsCommon.simpleUUID = dcsCommon.simpleUUID + 1 return dcsCommon.simpleUUID @@ -3025,6 +3027,53 @@ function dcsCommon.getMissionName() return mn end +function dcsCommon.numberArrayFromString(inString, default) -- moved from cfxZones + if not default then default = 0 end + if string.len(inString) < 1 then + trigger.action.outText("+++dcsCommon: empty numbers", 30) + return {default, } + end + + local flags = {} + local rawElements = dcsCommon.splitString(inString, ",") + -- go over all elements + for idx, anElement in pairs(rawElements) do + anElement = dcsCommon.trim(anElement) + if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then + -- interpret this as a range + local theRange = dcsCommon.splitString(anElement, "-") + local lowerBound = theRange[1] + lowerBound = tonumber(lowerBound) + local upperBound = theRange[2] + upperBound = tonumber(upperBound) + if lowerBound and upperBound then + -- swap if wrong order + if lowerBound > upperBound then + local temp = upperBound + upperBound = lowerBound + lowerBound = temp + end + -- now add add numbers to flags + for f=lowerBound, upperBound do + table.insert(flags, tostring(f)) + end + else + -- bounds illegal + trigger.action.outText("+++dcsCommon: ignored range <" .. anElement .. "> (range)", 30) + end + else + -- single number + f = dcsCommon.trim(anElement) + f = tonumber(f) + if f then + table.insert(flags, f) + end + end + end + if #flags < 1 then flags = {default, } end + return flags +end + function dcsCommon.flagArrayFromString(inString, verbose) if not verbose then verbose = false end @@ -3475,6 +3524,31 @@ function dcsCommon.iteratePlayers(callBack) end end + +-- +-- MISC POINT CREATION +-- +function dcsCommon.createPoint(x, y, z) + local newPoint = {} + newPoint.x = x + newPoint.y = y + newPoint.z = z -- works even if Z == nil + return newPoint +end + +function dcsCommon.copyPoint(inPoint) + local newPoint = {} + newPoint.x = inPoint.x + newPoint.y = inPoint.y + -- handle xz only + if inPoint.z then + newPoint.z = inPoint.z + else + newPoint.z = inPoint.y + end + return newPoint +end + -- -- SEMAPHORES -- diff --git a/modules/flareZone.lua b/modules/flareZone.lua index a1a6979..174442a 100644 --- a/modules/flareZone.lua +++ b/modules/flareZone.lua @@ -1,10 +1,13 @@ flareZone = {} -flareZone.version = "1.0.0" +flareZone.version = "1.1.0" flareZone.verbose = false flareZone.name = "flareZone" --[[-- VERSION HISTORY 1.0.0 - initial version + 1.1.0 - improvements to verbosity + - OOP + - small bugfix in doFlare assignment --]]-- flareZone.requiredLibs = { "dcsCommon", @@ -13,40 +16,39 @@ flareZone.requiredLibs = { flareZone.flares = {} -- all flare zones function flareZone.addFlareZone(theZone) - theZone.flareColor = cfxZones.getFlareColorStringFromZoneProperty(theZone, "flare", "green") + theZone.flareColor = theZone:getFlareColorStringFromZoneProperty("flare", "green") theZone.flareColor = dcsCommon.flareColor2Num(theZone.flareColor) - if cfxZones.hasProperty(theZone, "f?") then - cfxZones.theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "f?", "") - elseif cfxZones.hasProperty(theZone, "launchFlare?") then - theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "launchFlare?", "") + if theZone:hasProperty("f?") then + theZone.doFlare = theZone:getStringFromZoneProperty("f?", "") + elseif theZone:hasProperty("launchFlare?") then + theZone.doFlare = theZone:getStringFromZoneProperty( "launchFlare?", "") else - theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "launch?", "") + theZone.doFlare = theZone:getStringFromZoneProperty("launch?", "") end theZone.lastDoFlare = trigger.misc.getUserFlag(theZone.doFlare) -- triggerMethod - theZone.flareTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") - if cfxZones.hasProperty(theZone, "flareTriggerMethod") then - theZone.flareTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "flareTriggerMethod", "change") + theZone.flareTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") + if theZone:hasProperty("flareTriggerMethod") then + theZone.flareTriggerMethod = theZone:getStringFromZoneProperty("flareTriggerMethod", "change") end - theZone.azimuthL, theZone.azimuthH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "direction", 90) -- in degrees + theZone.azimuthL, theZone.azimuthH = theZone:getPositiveRangeFromZoneProperty("direction", 90) -- in degrees -- in DCS documentation, the parameter is incorrectly called 'azimuth' - if cfxZones.hasProperty(theZone, "azimuth") then - theZone.azimuthL, theZone.azimuthH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "azimuth", 90) -- in degrees + if theZone:hasProperty("azimuth") then + theZone.azimuthL, theZone.azimuthH = theZone:getPositiveRangeFromZoneProperty("azimuth", 90) -- in degrees end --- theZone.azimuth = theZone.azimuth * 0.0174533 -- rads - theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "altitude", 1) - if cfxZones.hasProperty(theZone, "alt") then - theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "alt", 1) - elseif cfxZones.hasProperty(theZone, "flareAlt") then - theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "flareAlt", 1) - elseif cfxZones.hasProperty(theZone, "agl") then - theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "agl", 1) + theZone.flareAlt = theZone:getNumberFromZoneProperty("altitude", 1) + if theZone:hasProperty("alt") then + theZone.flareAlt = theZone:getNumberFromZoneProperty("alt", 1) + elseif theZone:hasProperty("flareAlt") then + theZone.flareAlt = theZone:getNumberFromZoneProperty("flareAlt", 1) + elseif theZone:hasProperty("agl") then + theZone.flareAlt = theZone:getNumberFromZoneProperty("agl", 1) end - theZone.salvoSizeL, theZone.salvoSizeH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "salvo", 1) + theZone.salvoSizeL, theZone.salvoSizeH = theZone:getPositiveRangeFromZoneProperty("salvo", 1) - theZone.salvoDurationL, theZone.salvoDurationH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "duration", 1) + theZone.salvoDurationL, theZone.salvoDurationH = theZone:getPositiveRangeFromZoneProperty("duration", 1) if theZone.verbose or flareZone.verbose then trigger.action.outText("+++flrZ: new flare <" .. theZone.name .. ">, color (" .. theZone.flareColor .. ")", 30) @@ -57,13 +59,17 @@ end function flareZone.launch(theZone) local color = theZone.flareColor if color < 0 then color = math.random(4) - 1 end - if flareZone.verbose or theZone.verbose then - trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. ")", 30) - end + local loc = cfxZones.getPoint(theZone, true) -- with height loc.y = loc.y + theZone.flareAlt -- calculate azimuth - local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) * 0.0174533 -- in rads + local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) -- in deg + + if flareZone.verbose or theZone.verbose then + trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. "), azi <" .. azimuth .. "> [" .. theZone.azimuthL .. "-" .. theZone.azimuthH .. "]", 30) + end + azimuth = azimuth * 0.0174533 -- in rads + trigger.action.signalFlare(loc, color, azimuth) end diff --git a/modules/messenger.lua b/modules/messenger.lua index 5d1678b..8e48dc8 100644 --- a/modules/messenger.lua +++ b/modules/messenger.lua @@ -1,5 +1,5 @@ messenger = {} -messenger.version = "2.2.1" +messenger.version = "2.3.0" messenger.verbose = false messenger.requiredLibs = { "dcsCommon", -- always @@ -67,6 +67,7 @@ messenger.messengers = {} 2.2.1 - when messenger is linked to a unit, it can use the linked unit as reference point for relative wildcards. Always broadcasts to coalition. Can be used to broadcase 'eye in the sky' type information - fixed verbosity bug + 2.3.0 - cfxZones OOP switch --]]-- @@ -138,7 +139,7 @@ function messenger.dynamicUnitProcessing(inMsg, theZone, theUnit) local tHead = 0 if tZone then - thePoint = cfxZones.getPoint(tZone) + thePoint = tZone:getPoint() -- if this zone follows a unit, get the master units elevaltion if tZone.linkedUnit and Unit.isExist(tZone.linkedUnit) then local lU = tZone.linkedUnit @@ -225,90 +226,82 @@ end function messenger.createMessengerWithZone(theZone) -- start val - a range - local aMessage = cfxZones.getStringFromZoneProperty(theZone, "message", "") + local aMessage = theZone:getStringFromZoneProperty("message", "") theZone.message = aMessage -- refactoring: messenger.preProcMessage(aMessage, theZone) removed - theZone.spaceBefore = cfxZones.getBoolFromZoneProperty(theZone, "spaceBefore", false) - theZone.spaceAfter = cfxZones.getBoolFromZoneProperty(theZone, "spaceAfter", false) + theZone.spaceBefore = theZone:getBoolFromZoneProperty("spaceBefore", false) + theZone.spaceAfter = theZone:getBoolFromZoneProperty("spaceAfter", false) - theZone.soundFile = cfxZones.getStringFromZoneProperty(theZone, "soundFile", "") + theZone.soundFile = theZone:getStringFromZoneProperty("soundFile", "") - theZone.clearScreen = cfxZones.getBoolFromZoneProperty(theZone, "clearScreen", false) + theZone.clearScreen = theZone:getBoolFromZoneProperty("clearScreen", false) - theZone.duration = cfxZones.getNumberFromZoneProperty(theZone, "duration", 30) - if cfxZones.hasProperty(theZone, "messageDuration") then - theZone.duration = cfxZones.getNumberFromZoneProperty(theZone, "messageDuration", 30) + theZone.duration = theZone:getNumberFromZoneProperty("duration", 30) + if theZone:hasProperty("messageDuration") then + theZone.duration = theZone:getNumberFromZoneProperty( "messageDuration", 30) end -- msgTriggerMethod - theZone.msgTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") - if cfxZones.hasProperty(theZone, "msgTriggerMethod") then - theZone.msgTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "msgTriggerMethod", "change") + theZone.msgTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change") + if theZone:hasProperty("msgTriggerMethod") then + theZone.msgTriggerMethod = theZone:getStringFromZoneProperty("msgTriggerMethod", "change") end - - -- trigger flag f? in? messageOut?, add messenger? - - if cfxZones.hasProperty(theZone, "f?") then - theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "f?", "none") - -- may want to add deprecated note later - end - - + + if theZone:hasProperty("f?") then + theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("f?", "none") + end -- can also use in? for counting. we always use triggerMessagerFlag - if cfxZones.hasProperty(theZone, "in?") then - theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none") + if theZone:hasProperty("in?") then + theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("in?", "none") end - if cfxZones.hasProperty(theZone, "messageOut?") then - theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "messageOut?", "none") + if theZone:hasProperty("messageOut?") then + theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty("messageOut?", "none") end -- try default only if no other is set if not theZone.triggerMessagerFlag then - if not cfxZones.hasProperty(theZone, "messenger?") then + if not theZone:hasProperty("messenger?") then trigger.action.outText("*** Note: messenger in <" .. theZone.name .. "> can't be triggered", 30) end - theZone.triggerMessagerFlag = cfxZones.getStringFromZoneProperty(theZone, "messenger?", "none") + theZone.triggerMessagerFlag = theZone:getStringFromZoneProperty( "messenger?", "none") end - --- if theZone.triggerMessagerFlag then - theZone.lastMessageTriggerValue = cfxZones.getFlagValue(theZone.triggerMessagerFlag, theZone)-- save last value --- end - theZone.messageOff = cfxZones.getBoolFromZoneProperty(theZone, "mute", false) --false - if cfxZones.hasProperty(theZone, "messageMute") then - theZone.messageOff = cfxZones.getBoolFromZoneProperty(theZone, "messageMute", false) + theZone.lastMessageTriggerValue = theZone:getFlagValue(theZone.triggerMessagerFlag)-- save last value + + theZone.messageOff = theZone:getBoolFromZoneProperty("mute", false) --false + if theZone:hasProperty("messageMute") then + theZone.messageOff = theZone:getBoolFromZoneProperty( "messageMute", false) end - -- advisory: messageOff, messageOffFlag and lastMessageOff are all distinct - - if cfxZones.hasProperty(theZone, "messageOff?") then - theZone.messageOffFlag = cfxZones.getStringFromZoneProperty(theZone, "messageOff?", "*none") - theZone.lastMessageOff = cfxZones.getFlagValue(theZone.messageOffFlag, theZone) + -- advisory: messageOff, messageOffFlag and lastMessageOff are all distinct + if theZone:hasProperty("messageOff?") then + theZone.messageOffFlag = theZone:getStringFromZoneProperty("messageOff?", "*none") + theZone.lastMessageOff = theZone:getFlagValue(theZone.messageOffFlag) end - if cfxZones.hasProperty(theZone, "messageOn?") then - theZone.messageOnFlag = cfxZones.getStringFromZoneProperty(theZone, "messageOn?", "*none") - theZone.lastMessageOn = cfxZones.getFlagValue(theZone.messageOnFlag, theZone) + if theZone:hasProperty("messageOn?") then + theZone.messageOnFlag = theZone:getStringFromZoneProperty( "messageOn?", "*none") + theZone.lastMessageOn = theZone:getFlagValue(theZone.messageOnFlag) end -- reveiver: coalition, group, unit - if cfxZones.hasProperty(theZone, "coalition") then - theZone.msgCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0) - elseif cfxZones.hasProperty(theZone, "msgCoalition") then - theZone.msgCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "msgCoalition", 0) + if theZone:hasProperty("coalition") then + theZone.msgCoalition = theZone:getCoalitionFromZoneProperty("coalition", 0) + elseif theZone:hasProperty("msgCoalition") then + theZone.msgCoalition = theZone:getCoalitionFromZoneProperty("msgCoalition", 0) end - if cfxZones.hasProperty(theZone, "group") then - theZone.msgGroup = cfxZones.getStringFromZoneProperty(theZone, "group", "") - elseif cfxZones.hasProperty(theZone, "msgGroup") then - theZone.msgGroup = cfxZones.getStringFromZoneProperty(theZone, "msgGroup", "") + if theZone:hasProperty("group") then + theZone.msgGroup = theZone:getStringFromZoneProperty("group", "") + elseif theZone:hasProperty("msgGroup") then + theZone.msgGroup = theZone:getStringFromZoneProperty("msgGroup", "") end - if cfxZones.hasProperty(theZone, "unit") then - theZone.msgUnit = cfxZones.getStringFromZoneProperty(theZone, "unit", "") - elseif cfxZones.hasProperty(theZone, "msgUnit") then - theZone.msgUnit = cfxZones.getStringFromZoneProperty(theZone, "msgUnit", "") + if theZone:hasProperty("unit") then + theZone.msgUnit = theZone:getStringFromZoneProperty("unit", "") + elseif theZone:hasProperty("msgUnit") then + theZone.msgUnit = theZone:getStringFromZoneProperty("msgUnit", "") end if (theZone.msgGroup and theZone.msgUnit) or @@ -319,27 +312,27 @@ function messenger.createMessengerWithZone(theZone) end -- flag whose value can be read: to be deprecated - if cfxZones.hasProperty(theZone, "messageValue?") then - theZone.messageValue = cfxZones.getStringFromZoneProperty(theZone, "messageValue?", "") + if theZone:hasProperty("messageValue?") then + theZone.messageValue = theZone:getStringFromZoneProperty( "messageValue?", "") trigger.action.outText("+++Msg: Warning - zone <" .. theZone.name .. "> uses 'messageValue' attribute. Migrate to now!", 30) end -- time format for new - theZone.msgTimeFormat = cfxZones.getStringFromZoneProperty(theZone, "timeFormat", "<:h>:<:m>:<:s>") + theZone.msgTimeFormat = theZone:getStringFromZoneProperty( "timeFormat", "<:h>:<:m>:<:s>") - theZone.imperialUnits = cfxZones.getBoolFromZoneProperty(theZone, "imperial", false) - if cfxZones.hasProperty(theZone, "imperialUnits") then - theZone.imperialUnits = cfxZones.getBoolFromZoneProperty(theZone, "imperialUnits", false) + theZone.imperialUnits = theZone:getBoolFromZoneProperty("imperial", false) + if theZone:hasProperty("imperialUnits") then + theZone.imperialUnits = theZone:getBoolFromZoneProperty( "imperialUnits", false) end - theZone.errString = cfxZones.getStringFromZoneProperty(theZone, "error", "") - if cfxZones.hasProperty(theZone, "messageError") then - theZone.errString = cfxZones.getStringFromZoneProperty(theZone, "messageError", "") + theZone.errString = theZone:getStringFromZoneProperty("error", "") + if theZone:hasProperty("messageError") then + theZone.errString = theZone:getStringFromZoneProperty( "messageError", "") end -- possible responses for mapping - if cfxZones.hasProperty(theZone, "responses") then - local resp = cfxZones.getStringFromZoneProperty(theZone, "responses", "none") + if theZone:hasProperty("responses") then + local resp = theZone:getStringFromZoneProperty("responses", "none") theZone.msgResponses = dcsCommon.string2Array(resp, ",") end @@ -359,7 +352,7 @@ function messenger.getMessage(theZone) local zVal = "" if theZone.messageValue then trigger.action.outText("+++Msg: Warning - zone <" .. theZone.name .. "> uses 'messageValue' attribute. Migrate to now!") - zVal = cfxZones.getFlagValue(theZone.messageValue, theZone) + zVal = theZone:getFlagValue(theZone.messageValue) zVal = tostring(zVal) if not zVal then zVal = "" end end @@ -421,13 +414,13 @@ function messenger.isTriggered(theZone) end trigger.action.outSoundForUnit(ID, fileName) end - elseif cfxZones.getLinkedUnit(theZone) then + elseif theZone:getLinkedUnit() then -- this only works if the zone is linked to a unit -- and not using group or unit -- the linked unit is then used as reference -- outputs to all of same coalition as the linked -- unit - local theUnit = cfxZones.getLinkedUnit(theZone) + local theUnit = theZone:getLinkedUnit() local ID = theUnit:getID() local coa = theUnit:getCoalition() msg = messenger.dynamicUnitProcessing(msg, theZone, theUnit) @@ -451,7 +444,7 @@ function messenger.update() for idx, aZone in pairs(messenger.messengers) do -- make sure to re-start before reading time limit -- new trigger code - if cfxZones.testZoneFlag(aZone, aZone.triggerMessagerFlag, aZone.msgTriggerMethod, "lastMessageTriggerValue") then + if aZone:testZoneFlag(aZone.triggerMessagerFlag, aZone.msgTriggerMethod, "lastMessageTriggerValue") then if messenger.verbose or aZone.verbose then trigger.action.outText("+++msgr: triggered on in? for <".. aZone.name ..">", 30) end @@ -459,14 +452,14 @@ function messenger.update() end -- old trigger code - if cfxZones.testZoneFlag(aZone, aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then + if aZone:testZoneFlag(aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then aZone.messageOff = true if messenger.verbose or aZone.verbose then trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ***OFF***", 30) end end - if cfxZones.testZoneFlag(aZone, aZone.messageOnFlag, aZone.msgTriggerMethod, "lastMessageOn") then + if aZone:testZoneFlag(aZone.messageOnFlag, aZone.msgTriggerMethod, "lastMessageOn") then aZone.messageOff = false if messenger.verbose or aZone.verbose then trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30) @@ -487,7 +480,7 @@ function messenger.readConfigZone() theZone = cfxZones.createSimpleZone("messengerConfig") end - messenger.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + messenger.verbose = theZone:getBoolFromZoneProperty("verbose", false) if messenger.verbose then trigger.action.outText("+++msgr: read config", 30) diff --git a/modules/radioMenus.lua b/modules/radioMenus.lua index d984029..86923f1 100644 --- a/modules/radioMenus.lua +++ b/modules/radioMenus.lua @@ -1,5 +1,5 @@ radioMenu = {} -radioMenu.version = "2.0.1" +radioMenu.version = "2.1.0" radioMenu.verbose = false radioMenu.ups = 1 radioMenu.requiredLibs = { @@ -10,23 +10,27 @@ radioMenu.menus = {} --[[-- Version History - 1.0.0 Initial version - 1.0.1 spelling corrections - 1.1.0 removeMenu - addMenu - menuVisible - 2.0.0 redesign: handles multiple receivers - optional MX module - group option - type option - multiple group names - multiple types - gereric helo type - generic plane type - type works with coalition - 2.0.1 corrections to installMenu(), as suggested by GumidekCZ - - + 1.0.0 - Initial version + 1.0.1 - spelling corrections + 1.1.0 - removeMenu + addMenu + menuVisible + 2.0.0 - redesign: handles multiple receivers + optional MX module + group option + type option + multiple group names + multiple types + gereric helo type + generic plane type + type works with coalition + 2.0.1 - corrections to installMenu(), as suggested by GumidekCZ + 2.1.0 - valA/valB/valC/valD attributes + OOP cfxZones + corrected CD setting for "D" + ackA, ackB, ackC, ackD attributes + valA-D now define full method, not just values + full wildcard support for ack and cooldown --]]-- function radioMenu.addRadioMenu(theZone) @@ -183,8 +187,8 @@ function radioMenu.installMenu(theZone) theZone.rootMenu[0] = missionCommands.addSubMenuForCoalition(theZone.coalition, theZone.rootName, nil) end - if cfxZones.hasProperty(theZone, "itemA") then - local menuA = cfxZones.getStringFromZoneProperty(theZone, "itemA", "") + if theZone:hasProperty("itemA") then + local menuA = theZone:getStringFromZoneProperty("itemA", "") if theZone.menuGroup or theZone.menuTypes then theZone.menuA = {} for idx, grp in pairs(gID) do @@ -197,8 +201,8 @@ function radioMenu.installMenu(theZone) end end - if cfxZones.hasProperty(theZone, "itemB") then - local menuB = cfxZones.getStringFromZoneProperty(theZone, "itemB", "") + if theZone:hasProperty("itemB") then + local menuB = theZone:getStringFromZoneProperty("itemB", "") if theZone.menuGroup or theZone.menuTypes then theZone.menuB = {} for idx, grp in pairs(gID) do @@ -211,8 +215,8 @@ function radioMenu.installMenu(theZone) end end - if cfxZones.hasProperty(theZone, "itemC") then - local menuC = cfxZones.getStringFromZoneProperty(theZone, "itemC", "") + if theZone:hasProperty("itemC") then + local menuC = theZone:getStringFromZoneProperty("itemC", "") if theZone.menuGroup or theZone.menuTypes then theZone.menuC = {} for idx, grp in pairs(gID) do @@ -225,8 +229,8 @@ function radioMenu.installMenu(theZone) end end - if cfxZones.hasProperty(theZone, "itemD") then - local menuD = cfxZones.getStringFromZoneProperty(theZone, "itemD", "") + if theZone:hasProperty("itemD") then + local menuD = theZone:getStringFromZoneProperty("itemD", "") if theZone.menuGroup or theZone.menuTypes then theZone.menuD = {} for idx, grp in pairs(gID) do @@ -241,23 +245,23 @@ function radioMenu.installMenu(theZone) end function radioMenu.createRadioMenuWithZone(theZone) - theZone.rootName = cfxZones.getStringFromZoneProperty(theZone, "radioMenu", "") + theZone.rootName = theZone:getStringFromZoneProperty("radioMenu", "") - theZone.coalition = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0) + theZone.coalition = theZone:getCoalitionFromZoneProperty("coalition", 0) -- groups / types - if cfxZones.hasProperty(theZone, "group") then - theZone.menuGroup = cfxZones.getStringFromZoneProperty(theZone, "group", "") + if theZone:hasProperty("group") then + theZone.menuGroup = theZone:getStringFromZoneProperty("group", "") theZone.menuGroup = dcsCommon.trim(theZone.menuGroup) - elseif cfxZones.hasProperty(theZone, "groups") then - theZone.menuGroup = cfxZones.getStringFromZoneProperty(theZone, "groups", "") + elseif theZone:hasProperty("groups") then + theZone.menuGroup = theZone:getStringFromZoneProperty("groups", "") theZone.menuGroup = dcsCommon.trim(theZone.menuGroup) - elseif cfxZones.hasProperty(theZone, "type") then - theZone.menuTypes = cfxZones.getStringFromZoneProperty(theZone, "type", "none") - elseif cfxZones.hasProperty(theZone, "types") then - theZone.menuTypes = cfxZones.getStringFromZoneProperty(theZone, "types", "none") + elseif theZone:hasProperty("type") then + theZone.menuTypes = theZone:getStringFromZoneProperty("type", "none") + elseif theZone:hasProperty("types") then + theZone.menuTypes = theZone:getStringFromZoneProperty("types", "none") end - theZone.menuVisible = cfxZones.getBoolFromZoneProperty(theZone, "menuVisible", true) + theZone.menuVisible = theZone:getBoolFromZoneProperty("menuVisible", true) -- install menu if not hidden if theZone.menuVisible then @@ -265,41 +269,62 @@ function radioMenu.createRadioMenuWithZone(theZone) end -- get the triggers & methods here - theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") - if cfxZones.hasProperty(theZone, "radioMethod") then - theZone.radioMethod = cfxZones.getStringFromZoneProperty(theZone, "radioMethod", "inc") + theZone.radioMethod = theZone:getStringFromZoneProperty("method", "inc") + if theZone:hasProperty("radioMethod") then + theZone.radioMethod = theZone:getStringFromZoneProperty( "radioMethod", "inc") end - theZone.radioTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "radioTriggerMethod", "change") + theZone.radioTriggerMethod = theZone:getStringFromZoneProperty("radioTriggerMethod", "change") - theZone.itemAChosen = cfxZones.getStringFromZoneProperty(theZone, "A!", "*") - theZone.cooldownA = cfxZones.getNumberFromZoneProperty(theZone, "cooldownA", 0) - --theZone.mcdA = 0 - theZone.busyA = cfxZones.getStringFromZoneProperty(theZone, "busyA", "Please stand by ( seconds)") - - theZone.itemBChosen = cfxZones.getStringFromZoneProperty(theZone, "B!", "*") - theZone.cooldownB = cfxZones.getNumberFromZoneProperty(theZone, "cooldownB", 0) - --theZone.mcdB = 0 - theZone.busyB = cfxZones.getStringFromZoneProperty(theZone, "busyB", "Please stand by ( seconds)") - - theZone.itemCChosen = cfxZones.getStringFromZoneProperty(theZone, "C!", "*") - theZone.cooldownC = cfxZones.getNumberFromZoneProperty(theZone, "cooldownC", 0) - --theZone.mcdC = 0 - theZone.busyC = cfxZones.getStringFromZoneProperty(theZone, "busyC", "Please stand by ( seconds)") - - theZone.itemDChosen = cfxZones.getStringFromZoneProperty(theZone, "D!", "*") - theZone.cooldownD = cfxZones.getNumberFromZoneProperty(theZone, "cooldownD", 0) - --theZone.mcdD = 0 - theZone.busyD = cfxZones.getStringFromZoneProperty(theZone, "busyD", "Please stand by ( seconds)") - - if cfxZones.hasProperty(theZone, "removeMenu?") then - theZone.removeMenu = cfxZones.getStringFromZoneProperty(theZone, "removeMenu?", "*") - theZone.lastRemoveMenu = cfxZones.getFlagValue(theZone.removeMenu, theZone) + -- A! to D! + theZone.itemAChosen = theZone:getStringFromZoneProperty("A!", "*") + theZone.cooldownA = theZone:getNumberFromZoneProperty("cooldownA", 0) + theZone.busyA = theZone:getStringFromZoneProperty("busyA", "Please stand by ( seconds)") + if theZone:hasProperty("valA") then + theZone.outValA = theZone:getStringFromZoneProperty("valA", 1) + end + if theZone:hasProperty("ackA") then + theZone.ackA = theZone:getStringFromZoneProperty("ackA", "Acknowledged: A") end - if cfxZones.hasProperty(theZone, "addMenu?") then - theZone.addMenu = cfxZones.getStringFromZoneProperty(theZone, "addMenu?", "*") - theZone.lastAddMenu = cfxZones.getFlagValue(theZone.addMenu, theZone) + theZone.itemBChosen = theZone:getStringFromZoneProperty("B!", "*") + theZone.cooldownB = theZone:getNumberFromZoneProperty("cooldownB", 0) + theZone.busyB = theZone:getStringFromZoneProperty("busyB", "Please stand by ( seconds)") + if theZone:hasProperty("valB") then + theZone.outValB = theZone:getStringFromZoneProperty("valB", 1) + end + if theZone:hasProperty("ackB") then + theZone.ackB = theZone:getStringFromZoneProperty("ackB", "Acknowledged: B") + end + + theZone.itemCChosen = theZone:getStringFromZoneProperty("C!", "*") + theZone.cooldownC = theZone:getNumberFromZoneProperty("cooldownC", 0) + theZone.busyC = theZone:getStringFromZoneProperty("busyC", "Please stand by ( seconds)") + if theZone:hasProperty("valC") then + theZone.outValC = theZone:getStringFromZoneProperty("valC", 1) + end + if theZone:hasProperty("ackC") then + theZone.ackC = theZone:getStringFromZoneProperty("ackC", "Acknowledged: C") + end + + theZone.itemDChosen = theZone:getStringFromZoneProperty("D!", "*") + theZone.cooldownD = theZone:getNumberFromZoneProperty("cooldownD", 0) + theZone.busyD = theZone:getStringFromZoneProperty("busyD", "Please stand by ( seconds)") + if theZone:hasProperty("valD") then + theZone.outValD = theZone:getStringFromZoneProperty("valD", 1) + end + if theZone:hasProperty("ackD") then + theZone.ackC = theZone:getStringFromZoneProperty("ackD", "Acknowledged: D") + end + + if theZone:hasProperty("removeMenu?") then + theZone.removeMenu = theZone:getStringFromZoneProperty( "removeMenu?", "*") + theZone.lastRemoveMenu = theZone:getFlagValue(theZone.removeMenu) + end + + if theZone:hasProperty("addMenu?") then + theZone.addMenu = theZone:getStringFromZoneProperty("addMenu?", "*") + theZone.lastAddMenu = theZone:getFlagValue(theZone.addMenu) end if radioMenu.verbose or theZone.verbose then @@ -326,6 +351,28 @@ function radioMenu.processHMS(msg, delta) return dcsCommon.processHMS(msg, delta) end +function radioMenu.radioOutMsg(ack, gid, theZone) + -- group processing. only if gid>0 and cfxMX + local theMsg = ack + if (gid > 0) and cfxMX then + local gName = cfxMX.cfxMX.groupNamesByID[gid] + theMsg = theMsg:gsub("", gName) + end + + -- for the time being, we can't support many wildcards + -- leave them in, and simply proceed + -- note that theZone is the radio Menu zone! + theMsg = cfxZones.processStringWildcards(theMsg, theZone) + c = theZone.coalition + + if gid > 0 then + trigger.action.outTextForGroup(gid, theMsg, 30) + elseif c > 0 then + trigger.action.outTextForCoalition(c, theMsg, 30) + else + trigger.action.outText(theMsg, 30) + end +end -- -- Menu Branching @@ -360,20 +407,28 @@ function radioMenu.doMenuX(args) local cd = radioMenu.cdByGID(theZone.mcdA, theZone, theGroup) --theZone.mcdA local busy = theZone.busyA local theFlag = theZone.itemAChosen + local outVal = theZone.outValA + local ack = theZone.ackA -- decode A..X if theItemIndex == "B"then cd = radioMenu.cdByGID(theZone.mcdB, theZone, theGroup) -- theZone.mcdB busy = theZone.busyB theFlag = theZone.itemBChosen + outVal = theZone.outValB + ack = theZone.ackB elseif theItemIndex == "C" then cd = radioMenu.cdByGID(theZone.mcdC, theZone, theGroup) -- theZone.mcdC busy = theZone.busyC theFlag = theZone.itemCChosen + outVal = theZone.outValC + ack = theZone.ackC elseif theItemIndex == "D" then cd = radioMenu.cdByGID(theZone.mcdD, theZone, theGroup) -- theZone.mcdD busy = theZone.busyD theFlag = theZone.itemDChosen + outVal = theZone.outValD + ack = theZone.ackD end -- see if we are on cooldown @@ -381,8 +436,15 @@ function radioMenu.doMenuX(args) if now < cd then -- we are on cooldown. local msg = radioMenu.processHMS(busy, cd - now) - radioMenu.radioOutMessage(msg, theZone) + radioMenu.radioOutMsg(msg, theGroup, theZone) + --radioMenu.radioOutMessage(msg, theZone) return + else + -- see if we have an acknowledge + if ack then + local gid = theGroup + radioMenu.radioOutMsg(ack, gid, theZone) + end end -- set new cooldown -- needs own decoder A..X @@ -393,14 +455,23 @@ function radioMenu.doMenuX(args) elseif theItemIndex == "C" then radioMenu.setCDByGID("mcdC", theZone, theGroup, now + theZone.cooldownC) else - radioMenu.setCDByGID("mcdC", theZone, theGroup, now + theZone.cooldownC) + radioMenu.setCDByGID("mcdD", theZone, theGroup, now + theZone.cooldownD) end - cfxZones.pollFlag(theFlag, theZone.radioMethod, theZone) - if theZone.verbose or radioMenu.verbose then - trigger.action.outText("+++menu: banging with <" .. theZone.radioMethod .. "> on <" .. theFlag .. "> for " .. theZone.name, 30) - end - + -- poll flag, override with outVal if set + if outVal then + --outVal = "#"..outVal -- we force immediate mode + theZone:pollFlag(theFlag, outVal) + if theZone.verbose or radioMenu.verbose then + trigger.action.outText("+++menu: overriding index " .. theItemIndex .. " output method <" .. theZone.radioMethod .. "> with immediate value <" .. outVal .. ">", 30) + end + else + theZone:pollFlag(theFlag, theZone.radioMethod) + if theZone.verbose or radioMenu.verbose then + trigger.action.outText("+++menu: banging with <" .. theZone.radioMethod .. "> on <" .. theFlag .. "> for " .. theZone.name, 30) + end + end + end -- @@ -413,7 +484,7 @@ function radioMenu.update() -- iterate all menus for idx, theZone in pairs(radioMenu.menus) do if theZone.removeMenu - and cfxZones.testZoneFlag(theZone, theZone.removeMenu, theZone.radioTriggerMethod, "lastRemoveMenu") + and theZone:testZoneFlag(theZone.removeMenu, theZone.radioTriggerMethod, "lastRemoveMenu") and theZone.menuVisible then if theZone.menuGroup or theZone.menuTypes then @@ -430,7 +501,7 @@ function radioMenu.update() end if theZone.addMenu - and cfxZones.testZoneFlag(theZone, theZone.addMenu, theZone.radioTriggerMethod, "lastAddMenu") + and theZone:testZoneFlag(theZone.addMenu, theZone.radioTriggerMethod, "lastAddMenu") and (not theZone.menuVisible) then if theZone.verbose or radioMenu.verbose then @@ -453,10 +524,10 @@ function radioMenu.readConfigZone() if radioMenu.verbose then trigger.action.outText("+++radioMenu: NO config zone!", 30) end - return + theZone = cfxZones.createSimpleZone("radioMenuConfig") end - radioMenu.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + radioMenu.verbose = theZone:getBoolFromZoneProperty("verbose", false) if radioMenu.verbose then trigger.action.outText("+++radioMenu: read config", 30) diff --git a/modules/stopGaps.lua b/modules/stopGaps.lua index f3ed22c..da5ed21 100644 --- a/modules/stopGaps.lua +++ b/modules/stopGaps.lua @@ -1,5 +1,5 @@ stopGap = {} -stopGap.version = "1.0.6" +stopGap.version = "1.0.7" stopGap.verbose = false stopGap.ssbEnabled = true stopGap.ignoreMe = "-sg" @@ -40,6 +40,8 @@ stopGap.requiredLibs = { 1.0.4 - player units or groups that end in '-sg' are not stop-gapped 1.0.5 - triggerMethod 1.0.6 - spIgnore '-sp' + 1.0.7 - migrated to OOP zones + - corrected ssbEnabled config from sbb to ssb --]]-- stopGap.standInGroups = {} @@ -250,7 +252,8 @@ function stopGap.update() end -- check if signal for on? or off? - if stopGap.turnOn and cfxZones.testZoneFlag(stopGap, stopGap.turnOnFlag, stopGap.triggerMethod, "lastTurnOnFlag") then + if stopGap.turnOn and cfxZones.testZoneFlag(stopGap, stopGap.turnOnFlag, stopGap.triggerMethod, "lastTurnOnFlag") -- warning: stopGap is NOT dmlZone, requires cfxZone invocation + then if not stopGap.enabled then stopGap.turnOn() end @@ -328,12 +331,12 @@ end -- read stopGapZone -- function stopGap.createStopGapZone(theZone) - local sg = cfxZones.getBoolFromZoneProperty(theZone, "stopGap", true) + local sg = theZone:getBoolFromZoneProperty("stopGap", true) if sg then theZone.sgIgnore = false else theZone.sgIgnore = true end end function stopGap.createStopGapSPZone(theZone) - local sp = cfxZones.getBoolFromZoneProperty(theZone, "stopGapSP", true) + local sp = theZone:getBoolFromZoneProperty("stopGapSP", true) if sp then theZone.spIgnore = false else theZone.spIgnore = true end end @@ -344,17 +347,17 @@ stopGap.name = "stopGapConfig" -- cfxZones compatibility here function stopGap.readConfigZone(theZone) -- currently nothing to do stopGap.verbose = theZone.verbose - stopGap.ssbEnabled = cfxZones.getBoolFromZoneProperty(theZone, "sbb", true) - stopGap.enabled = cfxZones.getBoolFromZoneProperty(theZone, "onStart", true) - if cfxZones.hasProperty(theZone, "on?") then - stopGap.turnOnFlag = cfxZones.getStringFromZoneProperty(theZone, "on?", "*") + stopGap.ssbEnabled = theZone:getBoolFromZoneProperty("ssb", true) + stopGap.enabled = theZone:getBoolFromZoneProperty("onStart", true) + if theZone:hasProperty("on?") then + stopGap.turnOnFlag = theZone:getStringFromZoneProperty("on?", "*") stopGap.lastTurnOnFlag = trigger.misc.getUserFlag(stopGap.turnOnFlag) end - if cfxZones.hasProperty(theZone, "off?") then - stopGap.turnOffFlag = cfxZones.getStringFromZoneProperty(theZone, "off?", "*") + if theZone:hasProperty("off?") then + stopGap.turnOffFlag = theZone:getStringFromZoneProperty("off?", "*") stopGap.lastTurnOffFlag = trigger.misc.getUserFlag(stopGap.turnOffFlag) end - stopGap.triggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") + stopGap.triggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change") if stopGap.verbose then trigger.action.outText("+++StopG: config read, verbose = YES", 30) if stopGap.enabled then diff --git a/modules/tacan.lua b/modules/tacan.lua index b994266..aa3235a 100644 --- a/modules/tacan.lua +++ b/modules/tacan.lua @@ -1,8 +1,9 @@ tacan = {} -tacan.version = "1.0.0" +tacan.version = "1.1.0" --[[-- Version History 1.0.0 - initial version + 1.1.0 - OOP cfxZones --]]-- tacan.verbose = false @@ -14,59 +15,55 @@ tacan.tacanZones = {} function tacan.createTacanZone(theZone) - theZone.onStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", true) - local channels = cfxZones.getStringFromZoneProperty(theZone, "channel", "1") - theZone.channels = cfxZones.numberArrayFromString(channels, 1) + theZone.onStart = theZone:getBoolFromZoneProperty("onStart", true) + local channels = theZone:getStringFromZoneProperty("channel", "1") + theZone.channels = dcsCommon.numberArrayFromString(channels, 1) if theZone.verbose or tacan.verbose then trigger.action.outText("+++tcn: new tacan <" .. theZone.name .. "> for channels [" .. dcsCommon.array2string(theZone.channels, ", ") .. "]", 30) end - local mode = cfxZones.getStringFromZoneProperty(theZone, "mode", "X") + local mode = theZone:getStringFromZoneProperty("mode", "X") mode = string.upper(mode) - theZone.modes = cfxZones.flagArrayFromString(mode) + theZone.modes = dcsCommon.flagArrayFromString(mode) if theZone.verbose or tacan.verbose then trigger.action.outText("+++tcn: modes [" .. dcsCommon.array2string(theZone.modes, ", ") .. "]", 30) end - - theZone.coa = cfxZones.getCoalitionFromZoneProperty(theZone, "tacan", 0) - - theZone.heading = cfxZones.getNumberFromZoneProperty(theZone, "heading", 0) + theZone.coa = theZone:getCoalitionFromZoneProperty("tacan", 0) + theZone.heading = theZone:getNumberFromZoneProperty("heading", 0) theZone.heading = theZone.heading * 0.0174533 -- convert to rads - local callsign = cfxZones.getStringFromZoneProperty(theZone, "callsign", "TXN") + local callsign = theZone:getStringFromZoneProperty("callsign", "TXN") callsign = string.upper(callsign) - theZone.callsigns = cfxZones.flagArrayFromString(callsign) + theZone.callsigns = dcsCommon.flagArrayFromString(callsign) if theZone.verbose or tacan.verbose then trigger.action.outText("+++tcn: callsigns [" .. dcsCommon.array2string(theZone.callsigns) .. "]", 30) end - - theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "rndLoc", false) --- theZone.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") - theZone.triggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change") - if cfxZones.hasProperty(theZone, "deploy?") then - theZone.deployFlag = cfxZones.getStringFromZoneProperty(theZone, "deploy?", "") - theZone.lastDeployFlagValue = cfxZones.getFlagValue(theZone.deployFlag, theZone) + theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", false) + theZone.triggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change") + if theZone:hasProperty("deploy?") then + theZone.deployFlag = theZone:getStringFromZoneProperty("deploy?", "") + theZone.lastDeployFlagValue = theZone:getFlagValue(theZone.deployFlag) end if (not theZone.deployFlag) and (not theZone.onStart) then trigger.action.outText("+++tacan: WARNING: tacan zone <> is late activation and has no activation flag, will never activate.", 30) end theZone.spawnedTACANS = {} -- for GC and List - theZone.preWipe = cfxZones.getBoolFromZoneProperty(theZone, "preWipe", true) + theZone.preWipe = theZone:getBoolFromZoneProperty("preWipe", true) - if cfxZones.hasProperty(theZone, "destroy?") then - theZone.destroyFlag = cfxZones.getStringFromZoneProperty(theZone, "destroy?", "") - theZone.lastDestroyFlagValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone) + if theZone:hasProperty("destroy?") then + theZone.destroyFlag = theZone:getStringFromZoneProperty( "destroy?", "") + theZone.lastDestroyFlagValue = theZone:getFlagValue(theZone.destroyFlag) end - if cfxZones.hasProperty(theZone, "c#") then - theZone.channelOut = cfxZones.getStringFromZoneProperty(theZone, "C#", "") + if theZone:hasProperty("c#") then + theZone.channelOut = theZone:getStringFromZoneProperty("C#", "") end - theZone.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", false) + theZone.announcer = theZone:getBoolFromZoneProperty("announcer", false) -- interface to groupTracker - if cfxZones.hasProperty(theZone, "trackWith:") then - theZone.trackWith = cfxZones.getStringFromZoneProperty(theZone, "trackWith:", "") + if theZone:hasProperty("trackWith:") then + theZone.trackWith = theZone:getStringFromZoneProperty( "trackWith:", "") end -- see if we need to deploy now @@ -379,8 +376,8 @@ function tacan.readConfigZone() tacan.verbose = theZone.verbose tacan.list = cfxZones.getBoolFromZoneProperty(theZone, "list", false) - if cfxZones.hasProperty(theZone, "GUI") then - tacan.list = cfxZones.getBoolFromZoneProperty(theZone, "GUI", false) + if theZone:hasProperty("GUI") then + tacan.list = theZone:getBoolFromZoneProperty("GUI", false) end if tacan.verbose then