mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.4.7
New bombsAway module
This commit is contained in:
parent
08527a515d
commit
c3ba1e7c05
Binary file not shown.
Binary file not shown.
@ -22,8 +22,6 @@ FARPZones.verbose = false
|
|||||||
FARPZones.requiredLibs = {
|
FARPZones.requiredLibs = {
|
||||||
"dcsCommon",
|
"dcsCommon",
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
-- "cfxCommander", -- to make troops do stuff
|
|
||||||
-- "cfxGroundTroops", -- generic when dropping troops
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- *** DOES NOT EXTEND ZONES, USES OWN STRUCT ***
|
-- *** DOES NOT EXTEND ZONES, USES OWN STRUCT ***
|
||||||
|
|||||||
385
modules/TDZ.lua
Normal file
385
modules/TDZ.lua
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
tdz = {}
|
||||||
|
tdz.version = "0.9.0dev"
|
||||||
|
tdz.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
}
|
||||||
|
tdz.allTdz = {}
|
||||||
|
tdz.watchlist = {}
|
||||||
|
tdz.watching = false
|
||||||
|
tdz.timeoutAfter = 120 -- seconds.
|
||||||
|
--
|
||||||
|
-- rwy draw procs
|
||||||
|
--
|
||||||
|
function tdz.rotateXZPolyInRads(thePoly, rads)
|
||||||
|
if not rads then
|
||||||
|
trigger.action.outText("rotateXZPolyInRads (inner): no rads", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local c = math.cos(rads)
|
||||||
|
local s = math.sin(rads)
|
||||||
|
for idx, p in pairs(thePoly) do
|
||||||
|
local nx = p.x * c - p.z * s
|
||||||
|
local nz = p.x * s + p.z * c
|
||||||
|
p.x = nx
|
||||||
|
p.z = nz
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tdz.rotateXZPolyAroundCenterInRads(thePoly, center, rads)
|
||||||
|
if not rads then
|
||||||
|
trigger.action.outText("rotateXZPolyAroundCenterInRads: no rads", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local negCtr = {x = -center.x, y = -center.y, z = -center.z}
|
||||||
|
tdz.translatePoly(thePoly, negCtr)
|
||||||
|
if not rads then
|
||||||
|
trigger.action.outText("WHOA! rotateXZPolyAroundCenterInRads: no rads", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
tdz.rotateXZPolyInRads(thePoly, rads)
|
||||||
|
tdz.translatePoly(thePoly, center)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tdz.rotateXZPolyAroundCenterInDegrees(thePoly, center, degrees)
|
||||||
|
tdz.rotateXZPolyAroundCenterInRads(thePoly, center, degrees * 0.0174533)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tdz.translatePoly(thePoly, v) -- straight rot, translate to 0 first
|
||||||
|
for idx, aPoint in pairs(thePoly) do
|
||||||
|
aPoint.x = aPoint.x + v.x
|
||||||
|
if aPoint.y then aPoint.y = aPoint.y + v.y end
|
||||||
|
if aPoint.z then aPoint.z = aPoint.z + v.z end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--[[--
|
||||||
|
function tdz.frameRwy(center, length, width, rads, a, b) -- bearing in rads
|
||||||
|
if not a then a = 0 end
|
||||||
|
if not b then b = 1 end
|
||||||
|
|
||||||
|
-- create a 0-rotated centered poly
|
||||||
|
local poly = {}
|
||||||
|
local half = length / 2
|
||||||
|
local leftEdge = -half
|
||||||
|
poly[4] = { x = leftEdge + a * length, z = width / 2, y = 0}
|
||||||
|
poly[3] = { x = leftEdge + b * length, z = width / 2, y = 0}
|
||||||
|
poly[2] = { x = leftEdge + b * length, z = -width / 2, y = 0}
|
||||||
|
poly[1] = { x = leftEdge + a * length, z = -width / 2, y = 0}
|
||||||
|
-- move it to center in map
|
||||||
|
tdz.translatePoly(poly, center)
|
||||||
|
|
||||||
|
-- rotate it
|
||||||
|
tdz.rotateXZPolyAroundCenterInRads(poly, center, rads)
|
||||||
|
|
||||||
|
-- frame it
|
||||||
|
local mId = dcsCommon.numberUUID()
|
||||||
|
trigger.action.quadToAll(-1, mId, poly[1], poly[2], poly[3], poly[4], {1, 0, 0, 1}, {1, 0, 0, .5}, 3) -- dotted line, red
|
||||||
|
|
||||||
|
end
|
||||||
|
--]]--
|
||||||
|
function tdz.calcTDZone(name, center, length, width, rads, a, b)
|
||||||
|
if not a then a = 0 end
|
||||||
|
if not b then b = 1 end
|
||||||
|
-- create a 0-rotated centered poly
|
||||||
|
local poly = {}
|
||||||
|
local half = length / 2
|
||||||
|
local leftEdge = -half
|
||||||
|
poly[1] = { x = leftEdge + a * length, z = width / 2, y = 0}
|
||||||
|
poly[2] = { x = leftEdge + b * length, z = width / 2, y = 0}
|
||||||
|
poly[3] = { x = leftEdge + b * length, z = -width / 2, y = 0}
|
||||||
|
poly[4] = { x = leftEdge + a * length, z = -width / 2, y = 0}
|
||||||
|
-- move it to center in map
|
||||||
|
tdz.translatePoly(poly, center)
|
||||||
|
-- rotate it
|
||||||
|
tdz.rotateXZPolyAroundCenterInRads(poly, center, rads)
|
||||||
|
-- make it a dml zone
|
||||||
|
local theNewZone = cfxZones.createSimplePolyZone(name, center, poly)
|
||||||
|
return theNewZone--, left, right
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- create a tdz
|
||||||
|
--
|
||||||
|
function tdz.createTDZ(theZone)
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local theBase = dcsCommon.getClosestAirbaseTo(p) -- never get FARPS
|
||||||
|
theZone.base = theBase
|
||||||
|
theZone.baseName = theBase:getName()
|
||||||
|
|
||||||
|
-- get closest runway to TDZ
|
||||||
|
-- may get a bit hairy, so let's find a good way
|
||||||
|
local allRwys = theBase:getRunways()
|
||||||
|
local nearestRwy = nil
|
||||||
|
local minDist = math.huge
|
||||||
|
for idx, aRwy in pairs(allRwys) do
|
||||||
|
local rp = aRwy.position
|
||||||
|
local dist = dcsCommon.distFlat(p, rp)
|
||||||
|
if dist < minDist then
|
||||||
|
nearestRwy = aRwy
|
||||||
|
minDist = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local bearing = nearestRwy.course * (-1)
|
||||||
|
theZone.bearing = bearing
|
||||||
|
rwname = math.floor(dcsCommon.bearing2degrees(bearing)/10 + 0.5) -- nice number
|
||||||
|
degrees = math.floor(dcsCommon.bearing2degrees(bearing) * 10) / 10
|
||||||
|
if degrees < 0 then degrees = degrees + 360 end
|
||||||
|
if degrees > 360 then degrees = degrees - 360 end
|
||||||
|
if rwname < 0 then rwname = rwname + 36 end
|
||||||
|
if rwname > 36 then rwname = rwname - 36 end
|
||||||
|
local opName = rwname + 18
|
||||||
|
if opName > 36 then opName = opName - 36 end
|
||||||
|
if rwname < 10 then rwname = "0"..rwname end
|
||||||
|
if opName < 10 then opName = "0" .. opName end
|
||||||
|
theZone.rwName = rwname .. "/" .. opName
|
||||||
|
theZone.opName = opName .. "/" .. rwname
|
||||||
|
local rwLen = nearestRwy.length
|
||||||
|
local rwWid = nearestRwy.width
|
||||||
|
local pos = nearestRwy.position
|
||||||
|
-- p1 is for distance to centerline calculation, defining a point
|
||||||
|
-- length away in direction bearing, setting up the line
|
||||||
|
-- theZone.rwCenter, theZone.p1
|
||||||
|
theZone.rwCenter = pos
|
||||||
|
local p1 = {x = pos.x + math.cos(bearing) * rwLen, y = 0, z = pos.z + math.sin(bearing) * rwLen}
|
||||||
|
theZone.rwP1 = p1
|
||||||
|
theZone.starts = theZone:getNumberFromZoneProperty("starts", 0)
|
||||||
|
theZone.ends = theZone:getNumberFromZoneProperty("ends", 610) -- m = 2000 ft
|
||||||
|
theZone.opposing = theZone:getBoolFromZoneProperty("opposing", true)
|
||||||
|
|
||||||
|
theZone.runwayZone = tdz.calcTDZone(theZone.name .. "-" .. rwname .. "main", pos, rwLen, rwWid, bearing)
|
||||||
|
theZone.runwayZone:drawZone({0, 0, 0, 1}, {0, 0, 0, 0}) -- black outline
|
||||||
|
local theTDZone = tdz.calcTDZone(theZone.name .. "-" .. rwname, pos, rwLen, rwWid, bearing, theZone.starts / rwLen, theZone.ends/rwLen)
|
||||||
|
-- to do: mark the various zones of excellence in different colors, or at least the excellent one with more color
|
||||||
|
theTDZone:drawZone({0, 1, 0, 1}, {0, 1, 0, .25})
|
||||||
|
theZone.normTDZone = theTDZone
|
||||||
|
if theZone.opposing then
|
||||||
|
theTDZone = tdz.calcTDZone(theZone.name .. "-" .. opName, pos, rwLen, rwWid, bearing + math.pi, theZone.starts / rwLen, theZone.ends/rwLen)
|
||||||
|
theTDZone:drawZone({0, 1, 0, 1}, {0, 1, 0, .25})
|
||||||
|
theZone.opTDZone = theTDZone
|
||||||
|
theZone.opBearing = bearing + math.pi
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("landed!") then
|
||||||
|
theZone.landedFlag = theZone:getStringFromZoneProperty("landed!", "none")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("touchdown!") then
|
||||||
|
theZone.touchDownFlag = theZone:getStringFromZoneProperty("touchDown!", "none")
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("fail!") then
|
||||||
|
theZone.failFlag = theZone:getStringFromZoneProperty("fail!", "none")
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone.method = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- event handler
|
||||||
|
--
|
||||||
|
|
||||||
|
function tdz.playerLanded(theUnit, playerName)
|
||||||
|
if tdz.watchlist[playerName] then
|
||||||
|
-- this is not a new landing, for now ignore, increment bump count
|
||||||
|
-- make sure unit names match?
|
||||||
|
local entry = tdz.watchlist[playerName]
|
||||||
|
entry.hops = entry.hops + 1 -- uh oh.
|
||||||
|
-- trigger.action.outText("Bump!")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we may want to filter helicopters
|
||||||
|
|
||||||
|
-- see if we touched down inside of one of our watched zones
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
local msg = ""
|
||||||
|
local theZone = nil
|
||||||
|
for idx, aRunway in pairs(tdz.allTdz) do
|
||||||
|
local theRunway = aRunway.runwayZone
|
||||||
|
if theRunway:pointInZone(p) then
|
||||||
|
-- touchdown!
|
||||||
|
theZone = aRunway
|
||||||
|
if theZone.touchDownFlag then
|
||||||
|
theZone.pollFlag(theZone.touchDownFlag, theZone.method)
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(gID, "Touchdown! Come to a FULL STOP for evaluation", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not theZone then return end -- no landing eval zone hit
|
||||||
|
|
||||||
|
-- start a new watchlist entry
|
||||||
|
local entry = {}
|
||||||
|
entry.msg = ""
|
||||||
|
entry.playerName = playerName
|
||||||
|
entry.unitName = theUnit:getName()
|
||||||
|
entry.theType = theUnit:getTypeName()
|
||||||
|
entry.gID = gID
|
||||||
|
entry.theTime = timer.getTime()
|
||||||
|
entry.tdPoint = p
|
||||||
|
entry.tdVel = theUnit:getVelocity() -- vector
|
||||||
|
entry.hops = 1
|
||||||
|
entry.theZone = theZone
|
||||||
|
|
||||||
|
-- see if we are in main or opposite direction
|
||||||
|
local hdg = dcsCommon.getUnitHeading(theUnit)
|
||||||
|
local dHdg = math.abs(theZone.bearing - hdg) -- 0..Pi
|
||||||
|
local dOpHdg = math.abs(theZone.opBearing - hdg)
|
||||||
|
local opposite = false
|
||||||
|
if dOpHdg < dHdg then
|
||||||
|
opposite = true
|
||||||
|
dHdg = dOpHdg
|
||||||
|
trigger.action.outText("opposite rwy detected", 30)
|
||||||
|
end
|
||||||
|
if dHdg > math.pi * 1.5 then -- > 270+
|
||||||
|
dHdg = dHdg - math.pi * 1.5
|
||||||
|
elseif dHdg > math.pi / 2 then -- > 90+
|
||||||
|
dHdg = dHdg - math.pi / 2
|
||||||
|
end
|
||||||
|
dHdg = math.floor(dHdg * 572.958) / 10 -- in degrees
|
||||||
|
local lHdg = math.floor(hdg * 572.958) / 10 -- also in deg
|
||||||
|
-- now see how far off centerline.
|
||||||
|
local offcenter = dcsCommon.distanceOfPointPToLineXZ(p, theZone.rwCenter, theZone.rwP1)
|
||||||
|
offcenter = math.floor(offcenter * 10)/10
|
||||||
|
local vel = dcsCommon.vMag(entry.tdVel)
|
||||||
|
local vkm = math.floor(vel * 36) / 10
|
||||||
|
local kkm = math.floor(vel * 19.4383) / 10
|
||||||
|
entry.msg = entry.msg .. "\nLanded heading " .. lHdg .. "°, diverging by " .. dHdg .. "° from runway heading, velocity at touchdown " .. vkm .. " kmh/" .. kkm .. " kts, touchdown " .. offcenter .. " m off centerline\n"
|
||||||
|
|
||||||
|
-- inside TDZ?
|
||||||
|
local tdZone = theZone.normTDZone
|
||||||
|
if opposite and theZone.opposing then
|
||||||
|
|
||||||
|
tdZone = theZone.opTDZone
|
||||||
|
end
|
||||||
|
if tdZone:pointInZone(p) then
|
||||||
|
-- yes, how far behind threshold
|
||||||
|
-- project point onto line to see how far inside
|
||||||
|
local distBehind = dcsCommon.distanceOfPointPToLineXZ(p, tdZone.poly[1], tdZone.poly[4])
|
||||||
|
local zonelen = math.abs(theZone.starts-theZone.ends)
|
||||||
|
local percentile = math.floor(distBehind / zonelen * 100)
|
||||||
|
local rating = ""
|
||||||
|
if percentile < 5 or percentile > 90 then rating = "marginal"
|
||||||
|
elseif percentile < 15 or percentile > 80 then rating = "pass"
|
||||||
|
elseif percentile < 25 or percentile > 60 then rating = "good"
|
||||||
|
else rating = "excellent" end
|
||||||
|
entry.msg = entry.msg .. "Touchdown inside TD-Zone, <" .. math.floor(distBehind) .. " m> behind threshold, rating = " .. rating .. "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
tdz.watchlist[playerName] = entry
|
||||||
|
if not tdz.watching then
|
||||||
|
tdz.watching = true
|
||||||
|
timer.scheduleFunction(tdz.watchLandings, {}, timer.getTime() + 0.2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tdz:onEvent(event)
|
||||||
|
if not event.initiator then return end
|
||||||
|
local theUnit = event.initiator
|
||||||
|
if not theUnit.getPlayerName then return end
|
||||||
|
local playerName = theUnit:getPlayerName()
|
||||||
|
if not playerName then return end
|
||||||
|
if event.id == 4 then
|
||||||
|
-- player landed
|
||||||
|
tdz.playerLanded(theUnit, playerName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Monitor landings in progress
|
||||||
|
--
|
||||||
|
function tdz.watchLandings()
|
||||||
|
local filtered = {}
|
||||||
|
local count = 0
|
||||||
|
local transfer = false
|
||||||
|
local success = false
|
||||||
|
local now = timer.getTime()
|
||||||
|
for playerName, aLanding in pairs (tdz.watchlist) do
|
||||||
|
-- see if landing timed out
|
||||||
|
local tdiff = now - aLanding.theTime
|
||||||
|
if tdiff < tdz.timeoutAfter then
|
||||||
|
local theUnit = Unit.getByName(aLanding.unitName)
|
||||||
|
if theUnit and Unit.isExist(theUnit) then
|
||||||
|
local vel = theUnit:getVelocity()
|
||||||
|
local vel = dcsCommon.vMag(vel)
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
if aLanding.theZone.runwayZone:pointInZone(p) then
|
||||||
|
-- we must slow down to below 3.6 km/h
|
||||||
|
if vel < 1 then
|
||||||
|
-- make sure that we are still inside the runway
|
||||||
|
success = true
|
||||||
|
else
|
||||||
|
transfer = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outTextForGroup(aLanding.gID, "Ran off runway.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if transfer then
|
||||||
|
count = count + 1
|
||||||
|
filtered[playerName] = aLanding
|
||||||
|
else
|
||||||
|
local theZone = aLanding.theZone
|
||||||
|
if success then
|
||||||
|
local theUnit = Unit.getByName(aLanding.unitName)
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
local tdist = math.floor(dcsCommon.distFlat(p, aLanding.tdPoint))
|
||||||
|
aLanding.msg = aLanding.msg .."\nSuccessful landing for " .. aLanding.playerName .." in a " .. aLanding.theType .. ". Landing run = <" .. tdist .. " m>, <" .. math.floor(tdiff*10)/10 .. "> seconds from touch-down to standstill."
|
||||||
|
|
||||||
|
if aLanding.hops > 1 then
|
||||||
|
aLanding.msg = aLanding.msg .. "\nNumber of hops: " .. aLanding.hops
|
||||||
|
end
|
||||||
|
if theZone.landedFlag then
|
||||||
|
theZone:pollFlag(theZone.landedFlag, theZone.method)
|
||||||
|
end
|
||||||
|
aLanding.msg = aLanding.msg .."\n"
|
||||||
|
trigger.action.outTextForGroup(aLanding.gID, aLanding.msg, 30)
|
||||||
|
else
|
||||||
|
if theZone.failFlag then
|
||||||
|
theZone:pollFlag(theZone.failFlag, theZone.method)
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(aLanding.gID, "Landing for " .. aLanding.playerName .." incomplete.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
tdz.watchlist = filtered
|
||||||
|
|
||||||
|
if count > 0 then
|
||||||
|
timer.scheduleFunction(tdz.watchLandings, {}, timer.getTime() + 0.2)
|
||||||
|
else
|
||||||
|
tdz.watching = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- Start
|
||||||
|
--
|
||||||
|
function tdz.readConfigZone()
|
||||||
|
end
|
||||||
|
|
||||||
|
function tdz.start()
|
||||||
|
if not dcsCommon.libCheck("cfx TDZ",
|
||||||
|
tdz.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
tdz.readConfigZone()
|
||||||
|
|
||||||
|
-- collect all wp target zones
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("TDZ")
|
||||||
|
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
tdz.createTDZ(aZone) -- process attribute and add to zone
|
||||||
|
table.insert(tdz.allTdz, aZone) -- remember it so we can smoke it
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add event handler
|
||||||
|
world.addEventHandler(tdz)
|
||||||
|
|
||||||
|
trigger.action.outText("cf/x TDZ version " .. tdz.version .. " running", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not tdz.start() then
|
||||||
|
trigger.action.outText("cf/x TDZ aborted: missing libraries", 30)
|
||||||
|
tdz = nil
|
||||||
|
end
|
||||||
564
modules/bombRange.lua
Normal file
564
modules/bombRange.lua
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
bombRange = {}
|
||||||
|
bombRange.version = "1.0.0"
|
||||||
|
bombRange.dh = 1 -- meters above ground level burst
|
||||||
|
|
||||||
|
bombRange.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
}
|
||||||
|
|
||||||
|
bombRange.bombs = {} -- live tracking
|
||||||
|
bombRange.ranges = {} -- all bomb ranges
|
||||||
|
bombRange.playerData = {} -- player accumulated data
|
||||||
|
bombRange.unitComms = {} -- command interface per unit
|
||||||
|
bombRange.tracking = false -- if true, we are tracking projectiles
|
||||||
|
bombRange.myStatics = {} -- indexed by id
|
||||||
|
|
||||||
|
function bombRange.addBomb(theBomb)
|
||||||
|
table.insert(bombRange.bombs, theBomb)
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.addRange(theZone)
|
||||||
|
table.insert(bombRange.ranges, theZone)
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.markRange(theZone)
|
||||||
|
local newObjects = theZone:markZoneWithObjects(theZone.markType, theZone.markNum, false)
|
||||||
|
for idx, aStatic in pairs(newObjects) do
|
||||||
|
local theID = tonumber(aStatic:getID())
|
||||||
|
bombRange.myStatics[theID] = aStatic
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.markCenter(theZone)
|
||||||
|
local theObject = theZone:markCenterWithObject(theZone.centerType)
|
||||||
|
--table.insert(bombRange.myStatics, theObject)
|
||||||
|
local theID = tonumber(theObject:getID())
|
||||||
|
bombRange.myStatics[theID] = theObject
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.createRange(theZone) -- has bombRange attribte to mark it
|
||||||
|
theZone.usePercentage = theZone:getBoolFromZoneProperty("percentage", theZone.isCircle)
|
||||||
|
if theZone.usePercentage and theZone.isPoly then
|
||||||
|
trigger.action.outText("+++bRng: WARNING: zone <" .. theZone.name .. "> is not a circular zone but wants to use percentage scoring!", 30)
|
||||||
|
end
|
||||||
|
theZone.details = theZone:getBoolFromZoneProperty("details", false)
|
||||||
|
theZone.reporter = theZone:getBoolFromZoneProperty("reporter", true)
|
||||||
|
theZone.reportName = theZone:getBoolFromZoneProperty("reportName", true)
|
||||||
|
theZone.smokeHits = theZone:getBoolFromZoneProperty("smokeHits", false)
|
||||||
|
theZone.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "blue")
|
||||||
|
theZone.flagHits = theZone:getBoolFromZoneProperty("flagHits", false)
|
||||||
|
theZone.flagType = theZone:getStringFromZoneProperty("flagType", "Red_Flag")
|
||||||
|
theZone.clipDist = theZone:getNumberFromZoneProperty("clipDist", 2000) -- when further way, the drop will be disregarded
|
||||||
|
|
||||||
|
theZone.method = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
|
if theZone:hasProperty("hit!") then
|
||||||
|
theZone.hitOut = theZone:getStringFromZoneProperty("hit!", "<none>")
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone.markType = theZone:getStringFromZoneProperty("markType", "Black_Tyre_RF")
|
||||||
|
theZone.markBoundary = theZone:getBoolFromZoneProperty("markBoundary", false)
|
||||||
|
theZone.markNum = theZone:getNumberFromZoneProperty("markNum", 3) -- per quarter
|
||||||
|
theZone.markCenter = theZone:getBoolFromZoneProperty("markCenter", false)
|
||||||
|
theZone.centerType = theZone:getStringFromZoneProperty("centerType", "house2arm")
|
||||||
|
theZone.markOnMap = theZone:getBoolFromZoneProperty("markOnMap", false)
|
||||||
|
theZone.mapColor = theZone:getRGBAVectorFromZoneProperty("mapColor", {0.8, 0.8, 0.8, 1.0})
|
||||||
|
theZone.mapFillColor = theZone:getRGBAVectorFromZoneProperty("mapFillColor", {0.8, 0.8, 0.8, 0.2})
|
||||||
|
if theZone.markBoundary then bombRange.markRange(theZone) end
|
||||||
|
if theZone.markCenter then bombRange.markCenter(theZone) end
|
||||||
|
if theZone.markOnMap then
|
||||||
|
local markID = theZone:drawZone(theZone.mapColor, theZone.mapFillColor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- player data
|
||||||
|
--
|
||||||
|
function bombRange.getPlayerData(name)
|
||||||
|
local theData = bombRange.playerData[name]
|
||||||
|
if not theData then
|
||||||
|
theData = {}
|
||||||
|
theData.aircraft = {} -- by typeDesc contains all drops per weapon type
|
||||||
|
theData.totalDrops = 0
|
||||||
|
theData.totalHits = 0
|
||||||
|
theData.totalPercentage = 0 -- sum, must be divided by drops
|
||||||
|
bombRange.playerData[name] = theData
|
||||||
|
-- trigger.action.outText("created new player data for " .. name, 30)
|
||||||
|
end
|
||||||
|
return theData
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.addImpactForWeapon(weapon, isInside, percentage)
|
||||||
|
if not percentage then percentage = 0 end
|
||||||
|
if type(percentage) == "string" then percentage = 1 end -- handle poly
|
||||||
|
|
||||||
|
local theData = bombRange.getPlayerData(weapon.pName)
|
||||||
|
local uType = weapon.uType
|
||||||
|
local uData = theData.aircraft[uType]
|
||||||
|
if not uData then
|
||||||
|
uData = {}
|
||||||
|
uData.wTypes = {}
|
||||||
|
theData.aircraft[uType] = uData
|
||||||
|
end
|
||||||
|
wType = weapon.type
|
||||||
|
local wData = uData.wTypes[wType]
|
||||||
|
if not wData then
|
||||||
|
wData = {shots = 0, hits = 0, percentage = 0}
|
||||||
|
uData.wTypes[wType] = wData
|
||||||
|
end
|
||||||
|
|
||||||
|
wData.shots = wData.shots + 1
|
||||||
|
if isInside then
|
||||||
|
wData.hits = wData.hits + 1
|
||||||
|
wData.percentage = wData.percentage + percentage
|
||||||
|
else
|
||||||
|
end
|
||||||
|
theData.totalDrops = theData.totalDrops + 1
|
||||||
|
if isInside then
|
||||||
|
theData.totalHits = theData.totalHits + 1
|
||||||
|
theData.totalPercentage = theData.totalPercentage + percentage
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.showStatsForPlayer(pName, gID, unitName)
|
||||||
|
local theData = bombRange.getPlayerData(pName)
|
||||||
|
local msg = "\nWeapons Range Statistics for " .. pName .. "\n"
|
||||||
|
local lineCount = 0
|
||||||
|
for aType, aircraft in pairs(theData.aircraft) do
|
||||||
|
if aircraft.wTypes then
|
||||||
|
if lineCount < 1 then
|
||||||
|
msg = msg .. " Aircraft / Munition : Drops / Hits / Quality\n"
|
||||||
|
end
|
||||||
|
for wName, wData in pairs(aircraft.wTypes) do
|
||||||
|
local pct = wData.percentage / wData.shots
|
||||||
|
pct = math.floor(pct * 10) / 10
|
||||||
|
msg = msg .. " " .. aType .. " / " .. wName .. ": " .. wData.shots .. " / " .. wData.hits .. " / " .. pct .. "%\n"
|
||||||
|
lineCount = lineCount + 1
|
||||||
|
end
|
||||||
|
end -- if weapon per aircraft
|
||||||
|
end
|
||||||
|
|
||||||
|
if lineCount < 1 then
|
||||||
|
msg = msg .. "\n NO DATA\n\n"
|
||||||
|
else
|
||||||
|
msg = msg .. "\n Total ordnance drops: " .. theData.totalDrops
|
||||||
|
msg = msg .. "\n Total on target: " .. theData.totalHits
|
||||||
|
local q = math.floor(theData.totalPercentage / theData.totalDrops * 10) / 10
|
||||||
|
msg = msg .. "\n Total Quality: " .. q .. "%\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
if bombRange.mustCheckIn then
|
||||||
|
local comms = bombRange.unitComms[unitName]
|
||||||
|
if comms.checkedIn then
|
||||||
|
msg = msg .. "\nYou are checked in with weapons range command.\n"
|
||||||
|
else
|
||||||
|
msg = msg .. "\nPLEASE CHECK IN with weapons range command.\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
trigger.action.outTextForGroup(gID, msg, 30)
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
--
|
||||||
|
-- unit UI
|
||||||
|
--
|
||||||
|
|
||||||
|
function bombRange.initCommsForUnit(theUnit)
|
||||||
|
local uName = theUnit:getName()
|
||||||
|
local pName = theUnit:getPlayerName()
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
local comms = bombRange.unitComms[uName]
|
||||||
|
if comms then
|
||||||
|
if bombRange.mustCheckIn then
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.checkin)
|
||||||
|
end
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.reset)
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.getStat)
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.root)
|
||||||
|
end
|
||||||
|
comms = {}
|
||||||
|
comms.checkedIn = false
|
||||||
|
comms.root = missionCommands.addSubMenuForGroup(gID, bombRange.menuTitle)
|
||||||
|
comms.getStat = missionCommands.addCommandForGroup(gID, "Get statistics for " .. pName, comms.root, bombRange.redirectComms, {"getStat", uName, pName, gID})
|
||||||
|
comms.reset = missionCommands.addCommandForGroup(gID, "RESET statistics for " .. pName, comms.root, bombRange.redirectComms, {"reset", uName, pName, gID})
|
||||||
|
if bombRange.mustCheckIn then
|
||||||
|
comms.checkin = missionCommands.addCommandForGroup(gID, "Check in with range", comms.root, bombRange.redirectComms, {"check", uName, pName, gID})
|
||||||
|
end
|
||||||
|
bombRange.unitComms[uName] = comms
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.redirectComms(args)
|
||||||
|
timer.scheduleFunction(bombRange.commsRequest, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.commsRequest(args)
|
||||||
|
local command = args[1] -- getStat, check,
|
||||||
|
local uName = args[2]
|
||||||
|
local pName = args[3]
|
||||||
|
local theUnit = Unit.getByName(uName)
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gID = theGroup:getID()
|
||||||
|
|
||||||
|
if command == "getStat" then
|
||||||
|
bombRange.showStatsForPlayer(pName, gID, uName)
|
||||||
|
end
|
||||||
|
|
||||||
|
if command == "reset" then
|
||||||
|
bombRange.playerData[pName] = nil
|
||||||
|
trigger.action.outTextForGroup(gID, "Clean slate, " .. uName .. ", all existing records have been deleted.", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
if command == "check" then
|
||||||
|
comms = bombRange.unitComms[uName]
|
||||||
|
if comms.checkedIn then
|
||||||
|
comms.checkedIn = false -- we are now checked out
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.checkin)
|
||||||
|
comms.checkin = missionCommands.addCommandForGroup(gID, "Check in with range", comms.root, bombRange.redirectComms, {"check", uName, pName, gID})
|
||||||
|
|
||||||
|
trigger.action.outTextForGroup(gID, "Roger, " .. uName .. ", terminating range advisory. Have a good day!", 30)
|
||||||
|
if bombRange.signOut then
|
||||||
|
cfxZones.pollFlag(bombRange.signOut, bombRange.method, bombRange)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
comms.checkedIn = true
|
||||||
|
missionCommands.removeItemForGroup(gID, comms.checkin)
|
||||||
|
comms.checkin = missionCommands.addCommandForGroup(gID, "Check OUT " .. uName .. " from range", comms.root, bombRange.redirectComms, {"check", uName, pName, gID})trigger.action.outTextForGroup(gID, uName .. ", you are go for weapons deployment, observers standing by.", 30)
|
||||||
|
if bombRange.signIn then
|
||||||
|
cfxZones.pollFlag(bombRange.signIn, bombRange.method, bombRange)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Event Proccing
|
||||||
|
--
|
||||||
|
function bombRange.suspectedHit(weapon, target)
|
||||||
|
if not bombRange.tracking then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not target then return end
|
||||||
|
local theType = target:getTypeName()
|
||||||
|
|
||||||
|
for idx, aType in pairs(bombRange.filterTypes) do
|
||||||
|
if theType == aType then return end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- try and match target to my known statics, exit if match
|
||||||
|
if not target.getID then return end -- units have no getID!
|
||||||
|
local theID = tonumber(target:getID())
|
||||||
|
if bombRange.myStatics[theID] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- look through the tracked weapons for a match
|
||||||
|
local filtered = {}
|
||||||
|
local hasfound = false
|
||||||
|
for idx, b in pairs (bombRange.bombs) do
|
||||||
|
if b.weapon == weapon then
|
||||||
|
hasfound = true
|
||||||
|
-- update b to current position and velocity
|
||||||
|
b.pos = weapon:getPoint()
|
||||||
|
b.v = weapon:getVelocity()
|
||||||
|
bombRange.impacted(b, target)
|
||||||
|
else
|
||||||
|
table.insert(filtered, b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if hasfound then
|
||||||
|
bombRange.bombs = filtered
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange:onEvent(event)
|
||||||
|
if not event.initiator then return end
|
||||||
|
local theUnit = event.initiator
|
||||||
|
|
||||||
|
if event.id == 2 then -- hit
|
||||||
|
if not event.weapon then return end
|
||||||
|
bombRange.suspectedHit(event.weapon, event.target)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local uName = nil
|
||||||
|
local pName = nil
|
||||||
|
if theUnit.getPlayerName and theUnit:getPlayerName() ~= nil then
|
||||||
|
uName = theUnit:getName()
|
||||||
|
pName = theUnit:getPlayerName()
|
||||||
|
else return end
|
||||||
|
|
||||||
|
if event.id == 1 then -- shot event, from player
|
||||||
|
if not event.weapon then return end
|
||||||
|
local uComms = bombRange.unitComms[uName]
|
||||||
|
if bombRange.mustCheckIn and (not uComms.checkedIn) then
|
||||||
|
if bombRange.verbose then
|
||||||
|
trigger.action.outText("+++bRng: Player <" .. pName .. "> not checked in.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local w = event.weapon
|
||||||
|
local b = {}
|
||||||
|
local bName = w:getName()
|
||||||
|
b.name = bName
|
||||||
|
b.type = w:getTypeName()
|
||||||
|
-- may need to verify type: how do we handle clusters or flares?
|
||||||
|
b.pos = w:getPoint()
|
||||||
|
b.v = w:getVelocity()
|
||||||
|
b.pName = pName
|
||||||
|
b.uName = uName
|
||||||
|
b.uType = theUnit:getTypeName()
|
||||||
|
b.gID = theUnit:getGroup():getID()
|
||||||
|
b.weapon = w
|
||||||
|
b.released = timer.getTime()
|
||||||
|
b.relPos = b.pos
|
||||||
|
table.insert(bombRange.bombs, b)
|
||||||
|
if not bombRange.tracking then
|
||||||
|
timer.scheduleFunction(bombRange.updateBombs, {}, timer.getTime() + 1/bombRange.ups)
|
||||||
|
bombRange.tracking = true
|
||||||
|
if bombRange.verbose then
|
||||||
|
trigger.action.outText("+++bRng: start tracking.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if bombRange.verbose then
|
||||||
|
trigger.action.outText("+++bRng: Player <" .. pName .. "> fired a <" .. b.type .. ">, named <" .. b.name .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.id == 15 then
|
||||||
|
bombRange.initCommsForUnit(theUnit)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Update
|
||||||
|
--
|
||||||
|
function bombRange.impacted(weapon, target)
|
||||||
|
local targetName = nil
|
||||||
|
local ipos = weapon.pos -- default to weapon location
|
||||||
|
if target then
|
||||||
|
ipos = target:getPoint()
|
||||||
|
targetName = target:getDesc()
|
||||||
|
if targetName then targetName = targetName.displayName end
|
||||||
|
if not targetName then targetName = target:getTypeName() end
|
||||||
|
else
|
||||||
|
-- not an object hit, interpolate the impact point on ground:
|
||||||
|
-- calculate impact point. we use the linear equation
|
||||||
|
-- pos.y + t*velocity.y - height = 1 (height above gnd) and solve for t
|
||||||
|
local h = land.getHeight({x=weapon.pos.x, y=weapon.pos.z}) - bombRange.dh -- dh m above gnd
|
||||||
|
local t = (h-weapon.pos.y) / weapon.v.y
|
||||||
|
-- having t, we project location using pos and vel
|
||||||
|
-- impactpos = pos + t * velocity
|
||||||
|
local imod = dcsCommon.vMultScalar(weapon.v, t)
|
||||||
|
ipos = dcsCommon.vAdd(weapon.pos, imod) -- calculated impact point
|
||||||
|
end
|
||||||
|
|
||||||
|
-- see if inside a range
|
||||||
|
if #bombRange.ranges < 1 then
|
||||||
|
trigger.action.outText("+++bRng: No Bomb Ranges detected!")
|
||||||
|
return -- no need to update anything
|
||||||
|
end
|
||||||
|
local minDist = math.huge
|
||||||
|
local theRange = nil
|
||||||
|
for idx, theZone in pairs(bombRange.ranges) do
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local dist = dcsCommon.distFlat(p, ipos)
|
||||||
|
if dist < minDist then
|
||||||
|
minDist = dist
|
||||||
|
theRange = theZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not theRange then
|
||||||
|
trigger.action.outText("+++bRng: nil <theRange> on eval. skipping.", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if minDist > theRange.clipDist then
|
||||||
|
-- no taget zone inside clip dist. disregard this one, too far off
|
||||||
|
if bombRange.reportLongMisses then
|
||||||
|
trigger.action.outTextForGroup(weapon.gID, "Impact of <" .. weapon.type .. "> released by <" .. weapon.pName .. "> outside bomb range and disregarded.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if theRange.smokeHits then
|
||||||
|
trigger.action.smoke(ipos, theRange.smokeColor)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not target) and theRange.flagHits then -- only ground imparts are flagged
|
||||||
|
local cty = dcsCommon.getACountryForCoalition(0) -- some neutral county
|
||||||
|
local p = {x=ipos.x, y=ipos.z}
|
||||||
|
local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(weapon.type .. " impact"), theRange.flagType)
|
||||||
|
dcsCommon.moveStaticDataTo(theStaticData, p.x, p.y)
|
||||||
|
local theObject = coalition.addStaticObject(cty, theStaticData)
|
||||||
|
end
|
||||||
|
|
||||||
|
if theRange.reporter and theRange.details then
|
||||||
|
local t = math.floor((timer.getTime() - weapon.released) * 10) / 10
|
||||||
|
local v = math.floor(dcsCommon.vMag(weapon.v))
|
||||||
|
local tDist = dcsCommon.dist(ipos, weapon.relPos)/1000
|
||||||
|
tDist = math.floor(tDist*100) /100
|
||||||
|
trigger.action.outTextForGroup(weapon.gID, "impact of " .. weapon.type .. " released by " .. weapon.pName .. " from " .. weapon.uType .. " after traveling " .. tDist .. " km in " .. t .. " sec, impact velocity at impact is " .. v .. " m/s!", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
local msg = ""
|
||||||
|
if theRange:pointInZone(ipos) then
|
||||||
|
local percentage = 0
|
||||||
|
if theRange.isPoly then
|
||||||
|
percentage = 100
|
||||||
|
else
|
||||||
|
percentage = 1 - (minDist / theRange.radius)
|
||||||
|
percentage = math.floor(percentage * 100)
|
||||||
|
end
|
||||||
|
msg = "INSIDE target area"
|
||||||
|
if theRange.reportName then msg = msg .. " " .. theRange.name end
|
||||||
|
if (not targetName) and theRange.details then msg = msg .. ", off-center by " .. math.floor(minDist *10)/10 .. " m" end
|
||||||
|
if targetName then msg = msg .. ", hit on " .. targetName end
|
||||||
|
|
||||||
|
if not theRange.usePercentage then
|
||||||
|
percentage = 100
|
||||||
|
else
|
||||||
|
msg = msg .. " (Quality " .. percentage .."%)"
|
||||||
|
end
|
||||||
|
|
||||||
|
if theRange.hitOut then
|
||||||
|
theZone:pollFlag(theRange.hitOut, theRange.method)
|
||||||
|
end
|
||||||
|
|
||||||
|
bombRange.addImpactForWeapon(weapon, true, percentage)
|
||||||
|
else
|
||||||
|
msg = "Outside target area"
|
||||||
|
if theRange.reportName then msg = msg .. " " .. theRange.name end
|
||||||
|
if theRange.details then msg = msg .. "(off-center by " .. math.floor(minDist *10)/10 .. " m)" end
|
||||||
|
msg = msg .. ", no hit."
|
||||||
|
bombRange.addImpactForWeapon(weapon, false, 0)
|
||||||
|
end
|
||||||
|
if theRange.reporter then
|
||||||
|
trigger.action.outTextForGroup(weapon.gID,msg , 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.updateBombs()
|
||||||
|
|
||||||
|
local filtered = {}
|
||||||
|
for idx, theWeapon in pairs(bombRange.bombs) do
|
||||||
|
if Weapon.isExist(theWeapon.weapon) then
|
||||||
|
-- update pos and vel
|
||||||
|
theWeapon.pos = theWeapon.weapon:getPoint()
|
||||||
|
theWeapon.v = theWeapon.weapon:getVelocity()
|
||||||
|
table.insert(filtered, theWeapon)
|
||||||
|
else
|
||||||
|
-- interpolate the impact position from last position
|
||||||
|
bombRange.impacted(theWeapon)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
bombRange.bombs = filtered
|
||||||
|
if #filtered > 0 then
|
||||||
|
timer.scheduleFunction(bombRange.updateBombs, {}, timer.getTime() + 1/bombRange.ups)
|
||||||
|
bombRange.tracking = true
|
||||||
|
else
|
||||||
|
bombRange.tracking = false
|
||||||
|
if bombRange.verbose then
|
||||||
|
trigger.action.outText("+++bRng: stopped tracking.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- load & save data
|
||||||
|
--
|
||||||
|
function bombRange.saveData()
|
||||||
|
local theData = {}
|
||||||
|
-- save current score list. simple clone
|
||||||
|
local theStats = dcsCommon.clone(bombRange.playerData)
|
||||||
|
theData.theStats = theStats
|
||||||
|
return theData
|
||||||
|
end
|
||||||
|
|
||||||
|
function bombRange.loadData()
|
||||||
|
if not persistence then return end
|
||||||
|
local theData = persistence.getSavedDataForModule("bombRange")
|
||||||
|
if not theData then
|
||||||
|
if bombRange.verbose then
|
||||||
|
trigger.action.outText("+++bRng: no save date received, skipping.", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local theStats = theData.theStats
|
||||||
|
bombRange.playerData = theStats
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Config & Start
|
||||||
|
--
|
||||||
|
function bombRange.readConfigZone()
|
||||||
|
bombRange.name = "bombRangeConfig"
|
||||||
|
local theZone = cfxZones.getZoneByName("bombRangeConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("bombRangeConfig")
|
||||||
|
end
|
||||||
|
local theSet = theZone:getStringFromZoneProperty("filterTypes", "house2arm, Black_Tyre_RF, Red_Flag")
|
||||||
|
theSet = dcsCommon.splitString(theSet, ",")
|
||||||
|
bombRange.filterTypes = dcsCommon.trimArray(theSet)
|
||||||
|
bombRange.reportLongMisses = theZone:getBoolFromZoneProperty("reportLongMisses", false)
|
||||||
|
bombRange.mustCheckIn = theZone:getBoolFromZoneProperty("mustCheckIn", false)
|
||||||
|
bombRange.ups = theZone:getNumberFromZoneProperty("ups", 20)
|
||||||
|
bombRange.menuTitle = theZone:getStringFromZoneProperty("menuTitle","Contact BOMB RANGE")
|
||||||
|
if theZone:hasProperty("signIn!") then
|
||||||
|
bombRange.signIn = theZone:getStringFromZoneProperty("signIn!", 30)
|
||||||
|
end
|
||||||
|
if theZone:hasProperty("signOut!") then
|
||||||
|
bombRange.signOut = theZone:getStringFromZoneProperty("signOut!", 30)
|
||||||
|
end
|
||||||
|
bombRange.method = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
|
bombRange.verbose = theZone.verbose
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function bombRange.start()
|
||||||
|
if not dcsCommon.libCheck("cfx bombRange",
|
||||||
|
bombRange.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
bombRange.readConfigZone()
|
||||||
|
|
||||||
|
-- collect all wp target zones
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("bombRange")
|
||||||
|
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
bombRange.createRange(aZone) -- process attribute and add to zone
|
||||||
|
bombRange.addRange(aZone) -- remember it so we can smoke it
|
||||||
|
end
|
||||||
|
|
||||||
|
-- load data
|
||||||
|
if persistence then
|
||||||
|
-- sign up for persistence
|
||||||
|
callbacks = {}
|
||||||
|
callbacks.persistData = bombRange.saveData
|
||||||
|
persistence.registerModule("bombRange", callbacks)
|
||||||
|
-- now load my data
|
||||||
|
bombRange.loadData()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add event handler
|
||||||
|
world.addEventHandler(bombRange)
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
if not bombRange.start() then
|
||||||
|
trigger.action.outText("cf/x Bomb Range aborted: missing libraries", 30)
|
||||||
|
bombRange = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- add persistence
|
||||||
|
--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
cfxNDB = {}
|
cfxNDB = {}
|
||||||
cfxNDB.version = "1.2.1"
|
cfxNDB.version = "1.3.0"
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
cfxNDB:
|
cfxNDB:
|
||||||
@ -26,6 +26,7 @@ cfxNDB.version = "1.2.1"
|
|||||||
- update only when moving and delta > maxDelta
|
- update only when moving and delta > maxDelta
|
||||||
- zone-local verbosity support
|
- zone-local verbosity support
|
||||||
- better config defaulting
|
- better config defaulting
|
||||||
|
1.3.0 - dmlZones
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -104,42 +105,42 @@ function cfxNDB.stopNDB(theNDB)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxNDB.createNDBWithZone(theZone)
|
function cfxNDB.createNDBWithZone(theZone)
|
||||||
theZone.freq = cfxZones.getNumberFromZoneProperty(theZone, "NDB", 124) -- in MHz
|
theZone.freq = theZone:getNumberFromZoneProperty("NDB", 124) -- in MHz
|
||||||
-- convert MHz to Hz
|
-- convert MHz to Hz
|
||||||
theZone.freq = theZone.freq * 1000000 -- Hz
|
theZone.freq = theZone.freq * 1000000 -- Hz
|
||||||
theZone.fm = cfxZones.getBoolFromZoneProperty(theZone, "fm", false)
|
theZone.fm = theZone:getBoolFromZoneProperty("fm", false)
|
||||||
theZone.ndbSound = cfxZones.getStringFromZoneProperty(theZone, "soundFile", "<none>")
|
theZone.ndbSound = theZone:getStringFromZoneProperty("soundFile", "<none>")
|
||||||
theZone.power = cfxZones.getNumberFromZoneProperty(theZone, "watts", cfxNDB.power)
|
theZone.power = theZone:getNumberFromZoneProperty("watts", cfxNDB.power)
|
||||||
theZone.loop = true -- always. NDB always loops
|
theZone.loop = true -- always. NDB always loops
|
||||||
-- UNSUPPORTED refresh. Although read individually, it only works
|
-- UNSUPPORTED refresh. Although read individually, it only works
|
||||||
-- when LARGER than module's refresh.
|
-- when LARGER than module's refresh.
|
||||||
theZone.ndbRefresh = cfxZones.getNumberFromZoneProperty(theZone, "ndbRefresh", cfxNDB.refresh) -- only used if linked
|
theZone.ndbRefresh = theZone:getNumberFromZoneProperty("ndbRefresh", cfxNDB.refresh) -- only used if linked
|
||||||
theZone.ndbRefreshTime = timer.getTime() + theZone.ndbRefresh -- only used with linkedUnit, but set up nonetheless
|
theZone.ndbRefreshTime = timer.getTime() + theZone.ndbRefresh -- only used with linkedUnit, but set up nonetheless
|
||||||
|
|
||||||
-- paused
|
-- paused
|
||||||
theZone.paused = cfxZones.getBoolFromZoneProperty(theZone, "paused", false)
|
theZone.paused = theZone:getBoolFromZoneProperty("paused", false)
|
||||||
|
|
||||||
-- watchflags
|
-- watchflags
|
||||||
theZone.ndbTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
theZone.ndbTriggerMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||||
if cfxZones.hasProperty(theZone, "ndbTriggerMethod") then
|
if theZone:hasProperty("ndbTriggerMethod") then
|
||||||
theZone.ndbTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "ndbTriggerMethod", "change")
|
theZone.ndbTriggerMethod = theZone:getStringFromZoneProperty("ndbTriggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- on/offf query flags
|
-- on/offf query flags
|
||||||
if cfxZones.hasProperty(theZone, "on?") then
|
if theZone:hasProperty("on?") then
|
||||||
theZone.onFlag = cfxZones.getStringFromZoneProperty(theZone, "on?", "none")
|
theZone.onFlag = theZone:getStringFromZoneProperty("on?", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.onFlag then
|
if theZone.onFlag then
|
||||||
theZone.onFlagVal = cfxZones.getFlagValue(theZone.onFlag, theZone) -- trigger.misc.getUserFlag(theZone.onFlag) -- save last value
|
theZone.onFlagVal = theZone:getFlagValue(theZone.onFlag) -- save last value
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "off?") then
|
if theZone:hasProperty("off?") then
|
||||||
theZone.offFlag = cfxZones.getStringFromZoneProperty(theZone, "off?", "none")
|
theZone.offFlag = theZone:getStringFromZoneProperty("off?", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.offFlag then
|
if theZone.offFlag then
|
||||||
theZone.offFlagVal = cfxZones.getFlagValue(theZone.offFlag, theZone) --trigger.misc.getUserFlag(theZone.offFlag) -- save last value
|
theZone.offFlagVal = theZone:getFlagValue(theZone.offFlag) -- save last value
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start it
|
-- start it
|
||||||
@ -170,7 +171,7 @@ function cfxNDB.update()
|
|||||||
if not theNDB.lastLoc then
|
if not theNDB.lastLoc then
|
||||||
cfxNDB.startNDB(theNDB) -- never was started
|
cfxNDB.startNDB(theNDB) -- never was started
|
||||||
else
|
else
|
||||||
local loc = cfxZones.getPoint(theNDB) -- y === 0
|
local loc = theNDB:getPoint() -- y === 0
|
||||||
loc.y = land.getHeight({x = loc.x, y = loc.z}) -- get y from land
|
loc.y = land.getHeight({x = loc.x, y = loc.z}) -- get y from land
|
||||||
local delta = dcsCommon.dist(loc, theNDB.lastLoc)
|
local delta = dcsCommon.dist(loc, theNDB.lastLoc)
|
||||||
if delta > cfxNDB.maxDist then
|
if delta > cfxNDB.maxDist then
|
||||||
@ -182,17 +183,15 @@ function cfxNDB.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- now check triggers to start/stop
|
-- now check triggers to start/stop
|
||||||
if cfxZones.testZoneFlag(theNDB, theNDB.onFlag, theNDB.ndbTriggerMethod, "onFlagVal") then
|
if theNDB:testZoneFlag(theNDB.onFlag, theNDB.ndbTriggerMethod, "onFlagVal") then
|
||||||
-- yupp, trigger start
|
-- yupp, trigger start
|
||||||
cfxNDB.startNDB(theNDB)
|
cfxNDB.startNDB(theNDB)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if theNDB:testZoneFlag(theNDB.offFlag, theNDB.ndbTriggerMethod, "offFlagVal") then
|
||||||
if cfxZones.testZoneFlag(theNDB, theNDB.offFlag, theNDB.ndbTriggerMethod, "offFlagVal") then
|
|
||||||
-- yupp, trigger start
|
-- yupp, trigger start
|
||||||
cfxNDB.stopNDB(theNDB)
|
cfxNDB.stopNDB(theNDB)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -205,10 +204,10 @@ function cfxNDB.readConfig()
|
|||||||
theZone = cfxZones.createSimpleZone("ndbConfig")
|
theZone = cfxZones.createSimpleZone("ndbConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxNDB.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxNDB.verbose = theZone.verbose
|
||||||
cfxNDB.ndbRefresh = cfxZones.getNumberFromZoneProperty(theZone, "ndbRefresh", 10)
|
cfxNDB.ndbRefresh = theZone:getNumberFromZoneProperty("ndbRefresh", 10)
|
||||||
|
|
||||||
cfxNDB.maxDist = cfxZones.getNumberFromZoneProperty(theZone, "maxDist", 50) -- max 50m error for movement
|
cfxNDB.maxDist = theZone:getNumberFromZoneProperty("maxDist", 50) -- max 50m error for movement
|
||||||
|
|
||||||
if cfxNDB.verbose then
|
if cfxNDB.verbose then
|
||||||
trigger.action.outText("***ndb: read config", 30)
|
trigger.action.outText("***ndb: read config", 30)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxObjectSpawnZones = {}
|
cfxObjectSpawnZones = {}
|
||||||
cfxObjectSpawnZones.version = "1.3.1"
|
cfxObjectSpawnZones.version = "2.0.0"
|
||||||
cfxObjectSpawnZones.requiredLibs = {
|
cfxObjectSpawnZones.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
|
||||||
@ -8,31 +8,33 @@ cfxObjectSpawnZones.requiredLibs = {
|
|||||||
}
|
}
|
||||||
cfxObjectSpawnZones.ups = 1
|
cfxObjectSpawnZones.ups = 1
|
||||||
cfxObjectSpawnZones.verbose = false
|
cfxObjectSpawnZones.verbose = false
|
||||||
--
|
--[[--
|
||||||
-- Zones that conform with this requirements spawn toops automatically
|
Zones that conform with this requirements spawn objects automatically
|
||||||
-- *** DOES NOT EXTEND ZONES ***
|
*** DOES NOT EXTEND ZONES ***
|
||||||
--
|
|
||||||
-- version history
|
|
||||||
-- 1.0.0 - based on 1.4.6 version from cfxSpawnZones
|
|
||||||
-- 1.1.0 - uses linkedUnit, so spawnming can occur on ships
|
|
||||||
-- make sure you also enable useOffset to place the
|
|
||||||
-- statics away from the center of the ship
|
|
||||||
-- 1.1.1 - also processes paused flag
|
|
||||||
-- - despawnRemaining(spawner)
|
|
||||||
-- 1.1.2 - autoRemove option re-installed
|
|
||||||
-- - added possibility to autoUnlink
|
|
||||||
-- 1.1.3 - ME-triggered flag via f? and triggerFlag
|
|
||||||
-- 1.1.4 - activate?, pause? attributes
|
|
||||||
-- 1.1.5 - spawn?, spawnObjects? synonyms
|
|
||||||
-- 1.2.0 - DML flag upgrade
|
|
||||||
-- 1.2.1 - config zone
|
|
||||||
-- - autoLink bug (zone instead of spawner accessed)
|
|
||||||
-- 1.3.0 - better synonym handling
|
|
||||||
-- - useDelicates link to delicate when spawned
|
|
||||||
-- - spawned single and multi-objects can be made delicates
|
|
||||||
-- 1.3.1 - baseName can be set to zone's name by giving "*"
|
|
||||||
-- 1.3.2 - delicateName supports '*' to refer to own zone
|
|
||||||
|
|
||||||
|
version history
|
||||||
|
1.0.0 - based on 1.4.6 version from cfxSpawnZones
|
||||||
|
1.1.0 - uses linkedUnit, so spawnming can occur on ships
|
||||||
|
make sure you also enable useOffset to place the
|
||||||
|
statics away from the center of the ship
|
||||||
|
1.1.1 - also processes paused flag
|
||||||
|
- despawnRemaining(spawner)
|
||||||
|
1.1.2 - autoRemove option re-installed
|
||||||
|
- added possibility to autoUnlink
|
||||||
|
1.1.3 - ME-triggered flag via f? and triggerFlag
|
||||||
|
1.1.4 - activate?, pause? attributes
|
||||||
|
1.1.5 - spawn?, spawnObjects? synonyms
|
||||||
|
1.2.0 - DML flag upgrade
|
||||||
|
1.2.1 - config zone
|
||||||
|
- autoLink bug (zone instead of spawner accessed)
|
||||||
|
1.3.0 - better synonym handling
|
||||||
|
- useDelicates link to delicate when spawned
|
||||||
|
- spawned single and multi-objects can be made delicates
|
||||||
|
1.3.1 - baseName can be set to zone's name by giving "*"
|
||||||
|
1.3.2 - delicateName supports '*' to refer to own zone
|
||||||
|
2.0.0 - dmlZones
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
-- respawn currently happens after theSpawns is deleted and cooldown seconds have passed
|
-- respawn currently happens after theSpawns is deleted and cooldown seconds have passed
|
||||||
cfxObjectSpawnZones.allSpawners = {}
|
cfxObjectSpawnZones.allSpawners = {}
|
||||||
@ -59,72 +61,70 @@ end
|
|||||||
function cfxObjectSpawnZones.createSpawner(inZone)
|
function cfxObjectSpawnZones.createSpawner(inZone)
|
||||||
local theSpawner = {}
|
local theSpawner = {}
|
||||||
theSpawner.zone = inZone
|
theSpawner.zone = inZone
|
||||||
theSpawner.name = inZone.name
|
theSpawner.name = inZone.name -- provide compat with cfxZones (not dmlZones, though)
|
||||||
|
|
||||||
-- connect with ME if a trigger flag is given
|
-- connect with ME if a trigger flag is given
|
||||||
if cfxZones.hasProperty(inZone, "f?") then
|
if inZone:hasProperty("f?") then
|
||||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "f?", "none")
|
theSpawner.triggerFlag = inZone:getStringFromZoneProperty("f?", "none")
|
||||||
elseif cfxZones.hasProperty(inZone, "spawn?") then
|
elseif inZone:hasProperty("spawn?") then
|
||||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawn?", "none")
|
theSpawner.triggerFlag = inZone:getStringFromZoneProperty("spawn?", "none")
|
||||||
elseif cfxZones.hasProperty(inZone, "spawnObjects?") then
|
elseif inZone:hasProperty("spawnObjects?") then
|
||||||
theSpawner.triggerFlag = cfxZones.getStringFromZoneProperty(inZone, "spawnObjects?", "none")
|
theSpawner.triggerFlag = inZone:getStringFromZoneProperty( "spawnObjects?", "none")
|
||||||
end
|
end
|
||||||
|
|
||||||
if theSpawner.triggerFlag then
|
if theSpawner.triggerFlag then
|
||||||
theSpawner.lastTriggerValue = cfxZones.getFlagValue(theSpawner.triggerFlag, theSpawner) -- trigger.misc.getUserFlag(theSpawner.triggerFlag)
|
theSpawner.lastTriggerValue = cfxZones.getFlagValue(theSpawner.triggerFlag, theSpawner)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(inZone, "activate?") then
|
if inZone:hasProperty("activate?") then
|
||||||
theSpawner.activateFlag = cfxZones.getStringFromZoneProperty(inZone, "activate?", "none")
|
theSpawner.activateFlag = inZone:getStringFromZoneProperty( "activate?", "none")
|
||||||
theSpawner.lastActivateValue = cfxZones.getFlagValue(theSpawner.activateFlag, theSpawner) --trigger.misc.getUserFlag(theSpawner.activateFlag)
|
theSpawner.lastActivateValue = cfxZones.getFlagValue(theSpawner.activateFlag, theSpawner) --trigger.misc.getUserFlag(theSpawner.activateFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(inZone, "pause?") then
|
if inZone:hasProperty("pause?") then
|
||||||
theSpawner.pauseFlag = cfxZones.getStringFromZoneProperty(inZone, "pause?", "none")
|
theSpawner.pauseFlag = inZone:getStringFromZoneProperty("pause?", "none")
|
||||||
theSpawner.lastPauseValue = cfxZones.getFlagValue(theSpawner.lastPauseValue, theSpawner) -- trigger.misc.getUserFlag(theSpawner.pauseFlag)
|
theSpawner.lastPauseValue = cfxZones.getFlagValue(theSpawner.lastPauseValue, theSpawner)
|
||||||
end
|
end
|
||||||
|
|
||||||
theSpawner.types = cfxZones.getStringFromZoneProperty(inZone, "types", "White_Tyre")
|
theSpawner.types = inZone:getStringFromZoneProperty("types", "White_Tyre")
|
||||||
local n = cfxZones.getNumberFromZoneProperty(inZone, "count", 1) -- DO NOT CONFUSE WITH OWN PROPERTY COUNT for unique names!!!
|
local n = inZone:getNumberFromZoneProperty("count", 1) -- DO NOT CONFUSE WITH OWN PROPERTY COUNT for unique names!!!
|
||||||
if n < 1 then n = 1 end -- sanity check.
|
if n < 1 then n = 1 end -- sanity check.
|
||||||
|
|
||||||
theSpawner.numObj = n
|
theSpawner.numObj = n
|
||||||
|
|
||||||
theSpawner.country = cfxZones.getNumberFromZoneProperty(inZone, "country", 2) -- coalition2county(theSpawner.owner)
|
theSpawner.country = inZone:getNumberFromZoneProperty("country", 2)
|
||||||
|
|
||||||
theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)
|
theSpawner.rawOwner = coalition.getCountryCoalition(theSpawner.country)
|
||||||
theSpawner.baseName = cfxZones.getStringFromZoneProperty(inZone, "baseName", dcsCommon.uuid("objSpwn"))
|
theSpawner.baseName = inZone:getStringFromZoneProperty("baseName", "*")
|
||||||
theSpawner.baseName = dcsCommon.trim(theSpawner.baseName)
|
theSpawner.baseName = dcsCommon.trim(theSpawner.baseName)
|
||||||
if theSpawner.baseName == "*" then
|
if theSpawner.baseName == "*" then
|
||||||
theSpawner.baseName = inZone.name -- convenience shortcut
|
theSpawner.baseName = inZone.name -- convenience shortcut
|
||||||
end
|
end
|
||||||
--cfxZones.getZoneProperty(inZone, "baseName")
|
--cfxZones.getZoneProperty(inZone, "baseName")
|
||||||
theSpawner.cooldown = cfxZones.getNumberFromZoneProperty(inZone, "cooldown", 60)
|
theSpawner.cooldown = inZone:getNumberFromZoneProperty("cooldown", 60)
|
||||||
theSpawner.lastSpawnTimeStamp = -10000 -- just init so it will always work
|
theSpawner.lastSpawnTimeStamp = -10000 -- just init so it will always work
|
||||||
theSpawner.autoRemove = cfxZones.getBoolFromZoneProperty(inZone, "autoRemove", false)
|
theSpawner.autoRemove = inZone:getBoolFromZoneProperty("autoRemove", false)
|
||||||
theSpawner.autoLink = cfxZones.getBoolFromZoneProperty(inZone, "autoLink", true)
|
theSpawner.autoLink = inZone:getBoolFromZoneProperty("autoLink", true)
|
||||||
|
|
||||||
theSpawner.heading = cfxZones.getNumberFromZoneProperty(inZone, "heading", 0)
|
theSpawner.heading = inZone:getNumberFromZoneProperty("heading", 0)
|
||||||
theSpawner.weight = cfxZones.getNumberFromZoneProperty(inZone, "weight", 0)
|
theSpawner.weight = inZone:getNumberFromZoneProperty("weight", 0)
|
||||||
if theSpawner.weight < 0 then theSpawner.weight = 0 end
|
if theSpawner.weight < 0 then theSpawner.weight = 0 end
|
||||||
|
|
||||||
theSpawner.isCargo = cfxZones.getBoolFromZoneProperty(inZone, "isCargo", false)
|
theSpawner.isCargo = inZone:getBoolFromZoneProperty("isCargo", false)
|
||||||
if theSpawner.isCargo == true and theSpawner.weight < 100 then theSpawner.weight = 100 end
|
if theSpawner.isCargo == true and theSpawner.weight < 100 then theSpawner.weight = 100 end
|
||||||
theSpawner.managed = cfxZones.getBoolFromZoneProperty(inZone, "managed", true) -- defaults to managed cargo
|
theSpawner.managed = inZone:getBoolFromZoneProperty("managed", true) -- defaults to managed cargo
|
||||||
|
|
||||||
theSpawner.cdTimer = 0 -- used for cooldown. if timer.getTime < this value, don't spawn
|
theSpawner.cdTimer = 0 -- used for cooldown. if timer.getTime < this value, don't spawn
|
||||||
-- theSpawner.cdStarted = false -- used to initiate cooldown when all items in theSpawns disappear
|
|
||||||
theSpawner.count = 1 -- used to create names, and count how many groups created
|
theSpawner.count = 1 -- used to create names, and count how many groups created
|
||||||
theSpawner.theSpawns = {} -- all items that are spawned. re-spawn happens if they are all out
|
theSpawner.theSpawns = {} -- all items that are spawned. re-spawn happens if they are all out
|
||||||
theSpawner.maxSpawns = cfxZones.getNumberFromZoneProperty(inZone, "maxSpawns", 1)
|
theSpawner.maxSpawns = inZone:getNumberFromZoneProperty("maxSpawns", 1)
|
||||||
theSpawner.paused = cfxZones.getBoolFromZoneProperty(inZone, "paused", false)
|
theSpawner.paused = inZone:getBoolFromZoneProperty("paused", false)
|
||||||
theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false)
|
theSpawner.requestable = inZone:getBoolFromZoneProperty("requestable", false)
|
||||||
if theSpawner.requestable then theSpawner.paused = true end
|
if theSpawner.requestable then theSpawner.paused = true end
|
||||||
|
|
||||||
-- see if the spawn can be made brittle/delicte
|
-- see if the spawn can be made brittle/delicte
|
||||||
if cfxZones.hasProperty(inZone, "useDelicates") then
|
if inZone:hasProperty("useDelicates") then
|
||||||
theSpawner.delicateName = dcsCommon.trim(cfxZones.getStringFromZoneProperty(inZone, "useDelicates", "<none>"))
|
theSpawner.delicateName = dcsCommon.trim(inZone:getStringFromZoneProperty("useDelicates", "<none>"))
|
||||||
if theSpawner.delicateName == "*" then theSpawner.delicateName = inZone.name end
|
if theSpawner.delicateName == "*" then theSpawner.delicateName = inZone.name end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -43,11 +43,7 @@ cfxOwnedZones.initialized = false
|
|||||||
owned zones is a module that manages conquerable zones and keeps a record
|
owned zones is a module that manages conquerable zones and keeps a record
|
||||||
of who owns the zone based on rules
|
of who owns the zone based on rules
|
||||||
|
|
||||||
|
*** EXTENTDS ZONES ***
|
||||||
*** EXTENTDS ZONES ***, so compatible with cfxZones, pilotSafe (limited airframes), may conflict with FARPZones
|
|
||||||
|
|
||||||
|
|
||||||
owned zones are identified by the 'owner' property. It can be initially set to nothing (default), NEUTRAL, RED or BLUE
|
|
||||||
|
|
||||||
when a zone changes hands, a callback can be installed to be told of that fact
|
when a zone changes hands, a callback can be installed to be told of that fact
|
||||||
callback has the format (zone, newOwner, formerOwner) with zone being the Zone, and new owner and former owners
|
callback has the format (zone, newOwner, formerOwner) with zone being the Zone, and new owner and former owners
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "4.0.7"
|
cfxZones.version = "4.0.9"
|
||||||
|
|
||||||
-- 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
|
||||||
@ -56,7 +56,15 @@ cfxZones.version = "4.0.7"
|
|||||||
- processDynamicValueVU
|
- processDynamicValueVU
|
||||||
- 4.0.6 - hash mark forgotten QoL
|
- 4.0.6 - hash mark forgotten QoL
|
||||||
- 4.0.7 - drawZone()
|
- 4.0.7 - drawZone()
|
||||||
|
- 4.0.8 - markZoneWithObjects()
|
||||||
|
- cleanup
|
||||||
|
- markCenterWithObject
|
||||||
|
- markPointWithObject
|
||||||
|
- 4.0.9 - createPolyZone now correctly returns new zone
|
||||||
|
- createSimplePolyZone correctly passes location to createPolyZone
|
||||||
|
- createPolyZone now correctly sets zone.point
|
||||||
|
- createPolyZone now correctly inits dcsOrigin
|
||||||
|
- createCircleZone noew correctly inits dcsOrigin
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -304,7 +312,7 @@ function cfxZones.calculateZoneBounds(theZone)
|
|||||||
-- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur
|
-- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur
|
||||||
-- store pRad
|
-- store pRad
|
||||||
theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it
|
theZone.pRad = pRad -- not sure we'll ever need that, but at least we have it
|
||||||
-- trigger.action.outText("+++Zones: poly zone <" .. theZone.name .. "> has pRad = " .. pRad, 30) -- remember to remove me
|
|
||||||
else
|
else
|
||||||
-- huston, we have a problem
|
-- huston, we have a problem
|
||||||
if cfxZones.verbose then
|
if cfxZones.verbose then
|
||||||
@ -319,28 +327,11 @@ function dmlZone:calculateZoneBounds()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.createPoint(x, y, z) -- bridge to dcsCommon, backward comp.
|
function cfxZones.createPoint(x, y, z) -- bridge to dcsCommon, backward comp.
|
||||||
return dcsCommon.createPoint(x, y, z)
|
return dcsCommon.createPoint(x, y, z)
|
||||||
--[[--
|
|
||||||
local newPoint = {}
|
|
||||||
newPoint.x = x
|
|
||||||
newPoint.y = y
|
|
||||||
newPoint.z = z
|
|
||||||
return newPoint --]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.copyPoint(inPoint) -- bridge to dcsCommon, backward comp.
|
function cfxZones.copyPoint(inPoint) -- bridge to dcsCommon, backward comp.
|
||||||
return dcsCommon.copyPoint(inPoint)
|
return dcsCommon.copyPoint(inPoint)
|
||||||
--[[--
|
|
||||||
local newPoint = {}
|
|
||||||
newPoint.x = inPoint.x
|
|
||||||
newPoint.y = inPoint.y
|
|
||||||
-- handle xz only
|
|
||||||
if inPoint.z then
|
|
||||||
newPoint.z = inPoint.z
|
|
||||||
else
|
|
||||||
newPoint.z = inPoint.y
|
|
||||||
end
|
|
||||||
return newPoint --]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.createHeightCorrectedPoint(inPoint) -- this should be in dcsCommon
|
function cfxZones.createHeightCorrectedPoint(inPoint) -- this should be in dcsCommon
|
||||||
@ -535,7 +526,8 @@ function cfxZones.createCircleZone(name, x, z, radius)
|
|||||||
newZone.name = name
|
newZone.name = name
|
||||||
newZone.radius = radius
|
newZone.radius = radius
|
||||||
newZone.point = dcsCommon.createPoint(x, 0, z)
|
newZone.point = dcsCommon.createPoint(x, 0, z)
|
||||||
|
newZone.dcsOrigin = dcsCommon.createPoint(x, 0, z)
|
||||||
|
|
||||||
-- props
|
-- props
|
||||||
newZone.properties = {}
|
newZone.properties = {}
|
||||||
|
|
||||||
@ -552,8 +544,9 @@ function cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
|||||||
end
|
end
|
||||||
if not location.x then location.x = 0 end
|
if not location.x then location.x = 0 end
|
||||||
if not location.z then location.z = 0 end
|
if not location.z then location.z = 0 end
|
||||||
|
if not location.y then location.y = 0 end
|
||||||
|
|
||||||
local newZone = cfxZones.createPolyZone(name, points)
|
local newZone = cfxZones.createPolyZone(name, points, location)
|
||||||
|
|
||||||
if addToManaged then
|
if addToManaged then
|
||||||
cfxZones.addZoneToManagedZones(newZone)
|
cfxZones.addZoneToManagedZones(newZone)
|
||||||
@ -593,8 +586,11 @@ function cfxZones.createSimpleQuadZone(name, location, points, addToManaged)
|
|||||||
return cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
return cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.createPolyZone(name, poly) -- poly must be array of point type
|
function cfxZones.createPolyZone(name, poly, location) -- poly must be array of point type
|
||||||
local newZone = dmlZone:new(nil) -- {} OOP compatibility
|
local newZone = dmlZone:new(nil) -- {} OOP compatibility
|
||||||
|
if not location then location = {x=0, y=0, z=0} end
|
||||||
|
newZone.point = dcsCommon.createPoint(location.x, 0, location.z)
|
||||||
|
newZone.dcsOrigin = dcsCommon.createPoint(location.x, 0, location.z)
|
||||||
newZone.isCircle = false
|
newZone.isCircle = false
|
||||||
newZone.isPoly = true
|
newZone.isPoly = true
|
||||||
newZone.poly = {}
|
newZone.poly = {}
|
||||||
@ -612,10 +608,9 @@ function cfxZones.createPolyZone(name, poly) -- poly must be array of point type
|
|||||||
newZone.properties = {}
|
newZone.properties = {}
|
||||||
|
|
||||||
cfxZones.calculateZoneBounds(newZone)
|
cfxZones.calculateZoneBounds(newZone)
|
||||||
|
return newZone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyInside)
|
function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyInside)
|
||||||
-- create a new circular zone with center placed inside inZone
|
-- create a new circular zone with center placed inside inZone
|
||||||
-- if entirelyInside is false, only the zone's center is guaranteed to be inside
|
-- if entirelyInside is false, only the zone's center is guaranteed to be inside
|
||||||
@ -641,23 +636,6 @@ function cfxZones.createRandomZoneInZone(name, inZone, targetRadius, entirelyIns
|
|||||||
return newZone
|
return newZone
|
||||||
|
|
||||||
elseif inZone.isPoly then
|
elseif inZone.isPoly then
|
||||||
-- we have a poly zone. the way we do this is simple:
|
|
||||||
-- generate random x, z with ranges of the bounding box
|
|
||||||
-- until the point falls within the polygon.
|
|
||||||
--[[ replaced by new code
|
|
||||||
|
|
||||||
local newPoint = {}
|
|
||||||
local emergencyBrake = 0
|
|
||||||
repeat
|
|
||||||
newPoint = cfxZones.createRandomPointInsideBounds(inZone.bounds)
|
|
||||||
emergencyBrake = emergencyBrake + 1
|
|
||||||
if (emergencyBrake > 100) then
|
|
||||||
newPoint = cfxZones.copyPoint(inZone.Point)
|
|
||||||
trigger.action.outText("CreateZoneInZone: emergency brake for inZone" .. inZone.name, 10)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
until cfxZones.isPointInsidePoly(newPoint, inZone.poly)
|
|
||||||
--]]--
|
|
||||||
local newPoint = cfxZones.createRandomPointInPolyZone(inZone)
|
local newPoint = cfxZones.createRandomPointInPolyZone(inZone)
|
||||||
-- construct new zone
|
-- construct new zone
|
||||||
local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius)
|
local newZone = cfxZones.createCircleZone(name, newPoint.x, newPoint.z, targetRadius)
|
||||||
@ -833,7 +811,6 @@ function cfxZones.getZoneVolume(theZone)
|
|||||||
}
|
}
|
||||||
return vol
|
return vol
|
||||||
elseif (theZone.isPoly) then
|
elseif (theZone.isPoly) then
|
||||||
--trigger.action.outText("zne: isPointInside: " .. theZone.name .. " is Polyzone!", 30)
|
|
||||||
-- build the box volume, using the zone's bounds ll and ur points
|
-- build the box volume, using the zone's bounds ll and ur points
|
||||||
local lowerLeft = {}
|
local lowerLeft = {}
|
||||||
-- we build x = westerm y = southern, Z = alt
|
-- we build x = westerm y = southern, Z = alt
|
||||||
@ -870,17 +847,11 @@ end
|
|||||||
function cfxZones.declutterZone(theZone)
|
function cfxZones.declutterZone(theZone)
|
||||||
if not theZone then return end
|
if not theZone then return end
|
||||||
local theVol = cfxZones.getZoneVolume(theZone)
|
local theVol = cfxZones.getZoneVolume(theZone)
|
||||||
-- if theZone.verbose then
|
|
||||||
-- dcsCommon.dumpVar2Str("vol", theVol)
|
|
||||||
-- end
|
|
||||||
world.removeJunk(theVol)
|
world.removeJunk(theVol)
|
||||||
end
|
end
|
||||||
|
|
||||||
function dmlZone:declutterZone()
|
function dmlZone:declutterZone()
|
||||||
local theVol = cfxZones.getZoneVolume(self)
|
local theVol = cfxZones.getZoneVolume(self)
|
||||||
-- if self.verbose then
|
|
||||||
-- dcsCommon.dumpVar2Str("vol", theVol)
|
|
||||||
-- end
|
|
||||||
world.removeJunk(theVol)
|
world.removeJunk(theVol)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -888,8 +859,7 @@ end
|
|||||||
-- units / groups in zone
|
-- units / groups in zone
|
||||||
--
|
--
|
||||||
function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be code
|
function cfxZones.allGroupsInZone(theZone, categ) -- categ is optional, must be code
|
||||||
-- warning: does not check for exiting!
|
-- warning: does not check for existing!
|
||||||
--trigger.action.outText("Zone " .. theZone.name .. " radius " .. theZone.radius, 30)
|
|
||||||
local inZones = {}
|
local inZones = {}
|
||||||
local coals = {0, 1, 2} -- all coalitions
|
local coals = {0, 1, 2} -- all coalitions
|
||||||
for idx, coa in pairs(coals) do
|
for idx, coa in pairs(coals) do
|
||||||
@ -908,8 +878,7 @@ function dmlZone:allGroupsInZone(categ)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must be code
|
function cfxZones.allGroupNamesInZone(theZone, categ) -- categ is optional, must be code
|
||||||
-- warning: does not check for exiting!
|
-- warning: does not check for existing!
|
||||||
--trigger.action.outText("Zone " .. theZone.name .. " radius " .. theZone.radius, 30)
|
|
||||||
local inZones = {}
|
local inZones = {}
|
||||||
local coals = {0, 1, 2} -- all coalitions
|
local coals = {0, 1, 2} -- all coalitions
|
||||||
for idx, coa in pairs(coals) do
|
for idx, coa in pairs(coals) do
|
||||||
@ -928,7 +897,7 @@ function dmlZone:allGroupNamesInZone(categ)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, must be code
|
function cfxZones.allStaticsInZone(theZone, useOrigin) -- categ is optional, must be code
|
||||||
-- warning: does not check for exiting!
|
-- warning: does not check for existing!
|
||||||
local inZones = {}
|
local inZones = {}
|
||||||
local coals = {0, 1, 2} -- all coalitions
|
local coals = {0, 1, 2} -- all coalitions
|
||||||
for idx, coa in pairs(coals) do
|
for idx, coa in pairs(coals) do
|
||||||
@ -976,11 +945,9 @@ function cfxZones.isGroupPartiallyInZone(aGroup, aZone)
|
|||||||
if aUnit:isExist() and aUnit:getLife() > 1 then
|
if aUnit:isExist() and aUnit:getLife() > 1 then
|
||||||
local p = aUnit:getPoint()
|
local p = aUnit:getPoint()
|
||||||
local inzone, percent, dist = cfxZones.pointInZone(p, aZone)
|
local inzone, percent, dist = cfxZones.pointInZone(p, aZone)
|
||||||
if inzone then -- cfxZones.isPointInsideZone(p, aZone) then
|
if inzone then
|
||||||
--trigger.action.outText("zne: YAY <" .. aUnit:getName() .. "> IS IN " .. aZone.name, 30)
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
--trigger.action.outText("zne: <" .. aUnit:getName() .. "> not in " .. aZone.name .. ", dist = " .. dist .. ", rad = ", aZone.radius, 30)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -1073,11 +1040,7 @@ function cfxZones.dumpZones(zoneTable)
|
|||||||
end
|
end
|
||||||
trigger.action.outText("Zones end", 10)
|
trigger.action.outText("Zones end", 10)
|
||||||
end
|
end
|
||||||
--[[-- moved to dcsCommon
|
|
||||||
function cfxZones.stringStartsWith(theString, thePrefix)
|
|
||||||
return theString:find(thePrefix) == 1
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
function cfxZones.keysForTable(theTable)
|
function cfxZones.keysForTable(theTable)
|
||||||
local keyset={}
|
local keyset={}
|
||||||
local n=0
|
local n=0
|
||||||
@ -1113,17 +1076,12 @@ end
|
|||||||
-- return all zones from the zone table that begin with string prefix
|
-- return all zones from the zone table that begin with string prefix
|
||||||
--
|
--
|
||||||
function cfxZones.zonesStartingWithName(prefix, searchSet)
|
function cfxZones.zonesStartingWithName(prefix, searchSet)
|
||||||
|
|
||||||
if not searchSet then searchSet = cfxZones.zones end
|
if not searchSet then searchSet = cfxZones.zones end
|
||||||
|
|
||||||
-- trigger.action.outText("Enter: zonesStartingWithName for " .. prefix , 30)
|
|
||||||
local prefixZones = {}
|
local prefixZones = {}
|
||||||
prefix = prefix:upper() -- all zones have UPPERCASE NAMES! THEY SCREAM AT YOU
|
prefix = prefix:upper() -- all zones have UPPERCASE NAMES! THEY SCREAM AT YOU
|
||||||
for name, zone in pairs(searchSet) do
|
for name, zone in pairs(searchSet) do
|
||||||
-- trigger.action.outText("testing " .. name:upper() .. " starts with " .. prefix , 30)
|
|
||||||
if dcsCommon.stringStartsWith(name:upper(), prefix) then
|
if dcsCommon.stringStartsWith(name:upper(), prefix) then
|
||||||
prefixZones[name] = zone -- note: ref copy!
|
prefixZones[name] = zone -- note: ref copy!
|
||||||
--trigger.action.outText("zone with prefix <" .. prefix .. "> found: " .. name, 10)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1396,19 +1354,6 @@ function dmlZone:closestUnitToZoneCenter(theUnits)
|
|||||||
return cfxZones.closestUnitToZoneCenter(theUnits, self)
|
return cfxZones.closestUnitToZoneCenter(theUnits, self)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
|
||||||
function cfxZones.anyPlayerInZone(theZone) -- returns first player it finds
|
|
||||||
for pname, pinfo in pairs(cfxPlayer.playerDB) do
|
|
||||||
local playerUnit = pinfo.unit
|
|
||||||
if (cfxZones.unitInZone(playerUnit, theZone)) then
|
|
||||||
return true, playerUnit
|
|
||||||
end
|
|
||||||
end -- for all players
|
|
||||||
return false, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
-- grow zone
|
-- grow zone
|
||||||
function cfxZones.growZone()
|
function cfxZones.growZone()
|
||||||
-- circular zones simply increase radius
|
-- circular zones simply increase radius
|
||||||
@ -1776,14 +1721,6 @@ end
|
|||||||
function dmlZone:getFlagValue(theFlag)
|
function dmlZone:getFlagValue(theFlag)
|
||||||
return cfxZones.getFlagValue(theFlag, self)
|
return cfxZones.getFlagValue(theFlag, self)
|
||||||
end
|
end
|
||||||
--[[--
|
|
||||||
function cfxZones.isMEFlag(inFlag)
|
|
||||||
-- do NOT use me
|
|
||||||
trigger.action.outText("+++zne: warning: deprecated isMEFlag", 30)
|
|
||||||
return true
|
|
||||||
-- returns true if inFlag is a pure positive number
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
function cfxZones.verifyMethod(theMethod, theZone)
|
function cfxZones.verifyMethod(theMethod, theZone)
|
||||||
local lMethod = string.lower(theMethod)
|
local lMethod = string.lower(theMethod)
|
||||||
@ -2082,7 +2019,6 @@ function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName)
|
|||||||
return false, currVal
|
return false, currVal
|
||||||
end
|
end
|
||||||
|
|
||||||
--trigger.action.outText("+++Zne: about to test: c = " .. currVal .. ", l = " .. lastVal, 30)
|
|
||||||
local testResult = cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)
|
local testResult = cfxZones.testFlagByMethodForZone(currVal, lastVal, theMethod, theZone)
|
||||||
|
|
||||||
-- update latch by method
|
-- update latch by method
|
||||||
@ -2197,12 +2133,10 @@ end
|
|||||||
function cfxZones.getZoneProperty(cZone, theKey)
|
function cfxZones.getZoneProperty(cZone, theKey)
|
||||||
if not cZone then
|
if not cZone then
|
||||||
trigger.action.outText("+++zone: no zone in getZoneProperty", 30)
|
trigger.action.outText("+++zone: no zone in getZoneProperty", 30)
|
||||||
-- breek.here.noew = 1
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if not theKey then
|
if not theKey then
|
||||||
trigger.action.outText("+++zone: no property key in getZoneProperty for zone " .. cZone.name, 30)
|
trigger.action.outText("+++zone: no property key in getZoneProperty for zone " .. cZone.name, 30)
|
||||||
-- breakme.here = 1
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2224,7 +2158,6 @@ end
|
|||||||
|
|
||||||
function cfxZones.getStringFromZoneProperty(theZone, theProperty, default)
|
function cfxZones.getStringFromZoneProperty(theZone, theProperty, default)
|
||||||
if not default then default = "" end
|
if not default then default = "" end
|
||||||
-- local p = cfxZones.getZoneProperty(theZone, theProperty)
|
|
||||||
-- OOP heavy duty test here
|
-- OOP heavy duty test here
|
||||||
local p = theZone:getZoneProperty(theProperty)
|
local p = theZone:getZoneProperty(theProperty)
|
||||||
if not p then return default end
|
if not p then return default end
|
||||||
@ -2378,7 +2311,6 @@ function cfxZones.hasProperty(theZone, theProperty)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
-- return foundIt ~= nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function dmlZone:hasProperty(theProperty)
|
function dmlZone:hasProperty(theProperty)
|
||||||
@ -2539,30 +2471,22 @@ function dmlZone:getCoalitionFromZoneProperty(theProperty, default)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxZones.getNumberFromZoneProperty(theZone, theProperty, default)
|
function cfxZones.getNumberFromZoneProperty(theZone, theProperty, default)
|
||||||
--TODO: trim string
|
|
||||||
if not default then default = 0 end
|
if not default then default = 0 end
|
||||||
default = tonumber(default)
|
default = tonumber(default)
|
||||||
if not default then default = 0 end -- enforce default numbner as well
|
if not default then default = 0 end -- enforce default numbner as well
|
||||||
local p = cfxZones.getZoneProperty(theZone, theProperty)
|
local p = cfxZones.getZoneProperty(theZone, theProperty)
|
||||||
p = tonumber(p)
|
p = tonumber(p)
|
||||||
if not p then p = default end
|
if not p then p = default end
|
||||||
-- if theZone.verbose then
|
|
||||||
-- trigger.action.outText("+++zne: getNumberFromZoneProperty returns <" .. p .. "> for prop <" .. theProperty .. "> in zone <" .. theZone.name .. ">", 30)
|
|
||||||
-- end
|
|
||||||
return p
|
return p
|
||||||
end
|
end
|
||||||
|
|
||||||
function dmlZone:getNumberFromZoneProperty(theProperty, default)
|
function dmlZone:getNumberFromZoneProperty(theProperty, default)
|
||||||
--TODO: trim string
|
|
||||||
if not default then default = 0 end
|
if not default then default = 0 end
|
||||||
default = tonumber(default)
|
default = tonumber(default)
|
||||||
if not default then default = 0 end -- enforce default numbner as well
|
if not default then default = 0 end -- enforce default numbner as well
|
||||||
local p = self:getZoneProperty(theProperty)
|
local p = self:getZoneProperty(theProperty)
|
||||||
p = tonumber(p)
|
p = tonumber(p)
|
||||||
if not p then p = default end
|
if not p then p = default end
|
||||||
-- if self.verbose then
|
|
||||||
-- trigger.action.outText("+++zne OOP: getNumberFromZoneProperty returns <" .. p .. "> for prop <" .. theProperty .. "> in zone <" .. self.name .. ">", 30)
|
|
||||||
-- end
|
|
||||||
return p
|
return p
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2883,7 +2807,6 @@ function cfxZones.processDynamicValues(inMsg, theZone, msgResponses)
|
|||||||
repeat -- iterate all patterns one by one
|
repeat -- iterate all patterns one by one
|
||||||
local startLoc, endLoc = string.find(outMsg, pattern)
|
local startLoc, endLoc = string.find(outMsg, pattern)
|
||||||
if startLoc then
|
if startLoc then
|
||||||
--trigger.action.outText("response: found an occurence", 30)
|
|
||||||
local theValParam = string.sub(outMsg, startLoc, endLoc)
|
local theValParam = string.sub(outMsg, startLoc, endLoc)
|
||||||
-- strip lead and trailer
|
-- strip lead and trailer
|
||||||
local param = string.gsub(theValParam, "<rsp:%s*", "")
|
local param = string.gsub(theValParam, "<rsp:%s*", "")
|
||||||
@ -3131,7 +3054,6 @@ function cfxZones.processDynamicAB(inMsg, locale)
|
|||||||
left = dcsCommon.trim(left)
|
left = dcsCommon.trim(left)
|
||||||
right = string.sub(rp, rightA+1, rightB-1)
|
right = string.sub(rp, rightA+1, rightB-1)
|
||||||
right = dcsCommon.trim(right)
|
right = dcsCommon.trim(right)
|
||||||
-- trigger.action.outText("+++replacer pattern <" .. rp .. "> found, val = <" .. val .. ">, A = <" .. left .. ">, B = <" .. right .. ">", 30)
|
|
||||||
local yesno = false
|
local yesno = false
|
||||||
-- see if unit exists
|
-- see if unit exists
|
||||||
local theUnit = Unit.getByName(val)
|
local theUnit = Unit.getByName(val)
|
||||||
@ -3336,7 +3258,6 @@ function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really
|
|||||||
-- direction to zone
|
-- direction to zone
|
||||||
theZone.uHdg = unitHeading -- original unit heading to turn other
|
theZone.uHdg = unitHeading -- original unit heading to turn other
|
||||||
-- units if need be
|
-- units if need be
|
||||||
--trigger.action.outText("Link setup: dx=<" .. dx .. ">, dy=<" .. dy .. "> unit original hdg = <" .. math.floor(57.2958 * unitHeading) .. ">", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function dmlZone:linkUnitToZone(theUnit, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
function dmlZone:linkUnitToZone(theUnit, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
||||||
@ -3386,8 +3307,6 @@ function cfxZones.calcHeadingOffset(aZone, theUnit)
|
|||||||
-- in DCS, positive x is north (wtf?) and positive z is east
|
-- in DCS, positive x is north (wtf?) and positive z is east
|
||||||
local dy = (-aZone.rxy) * math.sin(zoneBearing)
|
local dy = (-aZone.rxy) * math.sin(zoneBearing)
|
||||||
local dx = aZone.rxy * math.cos(zoneBearing)
|
local dx = aZone.rxy * math.cos(zoneBearing)
|
||||||
|
|
||||||
--trigger.action.outText("zone bearing is " .. math.floor(zoneBearing * 57.2958) .. " dx = <" .. dx .. "> , dy = <" .. dy .. ">", 30)
|
|
||||||
return dx, -dy -- note: dy is z coord!!!!
|
return dx, -dy -- note: dy is z coord!!!!
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3542,8 +3461,101 @@ function cfxZones.startMovingZones()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- marking zones
|
||||||
|
--
|
||||||
|
|
||||||
|
function cfxZones.spreadNObjectsOverLine(theZone, n, objType, left, right, cty) -- leaves last position free
|
||||||
|
trigger.action.outText("left = " .. dcsCommon.point2text(left) .. ", right = " .. dcsCommon.point2text(right),30)
|
||||||
|
|
||||||
|
local a = {x=left.x, y=left.z}
|
||||||
|
local b = {x=right.x, y=right.z}
|
||||||
|
local dir = dcsCommon.vSub(b,a) -- vector from left to right
|
||||||
|
local dirInc = dcsCommon.vMultScalar(dir, 1/n)
|
||||||
|
local count = 0
|
||||||
|
local p = {x=left.x, y = left.z}
|
||||||
|
local baseName = dcsCommon.uuid(theZone.name)
|
||||||
|
while count < n do
|
||||||
|
local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)
|
||||||
|
dcsCommon.moveStaticDataTo(theStaticData, p.x, p.y)
|
||||||
|
local theObject = coalition.addStaticObject(cty, theStaticData)
|
||||||
|
p = dcsCommon.vAdd(p, dirInc)
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.markZoneWithObjects(theZone, objType, qtrNum, markCenter, cty) -- returns set
|
||||||
|
if not objType then objType = "Black_Tyre_RF" end
|
||||||
|
if not qtrNum then qtrNum = 3 end -- +1 for number of marks per quarter
|
||||||
|
if not cty then cty = dcsCommon.getACountryForCoalition(0) end -- some neutral county
|
||||||
|
local p = theZone:getPoint()
|
||||||
|
local newObjects = {}
|
||||||
|
|
||||||
|
if theZone.isPoly then
|
||||||
|
-- we place 4 * (qtrnum + 1) objects around the edge of the zone
|
||||||
|
-- we mark each poly along v-->v+1, placing ip and qtrNum additional points
|
||||||
|
local o = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[1], theZone.poly[2], cty)
|
||||||
|
local p = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[2], theZone.poly[3], cty)
|
||||||
|
local q = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[3], theZone.poly[4], cty)
|
||||||
|
local r = cfxZones.spreadNObjectsOverLine(theZone, qtrNum + 1, objType, theZone.poly[4], theZone.poly[1], cty)
|
||||||
|
o = dcsCommon.combineTables(o,p)
|
||||||
|
p = dcsCommon.combineTables(q,r)
|
||||||
|
newObjects = dcsCommon.combineTables(o,p)
|
||||||
|
|
||||||
|
else
|
||||||
|
local numObjects = (qtrNum + 1) * 4
|
||||||
|
local degrees = 3.14157 / 180
|
||||||
|
local degreeIncrement = (360 / numObjects) * degrees
|
||||||
|
local currDegree = 0
|
||||||
|
local radius = theZone.radius
|
||||||
|
for i=1, numObjects do
|
||||||
|
local ox = p.x + math.cos(currDegree) * radius
|
||||||
|
local oy = p.z + math.sin(currDegree) * radius -- note: z!
|
||||||
|
local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), objType)
|
||||||
|
dcsCommon.moveStaticDataTo(theStaticData, ox, oy)
|
||||||
|
local theObject = coalition.addStaticObject(cty, theStaticData)
|
||||||
|
table.insert(newObjects, theObject)
|
||||||
|
currDegree = currDegree + degreeIncrement
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if markCenter then
|
||||||
|
-- also mark the center
|
||||||
|
local theObject = cfxZones.markPointWithObject(p, objType, cty)
|
||||||
|
table.insert(newObjects, theObject)
|
||||||
|
end
|
||||||
|
|
||||||
|
return newObjects
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:markZoneWithObjects(objType, qtrNum, markCenter, cty) -- returns set
|
||||||
|
return cfxZones.markZoneWithObjects(self, objType, qtrNum, markCenter)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.markCenterWithObject(theZone, objType, cty) -- returns object
|
||||||
|
local p = cfxZones.getPoint(theZone)
|
||||||
|
local theObject = cfxZones.markPointWithObject(theZone, p, objType, cty)
|
||||||
|
return theObject
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:markCenterWithObject(objType, cty) -- returns object
|
||||||
|
return cfxZones.markCenterWithObject(self, objType, cty)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.markPointWithObject(theZone, p, theType, cty) -- returns object
|
||||||
|
if not cty then cty = dcsCommon.getACountryForCoalition(0) end
|
||||||
|
local ox = p.x
|
||||||
|
local oy = p.y
|
||||||
|
if p.z then oy = p.z end -- support vec 2 and vec 3
|
||||||
|
local theStaticData = dcsCommon.createStaticObjectData(dcsCommon.uuid(theZone.name), theType)
|
||||||
|
dcsCommon.moveStaticDataTo(theStaticData, ox, oy)
|
||||||
|
local theObject = coalition.addStaticObject(cty, theStaticData)
|
||||||
|
return theObject
|
||||||
|
end
|
||||||
|
|
||||||
|
function dmlZone:markPointWithObject(p, theType, cty) -- returns object
|
||||||
|
return cfxZones.markPointWithObject(self, p, theType, cty)
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- ===========
|
-- ===========
|
||||||
-- INIT MODULE
|
-- INIT MODULE
|
||||||
|
|||||||
@ -98,10 +98,10 @@ function civAir.readConfigZone()
|
|||||||
civAir.aircraftTypes = typeArray
|
civAir.aircraftTypes = typeArray
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("ups") then
|
-- if theZone:hasProperty("ups") then
|
||||||
civAir.ups = theZone:getNumberFromZoneProperty("ups", 0.05)
|
civAir.ups = theZone:getNumberFromZoneProperty("ups", 0.05)
|
||||||
if civAir.ups < .0001 then civAir.ups = 0.05 end
|
if civAir.ups < .0001 then civAir.ups = 0.05 end
|
||||||
end
|
-- end
|
||||||
|
|
||||||
if theZone:hasProperty("maxTraffic") then
|
if theZone:hasProperty("maxTraffic") then
|
||||||
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxTraffic", 10)
|
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxTraffic", 10)
|
||||||
@ -109,15 +109,15 @@ function civAir.readConfigZone()
|
|||||||
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxFlights", 10)
|
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxFlights", 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone:hasProperty("maxIdle") then
|
-- if theZone:hasProperty("maxIdle") then
|
||||||
civAir.maxIdle = theZone:getNumberFromZoneProperty("maxIdle", 8 * 60)
|
civAir.maxIdle = theZone:getNumberFromZoneProperty("maxIdle", 8 * 60)
|
||||||
end
|
-- end
|
||||||
|
|
||||||
if theZone:hasProperty("initialAirSpawns") then
|
-- if theZone:hasProperty("initialAirSpawns") then
|
||||||
civAir.initialAirSpawns = theZone:getBoolFromZoneProperty( "initialAirSpawns", true)
|
civAir.initialAirSpawns = theZone:getBoolFromZoneProperty( "initialAirSpawns", true)
|
||||||
end
|
-- end
|
||||||
|
|
||||||
civAir.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
civAir.verbose = theZone.verbose
|
||||||
end
|
end
|
||||||
|
|
||||||
function civAir.processZone(theZone)
|
function civAir.processZone(theZone)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
csarManager = {}
|
csarManager = {}
|
||||||
csarManager.version = "2.2.6"
|
csarManager.version = "2.3.1"
|
||||||
csarManager.verbose = false
|
csarManager.verbose = false
|
||||||
csarManager.ups = 1
|
csarManager.ups = 1
|
||||||
|
|
||||||
@ -65,6 +65,14 @@ csarManager.ups = 1
|
|||||||
- 2.2.5 - manual freq for CSAR was off by a factor of 10 - Corrected
|
- 2.2.5 - manual freq for CSAR was off by a factor of 10 - Corrected
|
||||||
- 2.2.6 - useFlare, now also launches a flare in addition to smoke
|
- 2.2.6 - useFlare, now also launches a flare in addition to smoke
|
||||||
- zone testing uses getPoint for zones, supports moving csar zones
|
- zone testing uses getPoint for zones, supports moving csar zones
|
||||||
|
- 2.3.0 - dmlZones
|
||||||
|
- onRoad attribute for CSAR mission Zones
|
||||||
|
- rndLoc support
|
||||||
|
- triggerMethod support
|
||||||
|
- 2.3.1 - addPrefix option
|
||||||
|
- delay asynch OK (message only)
|
||||||
|
- offset zone on randomized soldier
|
||||||
|
- smokeDist
|
||||||
|
|
||||||
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
|
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
|
||||||
|
|
||||||
@ -133,6 +141,9 @@ function csarManager.createDownedPilot(theMission, existingUnit)
|
|||||||
-- a radius of <1, this is only for ejecting pilots
|
-- a radius of <1, this is only for ejecting pilots
|
||||||
if newTargetZone.radius > 1 then
|
if newTargetZone.radius > 1 then
|
||||||
aLocation, aHeading = dcsCommon.randomPointOnPerimeter(newTargetZone.radius / 2 + 3, newTargetZone.point.x, newTargetZone.point.z)
|
aLocation, aHeading = dcsCommon.randomPointOnPerimeter(newTargetZone.radius / 2 + 3, newTargetZone.point.x, newTargetZone.point.z)
|
||||||
|
-- we now move the entire zone so it centers on unit
|
||||||
|
newTargetZone.point.x = aLocation.x
|
||||||
|
newTargetZone.point.z = aLocation.z
|
||||||
else
|
else
|
||||||
aLocation.x = newTargetZone.point.x
|
aLocation.x = newTargetZone.point.x
|
||||||
aLocation.z = newTargetZone.point.z
|
aLocation.z = newTargetZone.point.z
|
||||||
@ -188,8 +199,10 @@ function csarManager.createCSARMissionData(point, theSide, freq, name, numCrew,
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not inRadius then inRadius = csarManager.rescueRadius end
|
if not inRadius then inRadius = csarManager.rescueRadius end
|
||||||
|
newMission.name = name .. "-" .. csarManager.missionID -- make it uuid-capable
|
||||||
newMission.name = "downed " .. name .. "-" .. csarManager.missionID -- make it uuid-capable
|
if csarManager.addPrefix then
|
||||||
|
newMission.name = "downed " .. newMission.name
|
||||||
|
end
|
||||||
newMission.zone = cfxZones.createSimpleZone(newMission.name, point, inRadius) --csarManager.rescueRadius)
|
newMission.zone = cfxZones.createSimpleZone(newMission.name, point, inRadius) --csarManager.rescueRadius)
|
||||||
newMission.marker = mapMarker -- so it can be removed later
|
newMission.marker = mapMarker -- so it can be removed later
|
||||||
newMission.isHot = false -- creating adversaries will make it hot, or when units are near. maybe implement a search later?
|
newMission.isHot = false -- creating adversaries will make it hot, or when units are near. maybe implement a search later?
|
||||||
@ -468,15 +481,11 @@ function csarManager.heloLanded(theUnit)
|
|||||||
"Evacuees",
|
"Evacuees",
|
||||||
theMassObject)
|
theMassObject)
|
||||||
msn = theMassObject.ref
|
msn = theMassObject.ref
|
||||||
-- csarManager.successMission(myName, base.name, msn)
|
|
||||||
-- to be done when we remove troopsOnBoard
|
|
||||||
end
|
end
|
||||||
-- reset weight
|
-- reset weight
|
||||||
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
||||||
trigger.action.setUnitInternalCargo(myName, totalMass) -- super recalcs
|
trigger.action.setUnitInternalCargo(myName, totalMass) -- super recalcs
|
||||||
-- trigger.action.outText("+++csar: delivered - set internal weight for " .. myName .. " to " .. totalMass, 30)
|
|
||||||
|
|
||||||
-- trigger.action.setUnitInternalCargo(myName, 10) -- 10 kg as empty
|
|
||||||
conf.troopsOnBoard = {} -- empty out troops on board
|
conf.troopsOnBoard = {} -- empty out troops on board
|
||||||
-- we do *not* return so we can pick up troops on
|
-- we do *not* return so we can pick up troops on
|
||||||
-- a CSARBASE if they were dropped there
|
-- a CSARBASE if they were dropped there
|
||||||
@ -521,6 +530,12 @@ function csarManager.heloLanded(theUnit)
|
|||||||
30)
|
30)
|
||||||
didPickup = true;
|
didPickup = true;
|
||||||
|
|
||||||
|
local args = {}
|
||||||
|
args.theName = theMission.name
|
||||||
|
args.mySide = mySide
|
||||||
|
args.unitName = myName
|
||||||
|
timer.scheduleFunction(csarManager.asynchSuccess, args, timer.getTime() + 3)
|
||||||
|
|
||||||
csarManager.removeMission(theMission)
|
csarManager.removeMission(theMission)
|
||||||
table.insert(conf.troopsOnBoard, theMission)
|
table.insert(conf.troopsOnBoard, theMission)
|
||||||
theMission.group:destroy() -- will shut up radio as well
|
theMission.group:destroy() -- will shut up radio as well
|
||||||
@ -536,16 +551,24 @@ function csarManager.heloLanded(theUnit)
|
|||||||
theMassObject)
|
theMassObject)
|
||||||
end
|
end
|
||||||
if didPickup then
|
if didPickup then
|
||||||
trigger.action.outSoundForCoalition(mySide, csarManager.actionSound) -- "Quest Snare 3.wav")
|
local args = {}
|
||||||
|
args.mySide = mySide
|
||||||
|
timer.scheduleFunction(csarManager.asynchSound, args, timer.getTime() + 3)
|
||||||
end
|
end
|
||||||
-- reset unit's weight based on people on board
|
-- reset unit's weight based on people on board
|
||||||
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
local totalMass = cargoSuper.calculateTotalMassFor(myName)
|
||||||
-- WAS: trigger.action.setUnitInternalCargo(myName, 10 + #conf.troopsOnBoard * csarManager.pilotWeight) -- 10 kg as empty + per-unit time people
|
|
||||||
trigger.action.setUnitInternalCargo(myName, totalMass) -- 10 kg as empty + per-unit time people
|
trigger.action.setUnitInternalCargo(myName, totalMass) -- 10 kg as empty + per-unit time people
|
||||||
-- trigger.action.outText("+++csar: set internal weight for " .. myName .. " to " .. totalMass, 30)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function csarManager.asynchSuccess(args)
|
||||||
|
-- currently, we always say "OK", will check for fail later
|
||||||
|
trigger.action.outTextForCoalition(args.mySide, args.unitName .. " has loaded " .. args.theName .. "!", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
function csarManager.asynchSound(args)
|
||||||
|
trigger.action.outSoundForCoalition(args.mySide, csarManager.actionSound)
|
||||||
|
end
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- Helo took off
|
-- Helo took off
|
||||||
@ -747,7 +770,6 @@ function csarManager.doListCSARRequests(args)
|
|||||||
local point = theUnit:getPoint()
|
local point = theUnit:getPoint()
|
||||||
local theSide = theUnit:getCoalition()
|
local theSide = theUnit:getCoalition()
|
||||||
|
|
||||||
--trigger.action.outText("+++csar: ".. theUnit:getName() .." issued csar status request", 30)
|
|
||||||
local report = "\nCrews requesting evacuation\n"
|
local report = "\nCrews requesting evacuation\n"
|
||||||
local openMissions = csarManager.openMissionsForSide(theSide)
|
local openMissions = csarManager.openMissionsForSide(theSide)
|
||||||
|
|
||||||
@ -772,11 +794,10 @@ function csarManager.doListCSARRequests(args)
|
|||||||
if #myBases < 1 then
|
if #myBases < 1 then
|
||||||
report = report .. "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES TO"
|
report = report .. "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES TO"
|
||||||
end
|
end
|
||||||
|
|
||||||
report = report .. "\n"
|
report = report .. "\n"
|
||||||
|
|
||||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
function csarManager.redirectStatusCarrying(args)
|
function csarManager.redirectStatusCarrying(args)
|
||||||
@ -788,8 +809,6 @@ function csarManager.doStatusCarrying(args)
|
|||||||
local param = args[2]
|
local param = args[2]
|
||||||
local theUnit = conf.unit
|
local theUnit = conf.unit
|
||||||
|
|
||||||
--trigger.action.outText("+++csar: ".. theUnit:getName() .." wants to know how their rescued troops are doing", 30)
|
|
||||||
|
|
||||||
-- build status report
|
-- build status report
|
||||||
local report = "\nCrew Rescue Status:\n"
|
local report = "\nCrew Rescue Status:\n"
|
||||||
if #conf.troopsOnBoard < 1 then
|
if #conf.troopsOnBoard < 1 then
|
||||||
@ -809,7 +828,7 @@ function csarManager.doStatusCarrying(args)
|
|||||||
report = report .. "\n"
|
report = report .. "\n"
|
||||||
|
|
||||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||||
end
|
end
|
||||||
|
|
||||||
function csarManager.redirectUnloadOne(args)
|
function csarManager.redirectUnloadOne(args)
|
||||||
@ -825,7 +844,7 @@ function csarManager.unloadOne(args)
|
|||||||
local report = "NYI: unload one"
|
local report = "NYI: unload one"
|
||||||
|
|
||||||
if theUnit:inAir() then
|
if theUnit:inAir() then
|
||||||
report = "STRONGLY recommend we land first, Sir!"
|
report = "STRONGLY recommend we land first, sir!"
|
||||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||||
return
|
return
|
||||||
@ -961,22 +980,16 @@ end
|
|||||||
function csarManager.addCSARBase(aZone)
|
function csarManager.addCSARBase(aZone)
|
||||||
local csarBase = {}
|
local csarBase = {}
|
||||||
csarBase.zone = aZone
|
csarBase.zone = aZone
|
||||||
|
-- CSARBASE carries the coalition in the CSARBASE attribute
|
||||||
--[[--
|
csarBase.side = aZone:getCoalitionFromZoneProperty("CSARBASE", 0)
|
||||||
local bName = cfxZones.getStringFromZoneProperty(aZone, "CSARBASE", "XXX")
|
|
||||||
if bName == "XXX" then bName = aZone.name end
|
|
||||||
csarBase.name = cfxZones.getStringFromZoneProperty(aZone, "name", bName)
|
|
||||||
--]]--
|
|
||||||
-- CSARBASE now carries the coalition in the CSARBASE attribute
|
|
||||||
csarBase.side = cfxZones.getCoalitionFromZoneProperty(aZone, "CSARBASE", 0)
|
|
||||||
-- backward-compatibility to older versions.
|
-- backward-compatibility to older versions.
|
||||||
-- will be deprecated
|
-- will be deprecated
|
||||||
if cfxZones.hasProperty(aZone, "coalition") then
|
if aZone:hasProperty("coalition") then
|
||||||
csarBase.side = cfxZones.getCoalitionFromZoneProperty(aZone, "CSARBASE", 0)
|
csarBase.side = aZone:getCoalitionFromZoneProperty("CSARBASE", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if we have provided a name field, default zone name
|
-- see if we have provided a name field, default zone name
|
||||||
csarBase.name = cfxZones.getStringFromZoneProperty(aZone, "name", aZone.name)
|
csarBase.name = aZone:getStringFromZoneProperty("name", aZone.name)
|
||||||
|
|
||||||
table.insert(csarManager.csarBases, csarBase)
|
table.insert(csarManager.csarBases, csarBase)
|
||||||
|
|
||||||
@ -1003,12 +1016,11 @@ function csarManager.getCSARBasesForSide(theSide)
|
|||||||
end
|
end
|
||||||
return bases
|
return bases
|
||||||
end
|
end
|
||||||
--
|
|
||||||
--
|
--
|
||||||
-- U P D A T E
|
-- U P D A T E
|
||||||
-- ===========
|
-- ===========
|
||||||
--
|
--
|
||||||
--
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- updateCSARMissions: make sure evacuees are still alive
|
-- updateCSARMissions: make sure evacuees are still alive
|
||||||
@ -1106,7 +1118,7 @@ function csarManager.update() -- every second
|
|||||||
-- also pop smoke if not popped already, or more than 5 minutes ago
|
-- also pop smoke if not popped already, or more than 5 minutes ago
|
||||||
if csarManager.useSmoke and (timer.getTime() - csarMission.lastSmokeTime) >= 5 * 60 then
|
if csarManager.useSmoke and (timer.getTime() - csarMission.lastSmokeTime) >= 5 * 60 then
|
||||||
local smokePoint = dcsCommon.randomPointOnPerimeter(
|
local smokePoint = dcsCommon.randomPointOnPerimeter(
|
||||||
50, csarMission.zone.point.x, csarMission.zone.point.z) --cfxZones.createHeightCorrectedPoint(csarMission.zone.point)
|
csarManager.smokeDist, csarMission.zone.point.x, csarMission.zone.point.z) --cfxZones.createHeightCorrectedPoint(csarMission.zone.point)
|
||||||
-- trigger.action.smoke(smokePoint, 4 )
|
-- trigger.action.smoke(smokePoint, 4 )
|
||||||
dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor)
|
dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor)
|
||||||
csarMission.lastSmokeTime = timer.getTime()
|
csarMission.lastSmokeTime = timer.getTime()
|
||||||
@ -1120,11 +1132,11 @@ function csarManager.update() -- every second
|
|||||||
local ep = evacuee:getPoint()
|
local ep = evacuee:getPoint()
|
||||||
d = dcsCommon.distFlat(uPoint, ep)
|
d = dcsCommon.distFlat(uPoint, ep)
|
||||||
d = math.floor(d * 10) / 10
|
d = math.floor(d * 10) / 10
|
||||||
if d < csarManager.hoverRadius * 2 then
|
if d < csarManager.rescueTriggerRange * 0.5 then --csarManager.hoverRadius * 2 then
|
||||||
local ownHeading = dcsCommon.getUnitHeadingDegrees(aUnit)
|
local ownHeading = dcsCommon.getUnitHeadingDegrees(aUnit)
|
||||||
local oclock = dcsCommon.clockPositionOfARelativeToB(ep, uPoint, ownHeading) .. " o'clock"
|
local oclock = dcsCommon.clockPositionOfARelativeToB(ep, uPoint, ownHeading) .. " o'clock"
|
||||||
-- log distance
|
-- log distance
|
||||||
local hoverMsg = "Closing on " .. csarMission.name .. ", " .. d * 3 .. "ft on your " .. oclock .. " o'clock"
|
local hoverMsg = "Closing on " .. csarMission.name .. ", " .. d * 1 .. "m on your " .. oclock .. " o'clock"
|
||||||
|
|
||||||
if d < csarManager.hoverRadius then
|
if d < csarManager.hoverRadius then
|
||||||
if (agl <= csarManager.hoverAlt) and (agl > 3) then
|
if (agl <= csarManager.hoverAlt) and (agl > 3) then
|
||||||
@ -1137,7 +1149,7 @@ function csarManager.update() -- every second
|
|||||||
hoverTime = timer.getTime() - hoverTime -- calculate number of seconds
|
hoverTime = timer.getTime() - hoverTime -- calculate number of seconds
|
||||||
local remainder = math.floor(csarManager.hoverDuration - hoverTime)
|
local remainder = math.floor(csarManager.hoverDuration - hoverTime)
|
||||||
if remainder < 1 then remainder = 1 end
|
if remainder < 1 then remainder = 1 end
|
||||||
hoverMsg = "Steady... " .. d * 3 .. "ft to your " .. oclock .. " o'clock, winching... (" .. remainder .. ")"
|
hoverMsg = "Steady... " .. d * 1 .. "m to your " .. oclock .. " o'clock, winching... (" .. remainder .. ")"
|
||||||
if hoverTime > csarManager.hoverDuration then
|
if hoverTime > csarManager.hoverDuration then
|
||||||
-- we rescued the guy!
|
-- we rescued the guy!
|
||||||
hoverMsg = "We have " .. csarMission.name .. " safely on board!"
|
hoverMsg = "We have " .. csarMission.name .. " safely on board!"
|
||||||
@ -1172,7 +1184,7 @@ function csarManager.update() -- every second
|
|||||||
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||||
return -- only ever one winch op
|
return -- only ever one winch op
|
||||||
else -- too high for hover
|
else -- too high for hover
|
||||||
hoverMsg = "Evacuee " .. d * 3 .. "ft on your " .. oclock .. " o'clock; land or descend to between 10 and 90 AGL for winching"
|
hoverMsg = "Evacuee " .. d * 1 .. "m on your " .. oclock .. " o'clock; land or descend to between 10 and 90 AGL for winching"
|
||||||
csarMission.hoveringUnits[uName] = nil -- reset timer
|
csarMission.hoveringUnits[uName] = nil -- reset timer
|
||||||
end
|
end
|
||||||
else -- not inside hover dist
|
else -- not inside hover dist
|
||||||
@ -1199,12 +1211,17 @@ function csarManager.update() -- every second
|
|||||||
-- check if their flag value has changed
|
-- check if their flag value has changed
|
||||||
if theZone.startCSAR then
|
if theZone.startCSAR then
|
||||||
-- this should always be true, but you never know
|
-- this should always be true, but you never know
|
||||||
local currVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
-- local currVal = theZone:getFlagValue(theZone.startCSAR)
|
||||||
if currVal ~= theZone.lastCSARVal then
|
-- if currVal ~= theZone.lastCSARVal then
|
||||||
|
if theZone:testZoneFlag(theZone.startCSAR, theZone.triggerMethod, "lastCSARVal") then
|
||||||
-- set up random point in zone
|
-- set up random point in zone
|
||||||
local mPoint = cfxZones.createRandomPointInZone(theZone)
|
local mPoint = theZone:getPoint()
|
||||||
|
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||||
|
if theZone.onRoad then
|
||||||
|
mPoint.x, mPoint.z = land.getClosestPointOnRoads('roads',mPoint.x, mPoint.z)
|
||||||
|
end
|
||||||
local theMission = csarManager.createCSARMissionData(
|
local theMission = csarManager.createCSARMissionData(
|
||||||
mPoint, --cfxZones.getPoint(theZone), -- point
|
mPoint,
|
||||||
theZone.csarSide, -- theSide
|
theZone.csarSide, -- theSide
|
||||||
theZone.csarFreq, -- freq
|
theZone.csarFreq, -- freq
|
||||||
theZone.csarName, -- name
|
theZone.csarName, -- name
|
||||||
@ -1214,7 +1231,7 @@ function csarManager.update() -- every second
|
|||||||
0.1, --theZone.radius) -- radius
|
0.1, --theZone.radius) -- radius
|
||||||
nil) -- parashoo unit
|
nil) -- parashoo unit
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
theZone.lastCSARVal = currVal
|
--theZone.lastCSARVal = currVal
|
||||||
if csarManager.verbose then
|
if csarManager.verbose then
|
||||||
trigger.action.outText("+++csar: started CSAR mission " .. theZone.csarName, 30)
|
trigger.action.outText("+++csar: started CSAR mission " .. theZone.csarName, 30)
|
||||||
end
|
end
|
||||||
@ -1235,23 +1252,9 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
|||||||
local coal = theUnit:getCoalition()
|
local coal = theUnit:getCoalition()
|
||||||
|
|
||||||
local csarPoint = dcsCommon.randomPointInCircle(radius, radius/2, point.x, point.z)
|
local csarPoint = dcsCommon.randomPointInCircle(radius, radius/2, point.x, point.z)
|
||||||
|
|
||||||
-- check the ground- water will kill the pilot
|
|
||||||
-- not any more! pilot can float
|
|
||||||
|
|
||||||
csarPoint.y = csarPoint.z
|
csarPoint.y = csarPoint.z
|
||||||
local surf = land.getSurfaceType(csarPoint)
|
local surf = land.getSurfaceType(csarPoint)
|
||||||
|
|
||||||
--[[--
|
|
||||||
if surf == 2 or surf == 3 then
|
|
||||||
if not silent then
|
|
||||||
trigger.action.outTextForCoalition(coal, "Bad chute! Bad chute! ".. pilotName .. " did not survive ejection out of their " .. theUnit:getTypeName(), 30)
|
|
||||||
trigger.action.outSoundForGroup(coal, csarManager.actionSound) -- "Quest Snare 3.wav")
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
|
|
||||||
csarPoint.y = land.getHeight(csarPoint)
|
csarPoint.y = land.getHeight(csarPoint)
|
||||||
|
|
||||||
-- when we get here, the terrain is ok, so let's drop the pilot
|
-- when we get here, the terrain is ok, so let's drop the pilot
|
||||||
@ -1278,10 +1281,8 @@ function csarManager.createCSARForParachutist(theUnit, name) -- invoked with par
|
|||||||
-- create a CSAR mission now
|
-- create a CSAR mission now
|
||||||
local theMission = csarManager.createCSARMissionData(pos, coa, nil, name, nil, nil, nil, 0.1, nil)
|
local theMission = csarManager.createCSARMissionData(pos, coa, nil, name, nil, nil, nil, 0.1, nil)
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
-- if not silent then
|
|
||||||
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction after eject!", 30)
|
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction after eject!", 30)
|
||||||
trigger.action.outSoundForGroup(coa, csarManager.actionSound) -- "Quest Snare 3.wav")
|
trigger.action.outSoundForGroup(coa, csarManager.actionSound)
|
||||||
-- end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -1290,7 +1291,6 @@ end
|
|||||||
|
|
||||||
function csarManager.processCSARBASE()
|
function csarManager.processCSARBASE()
|
||||||
local csarBases = cfxZones.zonesWithProperty("CSARBASE")
|
local csarBases = cfxZones.zonesWithProperty("CSARBASE")
|
||||||
|
|
||||||
-- now add all zones to my zones table, and init additional info
|
-- now add all zones to my zones table, and init additional info
|
||||||
-- from properties
|
-- from properties
|
||||||
for k, aZone in pairs(csarBases) do
|
for k, aZone in pairs(csarBases) do
|
||||||
@ -1305,54 +1305,58 @@ end
|
|||||||
function csarManager.readCSARZone(theZone)
|
function csarManager.readCSARZone(theZone)
|
||||||
-- zones have attribute "CSAR"
|
-- zones have attribute "CSAR"
|
||||||
-- gather data, and then create a mission from this
|
-- gather data, and then create a mission from this
|
||||||
local mName = cfxZones.getStringFromZoneProperty(theZone, "CSAR", "Lt. Unknown")
|
local mName = theZone:getStringFromZoneProperty("CSAR", theZone.name)
|
||||||
if mName == "" then mName = theZone.name end
|
-- if mName == "" then mName = theZone.name end
|
||||||
local theSide = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
|
local theSide = theZone:getCoalitionFromZoneProperty("coalition", 0)
|
||||||
theZone.csarSide = theSide
|
theZone.csarSide = theSide
|
||||||
theZone.csarName = mName -- now deprecating name attributes
|
theZone.csarName = mName -- now deprecating name attributes
|
||||||
if cfxZones.hasProperty(theZone, "name") then
|
if theZone:hasProperty("name") then
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "name", "<none>")
|
theZone.csarName = theZone:getStringFromZoneProperty("name", "<none>")
|
||||||
elseif cfxZones.hasProperty(theZone, "csarName") then
|
elseif theZone:hasProperty("csarName") then
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "csarName", "<none>")
|
theZone.csarName = theZone:getStringFromZoneProperty("csarName", "<none>")
|
||||||
elseif cfxZones.hasProperty(theZone, "pilotName") then
|
elseif theZone:hasProperty("pilotName") then
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "pilotName", "<none>")
|
theZone.csarName = theZone:getStringFromZoneProperty("pilotName", "<none>")
|
||||||
elseif cfxZones.hasProperty(theZone, "victimName") then
|
elseif theZone:hasProperty("victimName") then
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "victimName", "<none>")
|
theZone.csarName = theZone:getStringFromZoneProperty("victimName", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.csarFreq = cfxZones.getNumberFromZoneProperty(theZone, "freq", 0)
|
theZone.csarFreq = theZone:getNumberFromZoneProperty("freq", 0)
|
||||||
-- since freqs are set in 10kHz multiplier by DML
|
-- since freqs are set in 10kHz multiplier by DML
|
||||||
-- we have to divide the feq given here by 10
|
-- we have to divide the feq given here by 10
|
||||||
theZone.csarFreq = theZone.csarFreq / 10
|
theZone.csarFreq = theZone.csarFreq / 10
|
||||||
if theZone.csarFreq < 0.01 then theZone.csarFreq = nil end
|
if theZone.csarFreq < 0.01 then theZone.csarFreq = nil end
|
||||||
theZone.numCrew = 1
|
theZone.numCrew = 1
|
||||||
theZone.csarMapMarker = nil
|
theZone.csarMapMarker = nil
|
||||||
theZone.timeLimit = cfxZones.getNumberFromZoneProperty(theZone, "timeLimit", 0)
|
theZone.timeLimit = theZone:getNumberFromZoneProperty("timeLimit", 0)
|
||||||
if theZone.timeLimit == 0 then theZone.timeLimit = nil else theZone.timeLimit = timeLimit * 60 end
|
if theZone.timeLimit == 0 then theZone.timeLimit = nil else theZone.timeLimit = timeLimit * 60 end
|
||||||
|
|
||||||
local deferred = cfxZones.getBoolFromZoneProperty(theZone, "deferred", false)
|
local deferred = theZone:getBoolFromZoneProperty("deferred", false)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "in?") then
|
if theZone:hasProperty("in?") then
|
||||||
theZone.startCSAR = cfxZones.getStringFromZoneProperty(theZone, "in?", "*none")
|
theZone.startCSAR = theZone:getStringFromZoneProperty("in?", "*none")
|
||||||
theZone.lastCSARVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
theZone.lastCSARVal = theZone:getFlagValue(theZone.startCSAR)
|
||||||
|
elseif theZone:hasProperty("start?") then
|
||||||
|
theZone.startCSAR = theZone:getStringFromZoneProperty("start?", "*none")
|
||||||
|
theZone.lastCSARVal = theZone:getFlagValue(theZone.startCSAR)
|
||||||
|
elseif theZone:hasProperty("startCSAR?") then
|
||||||
|
theZone.startCSAR = theZone:getStringFromZoneProperty("startCSAR?", "*none")
|
||||||
|
theZone.lastCSARVal = theZone:getFlagValue(theZone.startCSAR)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "start?") then
|
if theZone:hasProperty("score") then
|
||||||
theZone.startCSAR = cfxZones.getStringFromZoneProperty(theZone, "start?", "*none")
|
theZone.score = theZone:getNumberFromZoneProperty("score", 100)
|
||||||
theZone.lastCSARVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
|
||||||
end
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "startCSAR?") then
|
|
||||||
theZone.startCSAR = cfxZones.getStringFromZoneProperty(theZone, "startCSAR?", "*none")
|
|
||||||
theZone.lastCSARVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
|
||||||
end
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "score") then
|
|
||||||
theZone.score = cfxZones.getNumberFromZoneProperty(theZone, "score", 100)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||||
|
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", true)
|
||||||
|
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||||
|
|
||||||
if (not deferred) then
|
if (not deferred) then
|
||||||
local mPoint = cfxZones.createRandomPointInZone(theZone)
|
local mPoint = theZone:getPoint()
|
||||||
|
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||||
|
if theZone.onRoad then
|
||||||
|
mPoint.x, mPoint.z = land.getClosestPointOnRoads('roads',mPoint.x, mPoint.z)
|
||||||
|
end
|
||||||
local theMission = csarManager.createCSARMissionData(
|
local theMission = csarManager.createCSARMissionData(
|
||||||
mPoint,
|
mPoint,
|
||||||
theZone.csarSide,
|
theZone.csarSide,
|
||||||
@ -1379,13 +1383,10 @@ end
|
|||||||
|
|
||||||
function csarManager.processCSARZones()
|
function csarManager.processCSARZones()
|
||||||
local csarBases = cfxZones.zonesWithProperty("CSAR")
|
local csarBases = cfxZones.zonesWithProperty("CSAR")
|
||||||
|
|
||||||
-- now add all zones to my zones table, and init additional info
|
-- now add all zones to my zones table, and init additional info
|
||||||
-- from properties
|
-- from properties
|
||||||
for k, aZone in pairs(csarBases) do
|
for k, aZone in pairs(csarBases) do
|
||||||
|
|
||||||
csarManager.readCSARZone(aZone)
|
csarManager.readCSARZone(aZone)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1406,6 +1407,7 @@ function csarManager.installCallback(theCB)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function csarManager.readConfigZone()
|
function csarManager.readConfigZone()
|
||||||
|
csarManager.name = "csarManagerConfig" -- compat with cfxZones
|
||||||
local theZone = cfxZones.getZoneByName("csarManagerConfig")
|
local theZone = cfxZones.getZoneByName("csarManagerConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if csarManager.verbose then
|
if csarManager.verbose then
|
||||||
@ -1415,48 +1417,46 @@ function csarManager.readConfigZone()
|
|||||||
end
|
end
|
||||||
csarManager.configZone = theZone -- save for flag banging compatibility
|
csarManager.configZone = theZone -- save for flag banging compatibility
|
||||||
|
|
||||||
csarManager.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
csarManager.verbose = theZone.verbose
|
||||||
|
csarManager.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
|
|
||||||
csarManager.ups = cfxZones.getNumberFromZoneProperty(theZone, "ups", 1)
|
csarManager.useSmoke = theZone:getBoolFromZoneProperty("useSmoke", true)
|
||||||
|
csarManager.smokeColor = theZone:getSmokeColorStringFromZoneProperty("smokeColor", "blue")
|
||||||
csarManager.useSmoke = cfxZones.getBoolFromZoneProperty(theZone, "useSmoke", true)
|
csarManager.smokeDist = theZone:getNumberFromZoneProperty("smokeDist", 30)
|
||||||
csarManager.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "blue")
|
|
||||||
csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor)
|
csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor)
|
||||||
|
|
||||||
csarManager.useFlare = cfxZones.getBoolFromZoneProperty(theZone, "useFlare", true)
|
csarManager.useFlare = theZone:getBoolFromZoneProperty("useFlare", true)
|
||||||
csarManager.flareColor = cfxZones.getFlareColorStringFromZoneProperty(theZone, "flareColor", "red")
|
csarManager.flareColor = theZone:getFlareColorStringFromZoneProperty("flareColor", "red")
|
||||||
csarManager.flareColor = dcsCommon.flareColor2Num(csarManager.flareColor)
|
csarManager.flareColor = dcsCommon.flareColor2Num(csarManager.flareColor)
|
||||||
|
|
||||||
|
if theZone:hasProperty("csarRedDelivered!") then
|
||||||
if cfxZones.hasProperty(theZone, "csarRedDelivered!") then
|
csarManager.csarRedDelivered = theZone:getStringFromZoneProperty("csarRedDelivered!", "*<none>")
|
||||||
csarManager.csarRedDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarRedDelivered!", "*<none>")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "csarBlueDelivered!") then
|
if theZone:hasProperty("csarBlueDelivered!") then
|
||||||
csarManager.csarBlueDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarBlueDelivered!", "*<none>")
|
csarManager.csarBlueDelivered = theZone:getStringFromZoneProperty("csarBlueDelivered!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "csarDelivered!") then
|
if theZone:hasProperty("csarDelivered!") then
|
||||||
csarManager.csarDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarDelivered!", "*<none>")
|
csarManager.csarDelivered = theZone:getStringFromZoneProperty("csarDelivered!", "*<none>")
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
csarManager.rescueRadius = cfxZones.getNumberFromZoneProperty(theZone, "rescueRadius", 70) --70 -- must land within 50m to rescue
|
csarManager.rescueRadius = theZone:getNumberFromZoneProperty( "rescueRadius", 70)
|
||||||
csarManager.hoverRadius = cfxZones.getNumberFromZoneProperty(theZone, "hoverRadius", 30) -- 30 -- must hover within 10m of unit
|
csarManager.hoverRadius = theZone:getNumberFromZoneProperty( "hoverRadius", 30)
|
||||||
csarManager.hoverAlt = cfxZones.getNumberFromZoneProperty(theZone, "hoverAlt", 40) -- 40 -- must hover below this alt
|
csarManager.hoverAlt = theZone:getNumberFromZoneProperty("hoverAlt", 40)
|
||||||
csarManager.hoverDuration = cfxZones.getNumberFromZoneProperty(theZone, "hoverDuration", 20) -- 20 -- must hover for this duration
|
csarManager.hoverDuration = theZone:getNumberFromZoneProperty( "hoverDuration", 20)
|
||||||
csarManager.rescueTriggerRange = cfxZones.getNumberFromZoneProperty(theZone, "rescueTriggerRange", 2000) -- 2000 -- when the unit pops smoke and radios
|
csarManager.rescueTriggerRange = theZone:getNumberFromZoneProperty("rescueTriggerRange", 2000)
|
||||||
csarManager.beaconSound = cfxZones.getStringFromZoneProperty(theZone, "beaconSound", "Radio_beacon_of_distress_on_121.ogg") --"Radio_beacon_of_distress_on_121,5_MHz.ogg"
|
csarManager.beaconSound = theZone:getStringFromZoneProperty( "beaconSound", "Radio_beacon_of_distress_on_121,5_MHz.ogg")
|
||||||
csarManager.pilotWeight = cfxZones.getNumberFromZoneProperty(theZone, "pilotWeight", 120) -- 120
|
csarManager.pilotWeight = theZone:getNumberFromZoneProperty("pilotWeight", 120)
|
||||||
|
|
||||||
csarManager.rescueScore = cfxZones.getNumberFromZoneProperty(theZone, "rescueScore", 100)
|
csarManager.rescueScore = theZone:getNumberFromZoneProperty( "rescueScore", 100)
|
||||||
|
|
||||||
csarManager.actionSound = cfxZones.getStringFromZoneProperty(theZone, "actionSound", "Quest Snare 3.wav")
|
csarManager.actionSound = theZone:getStringFromZoneProperty( "actionSound", "Quest Snare 3.wav")
|
||||||
csarManager.vectoring = cfxZones.getBoolFromZoneProperty(theZone, "vectoring", true)
|
csarManager.vectoring = theZone:getBoolFromZoneProperty("vectoring", true)
|
||||||
|
|
||||||
-- add own troop carriers
|
-- add own troop carriers
|
||||||
if cfxZones.hasProperty(theZone, "troopCarriers") then
|
if theZone:hasProperty("troopCarriers") then
|
||||||
local tc = cfxZones.getStringFromZoneProperty(theZone, "troopCarriers", "UH-1D")
|
local tc = theZone:getStringFromZoneProperty("troopCarriers", "UH-1D")
|
||||||
tc = dcsCommon.splitString(tc, ",")
|
tc = dcsCommon.splitString(tc, ",")
|
||||||
csarManager.troopCarriers = dcsCommon.trimArray(tc)
|
csarManager.troopCarriers = dcsCommon.trimArray(tc)
|
||||||
if csarManager.verbose then
|
if csarManager.verbose then
|
||||||
@ -1467,6 +1467,8 @@ function csarManager.readConfigZone()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
csarManager.addPrefix = theZone:getBoolFromZoneProperty("addPrefix", true)
|
||||||
|
|
||||||
if csarManager.verbose then
|
if csarManager.verbose then
|
||||||
trigger.action.outText("+++csar: read config", 30)
|
trigger.action.outText("+++csar: read config", 30)
|
||||||
end
|
end
|
||||||
@ -1536,5 +1538,5 @@ end
|
|||||||
|
|
||||||
-- allow any airfied to be csarsafe by default, no longer *requires* csarbase
|
-- allow any airfied to be csarsafe by default, no longer *requires* csarbase
|
||||||
|
|
||||||
-- support quad zones and optionally non-random placement
|
-- remove cfxPlayer dependency
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.9.3"
|
dcsCommon.version = "2.9.5"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
2.2.6 - compassPositionOfARelativeToB
|
||||||
- clockPositionOfARelativeToB
|
- clockPositionOfARelativeToB
|
||||||
@ -173,6 +173,9 @@ dcsCommon.version = "2.9.3"
|
|||||||
- new getCountriesForCoalition()
|
- new getCountriesForCoalition()
|
||||||
2.9.2 - updated event2text
|
2.9.2 - updated event2text
|
||||||
2.9.3 - getAirbasesWhoseNameContains now supports category tables for filtering
|
2.9.3 - getAirbasesWhoseNameContains now supports category tables for filtering
|
||||||
|
2.9.4 - new bearing2degrees()
|
||||||
|
2.9.5 - distanceOfPointPToLineXZ(p, p1, p2)
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
@ -845,6 +848,13 @@ dcsCommon.version = "2.9.3"
|
|||||||
return "North"
|
return "North"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.bearing2degrees(inRad)
|
||||||
|
local degrees = inRad / math.pi * 180
|
||||||
|
if degrees < 0 then degrees = degrees + 360 end
|
||||||
|
if degrees > 360 then degrees = degrees - 360 end
|
||||||
|
return degrees
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.bearing2compass(inrad)
|
function dcsCommon.bearing2compass(inrad)
|
||||||
local bearing = math.floor(inrad / math.pi * 180)
|
local bearing = math.floor(inrad / math.pi * 180)
|
||||||
if bearing < 0 then bearing = bearing + 360 end
|
if bearing < 0 then bearing = bearing + 360 end
|
||||||
@ -977,6 +987,19 @@ dcsCommon.version = "2.9.3"
|
|||||||
-- note: no separate case for straight in front or behind
|
-- note: no separate case for straight in front or behind
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Distance of point p to line defined by p1,p2
|
||||||
|
-- only on XZ map
|
||||||
|
function dcsCommon.distanceOfPointPToLineXZ(p, p1, p2)
|
||||||
|
local x21 = p2.x - p1.x
|
||||||
|
local y10 = p1.z - p.z
|
||||||
|
local x10 = p1.x - p.x
|
||||||
|
local y21 = p2.z - p1.z
|
||||||
|
local numer = math.abs((x21*y10) - (x10 * y21))
|
||||||
|
local denom = math.sqrt(x21 * x21 + y21 * y21)
|
||||||
|
local dist = numer/denom
|
||||||
|
return dist
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.randomDegrees()
|
function dcsCommon.randomDegrees()
|
||||||
local degrees = math.random(360) * 3.14152 / 180
|
local degrees = math.random(360) * 3.14152 / 180
|
||||||
return degrees
|
return degrees
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
delayFlag = {}
|
delayFlag = {}
|
||||||
delayFlag.version = "1.3.0"
|
delayFlag.version = "1.4.0"
|
||||||
delayFlag.verbose = false
|
delayFlag.verbose = false
|
||||||
delayFlag.requiredLibs = {
|
delayFlag.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -38,6 +38,8 @@ delayFlag.flags = {}
|
|||||||
- continueDelay
|
- continueDelay
|
||||||
- delayLeft
|
- delayLeft
|
||||||
1.3.0 - persistence
|
1.3.0 - persistence
|
||||||
|
1.4.0 - dmlZones
|
||||||
|
- delayLeft#
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -64,70 +66,70 @@ end
|
|||||||
--
|
--
|
||||||
function delayFlag.createTimerWithZone(theZone)
|
function delayFlag.createTimerWithZone(theZone)
|
||||||
-- delay
|
-- delay
|
||||||
theZone.delayMin, theZone.delayMax = cfxZones.getPositiveRangeFromZoneProperty(theZone, "timeDelay", 1) -- same as zone signature
|
theZone.delayMin, theZone.delayMax = theZone:getPositiveRangeFromZoneProperty("timeDelay", 1) -- same as zone signature
|
||||||
if delayFlag.verbose or theZone.verbose then
|
if delayFlag.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++dlyF: time delay is <" .. theZone.delayMin .. ", " .. theZone.delayMax .. "> seconds", 30)
|
trigger.action.outText("+++dlyF: time delay is <" .. theZone.delayMin .. ", " .. theZone.delayMax .. "> seconds", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- watchflags:
|
-- watchflags:
|
||||||
-- triggerMethod
|
-- triggerMethod
|
||||||
theZone.delayTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
theZone.delayTriggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "delayTriggerMethod") then
|
if theZone:hasProperty("delayTriggerMethod") then
|
||||||
theZone.delayTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "delayTriggerMethod", "change")
|
theZone.delayTriggerMethod = theZone:getStringFromZoneProperty("delayTriggerMethod", "change")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- trigger flag
|
-- trigger flag
|
||||||
if cfxZones.hasProperty(theZone, "f?") then
|
if theZone:hasProperty("f?") then
|
||||||
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "f?", "none")
|
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("f?", "none")
|
||||||
end
|
elseif theZone:hasProperty("in?") then
|
||||||
|
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("in?", "none")
|
||||||
if cfxZones.hasProperty(theZone, "in?") then
|
elseif theZone:hasProperty("startDelay?") then
|
||||||
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "in?", "none")
|
theZone.triggerDelayFlag = theZone:getStringFromZoneProperty("startDelay?", "none")
|
||||||
end
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "startDelay?") then
|
|
||||||
theZone.triggerDelayFlag = cfxZones.getStringFromZoneProperty(theZone, "startDelay?", "none")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if theZone.triggerDelayFlag then
|
if theZone.triggerDelayFlag then
|
||||||
theZone.lastDelayTriggerValue = cfxZones.getFlagValue(theZone.triggerDelayFlag, theZone) -- trigger.misc.getUserFlag(theZone.triggerDelayFlag) -- save last value
|
theZone.lastDelayTriggerValue = theZone:getFlagValue(theZone.triggerDelayFlag)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
theZone.delayMethod = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "delayMethod") then
|
if theZone:hasProperty("delayMethod") then
|
||||||
theZone.delayMethod = cfxZones.getStringFromZoneProperty(theZone, "delayMethod", "inc")
|
theZone.delayMethod = theZone:getStringFromZoneProperty( "delayMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- out flag
|
-- out flag
|
||||||
theZone.delayDoneFlag = cfxZones.getStringFromZoneProperty(theZone, "out!", "*<none>")
|
theZone.delayDoneFlag = theZone:getStringFromZoneProperty("out!", "*<none>")
|
||||||
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "delayDone!") then
|
if theZone:hasProperty("delayDone!") then
|
||||||
theZone.delayDoneFlag = cfxZones.getStringFromZoneProperty(theZone, "delayDone!", "*<none>")
|
theZone.delayDoneFlag = theZone:getStringFromZoneProperty( "delayDone!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- stop the press!
|
-- stop the press!
|
||||||
if cfxZones.hasProperty(theZone, "stopDelay?") then
|
if theZone:hasProperty("stopDelay?") then
|
||||||
theZone.triggerStopDelay = cfxZones.getStringFromZoneProperty(theZone, "stopDelay?", "none")
|
theZone.triggerStopDelay = theZone:getStringFromZoneProperty("stopDelay?", "none")
|
||||||
theZone.lastTriggerStopValue = cfxZones.getFlagValue(theZone.triggerStopDelay, theZone)
|
theZone.lastTriggerStopValue = theZone:getFlagValue(theZone.triggerStopDelay)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- pause and continue
|
-- pause and continue
|
||||||
if cfxZones.hasProperty(theZone, "pauseDelay?") then
|
if theZone:hasProperty("pauseDelay?") then
|
||||||
theZone.triggerPauseDelay = cfxZones.getStringFromZoneProperty(theZone, "pauseDelay?", "none")
|
theZone.triggerPauseDelay = theZone:getStringFromZoneProperty("pauseDelay?", "none")
|
||||||
theZone.lastTriggerPauseValue = cfxZones.getFlagValue(theZone.triggerPauseDelay, theZone)
|
theZone.lastTriggerPauseValue = theZone:getFlagValue(theZone.triggerPauseDelay)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "continueDelay?") then
|
if theZone:hasProperty("continueDelay?") then
|
||||||
theZone.triggerContinueDelay = cfxZones.getStringFromZoneProperty(theZone, "continueDelay?", "none")
|
theZone.triggerContinueDelay = theZone:getStringFromZoneProperty("continueDelay?", "none")
|
||||||
theZone.lastTriggerContinueValue = cfxZones.getFlagValue(theZone.triggerContinueDelay, theZone)
|
theZone.lastTriggerContinueValue = theZone:getFlagValue(theZone.triggerContinueDelay)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- timeInfo
|
-- timeInfo
|
||||||
theZone.delayTimeLeft = cfxZones.getStringFromZoneProperty(theZone, "delayLeft", "*cfxIgnored")
|
if theZone:hasProperty("delayLeft") then
|
||||||
|
theZone.delayTimeLeft = theZone:getStringFromZoneProperty("delayLeft", "*cfxIgnored")
|
||||||
|
else
|
||||||
|
theZone.delayTimeLeft = theZone:getStringFromZoneProperty("delayLeft#", "*cfxIgnored")
|
||||||
|
end
|
||||||
|
|
||||||
-- init
|
-- init
|
||||||
theZone.delayRunning = false
|
theZone.delayRunning = false
|
||||||
@ -136,7 +138,7 @@ function delayFlag.createTimerWithZone(theZone)
|
|||||||
theZone.timeLeft = -1 -- in seconds, always kept up to date
|
theZone.timeLeft = -1 -- in seconds, always kept up to date
|
||||||
-- but not really used
|
-- but not really used
|
||||||
|
|
||||||
cfxZones.setFlagValue(theZone.delayTimeLeft, -1, theZone)
|
theZone:setFlagValue(theZone.delayTimeLeft, -1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -167,7 +169,7 @@ function delayFlag.startDelay(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
theZone.timeLimit = timer.getTime() + delay
|
theZone.timeLimit = timer.getTime() + delay
|
||||||
cfxZones.setFlagValue(theZone.delayTimeLeft, delay, theZone)
|
theZone:setFlagValue(theZone.delayTimeLeft, delay)
|
||||||
end
|
end
|
||||||
|
|
||||||
function delayFlag.pauseDelay(theZone)
|
function delayFlag.pauseDelay(theZone)
|
||||||
@ -194,7 +196,7 @@ function delayFlag.update()
|
|||||||
if remaining < 0 then remaining = -1 end
|
if remaining < 0 then remaining = -1 end
|
||||||
|
|
||||||
-- see if we need to stop
|
-- see if we need to stop
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.triggerStopDelay, aZone.delayTriggerMethod, "lastTriggerStopValue") then
|
if aZone:testZoneFlag(aZone.triggerStopDelay, aZone.delayTriggerMethod, "lastTriggerStopValue") then
|
||||||
aZone.delayRunning = false -- simply stop.
|
aZone.delayRunning = false -- simply stop.
|
||||||
if delayFlag.verbose or aZone.verbose then
|
if delayFlag.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++dlyF: stopped delay " .. aZone.name, 30)
|
trigger.action.outText("+++dlyF: stopped delay " .. aZone.name, 30)
|
||||||
@ -202,7 +204,7 @@ function delayFlag.update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.triggerDelayFlag, aZone.delayTriggerMethod, "lastDelayTriggerValue") then
|
if aZone:testZoneFlag(aZone.triggerDelayFlag, aZone.delayTriggerMethod, "lastDelayTriggerValue") then
|
||||||
if delayFlag.verbose or aZone.verbose then
|
if delayFlag.verbose or aZone.verbose then
|
||||||
if aZone.delayRunning then
|
if aZone.delayRunning then
|
||||||
trigger.action.outText("+++dlyF: re-starting timer " .. aZone.name, 30)
|
trigger.action.outText("+++dlyF: re-starting timer " .. aZone.name, 30)
|
||||||
@ -216,7 +218,7 @@ function delayFlag.update()
|
|||||||
|
|
||||||
if not aZone.delayPaused then
|
if not aZone.delayPaused then
|
||||||
|
|
||||||
if aZone.delayRunning and cfxZones.testZoneFlag(aZone, aZone.triggerPauseDelay, aZone.delayTriggerMethod, "lastTriggerPauseValue") then
|
if aZone.delayRunning and aZone:testZoneFlag( aZone.triggerPauseDelay, aZone.delayTriggerMethod, "lastTriggerPauseValue") then
|
||||||
if delayFlag.verbose or aZone.verbose then
|
if delayFlag.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++dlyF: pausing timer <" .. aZone.name .. "> with <" .. remaining .. "> remaining", 30)
|
trigger.action.outText("+++dlyF: pausing timer <" .. aZone.name .. "> with <" .. remaining .. "> remaining", 30)
|
||||||
end
|
end
|
||||||
@ -232,14 +234,14 @@ function delayFlag.update()
|
|||||||
if delayFlag.verbose or aZone.verbose then
|
if delayFlag.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++dlyF: banging on " .. aZone.delayDoneFlag, 30)
|
trigger.action.outText("+++dlyF: banging on " .. aZone.delayDoneFlag, 30)
|
||||||
end
|
end
|
||||||
cfxZones.pollFlag(aZone.delayDoneFlag, aZone.delayMethod, aZone)
|
aZone:pollFlag(aZone.delayDoneFlag, aZone.delayMethod)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxZones.setFlagValue(aZone.delayTimeLeft, remaining, aZone)
|
aZone:setFlagValue(aZone.delayTimeLeft, remaining)
|
||||||
else
|
else
|
||||||
-- we are paused. Check for 'continue'
|
-- we are paused. Check for 'continue'
|
||||||
if aZone.delayRunning and cfxZones.testZoneFlag(aZone, aZone.triggerContinueDelay, aZone.delayTriggerMethod, "lastTriggerContinueValue") then
|
if aZone.delayRunning and aZone:testZoneFlag( aZone.triggerContinueDelay, aZone.delayTriggerMethod, "lastTriggerContinueValue") then
|
||||||
if delayFlag.verbose or aZone.verbose then
|
if delayFlag.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++dlyF: continuing timer <" .. aZone.name .. "> with <" .. aZone.remainingTime .. "> seconds remaining", 30)
|
trigger.action.outText("+++dlyF: continuing timer <" .. aZone.name .. "> with <" .. aZone.remainingTime .. "> seconds remaining", 30)
|
||||||
end
|
end
|
||||||
@ -312,13 +314,10 @@ end
|
|||||||
function delayFlag.readConfigZone()
|
function delayFlag.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("delayFlagsConfig")
|
local theZone = cfxZones.getZoneByName("delayFlagsConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if delayFlag.verbose then
|
theZone = cfxZones.createSimpleZone("delayFlagsConfig")
|
||||||
trigger.action.outText("+++dlyF: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
delayFlag.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
delayFlag.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
|
|
||||||
if delayFlag.verbose then
|
if delayFlag.verbose then
|
||||||
trigger.action.outText("+++dlyF: read config", 30)
|
trigger.action.outText("+++dlyF: read config", 30)
|
||||||
|
|||||||
@ -111,7 +111,7 @@ function mxObjects.showNClosestTextObjectsToUnit(n, theUnit, numbered)
|
|||||||
-- dist
|
-- dist
|
||||||
msg = msg .. " " .. dist .. units
|
msg = msg .. " " .. dist .. units
|
||||||
msg = msg .. "\n" -- add line feed
|
msg = msg .. "\n" -- add line feed
|
||||||
if mxObjects.doubleLine then msg = msg .. "\n" end
|
if mxObjects.doubleLine and (i < n) then msg = msg .. "\n" end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return msg
|
return msg
|
||||||
|
|||||||
@ -51,7 +51,7 @@ function ownAll.ownAllForZone(theZone)
|
|||||||
theZone:setFlagValue(theZone.totalNum, #filtered)
|
theZone:setFlagValue(theZone.totalNum, #filtered)
|
||||||
end
|
end
|
||||||
|
|
||||||
theZone.ownershipUplink = theZone:getBoolFromZoneProperty("uplink", true)
|
theZone.ownershipUplink = theZone:getBoolFromZoneProperty("uplink", true) -- not documented
|
||||||
|
|
||||||
local redNum, blueNum
|
local redNum, blueNum
|
||||||
theZone.state, redNum, blueNum = ownAll.calcState(theZone)
|
theZone.state, redNum, blueNum = ownAll.calcState(theZone)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
radioMenu = {}
|
radioMenu = {}
|
||||||
radioMenu.version = "2.1.0"
|
radioMenu.version = "2.1.1"
|
||||||
radioMenu.verbose = false
|
radioMenu.verbose = false
|
||||||
radioMenu.ups = 1
|
radioMenu.ups = 1
|
||||||
radioMenu.requiredLibs = {
|
radioMenu.requiredLibs = {
|
||||||
@ -31,6 +31,7 @@ radioMenu.menus = {}
|
|||||||
ackA, ackB, ackC, ackD attributes
|
ackA, ackB, ackC, ackD attributes
|
||||||
valA-D now define full method, not just values
|
valA-D now define full method, not just values
|
||||||
full wildcard support for ack and cooldown
|
full wildcard support for ack and cooldown
|
||||||
|
2.1.1 - outMessage now works correctly
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function radioMenu.addRadioMenu(theZone)
|
function radioMenu.addRadioMenu(theZone)
|
||||||
@ -355,7 +356,7 @@ function radioMenu.radioOutMsg(ack, gid, theZone)
|
|||||||
-- group processing. only if gid>0 and cfxMX
|
-- group processing. only if gid>0 and cfxMX
|
||||||
local theMsg = ack
|
local theMsg = ack
|
||||||
if (gid > 0) and cfxMX then
|
if (gid > 0) and cfxMX then
|
||||||
local gName = cfxMX.cfxMX.groupNamesByID[gid]
|
local gName = cfxMX.groupNamesByID[gid]
|
||||||
theMsg = theMsg:gsub("<group>", gName)
|
theMsg = theMsg:gsub("<group>", gName)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
BIN
tutorial & demo missions/demo - bombs away.miz
Normal file
BIN
tutorial & demo missions/demo - bombs away.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user