mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.3.4
New Tacan Zone module
This commit is contained in:
parent
3f0de87b07
commit
a231673292
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "3.1.2"
|
||||
cfxZones.version = "3.1.3"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -132,6 +132,11 @@ cfxZones.version = "3.1.2"
|
||||
- 3.1.1 - getRGBAVectorFromZoneProperty now supports #RRGGBBAA and #RRGGBB format
|
||||
- owner for all, default 0
|
||||
- 3.1.2 - getAllZoneProperties has numbersOnly option
|
||||
- 3.1.3 - new numberArrayFromString()
|
||||
- new declutterZone()
|
||||
- new getZoneVolume()
|
||||
- offsetZone also updates zone bounds when moving zones
|
||||
- corrected bug in calculateZoneBounds()
|
||||
|
||||
--]]--
|
||||
cfxZones.verbose = false
|
||||
@ -340,7 +345,8 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
if (vertex.x < ll.x) then ll.x = vertex.x; ul.x = vertex.x end
|
||||
if (vertex.x > lr.x) then lr.x = vertex.x; ur.x = vertex.x end
|
||||
if (vertex.z < ul.z) then ul.z = vertex.z; ur.z = vertex.z end
|
||||
if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end
|
||||
--if (vertex.z > ll.z) then ll.z = vertex.z; lr.z = vertex.z end
|
||||
if (vertex.z > ur.z) then ur.z = vertex.z; ul.z = vertex.z end
|
||||
local dp = dcsCommon.dist(theZone.point, vertex)
|
||||
if dp > pRad then pRad = dp end -- find largst distance to vertex
|
||||
end
|
||||
@ -351,6 +357,7 @@ function cfxZones.calculateZoneBounds(theZone)
|
||||
bounds.lr = lr
|
||||
bounds.ul = ul
|
||||
bounds.ur = ur
|
||||
-- we may need to ascertain why we need ul, ur, ll, lr instead of just ll and ur
|
||||
-- store pRad
|
||||
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
|
||||
@ -804,6 +811,66 @@ function cfxZones.getZonesWithAttributeNamed(attributeName, testZones)
|
||||
return attributZones
|
||||
end
|
||||
|
||||
--
|
||||
-- zone volume management
|
||||
--
|
||||
|
||||
function cfxZones.getZoneVolume(theZone)
|
||||
if not theZone then return nil end
|
||||
|
||||
if (theZone.isCircle) then
|
||||
-- create a sphere volume
|
||||
local p = cfxZones.getPoint(theZone)
|
||||
p.y = land.getHeight({x = p.x, y = p.z})
|
||||
local r = theZone.radius
|
||||
if r < 10 then r = 10 end
|
||||
local vol = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = p,
|
||||
radius = r
|
||||
}
|
||||
}
|
||||
return vol
|
||||
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
|
||||
local lowerLeft = {}
|
||||
-- we build x = westerm y = southern, Z = alt
|
||||
local alt = land.getHeight({x=theZone.bounds.ll.x, y = theZone.bounds.ll.z}) - 10
|
||||
lowerLeft.x = theZone.bounds.ll.x
|
||||
lowerLeft.z = theZone.bounds.ll.z
|
||||
lowerLeft.y = alt -- we go lower
|
||||
|
||||
local upperRight = {}
|
||||
alt = land.getHeight({x=theZone.bounds.ur.x, y = theZone.bounds.ur.z}) + 10
|
||||
upperRight.x = theZone.bounds.ur.x
|
||||
upperRight.z = theZone.bounds.ur.z
|
||||
upperRight.y = alt -- we go higher
|
||||
|
||||
-- construct volume
|
||||
local vol = {
|
||||
id = world.VolumeType.BOX,
|
||||
params = {
|
||||
min = lowerLeft,
|
||||
max = upperRight
|
||||
}
|
||||
}
|
||||
return vol
|
||||
else
|
||||
trigger.action.outText("zne: unknown zone type for <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function cfxZones.declutterZone(theZone)
|
||||
if not theZone then return end
|
||||
local theVol = cfxZones.getZoneVolume(theZone)
|
||||
if theZone.verbose then
|
||||
dcsCommon.dumpVar2Str("vol", theVol)
|
||||
end
|
||||
world.removeJunk(theVol)
|
||||
end
|
||||
|
||||
--
|
||||
-- units / groups in zone
|
||||
--
|
||||
@ -924,6 +991,18 @@ function cfxZones.offsetZone(theZone, dx, dz)
|
||||
theZone.poly[v].x = theZone.poly[v].x + dx
|
||||
theZone.poly[v].z = theZone.poly[v].z + dz
|
||||
end
|
||||
|
||||
-- update zone bounds
|
||||
theZone.bounds.ll.x = theZone.bounds.ll.x + dx
|
||||
theZone.bounds.lr.x = theZone.bounds.lr.x + dx
|
||||
theZone.bounds.ul.x = theZone.bounds.ul.x + dx
|
||||
theZone.bounds.ur.x = theZone.bounds.ur.x + dx
|
||||
|
||||
theZone.bounds.ll.z = theZone.bounds.ll.z + dz
|
||||
theZone.bounds.lr.z = theZone.bounds.lr.z + dz
|
||||
theZone.bounds.ul.z = theZone.bounds.ul.z + dz
|
||||
theZone.bounds.ur.z = theZone.bounds.ur.z + dz
|
||||
|
||||
end
|
||||
|
||||
function cfxZones.moveZoneTo(theZone, x, z)
|
||||
@ -1886,7 +1965,58 @@ function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName)
|
||||
return testResult, currVal
|
||||
end
|
||||
|
||||
|
||||
function cfxZones.numberArrayFromString(inString, default)
|
||||
if not default then default = 0 end
|
||||
if string.len(inString) < 1 then
|
||||
trigger.action.outText("+++zne: empty numbers", 30)
|
||||
return {default, }
|
||||
end
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("+++zne: processing <" .. inString .. ">", 30)
|
||||
end
|
||||
|
||||
local flags = {}
|
||||
local rawElements = dcsCommon.splitString(inString, ",")
|
||||
-- go over all elements
|
||||
for idx, anElement in pairs(rawElements) do
|
||||
anElement = dcsCommon.trim(anElement)
|
||||
if dcsCommon.stringStartsWithDigit(anElement) and dcsCommon.containsString(anElement, "-") then
|
||||
-- interpret this as a range
|
||||
local theRange = dcsCommon.splitString(anElement, "-")
|
||||
local lowerBound = theRange[1]
|
||||
lowerBound = tonumber(lowerBound)
|
||||
local upperBound = theRange[2]
|
||||
upperBound = tonumber(upperBound)
|
||||
if lowerBound and upperBound then
|
||||
-- swap if wrong order
|
||||
if lowerBound > upperBound then
|
||||
local temp = upperBound
|
||||
upperBound = lowerBound
|
||||
lowerBound = temp
|
||||
end
|
||||
-- now add add numbers to flags
|
||||
for f=lowerBound, upperBound do
|
||||
table.insert(flags, tostring(f))
|
||||
end
|
||||
else
|
||||
-- bounds illegal
|
||||
trigger.action.outText("+++zne: ignored range <" .. anElement .. "> (range)", 30)
|
||||
end
|
||||
else
|
||||
-- single number
|
||||
f = dcsCommon.trim(anElement)
|
||||
f = tonumber(f)
|
||||
if f then
|
||||
table.insert(flags, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
if #flags < 1 then flags = {default, } end
|
||||
if cfxZones.verbose then
|
||||
trigger.action.outText("+++zne: <" .. #flags .. "> flags total", 30)
|
||||
end
|
||||
return flags
|
||||
end
|
||||
|
||||
function cfxZones.flagArrayFromString(inString)
|
||||
-- original code from RND flag
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cloneZones = {}
|
||||
cloneZones.version = "1.7.1"
|
||||
cloneZones.version = "1.7.3"
|
||||
cloneZones.verbose = false
|
||||
cloneZones.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -94,6 +94,8 @@ cloneZones.respawnOnGroupID = true
|
||||
1.7.1 - useDelicates handOff for delicates
|
||||
- forcedRespawn passes zone instead of verbose
|
||||
1.7.2 - onPerimeter attribute
|
||||
1.7.3 - declutter option
|
||||
|
||||
|
||||
--]]--
|
||||
|
||||
@ -256,6 +258,9 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
end
|
||||
end
|
||||
|
||||
-- declutter
|
||||
theZone.declutter = cfxZones.getBoolFromZoneProperty(theZone, "declutter", false)
|
||||
|
||||
-- watchflags
|
||||
theZone.cloneTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
||||
|
||||
@ -1543,6 +1548,14 @@ function cloneZones.spawnWithCloner(theZone)
|
||||
cloneZones.invokeCallbacks(theZone, "wiped", {})
|
||||
end
|
||||
|
||||
-- declutter?
|
||||
if theZone.declutter then
|
||||
cfxZones.declutterZone(theZone)
|
||||
if theZone.verbose then
|
||||
trigger.action.outText("+++clnZ: cloner <" .. theZone.name .. "> declutter complete.", 30)
|
||||
end
|
||||
end
|
||||
|
||||
local theClones, theStatics = cloneZones.spawnWithTemplateForZone(templateZone, theZone)
|
||||
-- reset hasClones so we know our spawns are full and we can
|
||||
-- detect complete destruction
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "2.8.9"
|
||||
dcsCommon.version = "2.8.10"
|
||||
--[[-- VERSION HISTORY
|
||||
2.2.6 - compassPositionOfARelativeToB
|
||||
- clockPositionOfARelativeToB
|
||||
@ -156,6 +156,13 @@ dcsCommon.version = "2.8.9"
|
||||
2.8.9 - vAdd supports xy and xyz
|
||||
- vSub supports xy and xyz
|
||||
- vMultScalar supports xy and xyz
|
||||
2.8.10 - tacan2freq now integrated with module (blush)
|
||||
- array2string cosmetic default
|
||||
- vMultScalar corrected bug in accessing b.z
|
||||
- new randomLetter()
|
||||
- new getPlayerUnit()
|
||||
- new getMapName()
|
||||
- new getMagDeclForPoint()
|
||||
|
||||
--]]--
|
||||
|
||||
@ -2236,13 +2243,14 @@ end
|
||||
end
|
||||
|
||||
function dcsCommon.array2string(inArray, deli)
|
||||
if not deli then deli = "," end
|
||||
if not deli then deli = ", " end
|
||||
if type(inArray) ~= "table" then return "<err in array2string: not an array>" end
|
||||
local s = ""
|
||||
local count = 0
|
||||
for idx, ele in pairs(inArray) do
|
||||
if count > 0 then s = s .. deli .. " " end
|
||||
s = s .. ele
|
||||
count = count + 1
|
||||
end
|
||||
return s
|
||||
end
|
||||
@ -2475,6 +2483,7 @@ end
|
||||
end
|
||||
|
||||
function dcsCommon.dumpVar2Str(key, value, prefix, inrecursion)
|
||||
-- dumps to screen, not string
|
||||
if not inrecursion then
|
||||
-- output a marker to find in the log / screen
|
||||
trigger.action.outText("*** dcsCommon vardump START",30)
|
||||
@ -2599,7 +2608,7 @@ end
|
||||
end
|
||||
|
||||
-- based on buzzer1977's idea, channel is number, eg in 74X, channel is 74, mode is "X"
|
||||
function tacan2freq(channel, mode)
|
||||
function dcsCommon.tacan2freq(channel, mode)
|
||||
if not mode then mode = "X" end
|
||||
if not channel then channel = 1 end
|
||||
if type(mode) ~= "string" then mode = "X" end
|
||||
@ -2712,7 +2721,7 @@ function dcsCommon.vMultScalar(a, f)
|
||||
if not f then f = 0 end
|
||||
r.x = a.x * f
|
||||
r.y = a.y * f
|
||||
if a.z and b.z then
|
||||
if a.z then
|
||||
r.z = a.z * f
|
||||
end
|
||||
return r
|
||||
@ -2812,15 +2821,6 @@ function dcsCommon.isTroopCarrier(theUnit, carriers)
|
||||
return dcsCommon.isTroopCarrierType(uType, carriers)
|
||||
end
|
||||
|
||||
function dcsCommon.isPlayerUnit(theUnit)
|
||||
-- new patch. simply check if getPlayerName returns something
|
||||
if not theUnit then return false end
|
||||
if not Unit.isExist(theUnit) then return end
|
||||
if not theUnit.getPlayerName then return false end -- map/static object
|
||||
local pName = theUnit:getPlayerName()
|
||||
if pName then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
local apu = {}
|
||||
@ -3360,6 +3360,14 @@ function dcsCommon.spellString(inString)
|
||||
return res
|
||||
end
|
||||
|
||||
dcsCommon.letters = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||
"O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", }
|
||||
function dcsCommon.randomLetter(lowercase)
|
||||
local theLetter = dcsCommon.pickRandom(dcsCommon.letters)
|
||||
if lowercase then theLetter = string.lower(theLetter) end
|
||||
return theLetter
|
||||
end
|
||||
|
||||
--
|
||||
-- RGBA from hex
|
||||
--
|
||||
@ -3401,6 +3409,55 @@ function dcsCommon.playerName2Coalition(playerName)
|
||||
return 0
|
||||
end
|
||||
|
||||
function dcsCommon.isPlayerUnit(theUnit)
|
||||
-- new patch. simply check if getPlayerName returns something
|
||||
if not theUnit then return false end
|
||||
if not Unit.isExist(theUnit) then return end
|
||||
if not theUnit.getPlayerName then return false end -- map/static object
|
||||
local pName = theUnit:getPlayerName()
|
||||
if pName then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
function dcsCommon.getPlayerUnit(name)
|
||||
for coa = 1, 2 do
|
||||
local players = coalition.getPlayers(coa)
|
||||
for idx, theUnit in pairs(players) do
|
||||
if theUnit:getPlayerName() == name then return theUnit end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--
|
||||
-- theater and theater-related stuff
|
||||
--
|
||||
function dcsCommon.getMapName()
|
||||
return env.mission.theatre
|
||||
end
|
||||
|
||||
dcsCommon.magDecls = {Caucasus = 6.5,
|
||||
MarianaIslands = 1,
|
||||
Nevada = 12,
|
||||
PersianGulf = 2,
|
||||
Syria = 4,
|
||||
Normandy = -12 -- 1944, -1 in 2016
|
||||
-- SinaiMap still missing
|
||||
-- Falklands still missing, big differences
|
||||
}
|
||||
|
||||
function dcsCommon.getMagDeclForPoint(point)
|
||||
-- WARNING! Approximations only, map-wide, not adjusted for year nor location!
|
||||
-- serves as a stub for the day when DCS provides correct info
|
||||
local map = dcsCommon.getMapName()
|
||||
local decl = dcsCommon.magDecls[map]
|
||||
if not decl then
|
||||
trigger.action.outText("+++dcsC: unknown map <" .. map .. ">, using dclenation 0", 30)
|
||||
decl = 0
|
||||
end
|
||||
return decl
|
||||
end
|
||||
|
||||
--
|
||||
-- iterators
|
||||
--
|
||||
|
||||
430
modules/tacan.lua
Normal file
430
modules/tacan.lua
Normal file
@ -0,0 +1,430 @@
|
||||
tacan = {}
|
||||
tacan.version = "1.0.0"
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - initial version
|
||||
|
||||
--]]--
|
||||
tacan.verbose = false
|
||||
tacan.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
}
|
||||
tacan.tacanZones = {}
|
||||
|
||||
|
||||
function tacan.createTacanZone(theZone)
|
||||
theZone.onStart = cfxZones.getBoolFromZoneProperty(theZone, "onStart", true)
|
||||
local channels = cfxZones.getStringFromZoneProperty(theZone, "channel", "1")
|
||||
theZone.channels = cfxZones.numberArrayFromString(channels, 1)
|
||||
if theZone.verbose or tacan.verbose then
|
||||
trigger.action.outText("+++tcn: new tacan <" .. theZone.name .. "> for channels [" .. dcsCommon.array2string(theZone.channels, ", ") .. "]", 30)
|
||||
end
|
||||
|
||||
local mode = cfxZones.getStringFromZoneProperty(theZone, "mode", "X")
|
||||
mode = string.upper(mode)
|
||||
theZone.modes = cfxZones.flagArrayFromString(mode)
|
||||
if theZone.verbose or tacan.verbose then
|
||||
trigger.action.outText("+++tcn: modes [" .. dcsCommon.array2string(theZone.modes, ", ") .. "]", 30)
|
||||
end
|
||||
|
||||
theZone.coa = cfxZones.getCoalitionFromZoneProperty(theZone, "tacan", 0)
|
||||
|
||||
theZone.heading = cfxZones.getNumberFromZoneProperty(theZone, "heading", 0)
|
||||
theZone.heading = theZone.heading * 0.0174533 -- convert to rads
|
||||
local callsign = cfxZones.getStringFromZoneProperty(theZone, "callsign", "TXN")
|
||||
callsign = string.upper(callsign)
|
||||
theZone.callsigns = cfxZones.flagArrayFromString(callsign)
|
||||
if theZone.verbose or tacan.verbose then
|
||||
trigger.action.outText("+++tcn: callsigns [" .. dcsCommon.array2string(theZone.callsigns) .. "]", 30)
|
||||
end
|
||||
|
||||
theZone.rndLoc = cfxZones.getBoolFromZoneProperty(theZone, "rndLoc", false)
|
||||
-- theZone.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||
theZone.triggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
|
||||
if cfxZones.hasProperty(theZone, "deploy?") then
|
||||
theZone.deployFlag = cfxZones.getStringFromZoneProperty(theZone, "deploy?", "<none>")
|
||||
theZone.lastDeployFlagValue = cfxZones.getFlagValue(theZone.deployFlag, theZone)
|
||||
end
|
||||
if (not theZone.deployFlag) and (not theZone.onStart) then
|
||||
trigger.action.outText("+++tacan: WARNING: tacan zone <> is late activation and has no activation flag, will never activate.", 30)
|
||||
end
|
||||
|
||||
theZone.spawnedTACANS = {} -- for GC and List
|
||||
theZone.preWipe = cfxZones.getBoolFromZoneProperty(theZone, "preWipe", true)
|
||||
|
||||
if cfxZones.hasProperty(theZone, "destroy?") then
|
||||
theZone.destroyFlag = cfxZones.getStringFromZoneProperty(theZone, "destroy?", "<none>")
|
||||
theZone.lastDestroyFlagValue = cfxZones.getFlagValue(theZone.destroyFlag, theZone)
|
||||
end
|
||||
|
||||
if cfxZones.hasProperty(theZone, "c#") then
|
||||
theZone.channelOut = cfxZones.getStringFromZoneProperty(theZone, "C#", "<none>")
|
||||
end
|
||||
|
||||
theZone.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", false)
|
||||
|
||||
-- interface to groupTracker
|
||||
if cfxZones.hasProperty(theZone, "trackWith:") then
|
||||
theZone.trackWith = cfxZones.getStringFromZoneProperty(theZone, "trackWith:", "<None>")
|
||||
end
|
||||
|
||||
-- see if we need to deploy now
|
||||
if theZone.onStart then
|
||||
tacan.TacanFromZone(theZone, true) -- true = silent
|
||||
end
|
||||
end
|
||||
|
||||
-- hand off to tracker
|
||||
-- from cloneZones
|
||||
function tacan.handoffTracking(theGroup, theZone)
|
||||
if not groupTracker then
|
||||
trigger.action.outText("+++tacan: <" .. theZone.name .. "> attribute 'trackWith:' requires groupTracker module", 30)
|
||||
return
|
||||
end
|
||||
local trackerName = theZone.trackWith
|
||||
-- now assemble a list of all trackers
|
||||
if tacan.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++tacan: tacan tracked with: " .. trackerName, 30)
|
||||
end
|
||||
|
||||
local trackerNames = {}
|
||||
if dcsCommon.containsString(trackerName, ',') then
|
||||
trackerNames = dcsCommon.splitString(trackerName, ',')
|
||||
else
|
||||
table.insert(trackerNames, trackerName)
|
||||
end
|
||||
for idx, aTrk in pairs(trackerNames) do
|
||||
local theName = dcsCommon.trim(aTrk)
|
||||
if theName == "*" then theName = theZone.name end
|
||||
local theTracker = groupTracker.getTrackerByName(theName)
|
||||
if not theTracker then
|
||||
trigger.action.outText("+++tacan: <" .. theZone.name .. ">: cannot find tracker named <".. theName .. ">", 30)
|
||||
else
|
||||
groupTracker.addGroupToTracker(theGroup, theTracker)
|
||||
if tacan.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++tacan: added " .. theGroup:getName() .. " to tracker " .. theName, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- create a tacan
|
||||
function tacan.createTacanInZone(theZone, channel, mode, callsign)
|
||||
local point = cfxZones.getPoint(theZone)
|
||||
local name = theZone.name
|
||||
local heading = theZone.heading
|
||||
local unitID = dcsCommon.numberUUID()
|
||||
if theZone.rndLoc then
|
||||
point = cfxZones.createRandomPointInZone(theZone)
|
||||
end
|
||||
local data = tacan.buildTacanData(name, channel, mode, callsign, point, unitID, heading)
|
||||
if theZone.verbose or tacan.verbose then
|
||||
trigger.action.outText("+++tcn: new TACAN for <" .. theZone.name .. ">: ch<" .. channel .. ">, mode <" .. mode .. ">, call <" .. callsign .. ">", 30)
|
||||
end
|
||||
data.thePoint = point -- save location
|
||||
--local s = dcsCommon.dumpVar2Str("data", data)
|
||||
local coa = theZone.coa -- neutral
|
||||
local cty = dcsCommon.getACountryForCoalition(coa)
|
||||
local theCopy = dcsCommon.clone(data)
|
||||
local theGroup = coalition.addGroup(cty, Group.Category.GROUND, data)
|
||||
|
||||
-- handoff for tracking
|
||||
if theZone.trackWith then
|
||||
tacan.handoffTracking(theGroup, theZone)
|
||||
end
|
||||
|
||||
-- add to my spawns for GC to watch over
|
||||
local t = {}
|
||||
t.activeMode = mode
|
||||
t.activeCallsign = callsign
|
||||
t.activeChan = channel
|
||||
t.theGroup = theGroup
|
||||
t.theData = theCopy
|
||||
table.insert(theZone.spawnedTACANS, t)
|
||||
|
||||
-- run a GC cycle
|
||||
tacan.GC(true)
|
||||
return theGroup, theCopy
|
||||
end
|
||||
|
||||
function tacan.TacanFromZone(theZone, silent)
|
||||
local channel = tonumber(dcsCommon.pickRandom(theZone.channels))
|
||||
local mode = dcsCommon.pickRandom(theZone.modes)
|
||||
local callsign = dcsCommon.pickRandom(theZone.callsigns)
|
||||
|
||||
if theZone.preWipe and theZone.activeTacan then
|
||||
Group.destroy(theZone.activeTacan)
|
||||
theZone.activeTacan = nil
|
||||
end
|
||||
|
||||
local theGroup, data = tacan.createTacanInZone(theZone, channel, mode, callsign)
|
||||
theZone.activeTacan = theGroup
|
||||
theZone.activeChan = channel
|
||||
if theZone.channelOut then
|
||||
trigger.action.setUserFlag(theZone.channelOut, channel)
|
||||
end
|
||||
|
||||
theZone.activeMode = mode
|
||||
theZone.activeCallsign = callsign
|
||||
theZone.activeName = data.name
|
||||
if theGroup then
|
||||
if theZone.verbose or tacan.verbose then
|
||||
trigger.action.outText("+++tcn: created tacan <" .. data.name ..">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
if (not silent) and theZone.announcer then
|
||||
local str = "NOTAM: Deployed new TACAN " .. theZone.name .. " <" .. callsign .. ">, channel " .. channel .. mode .. ", active now"
|
||||
if theZone.coa == 0 then
|
||||
trigger.action.outText(str, 30)
|
||||
else
|
||||
trigger.action.outTextForCoalition(theZone.coa, str, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tacan.destroyTacan(theZone, announce)
|
||||
if theZone.activeTacan then
|
||||
Group.destroy(theZone.activeTacan) -- only destroys last allocated
|
||||
theZone.activeTacan = nil
|
||||
|
||||
if announce then
|
||||
local coa = theZone.coa
|
||||
local str = "NOTAM: TACAN " .. theZone.name .. " <" .. theZone.activeCallsign .. "> deactivated"
|
||||
if coa == 0 then
|
||||
trigger.action.outText(str, 30)
|
||||
else
|
||||
trigger.action.outTextForCoalition(coa, str, 30)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
if theZone.channelOut then
|
||||
trigger.action.setUserFlag(theZone.channelOut, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- create a TACAN group for the requested TACAN
|
||||
function tacan.buildTacanData(name, channel, mode, callsign, point, unitID, heading) -- point = (xyz)!
|
||||
if not heading then heading = 0 end
|
||||
if not mode then mode = "X" end
|
||||
mode = string.upper(mode)
|
||||
local x = point.x
|
||||
local y = point.z
|
||||
local alt = land.getHeight({x = x, y = y})
|
||||
local g = {} -- group
|
||||
g.name = name .. dcsCommon.numberUUID()
|
||||
g.x = x
|
||||
g.y = y
|
||||
g.tasks = {}
|
||||
g.task = "Ground Nothing"
|
||||
local r = {} -- group.route
|
||||
g.route = r
|
||||
local p = {} -- group.route.points
|
||||
r.points = p --
|
||||
local p1 = {} -- group.route.points[1]
|
||||
p[1] = p1
|
||||
p1.alt = alt + 3
|
||||
p1.x = x
|
||||
p1.y = y
|
||||
local t = {} -- group.route.points[1].task
|
||||
p1.task = t
|
||||
t.id = "ComboTask"
|
||||
local params = {} -- group.route.points[1].task.params
|
||||
t.params = params
|
||||
local tasks = {} -- group.route.points[1].task.params.tasks
|
||||
params.tasks = tasks
|
||||
local t1 = {} -- group.route.points[1].task.params.tasks[1]
|
||||
tasks[1] = t1
|
||||
|
||||
t1.enabled = true
|
||||
t1.auto = false
|
||||
t1.id = "WrappedAction"
|
||||
t1.number = 1
|
||||
local pm = {} -- group.route.points[1].task.params.tasks[1].params
|
||||
t1.params = pm
|
||||
local a = {} -- group.route.points[1].task.params.tasks[1].params.action
|
||||
pm.action = a
|
||||
a.id = "ActivateBeacon"
|
||||
local ps = {} -- group.route.points[1].task.params.tasks[1].params.action.params
|
||||
a.params = ps
|
||||
ps.type = 4
|
||||
ps.AA = false
|
||||
ps.unitID = unitID
|
||||
ps.modeChannel = mode
|
||||
ps.channel = channel
|
||||
ps.system = 18 -- mysterious
|
||||
ps.callsign = callsign
|
||||
ps.bearing = true
|
||||
ps.frequency = dcsCommon.tacan2freq(channel, mode)
|
||||
if tacan.verbose then
|
||||
trigger.action.outText("tacan channel <" .. channel .. "> = freq <" .. ps.frequency .. ">", 30)
|
||||
end
|
||||
|
||||
-- now build unit
|
||||
local u = {}
|
||||
g.units = u
|
||||
local u1 = {}
|
||||
u[1] = u1
|
||||
u1.skill = "High"
|
||||
u1.type = "TACAN_beacon"
|
||||
u1.x = x
|
||||
u1.y = y
|
||||
u1.name = "u_" .. g.name
|
||||
u1.unitId = unitID
|
||||
|
||||
return g -- return data block
|
||||
end
|
||||
|
||||
--
|
||||
-- Update
|
||||
--
|
||||
function tacan.update()
|
||||
timer.scheduleFunction(tacan.update, {}, timer.getTime() + 1)
|
||||
|
||||
for tName, theZone in pairs(tacan.tacanZones) do
|
||||
-- was start called?
|
||||
if cfxZones.testZoneFlag(theZone, theZone.deployFlag, theZone.triggerMethod, "lastDeployFlagValue") then
|
||||
-- we want to deploy and start the tacan.
|
||||
-- first test if one is still up and running
|
||||
if theZone.activeTacan and theZone.preWipe then
|
||||
tacan.destroyTacan(theZone, false)
|
||||
end
|
||||
tacan.TacanFromZone(theZone)
|
||||
end
|
||||
|
||||
if cfxZones.testZoneFlag(theZone, theZone.destroyFlag, theZone.triggerMethod, "lastDestroyFlagValue") then
|
||||
tacan.destroyTacan(theZone, theZone.announcer)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function tacan.GC(singleCall)
|
||||
if singleCall then
|
||||
if tacan.verbose then
|
||||
trigger.action.outText("+++tacan: single-pass GC invoked", 30)
|
||||
end
|
||||
else
|
||||
timer.scheduleFunction(tacan.update, nil, timer.getTime() + 60)
|
||||
end
|
||||
for tName, theZone in pairs(tacan.tacanZones) do
|
||||
local filteredTACANS = {}
|
||||
for idx, theActive in pairs(theZone.spawnedTACANS) do
|
||||
-- check if this tacan still exists
|
||||
local name = theActive.theData.name -- group name
|
||||
local theGroup = Group.getByName(name)
|
||||
if theGroup and Group.isExist(theGroup) then
|
||||
table.insert(filteredTACANS, theActive)
|
||||
else
|
||||
if tacan.verbose then
|
||||
trigger.action.outText("+++tacan: filtered <" .. name .. ">: no longer exist", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
theZone.spawnedTACANS = filteredTACANS
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- comms: List TACAN radio command
|
||||
--
|
||||
function tacan.installComms()
|
||||
tacan.redC = missionCommands.addCommandForCoalition(1, "Available TACAN stations", nil, tacan.listTacan, 1)
|
||||
tacan.blueC = missionCommands.addCommandForCoalition(2, "Available TACAN stations", nil, tacan.listTacan, 2)
|
||||
|
||||
end
|
||||
|
||||
function tacan.listTacan(side)
|
||||
timer.scheduleFunction(tacan.doListTacan, side, timer.getTime() + 0.1)
|
||||
end
|
||||
|
||||
function tacan.doListTacan(args)
|
||||
tacan.GC(true) -- force GC, once.
|
||||
|
||||
-- collect all neutral and same (as in args)-side tacans
|
||||
local theTs = {}
|
||||
for name, theZone in pairs(tacan.tacanZones) do
|
||||
if theZone.coa == 0 or theZone.coa == args then
|
||||
for idx, aTacan in pairs(theZone.spawnedTACANS) do
|
||||
table.insert(theTs, aTacan)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #theTs < 1 then
|
||||
trigger.action.outTextForCoalition(args, "No active TACAN.", 30)
|
||||
return
|
||||
end
|
||||
|
||||
local msg = "\nActive TACAN:"
|
||||
|
||||
for idx, aTacan in pairs(theTs) do
|
||||
msg = msg .. "\n - " .. aTacan.activeCallsign .. ": " .. aTacan.activeChan .. aTacan.activeMode
|
||||
end
|
||||
msg = msg .. "\n"
|
||||
trigger.action.outTextForCoalition(args, msg, 30)
|
||||
end
|
||||
|
||||
--
|
||||
-- Start up: config etc
|
||||
--
|
||||
|
||||
function tacan.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("tacanConfig")
|
||||
if not theZone then theZone = cfxZones.createSimpleZone("tacanConfig") end
|
||||
|
||||
tacan.verbose = theZone.verbose
|
||||
|
||||
tacan.list = cfxZones.getBoolFromZoneProperty(theZone, "list", false)
|
||||
if cfxZones.hasProperty(theZone, "GUI") then
|
||||
tacan.list = cfxZones.getBoolFromZoneProperty(theZone, "GUI", false)
|
||||
end
|
||||
|
||||
if tacan.verbose then
|
||||
trigger.action.outText("+++tcn: read config", 30)
|
||||
end
|
||||
end
|
||||
|
||||
function tacan.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx Tacan requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx TACAN",
|
||||
tacan.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
tacan.readConfigZone()
|
||||
|
||||
-- set comms
|
||||
if tacan.list then tacan.installComms() end
|
||||
|
||||
-- collect tacan zones
|
||||
local tZones = cfxZones.zonesWithProperty("tacan")
|
||||
for k, aZone in pairs(tZones) do
|
||||
tacan.createTacanZone(aZone)
|
||||
tacan.tacanZones[aZone.name] = aZone
|
||||
end
|
||||
|
||||
-- start update
|
||||
tacan.update()
|
||||
|
||||
-- start GC
|
||||
tacan.GC()
|
||||
|
||||
-- say Hi!
|
||||
trigger.action.outText("cfx Tacan v" .. tacan.version .. " started.",30)
|
||||
end
|
||||
|
||||
tacan.start()
|
||||
|
||||
--[[--
|
||||
Ideas
|
||||
- moving tacan, as in ndb
|
||||
--]]--
|
||||
BIN
tutorial & demo missions/demo - Take on TACAN.miz
Normal file
BIN
tutorial & demo missions/demo - Take on TACAN.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user