diff --git a/Doc/DML Documentation.pdf b/Doc/DML Documentation.pdf index 08f61ff..6067e2e 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 def86ca..806b6da 100644 Binary files a/Doc/DML Quick Reference.pdf and b/Doc/DML Quick Reference.pdf differ diff --git a/modules/airfield.lua b/modules/airfield.lua index 7dd5151..a6f2977 100644 --- a/modules/airfield.lua +++ b/modules/airfield.lua @@ -1,5 +1,5 @@ airfield = {} -airfield.version = "1.1.0" +airfield.version = "1.1.2" airfield.requiredLibs = { "dcsCommon", "cfxZones", @@ -7,7 +7,7 @@ airfield.requiredLibs = { airfield.verbose = false airfield.myAirfields = {} -- indexed by name airfield.farps = false - +airfield.gracePeriod = 3 --[[-- This module generates signals when the nearest airfield changes hands, can force the coalition of an airfield, and always provides the @@ -20,12 +20,17 @@ airfield.farps = false - allow zone.local farps designation - always checks farp cap events - added verbosity + 1.1.1 - GC grace period correction of ownership + 1.1.2 - 'show' attribute + line color attributes per zone + line color defaults in config --]]-- -- -- setting up airfield -- function airfield.createAirFieldFromZone(theZone) +--trigger.action.outText("Enter airfield for <" .. theZone.name .. ">", 30) theZone.farps = theZone:getBoolFromZoneProperty("farps", false) local filterCat = 0 @@ -34,17 +39,18 @@ function airfield.createAirFieldFromZone(theZone) local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat) theZone.airfield = theBase theZone.afName = theBase:getName() - if airfield.verbose then - trigger.action.outText("+++airF: zone <" .. theZone:getName() .. "> associates with <" .. theZone.afName .. ">", 30) + + -- set zone's owner + theZone.owner = theBase:getCoalition() + theZone.mismatchCount = airfield.gracePeriod + if theZone.verbose or airfield.verbose then + trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30) end -- methods theZone.method = theZone:getStringFromZoneProperty("method", "inc") theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") - - -- set zone's owner - theZone.owner = theBase:getCoalition() - + if theZone:hasProperty("red!") then theZone.redCap = theZone:getStringFromZoneProperty("red!") end @@ -90,14 +96,77 @@ function airfield.createAirFieldFromZone(theZone) -- index by name, and warn if duplicate associate if airfield.myAirfields[theZone.afName] then - trigger.action.outText("+++airF: WARNING - zone <" .. theZone:getName() .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30) + trigger.action.outText("+++airF: WARNING - zone <" .. theZone.name .. "> redefines airfield <" .. theZone.afName .. ">, discarded!", 30) else airfield.myAirfields[theZone.afName] = theZone end - if theZone.verbose or airfield.verbose then - trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30) + theZone.show = theZone:getBoolFromZoneProperty("show", false) + theZone.ownerMark = nil + + -- individual colors, else default from config + theZone.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", airfield.redLine) + theZone.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", airfield.redFill) + theZone.blueLine = theZone:getRGBAVectorFromZoneProperty("blueLine", airfield.blueLine) + theZone.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", airfield.blueFill) + theZone.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", airfield.neutralLine) + theZone.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", airfield.neutralFill) + + airfield.showAirfield(theZone) + +--trigger.action.outText("Exit airfield for <" .. theZone.name .. ">", 30) + +end + +function airfield.showAirfield(theZone) + if not theZone then return end + if theZone.ownerMark then + -- remove previous mark + trigger.action.removeMark(theZone.ownerMark) + theZone.ownerMark = nil + end + if not theZone.show then return end -- we don't show in map + + local lineColor = theZone.redLine -- {1.0, 0, 0, 1.0} -- red + local fillColor = theZone.redFill -- {1.0, 0, 0, 0.2} -- red + local owner = theZone.owner + if owner == 2 then + lineColor = theZone.blueLine -- {0.0, 0, 1.0, 1.0} + fillColor = theZone.blueFill -- {0.0, 0, 1.0, 0.2} + elseif owner == 0 or owner == 3 then + lineColor = theZone.neutralLine -- {0.8, 0.8, 0.8, 1.0} + fillColor = theZone.neutralFill -- {0.8, 0.8, 0.8, 0.2} end + + -- always center on airfield, always 2km radius + local markID = dcsCommon.numberUUID() + local radius = 2000 -- meters + local p = theZone.airfield:getPoint() + -- if there are runways, we center on first runway + local rws = theZone.airfield:getRunways() + if rws then -- all airfields and farps have runways, but that array isnt filled for FARPS + local rw1 = rws[1] + if rw1 then + p.x = rw1.position.x + p.z = rw1.position.z + if airfield.verbose or theZone.verbose then + trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has rw1, x=" .. p.x .. ", z=" .. p.z, 30) + end + else + if airfield.verbose or theZone.verbose then + trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has no rw1", 30) + end + end + else + if airfield.verbose or theZone.verbose then + trigger.action.outText("+++airF: zone <" .. theZone.name .. "> assoc airfield <" .. theZone.afName .. "> has no runways", 30) + end + end + p.y = 0 + + trigger.action.circleToAll(-1, markID, p, radius, lineColor, fillColor, 1, true, "") + theZone.ownerMark = markID + end function airfield.assumeControl(theZone) @@ -132,6 +201,8 @@ function airfield.airfieldCaptured(theBase) trigger.action.outText("+++airF: handling capture event/command for airfield <" .. bName .. "> with zone <" .. theZone:getName() .. ">", 30) end + airfield.showAirfield(theZone) -- show if enabled + -- outputs if theZone.ownedBy then trigger.action.setUserFlag(theZone.ownedBy, theZone.owner) @@ -218,6 +289,39 @@ function airfield.update() end end + +function airfield.GC() + timer.scheduleFunction(airfield.GC, {}, timer.getTime() + 2) + for afName, theZone in pairs(airfield.myAirfields) do + local theAirfield = theZone.airfield + local afOwner = theAirfield:getCoalition() + if afOwner == theZone.owner then + theZone.mismatchCount = airfield.gracePeriod + -- all quiet + elseif afOwner == 3 then + -- contested + if theZone.verbose or airfield.verbose then + trigger.action.outText("+++airF: airfield <" .. theZone.name .. ">: ownership is contested.", 30) + end + else + if theZone.mismatchCount > 0 then + if theZone.verbose or airfield.verbose then + trigger.action.outText("we have a problem with owner for <" .. theZone.name .. ">: afO = <" .. afOwner .. ">, zo = <" .. theZone.owner .. ">, grace count = <" .. theZone.mismatchCount..">", 30) + end + theZone.mismatchCount = theZone.mismatchCount - 1 + else + airfield.airfieldCaptured(theAirfield) + theZone.mismatchCount = airfield.gracePeriod + if theZone.verbose or airfield.verbose then + trigger.action.outText("+++airF: corrected ownership after grace period", 30) + end + end + + end + end + +end + -- -- LOAD / SAVE -- @@ -289,6 +393,13 @@ function airfield.readConfig() airfield.verbose = theZone.verbose airfield.farps = theZone:getBoolFromZoneProperty("farps", false) + -- colors for line and fill + airfield.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0}) + airfield.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", {1.0, 0, 0, 0.2}) + airfield.blueLine = theZone:getRGBAVectorFromZoneProperty("blueLine", {0.0, 0, 1.0, 1.0}) + airfield.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2}) + airfield.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0}) + airfield.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2}) end function airfield.start() @@ -320,6 +431,9 @@ function airfield.start() -- start update in 1 second timer.scheduleFunction(airfield.update, {}, timer.getTime() + 1) + -- start GC + timer.scheduleFunction(airfield.GC, {}, timer.getTime() + 2) + trigger.action.outText("cfx airfield v" .. airfield.version .. " loaded.", 30) return true end diff --git a/modules/cfxOwnedZones.lua b/modules/cfxOwnedZones.lua index 247cfcd..180e57a 100644 --- a/modules/cfxOwnedZones.lua +++ b/modules/cfxOwnedZones.lua @@ -1,5 +1,5 @@ cfxOwnedZones = {} -cfxOwnedZones.version = "2.0.1" +cfxOwnedZones.version = "2.1.0" cfxOwnedZones.verbose = false cfxOwnedZones.announcer = true cfxOwnedZones.name = "cfxOwnedZones" @@ -22,6 +22,13 @@ cfxOwnedZones.name = "cfxOwnedZones" - fixWingCap option - filter water owned zones for groundTroops 2.0.1 - RGBA colors can be entered hex style #ff340799 +2.1.0 - dmlZones + - full support for multiple out flags + - "Neutral (C)" returned for ownership if contested owner + - corrected some typos in output text + - method support for individual owned zones + - method support for global (config) output + - moved drawZone to cfxZones --]]-- cfxOwnedZones.requiredLibs = { @@ -72,6 +79,7 @@ end function cfxOwnedZones.side2name(theSide) if theSide == 1 then return "REDFORCE" end if theSide == 2 then return "BLUEFORCE" end + if theSide == 3 then return "Neutral (C)" end return "Neutral" end @@ -79,7 +87,7 @@ function cfxOwnedZones.conqTemplate(aZone, newOwner, lastOwner) if true then return end -- do not output if lastOwner == 0 then - trigger.action.outText(cfxOwnedZones.side2name(newOwner) .. " have taken possession zone " .. aZone.name, 30) + trigger.action.outText(cfxOwnedZones.side2name(newOwner) .. " have taken possession of zone " .. aZone.name, 30) return end @@ -107,18 +115,8 @@ function cfxOwnedZones.drawZoneInMap(aZone) lineColor = aZone.neutralLine -- {0.8, 0.8, 0.8, 1.0} fillColor = aZone.neutralFill -- {0.8, 0.8, 0.8, 0.2} end - --- local theShape = 2 -- circle - local markID = dcsCommon.numberUUID() - - if aZone.isCircle then - trigger.action.circleToAll(-1, markID, aZone.point, aZone.radius, lineColor, fillColor, 1, true, "") - else - local poly = aZone.poly - trigger.action.quadToAll(-1, markID, poly[4], poly[3], poly[2], poly[1], lineColor, fillColor, 1, true, "") -- note: left winding to get fill color - end - - aZone.markID = markID + + aZone.markID = aZone:drawZone(lineColor, fillColor) -- markID end function cfxOwnedZones.getOwnedZoneByName(zName) @@ -129,44 +127,46 @@ function cfxOwnedZones.getOwnedZoneByName(zName) end function cfxOwnedZones.addOwnedZone(aZone) - local owner = aZone.owner --cfxZones.getCoalitionFromZoneProperty(aZone, "owner", 0) -- is already read + local owner = aZone.owner - if cfxZones.hasProperty(aZone, "conquered!") then - aZone.conqueredFlag = cfxZones.getStringFromZoneProperty(aZone, "conquered!", "*") + if aZone:hasProperty("conquered!") then + aZone.conqueredFlag = aZone:getStringFromZoneProperty("conquered!", "*") end - if cfxZones.hasProperty(aZone, "redCap!") then - aZone.redCap = cfxZones.getStringFromZoneProperty(aZone, "redCap!", "none") + if aZone:hasProperty("redCap!") then + aZone.redCap = aZone:getStringFromZoneProperty("redCap!", "none") end - if cfxZones.hasProperty(aZone, "redLost!") then - aZone.redLost = cfxZones.getStringFromZoneProperty(aZone, "redLost!", "none") + if aZone:hasProperty("redLost!") then + aZone.redLost = aZone:getStringFromZoneProperty("redLost!", "none") end - if cfxZones.hasProperty(aZone, "blueCap!") then - aZone.blueCap = cfxZones.getStringFromZoneProperty(aZone, "blueCap!", "none") + if aZone:hasProperty("blueCap!") then + aZone.blueCap = aZone:getStringFromZoneProperty("blueCap!", "none") end - if cfxZones.hasProperty(aZone, "blueLost!") then - aZone.blueLost = cfxZones.getStringFromZoneProperty(aZone, "blueLost!", "none") + if aZone:hasProperty("blueLost!") then + aZone.blueLost = aZone:getStringFromZoneProperty("blueLost!", "none") end - if cfxZones.hasProperty(aZone, "neutral!") then - aZone.neutralCap = cfxZones.getStringFromZoneProperty(aZone, "neutral!", "none") + if aZone:hasProperty("neutral!") then + aZone.neutralCap = aZone:getStringFromZoneProperty("neutral!", "none") end - if cfxZones.hasProperty(aZone, "ownedBy#") then - aZone.ownedBy = cfxZones.getStringFromZoneProperty(aZone, "ownedBy#", "none") - elseif cfxZones.hasProperty(aZone, "ownedBy") then - aZone.ownedBy = cfxZones.getStringFromZoneProperty(aZone, "ownedBy", "none") + if aZone:hasProperty("ownedBy#") then + aZone.ownedBy = aZone:getStringFromZoneProperty("ownedBy#", "none") + elseif aZone:hasProperty("ownedBy") then + aZone.ownedBy = aZone:getStringFromZoneProperty("ownedBy", "none") end - aZone.unbeatable = cfxZones.getBoolFromZoneProperty(aZone, "unbeatable", false) - aZone.untargetable = cfxZones.getBoolFromZoneProperty(aZone, "untargetable", false) + aZone.unbeatable = aZone:getBoolFromZoneProperty("unbeatable", false) + aZone.untargetable = aZone:getBoolFromZoneProperty("untargetable", false) - aZone.hidden = cfxZones.getBoolFromZoneProperty(aZone, "hidden", false) + aZone.hidden = aZone:getBoolFromZoneProperty("hidden", false) -- individual colors, else default from config - aZone.redLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "redLine", cfxOwnedZones.redLine) - aZone.redFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "redFill", cfxOwnedZones.redFill) - aZone.blueLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "blueLine", cfxOwnedZones.blueLine) - aZone.blueFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "blueFill", cfxOwnedZones.blueFill) - aZone.neutralLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "neutralLine", cfxOwnedZones.neutralLine) - aZone.neutralFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "neutralFill", cfxOwnedZones.neutralFill) + aZone.redLine = aZone:getRGBAVectorFromZoneProperty("redLine", cfxOwnedZones.redLine) + aZone.redFill = aZone:getRGBAVectorFromZoneProperty("redFill", cfxOwnedZones.redFill) + aZone.blueLine = aZone:getRGBAVectorFromZoneProperty("blueLine", cfxOwnedZones.blueLine) + aZone.blueFill = aZone:getRGBAVectorFromZoneProperty("blueFill", cfxOwnedZones.blueFill) + aZone.neutralLine = aZone:getRGBAVectorFromZoneProperty("neutralLine", cfxOwnedZones.neutralLine) + aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill) + + aZone.method = aZone:getStringFromZoneProperty("method", "inc") cfxOwnedZones.zones[aZone] = aZone cfxOwnedZones.drawZoneInMap(aZone) @@ -178,32 +178,36 @@ end function cfxOwnedZones.bangNeutral(value) if not cfxOwnedZones.neutralTriggerFlag then return end - local newVal = trigger.misc.getUserFlag(cfxOwnedZones.neutralTriggerFlag) + value - trigger.action.setUserFlag(cfxOwnedZones.neutralTriggerFlag, newVal) + --local newVal = trigger.misc.getUserFlag(cfxOwnedZones.neutralTriggerFlag) + value + --trigger.action.setUserFlag(cfxOwnedZones.neutralTriggerFlag, newVal) + cfxZones.pollFlag(cfxOwnedZones.neutralTriggerFlag, cfxOwnedZones.method, cfxOwnedZones) end -function cfxOwnedZones.bangRed(value) +function cfxOwnedZones.bangRed(value, theZone) if not cfxOwnedZones.redTriggerFlag then return end - local newVal = trigger.misc.getUserFlag(cfxOwnedZones.redTriggerFlag) + value - trigger.action.setUserFlag(cfxOwnedZones.redTriggerFlag, newVal) + --local newVal = trigger.misc.getUserFlag(cfxOwnedZones.redTriggerFlag) + value + --trigger.action.setUserFlag(cfxOwnedZones.redTriggerFlag, newVal) + cfxZones.pollFlag(cfxOwnedZones.redTriggerFlag, cfxOwnedZones.method, cfxOwnedZones) end -function cfxOwnedZones.bangBlue(value) +function cfxOwnedZones.bangBlue(value, theZone) if not cfxOwnedZones.blueTriggerFlag then return end local newVal = trigger.misc.getUserFlag(cfxOwnedZones.blueTriggerFlag) + value - trigger.action.setUserFlag(cfxOwnedZones.blueTriggerFlag, newVal) + -- trigger.action.setUserFlag(cfxOwnedZones.blueTriggerFlag, newVal) + -- cfxZones.setFlagValue(cfxOwnedZones.blueTriggerFlag, newVal, cfxOwnedZones) + cfxZones.pollFlag(cfxOwnedZones.blueTriggerFlag, cfxOwnedZones.method, cfxOwnedZones) end -function cfxOwnedZones.bangSide(theSide, value) +function cfxOwnedZones.bangSide(theSide, value, theZone) if theSide == 2 then - cfxOwnedZones.bangBlue(value) + cfxOwnedZones.bangBlue(value, theZone) return end if theSide == 1 then - cfxOwnedZones.bangRed(value) + cfxOwnedZones.bangRed(value, theZone) return end - cfxOwnedZones.bangNeutral(value) + cfxOwnedZones.bangNeutral(value, theZone) end function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral 1 = RED 2 = BLUE @@ -231,35 +235,35 @@ function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral end if aZone.conqueredFlag then - cfxZones.pollFlag(aZone.conqueredFlag, "inc", aZone) + aZone:pollFlag(aZone.conqueredFlag, aZone.method) end if theSide == 1 and aZone.redCap then - cfxZones.pollFlag(aZone.redCap, "inc", aZone) + aZone:pollFlag(aZone.redCap, aZone.method) end if formerOwner == 1 and aZone.redLost then - cfxZones.pollFlag(aZone.redLost, "inc", aZone) + aZone:pollFlag(aZone.redLost, aZone.method) end if theSide == 2 and aZone.blueCap then - cfxZones.pollFlag(aZone.blueCap, "inc", aZone) + aZone:pollFlag(aZone.blueCap, aZone.method) end if formerOwner == 2 and aZone.blueLost then - cfxZones.pollFlag(aZone.blueLost, "inc", aZone) + aZone:pollFlag(aZone.blueLost, aZone.method) end if theSide == 0 and aZone.neutralCap then - cfxZones.pollFlag(aZone.neutralCap, "inc", aZone) + aZone:pollFlag(aZone.neutralCap, aZone.method) end -- invoke callbacks now cfxOwnedZones.invokeConqueredCallbacks(aZone, theSide, formerOwner) -- bang! flag support - cfxOwnedZones.bangSide(theSide, 1) -- winner - cfxOwnedZones.bangSide(formerOwner, -1) -- loser + cfxOwnedZones.bangSide(theSide, 1, aZone) -- winner + cfxOwnedZones.bangSide(formerOwner, -1, aZone) -- loser -- update map cfxOwnedZones.drawZoneInMap(aZone) -- update status in map. will erase previous version @@ -313,13 +317,13 @@ function cfxOwnedZones.update() if cfxOwnedZones.fastEval then -- we only check first unit that is alive local theUnit = dcsCommon.getGroupUnit(aGroup) - if theUnit and (not theUnit:inAir()) and cfxZones.unitInZone(theUnit, theZone) then + if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then theZone.numRed = theZone.numRed + aGroup:getSize() end else local allUnits = aGroup:getUnits() for idy, theUnit in pairs(allUnits) do - if (not theUnit:inAir()) and cfxZones.unitInZone(theUnit, theZone) then + if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then theZone.numRed = theZone.numRed + 1 end end @@ -332,13 +336,13 @@ function cfxOwnedZones.update() if cfxOwnedZones.fastEval then -- we only check first unit that is alive local theUnit = dcsCommon.getGroupUnit(aGroup) - if theUnit and (not theUnit:inAir()) and cfxZones.unitInZone(theUnit, theZone) then + if theUnit and (not theUnit:inAir()) and theZone:unitInZone(theUnit) then theZone.numBlue = theZone.numBlue + aGroup:getSize() end else local allUnits = aGroup:getUnits() for idy, theUnit in pairs(allUnits) do - if (not theUnit:inAir()) and cfxZones.unitInZone(theUnit, theZone) then + if (not theUnit:inAir()) and theZone:unitInZone(theUnit) then theZone.numBlue = theZone.numBlue + 1 end end @@ -423,7 +427,7 @@ function cfxOwnedZones.update() -- update ownership flag if exists if theZone.ownedBy then - cfxZones.setFlagValueMult(theZone.ownedBy, theZone.owner, theZone) + theZone:setFlagValue(theZone.ownedBy, theZone.owner) end -- now add this zone to relevant side @@ -440,30 +444,30 @@ function cfxOwnedZones.update() -- update totals if cfxOwnedZones.redOwned then - cfxZones.setFlagValueMult(cfxOwnedZones.redOwned, redZoneNum, cfxOwnedZones) + cfxZones.setFlagValue(cfxOwnedZones.redOwned, redZoneNum, cfxOwnedZones) end if cfxOwnedZones.blueOwned then - cfxZones.setFlagValueMult(cfxOwnedZones.blueOwned, blueZoneNum, cfxOwnedZones) + cfxZones.setFlagValue(cfxOwnedZones.blueOwned, blueZoneNum, cfxOwnedZones) end if cfxOwnedZones.neutralOwned then - cfxZones.setFlagValueMult(cfxOwnedZones.neutralOwned, greyZoneNum, cfxOwnedZones) + cfxZones.setFlagValue(cfxOwnedZones.neutralOwned, greyZoneNum, cfxOwnedZones) end if cfxOwnedZones.totalOwnedZones then - cfxZones.setFlagValueMult(cfxOwnedZones.totalOwnedZones, totalZoneNum, cfxOwnedZones) + cfxZones.setFlagValue(cfxOwnedZones.totalOwnedZones, totalZoneNum, cfxOwnedZones) end -- see if one side owns all and bang the flags if requiredLibs if cfxOwnedZones.allBlue and not cfxOwnedZones.hasAllBlue then if cfxOwnedZones.sideOwnsAll(2) then - cfxZones.pollFlag(cfxOwnedZones.allBlue, "inc", cfxOwnedZones) + cfxZones.pollFlag(cfxOwnedZones.allBlue, cfxOwnedZones.method, cfxOwnedZones) cfxOwnedZones.hasAllBlue = true end end if cfxOwnedZones.allRed and not cfxOwnedZones.hasAllRed then if cfxOwnedZones.sideOwnsAll(1) then - cfxZones.pollFlag(cfxOwnedZones.allRed, "inc", cfxOwnedZones) + cfxZones.pollFlag(cfxOwnedZones.allRed, cfxOwnedZones.method, cfxOwnedZones) cfxOwnedZones.hasAllRed = true end end @@ -501,7 +505,7 @@ function cfxOwnedZones.collectZones(mode) if mode == "land" then local landZones = {} for idx, theZone in pairs(cfxOwnedZones.zones) do - p = cfxZones.getPoint(theZone) + p = theZone:getPoint() p.y = p.z local surfType = land.getSurfaceType(p) if surfType == 3 then @@ -539,7 +543,7 @@ function cfxOwnedZones.getNearestOwnedZoneToPoint(aPoint) local allZones = cfxOwnedZones.collectZones() for zKey, aZone in pairs(allZones) do - local zPoint = cfxZones.getPoint(aZone) + local zPoint = aZone:getPoint() currDist = dcsCommon.dist(zPoint, aPoint) if aZone.untargetable ~= true and currDist < shortestDist then @@ -554,10 +558,10 @@ end function cfxOwnedZones.getNearestOwnedZone(theZone) local shortestDist = math.huge local closestZone = nil - local aPoint = cfxZones.getPoint(theZone) + local aPoint = theZone:getPoint() local allZones = cfxOwnedZones.collectZones() for zKey, aZone in pairs(allZones) do - local zPoint = cfxZones.getPoint(aZone) + local zPoint = aZone:getPoint() currDist = dcsCommon.dist(zPoint, aPoint) if aZone.untargetable ~= true and currDist < shortestDist then shortestDist = currDist @@ -575,13 +579,13 @@ function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral) local allZones = cfxOwnedZones.collectZones() local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner) if not ourEnemy then return nil end -- we called for a neutral zone. they have no enemies - local zPoint = cfxZones.getPoint(theZone) + local zPoint = theZone:getPoint() for zKey, aZone in pairs(allZones) do if targetNeutral then -- return all zones that do not belong to us if aZone.owner ~= theZone.owner then - local aPoint = cfxZones.getPoint(aZone) + local aPoint = aZone:getPoint() currDist = dcsCommon.dist(aPoint, zPoint) if aZone.untargetable ~= true and currDist < shortestDist then shortestDist = currDist @@ -591,7 +595,7 @@ function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral) else -- return zones that are taken by the Enenmy if aZone.owner == ourEnemy then -- only check own zones - local aPoint = cfxZones.getPoint(aZone) + local aPoint = aZone:getPoint() currDist = dcsCommon.dist(zPoint, aPoint) if aZone.untargetable ~= true and currDist < shortestDist then shortestDist = currDist @@ -610,14 +614,14 @@ function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral) local closestZone = nil local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner) if not ourEnemy then return nil end -- we called for a neutral zone. they have no enemies nor friends, all zones would be legal. - local zPoint = cfxZones.getPoint(theZone) + local zPoint = theZone:getPoint() local allZones = cfxOwnedZones.collectZones() for zKey, aZone in pairs(allZones) do if targetNeutral then -- target all zones that do not belong to the enemy if aZone.owner ~= ourEnemy then - local aPoint = cfxZones.getPoint(aZone) + local aPoint = aZone:getPoint() currDist = dcsCommon.dist(zPoint, aPoint) if aZone.untargetable ~= true and currDist < shortestDist then shortestDist = currDist @@ -627,7 +631,7 @@ function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral) else -- only target zones that are taken by us if aZone.owner == theZone.owner then -- only check own zones - local aPoint = cfxZones.getPoint(aZone) + local aPoint = aZone:getPoint() currDist = dcsCommon.dist(zPoint, aPoint) if aZone.untargetable ~= true and currDist < shortestDist then shortestDist = currDist @@ -659,7 +663,7 @@ function cfxOwnedZones.saveData() local zoneData = {} if theZone.conqueredFlag then - zoneData.conquered = cfxZones.getFlagValue(theZone.conqueredFlag, theZone) + zoneData.conquered = theZone:getFlagValue(theZone.conqueredFlag) end zoneData.owner = theZone.owner @@ -700,7 +704,7 @@ function cfxOwnedZones.loadData() if theZone then theZone.owner = zData.owner if zData.conquered then - cfxZones.setFlagValue(theZone.conqueredFlag, zData.conquered, theZone) + theZone:setFlagValue(theZone.conqueredFlag, zData.conquered) end -- update mark in map cfxOwnedZones.drawZoneInMap(theZone) @@ -724,72 +728,73 @@ function cfxOwnedZones.readConfigZone(theZone) if not theZone then theZone = cfxZones.createSimpleZone("ownedZonesConfig") end cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones - cfxOwnedZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) - cfxOwnedZones.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true) + cfxOwnedZones.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) + cfxOwnedZones.announcer = theZone:getBoolFromZoneProperty("announcer", true) - if cfxZones.hasProperty(theZone, "r!") then - cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r!", "*") + if theZone:hasProperty("r!") then + cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r!", "*") else - cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r#", "*") + cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r#", "*") end - if cfxZones.hasProperty(theZone, "b!") then - cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b!", "*") + if theZone:hasProperty("b!") then + cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("b!", "*") else - cfxOwnedZones.blueTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b#", "*") + cfxOwnedZones.blueTriggerFlag = theZone:getStringFromZoneProperty("b#", "*") end - if cfxZones.hasProperty(theZone, "n!") then - cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n!", "*") + if theZone:hasProperty("n!") then + cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("n!", "*") else - cfxOwnedZones.neutralTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n#", "*") + cfxOwnedZones.neutralTriggerFlag = theZone:getStringFromZoneProperty("n#", "*") end -- allXXX flags - if cfxZones.hasProperty(theZone, "allBlue!") then - cfxOwnedZones.allBlue = cfxZones.getStringFromZoneProperty(theZone, "allBlue!", "*") + if theZone:hasProperty("allBlue!") then + cfxOwnedZones.allBlue = theZone:getStringFromZoneProperty( "allBlue!", "*") cfxOwnedZones.hasAllBlue = nil end - if cfxZones.hasProperty(theZone, "allRed!") then - cfxOwnedZones.allRed = cfxZones.getStringFromZoneProperty(theZone, "allRed!", "*") + if theZone:hasProperty("allRed!") then + cfxOwnedZones.allRed = theZone:getStringFromZoneProperty("allRed!", "*") cfxOwnedZones.hasAllRed = nil end - if cfxZones.hasProperty(theZone, "redOwned#") then - cfxOwnedZones.redOwned = cfxZones.getStringFromZoneProperty(theZone, "redOwned#", "*") + if theZone:hasProperty("redOwned#") then + cfxOwnedZones.redOwned = theZone:getStringFromZoneProperty("redOwned#", "*") end - if cfxZones.hasProperty(theZone, "blueOwned#") then - cfxOwnedZones.blueOwned = cfxZones.getStringFromZoneProperty(theZone, "blueOwned#", "*") + if theZone:hasProperty("blueOwned#") then + cfxOwnedZones.blueOwned = theZone:getStringFromZoneProperty( "blueOwned#", "*") end - if cfxZones.hasProperty(theZone, "neutralOwned#") then - cfxOwnedZones.neutralOwned = cfxZones.getStringFromZoneProperty(theZone, "neutralOwned#", "*") + if theZone:hasProperty("neutralOwned#") then + cfxOwnedZones.neutralOwned = theZone:getStringFromZoneProperty("neutralOwned#", "*") end - if cfxZones.hasProperty(theZone, "totalZones#") then - cfxOwnedZones.totalOwnedZones = cfxZones.getStringFromZoneProperty(theZone, "totalZones#", "*") + if theZone:hasProperty("totalZones#") then + cfxOwnedZones.totalOwnedZones = theZone:getStringFromZoneProperty("totalZones#", "*") end -- numKeep, numCap, fastEval, easyContest - cfxOwnedZones.numCap = cfxZones.getNumberFromZoneProperty(theZone, "numCap", 1) -- minimal number of units required to cap zone - cfxOwnedZones.numKeep = cfxZones.getNumberFromZoneProperty(theZone, "numKeep", 0) -- number required to keep zone - cfxOwnedZones.fastEval = cfxZones.getBoolFromZoneProperty(theZone, "fastEval", true) - cfxOwnedZones.easyContest = cfxZones.getBoolFromZoneProperty(theZone, "easyContest", false) + cfxOwnedZones.numCap = theZone:getNumberFromZoneProperty("numCap", 1) -- minimal number of units required to cap zone + cfxOwnedZones.numKeep = theZone:getNumberFromZoneProperty("numKeep", 0) -- number required to keep zone + cfxOwnedZones.fastEval = theZone:getBoolFromZoneProperty("fastEval", true) + cfxOwnedZones.easyContest = theZone:getBoolFromZoneProperty("easyContest", false) -- winSound, loseSound - cfxOwnedZones.winSound = cfxZones.getStringFromZoneProperty(theZone, "winSound", "Quest Snare 3.wav" ) - cfxOwnedZones.loseSound = cfxZones.getStringFromZoneProperty(theZone, "loseSound", "Death BRASS.wav") + cfxOwnedZones.winSound = theZone:getStringFromZoneProperty("winSound", "Quest Snare 3.wav") + cfxOwnedZones.loseSound = theZone:getStringFromZoneProperty("loseSound", "Death BRASS.wav") -- capture options - cfxOwnedZones.groundCap = cfxZones.getBoolFromZoneProperty(theZone, "groundCap", true) - cfxOwnedZones.navalCap = cfxZones.getBoolFromZoneProperty(theZone, "navalCap", false) - cfxOwnedZones.heloCap = cfxZones.getBoolFromZoneProperty(theZone, "heloCap") - cfxOwnedZones.fixWingCap = cfxZones.getBoolFromZoneProperty(theZone, "fixWingCap") + cfxOwnedZones.groundCap = theZone:getBoolFromZoneProperty("groundCap", true) + cfxOwnedZones.navalCap = theZone:getBoolFromZoneProperty("navalCap", false) + cfxOwnedZones.heloCap = theZone:getBoolFromZoneProperty("heloCap") + cfxOwnedZones.fixWingCap = theZone:getBoolFromZoneProperty("fixWingCap") -- colors for line and fill - cfxOwnedZones.redLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "redLine", {1.0, 0, 0, 1.0}) - cfxOwnedZones.redFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "redFill", {1.0, 0, 0, 0.2}) - cfxOwnedZones.blueLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "blueLine", {0.0, 0, 1.0, 1.0}) - cfxOwnedZones.blueFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "blueFill", {0.0, 0, 1.0, 0.2}) - cfxOwnedZones.neutralLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "neutralLine", {0.8, 0.8, 0.8, 1.0}) - cfxOwnedZones.neutralFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "neutralFill", {0.8, 0.8, 0.8, 0.2}) + cfxOwnedZones.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0}) + cfxOwnedZones.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", {1.0, 0, 0, 0.2}) + cfxOwnedZones.blueLine = theZone:getRGBAVectorFromZoneProperty("blueLine", {0.0, 0, 1.0, 1.0}) + cfxOwnedZones.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2}) + cfxOwnedZones.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0}) + cfxOwnedZones.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2}) + cfxOwnedZones.method = theZone:getStringFromZoneProperty("method", "inc") end function cfxOwnedZones.init() diff --git a/modules/cfxPlayerScore.lua b/modules/cfxPlayerScore.lua index 28eae86..a218352 100644 --- a/modules/cfxPlayerScore.lua +++ b/modules/cfxPlayerScore.lua @@ -1444,11 +1444,11 @@ function cfxPlayerScore.start() -- identify and process a score table zones local theZone = cfxZones.getZoneByName("playerScoreTable") if not theZone then - trigger.action.outText("+++scr: no score table!", 30) +-- trigger.action.outText("+++scr: no score table!", 30) else -- read all into my types registry, replacing whatever is there cfxPlayerScore.typeScore = cfxZones.getAllZoneProperties(theZone) - trigger.action.outText("+++scr: read score table", 30) +-- trigger.action.outText("+++scr: read score table", 30) end -- read score tiggers and values @@ -1486,7 +1486,7 @@ function cfxPlayerScore.start() -- now read my config zone local theZone = cfxZones.getZoneByName("playerScoreConfig") if not theZone then - trigger.action.outText("+++pScr: no config!", 30) +-- trigger.action.outText("+++pScr: no config!", 30) theZone = cfxZones.createSimpleZone("playerScoreConfig") end cfxPlayerScore.readConfigZone(theZone) diff --git a/modules/cfxSpawnZones.lua b/modules/cfxSpawnZones.lua index 6cfd73c..97ca160 100644 --- a/modules/cfxSpawnZones.lua +++ b/modules/cfxSpawnZones.lua @@ -1,5 +1,5 @@ cfxSpawnZones = {} -cfxSpawnZones.version = "2.0.0" +cfxSpawnZones.version = "2.0.1" cfxSpawnZones.requiredLibs = { "dcsCommon", -- common is of course needed for everything -- pretty stupid to check for this since we @@ -72,6 +72,7 @@ cfxSpawnZones.spawnedGroups = {} - moved "types" to spawner - baseName defaults to zone name, as it is safe for naming - spawnWithSpawner direct link in spawner to spawnZones + 2.0.1 - fix in verifySpawnOwnership() when not master zone found --]]-- cfxSpawnZones.allSpawners = {} @@ -275,6 +276,7 @@ function cfxSpawnZones.verifySpawnOwnership(spawner) local masterZone = cfxZones.getZoneByName(spawner.masterZoneName) if not masterZone then trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30) + return false end if not masterZone.owner then diff --git a/modules/cfxZones.lua b/modules/cfxZones.lua index e3cae67..5140281 100644 --- a/modules/cfxZones.lua +++ b/modules/cfxZones.lua @@ -1,117 +1,14 @@ cfxZones = {} -cfxZones.version = "4.0.5" +cfxZones.version = "4.0.7" -- cf/x zone management module -- reads dcs zones and makes them accessible and mutable -- by scripting. -- --- Copyright (c) 2021, 2022 by Christian Franz and cf/x AG +-- Copyright (c) 2021 - 2023 by Christian Franz and cf/x AG -- --[[-- VERSION HISTORY - - 2.2.4 - getCoalitionFromZoneProperty - - getStringFromZoneProperty - - 2.2.5 - createGroundUnitsInZoneForCoalition corrected coalition --> country - - 2.2.6 - getVectorFromZoneProperty(theZone, theProperty, defaultVal) - - 2.2.7 - allow 'yes' as 'true' for boolean attribute - - 2.2.8 - getBoolFromZoneProperty supports default - - cfxZones.hasProperty - - 2.3.0 - property names are case insensitive - - 2.3.1 - getCoalitionFromZoneProperty allows 0, 1, 2 also - - 2.4.0 - all zones look for owner attribute, and set it to 0 (neutral) if not present - - 2.4.1 - getBoolFromZoneProperty upgraded by expected bool - - markZoneWithSmoke raised by 3 meters - - 2.4.2 - getClosestZone also returns delta - - 2.4.3 - getCoalitionFromZoneProperty() accepts 'all' as neutral - createUniqueZoneName() - getStringFromZoneProperty returns default if property value = "" - corrected bug in addZoneToManagedZones - - 2.4.4 - getPoint(aZone) returns uip-to-date pos for linked and normal zones - - linkUnit can use "useOffset" property to keep relative position - - 2.4.5 - updated various methods to support getPoint when referencing - zone.point - - 2.4.6 - corrected spelling in markZoneWithSmoke - - 2.4.7 - copy reference to dcs zone into cfx zone - - 2.4.8 - getAllZoneProperties - - 2.4.9 - createSimpleZone no longer requires location - - parse dcs adds empty .properties = {} if none tehre - - createCircleZone adds empty properties - - createPolyZone adds empty properties - - 2.4.10 - pickRandomZoneFrom now defaults to all cfxZones.zones - - getBoolFromZoneProperty also recognizes 0, 1 - - removed autostart - - 2.4.11 - removed typo in get closest zone - - 2.4.12 - getStringFromZoneProperty - - 2.5.0 - harden getZoneProperty and all getPropertyXXXX - - 2.5.1 - markZoneWithSmoke supports alt attribute - - 2.5.2 - getPoint also writes through to zone itself for optimization - - new method getPositiveRangeFromZoneProperty(theZone, theProperty, default) - - 2.5.3 - new getAllGroupsInZone() - - 2.5.4 - cleaned up getZoneProperty break on no properties - - extractPropertyFromDCS trims key and property - - 2.5.5 - pollFlag() centralized for banging - - allStaticsInZone - - 2.5.6 - flag accessor setFlagValue(), getFlagValue() - - pollFlag supports theZone as final parameter - - randomDelayFromPositiveRange - - isMEFlag - - 2.5.7 - pollFlag supports dml flags - - 2.5.8 - flagArrayFromString - - getFlagNumber invokes tonumber() before returning result - - 2.5.9 - removed pass-back flag in getPoint() - - 2.6.0 - testZoneFlag() method based flag testing - - 2.6.1 - Watchflag parsing of zone condition for number-named flags - - case insensitive - - verbose for zone-local accepted (but not acted upon) - - hasProperty now offers active information when looking for '*?' and '*!' - - 2.7.0 - doPollFlag - fully support multiple flags per bang! - - 2.7.1 - setFlagValueMult() - - 2.7.2 - '261 repair' - - 2.7.3 - testZoneFlag returns mathodResult, lastVal - - evalFlagMethodImmediate() - - 2.7.4 - doPollFlag supports immediate number setting - - 2.7.5 - more QoL checks when mixing up ? and ! for attributes - - 2.7.6 - trim for getBoolFromZoneProperty and getStringFromZoneProperty - - 2.7.7 - randomInRange() - - show number of zones - - 2.7.8 - inc method now triggers if curr value > last value - - dec method noew triggers when curr value < last value - - testFlagByMethodForZone supports lohi, hilo transitions - - doPollFlag supports 'pulse' - - pulseFlag - - unpulse -- 2.7.9 - getFlagValue QoL for - - setFlagValue QoL for -- 2.8.0 - new allGroupNamesInZone() -- 2.8.1 - new zonesLinkedToUnit() -- 2.8.2 - flagArrayFromString trims elements before range check -- 2.8.3 - new verifyMethod() - - changed extractPropertyFromDCS() to also match attributes with blanks like "the Attr" to "theAttr" - - new expandFlagName() -- 2.8.4 - fixed bug in setFlagValue() -- 2.8.5 - createGroundUnitsInZoneForCoalition() now always passes back a copy of the group data - - data also contains cty = country and cat = category for easy spawn - - getFlagValue additional zone name guards -- 2.8.6 - fix in getFlagValue for missing delay -- 2.8.7 - update isPointInsideZone(thePoint, theZone, radiusIncrease) - new radiusIncrease - - isPointInsideZone() returns delta as well -- 2.9.0 - linked zones can useOffset and useHeading - - getPoint update - - pointInZone understands useOrig - - allStaticsInZone supports useOrig - - dPhi for zones with useHeading - - uHdg for zones with useHading, contains linked unit's original heading - - Late-linking implemented: - - linkUnit works for late-activating units - - linkUnit now also works for player / clients, dynamic (re-)linking - - linkUnit uses zone's origin for all calculations -- 2.9.1 - new evalRemainder() - - pollFlag supports +/- for immediate numbers, flags, number flags in parantheses - - stronger guards in hasProperty -- 2.9.2 - new createRandomPointInPolyZone() - - createRandomZoneInZone uses createRandomPointInPolyZone - - new createRandomPointInZone() - - new randomPointInZone() - 3.0.0 - support for DCS 2.8 linkUnit attribute, integration with linedUnit and warning. - initZoneVerbosity() @@ -157,6 +54,8 @@ cfxZones.version = "4.0.5" - doSetFlagValue optimizations - 4.0.5 - dynamicAB wildcard - processDynamicValueVU +- 4.0.6 - hash mark forgotten QoL +- 4.0.7 - drawZone() --]]-- @@ -2208,6 +2107,30 @@ function cfxZones.flagArrayFromString(inString) -- dcsCommon bridge end +-- +-- Drawing a Zone +-- + +function cfxZones.drawZone(theZone, lineColor, fillColor, markID) + if not theZone then return 0 end + if not lineColor then lineColor = {0.8, 0.8, 0.8, 1.0} end + if not fillColor then fillColor = {0.8, 0.8, 0.8, 0.2} end + if not markID then markID = dcsCommon.numberUUID() end + + if theZone.isCircle then + trigger.action.circleToAll(-1, markID, theZone.point, theZone.radius, lineColor, fillColor, 1, true, "") + else + local poly = theZone.poly + trigger.action.quadToAll(-1, markID, poly[4], poly[3], poly[2], poly[1], lineColor, fillColor, 1, true, "") -- note: left winding to get fill color + end + + return markID +end + +function dmlZone:drawZone(lineColor, fillColor, markID) + return cfxZones.drawZone(self, lineColor, fillColor, markID) +end + -- -- =================== -- PROPERTY PROCESSING @@ -2444,6 +2367,14 @@ function cfxZones.hasProperty(theZone, theProperty) return false end + if string.sub(theProperty, -1) == "#" then + local lessOp = theProperty:sub(1,-2) + if cfxZones.getZoneProperty(theZone, lessOp) ~= nil then + trigger.action.outText("*** NOTE: " .. theZone.name .. "'s property <" .. lessOp .. "> may be missing a hash mark ('#') at end", 30) + end + return false + end + return false end return true diff --git a/modules/limitedAirframes.lua b/modules/limitedAirframes.lua index 46aa202..1efe929 100644 --- a/modules/limitedAirframes.lua +++ b/modules/limitedAirframes.lua @@ -1,25 +1,13 @@ limitedAirframes = {} -limitedAirframes.version = "1.5.4" -limitedAirframes.verbose = false -limitedAirframes.enabled = true -- can be turned off -limitedAirframes.userCanToggle = true -- F10 menu? -limitedAirframes.onlyOwnSide = true -- F10 query only shows own side count, for later expansion -limitedAirframes.maxRed = -1 -- -1 == infinite -limitedAirframes.maxBlue = -1 -- = infinite -limitedAirframes.redWinsFlag = "999" -limitedAirframes.blueWinsFlag = "998" -limitedAirframes.method = "inc" +limitedAirframes.version = "1.6.0" + limitedAirframes.warningSound = "Quest Snare 3.wav" limitedAirframes.loseSound = "Death PIANO.wav" limitedAirframes.winSound = "Triumphant Victory.wav" -limitedAirframes.announcer = true limitedAirframes.requiredLibs = { - "dcsCommon", -- common is of course needed for everything - -- pretty stupid to check for this since we - -- need common to invoke the check, but anyway - "cfxZones", -- Zones, of course for safe landings - -- "cfxPlayer", no longer needed + "dcsCommon", + "cfxZones", } --[[-- VERSION HISTORY @@ -57,6 +45,10 @@ limitedAirframes.requiredLibs = { - 1.5.3 - ... but do allow it if not coming from 'ejected' so ditching a plane will again create CSAR missions 1.5.4 - red# and blue# instead of #red and #blue + 1.6.0 - dmlZones + - new hasUI attribute + - minor clean-up + - set numRed and numBlue on startup --]]-- @@ -66,26 +58,15 @@ limitedAirframes.requiredLibs = { -- when the number reaches -1 or smaller, other side wins -- !!!Only affects player planes!! --- *** EXTENDS ZONES *** -- safe zones must have a property "pilotSafe" -- - pilotSafe - this is a zone to safely change airframes in -- - can also carry 'red' or 'blue' to enable --- - redSafe (optional, defaults to true) --- - blueSafe (optional, defaults to true) --- set to "false" or "no" to disallow that side to change --- airframes even when safer -- if zone can change ownership, player's coalition -- is checked against current zone ownership -- zone owner. --- when red wins due to blue frame loss, flag 999 is set to true --- when blue wins due to red frame loss, flag 998 is set to true --- set a mission trigger to end mission if you want to end mission --- or simply keep running, and a CHEATER! message will flash --- every time the losing side enters a new aircraft limitedAirframes.safeZones = {} -- safezones are zones where a crash or change plane does not --- these zones are created by adding an 'pilotSafe' attribute limitedAirframes.myEvents = {5, 9, 30, 6, 20, 21, 15 } -- 5 = crash, 9 - dead, 30 - unit lost, 6 - eject, 20 - enter unit, 21 - leave unit, 15 - birth @@ -124,67 +105,71 @@ function limitedAirframes.readConfigZone() -- note: must match exactly!!!! local theZone = cfxZones.getZoneByName("limitedAirframesConfig") if not theZone then - if limitedAirframes.verbose then - trigger.action.outText("+++limA: NO config zone!", 30) - end theZone = cfxZones.createSimpleZone("limitedAirframesConfig") end - -- remember me limitedAirframes.config = theZone limitedAirframes.name = "limitedAirframes" -- so we can call cfxZones with ourself as param - limitedAirframes.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) - + limitedAirframes.verbose = theZone.verbose if limitedAirframes.verbose then trigger.action.outText("+++limA: found config zone!", 30) end -- ok, for each property, load it if it exists - limitedAirframes.enabled = cfxZones.getBoolFromZoneProperty(theZone, "enabled", true) + limitedAirframes.enabled = theZone:getBoolFromZoneProperty("enabled", true) - limitedAirframes.userCanToggle = cfxZones.getBoolFromZoneProperty(theZone, "userCanToggle", true) + limitedAirframes.userCanToggle = theZone:getBoolFromZoneProperty( "userCanToggle", true) + limitedAirframes.hasUI = theZone:getBoolFromZoneProperty("hasUI", true) + limitedAirframes.maxRed = theZone:getNumberFromZoneProperty("maxRed", -1) - limitedAirframes.maxRed = cfxZones.getNumberFromZoneProperty(theZone, "maxRed", -1) + limitedAirframes.maxBlue = theZone:getNumberFromZoneProperty("maxBlue", -1) + limitedAirframes.currRed = limitedAirframes.maxRed + limitedAirframes.currBlue = limitedAirframes.maxBlue - limitedAirframes.maxBlue = cfxZones.getNumberFromZoneProperty(theZone, "maxBlue", -1) - - if cfxZones.hasProperty(theZone, "#red") then - limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "#red", "*none") + if theZone:hasProperty("#red") then + limitedAirframes.numRed = theZone:getStringFromZoneProperty("#red", "*none") else - limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "red#", "*none") + limitedAirframes.numRed = theZone:getStringFromZoneProperty("red#", "*none") end - if cfxZones.hasProperty(theZone, "#blue") then - limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "#blue", "*none") + if theZone:hasProperty("#blue") then + limitedAirframes.numBlue = theZone:getStringFromZoneProperty("#blue", "*none") else - limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "blue#", "*none") + limitedAirframes.numBlue = theZone:getStringFromZoneProperty("blue#", "*none") end - limitedAirframes.redWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "redWins!", "*none") + limitedAirframes.redWinsFlag = theZone:getStringFromZoneProperty("redWins!", "*none") - if cfxZones.hasProperty(theZone, "redWinsFlag!") then - limitedAirframes.redWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "redWinsFlag!", "*none") + if theZone:hasProperty("redWinsFlag!") then + limitedAirframes.redWinsFlag = theZone:getStringFromZoneProperty("redWinsFlag!", "*none") end - limitedAirframes.blueWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "blueWins!", "*none") - if cfxZones.hasProperty(theZone, "blueWinsFlag!") then - limitedAirframes.blueWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "blueWinsFlag!", "*none") + limitedAirframes.blueWinsFlag = theZone:getStringFromZoneProperty("blueWins!", "*none") + if theZone:hasProperty("blueWinsFlag!") then + limitedAirframes.blueWinsFlag = theZone:getStringFromZoneProperty("blueWinsFlag!", "*none") end - limitedAirframes.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") + limitedAirframes.method = theZone:getStringFromZoneProperty("method", "inc") - if cfxZones.hasProperty(theZone, "warningSound") then - limitedAirframes.warningSound = cfxZones.getStringFromZoneProperty(theZone, "warningSound", "none") + if theZone:hasProperty("warningSound") then + limitedAirframes.warningSound = theZone:getStringFromZoneProperty("warningSound", "none") end - if cfxZones.hasProperty(theZone, "winSound") then - limitedAirframes.winSound = cfxZones.getStringFromZoneProperty(theZone, "winSound", "none") + if theZone:hasProperty("winSound") then + limitedAirframes.winSound = theZone:getStringFromZoneProperty("winSound", "none") end - if cfxZones.hasProperty(theZone, "loseSound") then - limitedAirframes.loseSound = cfxZones.getStringFromZoneProperty(theZone, "loseSound", "none") + if theZone:hasProperty("loseSound") then + limitedAirframes.loseSound = theZone:getStringFromZoneProperty("loseSound", "none") end - limitedAirframes.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true) + if limitedAirframes.numRed then + cfxZones.setFlagValue(limitedAirframes.numRed, limitedAirframes.currRed, limitedAirframes) + end + + if limitedAirframes.numBlue then + cfxZones.setFlagValue(limitedAirframes.numBlue, limitedAirframes.currBlue, limitedAirframes) + end + limitedAirframes.announcer = theZone:getBoolFromZoneProperty( "announcer", true) end -- @@ -219,7 +204,7 @@ function limitedAirframes.addPlayerUnit(theUnit) local desc = "unit <" .. uName .. "> controlled by <" .. pName .. ">" if not(limitedAirframes.isKnownUnitName(uName)) then - --desc = "+++lim: added ".. desc .. " to list of known player units" + else if limitedAirframes.playerUnits[uName] == pName then desc = "player unit <".. uName .. "> controlled by <".. limitedAirframes.playerUnits[uName].."> re-seated" @@ -269,7 +254,6 @@ function limitedAirframes.updatePlayer(pName, status) end limitedAirframes.players[pName] = status - -- if desc then trigger.action.outText(desc, 30) end end function limitedAirframes.getStatusOfPlayerInUnit(theUnit) @@ -327,7 +311,6 @@ function limitedAirframes.preProcessor(event) return false -- no longer of interest end - if not dcsCommon.isPlayerUnit(theUnit) then -- not a player unit. Events 5 and 6 have been -- handled before, so we can safely ignore @@ -376,8 +359,8 @@ function limitedAirframes.somethingHappened(event) if ID == 20 then -- 20 ENTER UNIT - local pName = limitedAirframes.getKnownUnitPilotByUnit(theUnit) - if not pName then pName = "***UNKNOWN***" end +-- local pName = limitedAirframes.getKnownUnitPilotByUnit(theUnit) +-- if not pName then pName = "***UNKNOWN***" end return end @@ -401,8 +384,7 @@ function limitedAirframes.somethingHappened(event) trigger.action.outText("limAir: ***WARNING: Ignored player event (" .. ID .. "): unable to retrieve player name for " .. unitName, 30) return -- plane no longer of interest cant retrieve pilot -- BUG!!! end - - + -- event 6 - eject - plane divorced but player pilot is known if ID == 6 then -- eject limitedAirframes.pilotEjected(event) @@ -422,7 +404,6 @@ function limitedAirframes.somethingHappened(event) -- so if pilot is still alive and not MIA, he's now dead. -- forget the helo check, this now applies to all - local pStatus = limitedAirframes.getStatusOfPlayerInUnit(theUnit) if pStatus == "alive" then -- this frame was carrrying a live player @@ -441,7 +422,7 @@ function limitedAirframes.somethingHappened(event) if ID == 21 then -- player left unit -- remove pilot name from unit name limitedAirframes.unitFlownByPlayer[unitName] = nil - --trigger.action.outText("limAir: 21 -- unit " .. unitName .. " unoccupied", 30) + if limitedAirframes.verbose then trigger.action.outText("limAir: 21 (player left) for unit " .. unitName , 30) end @@ -453,7 +434,6 @@ function limitedAirframes.somethingHappened(event) if ID == 9 then -- died - --trigger.action.outText("limAir: 9 (PILOT DEAD) for unit " .. unitName , 30) local thePilot = limitedAirframes.unitFlownByPlayer[unitName] if not thePilot then if limitedAirframes.verbose then @@ -514,27 +494,11 @@ function limitedAirframes.handlePlayerLeftUnit(event) if limitedAirframes.verbose then trigger.action.outText("+++limA: " .. theSafeZone.name .. " ownership: myside = " .. mySide .. " zone owner is " .. theSafeZone.owner, 30) end - else - end - -- check we are at rest below 10m height. agl may give - -- misreadings on carriers and FARPs, while speed may read - -- wrongly on carriers (tbd). we now use isInAir to determine if - -- we ditched while in flight - --if speed > 2 or agl > 10 then isSafe = false end - --if not isInAir then return false end -- why *not* isInAir??? - - -- for matter of fact, why did we return ANYTHING??? - -- there may be a bug here - -- maybe it should be "if isInAir then isSafe = false"?? if isInAir then isSafe = false end if isSafe then --- if limitedAirframes.announcer then --- trigger.action.outTextForCoalition(mySide, "Pilot " .. theUnit:getPlayerName() .. " left unit " .. theUnit:getName() .. " legally in zone " .. theSafeZone.name, 30) --- end - return; end end @@ -576,7 +540,6 @@ function limitedAirframes.pilotEjected(event) end function limitedAirframes.pilotDied(theUnit) - --limitedAirframes.killPlayerInUnit(theUnit) local theSide = theUnit:getCoalition() local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit) local uName = theUnit:getName() @@ -649,9 +612,7 @@ function limitedAirframes.pilotLost(theUnit) return true end trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound) --- if limitedAirframes.announcer then trigger.action.outTextForCoalition(theSide, "You have lost a pilot! Remaining: " .. limitedAirframes.currBlue, 30) --- end end return false end @@ -706,24 +667,19 @@ function limitedAirframes.addSafeZone(aZone) trigger.action.outText("WARNING: NIL Zone in addSafeZone", 30) return end - - -- transfer properties if they exist - -- blueSafe, redSafe: what side this is safe for, default = yes -- add zone to my list limitedAirframes.safeZones[aZone] = aZone -- deprecated old code. new code contains 'red, blue' in value for pilotsafe - local safeSides = cfxZones.getStringFromZoneProperty(aZone, "pilotsafe", "") + local safeSides = aZone:getStringFromZoneProperty("pilotsafe", "") safeSides = safeSides:lower() if dcsCommon.containsString(safeSides, "red") or dcsCommon.containsString(safeSides, "blue") then aZone.redSafe = dcsCommon.containsString(safeSides, "red") aZone.blueSafe = dcsCommon.containsString(safeSides, "blue") else - --aZone.redSafe = true - aZone.redSafe = cfxZones.getBoolFromZoneProperty(aZone, "redSafe", true) - --aZone.blueSafe = true - aZone.blueSafe = cfxZones.getBoolFromZoneProperty(aZone, "blueSafe", true) + aZone.redSafe = aZone:getBoolFromZoneProperty("redSafe", true) + aZone.blueSafe = aZone:getBoolFromZoneProperty("blueSafe", true) end if limitedAirframes.verbose or aZone.verbose then @@ -733,14 +689,10 @@ function limitedAirframes.addSafeZone(aZone) if aZone.blueSafe then trigger.action.outText("+++limA: <" .. aZone.name .. "> is safe for BLUE pilots", 30) end - trigger.action.outText("+++limA: added safeZone " .. aZone.name, 30) - end end - - -- -- COMMAND & CONFIGURATION -- @@ -751,10 +703,13 @@ function limitedAirframes.setCommsMenu() desc = "Pilot Count (Currently OFF)" desc2 = "ENABLE Pilot Count" end + if not limitedAirframes.userCanToggle then desc = "Pilot Count" end -- remove previous version if limitedAirframes.rootMenu then - missionCommands.removeItem(limitedAirframes.theScore) - missionCommands.removeItem(limitedAirframes.theCommand) + missionCommands.removeItem(limitedAirframes.theScore) -- frames left + if limitedAirframes.userCanToggle then + missionCommands.removeItem(limitedAirframes.theCommand) -- toggle on/off + end missionCommands.removeItem(limitedAirframes.rootMenu) end limitedAirframes.theCommand = nil @@ -765,7 +720,9 @@ function limitedAirframes.setCommsMenu() limitedAirframes.theScore = missionCommands.addCommand("How many airframes left?" , limitedAirframes.rootMenu, limitedAirframes.redirectAirframeScore, {"none"}) - limitedAirframes.theCommand = missionCommands.addCommand(desc2 , limitedAirframes.rootMenu, limitedAirframes.redirectToggleAirFrames, {"none"}) + if limitedAirframes.userCanToggle then + limitedAirframes.theCommand = missionCommands.addCommand(desc2 , limitedAirframes.rootMenu, limitedAirframes.redirectToggleAirFrames, {"none"}) + end end @@ -897,8 +854,8 @@ function limitedAirframes.start() limitedAirframes.readConfigZone() -- set output flags - cfxZones.setFlagValueMult(limitedAirframes.numBlue, limitedAirframes.currBlue, limitedAirframes.config) - cfxZones.setFlagValueMult(limitedAirframes.numRed, limitedAirframes.currRed, limitedAirframes.config) +-- cfxZones.setFlagValue(limitedAirframes.numBlue, limitedAirframes.currBlue, limitedAirframes.config) +-- cfxZones.setFlagValue(limitedAirframes.numRed, limitedAirframes.currRed, limitedAirframes.config) -- collect all zones that are airframe safe local afsZones = cfxZones.zonesWithProperty("pilotSafe") @@ -936,20 +893,20 @@ function limitedAirframes.start() -- set current values - limitedAirframes.currRed = limitedAirframes.maxRed - limitedAirframes.currBlue = limitedAirframes.maxBlue +-- limitedAirframes.currRed = limitedAirframes.maxRed +-- limitedAirframes.currBlue = limitedAirframes.maxBlue -- collect active player unit names local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw() for i=1, #allPlayerUnits do local aUnit = allPlayerUnits[i] limitedAirframes.addPlayerUnit(aUnit) --- trigger.action.outText("limAir: detected active player unit " .. aUnit:getName(), 30) end -- allow configuration menu - if limitedAirframes.userCanToggle then + --if limitedAirframes.userCanToggle then + if limitedAirframes.hasUI then limitedAirframes.setCommsMenu() end diff --git a/modules/messenger.lua b/modules/messenger.lua index 8e48dc8..c1f521a 100644 --- a/modules/messenger.lua +++ b/modules/messenger.lua @@ -1,5 +1,5 @@ messenger = {} -messenger.version = "2.3.0" +messenger.version = "2.3.1" messenger.verbose = false messenger.requiredLibs = { "dcsCommon", -- always @@ -68,6 +68,7 @@ messenger.messengers = {} 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 + 2.3.1 - triggering message AFTER the on/off switches are tested --]]-- @@ -444,14 +445,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 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 - messenger.isTriggered(aZone) - end - -- old trigger code if aZone:testZoneFlag(aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then aZone.messageOff = true if messenger.verbose or aZone.verbose then @@ -465,6 +459,15 @@ function messenger.update() trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30) end end + + 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 + messenger.isTriggered(aZone) + end + + end end diff --git a/modules/mxObjects.lua b/modules/mxObjects.lua index cb20244..9287d21 100644 --- a/modules/mxObjects.lua +++ b/modules/mxObjects.lua @@ -1,4 +1,132 @@ mxObjects = {} +mxObjects.version = "1.0.0" +mxObjects.allObjects = {} +mxObjects.textBoxes = {} +mxObjects.miscObjects = {} +mxObjects.imperial = true +mxObjects.doubleLine = true + +-- scan mission to set up object DB + +function mxObjects.scanMissionData() + if not env.mission.drawings then + trigger.action.outText("+++mxO: Mission has no object layer", 30) + return + end + local drawings = env.mission.drawings + + -- all drawings are in drawings[layer] + local layers = drawings["layers"] + if not layers then + trigger.action.outText("+++mxO: Mission has no layers in objects", 30) + return + end + + -- each layer has a "name" field that identifies the layer, and + -- per layer there are the objects. Let's flatten the structure, + -- since object names are unique + local count = 0 + for idx, aLayer in pairs(layers) do + local layerName = aLayer.name + local objects = aLayer.objects + -- scan objects in this layer + for idy, theObject in pairs (objects) do + local theData = dcsCommon.clone(theObject) + theData.dist = math.huge -- simply init field + -- make theData point-compatible, handle y<>z adapt + theData.x = theData.mapX -- set up x, y, z + if not theData.x then theData.x = 0 end + theData.y = 0 + theData.z = theData.mapY + if not theData.z then theData.z = 0 end + + if mxObjects.allObjects[theData.name] then + trigger.action.outText("+++mxO: name collision for drawing object named <" .. theData.name .. ">, skipped.", 30) + else + mxObjects.allObjects[theData.name] = theData + count = count + 1 + -- sort into quick-access "type" slots + if theData.primitiveType == "TextBox" then + mxObjects.textBoxes[theData.name] = theData + else + mxObjects.miscObjects[theData.name] = theData + end + end + end + end +end + +function mxObjects.sortObjectsInRelationTo(p, objects) + if not p then return nil end + -- calculate distance to all into new list + local disted = {} + for name, theData in pairs(objects) do + theData.dist = dcsCommon.dist(p, theData) + table.insert(disted, theData) + end + table.sort(disted, + function (e1, e2) return e1.dist < e2.dist end + ) + return disted +end + + +function mxObjects.showNClosestTextObjectsToUnit(n, theUnit, numbered) + if numbered == nil then numbered = true end + local p = theUnit:getPoint() + local headingInDegrees = dcsCommon.getUnitHeadingDegrees(theUnit) + local theList = mxObjects.sortObjectsInRelationTo(p, mxObjects.textBoxes) + local msg = "\n" + if #theList < 1 then + msg = msg .. " NO OBJECTS " + else + if n > #theList then n = #theList end + for i = 1, n do + theObject = theList[i] + local dist = theObject.dist + units = "km" + if mxObjects.imperial then + dist = dist * 3.28084 + dist = math.floor(dist * 0.0016457883895983) -- in 0.1 nautmil + units = "nm" + else + dist = math.floor(theObject.dist / 100) -- dekameters + end + dist = dist / 10 + + if numbered then + if i < 10 and n > 9 then + msg = msg .. "0" + end + msg = msg .. i .. ". " + end + -- show text + msg = msg .. theObject.text + -- bearing + local bea = dcsCommon.bearingInDegreesFromAtoB(p, theObject) + msg = msg .. " bearing " .. bea .. "°," + -- get clock position + local clockPos = dcsCommon.clockPositionOfARelativeToB(theObject, p, headingInDegrees) + msg = msg .. " your " .. clockPos .. " o'clock, " + -- dist + msg = msg .. " " .. dist .. units + msg = msg .. "\n" -- add line feed + if mxObjects.doubleLine then msg = msg .. "\n" end + end + end + return msg + +end + +function mxObjects.getClosestTo(p, objects) + if not p then return nil, nil end + local closest = nil + local theDist = math.huge + for oName, theData in pairs (objects) do + + end + +end function mxObjects.getObjectFreePoly(layerName, polyName, rel) -- omit rel to get absolute points, else pass 'true' to get relative to first point. if not rel then rel = false end -- relative or absolute @@ -50,3 +178,16 @@ function mxObjects.getObjectFreePoly(layerName, polyName, rel) -- omit rel to ge trigger.action.outText("+++mxO: no polygon named <" .. polyName .. "> in layer <" ..layerName .. ">", 30) return {} end + +function mxObjects.start() + mxObjects.scanMissionData() + trigger.action.outText("mxObjects v" .. mxObjects.version .. " loaded.", 30) + +end + +mxObjects.start() +--[[-- +local theUnit = Unit.getByName("Bannok") +local msg = mxObjects.showNClosestTextObjectsToUnit(8, theUnit, numbered) +trigger.action.outText(msg, 30) +--]]-- diff --git a/modules/ownAll.lua b/modules/ownAll.lua new file mode 100644 index 0000000..1063c2c --- /dev/null +++ b/modules/ownAll.lua @@ -0,0 +1,167 @@ +ownAll = {} +ownAll.version = "1.0.0" +ownAll.verbose = false +ownAll.requiredLibs = { + "dcsCommon", -- always + "cfxZones", -- Zones, of course +} + +--[[-- +VERSION HISTORY + - 1.0.0 - Initial version + +--]]-- + +ownAll.zones = {} + +function ownAll.ownAllForZone(theZone) + local allZones = theZone:getStringFromZoneProperty("ownAll", "") + local zVec = dcsCommon.splitString(allZones, ",") + zVec = dcsCommon.trimArray(zVec) + local filtered = {} + for idx, aName in pairs (zVec) do + local found = cfxZones.getZoneByName(aName) + if not found then + trigger.action.outText("+++oAll: <" .. theZone.name .. ">: zone <" .. aName .. "> does not exist.", 30) + else + table.insert(filtered, found) + end + end + if #filtered < 2 then + trigger.action.outText("+++oAll: WARNING - <" .. theZone.name .. "> has only <" .. #filtered .. "> zones", 30) + end + theZone.zones = filtered + theZone.ownState = -1 -- not all owned by one + if theZone:hasProperty("red!") then + theZone.allRed = theZone:getStringFromZoneProperty("red!", "none") + end + if theZone:hasProperty("red#") then + theZone.redNum = theZone:getStringFromZoneProperty("red#", "none") + end + if theZone:hasProperty("blue!") then + theZone.allBlue = theZone:getStringFromZoneProperty("blue!", "none") + end + if theZone:hasProperty("blue#") then + theZone.blueNum = theZone:getStringFromZoneProperty("blue#", "none") + end + theZone.method = theZone:getStringFromZoneProperty("method", "inc") + + if theZone:hasProperty("total#") then + theZone.totalNum = theZone:getStringFromZoneProperty("total#", "none") + theZone:setFlagValue(theZone.totalNum, #filtered) + end + + theZone.ownershipUplink = theZone:getBoolFromZoneProperty("uplink", true) + + local redNum, blueNum + theZone.state, redNum, blueNum = ownAll.calcState(theZone) + if theZone.redNum then + theZone:setFlagValue(theZone.redNum, redNum) + end + if theZone.blueNum then + theZone:setFlagValue(theZone.blueNum, blueNum) + end + +end + +function ownAll.calcState(theZone) + local redNum = 0 + local blueNum = 0 + local allSame = true + if #theZone.zones < 1 then return -1, 0, 0 end + local s = theZone.zones[1].owner + if not s then + trigger.action.outText("+++oAll: zone <" .. theZone.zones[1].name .."> has no owner (?)", 30) + s = -1 + end + for idx, aZone in pairs (theZone.zones) do + local s2 = aZone.owner + if not s2 then + trigger.action.outText("+++oAll: zone <" .. aZone.name .."> has no owner (?)", 30) + s2 = -1 + elseif s2 == 1 then + redNum = redNum + 1 + elseif s2 == 2 then + blueNum = blueNum + 1 + end -- note: no separate counting for neutral or contested + if s ~= s2 then allSame = false end + end + local res = s + if not allSame then s = -1 end + return s, redNum, blueNum +end + +function ownAll.update() + timer.scheduleFunction(ownAll.update, {}, timer.getTime() + 1) + + for idx, theZone in pairs(ownAll.zones) do + local newState, redNum, blueNum = ownAll.calcState(theZone) + if newState ~= theZone.state then + -- all are owned by a different than last time + if newState == 1 and theZone.allRed then + theZone:pollFlag(theZone.allRed, theZone.method) + elseif newState == 2 and theZone.allBlue then + theZone:pollFlag(theZone.allBlue, theZone.method) + end + if theZone.verbose then + trigger.action.outText("+++oAll: zone <" .. theZone.name .. "> status changed to <" .. newState .. ">", 30) + end + theZone.state = newState + end + + if theZone.ownershipUplink then + if theZone.state == 1 or theZone.state == 2 then + theZone.owner = theZone.state + else + theZone.owner = 0 + end + end + + if theZone.redNum then + theZone:setFlagValue(theZone.redNum, redNum) + end + if theZone.blueNum then + theZone:setFlagValue(theZone.blueNum, blueNum) + end + end +end + +function ownAll.readConfigZone() + local theZone = cfxZones.getZoneByName("ownAllConfig") + if not theZone then + theZone = cfxZones.createSimpleZone("ownAllConfig") + end +end + +function ownAll.start() + -- lib check + if not dcsCommon.libCheck then + trigger.action.outText("cfx ownAlll requires dcsCommon", 30) + return false + end + if not dcsCommon.libCheck("cfx ownAll", ownAll.requiredLibs) then + return false + end + + -- read config + ownAll.readConfigZone() + + -- process cloner Zones + local attrZones = cfxZones.getZonesWithAttributeNamed("ownAll") + for k, aZone in pairs(attrZones) do + ownAll.ownAllForZone(aZone) -- process attributes + table.insert(ownAll.zones, aZone) -- add to list + end + + -- start update + timer.scheduleFunction(ownAll.update, {}, timer.getTime() + 1) + + trigger.action.outText("cfx ownAll v" .. ownAll.version .. " started.", 30) + return true +end + +-- let's go! +if not ownAll.start() then + trigger.action.outText("cfx ownAll aborted: missing libraries", 30) + ownAll = nil +end \ No newline at end of file diff --git a/tutorial & demo missions/demo - All is what I own.miz b/tutorial & demo missions/demo - All is what I own.miz new file mode 100644 index 0000000..5c587d7 Binary files /dev/null and b/tutorial & demo missions/demo - All is what I own.miz differ