mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.4.1
Civ Air off-map locations Guardian Angel updates Recon Mode updates
This commit is contained in:
parent
3117bfd80c
commit
7c6deec7a6
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
cfxReconMode = {}
|
cfxReconMode = {}
|
||||||
cfxReconMode.version = "2.1.4"
|
cfxReconMode.version = "2.2.0"
|
||||||
cfxReconMode.verbose = false -- set to true for debug info
|
cfxReconMode.verbose = false -- set to true for debug info
|
||||||
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
||||||
|
|
||||||
@ -86,6 +86,10 @@ VERSION HISTORY
|
|||||||
2.1.3 - added cfxReconMode.name to allow direct acces with test zone flag
|
2.1.3 - added cfxReconMode.name to allow direct acces with test zone flag
|
||||||
2.1.4 - canDetect() also checks if unit has been activated
|
2.1.4 - canDetect() also checks if unit has been activated
|
||||||
canDetect has strenghtened isExist() guard
|
canDetect has strenghtened isExist() guard
|
||||||
|
2.2.0 - new marksLocked config attribute, defaults to false
|
||||||
|
- new marksFadeAfter config attribute to control mark time
|
||||||
|
- dmlZones OOP upgrade
|
||||||
|
|
||||||
|
|
||||||
cfxReconMode is a script that allows units to perform reconnaissance
|
cfxReconMode is a script that allows units to perform reconnaissance
|
||||||
missions and, after detecting units, marks them on the map with
|
missions and, after detecting units, marks them on the map with
|
||||||
@ -355,7 +359,7 @@ function cfxReconMode.placeMarkForUnit(location, theSide, theGroup)
|
|||||||
theDesc,
|
theDesc,
|
||||||
location,
|
location,
|
||||||
theSide,
|
theSide,
|
||||||
false,
|
cfxReconMode.marksLocked, -- readOnly -- false,
|
||||||
nil)
|
nil)
|
||||||
return theID
|
return theID
|
||||||
end
|
end
|
||||||
@ -492,7 +496,7 @@ function cfxReconMode.processZoneMessage(inMsg, theZone, theGroup)
|
|||||||
|
|
||||||
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
||||||
-- and <mgrs> with mgrs coords of zone point
|
-- and <mgrs> with mgrs coords of zone point
|
||||||
local currPoint = cfxZones.getPoint(theZone)
|
local currPoint = theZone:getPoint()
|
||||||
if theGroup and theGroup:isExist() then
|
if theGroup and theGroup:isExist() then
|
||||||
-- only use group's point when group exists and alive
|
-- only use group's point when group exists and alive
|
||||||
local theUnit = dcsCommon.getFirstLivingUnit(theGroup)
|
local theUnit = dcsCommon.getFirstLivingUnit(theGroup)
|
||||||
@ -529,7 +533,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- put a mark on the map
|
-- put a mark on the map
|
||||||
if not silent and cfxReconMode.applyMarks then
|
if (not silent) and cfxReconMode.applyMarks then
|
||||||
local theID = cfxReconMode.placeMarkForUnit(theLoc, mySide, theGroup)
|
local theID = cfxReconMode.placeMarkForUnit(theLoc, mySide, theGroup)
|
||||||
local gName = theGroup:getName()
|
local gName = theGroup:getName()
|
||||||
local args = {mySide, theScout, theGroup, theID, gName}
|
local args = {mySide, theScout, theGroup, theID, gName}
|
||||||
@ -541,10 +545,9 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- say something
|
-- say something
|
||||||
if not silent and cfxReconMode.announcer then
|
if (not silent) and cfxReconMode.announcer then
|
||||||
local msg = cfxReconMode.generateSALT(theScout, theGroup)
|
local msg = cfxReconMode.generateSALT(theScout, theGroup)
|
||||||
trigger.action.outTextForCoalition(mySide, msg, cfxReconMode.reportTime)
|
trigger.action.outTextForCoalition(mySide, msg, cfxReconMode.reportTime)
|
||||||
-- trigger.action.outTextForCoalition(mySide, theScout:getName() .. " reports new ground contact " .. theGroup:getName(), 30)
|
|
||||||
if cfxReconMode.verbose then
|
if cfxReconMode.verbose then
|
||||||
trigger.action.outText("+++rcn: announced for side " .. mySide, 30)
|
trigger.action.outText("+++rcn: announced for side " .. mySide, 30)
|
||||||
end
|
end
|
||||||
@ -554,7 +557,6 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- see if it was a prio target
|
-- see if it was a prio target
|
||||||
--local inList, gName = cfxReconMode.isStringInList(theGroup:getName(), cfxReconMode.prioList)
|
|
||||||
if inList then
|
if inList then
|
||||||
-- if cfxReconMode.announcer then
|
-- if cfxReconMode.announcer then
|
||||||
if cfxReconMode.verbose then
|
if cfxReconMode.verbose then
|
||||||
@ -565,7 +567,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
|
|
||||||
-- increase prio flag
|
-- increase prio flag
|
||||||
if cfxReconMode.prioFlag then
|
if cfxReconMode.prioFlag then
|
||||||
cfxZones.pollFlag(cfxReconMode.prioFlag, cfxReconMode.method, cfxReconMode.theZone)
|
cfxReconMode.theZone:pollFlag(cfxReconMode.prioFlag, cfxReconMode.method )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if we were passed additional info in zInfo
|
-- see if we were passed additional info in zInfo
|
||||||
@ -583,7 +585,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if zInfo.theFlag then
|
if zInfo.theFlag then
|
||||||
cfxZones.pollFlag(zInfo.theFlag, cfxReconMode.method, zInfo.theZone)
|
zInfo.theZone:pollFlag(zInfo.theFlag, cfxReconMode.method)
|
||||||
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
||||||
trigger.action.outText("+++rcn: banging <" .. zInfo.theFlag .. "> for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
trigger.action.outText("+++rcn: banging <" .. zInfo.theFlag .. "> for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
||||||
end
|
end
|
||||||
@ -595,7 +597,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
|
|
||||||
-- increase normal flag
|
-- increase normal flag
|
||||||
if cfxReconMode.detectFlag then
|
if cfxReconMode.detectFlag then
|
||||||
cfxZones.pollFlag(cfxReconMode.detectFlag, cfxReconMode.method, cfxReconMode.theZone)
|
cfxReconMode.theZone:pollFlag(cfxReconMode.detectFlag, cfxReconMode.method)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -655,7 +657,7 @@ function cfxReconMode.doDeActivate()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconMode.updateQueues()
|
function cfxReconSMode.updateQueues()
|
||||||
-- schedule next call
|
-- schedule next call
|
||||||
timer.scheduleFunction(cfxReconMode.updateQueues, {}, timer.getTime() + 1/cfxReconMode.ups)
|
timer.scheduleFunction(cfxReconMode.updateQueues, {}, timer.getTime() + 1/cfxReconMode.ups)
|
||||||
|
|
||||||
@ -681,7 +683,6 @@ function cfxReconMode.updateQueues()
|
|||||||
-- the scouts array, move it to processed and then shrink
|
-- the scouts array, move it to processed and then shrink
|
||||||
-- scouts table until it's empty. When empty, transfer all
|
-- scouts table until it's empty. When empty, transfer all
|
||||||
-- back and start cycle anew
|
-- back and start cycle anew
|
||||||
|
|
||||||
local theFocusScoutName = nil
|
local theFocusScoutName = nil
|
||||||
local procCount = 0 -- no iterations done yet
|
local procCount = 0 -- no iterations done yet
|
||||||
for name, scout in pairs(cfxReconMode.scouts) do
|
for name, scout in pairs(cfxReconMode.scouts) do
|
||||||
@ -736,7 +737,6 @@ function cfxReconMode.autoRemove()
|
|||||||
local toRemove = {}
|
local toRemove = {}
|
||||||
-- scan all marked groups, and when they no longer exist, remove them
|
-- scan all marked groups, and when they no longer exist, remove them
|
||||||
for idx, args in pairs (cfxReconMode.activeMarks) do
|
for idx, args in pairs (cfxReconMode.activeMarks) do
|
||||||
-- args = {mySide, theScout, theGroup, theID, gName}
|
|
||||||
local gName = args[5]
|
local gName = args[5]
|
||||||
if not cfxReconMode.isGroupStillAlive(gName) then
|
if not cfxReconMode.isGroupStillAlive(gName) then
|
||||||
-- remove mark, remove group from set
|
-- remove mark, remove group from set
|
||||||
@ -766,7 +766,7 @@ function cfxReconMode.lateEvalPlayerUnit(theUnit)
|
|||||||
for idx, theZone in pairs (cfxReconMode.scoutZones) do
|
for idx, theZone in pairs (cfxReconMode.scoutZones) do
|
||||||
local isScout = theZone.isScout
|
local isScout = theZone.isScout
|
||||||
local dynamic = theZone.dynamic
|
local dynamic = theZone.dynamic
|
||||||
local inZone = cfxZones.pointInZone(p, theZone)
|
local inZone = theZone:pointInZone(p)
|
||||||
if inZone then
|
if inZone then
|
||||||
if isScout then
|
if isScout then
|
||||||
cfxReconMode.addToAllowedScoutList(aGroup, dynamic)
|
cfxReconMode.addToAllowedScoutList(aGroup, dynamic)
|
||||||
@ -886,7 +886,6 @@ function cfxReconMode:onEvent(event)
|
|||||||
end
|
end
|
||||||
cfxReconMode.addScout(theUnit)
|
cfxReconMode.addScout(theUnit)
|
||||||
end
|
end
|
||||||
-- trigger.action.outText("+++rcn-onEvent: " .. event.id .. " for <" .. theUnit:getName() .. ">", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -984,68 +983,70 @@ function cfxReconMode.readConfigZone()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
cfxReconMode.verbose = theZone.verbose --cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
|
|
||||||
cfxReconMode.autoRecon = cfxZones.getBoolFromZoneProperty(theZone, "autoRecon", true)
|
cfxReconMode.autoRecon = theZone:getBoolFromZoneProperty("autoRecon", true)
|
||||||
cfxReconMode.redScouts = cfxZones.getBoolFromZoneProperty(theZone, "redScouts", false)
|
cfxReconMode.redScouts = theZone:getBoolFromZoneProperty("redScouts", false)
|
||||||
cfxReconMode.blueScouts = cfxZones.getBoolFromZoneProperty(theZone, "blueScouts", true)
|
cfxReconMode.blueScouts = theZone:getBoolFromZoneProperty( "blueScouts", true)
|
||||||
cfxReconMode.greyScouts = cfxZones.getBoolFromZoneProperty(theZone, "greyScouts", false)
|
cfxReconMode.greyScouts = theZone:getBoolFromZoneProperty( "greyScouts", false)
|
||||||
cfxReconMode.playerOnlyRecon = cfxZones.getBoolFromZoneProperty(theZone, "playerOnlyRecon", false)
|
cfxReconMode.playerOnlyRecon = theZone:getBoolFromZoneProperty("playerOnlyRecon", false)
|
||||||
cfxReconMode.reportNumbers = cfxZones.getBoolFromZoneProperty(theZone, "reportNumbers", true)
|
cfxReconMode.reportNumbers = theZone:getBoolFromZoneProperty( "reportNumbers", true)
|
||||||
cfxReconMode.reportTime = cfxZones.getNumberFromZoneProperty(theZone, "reportTime", 30)
|
cfxReconMode.reportTime = theZone:getNumberFromZoneProperty( "reportTime", 30)
|
||||||
|
|
||||||
cfxReconMode.detectionMinRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMinRange", 3000)
|
cfxReconMode.detectionMinRange = theZone:getNumberFromZoneProperty("detectionMinRange", 3000)
|
||||||
cfxReconMode.detectionMaxRange = cfxZones.getNumberFromZoneProperty(theZone, "detectionMaxRange", 12000)
|
cfxReconMode.detectionMaxRange = theZone:getNumberFromZoneProperty("detectionMaxRange", 12000)
|
||||||
cfxReconMode.maxAlt = cfxZones.getNumberFromZoneProperty(theZone, "maxAlt", 9000)
|
cfxReconMode.maxAlt = theZone:getNumberFromZoneProperty("maxAlt", 9000)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "prio+") then
|
if theZone:hasProperty("prio+") then -- deprecated. remove next update
|
||||||
cfxReconMode.prioFlag = cfxZones.getStringFromZoneProperty(theZone, "prio+", "none")
|
cfxReconMode.prioFlag = theZone:getStringFromZoneProperty("prio+", "none")
|
||||||
elseif cfxZones.hasProperty(theZone, "prio!") then
|
elseif theZone:hasProperty("prio!") then
|
||||||
cfxReconMode.prioFlag = cfxZones.getStringFromZoneProperty(theZone, "prio!", "*<none>")
|
cfxReconMode.prioFlag = theZone:getStringFromZoneProperty("prio!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "detect+") then
|
if theZone:hasProperty("detect+") then -- deprecated
|
||||||
cfxReconMode.detectFlag = cfxZones.getStringFromZoneProperty(theZone, "detect+", "none")
|
cfxReconMode.detectFlag = theZone:getStringFromZoneProperty("detect+", "none")
|
||||||
elseif cfxZones.hasProperty(theZone, "detect!") then
|
elseif theZone:hasProperty("detect!") then
|
||||||
cfxReconMode.detectFlag = cfxZones.getStringFromZoneProperty(theZone, "detect!", "*<none>")
|
cfxReconMode.detectFlag = theZone:getStringFromZoneProperty("detect!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
cfxReconMode.method = theZone:getStringFromZoneProperty("method", "inc")
|
||||||
if cfxZones.hasProperty(theZone, "reconMethod") then
|
if theZone:hasProperty("reconMethod") then
|
||||||
cfxReconMode.method = cfxZones.getStringFromZoneProperty(theZone, "reconMethod", "inc")
|
cfxReconMode.method = theZone:getStringFromZoneProperty("reconMethod", "inc")
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.applyMarks = cfxZones.getBoolFromZoneProperty(theZone, "applyMarks", true)
|
cfxReconMode.applyMarks = theZone:getBoolFromZoneProperty( "applyMarks", true)
|
||||||
cfxReconMode.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
cfxReconMode.marksFadeAfter = theZone:getNumberFromZoneProperty("marksFadeAfter", 30*60) -- 30 minutes default
|
||||||
-- trigger.action.outText("recon: announcer is " .. dcsCommon.bool2Text(cfxReconMode.announcer), 30) -- announced
|
cfxReconMode.marksLocked = theZone:getBoolFromZoneProperty("marksLocked", false) -- if true, players cannot remove the marks
|
||||||
if cfxZones.hasProperty(theZone, "reconSound") then
|
cfxReconMode.announcer = theZone:getBoolFromZoneProperty( "announcer", true)
|
||||||
cfxReconMode.reconSound = cfxZones.getStringFromZoneProperty(theZone, "reconSound", "<nosound>")
|
|
||||||
|
if theZone:hasProperty("reconSound") then
|
||||||
|
cfxReconMode.reconSound = theZone:getStringFromZoneProperty("reconSound", "<nosound>")
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.removeWhenDestroyed = cfxZones.getBoolFromZoneProperty(theZone, "autoRemove", true)
|
cfxReconMode.removeWhenDestroyed = theZone:getBoolFromZoneProperty("autoRemove", true)
|
||||||
|
|
||||||
cfxReconMode.mgrs = cfxZones.getBoolFromZoneProperty(theZone, "mgrs", false)
|
cfxReconMode.mgrs = theZone:getBoolFromZoneProperty("mgrs", false)
|
||||||
|
|
||||||
cfxReconMode.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
cfxReconMode.active = theZone:getBoolFromZoneProperty("active", true)
|
||||||
if cfxZones.hasProperty(theZone, "activate?") then
|
if theZone:hasProperty("activate?") then
|
||||||
cfxReconMode.activate = cfxZones.getStringFromZoneProperty(theZone, "activate?", "*<none>")
|
cfxReconMode.activate = theZone:getStringFromZoneProperty("activate?", "*<none>")
|
||||||
cfxReconMode.lastActivate = cfxZones.getFlagValue(cfxReconMode.activate, theZone)
|
cfxReconMode.lastActivate = theZone:getFlagValue(cfxReconMode.activate)
|
||||||
elseif cfxZones.hasProperty(theZone, "on?") then
|
elseif theZone:hasProperty("on?") then
|
||||||
cfxReconMode.activate = cfxZones.getStringFromZoneProperty(theZone, "on?", "*<none>")
|
cfxReconMode.activate = theZone:getStringFromZoneProperty("on?", "*<none>")
|
||||||
cfxReconMode.lastActivate = cfxZones.getFlagValue(cfxReconMode.activate, theZone)
|
cfxReconMode.lastActivate = theZone:getFlagValue(cfxReconMode.activate)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "deactivate?") then
|
if theZone:hasProperty("deactivate?") then
|
||||||
cfxReconMode.deactivate = cfxZones.getStringFromZoneProperty(theZone, "deactivate?", "*<none>")
|
cfxReconMode.deactivate = theZone:getStringFromZoneProperty("deactivate?", "*<none>")
|
||||||
cfxReconMode.lastDeActivate = cfxZones.getFlagValue(cfxReconMode.deactivate, theZone)
|
cfxReconMode.lastDeActivate = theZone:getFlagValue(cfxReconMode.deactivate)
|
||||||
elseif cfxZones.hasProperty(theZone, "off?") then
|
elseif theZone:hasProperty("off?") then
|
||||||
cfxReconMode.deactivate = cfxZones.getStringFromZoneProperty(theZone, "off?", "*<none>")
|
cfxReconMode.deactivate = theZone:getStringFromZoneProperty("off?", "*<none>")
|
||||||
cfxReconMode.lastDeActivate = cfxZones.getFlagValue(cfxReconMode.deactivate, theZone)
|
cfxReconMode.lastDeActivate = theZone:getFlagValue(cfxReconMode.deactivate)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.imperialUnits = cfxZones.getBoolFromZoneProperty(theZone, "imperial", false)
|
cfxReconMode.imperialUnits = theZone:getBoolFromZoneProperty("imperial", false)
|
||||||
if cfxZones.hasProperty(theZone, "imperialUnits") then
|
if theZone:hasProperty("imperialUnits") then
|
||||||
cfxReconMode.imperialUnits = cfxZones.getBoolFromZoneProperty(theZone, "imperialUnits", false)
|
cfxReconMode.imperialUnits = theZone:getBoolFromZoneProperty( "imperialUnits", false)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfxReconMode.theZone = theZone -- save this zone
|
cfxReconMode.theZone = theZone -- save this zone
|
||||||
@ -1057,24 +1058,24 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function cfxReconMode.processReconZone(theZone)
|
function cfxReconMode.processReconZone(theZone)
|
||||||
local theList = cfxZones.getStringFromZoneProperty(theZone, "recon", "prio")
|
local theList = theZone:getStringFromZoneProperty("recon", "prio")
|
||||||
theList = string.upper(theList)
|
theList = string.upper(theList)
|
||||||
local isBlack = dcsCommon.stringStartsWith(theList, "BLACK")
|
local isBlack = dcsCommon.stringStartsWith(theList, "BLACK")
|
||||||
|
|
||||||
local zInfo = {}
|
local zInfo = {}
|
||||||
zInfo.theZone = theZone
|
zInfo.theZone = theZone
|
||||||
zInfo.isBlack = isBlack
|
zInfo.isBlack = isBlack
|
||||||
zInfo.silent = cfxZones.getBoolFromZoneProperty(theZone, "silent", false)
|
zInfo.silent = theZone:getBoolFromZoneProperty("silent", false)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "spotted!") then
|
if theZone:hasProperty("spotted!") then
|
||||||
zInfo.theFlag = cfxZones.getStringFromZoneProperty(theZone, "spotted!", "*<none>")
|
zInfo.theFlag = theZone:getStringFromZoneProperty("spotted!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "prioMessage") then
|
if theZone:hasProperty("prioMessage") then
|
||||||
zInfo.prioMessage = cfxZones.getStringFromZoneProperty(theZone, "prioMessage", "<none>")
|
zInfo.prioMessage = theZone:getStringFromZoneProperty("prioMessage", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
local dynamic = cfxZones.getBoolFromZoneProperty(theZone, "dynamic", false)
|
local dynamic = theZone:getBoolFromZoneProperty("dynamic", false)
|
||||||
zInfo.dynamic = dynamic
|
zInfo.dynamic = dynamic
|
||||||
local categ = 2 -- ground troops only
|
local categ = 2 -- ground troops only
|
||||||
local allGroups = cfxZones.allGroupsInZone(theZone, categ)
|
local allGroups = cfxZones.allGroupsInZone(theZone, categ)
|
||||||
@ -1100,15 +1101,15 @@ function cfxReconMode.processReconZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconMode.processScoutZone(theZone)
|
function cfxReconMode.processScoutZone(theZone)
|
||||||
local isScout = cfxZones.getBoolFromZoneProperty(theZone, "scout", true)
|
local isScout = theZone:getBoolFromZoneProperty("scout", true)
|
||||||
local dynamic = cfxZones.getBoolFromZoneProperty(theZone, "dynamic")
|
local dynamic = theZone:getBoolFromZoneProperty("dynamic")
|
||||||
theZone.dynamic = dynamic
|
theZone.dynamic = dynamic
|
||||||
theZone.isScout = isScout
|
theZone.isScout = isScout
|
||||||
|
|
||||||
local categ = 0 -- aircraft
|
local categ = 0 -- aircraft
|
||||||
local allFixed = cfxZones.allGroupsInZone(theZone, categ)
|
local allFixed = theZone:allGroupsInZone(categ)
|
||||||
local categ = 1 -- helos
|
local categ = 1 -- helos
|
||||||
local allRotor = cfxZones.allGroupsInZone(theZone, categ)
|
local allRotor = theZone:allGroupsInZone(categ)
|
||||||
local allGroups = dcsCommon.combineTables(allFixed, allRotor)
|
local allGroups = dcsCommon.combineTables(allFixed, allRotor)
|
||||||
for idx, aGroup in pairs(allGroups) do
|
for idx, aGroup in pairs(allGroups) do
|
||||||
if isScout then
|
if isScout then
|
||||||
|
|||||||
@ -149,6 +149,7 @@ cfxZones.version = "4.0.0"
|
|||||||
- getNumberFromZoneProperty() enforces number return even on default
|
- getNumberFromZoneProperty() enforces number return even on default
|
||||||
- immediate method switched to preceeding '#', to resolve conflict witzh
|
- immediate method switched to preceeding '#', to resolve conflict witzh
|
||||||
negative numbers, backwards compatibility with old (dysfunctional) method
|
negative numbers, backwards compatibility with old (dysfunctional) method
|
||||||
|
- 4.0.1 - dmlZone:getName()
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -3258,6 +3259,10 @@ function dmlZone:getPoint(getHeight)
|
|||||||
return thePos
|
return thePos
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dmlZone:getName() -- no cfxZones.bridge!
|
||||||
|
return self.name
|
||||||
|
end
|
||||||
|
|
||||||
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
function cfxZones.linkUnitToZone(theUnit, theZone, dx, dy) -- note: dy is really Z, don't get confused!!!!
|
||||||
theZone.linkedUnit = theUnit
|
theZone.linkedUnit = theUnit
|
||||||
if not dx then dx = 0 end
|
if not dx then dx = 0 end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
civAir = {}
|
civAir = {}
|
||||||
civAir.version = "1.5.2"
|
civAir.version = "2.0.0"
|
||||||
--[[--
|
--[[--
|
||||||
1.0.0 initial version
|
1.0.0 initial version
|
||||||
1.1.0 exclude list for airfields
|
1.1.0 exclude list for airfields
|
||||||
@ -23,7 +23,15 @@ civAir.version = "1.5.2"
|
|||||||
exclude list and include list
|
exclude list and include list
|
||||||
1.5.1 added depart only and arrive only options for airfields
|
1.5.1 added depart only and arrive only options for airfields
|
||||||
1.5.2 fixed bugs inb verbosity
|
1.5.2 fixed bugs inb verbosity
|
||||||
|
2.0.0 dmlZones
|
||||||
|
inbound zones
|
||||||
|
outbound zones
|
||||||
|
on start location is randomizes 30-70% of the way
|
||||||
|
guarded 'no longer exist' warning for verbosity
|
||||||
|
changed unit naming from -civA to -GA
|
||||||
|
strenghtened guard on testing against free slots for other units
|
||||||
|
flights are now of random neutral countries
|
||||||
|
maxFlights synonym for maxTraffic
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -48,22 +56,22 @@ civAir.trafficCenters = {}
|
|||||||
-- If the attribute's value is anything
|
-- If the attribute's value is anything
|
||||||
-- but "exclude", the closest airfield to the zone
|
-- but "exclude", the closest airfield to the zone
|
||||||
-- is added to trafficCenters
|
-- is added to trafficCenters
|
||||||
|
|
||||||
-- if you leave this list empty, and do not add airfields
|
-- if you leave this list empty, and do not add airfields
|
||||||
-- by zones, the list is automatically populated with all
|
-- by zones, the list is automatically populated with all
|
||||||
-- airfields in the map
|
-- airfields in the map
|
||||||
|
-- if name starts with "***" then it is not an airfield, but zone
|
||||||
|
|
||||||
civAir.excludeAirfields = {}
|
civAir.excludeAirfields = {}
|
||||||
-- list all airfields that must NOT be included in
|
-- list all airfields that must NOT be included in
|
||||||
-- civilian activities. Will be used for neither landing
|
-- civilian activities. Will be used for neither landing
|
||||||
-- nor departure. overrides any airfield that was included
|
-- nor departure. overrides any airfield that was included
|
||||||
-- in trafficCenters. Here, Senaki is off limits for
|
-- in trafficCenters.
|
||||||
-- civilian air traffic
|
|
||||||
-- can be populated by zone on the map that have the
|
-- can be populated by zone on the map that have the
|
||||||
-- 'civAir' attribute with value "exclude"
|
-- 'civAir' attribute with value "exclude"
|
||||||
|
|
||||||
civAir.departOnly = {} -- use only to start from
|
civAir.departOnly = {} -- use only to start from
|
||||||
civAir.landingOnly = {} -- use only to land at
|
civAir.landingOnly = {} -- use only to land at
|
||||||
|
civAir.inoutZones = {} -- off-map connector zones
|
||||||
|
|
||||||
civAir.requiredLibs = {
|
civAir.requiredLibs = {
|
||||||
"dcsCommon", -- common is of course needed for everything
|
"dcsCommon", -- common is of course needed for everything
|
||||||
@ -72,57 +80,73 @@ civAir.requiredLibs = {
|
|||||||
|
|
||||||
civAir.activePlanes = {}
|
civAir.activePlanes = {}
|
||||||
civAir.idlePlanes = {}
|
civAir.idlePlanes = {}
|
||||||
|
civAir.outboundFlights = {} -- only flights that are enroute to an outbound zone
|
||||||
|
|
||||||
function civAir.readConfigZone()
|
function civAir.readConfigZone()
|
||||||
-- note: must match exactly!!!!
|
-- note: must match exactly!!!!
|
||||||
local theZone = cfxZones.getZoneByName("civAirConfig")
|
local theZone = cfxZones.getZoneByName("civAirConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
trigger.action.outText("***civA: NO config zone!", 30)
|
trigger.action.outText("***civA: NO config zone!", 30)
|
||||||
return
|
theZone = cfxZones.createSimpleZone("civAirConfig")
|
||||||
end
|
end
|
||||||
|
|
||||||
trigger.action.outText("civA: found config zone!", 30)
|
|
||||||
|
|
||||||
-- ok, for each property, load it if it exists
|
-- ok, for each property, load it if it exists
|
||||||
if cfxZones.hasProperty(theZone, "aircraftTypes") then
|
if theZone:hasProperty("aircraftTypes") then
|
||||||
local theTypes = cfxZones.getStringFromZoneProperty(theZone, "aircraftTypes", "Yak-40")
|
local theTypes = theZone:getStringFromZoneProperty( "aircraftTypes", civAir.aircraftTypes) -- "Yak-40")
|
||||||
local typeArray = dcsCommon.splitString(theTypes, ",")
|
local typeArray = dcsCommon.splitString(theTypes, ",")
|
||||||
typeArray = dcsCommon.trimArray(typeArray)
|
typeArray = dcsCommon.trimArray(typeArray)
|
||||||
civAir.aircraftTypes = typeArray
|
civAir.aircraftTypes = typeArray
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "ups") then
|
if theZone:hasProperty("ups") then
|
||||||
civAir.ups = cfxZones.getNumberFromZoneProperty(theZone, "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 cfxZones.hasProperty(theZone, "maxTraffic") then
|
if theZone:hasProperty("maxTraffic") then
|
||||||
civAir.maxTraffic = cfxZones.getNumberFromZoneProperty(theZone, "maxTraffic", 10)
|
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxTraffic", 10)
|
||||||
|
elseif theZone:hasProperty("maxFlights") then
|
||||||
|
civAir.maxTraffic = theZone:getNumberFromZoneProperty( "maxFlights", 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "maxIdle") then
|
if theZone:hasProperty("maxIdle") then
|
||||||
civAir.maxIdle = cfxZones.getNumberFromZoneProperty(theZone, "maxIdle", 8 * 60)
|
civAir.maxIdle = theZone:getNumberFromZoneProperty("maxIdle", 8 * 60)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "initialAirSpawns") then
|
if theZone:hasProperty("initialAirSpawns") then
|
||||||
civAir.initialAirSpawns = cfxZones.getBoolFromZoneProperty(theZone, "initialAirSpawns", true)
|
civAir.initialAirSpawns = theZone:getBoolFromZoneProperty( "initialAirSpawns", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
civAir.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
civAir.verbose = theZone.verbose -- cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
end
|
end
|
||||||
|
|
||||||
function civAir.processZone(theZone)
|
function civAir.processZone(theZone)
|
||||||
local value = cfxZones.getStringFromZoneProperty(theZone, "civAir", "")
|
local value = theZone:getStringFromZoneProperty("civAir", "")
|
||||||
local af = dcsCommon.getClosestAirbaseTo(theZone.point, 0) -- 0 = only airfields, not farp or ships
|
local af = dcsCommon.getClosestAirbaseTo(theZone.point, 0) -- 0 = only airfields, not farp or ships
|
||||||
|
local inoutName = "***" .. theZone:getName()
|
||||||
|
|
||||||
if af then
|
if af then
|
||||||
local afName = af:getName()
|
local afName = af:getName()
|
||||||
value = value:lower()
|
value = value:lower()
|
||||||
if value == "exclude" then
|
if value == "exclude" or value == "closed" then
|
||||||
table.insert(civAir.excludeAirfields, afName)
|
table.insert(civAir.excludeAirfields, afName)
|
||||||
elseif dcsCommon.stringStartsWith(value, "depart") or dcsCommon.stringStartsWith(value, "start") then
|
elseif dcsCommon.stringStartsWith(value, "depart") or dcsCommon.stringStartsWith(value, "start") or dcsCommon.stringStartsWith(value, "take") then
|
||||||
table.insert(civAir.departOnly, afName)
|
table.insert(civAir.departOnly, afName)
|
||||||
elseif dcsCommon.stringStartsWith(value, "land") or dcsCommon.stringStartsWith(value, "arriv") then
|
elseif dcsCommon.stringStartsWith(value, "land") or dcsCommon.stringStartsWith(value, "arriv") then
|
||||||
table.insert(civAir.landingOnly, afName)
|
table.insert(civAir.landingOnly, afName)
|
||||||
|
elseif dcsCommon.stringStartsWith(value, "inb") then
|
||||||
|
table.insert(civAir.departOnly, inoutName) -- start in inbound zone
|
||||||
|
civAir.inoutZones[inoutName] = theZone
|
||||||
|
-- theZone.inbound = true
|
||||||
|
elseif dcsCommon.stringStartsWith(value, "outb") then
|
||||||
|
table.insert(civAir.landingOnly, inoutName)
|
||||||
|
civAir.inoutZones[inoutName] = theZone
|
||||||
|
-- theZone.outbound = true
|
||||||
|
elseif dcsCommon.stringStartsWith(value, "in/out") then
|
||||||
|
table.insert(civAir.trafficCenters, inoutName)
|
||||||
|
civAir.inoutZones[inoutName] = theZone
|
||||||
|
-- theZone.inbound = true
|
||||||
|
-- theZone.outbound = true
|
||||||
else
|
else
|
||||||
table.insert(civAir.trafficCenters, afName) -- note that adding the same twice makes it more likely to be picked
|
table.insert(civAir.trafficCenters, afName) -- note that adding the same twice makes it more likely to be picked
|
||||||
end
|
end
|
||||||
@ -142,10 +166,11 @@ function civAir.removePlaneGroupByName(aName)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
if civAir.activePlanes[aName] then
|
if civAir.activePlanes[aName] then
|
||||||
--trigger.action.outText("civA: REMOVING " .. aName .. " ***", 30)
|
|
||||||
civAir.activePlanes[aName] = nil
|
civAir.activePlanes[aName] = nil
|
||||||
else
|
else
|
||||||
trigger.action.outText("civA: warning - ".. aName .." remove req but not found", 30)
|
if civAir.verbose then
|
||||||
|
trigger.action.outText("civA: warning - ".. aName .." remove req but not found", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -207,10 +232,25 @@ function civAir.getTwoAirbases()
|
|||||||
tries = tries + 1 -- only try 10 times
|
tries = tries + 1 -- only try 10 times
|
||||||
until fAB ~= sAB or tries > 10
|
until fAB ~= sAB or tries > 10
|
||||||
|
|
||||||
fAB = dcsCommon.getFirstAirbaseWhoseNameContains(fAB, 0)
|
|
||||||
sAB = dcsCommon.getFirstAirbaseWhoseNameContains(sAB, 0)
|
local civA = {}
|
||||||
|
if not (dcsCommon.stringStartsWith(fAB, '***')) then
|
||||||
|
civA.AB = dcsCommon.getFirstAirbaseWhoseNameContains(fAB, 0)
|
||||||
|
civA.name = civA.AB:getName()
|
||||||
|
else
|
||||||
|
civA.zone = civAir.inoutZones[fAB]
|
||||||
|
civA.name = civA.zone:getName()
|
||||||
|
end
|
||||||
|
local civB = {}
|
||||||
|
if not (dcsCommon.stringStartsWith(sAB, '***')) then
|
||||||
|
civB.AB = dcsCommon.getFirstAirbaseWhoseNameContains(sAB, 0)
|
||||||
|
civB.name = civB.AB:getName()
|
||||||
|
else
|
||||||
|
civB.zone = civAir.inoutZones[sAB]
|
||||||
|
civB.name = civB.zone:getName()
|
||||||
|
end
|
||||||
|
|
||||||
return fAB, sAB
|
return civA, civB -- fAB, sAB
|
||||||
end
|
end
|
||||||
|
|
||||||
function civAir.parkingIsFree(fromWP)
|
function civAir.parkingIsFree(fromWP)
|
||||||
@ -222,9 +262,9 @@ function civAir.parkingIsFree(fromWP)
|
|||||||
loc.z = fromWP.z
|
loc.z = fromWP.z
|
||||||
|
|
||||||
for name, aPlaneGroup in pairs(civAir.activePlanes) do
|
for name, aPlaneGroup in pairs(civAir.activePlanes) do
|
||||||
if aPlaneGroup:isExist() then
|
if Group.isExist(aPlaneGroup) then
|
||||||
local aPlane = aPlaneGroup:getUnit(1)
|
local aPlane = aPlaneGroup:getUnit(1)
|
||||||
if aPlane:isExist() then
|
if aPlane and Unit.isExist(aPlane) then
|
||||||
pos = aPlane:getPoint()
|
pos = aPlane:getPoint()
|
||||||
local delta = dcsCommon.dist(loc, pos)
|
local delta = dcsCommon.dist(loc, pos)
|
||||||
if delta < 21 then
|
if delta < 21 then
|
||||||
@ -242,21 +282,35 @@ end
|
|||||||
civAir.airStartSeparation = 0
|
civAir.airStartSeparation = 0
|
||||||
function civAir.createFlight(name, theTypeString, fromAirfield, toAirfield, inAirStart)
|
function civAir.createFlight(name, theTypeString, fromAirfield, toAirfield, inAirStart)
|
||||||
if not fromAirfield then
|
if not fromAirfield then
|
||||||
trigger.action.outText("civA: NIL fromAirfield", 30)
|
trigger.action.outText("civA: NIL source", 30)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if not toAirfield then
|
if not toAirfield then
|
||||||
trigger.action.outText("civA: NIL toAirfield", 30)
|
trigger.action.outText("civA: NIL destination", 30)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local randomizeLoc = inAirStart
|
||||||
|
|
||||||
local theGroup = dcsCommon.createEmptyAircraftGroupData (name)
|
local theGroup = dcsCommon.createEmptyAircraftGroupData (name)
|
||||||
local theAUnit = dcsCommon.createAircraftUnitData(name .. "-civA", theTypeString, false)
|
local theAUnit = dcsCommon.createAircraftUnitData(name .. "-GA", theTypeString, false)
|
||||||
theAUnit.payload.fuel = 100000
|
theAUnit.payload.fuel = 100000
|
||||||
dcsCommon.addUnitToGroupData(theAUnit, theGroup)
|
dcsCommon.addUnitToGroupData(theAUnit, theGroup)
|
||||||
|
|
||||||
local fromWP = dcsCommon.createTakeOffFromParkingRoutePointData(fromAirfield)
|
local fromWP
|
||||||
|
if fromAirfield.AB then
|
||||||
|
fromWP = dcsCommon.createTakeOffFromParkingRoutePointData(fromAirfield.AB)
|
||||||
|
else
|
||||||
|
-- we start in air from inside inbound zone
|
||||||
|
local p = fromAirfield.zone:createRandomPointInZone()
|
||||||
|
local alt = fromAirfield.zone:getNumberFromZoneProperty("alt", 8000)
|
||||||
|
fromWP = dcsCommon.createSimpleRoutePointData(p, alt)
|
||||||
|
theAUnit.alt = fromWP.alt
|
||||||
|
theAUnit.speed = fromWP.speed
|
||||||
|
inAirStart = false -- it already is, no separation shenigans
|
||||||
|
end
|
||||||
|
|
||||||
if not fromWP then
|
if not fromWP then
|
||||||
trigger.action.outText("civA: fromWP create failed", 30)
|
trigger.action.outText("civA: fromWP create failed", 30)
|
||||||
return nil
|
return nil
|
||||||
@ -266,38 +320,81 @@ function civAir.createFlight(name, theTypeString, fromAirfield, toAirfield, inAi
|
|||||||
fromWP.alt = fromWP.alt + 3000 + civAir.airStartSeparation -- 9000 ft overhead + separation
|
fromWP.alt = fromWP.alt + 3000 + civAir.airStartSeparation -- 9000 ft overhead + separation
|
||||||
fromWP.action = "Turning Point"
|
fromWP.action = "Turning Point"
|
||||||
fromWP.type = "Turning Point"
|
fromWP.type = "Turning Point"
|
||||||
|
|
||||||
fromWP.speed = 150;
|
fromWP.speed = 150;
|
||||||
fromWP.airdromeId = nil
|
fromWP.airdromeId = nil
|
||||||
|
|
||||||
theAUnit.alt = fromWP.alt
|
theAUnit.alt = fromWP.alt
|
||||||
theAUnit.speed = fromWP.speed
|
theAUnit.speed = fromWP.speed
|
||||||
end
|
end
|
||||||
-- sometimes, when landing kicks in too early, the plane lands
|
|
||||||
-- at the wrong airfield. AI sucks.
|
|
||||||
-- so we force overflight of target airfield
|
|
||||||
local overheadWP = dcsCommon.createOverheadAirdromeRoutPintData(toAirfield)
|
|
||||||
local toWP = dcsCommon.createLandAtAerodromeRoutePointData(toAirfield)
|
|
||||||
if not toWP then
|
|
||||||
trigger.action.outText("civA: toWP create failed", 30)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if not civAir.parkingIsFree(fromWP) then
|
-- now look at destination: airfield or zone?
|
||||||
trigger.action.outText("civA: failed free parking check for flight " .. name, 30)
|
local zoneApproach = toAirfield.zone
|
||||||
return nil
|
local toWP
|
||||||
|
local overheadWP
|
||||||
|
if zoneApproach then
|
||||||
|
-- we fly this plane to a zone, and then disappear it
|
||||||
|
local p = zoneApproach:getPoint()
|
||||||
|
local alt = zoneApproach:getNumberFromZoneProperty("alt", 8000)
|
||||||
|
toWP = dcsCommon.createSimpleRoutePointData(p, alt)
|
||||||
|
else
|
||||||
|
-- sometimes, when landing kicks in too early, the plane lands
|
||||||
|
-- at the wrong airfield. AI sucks.
|
||||||
|
-- so we force overflight of target airfield
|
||||||
|
overheadWP = dcsCommon.createOverheadAirdromeRoutPintData(toAirfield.AB)
|
||||||
|
toWP = dcsCommon.createLandAtAerodromeRoutePointData(toAirfield.AB)
|
||||||
|
if not toWP then
|
||||||
|
trigger.action.outText("civA: toWP create failed", 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not civAir.parkingIsFree(fromWP) then
|
||||||
|
trigger.action.outText("civA: failed free parking check for flight " .. name, 30)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if randomizeLoc then
|
||||||
|
-- make first wp to somewhere 30-70 towards toWP
|
||||||
|
local percent = (math.random(40) + 30) / 100
|
||||||
|
local mx = dcsCommon.lerp(fromWP.x, toWP.x, percent)
|
||||||
|
local my = dcsCommon.lerp(fromWP.y, toWP.y, percent)
|
||||||
|
fromWP.x = mx
|
||||||
|
fromWP.y = my
|
||||||
|
fromWP.speed = 150
|
||||||
|
fromWP.alt = 8000
|
||||||
|
theAUnit.alt = fromWP.alt
|
||||||
|
theAUnit.speed = fromWP.speed
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not fromAirfield.AB) or randomizedLoc or inAirStart then
|
||||||
|
-- set current heading correct towards toWP
|
||||||
|
local hdg = dcsCommon.bearingFromAtoBusingXY(fromWP, toWP)
|
||||||
|
theAUnit.heading = hdg
|
||||||
|
theAUnit.psi = -hdg
|
||||||
end
|
end
|
||||||
|
|
||||||
dcsCommon.moveGroupDataTo(theGroup,
|
dcsCommon.moveGroupDataTo(theGroup,
|
||||||
fromWP.x,
|
fromWP.x,
|
||||||
fromWP.y)
|
fromWP.y)
|
||||||
dcsCommon.addRoutePointForGroupData(theGroup, fromWP)
|
dcsCommon.addRoutePointForGroupData(theGroup, fromWP)
|
||||||
dcsCommon.addRoutePointForGroupData(theGroup, overheadWP)
|
if not zoneApproach then
|
||||||
|
dcsCommon.addRoutePointForGroupData(theGroup, overheadWP)
|
||||||
|
end
|
||||||
dcsCommon.addRoutePointForGroupData(theGroup, toWP)
|
dcsCommon.addRoutePointForGroupData(theGroup, toWP)
|
||||||
|
|
||||||
-- spawn
|
-- spawn
|
||||||
local groupCat = Group.Category.AIRPLANE
|
local groupCat = Group.Category.AIRPLANE
|
||||||
local theSpawnedGroup = coalition.addGroup(82, groupCat, theGroup) -- 82 is UN peacekeepers
|
local allNeutral = dcsCommon.getCountriesForCoalition(0)
|
||||||
|
local aRandomNeutral = dcsCommon.pickRandom(allNeutral)
|
||||||
|
if not aRandomNeutral then
|
||||||
|
trigger.action.outText("+++civA: WARNING: no neutral countries exist, flight is not neutral.", 30)
|
||||||
|
end
|
||||||
|
local theSpawnedGroup = coalition.addGroup(aRandomNeutral, groupCat, theGroup) -- 82 is UN peacekeepers
|
||||||
|
if zoneApproach then
|
||||||
|
-- track this flight to target zone
|
||||||
|
civAir.outboundFlights[name] = zoneApproach
|
||||||
|
end
|
||||||
return theSpawnedGroup
|
return theSpawnedGroup
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -312,7 +409,9 @@ function civAir.createNewFlight(inAirStart)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local name = fAB:getName() .. "-" .. sAB:getName().. "/" .. civAir.flightCount
|
-- fAB and sAB are tables that have either .base or AB set
|
||||||
|
|
||||||
|
local name = fAB.name .. "-" .. sAB.name.. "/" .. civAir.flightCount
|
||||||
local TypeString = dcsCommon.pickRandom(civAir.aircraftTypes)
|
local TypeString = dcsCommon.pickRandom(civAir.aircraftTypes)
|
||||||
local theFlight = civAir.createFlight(name, TypeString, fAB, sAB, inAirStart)
|
local theFlight = civAir.createFlight(name, TypeString, fAB, sAB, inAirStart)
|
||||||
|
|
||||||
@ -325,7 +424,7 @@ function civAir.createNewFlight(inAirStart)
|
|||||||
civAir.addPlane(theFlight) -- track it
|
civAir.addPlane(theFlight) -- track it
|
||||||
|
|
||||||
if civAir.verbose then
|
if civAir.verbose then
|
||||||
trigger.action.outText("civA: created flight from <" .. fAB:getName() .. "> to <" .. sAB:getName() .. ">", 30)
|
trigger.action.outText("civA: created flight from <" .. fAB.name .. "> to <" .. sAB.name .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -340,9 +439,40 @@ function civAir.airStartPopulation()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- U P D A T E L O O P
|
-- U P D A T E L O O P S
|
||||||
--
|
--
|
||||||
|
|
||||||
|
function civAir.trackOutbound()
|
||||||
|
timer.scheduleFunction(civAir.trackOutbound, {}, timer.getTime() + 10)
|
||||||
|
|
||||||
|
-- iterate all flights that are outbound
|
||||||
|
local filtered = {}
|
||||||
|
for gName, theZone in pairs(civAir.outboundFlights) do
|
||||||
|
local theGroup = Group.getByName(gName)
|
||||||
|
if theGroup then
|
||||||
|
local theUnit = theGroup:getUnit(1)
|
||||||
|
if theUnit and Unit.isExist(theUnit) then
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
local t = theZone:getPoint()
|
||||||
|
local d = dcsCommon.distFlat(p, t)
|
||||||
|
if d > 3000 then -- works unless plane faster than 300m/s = 1080 km/h
|
||||||
|
-- keep watching
|
||||||
|
filtered[gName] = theZone
|
||||||
|
else
|
||||||
|
-- we can disappear the group
|
||||||
|
if civAir.verbose then
|
||||||
|
trigger.action.outText("+++civA: flight <" .. gName .. "> has reached map outbound zone <" .. theZone:getName() .. "> and is removed", 30)
|
||||||
|
end
|
||||||
|
Group.destroy(theGroup)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++civ: lost unit in group <" .. gName .. "> heading for <" .. theZone:getName() .. ">", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
civAir.outboundFlights = filtered
|
||||||
|
end
|
||||||
|
|
||||||
function civAir.update()
|
function civAir.update()
|
||||||
-- reschedule me in the future. ups = updates per second.
|
-- reschedule me in the future. ups = updates per second.
|
||||||
timer.scheduleFunction(civAir.update, {}, timer.getTime() + 1/civAir.ups)
|
timer.scheduleFunction(civAir.update, {}, timer.getTime() + 1/civAir.ups)
|
||||||
@ -359,12 +489,15 @@ function civAir.update()
|
|||||||
|
|
||||||
for idx, name in pairs(removeMe) do
|
for idx, name in pairs(removeMe) do
|
||||||
civAir.activePlanes[name] = nil
|
civAir.activePlanes[name] = nil
|
||||||
trigger.action.outText("civA: warning - removed " .. name .. " from active roster, no longer exists", 30)
|
if civAir.verbose then
|
||||||
|
trigger.action.outText("civA: removed " .. name .. " from active roster, no longer exists", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- now, run through all existing flights and update their
|
-- now, run through all existing flights and update their
|
||||||
-- idle times. also count how many planes there are
|
-- idle times. also count how many planes there are
|
||||||
|
-- so we can respawn if we are below max
|
||||||
local planeNum = 0
|
local planeNum = 0
|
||||||
local overduePlanes = {}
|
local overduePlanes = {}
|
||||||
local now = timer.getTime()
|
local now = timer.getTime()
|
||||||
@ -372,18 +505,17 @@ function civAir.update()
|
|||||||
local speed = 0
|
local speed = 0
|
||||||
if aPlaneGroup:isExist() then
|
if aPlaneGroup:isExist() then
|
||||||
local aPlane = aPlaneGroup:getUnit(1)
|
local aPlane = aPlaneGroup:getUnit(1)
|
||||||
|
if aPlane and Unit.isExist(aPlane) and aPlane:getLife() >= 1 then
|
||||||
if aPlane and aPlane:isExist() and aPlane:getLife() >= 1 then
|
|
||||||
planeNum = planeNum + 1
|
planeNum = planeNum + 1
|
||||||
local vel = aPlane:getVelocity()
|
local vel = aPlane:getVelocity()
|
||||||
speed = dcsCommon.mag(vel.x, vel.y, vel.z)
|
speed = dcsCommon.mag(vel.x, vel.y, vel.z)
|
||||||
else
|
else
|
||||||
-- force removal of group
|
-- force removal of group, plane no longer exists
|
||||||
civAir.idlePlanes[name] = -1000
|
civAir.idlePlanes[name] = -1000
|
||||||
speed = 0
|
speed = 0
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- force removal
|
-- force removal, group no longer exists
|
||||||
civAir.idlePlanes[name] = -1000
|
civAir.idlePlanes[name] = -1000
|
||||||
speed = 0
|
speed = 0
|
||||||
end
|
end
|
||||||
@ -398,10 +530,9 @@ function civAir.update()
|
|||||||
table.insert(overduePlanes, name)
|
table.insert(overduePlanes, name)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- zero out idle plane
|
-- zero out idle plane, it's moving fast enough
|
||||||
civAir.idlePlanes[name] = nil
|
civAir.idlePlanes[name] = nil
|
||||||
end
|
end
|
||||||
--]]--
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if we have less than max flights running
|
-- see if we have less than max flights running
|
||||||
@ -414,9 +545,12 @@ function civAir.update()
|
|||||||
for idx, aName in pairs(overduePlanes) do
|
for idx, aName in pairs(overduePlanes) do
|
||||||
local aFlight = civAir.getPlane(aName) -- returns a group
|
local aFlight = civAir.getPlane(aName) -- returns a group
|
||||||
civAir.removePlaneGroupByName(aName) -- remove from roster
|
civAir.removePlaneGroupByName(aName) -- remove from roster
|
||||||
if aFlight and aFlight:isExist() then
|
if aFlight and Unit.isExist(aFlight) then
|
||||||
-- destroy can only work if group isexist!
|
-- destroy can only work if group isexist!
|
||||||
Group.destroy(aFlight) -- remember: flights are groups!
|
Group.destroy(aFlight) -- remember: flights are groups!
|
||||||
|
if civAir.verbose then
|
||||||
|
trigger.action.outText("+++civA: removed flight <" .. aName .. "> for overtime.", 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -504,7 +638,9 @@ function civAir.start()
|
|||||||
|
|
||||||
-- start the update loop
|
-- start the update loop
|
||||||
civAir.update()
|
civAir.update()
|
||||||
|
-- start outbound tracking
|
||||||
|
civAir.trackOutbound()
|
||||||
|
|
||||||
-- say hi!
|
-- say hi!
|
||||||
trigger.action.outText("cf/x civAir v" .. civAir.version .. " started.", 30)
|
trigger.action.outText("cf/x civAir v" .. civAir.version .. " started.", 30)
|
||||||
return true
|
return true
|
||||||
@ -518,10 +654,11 @@ end
|
|||||||
--[[--
|
--[[--
|
||||||
Additional ideas
|
Additional ideas
|
||||||
|
|
||||||
- border zones: ac can airstart in there and disappear in there
|
|
||||||
- callbacks for civ spawn / despawn
|
- callbacks for civ spawn / despawn
|
||||||
- add civkill callback / redCivKill blueCivKill flag bangers
|
- add civkill callback / redCivKill blueCivKill flag bangers
|
||||||
- Helicopter support
|
- Helicopter support
|
||||||
- departure only, destination only
|
|
||||||
- add slot checking to see if other planes block it even though DCS claims the slot is free
|
- add slot checking to see if other planes block it even though DCS claims the slot is free
|
||||||
|
- allow list of countries to choose civ air from
|
||||||
|
- ability to force a flight from a source? How do we make a destination? currently not a good idea
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.9.0"
|
dcsCommon.version = "2.9.2"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
2.2.6 - compassPositionOfARelativeToB
|
||||||
- clockPositionOfARelativeToB
|
- clockPositionOfARelativeToB
|
||||||
@ -166,7 +166,12 @@ dcsCommon.version = "2.9.0"
|
|||||||
2.9.0 - createPoint() moved from cfxZones
|
2.9.0 - createPoint() moved from cfxZones
|
||||||
- copyPoint() moved from cfxZones
|
- copyPoint() moved from cfxZones
|
||||||
- numberArrayFromString() moved from cfxZones
|
- numberArrayFromString() moved from cfxZones
|
||||||
|
2.9.1 - new createSimpleRoutePointData()
|
||||||
|
- createOverheadAirdromeRoutPintData corrected and legacy support added
|
||||||
|
- new bearingFromAtoBusingXY()
|
||||||
|
- corrected verbosity for bearingFromAtoB
|
||||||
|
- new getCountriesForCoalition()
|
||||||
|
2.9.2 - updated task2text
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -182,6 +187,7 @@ dcsCommon.version = "2.9.0"
|
|||||||
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
dcsCommon.cbID = 0 -- callback id for simple callback scheduling
|
||||||
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P"} -- Ka-50, Apache and Gazelle can't carry troops
|
dcsCommon.troopCarriers = {"Mi-8MT", "UH-1H", "Mi-24P"} -- Ka-50, Apache and Gazelle can't carry troops
|
||||||
dcsCommon.coalitionSides = {0, 1, 2}
|
dcsCommon.coalitionSides = {0, 1, 2}
|
||||||
|
dcsCommon.maxCountry = 86 -- number of countries defined in total
|
||||||
|
|
||||||
-- lookup tables
|
-- lookup tables
|
||||||
dcsCommon.groupID2Name = {}
|
dcsCommon.groupID2Name = {}
|
||||||
@ -747,23 +753,23 @@ dcsCommon.version = "2.9.0"
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not B then
|
if not B then
|
||||||
trigger.action.outText("WARNING: no 'A' in bearingFromAtoB", 30)
|
trigger.action.outText("WARNING: no 'B' in bearingFromAtoB", 30)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not A.x then
|
if not A.x then
|
||||||
trigger.action.outText("WARNING: no 'A.x' (type A =<" .. type(A) .. ">)in bearingFromAtoB", 30)
|
trigger.action.outText("WARNING: no 'A.x' (type A =<" .. type(A) .. ">)in bearingFromAtoB", 30)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not A.y then
|
if not A.z then
|
||||||
trigger.action.outText("WARNING: no 'A.x' (type A =<" .. type(A) .. ">)in bearingFromAtoB", 30)
|
trigger.action.outText("WARNING: no 'A.z' (type A =<" .. type(A) .. ">)in bearingFromAtoB", 30)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not B.x then
|
if not B.x then
|
||||||
trigger.action.outText("WARNING: no 'B.x' (type B =<" .. type(B) .. ">)in bearingFromAtoB", 30)
|
trigger.action.outText("WARNING: no 'B.x' (type B =<" .. type(B) .. ">)in bearingFromAtoB", 30)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if not B.y then
|
if not B.z then
|
||||||
trigger.action.outText("WARNING: no 'B.y' (type B =<" .. type(B) .. ">)in bearingFromAtoB", 30)
|
trigger.action.outText("WARNING: no 'B.z' (type B =<" .. type(B) .. ">)in bearingFromAtoB", 30)
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -773,6 +779,38 @@ dcsCommon.version = "2.9.0"
|
|||||||
return bearing
|
return bearing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.bearingFromAtoBusingXY(A, B) -- coords in x, y
|
||||||
|
if not A then
|
||||||
|
trigger.action.outText("WARNING: no 'A' in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if not B then
|
||||||
|
trigger.action.outText("WARNING: no 'B' in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if not A.x then
|
||||||
|
trigger.action.outText("WARNING: no 'A.x' (type A =<" .. type(A) .. ">)in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if not A.y then
|
||||||
|
trigger.action.outText("WARNING: no 'A.y' (type A =<" .. type(A) .. ">)in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if not B.x then
|
||||||
|
trigger.action.outText("WARNING: no 'B.x' (type B =<" .. type(B) .. ">)in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
if not B.y then
|
||||||
|
trigger.action.outText("WARNING: no 'B.y' (type B =<" .. type(B) .. ">)in bearingFromAtoBXY", 30)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local dx = B.x - A.x
|
||||||
|
local dz = B.y - A.y
|
||||||
|
local bearing = math.atan2(dz, dx) -- in radiants
|
||||||
|
return bearing
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.bearingInDegreesFromAtoB(A, B)
|
function dcsCommon.bearingInDegreesFromAtoB(A, B)
|
||||||
local bearing = dcsCommon.bearingFromAtoB(A, B) -- in rads
|
local bearing = dcsCommon.bearingFromAtoB(A, B) -- in rads
|
||||||
bearing = math.floor(bearing / math.pi * 180)
|
bearing = math.floor(bearing / math.pi * 180)
|
||||||
@ -1116,7 +1154,7 @@ dcsCommon.version = "2.9.0"
|
|||||||
-- coalition's countries
|
-- coalition's countries
|
||||||
-- we start with id=0 (Russia), go to id=85 (Slovenia), but skip id = 14
|
-- we start with id=0 (Russia), go to id=85 (Slovenia), but skip id = 14
|
||||||
local i = 0
|
local i = 0
|
||||||
while i < 86 do
|
while i < dcsCommon.maxCountry do -- 86 do
|
||||||
if i ~= 14 then
|
if i ~= 14 then
|
||||||
if (coalition.getCountryCoalition(i) == aCoalition) then return i end
|
if (coalition.getCountryCoalition(i) == aCoalition) then return i end
|
||||||
end
|
end
|
||||||
@ -1125,6 +1163,22 @@ dcsCommon.version = "2.9.0"
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.getCountriesForCoalition(aCoalition)
|
||||||
|
if not aCoalition then aCoalition = 0 end
|
||||||
|
local allCty = {}
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
while i < dcsCommon.maxCountry do
|
||||||
|
if i ~= 14 then -- there is no county 14
|
||||||
|
if (coalition.getCountryCoalition(i) == aCoalition) then
|
||||||
|
table.insert(allCty, i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return allCty
|
||||||
|
end
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- C A L L B A C K H A N D L E R
|
-- C A L L B A C K H A N D L E R
|
||||||
@ -1394,7 +1448,7 @@ dcsCommon.version = "2.9.0"
|
|||||||
return rp
|
return rp
|
||||||
end
|
end
|
||||||
|
|
||||||
function dcsCommon.createOverheadAirdromeRoutPintData(aerodrome)
|
function dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)
|
||||||
if not aerodrome then return nil end
|
if not aerodrome then return nil end
|
||||||
local rp = {}
|
local rp = {}
|
||||||
local p = aerodrome:getPoint()
|
local p = aerodrome:getPoint()
|
||||||
@ -1408,6 +1462,9 @@ dcsCommon.version = "2.9.0"
|
|||||||
rp.alt_type = "BARO"
|
rp.alt_type = "BARO"
|
||||||
return rp
|
return rp
|
||||||
end
|
end
|
||||||
|
function dcsCommon.createOverheadAirdromeRoutPintData(aerodrome) -- backwards-compat to typo
|
||||||
|
return dcsCommon.createOverheadAirdromeRoutePointData(aerodrome)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function dcsCommon.createLandAtAerodromeRoutePointData(aerodrome)
|
function dcsCommon.createLandAtAerodromeRoutePointData(aerodrome)
|
||||||
@ -1418,7 +1475,7 @@ dcsCommon.version = "2.9.0"
|
|||||||
rp.airdromeId = aerodrome:getID()
|
rp.airdromeId = aerodrome:getID()
|
||||||
rp.x = p.x
|
rp.x = p.x
|
||||||
rp.y = p.z
|
rp.y = p.z
|
||||||
rp.alt = p.y
|
rp.alt = land.getHeight({x=p.x, y=p.z}) --p.y
|
||||||
rp.action = "Landing"
|
rp.action = "Landing"
|
||||||
rp.type = "Land"
|
rp.type = "Land"
|
||||||
|
|
||||||
@ -1427,6 +1484,19 @@ dcsCommon.version = "2.9.0"
|
|||||||
return rp
|
return rp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.createSimpleRoutePointData(p, alt)
|
||||||
|
if not alt then alt = 8000 end -- 24'000 feet
|
||||||
|
local rp = {}
|
||||||
|
rp.x = p.x
|
||||||
|
rp.y = p.z
|
||||||
|
rp.alt = alt
|
||||||
|
rp.action = "Turning Point"
|
||||||
|
rp.type = "Turning Point"
|
||||||
|
|
||||||
|
rp.speed = 133; -- in m/s? If so, that's 360 km/h
|
||||||
|
rp.alt_type = "BARO"
|
||||||
|
return rp
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.createRPFormationData(findex) -- must be added as "task" to an RP. use 4 for Echelon right
|
function dcsCommon.createRPFormationData(findex) -- must be added as "task" to an RP. use 4 for Echelon right
|
||||||
local task = {}
|
local task = {}
|
||||||
@ -2481,8 +2551,8 @@ end
|
|||||||
|
|
||||||
if not inrecursion then
|
if not inrecursion then
|
||||||
-- output a marker to find in the log / screen
|
-- output a marker to find in the log / screen
|
||||||
trigger.action.outText("=== dcsCommon vardump END", 30)
|
trigger.action.outText("=== dcsCommon vardump end", 30)
|
||||||
env.info("=== dcsCommon vardump END")
|
env.info("=== dcsCommon vardump end")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2520,8 +2590,8 @@ end
|
|||||||
|
|
||||||
if not inrecursion then
|
if not inrecursion then
|
||||||
-- output a marker to find in the log / screen
|
-- output a marker to find in the log / screen
|
||||||
trigger.action.outText("=== dcsCommon vardump END", 30)
|
trigger.action.outText("=== dcsCommon vardump end", 30)
|
||||||
--env.info("=== dcsCommon vardump END")
|
--env.info("=== dcsCommon vardump end")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2541,15 +2611,19 @@ end
|
|||||||
if id == 0 then return "invalid" end
|
if id == 0 then return "invalid" end
|
||||||
-- translate the event id to text
|
-- translate the event id to text
|
||||||
local events = {"shot", "hit", "takeoff", "land",
|
local events = {"shot", "hit", "takeoff", "land",
|
||||||
"crash", "eject", "refuel", "dead",
|
"crash", "eject", "refuel", "dead", -- 8
|
||||||
"pilot dead", "base captured", "mission start", "mission end", -- 12
|
"pilot dead", "base captured", "mission start", "mission end", -- 12
|
||||||
"took control", "refuel stop", "birth", "human failure",
|
"took control", "refuel stop", "birth", "human failure", -- 16
|
||||||
"det. failure", "engine start", "engine stop", "player enter unit",
|
"det. failure", "engine start", "engine stop", "player enter unit", -- 20
|
||||||
"player leave unit", "player comment", "start shoot", "end shoot",
|
"player leave unit", "player comment", "start shoot", "end shoot", -- 24
|
||||||
"mark add", "mark changed", "makr removed", "kill",
|
"mark add", "mark changed", "mark removed", "kill", -- 28
|
||||||
"score", "unit lost", "land after eject", "Paratrooper land",
|
"score", "unit lost", "land after eject", "Paratrooper land", -- 32
|
||||||
"chair discard after eject", "weapon add", "trigger zone", "landing quality mark",
|
"chair discard after eject", "weapon add", "trigger zone", "landing quality mark", -- 36
|
||||||
"BDA", "max"}
|
"BDA", "AI Abort Mission", "DayNight", "Flight Time", -- 40
|
||||||
|
"Pilot Suicide", "player cap airfield", "emergency landing", "unit create task", -- 44
|
||||||
|
"unit delete task", "Simulation start", "weapon rearm", "weapon drop", -- 48
|
||||||
|
"unit task timeout", "unit task stage",
|
||||||
|
"max"}
|
||||||
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
if id > #events then return "Unknown (ID=" .. id .. ")" end
|
||||||
return events[id]
|
return events[id]
|
||||||
end
|
end
|
||||||
|
|||||||
253
modules/duel.lua
Normal file
253
modules/duel.lua
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
duel = {}
|
||||||
|
duel.version = "1.0.0"
|
||||||
|
duel.verbose = false
|
||||||
|
duel.requiredLibs = {
|
||||||
|
"dcsCommon",
|
||||||
|
"cfxZones",
|
||||||
|
"cfxMX",
|
||||||
|
}
|
||||||
|
--[[--
|
||||||
|
Version History
|
||||||
|
1.0.0 - Initial Version
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
ATTENTION!
|
||||||
|
- REQUIRES that SSB is running on the host
|
||||||
|
- REQUIRTES that SSB is confgured that '0' (zero) means slot is enabled (this is SSB default)
|
||||||
|
- This script must run at MISSION START and will enable SSB
|
||||||
|
|
||||||
|
--]]--
|
||||||
|
|
||||||
|
duel.duelZones = {}
|
||||||
|
duel.activeDuelists = {}
|
||||||
|
duel.allDuelists = {}
|
||||||
|
--
|
||||||
|
-- reading attributes
|
||||||
|
--
|
||||||
|
function duel.createDuelZone(theZone)
|
||||||
|
theZone.duelists = {} -- all player units in this zone
|
||||||
|
-- iterate all players and find any unit that is placed in this zone
|
||||||
|
for unitName, unitData in pairs(cfxMX.playerUnitByName) do
|
||||||
|
local p = {}
|
||||||
|
p.x = unitData.x
|
||||||
|
p.z = unitData.y -- !!
|
||||||
|
p.y = 0
|
||||||
|
|
||||||
|
if theZone:pointInZone(p) then
|
||||||
|
-- this is a player aircraft in this zone
|
||||||
|
local duelist = {}
|
||||||
|
duelist.data = unitData
|
||||||
|
duelist.name = unitName
|
||||||
|
duelist.type = unitData.type
|
||||||
|
local groupData = cfxMX.playerUnit2Group[unitName]
|
||||||
|
duelist.groupName = groupData.name
|
||||||
|
duelist.coa = cfxMX.groupCoalitionByName[duelist.groupName]
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("Detected player unit <" .. duelist.name .. ">, type <" .. duelist.type .. "> of group <" .. duelist.groupName .. "> of coa <" .. duelist.coa .. "> in zone <" .. theZone.name .. "> as duelist", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
duelist.active = false
|
||||||
|
duelist.arena = theZone.name
|
||||||
|
duelist.zone = theZone
|
||||||
|
|
||||||
|
-- enter into global table
|
||||||
|
-- player can only be in at maximum one duelist zones
|
||||||
|
if duel.allDuelists[unitName] then
|
||||||
|
trigger.action.outText("+++WARNING: overlapping duelists! Overwriting previous data", 30)
|
||||||
|
end
|
||||||
|
duel.allDuelists[unitName] = duelist
|
||||||
|
theZone.duelists[unitName] = duelist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone.state = "waiting" -- FSM, init to waiting state
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Event processing
|
||||||
|
--
|
||||||
|
function duel.closeSlotsForZoneAndCoaExceptGroupNamed(theZone, coa, groupName)
|
||||||
|
-- iterate this zone's duelist groups and tell SSB to close them now
|
||||||
|
local allDuelists = theZone.duelists
|
||||||
|
for unitName, theDuelist in pairs(allDuelists) do
|
||||||
|
local dgName = theDuelist.groupName
|
||||||
|
if (theDuelist.coa == coa) and (dgName ~= groupName) then
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("+++duel: closing SSB slot for group <" .. dgName .. ">, coa <" .. theDuelist.coa .. ">", 30)
|
||||||
|
trigger.action.setUserFlag(dgName,100) -- anything but 0 means closed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function duel.openSlotsForZoneAndCoa(theZone, coa)
|
||||||
|
local allDuelists = theZone.duelists
|
||||||
|
for unitName, theDuelist in pairs(allDuelists) do
|
||||||
|
if (theDuelist.coa == coa) then
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("+++duel: opening SSB slot for group <" .. theDuelist.groupName .. ">, coa <" .. theDuelist.coa .. ">", 30)
|
||||||
|
trigger.action.setUserFlag(theDuelist.groupName, 0) -- 0 means OPEN
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function duel.checkReopenSlotsForZoneAndCoa(theZone, coa)
|
||||||
|
-- test if one side can reopen all slots to enter the duel
|
||||||
|
-- if so, will reset FSM for zone
|
||||||
|
local allDuelists = theZone.duelists
|
||||||
|
local allUnengaged = true
|
||||||
|
for unitName, theDuelist in pairs(allDuelists) do
|
||||||
|
if (theDuelist.coa == coa) then
|
||||||
|
local theUnit = Unit.getByName(unitName)
|
||||||
|
if theUnit and Unit.isExist(theUnit) then
|
||||||
|
-- unit is still alive on this side, can't reopen
|
||||||
|
allUnengaged = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if allUnengaged then
|
||||||
|
duel.openSlotsForZoneAndCoa(theZone, coa)
|
||||||
|
theZone.state = "waiting"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function duel.duelistEnteredArena(theUnit, theDuelist)
|
||||||
|
-- we connect the player with duelist slot
|
||||||
|
theDuelist.playerName = theUnit:getPlayerName()
|
||||||
|
theDuelist.active = true
|
||||||
|
|
||||||
|
local player = theUnit:getPlayerName()
|
||||||
|
local unitName = theUnit:getName()
|
||||||
|
local groupName = theDuelist.groupName
|
||||||
|
local theZone = theDuelist.zone --duel.duelZones[theDuelist.arena]
|
||||||
|
local coa = theDuelist.coa
|
||||||
|
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("Player <" .. player .. "> entered arena <" .. theZone:getName() .. "> in unit <" .. unitName .. "> of group <" .. groupName .. "> type <" .. theDuelist.type .. ">, belongs to coalition <" .. coa .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- close all slots for this zone and coalition
|
||||||
|
duel.closeSlotsForZoneAndCoaExceptGroupNamed(theZone, coa, groupName)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function duel:onEvent(event)
|
||||||
|
if not event then return end
|
||||||
|
if duel.verbose then
|
||||||
|
--trigger.action.outText("Event: " .. event.id .. " (" .. dcsCommon.event2text(event.id) .. ")", 30)
|
||||||
|
end
|
||||||
|
local theUnit = event.initiator
|
||||||
|
if not theUnit then return end
|
||||||
|
|
||||||
|
if event.id == 15 then -- birth
|
||||||
|
local unitName = theUnit:getName()
|
||||||
|
-- see if this is a duelist that has spawned
|
||||||
|
if not duel.allDuelists[unitName] then
|
||||||
|
return -- not a duelist, not my problem
|
||||||
|
end
|
||||||
|
|
||||||
|
-- unit that entered is player controlled, and duelist
|
||||||
|
duel.duelistEnteredArena(theUnit, duel.allDuelists[unitName])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- update
|
||||||
|
--
|
||||||
|
|
||||||
|
function duel.update()
|
||||||
|
-- call me in a second to poll triggers
|
||||||
|
timer.scheduleFunction(duel.update, {}, timer.getTime() + 1/duel.ups)
|
||||||
|
|
||||||
|
-- find units that have disappeared, and react accordingly
|
||||||
|
for unitName, theDuelist in pairs (duel.allDuelists) do
|
||||||
|
local theZone = theDuelist.zone
|
||||||
|
if theDuelist.active then
|
||||||
|
-- trigger.action.outText("+++duel: unit <" .. unitName .. "> is active in zone <" .. theZone:getName() .. ">, controlled by <" .. theDuelist.playerName .. ">", 30)
|
||||||
|
|
||||||
|
local theUnit = Unit.getByName(unitName)
|
||||||
|
if theUnit and Unit.isExist(theUnit) then
|
||||||
|
-- all is well
|
||||||
|
else
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("+++duel: unit <" .. unitName .. "> controlled by <" .. theDuelist.playerName .. "> has disappeared, starting cleanup", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
theDuelist.playerName = nil
|
||||||
|
theDuelist.active = false
|
||||||
|
duel.checkReopenSlotsForZoneAndCoa(theZone, theDuelist.coa)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- now handle FSM for each zone separately
|
||||||
|
for zoneName, theZone in pairs(duel.duelZones) do
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Config & start
|
||||||
|
--
|
||||||
|
function duel.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("duelConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("duelConfig")
|
||||||
|
end
|
||||||
|
|
||||||
|
duel.verbose = theZone.verbose
|
||||||
|
duel.ups = theZone:getNumberFromZoneProperty("ups", 1)
|
||||||
|
|
||||||
|
duel.inside = theZone:getBoolFromZoneProperty("inside", true)
|
||||||
|
duel.gracePeriod = theZone:getNumberFromZoneProperty("gracePeriod", 30)
|
||||||
|
duel.keepScore = theZone:getBoolFromZoneProperty("score", true)
|
||||||
|
|
||||||
|
if duel.verbose then
|
||||||
|
trigger.action.outText("+++duel: read config", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function duel.start()
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("cfx duel requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("cfx Duel", duel.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- turn on SSB
|
||||||
|
trigger.action.setUserFlag("SSB",100)
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
duel.readConfigZone()
|
||||||
|
|
||||||
|
-- process cloner Zones
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("duel")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
duel.createDuelZone(aZone) -- process attributes
|
||||||
|
duel.duelZones[aZone.name] = aZone -- add to list
|
||||||
|
end
|
||||||
|
|
||||||
|
-- connect event handler
|
||||||
|
world.addEventHandler(duel)
|
||||||
|
|
||||||
|
-- start update
|
||||||
|
duel.update()
|
||||||
|
|
||||||
|
trigger.action.outText("cfx Duel v" .. duel.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if not duel.start() then
|
||||||
|
trigger.action.outText("cfx Duel aborted: missing libraries", 30)
|
||||||
|
duel = nil
|
||||||
|
end
|
||||||
@ -1,5 +1,5 @@
|
|||||||
guardianAngel = {}
|
guardianAngel = {}
|
||||||
guardianAngel.version = "3.0.4"
|
guardianAngel.version = "3.0.5"
|
||||||
guardianAngel.ups = 10
|
guardianAngel.ups = 10
|
||||||
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
guardianAngel.name = "Guardian Angel" -- just in case someone accesses .name
|
||||||
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
||||||
@ -61,7 +61,11 @@ guardianAngel.requiredLibs = {
|
|||||||
3.0.3 - monitorItem() guards against loss of target (nil)
|
3.0.3 - monitorItem() guards against loss of target (nil)
|
||||||
3.0.4 - launchSound attribute
|
3.0.4 - launchSound attribute
|
||||||
- interventionSound attribute
|
- interventionSound attribute
|
||||||
|
3.0.5 - better missiole names, their object IDs seem to have disappeared, also storing launcher name
|
||||||
|
- msgTime to control how long warnings remain on the screen
|
||||||
|
- disappear message now only on verbose
|
||||||
|
- dmlZones
|
||||||
|
|
||||||
|
|
||||||
This script detects missiles launched against protected aircraft an
|
This script detects missiles launched against protected aircraft an
|
||||||
removes them when they are about to hit
|
removes them when they are about to hit
|
||||||
@ -133,7 +137,7 @@ end
|
|||||||
--
|
--
|
||||||
-- watch q items
|
-- watch q items
|
||||||
--
|
--
|
||||||
function guardianAngel.createQItem(theWeapon, theTarget, threat)
|
function guardianAngel.createQItem(theWeapon, theTarget, threat, launcher)
|
||||||
if not theWeapon then return nil end
|
if not theWeapon then return nil end
|
||||||
if not theTarget then return nil end
|
if not theTarget then return nil end
|
||||||
if not theTarget:isExist() then return nil end
|
if not theTarget:isExist() then return nil end
|
||||||
@ -142,8 +146,26 @@ function guardianAngel.createQItem(theWeapon, theTarget, threat)
|
|||||||
-- watch it for re-targeting purposes
|
-- watch it for re-targeting purposes
|
||||||
|
|
||||||
local theItem = {}
|
local theItem = {}
|
||||||
|
local oName = tostring(theWeapon:getName())
|
||||||
|
if not oName or #oName < 1 then oName = dcsCommon.numberUUID() end
|
||||||
|
local wName = ""
|
||||||
|
if theWeapon.getDisplayName then
|
||||||
|
wName = theWeapon:getDisplayName() -- does this even exist any more?
|
||||||
|
elseif theWeapon.getTypeName then
|
||||||
|
wName = theWeapon:getTypeName()
|
||||||
|
else
|
||||||
|
wName = "<Generic>"
|
||||||
|
end
|
||||||
|
wName = wName .. "-" .. oName
|
||||||
|
local launcherName = launcher:getTypeName() .. " " .. launcher:getName()
|
||||||
|
|
||||||
theItem.theWeapon = theWeapon -- weapon that we are tracking
|
theItem.theWeapon = theWeapon -- weapon that we are tracking
|
||||||
theItem.weaponName = theWeapon:getName()
|
theItem.weaponName = wName -- theWeapon:getName()
|
||||||
|
-- usually weapons have no 'name' except an ID, so let's get the
|
||||||
|
-- type or display name. Weapons often have no display name.
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("gA: tracking missile <" .. wName .. "> launched by <" .. launcherName .. ">", guardianAngel.msgTime)
|
||||||
|
end
|
||||||
theItem.theTarget = theTarget
|
theItem.theTarget = theTarget
|
||||||
theItem.tGroup = theTarget:getGroup()
|
theItem.tGroup = theTarget:getGroup()
|
||||||
theItem.tID = theItem.tGroup:getID()
|
theItem.tID = theItem.tGroup:getID()
|
||||||
@ -156,6 +178,7 @@ function guardianAngel.createQItem(theWeapon, theTarget, threat)
|
|||||||
theItem.threat = threat
|
theItem.threat = threat
|
||||||
theItem.lastDesc = "(new)"
|
theItem.lastDesc = "(new)"
|
||||||
theItem.timeStamp = timer.getTime()
|
theItem.timeStamp = timer.getTime()
|
||||||
|
theItem.launcher = launcherName
|
||||||
return theItem
|
return theItem
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -222,14 +245,17 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
if not w then return false end
|
if not w then return false end
|
||||||
if not w:isExist() then
|
if not w:isExist() then
|
||||||
--if (not theItem.missed) and (not theItem.lostTrack) then
|
--if (not theItem.missed) and (not theItem.lostTrack) then
|
||||||
local desc = theItem.weaponName .. ": DISAPPEARED"
|
|
||||||
|
--[[--
|
||||||
if guardianAngel.announcer and theItem.threat then
|
if guardianAngel.announcer and theItem.threat then
|
||||||
|
local desc = theItem.weaponName .. ": DISAPPEARED"
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, 30)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
else
|
else
|
||||||
trigger.action.outText(desc, 30)
|
trigger.action.outText(desc, guardianAngel.msgTime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
if guardianAngel.verbose then
|
if guardianAngel.verbose then
|
||||||
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
|
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
|
||||||
end
|
end
|
||||||
@ -277,13 +303,13 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
||||||
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
|
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, 30)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.launchSound then
|
if guardianAngel.launchSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
||||||
trigger.action.outSoundForGroup(ID, fileName)
|
trigger.action.outSoundForGroup(ID, fileName)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
trigger.action.outText(desc, 30)
|
trigger.action.outText(desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.launchSound then
|
if guardianAngel.launchSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
||||||
trigger.action.outSound(fileName)
|
trigger.action.outSound(fileName)
|
||||||
@ -340,13 +366,13 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, 30)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.interventionSound then
|
if guardianAngel.interventionSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
|
||||||
trigger.action.outSoundForGroup(ID, fileName)
|
trigger.action.outSoundForGroup(ID, fileName)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
trigger.action.outText(desc, 30)
|
trigger.action.outText(desc, guardianAngel.msgTime)
|
||||||
if guardianAngel.interventionSound then
|
if guardianAngel.interventionSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.interventionSound
|
||||||
trigger.action.outSound(fileName)
|
trigger.action.outSound(fileName)
|
||||||
@ -375,9 +401,9 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, 30)
|
trigger.action.outTextForGroup(ID, desc, guardianAngel.msgTime)
|
||||||
else
|
else
|
||||||
trigger.action.outText(desc, 30)
|
trigger.action.outText(desc, guardianAngel.msgTime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
guardianAngel.invokeCallbacks("intervention", theItem.targetName, theItem.weaponName)
|
guardianAngel.invokeCallbacks("intervention", theItem.targetName, theItem.weaponName)
|
||||||
@ -591,6 +617,7 @@ function guardianAngel.somethingHappened(event)
|
|||||||
-- if we get here, we have weapon aimed at a target
|
-- if we get here, we have weapon aimed at a target
|
||||||
local targetName = theTarget:getName()
|
local targetName = theTarget:getName()
|
||||||
local watchedUnit = guardianAngel.getWatchedUnitByName(targetName)
|
local watchedUnit = guardianAngel.getWatchedUnitByName(targetName)
|
||||||
|
local launcher = theUnit
|
||||||
guardianAngel.missilesAndTargets[theWeapon:getName()] = targetName
|
guardianAngel.missilesAndTargets[theWeapon:getName()] = targetName
|
||||||
if not watchedUnit then
|
if not watchedUnit then
|
||||||
-- we may still want to watch this if the missile
|
-- we may still want to watch this if the missile
|
||||||
@ -599,14 +626,14 @@ function guardianAngel.somethingHappened(event)
|
|||||||
trigger.action.outText("+++gA: missile <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">, not a threat", 30)
|
trigger.action.outText("+++gA: missile <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">, not a threat", 30)
|
||||||
end
|
end
|
||||||
-- add it as no threat
|
-- add it as no threat
|
||||||
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, false) -- this is not a threat, simply watch for re-target
|
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, false, launcher) -- this is not a threat, simply watch for re-target
|
||||||
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
||||||
return
|
return
|
||||||
end -- fired at some other poor sucker, we don't care
|
end -- fired at some other poor sucker, we don't care
|
||||||
|
|
||||||
-- if we get here, someone fired a guided weapon at my watched units
|
-- if we get here, someone fired a guided weapon at my watched units
|
||||||
-- create a new item for my queue
|
-- create a new item for my queue
|
||||||
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, true) -- this is watched
|
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, true, launcher) -- this is watched
|
||||||
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
||||||
guardianAngel.invokeCallbacks("launch", theQItem.targetName, theQItem.weaponName)
|
guardianAngel.invokeCallbacks("launch", theQItem.targetName, theQItem.weaponName)
|
||||||
|
|
||||||
@ -624,13 +651,13 @@ function guardianAngel.somethingHappened(event)
|
|||||||
-- currently, we always detect immediately
|
-- currently, we always detect immediately
|
||||||
-- can be moved to update()
|
-- can be moved to update()
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(grpID, "Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
|
trigger.action.outTextForGroup(grpID, "Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, guardianAngel.msgTime)
|
||||||
if guardianAngel.launchSound then
|
if guardianAngel.launchSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
||||||
trigger.action.outSoundForGroup(grpID, fileName)
|
trigger.action.outSoundForGroup(grpID, fileName)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
trigger.action.outText("Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
|
trigger.action.outText("Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, guardianAngel.msgTime)
|
||||||
if guardianAngel.launchSound then
|
if guardianAngel.launchSound then
|
||||||
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
local fileName = "l10n/DEFAULT/" .. guardianAngel.launchSound
|
||||||
trigger.action.outSound(fileName)
|
trigger.action.outSound(fileName)
|
||||||
@ -847,40 +874,42 @@ function guardianAngel.readConfigZone()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
guardianAngel.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
guardianAngel.verbose = theZone:getBoolFromZoneProperty("verbose", false)
|
||||||
|
|
||||||
guardianAngel.autoAddPlayer = cfxZones.getBoolFromZoneProperty(theZone, "autoAddPlayer", true)
|
guardianAngel.autoAddPlayer = theZone:getBoolFromZoneProperty("autoAddPlayer", true)
|
||||||
guardianAngel.launchWarning = cfxZones.getBoolFromZoneProperty(theZone, "launchWarning", true)
|
guardianAngel.launchWarning = theZone:getBoolFromZoneProperty("launchWarning", true)
|
||||||
guardianAngel.intervention = cfxZones.getBoolFromZoneProperty(theZone, "intervention", true)
|
guardianAngel.intervention = theZone:getBoolFromZoneProperty("intervention", true)
|
||||||
guardianAngel.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
guardianAngel.announcer = theZone:getBoolFromZoneProperty( "announcer", true)
|
||||||
guardianAngel.private = cfxZones.getBoolFromZoneProperty(theZone, "private", false)
|
guardianAngel.private = theZone:getBoolFromZoneProperty("private", false)
|
||||||
guardianAngel.explosion = cfxZones.getNumberFromZoneProperty(theZone, "explosion", -1)
|
guardianAngel.explosion = theZone:getNumberFromZoneProperty("explosion", -1)
|
||||||
guardianAngel.fxDistance = cfxZones.getNumberFromZoneProperty(theZone, "fxDistance", 500)
|
guardianAngel.fxDistance = theZone:getNumberFromZoneProperty( "fxDistance", 500)
|
||||||
|
|
||||||
guardianAngel.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
guardianAngel.active = theZone:getBoolFromZoneProperty("active", true)
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "activate?") then
|
guardianAngel.msgTime = theZone:getNumberFromZoneProperty("msgTime", 30)
|
||||||
guardianAngel.activate = cfxZones.getStringFromZoneProperty(theZone, "activate?", "*<none>")
|
|
||||||
guardianAngel.lastActivate = cfxZones.getFlagValue(guardianAngel.activate, theZone)
|
if theZone:hasProperty("activate?") then
|
||||||
elseif cfxZones.hasProperty(theZone, "on?") then
|
guardianAngel.activate = theZone:getStringFromZoneProperty("activate?", "*<none>")
|
||||||
guardianAngel.activate = cfxZones.getStringFromZoneProperty(theZone, "on?", "*<none>")
|
guardianAngel.lastActivate = theZone:getFlagValue(guardianAngel.activate)
|
||||||
guardianAngel.lastActivate = cfxZones.getFlagValue(guardianAngel.activate, theZone)
|
elseif theZone:hasProperty("on?") then
|
||||||
|
guardianAngel.activate = theZone:getStringFromZoneProperty("on?", "*<none>")
|
||||||
|
guardianAngel.lastActivate = theZone:getFlagValue(guardianAngel.activate)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "deactivate?") then
|
if theZone:hasProperty("deactivate?") then
|
||||||
guardianAngel.deactivate = cfxZones.getStringFromZoneProperty(theZone, "deactivate?", "*<none>")
|
guardianAngel.deactivate = theZone:getStringFromZoneProperty("deactivate?", "*<none>")
|
||||||
guardianAngel.lastDeActivate = cfxZones.getFlagValue(guardianAngel.deactivate, theZone)
|
guardianAngel.lastDeActivate = theZone:getFlagValue(guardianAngel.deactivate)
|
||||||
elseif cfxZones.hasProperty(theZone, "off?") then
|
elseif theZone:hasProperty("off?") then
|
||||||
guardianAngel.deactivate = cfxZones.getStringFromZoneProperty(theZone, "off?", "*<none>")
|
guardianAngel.deactivate = theZone:getStringFromZoneProperty("off?", "*<none>")
|
||||||
guardianAngel.lastDeActivate = cfxZones.getFlagValue(guardianAngel.deactivate, theZone)
|
guardianAngel.lastDeActivate = theZone:getFlagValue(guardianAngel.deactivate)
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "launchSound") then
|
if theZone:hasProperty("launchSound") then
|
||||||
guardianAngel.launchSound = cfxZones.getStringFromZoneProperty(theZone, "launchSound", "nosound")
|
guardianAngel.launchSound = theZone:getStringFromZoneProperty("launchSound", "nosound")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "interventionSound") then
|
if theZone:hasProperty("interventionSound") then
|
||||||
guardianAngel.interventionSound = cfxZones.getStringFromZoneProperty(theZone, "interventionSound", "nosound")
|
guardianAngel.interventionSound = theZone:getStringFromZoneProperty("interventionSound", "nosound")
|
||||||
end
|
end
|
||||||
|
|
||||||
guardianAngel.configZone = theZone
|
guardianAngel.configZone = theZone
|
||||||
@ -894,7 +923,7 @@ end
|
|||||||
--
|
--
|
||||||
|
|
||||||
function guardianAngel.processGuardianZone(theZone)
|
function guardianAngel.processGuardianZone(theZone)
|
||||||
theZone.angelic = cfxZones.getBoolFromZoneProperty(theZone, "guardian", true)
|
theZone.angelic = theZone:getBoolFromZoneProperty("guardian", true)
|
||||||
|
|
||||||
|
|
||||||
if theZone.verbose or guardianAngel.verbose then
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
@ -947,7 +976,7 @@ function guardianAngel.start()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function guardianAngel.testCB(reason, targetName, weaponName)
|
function guardianAngel.testCB(reason, targetName, weaponName)
|
||||||
trigger.action.outText("gA - CB for ".. reason .. ": " .. targetName .. " w: " .. weaponName, 30)
|
trigger.action.outText("gA - CB for ".. reason .. ": " .. targetName .. " w: " .. weaponName, guardianAngel.msgTime)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- go go go
|
-- go go go
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
unitZone={}
|
unitZone={}
|
||||||
unitZone.version = "1.2.4"
|
unitZone.version = "1.2.5"
|
||||||
unitZone.verbose = false
|
unitZone.verbose = false
|
||||||
unitZone.ups = 1
|
unitZone.ups = 1
|
||||||
unitZone.requiredLibs = {
|
unitZone.requiredLibs = {
|
||||||
@ -17,6 +17,7 @@ unitZone.requiredLibs = {
|
|||||||
1.2.3 - better guards for enterZone!, exitZone!, changeZone!
|
1.2.3 - better guards for enterZone!, exitZone!, changeZone!
|
||||||
- better guards for uzOn? and uzOff?
|
- better guards for uzOn? and uzOff?
|
||||||
1.2.4 - more verbosity on uzDirect
|
1.2.4 - more verbosity on uzDirect
|
||||||
|
1.2.5 - reading config improvement
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -196,8 +197,9 @@ function unitZone.checkZoneStatus(theZone)
|
|||||||
local playerCheck = theZone.matching == "player"
|
local playerCheck = theZone.matching == "player"
|
||||||
if playerCheck then
|
if playerCheck then
|
||||||
-- we check the names for players only
|
-- we check the names for players only
|
||||||
|
-- collector holds units, not groups
|
||||||
for idx, pUnit in pairs(theGroups) do
|
for idx, pUnit in pairs(theGroups) do
|
||||||
local puName=pUnit:getName()
|
local puName = pUnit:getName()
|
||||||
local hasMatch = false
|
local hasMatch = false
|
||||||
if theZone.lookForBeginsWith then
|
if theZone.lookForBeginsWith then
|
||||||
hasMatch = dcsCommon.stringStartsWith(puName, lookFor)
|
hasMatch = dcsCommon.stringStartsWith(puName, lookFor)
|
||||||
@ -323,10 +325,7 @@ end
|
|||||||
function unitZone.readConfigZone()
|
function unitZone.readConfigZone()
|
||||||
local theZone = cfxZones.getZoneByName("unitZoneConfig")
|
local theZone = cfxZones.getZoneByName("unitZoneConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
if unitZone.verbose then
|
theZone = cfxZones.createSimpleZone("unitZoneConfig")
|
||||||
trigger.action.outText("+++uZne: NO config zone!", 30)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unitZone.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
unitZone.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
|
|||||||
BIN
tutorial & demo missions/demo - Civ Air International.miz
Normal file
BIN
tutorial & demo missions/demo - Civ Air International.miz
Normal file
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user