Version 1.4.6

ownAll
This commit is contained in:
Christian Franz 2023-10-19 08:21:35 +02:00
parent 28830f1378
commit 08527a515d
12 changed files with 677 additions and 357 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
airfield = {} airfield = {}
airfield.version = "1.1.0" airfield.version = "1.1.2"
airfield.requiredLibs = { airfield.requiredLibs = {
"dcsCommon", "dcsCommon",
"cfxZones", "cfxZones",
@ -7,7 +7,7 @@ airfield.requiredLibs = {
airfield.verbose = false airfield.verbose = false
airfield.myAirfields = {} -- indexed by name airfield.myAirfields = {} -- indexed by name
airfield.farps = false airfield.farps = false
airfield.gracePeriod = 3
--[[-- --[[--
This module generates signals when the nearest airfield changes hands, This module generates signals when the nearest airfield changes hands,
can force the coalition of an airfield, and always provides the can force the coalition of an airfield, and always provides the
@ -20,12 +20,17 @@ airfield.farps = false
- allow zone.local farps designation - allow zone.local farps designation
- always checks farp cap events - always checks farp cap events
- added verbosity - 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 -- setting up airfield
-- --
function airfield.createAirFieldFromZone(theZone) function airfield.createAirFieldFromZone(theZone)
--trigger.action.outText("Enter airfield for <" .. theZone.name .. ">", 30)
theZone.farps = theZone:getBoolFromZoneProperty("farps", false) theZone.farps = theZone:getBoolFromZoneProperty("farps", false)
local filterCat = 0 local filterCat = 0
@ -34,17 +39,18 @@ function airfield.createAirFieldFromZone(theZone)
local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat) local theBase = dcsCommon.getClosestAirbaseTo(p, filterCat)
theZone.airfield = theBase theZone.airfield = theBase
theZone.afName = theBase:getName() 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 end
-- methods -- methods
theZone.method = theZone:getStringFromZoneProperty("method", "inc") theZone.method = theZone:getStringFromZoneProperty("method", "inc")
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change") theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
-- set zone's owner
theZone.owner = theBase:getCoalition()
if theZone:hasProperty("red!") then if theZone:hasProperty("red!") then
theZone.redCap = theZone:getStringFromZoneProperty("red!") theZone.redCap = theZone:getStringFromZoneProperty("red!")
end end
@ -90,14 +96,77 @@ function airfield.createAirFieldFromZone(theZone)
-- index by name, and warn if duplicate associate -- index by name, and warn if duplicate associate
if airfield.myAirfields[theZone.afName] then 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 else
airfield.myAirfields[theZone.afName] = theZone airfield.myAirfields[theZone.afName] = theZone
end end
if theZone.verbose or airfield.verbose then theZone.show = theZone:getBoolFromZoneProperty("show", false)
trigger.action.outText("+++airF: airfield zone <" .. theZone.name .. "> associates with <" .. theZone.afName .. ">, current owner is <" .. theZone.owner .. ">", 30) 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 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 end
function airfield.assumeControl(theZone) 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) trigger.action.outText("+++airF: handling capture event/command for airfield <" .. bName .. "> with zone <" .. theZone:getName() .. ">", 30)
end end
airfield.showAirfield(theZone) -- show if enabled
-- outputs -- outputs
if theZone.ownedBy then if theZone.ownedBy then
trigger.action.setUserFlag(theZone.ownedBy, theZone.owner) trigger.action.setUserFlag(theZone.ownedBy, theZone.owner)
@ -218,6 +289,39 @@ function airfield.update()
end end
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 -- LOAD / SAVE
-- --
@ -289,6 +393,13 @@ function airfield.readConfig()
airfield.verbose = theZone.verbose airfield.verbose = theZone.verbose
airfield.farps = theZone:getBoolFromZoneProperty("farps", false) 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 end
function airfield.start() function airfield.start()
@ -320,6 +431,9 @@ function airfield.start()
-- start update in 1 second -- start update in 1 second
timer.scheduleFunction(airfield.update, {}, timer.getTime() + 1) 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) trigger.action.outText("cfx airfield v" .. airfield.version .. " loaded.", 30)
return true return true
end end

View File

@ -1,5 +1,5 @@
cfxOwnedZones = {} cfxOwnedZones = {}
cfxOwnedZones.version = "2.0.1" cfxOwnedZones.version = "2.1.0"
cfxOwnedZones.verbose = false cfxOwnedZones.verbose = false
cfxOwnedZones.announcer = true cfxOwnedZones.announcer = true
cfxOwnedZones.name = "cfxOwnedZones" cfxOwnedZones.name = "cfxOwnedZones"
@ -22,6 +22,13 @@ cfxOwnedZones.name = "cfxOwnedZones"
- fixWingCap option - fixWingCap option
- filter water owned zones for groundTroops - filter water owned zones for groundTroops
2.0.1 - RGBA colors can be entered hex style #ff340799 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 = { cfxOwnedZones.requiredLibs = {
@ -72,6 +79,7 @@ end
function cfxOwnedZones.side2name(theSide) function cfxOwnedZones.side2name(theSide)
if theSide == 1 then return "REDFORCE" end if theSide == 1 then return "REDFORCE" end
if theSide == 2 then return "BLUEFORCE" end if theSide == 2 then return "BLUEFORCE" end
if theSide == 3 then return "Neutral (C)" end
return "Neutral" return "Neutral"
end end
@ -79,7 +87,7 @@ function cfxOwnedZones.conqTemplate(aZone, newOwner, lastOwner)
if true then return end -- do not output if true then return end -- do not output
if lastOwner == 0 then 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 return
end end
@ -108,17 +116,7 @@ function cfxOwnedZones.drawZoneInMap(aZone)
fillColor = aZone.neutralFill -- {0.8, 0.8, 0.8, 0.2} fillColor = aZone.neutralFill -- {0.8, 0.8, 0.8, 0.2}
end end
-- local theShape = 2 -- circle aZone.markID = aZone:drawZone(lineColor, fillColor) -- markID
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
end end
function cfxOwnedZones.getOwnedZoneByName(zName) function cfxOwnedZones.getOwnedZoneByName(zName)
@ -129,44 +127,46 @@ function cfxOwnedZones.getOwnedZoneByName(zName)
end end
function cfxOwnedZones.addOwnedZone(aZone) 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 if aZone:hasProperty("conquered!") then
aZone.conqueredFlag = cfxZones.getStringFromZoneProperty(aZone, "conquered!", "*<cfxnone>") aZone.conqueredFlag = aZone:getStringFromZoneProperty("conquered!", "*<cfxnone>")
end end
if cfxZones.hasProperty(aZone, "redCap!") then if aZone:hasProperty("redCap!") then
aZone.redCap = cfxZones.getStringFromZoneProperty(aZone, "redCap!", "none") aZone.redCap = aZone:getStringFromZoneProperty("redCap!", "none")
end end
if cfxZones.hasProperty(aZone, "redLost!") then if aZone:hasProperty("redLost!") then
aZone.redLost = cfxZones.getStringFromZoneProperty(aZone, "redLost!", "none") aZone.redLost = aZone:getStringFromZoneProperty("redLost!", "none")
end end
if cfxZones.hasProperty(aZone, "blueCap!") then if aZone:hasProperty("blueCap!") then
aZone.blueCap = cfxZones.getStringFromZoneProperty(aZone, "blueCap!", "none") aZone.blueCap = aZone:getStringFromZoneProperty("blueCap!", "none")
end end
if cfxZones.hasProperty(aZone, "blueLost!") then if aZone:hasProperty("blueLost!") then
aZone.blueLost = cfxZones.getStringFromZoneProperty(aZone, "blueLost!", "none") aZone.blueLost = aZone:getStringFromZoneProperty("blueLost!", "none")
end end
if cfxZones.hasProperty(aZone, "neutral!") then if aZone:hasProperty("neutral!") then
aZone.neutralCap = cfxZones.getStringFromZoneProperty(aZone, "neutral!", "none") aZone.neutralCap = aZone:getStringFromZoneProperty("neutral!", "none")
end end
if cfxZones.hasProperty(aZone, "ownedBy#") then if aZone:hasProperty("ownedBy#") then
aZone.ownedBy = cfxZones.getStringFromZoneProperty(aZone, "ownedBy#", "none") aZone.ownedBy = aZone:getStringFromZoneProperty("ownedBy#", "none")
elseif cfxZones.hasProperty(aZone, "ownedBy") then elseif aZone:hasProperty("ownedBy") then
aZone.ownedBy = cfxZones.getStringFromZoneProperty(aZone, "ownedBy", "none") aZone.ownedBy = aZone:getStringFromZoneProperty("ownedBy", "none")
end end
aZone.unbeatable = cfxZones.getBoolFromZoneProperty(aZone, "unbeatable", false) aZone.unbeatable = aZone:getBoolFromZoneProperty("unbeatable", false)
aZone.untargetable = cfxZones.getBoolFromZoneProperty(aZone, "untargetable", 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 -- individual colors, else default from config
aZone.redLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "redLine", cfxOwnedZones.redLine) aZone.redLine = aZone:getRGBAVectorFromZoneProperty("redLine", cfxOwnedZones.redLine)
aZone.redFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "redFill", cfxOwnedZones.redFill) aZone.redFill = aZone:getRGBAVectorFromZoneProperty("redFill", cfxOwnedZones.redFill)
aZone.blueLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "blueLine", cfxOwnedZones.blueLine) aZone.blueLine = aZone:getRGBAVectorFromZoneProperty("blueLine", cfxOwnedZones.blueLine)
aZone.blueFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "blueFill", cfxOwnedZones.blueFill) aZone.blueFill = aZone:getRGBAVectorFromZoneProperty("blueFill", cfxOwnedZones.blueFill)
aZone.neutralLine = cfxZones.getRGBAVectorFromZoneProperty(aZone, "neutralLine", cfxOwnedZones.neutralLine) aZone.neutralLine = aZone:getRGBAVectorFromZoneProperty("neutralLine", cfxOwnedZones.neutralLine)
aZone.neutralFill = cfxZones.getRGBAVectorFromZoneProperty(aZone, "neutralFill", cfxOwnedZones.neutralFill) aZone.neutralFill = aZone:getRGBAVectorFromZoneProperty("neutralFill", cfxOwnedZones.neutralFill)
aZone.method = aZone:getStringFromZoneProperty("method", "inc")
cfxOwnedZones.zones[aZone] = aZone cfxOwnedZones.zones[aZone] = aZone
cfxOwnedZones.drawZoneInMap(aZone) cfxOwnedZones.drawZoneInMap(aZone)
@ -178,32 +178,36 @@ end
function cfxOwnedZones.bangNeutral(value) function cfxOwnedZones.bangNeutral(value)
if not cfxOwnedZones.neutralTriggerFlag then return end if not cfxOwnedZones.neutralTriggerFlag then return end
local newVal = trigger.misc.getUserFlag(cfxOwnedZones.neutralTriggerFlag) + value --local newVal = trigger.misc.getUserFlag(cfxOwnedZones.neutralTriggerFlag) + value
trigger.action.setUserFlag(cfxOwnedZones.neutralTriggerFlag, newVal) --trigger.action.setUserFlag(cfxOwnedZones.neutralTriggerFlag, newVal)
cfxZones.pollFlag(cfxOwnedZones.neutralTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
end end
function cfxOwnedZones.bangRed(value) function cfxOwnedZones.bangRed(value, theZone)
if not cfxOwnedZones.redTriggerFlag then return end if not cfxOwnedZones.redTriggerFlag then return end
local newVal = trigger.misc.getUserFlag(cfxOwnedZones.redTriggerFlag) + value --local newVal = trigger.misc.getUserFlag(cfxOwnedZones.redTriggerFlag) + value
trigger.action.setUserFlag(cfxOwnedZones.redTriggerFlag, newVal) --trigger.action.setUserFlag(cfxOwnedZones.redTriggerFlag, newVal)
cfxZones.pollFlag(cfxOwnedZones.redTriggerFlag, cfxOwnedZones.method, cfxOwnedZones)
end end
function cfxOwnedZones.bangBlue(value) function cfxOwnedZones.bangBlue(value, theZone)
if not cfxOwnedZones.blueTriggerFlag then return end if not cfxOwnedZones.blueTriggerFlag then return end
local newVal = trigger.misc.getUserFlag(cfxOwnedZones.blueTriggerFlag) + value 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 end
function cfxOwnedZones.bangSide(theSide, value) function cfxOwnedZones.bangSide(theSide, value, theZone)
if theSide == 2 then if theSide == 2 then
cfxOwnedZones.bangBlue(value) cfxOwnedZones.bangBlue(value, theZone)
return return
end end
if theSide == 1 then if theSide == 1 then
cfxOwnedZones.bangRed(value) cfxOwnedZones.bangRed(value, theZone)
return return
end end
cfxOwnedZones.bangNeutral(value) cfxOwnedZones.bangNeutral(value, theZone)
end end
function cfxOwnedZones.zoneConquered(aZone, theSide, formerOwner) -- 0 = neutral 1 = RED 2 = BLUE 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 end
if aZone.conqueredFlag then if aZone.conqueredFlag then
cfxZones.pollFlag(aZone.conqueredFlag, "inc", aZone) aZone:pollFlag(aZone.conqueredFlag, aZone.method)
end end
if theSide == 1 and aZone.redCap then if theSide == 1 and aZone.redCap then
cfxZones.pollFlag(aZone.redCap, "inc", aZone) aZone:pollFlag(aZone.redCap, aZone.method)
end end
if formerOwner == 1 and aZone.redLost then if formerOwner == 1 and aZone.redLost then
cfxZones.pollFlag(aZone.redLost, "inc", aZone) aZone:pollFlag(aZone.redLost, aZone.method)
end end
if theSide == 2 and aZone.blueCap then if theSide == 2 and aZone.blueCap then
cfxZones.pollFlag(aZone.blueCap, "inc", aZone) aZone:pollFlag(aZone.blueCap, aZone.method)
end end
if formerOwner == 2 and aZone.blueLost then if formerOwner == 2 and aZone.blueLost then
cfxZones.pollFlag(aZone.blueLost, "inc", aZone) aZone:pollFlag(aZone.blueLost, aZone.method)
end end
if theSide == 0 and aZone.neutralCap then if theSide == 0 and aZone.neutralCap then
cfxZones.pollFlag(aZone.neutralCap, "inc", aZone) aZone:pollFlag(aZone.neutralCap, aZone.method)
end end
-- invoke callbacks now -- invoke callbacks now
cfxOwnedZones.invokeConqueredCallbacks(aZone, theSide, formerOwner) cfxOwnedZones.invokeConqueredCallbacks(aZone, theSide, formerOwner)
-- bang! flag support -- bang! flag support
cfxOwnedZones.bangSide(theSide, 1) -- winner cfxOwnedZones.bangSide(theSide, 1, aZone) -- winner
cfxOwnedZones.bangSide(formerOwner, -1) -- loser cfxOwnedZones.bangSide(formerOwner, -1, aZone) -- loser
-- update map -- update map
cfxOwnedZones.drawZoneInMap(aZone) -- update status in map. will erase previous version cfxOwnedZones.drawZoneInMap(aZone) -- update status in map. will erase previous version
@ -313,13 +317,13 @@ function cfxOwnedZones.update()
if cfxOwnedZones.fastEval then if cfxOwnedZones.fastEval then
-- we only check first unit that is alive -- we only check first unit that is alive
local theUnit = dcsCommon.getGroupUnit(aGroup) 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() theZone.numRed = theZone.numRed + aGroup:getSize()
end end
else else
local allUnits = aGroup:getUnits() local allUnits = aGroup:getUnits()
for idy, theUnit in pairs(allUnits) do 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 theZone.numRed = theZone.numRed + 1
end end
end end
@ -332,13 +336,13 @@ function cfxOwnedZones.update()
if cfxOwnedZones.fastEval then if cfxOwnedZones.fastEval then
-- we only check first unit that is alive -- we only check first unit that is alive
local theUnit = dcsCommon.getGroupUnit(aGroup) 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() theZone.numBlue = theZone.numBlue + aGroup:getSize()
end end
else else
local allUnits = aGroup:getUnits() local allUnits = aGroup:getUnits()
for idy, theUnit in pairs(allUnits) do 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 theZone.numBlue = theZone.numBlue + 1
end end
end end
@ -423,7 +427,7 @@ function cfxOwnedZones.update()
-- update ownership flag if exists -- update ownership flag if exists
if theZone.ownedBy then if theZone.ownedBy then
cfxZones.setFlagValueMult(theZone.ownedBy, theZone.owner, theZone) theZone:setFlagValue(theZone.ownedBy, theZone.owner)
end end
-- now add this zone to relevant side -- now add this zone to relevant side
@ -440,30 +444,30 @@ function cfxOwnedZones.update()
-- update totals -- update totals
if cfxOwnedZones.redOwned then if cfxOwnedZones.redOwned then
cfxZones.setFlagValueMult(cfxOwnedZones.redOwned, redZoneNum, cfxOwnedZones) cfxZones.setFlagValue(cfxOwnedZones.redOwned, redZoneNum, cfxOwnedZones)
end end
if cfxOwnedZones.blueOwned then if cfxOwnedZones.blueOwned then
cfxZones.setFlagValueMult(cfxOwnedZones.blueOwned, blueZoneNum, cfxOwnedZones) cfxZones.setFlagValue(cfxOwnedZones.blueOwned, blueZoneNum, cfxOwnedZones)
end end
if cfxOwnedZones.neutralOwned then if cfxOwnedZones.neutralOwned then
cfxZones.setFlagValueMult(cfxOwnedZones.neutralOwned, greyZoneNum, cfxOwnedZones) cfxZones.setFlagValue(cfxOwnedZones.neutralOwned, greyZoneNum, cfxOwnedZones)
end end
if cfxOwnedZones.totalOwnedZones then if cfxOwnedZones.totalOwnedZones then
cfxZones.setFlagValueMult(cfxOwnedZones.totalOwnedZones, totalZoneNum, cfxOwnedZones) cfxZones.setFlagValue(cfxOwnedZones.totalOwnedZones, totalZoneNum, cfxOwnedZones)
end end
-- see if one side owns all and bang the flags if requiredLibs -- see if one side owns all and bang the flags if requiredLibs
if cfxOwnedZones.allBlue and not cfxOwnedZones.hasAllBlue then if cfxOwnedZones.allBlue and not cfxOwnedZones.hasAllBlue then
if cfxOwnedZones.sideOwnsAll(2) then if cfxOwnedZones.sideOwnsAll(2) then
cfxZones.pollFlag(cfxOwnedZones.allBlue, "inc", cfxOwnedZones) cfxZones.pollFlag(cfxOwnedZones.allBlue, cfxOwnedZones.method, cfxOwnedZones)
cfxOwnedZones.hasAllBlue = true cfxOwnedZones.hasAllBlue = true
end end
end end
if cfxOwnedZones.allRed and not cfxOwnedZones.hasAllRed then if cfxOwnedZones.allRed and not cfxOwnedZones.hasAllRed then
if cfxOwnedZones.sideOwnsAll(1) then if cfxOwnedZones.sideOwnsAll(1) then
cfxZones.pollFlag(cfxOwnedZones.allRed, "inc", cfxOwnedZones) cfxZones.pollFlag(cfxOwnedZones.allRed, cfxOwnedZones.method, cfxOwnedZones)
cfxOwnedZones.hasAllRed = true cfxOwnedZones.hasAllRed = true
end end
end end
@ -501,7 +505,7 @@ function cfxOwnedZones.collectZones(mode)
if mode == "land" then if mode == "land" then
local landZones = {} local landZones = {}
for idx, theZone in pairs(cfxOwnedZones.zones) do for idx, theZone in pairs(cfxOwnedZones.zones) do
p = cfxZones.getPoint(theZone) p = theZone:getPoint()
p.y = p.z p.y = p.z
local surfType = land.getSurfaceType(p) local surfType = land.getSurfaceType(p)
if surfType == 3 then if surfType == 3 then
@ -539,7 +543,7 @@ function cfxOwnedZones.getNearestOwnedZoneToPoint(aPoint)
local allZones = cfxOwnedZones.collectZones() local allZones = cfxOwnedZones.collectZones()
for zKey, aZone in pairs(allZones) do for zKey, aZone in pairs(allZones) do
local zPoint = cfxZones.getPoint(aZone) local zPoint = aZone:getPoint()
currDist = dcsCommon.dist(zPoint, aPoint) currDist = dcsCommon.dist(zPoint, aPoint)
if aZone.untargetable ~= true and if aZone.untargetable ~= true and
currDist < shortestDist then currDist < shortestDist then
@ -554,10 +558,10 @@ end
function cfxOwnedZones.getNearestOwnedZone(theZone) function cfxOwnedZones.getNearestOwnedZone(theZone)
local shortestDist = math.huge local shortestDist = math.huge
local closestZone = nil local closestZone = nil
local aPoint = cfxZones.getPoint(theZone) local aPoint = theZone:getPoint()
local allZones = cfxOwnedZones.collectZones() local allZones = cfxOwnedZones.collectZones()
for zKey, aZone in pairs(allZones) do for zKey, aZone in pairs(allZones) do
local zPoint = cfxZones.getPoint(aZone) local zPoint = aZone:getPoint()
currDist = dcsCommon.dist(zPoint, aPoint) currDist = dcsCommon.dist(zPoint, aPoint)
if aZone.untargetable ~= true and currDist < shortestDist then if aZone.untargetable ~= true and currDist < shortestDist then
shortestDist = currDist shortestDist = currDist
@ -575,13 +579,13 @@ function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
local allZones = cfxOwnedZones.collectZones() local allZones = cfxOwnedZones.collectZones()
local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner) local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner)
if not ourEnemy then return nil end -- we called for a neutral zone. they have no enemies 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 for zKey, aZone in pairs(allZones) do
if targetNeutral then if targetNeutral then
-- return all zones that do not belong to us -- return all zones that do not belong to us
if aZone.owner ~= theZone.owner then if aZone.owner ~= theZone.owner then
local aPoint = cfxZones.getPoint(aZone) local aPoint = aZone:getPoint()
currDist = dcsCommon.dist(aPoint, zPoint) currDist = dcsCommon.dist(aPoint, zPoint)
if aZone.untargetable ~= true and currDist < shortestDist then if aZone.untargetable ~= true and currDist < shortestDist then
shortestDist = currDist shortestDist = currDist
@ -591,7 +595,7 @@ function cfxOwnedZones.getNearestEnemyOwnedZone(theZone, targetNeutral)
else else
-- return zones that are taken by the Enenmy -- return zones that are taken by the Enenmy
if aZone.owner == ourEnemy then -- only check own zones if aZone.owner == ourEnemy then -- only check own zones
local aPoint = cfxZones.getPoint(aZone) local aPoint = aZone:getPoint()
currDist = dcsCommon.dist(zPoint, aPoint) currDist = dcsCommon.dist(zPoint, aPoint)
if aZone.untargetable ~= true and currDist < shortestDist then if aZone.untargetable ~= true and currDist < shortestDist then
shortestDist = currDist shortestDist = currDist
@ -610,14 +614,14 @@ function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral)
local closestZone = nil local closestZone = nil
local ourEnemy = dcsCommon.getEnemyCoalitionFor(theZone.owner) 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. 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() local allZones = cfxOwnedZones.collectZones()
for zKey, aZone in pairs(allZones) do for zKey, aZone in pairs(allZones) do
if targetNeutral then if targetNeutral then
-- target all zones that do not belong to the enemy -- target all zones that do not belong to the enemy
if aZone.owner ~= ourEnemy then if aZone.owner ~= ourEnemy then
local aPoint = cfxZones.getPoint(aZone) local aPoint = aZone:getPoint()
currDist = dcsCommon.dist(zPoint, aPoint) currDist = dcsCommon.dist(zPoint, aPoint)
if aZone.untargetable ~= true and currDist < shortestDist then if aZone.untargetable ~= true and currDist < shortestDist then
shortestDist = currDist shortestDist = currDist
@ -627,7 +631,7 @@ function cfxOwnedZones.getNearestFriendlyZone(theZone, targetNeutral)
else else
-- only target zones that are taken by us -- only target zones that are taken by us
if aZone.owner == theZone.owner then -- only check own zones if aZone.owner == theZone.owner then -- only check own zones
local aPoint = cfxZones.getPoint(aZone) local aPoint = aZone:getPoint()
currDist = dcsCommon.dist(zPoint, aPoint) currDist = dcsCommon.dist(zPoint, aPoint)
if aZone.untargetable ~= true and currDist < shortestDist then if aZone.untargetable ~= true and currDist < shortestDist then
shortestDist = currDist shortestDist = currDist
@ -659,7 +663,7 @@ function cfxOwnedZones.saveData()
local zoneData = {} local zoneData = {}
if theZone.conqueredFlag then if theZone.conqueredFlag then
zoneData.conquered = cfxZones.getFlagValue(theZone.conqueredFlag, theZone) zoneData.conquered = theZone:getFlagValue(theZone.conqueredFlag)
end end
zoneData.owner = theZone.owner zoneData.owner = theZone.owner
@ -700,7 +704,7 @@ function cfxOwnedZones.loadData()
if theZone then if theZone then
theZone.owner = zData.owner theZone.owner = zData.owner
if zData.conquered then if zData.conquered then
cfxZones.setFlagValue(theZone.conqueredFlag, zData.conquered, theZone) theZone:setFlagValue(theZone.conqueredFlag, zData.conquered)
end end
-- update mark in map -- update mark in map
cfxOwnedZones.drawZoneInMap(theZone) cfxOwnedZones.drawZoneInMap(theZone)
@ -724,72 +728,73 @@ function cfxOwnedZones.readConfigZone(theZone)
if not theZone then theZone = cfxZones.createSimpleZone("ownedZonesConfig") end if not theZone then theZone = cfxZones.createSimpleZone("ownedZonesConfig") end
cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones cfxOwnedZones.name = "cfxOwnedZones" -- just in case, so we can access with cfxZones
cfxOwnedZones.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false) cfxOwnedZones.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
cfxOwnedZones.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true) cfxOwnedZones.announcer = theZone:getBoolFromZoneProperty("announcer", true)
if cfxZones.hasProperty(theZone, "r!") then if theZone:hasProperty("r!") then
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r!", "*<cfxnone>") cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r!", "*<cfxnone>")
else else
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "r#", "*<cfxnone>") cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("r#", "*<cfxnone>")
end end
if cfxZones.hasProperty(theZone, "b!") then if theZone:hasProperty("b!") then
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b!", "*<cfxnone>") cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("b!", "*<cfxnone>")
else else
cfxOwnedZones.blueTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "b#", "*<cfxnone>") cfxOwnedZones.blueTriggerFlag = theZone:getStringFromZoneProperty("b#", "*<cfxnone>")
end end
if cfxZones.hasProperty(theZone, "n!") then if theZone:hasProperty("n!") then
cfxOwnedZones.redTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n!", "*<cfxnone>") cfxOwnedZones.redTriggerFlag = theZone:getStringFromZoneProperty("n!", "*<cfxnone>")
else else
cfxOwnedZones.neutralTriggerFlag = cfxZones.getStringFromZoneProperty(theZone, "n#", "*<cfxnone>") cfxOwnedZones.neutralTriggerFlag = theZone:getStringFromZoneProperty("n#", "*<cfxnone>")
end end
-- allXXX flags -- allXXX flags
if cfxZones.hasProperty(theZone, "allBlue!") then if theZone:hasProperty("allBlue!") then
cfxOwnedZones.allBlue = cfxZones.getStringFromZoneProperty(theZone, "allBlue!", "*<cfxnone>") cfxOwnedZones.allBlue = theZone:getStringFromZoneProperty( "allBlue!", "*<cfxnone>")
cfxOwnedZones.hasAllBlue = nil cfxOwnedZones.hasAllBlue = nil
end end
if cfxZones.hasProperty(theZone, "allRed!") then if theZone:hasProperty("allRed!") then
cfxOwnedZones.allRed = cfxZones.getStringFromZoneProperty(theZone, "allRed!", "*<cfxnone>") cfxOwnedZones.allRed = theZone:getStringFromZoneProperty("allRed!", "*<cfxnone>")
cfxOwnedZones.hasAllRed = nil cfxOwnedZones.hasAllRed = nil
end end
if cfxZones.hasProperty(theZone, "redOwned#") then if theZone:hasProperty("redOwned#") then
cfxOwnedZones.redOwned = cfxZones.getStringFromZoneProperty(theZone, "redOwned#", "*<cfxnone>") cfxOwnedZones.redOwned = theZone:getStringFromZoneProperty("redOwned#", "*<cfxnone>")
end end
if cfxZones.hasProperty(theZone, "blueOwned#") then if theZone:hasProperty("blueOwned#") then
cfxOwnedZones.blueOwned = cfxZones.getStringFromZoneProperty(theZone, "blueOwned#", "*<cfxnone>") cfxOwnedZones.blueOwned = theZone:getStringFromZoneProperty( "blueOwned#", "*<cfxnone>")
end end
if cfxZones.hasProperty(theZone, "neutralOwned#") then if theZone:hasProperty("neutralOwned#") then
cfxOwnedZones.neutralOwned = cfxZones.getStringFromZoneProperty(theZone, "neutralOwned#", "*<cfxnone>") cfxOwnedZones.neutralOwned = theZone:getStringFromZoneProperty("neutralOwned#", "*<cfxnone>")
end end
if cfxZones.hasProperty(theZone, "totalZones#") then if theZone:hasProperty("totalZones#") then
cfxOwnedZones.totalOwnedZones = cfxZones.getStringFromZoneProperty(theZone, "totalZones#", "*<cfxnone>") cfxOwnedZones.totalOwnedZones = theZone:getStringFromZoneProperty("totalZones#", "*<cfxnone>")
end end
-- numKeep, numCap, fastEval, easyContest -- numKeep, numCap, fastEval, easyContest
cfxOwnedZones.numCap = cfxZones.getNumberFromZoneProperty(theZone, "numCap", 1) -- minimal number of units required to cap zone cfxOwnedZones.numCap = theZone:getNumberFromZoneProperty("numCap", 1) -- minimal number of units required to cap zone
cfxOwnedZones.numKeep = cfxZones.getNumberFromZoneProperty(theZone, "numKeep", 0) -- number required to keep zone cfxOwnedZones.numKeep = theZone:getNumberFromZoneProperty("numKeep", 0) -- number required to keep zone
cfxOwnedZones.fastEval = cfxZones.getBoolFromZoneProperty(theZone, "fastEval", true) cfxOwnedZones.fastEval = theZone:getBoolFromZoneProperty("fastEval", true)
cfxOwnedZones.easyContest = cfxZones.getBoolFromZoneProperty(theZone, "easyContest", false) cfxOwnedZones.easyContest = theZone:getBoolFromZoneProperty("easyContest", false)
-- winSound, loseSound -- winSound, loseSound
cfxOwnedZones.winSound = cfxZones.getStringFromZoneProperty(theZone, "winSound", "Quest Snare 3.wav" ) cfxOwnedZones.winSound = theZone:getStringFromZoneProperty("winSound", "Quest Snare 3.wav")
cfxOwnedZones.loseSound = cfxZones.getStringFromZoneProperty(theZone, "loseSound", "Death BRASS.wav") cfxOwnedZones.loseSound = theZone:getStringFromZoneProperty("loseSound", "Death BRASS.wav")
-- capture options -- capture options
cfxOwnedZones.groundCap = cfxZones.getBoolFromZoneProperty(theZone, "groundCap", true) cfxOwnedZones.groundCap = theZone:getBoolFromZoneProperty("groundCap", true)
cfxOwnedZones.navalCap = cfxZones.getBoolFromZoneProperty(theZone, "navalCap", false) cfxOwnedZones.navalCap = theZone:getBoolFromZoneProperty("navalCap", false)
cfxOwnedZones.heloCap = cfxZones.getBoolFromZoneProperty(theZone, "heloCap") cfxOwnedZones.heloCap = theZone:getBoolFromZoneProperty("heloCap")
cfxOwnedZones.fixWingCap = cfxZones.getBoolFromZoneProperty(theZone, "fixWingCap") cfxOwnedZones.fixWingCap = theZone:getBoolFromZoneProperty("fixWingCap")
-- colors for line and fill -- colors for line and fill
cfxOwnedZones.redLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "redLine", {1.0, 0, 0, 1.0}) cfxOwnedZones.redLine = theZone:getRGBAVectorFromZoneProperty("redLine", {1.0, 0, 0, 1.0})
cfxOwnedZones.redFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "redFill", {1.0, 0, 0, 0.2}) cfxOwnedZones.redFill = theZone:getRGBAVectorFromZoneProperty("redFill", {1.0, 0, 0, 0.2})
cfxOwnedZones.blueLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "blueLine", {0.0, 0, 1.0, 1.0}) cfxOwnedZones.blueLine = theZone:getRGBAVectorFromZoneProperty("blueLine", {0.0, 0, 1.0, 1.0})
cfxOwnedZones.blueFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "blueFill", {0.0, 0, 1.0, 0.2}) cfxOwnedZones.blueFill = theZone:getRGBAVectorFromZoneProperty("blueFill", {0.0, 0, 1.0, 0.2})
cfxOwnedZones.neutralLine = cfxZones.getRGBAVectorFromZoneProperty(theZone, "neutralLine", {0.8, 0.8, 0.8, 1.0}) cfxOwnedZones.neutralLine = theZone:getRGBAVectorFromZoneProperty("neutralLine", {0.8, 0.8, 0.8, 1.0})
cfxOwnedZones.neutralFill = cfxZones.getRGBAVectorFromZoneProperty(theZone, "neutralFill", {0.8, 0.8, 0.8, 0.2}) cfxOwnedZones.neutralFill = theZone:getRGBAVectorFromZoneProperty("neutralFill", {0.8, 0.8, 0.8, 0.2})
cfxOwnedZones.method = theZone:getStringFromZoneProperty("method", "inc")
end end
function cfxOwnedZones.init() function cfxOwnedZones.init()

View File

@ -1444,11 +1444,11 @@ function cfxPlayerScore.start()
-- identify and process a score table zones -- identify and process a score table zones
local theZone = cfxZones.getZoneByName("playerScoreTable") local theZone = cfxZones.getZoneByName("playerScoreTable")
if not theZone then if not theZone then
trigger.action.outText("+++scr: no score table!", 30) -- trigger.action.outText("+++scr: no score table!", 30)
else else
-- read all into my types registry, replacing whatever is there -- read all into my types registry, replacing whatever is there
cfxPlayerScore.typeScore = cfxZones.getAllZoneProperties(theZone) cfxPlayerScore.typeScore = cfxZones.getAllZoneProperties(theZone)
trigger.action.outText("+++scr: read score table", 30) -- trigger.action.outText("+++scr: read score table", 30)
end end
-- read score tiggers and values -- read score tiggers and values
@ -1486,7 +1486,7 @@ function cfxPlayerScore.start()
-- now read my config zone -- now read my config zone
local theZone = cfxZones.getZoneByName("playerScoreConfig") local theZone = cfxZones.getZoneByName("playerScoreConfig")
if not theZone then if not theZone then
trigger.action.outText("+++pScr: no config!", 30) -- trigger.action.outText("+++pScr: no config!", 30)
theZone = cfxZones.createSimpleZone("playerScoreConfig") theZone = cfxZones.createSimpleZone("playerScoreConfig")
end end
cfxPlayerScore.readConfigZone(theZone) cfxPlayerScore.readConfigZone(theZone)

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {} cfxSpawnZones = {}
cfxSpawnZones.version = "2.0.0" cfxSpawnZones.version = "2.0.1"
cfxSpawnZones.requiredLibs = { cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything "dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we -- pretty stupid to check for this since we
@ -72,6 +72,7 @@ cfxSpawnZones.spawnedGroups = {}
- moved "types" to spawner - moved "types" to spawner
- baseName defaults to zone name, as it is safe for naming - baseName defaults to zone name, as it is safe for naming
- spawnWithSpawner direct link in spawner to spawnZones - spawnWithSpawner direct link in spawner to spawnZones
2.0.1 - fix in verifySpawnOwnership() when not master zone found
--]]-- --]]--
cfxSpawnZones.allSpawners = {} cfxSpawnZones.allSpawners = {}
@ -275,6 +276,7 @@ function cfxSpawnZones.verifySpawnOwnership(spawner)
local masterZone = cfxZones.getZoneByName(spawner.masterZoneName) local masterZone = cfxZones.getZoneByName(spawner.masterZoneName)
if not masterZone then if not masterZone then
trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30) trigger.action.outText("spawner " .. spawner.name .. " DID NOT FIND MASTER ZONE <" .. spawner.masterZoneName .. ">", 30)
return false
end end
if not masterZone.owner then if not masterZone.owner then

View File

@ -1,117 +1,14 @@
cfxZones = {} cfxZones = {}
cfxZones.version = "4.0.5" cfxZones.version = "4.0.7"
-- cf/x zone management module -- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable -- reads dcs zones and makes them accessible and mutable
-- by scripting. -- 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 --[[-- 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 <none>
- setFlagValue QoL for <none>
- 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 - 3.0.0 - support for DCS 2.8 linkUnit attribute, integration with
linedUnit and warning. linedUnit and warning.
- initZoneVerbosity() - initZoneVerbosity()
@ -157,6 +54,8 @@ cfxZones.version = "4.0.5"
- doSetFlagValue optimizations - doSetFlagValue optimizations
- 4.0.5 - dynamicAB wildcard - 4.0.5 - dynamicAB wildcard
- processDynamicValueVU - processDynamicValueVU
- 4.0.6 - hash mark forgotten QoL
- 4.0.7 - drawZone()
--]]-- --]]--
@ -2208,6 +2107,30 @@ function cfxZones.flagArrayFromString(inString) -- dcsCommon bridge
end 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 -- PROPERTY PROCESSING
@ -2444,6 +2367,14 @@ function cfxZones.hasProperty(theZone, theProperty)
return false return false
end 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 return false
end end
return true return true

View File

@ -1,25 +1,13 @@
limitedAirframes = {} limitedAirframes = {}
limitedAirframes.version = "1.5.4" limitedAirframes.version = "1.6.0"
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.warningSound = "Quest Snare 3.wav" limitedAirframes.warningSound = "Quest Snare 3.wav"
limitedAirframes.loseSound = "Death PIANO.wav" limitedAirframes.loseSound = "Death PIANO.wav"
limitedAirframes.winSound = "Triumphant Victory.wav" limitedAirframes.winSound = "Triumphant Victory.wav"
limitedAirframes.announcer = true
limitedAirframes.requiredLibs = { limitedAirframes.requiredLibs = {
"dcsCommon", -- common is of course needed for everything "dcsCommon",
-- pretty stupid to check for this since we "cfxZones",
-- need common to invoke the check, but anyway
"cfxZones", -- Zones, of course for safe landings
-- "cfxPlayer", no longer needed
} }
--[[-- VERSION HISTORY --[[-- VERSION HISTORY
@ -57,6 +45,10 @@ limitedAirframes.requiredLibs = {
- 1.5.3 - ... but do allow it if not coming from 'ejected' so ditching - 1.5.3 - ... but do allow it if not coming from 'ejected' so ditching
a plane will again create CSAR missions a plane will again create CSAR missions
1.5.4 - red# and blue# instead of #red and #blue 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 -- when the number reaches -1 or smaller, other side wins
-- !!!Only affects player planes!! -- !!!Only affects player planes!!
-- *** EXTENDS ZONES ***
-- safe zones must have a property "pilotSafe" -- safe zones must have a property "pilotSafe"
-- - pilotSafe - this is a zone to safely change airframes in -- - pilotSafe - this is a zone to safely change airframes in
-- - can also carry 'red' or 'blue' to enable -- - 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 -- if zone can change ownership, player's coalition
-- is checked against current zone ownership -- is checked against current zone ownership
-- zone owner. -- 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 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 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!!!! -- note: must match exactly!!!!
local theZone = cfxZones.getZoneByName("limitedAirframesConfig") local theZone = cfxZones.getZoneByName("limitedAirframesConfig")
if not theZone then if not theZone then
if limitedAirframes.verbose then
trigger.action.outText("+++limA: NO config zone!", 30)
end
theZone = cfxZones.createSimpleZone("limitedAirframesConfig") theZone = cfxZones.createSimpleZone("limitedAirframesConfig")
end end
-- remember me
limitedAirframes.config = theZone limitedAirframes.config = theZone
limitedAirframes.name = "limitedAirframes" -- so we can call cfxZones with ourself as param 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 if limitedAirframes.verbose then
trigger.action.outText("+++limA: found config zone!", 30) trigger.action.outText("+++limA: found config zone!", 30)
end end
-- ok, for each property, load it if it exists -- 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 theZone:hasProperty("#red") then
limitedAirframes.numRed = theZone:getStringFromZoneProperty("#red", "*none")
if cfxZones.hasProperty(theZone, "#red") then
limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "#red", "*none")
else else
limitedAirframes.numRed = cfxZones.getStringFromZoneProperty(theZone, "red#", "*none") limitedAirframes.numRed = theZone:getStringFromZoneProperty("red#", "*none")
end end
if cfxZones.hasProperty(theZone, "#blue") then if theZone:hasProperty("#blue") then
limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "#blue", "*none") limitedAirframes.numBlue = theZone:getStringFromZoneProperty("#blue", "*none")
else else
limitedAirframes.numBlue = cfxZones.getStringFromZoneProperty(theZone, "blue#", "*none") limitedAirframes.numBlue = theZone:getStringFromZoneProperty("blue#", "*none")
end end
limitedAirframes.redWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "redWins!", "*none") limitedAirframes.redWinsFlag = theZone:getStringFromZoneProperty("redWins!", "*none")
if cfxZones.hasProperty(theZone, "redWinsFlag!") then if theZone:hasProperty("redWinsFlag!") then
limitedAirframes.redWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "redWinsFlag!", "*none") limitedAirframes.redWinsFlag = theZone:getStringFromZoneProperty("redWinsFlag!", "*none")
end end
limitedAirframes.blueWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "blueWins!", "*none") limitedAirframes.blueWinsFlag = theZone:getStringFromZoneProperty("blueWins!", "*none")
if cfxZones.hasProperty(theZone, "blueWinsFlag!") then if theZone:hasProperty("blueWinsFlag!") then
limitedAirframes.blueWinsFlag = cfxZones.getStringFromZoneProperty(theZone, "blueWinsFlag!", "*none") limitedAirframes.blueWinsFlag = theZone:getStringFromZoneProperty("blueWinsFlag!", "*none")
end end
limitedAirframes.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc") limitedAirframes.method = theZone:getStringFromZoneProperty("method", "inc")
if cfxZones.hasProperty(theZone, "warningSound") then if theZone:hasProperty("warningSound") then
limitedAirframes.warningSound = cfxZones.getStringFromZoneProperty(theZone, "warningSound", "none") limitedAirframes.warningSound = theZone:getStringFromZoneProperty("warningSound", "none")
end end
if cfxZones.hasProperty(theZone, "winSound") then if theZone:hasProperty("winSound") then
limitedAirframes.winSound = cfxZones.getStringFromZoneProperty(theZone, "winSound", "none") limitedAirframes.winSound = theZone:getStringFromZoneProperty("winSound", "none")
end end
if cfxZones.hasProperty(theZone, "loseSound") then if theZone:hasProperty("loseSound") then
limitedAirframes.loseSound = cfxZones.getStringFromZoneProperty(theZone, "loseSound", "none") limitedAirframes.loseSound = theZone:getStringFromZoneProperty("loseSound", "none")
end 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 end
-- --
@ -219,7 +204,7 @@ function limitedAirframes.addPlayerUnit(theUnit)
local desc = "unit <" .. uName .. "> controlled by <" .. pName .. ">" local desc = "unit <" .. uName .. "> controlled by <" .. pName .. ">"
if not(limitedAirframes.isKnownUnitName(uName)) then if not(limitedAirframes.isKnownUnitName(uName)) then
--desc = "+++lim: added ".. desc .. " to list of known player units"
else else
if limitedAirframes.playerUnits[uName] == pName then if limitedAirframes.playerUnits[uName] == pName then
desc = "player unit <".. uName .. "> controlled by <".. limitedAirframes.playerUnits[uName].."> re-seated" desc = "player unit <".. uName .. "> controlled by <".. limitedAirframes.playerUnits[uName].."> re-seated"
@ -269,7 +254,6 @@ function limitedAirframes.updatePlayer(pName, status)
end end
limitedAirframes.players[pName] = status limitedAirframes.players[pName] = status
-- if desc then trigger.action.outText(desc, 30) end
end end
function limitedAirframes.getStatusOfPlayerInUnit(theUnit) function limitedAirframes.getStatusOfPlayerInUnit(theUnit)
@ -327,7 +311,6 @@ function limitedAirframes.preProcessor(event)
return false -- no longer of interest return false -- no longer of interest
end end
if not dcsCommon.isPlayerUnit(theUnit) then if not dcsCommon.isPlayerUnit(theUnit) then
-- not a player unit. Events 5 and 6 have been -- not a player unit. Events 5 and 6 have been
-- handled before, so we can safely ignore -- handled before, so we can safely ignore
@ -376,8 +359,8 @@ function limitedAirframes.somethingHappened(event)
if ID == 20 then -- 20 ENTER UNIT if ID == 20 then -- 20 ENTER UNIT
local pName = limitedAirframes.getKnownUnitPilotByUnit(theUnit) -- local pName = limitedAirframes.getKnownUnitPilotByUnit(theUnit)
if not pName then pName = "***UNKNOWN***" end -- if not pName then pName = "***UNKNOWN***" end
return return
end end
@ -402,7 +385,6 @@ function limitedAirframes.somethingHappened(event)
return -- plane no longer of interest cant retrieve pilot -- BUG!!! return -- plane no longer of interest cant retrieve pilot -- BUG!!!
end end
-- event 6 - eject - plane divorced but player pilot is known -- event 6 - eject - plane divorced but player pilot is known
if ID == 6 then -- eject if ID == 6 then -- eject
limitedAirframes.pilotEjected(event) limitedAirframes.pilotEjected(event)
@ -422,7 +404,6 @@ function limitedAirframes.somethingHappened(event)
-- so if pilot is still alive and not MIA, he's now dead. -- so if pilot is still alive and not MIA, he's now dead.
-- forget the helo check, this now applies to all -- forget the helo check, this now applies to all
local pStatus = limitedAirframes.getStatusOfPlayerInUnit(theUnit) local pStatus = limitedAirframes.getStatusOfPlayerInUnit(theUnit)
if pStatus == "alive" then if pStatus == "alive" then
-- this frame was carrrying a live player -- this frame was carrrying a live player
@ -441,7 +422,7 @@ function limitedAirframes.somethingHappened(event)
if ID == 21 then -- player left unit if ID == 21 then -- player left unit
-- remove pilot name from unit name -- remove pilot name from unit name
limitedAirframes.unitFlownByPlayer[unitName] = nil limitedAirframes.unitFlownByPlayer[unitName] = nil
--trigger.action.outText("limAir: 21 -- unit " .. unitName .. " unoccupied", 30)
if limitedAirframes.verbose then if limitedAirframes.verbose then
trigger.action.outText("limAir: 21 (player left) for unit " .. unitName , 30) trigger.action.outText("limAir: 21 (player left) for unit " .. unitName , 30)
end end
@ -453,7 +434,6 @@ function limitedAirframes.somethingHappened(event)
if ID == 9 then -- died if ID == 9 then -- died
--trigger.action.outText("limAir: 9 (PILOT DEAD) for unit " .. unitName , 30)
local thePilot = limitedAirframes.unitFlownByPlayer[unitName] local thePilot = limitedAirframes.unitFlownByPlayer[unitName]
if not thePilot then if not thePilot then
if limitedAirframes.verbose then if limitedAirframes.verbose then
@ -514,27 +494,11 @@ function limitedAirframes.handlePlayerLeftUnit(event)
if limitedAirframes.verbose then if limitedAirframes.verbose then
trigger.action.outText("+++limA: " .. theSafeZone.name .. " ownership: myside = " .. mySide .. " zone owner is " .. theSafeZone.owner, 30) trigger.action.outText("+++limA: " .. theSafeZone.name .. " ownership: myside = " .. mySide .. " zone owner is " .. theSafeZone.owner, 30)
end end
else
end 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 isInAir then isSafe = false end
if isSafe then 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; return;
end end
end end
@ -576,7 +540,6 @@ function limitedAirframes.pilotEjected(event)
end end
function limitedAirframes.pilotDied(theUnit) function limitedAirframes.pilotDied(theUnit)
--limitedAirframes.killPlayerInUnit(theUnit)
local theSide = theUnit:getCoalition() local theSide = theUnit:getCoalition()
local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit) local pilot = limitedAirframes.getKnownUnitPilotByUnit(theUnit)
local uName = theUnit:getName() local uName = theUnit:getName()
@ -649,9 +612,7 @@ function limitedAirframes.pilotLost(theUnit)
return true return true
end end
trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound) trigger.action.outSoundForCoalition(theSide, limitedAirframes.warningSound)
-- if limitedAirframes.announcer then
trigger.action.outTextForCoalition(theSide, "You have lost a pilot! Remaining: " .. limitedAirframes.currBlue, 30) trigger.action.outTextForCoalition(theSide, "You have lost a pilot! Remaining: " .. limitedAirframes.currBlue, 30)
-- end
end end
return false return false
end end
@ -707,23 +668,18 @@ function limitedAirframes.addSafeZone(aZone)
return return
end end
-- transfer properties if they exist
-- blueSafe, redSafe: what side this is safe for, default = yes
-- add zone to my list -- add zone to my list
limitedAirframes.safeZones[aZone] = aZone limitedAirframes.safeZones[aZone] = aZone
-- deprecated old code. new code contains 'red, blue' in value for pilotsafe -- 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() safeSides = safeSides:lower()
if dcsCommon.containsString(safeSides, "red") or dcsCommon.containsString(safeSides, "blue") then if dcsCommon.containsString(safeSides, "red") or dcsCommon.containsString(safeSides, "blue") then
aZone.redSafe = dcsCommon.containsString(safeSides, "red") aZone.redSafe = dcsCommon.containsString(safeSides, "red")
aZone.blueSafe = dcsCommon.containsString(safeSides, "blue") aZone.blueSafe = dcsCommon.containsString(safeSides, "blue")
else else
--aZone.redSafe = true aZone.redSafe = aZone:getBoolFromZoneProperty("redSafe", true)
aZone.redSafe = cfxZones.getBoolFromZoneProperty(aZone, "redSafe", true) aZone.blueSafe = aZone:getBoolFromZoneProperty("blueSafe", true)
--aZone.blueSafe = true
aZone.blueSafe = cfxZones.getBoolFromZoneProperty(aZone, "blueSafe", true)
end end
if limitedAirframes.verbose or aZone.verbose then if limitedAirframes.verbose or aZone.verbose then
@ -733,14 +689,10 @@ function limitedAirframes.addSafeZone(aZone)
if aZone.blueSafe then if aZone.blueSafe then
trigger.action.outText("+++limA: <" .. aZone.name .. "> is safe for BLUE pilots", 30) trigger.action.outText("+++limA: <" .. aZone.name .. "> is safe for BLUE pilots", 30)
end end
trigger.action.outText("+++limA: added safeZone " .. aZone.name, 30) trigger.action.outText("+++limA: added safeZone " .. aZone.name, 30)
end end
end end
-- --
-- COMMAND & CONFIGURATION -- COMMAND & CONFIGURATION
-- --
@ -751,10 +703,13 @@ function limitedAirframes.setCommsMenu()
desc = "Pilot Count (Currently OFF)" desc = "Pilot Count (Currently OFF)"
desc2 = "ENABLE Pilot Count" desc2 = "ENABLE Pilot Count"
end end
if not limitedAirframes.userCanToggle then desc = "Pilot Count" end
-- remove previous version -- remove previous version
if limitedAirframes.rootMenu then if limitedAirframes.rootMenu then
missionCommands.removeItem(limitedAirframes.theScore) missionCommands.removeItem(limitedAirframes.theScore) -- frames left
missionCommands.removeItem(limitedAirframes.theCommand) if limitedAirframes.userCanToggle then
missionCommands.removeItem(limitedAirframes.theCommand) -- toggle on/off
end
missionCommands.removeItem(limitedAirframes.rootMenu) missionCommands.removeItem(limitedAirframes.rootMenu)
end end
limitedAirframes.theCommand = nil limitedAirframes.theCommand = nil
@ -765,7 +720,9 @@ function limitedAirframes.setCommsMenu()
limitedAirframes.theScore = missionCommands.addCommand("How many airframes left?" , limitedAirframes.rootMenu, limitedAirframes.redirectAirframeScore, {"none"}) limitedAirframes.theScore = missionCommands.addCommand("How many airframes left?" , limitedAirframes.rootMenu, limitedAirframes.redirectAirframeScore, {"none"})
if limitedAirframes.userCanToggle then
limitedAirframes.theCommand = missionCommands.addCommand(desc2 , limitedAirframes.rootMenu, limitedAirframes.redirectToggleAirFrames, {"none"}) limitedAirframes.theCommand = missionCommands.addCommand(desc2 , limitedAirframes.rootMenu, limitedAirframes.redirectToggleAirFrames, {"none"})
end
end end
@ -897,8 +854,8 @@ function limitedAirframes.start()
limitedAirframes.readConfigZone() limitedAirframes.readConfigZone()
-- set output flags -- set output flags
cfxZones.setFlagValueMult(limitedAirframes.numBlue, limitedAirframes.currBlue, limitedAirframes.config) -- cfxZones.setFlagValue(limitedAirframes.numBlue, limitedAirframes.currBlue, limitedAirframes.config)
cfxZones.setFlagValueMult(limitedAirframes.numRed, limitedAirframes.currRed, limitedAirframes.config) -- cfxZones.setFlagValue(limitedAirframes.numRed, limitedAirframes.currRed, limitedAirframes.config)
-- collect all zones that are airframe safe -- collect all zones that are airframe safe
local afsZones = cfxZones.zonesWithProperty("pilotSafe") local afsZones = cfxZones.zonesWithProperty("pilotSafe")
@ -936,20 +893,20 @@ function limitedAirframes.start()
-- set current values -- set current values
limitedAirframes.currRed = limitedAirframes.maxRed -- limitedAirframes.currRed = limitedAirframes.maxRed
limitedAirframes.currBlue = limitedAirframes.maxBlue -- limitedAirframes.currBlue = limitedAirframes.maxBlue
-- collect active player unit names -- collect active player unit names
local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw() local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw()
for i=1, #allPlayerUnits do for i=1, #allPlayerUnits do
local aUnit = allPlayerUnits[i] local aUnit = allPlayerUnits[i]
limitedAirframes.addPlayerUnit(aUnit) limitedAirframes.addPlayerUnit(aUnit)
-- trigger.action.outText("limAir: detected active player unit " .. aUnit:getName(), 30)
end end
-- allow configuration menu -- allow configuration menu
if limitedAirframes.userCanToggle then --if limitedAirframes.userCanToggle then
if limitedAirframes.hasUI then
limitedAirframes.setCommsMenu() limitedAirframes.setCommsMenu()
end end

View File

@ -1,5 +1,5 @@
messenger = {} messenger = {}
messenger.version = "2.3.0" messenger.version = "2.3.1"
messenger.verbose = false messenger.verbose = false
messenger.requiredLibs = { messenger.requiredLibs = {
"dcsCommon", -- always "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 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 - fixed verbosity bug
2.3.0 - cfxZones OOP switch 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 for idx, aZone in pairs(messenger.messengers) do
-- make sure to re-start before reading time limit -- make sure to re-start before reading time limit
-- new trigger code -- 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 if aZone:testZoneFlag(aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then
aZone.messageOff = true aZone.messageOff = true
if messenger.verbose or aZone.verbose then if messenger.verbose or aZone.verbose then
@ -465,6 +459,15 @@ function messenger.update()
trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30) trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30)
end end
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
end end

View File

@ -1,4 +1,132 @@
mxObjects = {} 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. 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 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) trigger.action.outText("+++mxO: no polygon named <" .. polyName .. "> in layer <" ..layerName .. ">", 30)
return {} return {}
end 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)
--]]--

167
modules/ownAll.lua Normal file
View File

@ -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

Binary file not shown.