mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.0
One Dot Oh!
This commit is contained in:
parent
1e5cb8d35e
commit
92dc6ca40f
Binary file not shown.
Binary file not shown.
@ -1,5 +1,5 @@
|
|||||||
cfxArtilleryZones = {}
|
cfxArtilleryZones = {}
|
||||||
cfxArtilleryZones.version = "2.2.0"
|
cfxArtilleryZones.version = "2.2.1"
|
||||||
cfxArtilleryZones.requiredLibs = {
|
cfxArtilleryZones.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -29,6 +29,7 @@ cfxArtilleryZones.verbose = false
|
|||||||
2.1.0 - DML Flag Support
|
2.1.0 - DML Flag Support
|
||||||
- code cleanup
|
- code cleanup
|
||||||
2.2.0 - DML Watchflag integration
|
2.2.0 - DML Watchflag integration
|
||||||
|
2.2.1 - minor code clean-up
|
||||||
|
|
||||||
Artillery Target Zones *** EXTENDS ZONES ***
|
Artillery Target Zones *** EXTENDS ZONES ***
|
||||||
Target Zones for artillery. Can determine which zones are in range and visible and then handle artillery barrage to this zone
|
Target Zones for artillery. Can determine which zones are in range and visible and then handle artillery barrage to this zone
|
||||||
@ -136,11 +137,7 @@ function cfxArtilleryZones.processArtilleryZone(aZone)
|
|||||||
if cfxZones.hasProperty(aZone, "f?") then
|
if cfxZones.hasProperty(aZone, "f?") then
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
|
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "f?", "none")
|
||||||
end
|
end
|
||||||
--[[--
|
|
||||||
if cfxZones.hasProperty(aZone, "triggerFlag") then
|
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "triggerFlag", "none")
|
|
||||||
end
|
|
||||||
--]]--
|
|
||||||
if cfxZones.hasProperty(aZone, "artillery?") then
|
if cfxZones.hasProperty(aZone, "artillery?") then
|
||||||
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "artillery?", "none")
|
aZone.artyTriggerFlag = cfxZones.getStringFromZoneProperty(aZone, "artillery?", "none")
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
cfxReconMode = {}
|
cfxReconMode = {}
|
||||||
cfxReconMode.version = "1.5.0"
|
cfxReconMode.version = "2.0.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
|
||||||
|
|
||||||
cfxReconMode.prioList = {} -- group names that are high prio and generate special event
|
cfxReconMode.prioList = {} -- group names that are high prio and generate special event
|
||||||
cfxReconMode.blackList = {} -- group names that are NEVER detected. Comma separated strings, e.g. {"Always Hidden", "Invisible Group"}
|
cfxReconMode.blackList = {} -- group names that are NEVER detected. Comma separated strings, e.g. {"Always Hidden", "Invisible Group"}
|
||||||
|
cfxReconMode.dynamics = {} -- if a group name is dynamic
|
||||||
|
cfxReconMode.zoneInfo = {} -- additional zone info
|
||||||
|
|
||||||
|
cfxReconMode.scoutZones = {} -- zones that define aircraft. used for late eval of players
|
||||||
|
cfxReconMode.allowedScouts = {} -- when not using autoscouts
|
||||||
|
cfxReconMode.blindScouts = {} -- to exclude aircraft from being scouts
|
||||||
cfxReconMode.removeWhenDestroyed = true
|
cfxReconMode.removeWhenDestroyed = true
|
||||||
cfxReconMode.activeMarks = {} -- all marks and their groups, indexed by groupName
|
cfxReconMode.activeMarks = {} -- all marks and their groups, indexed by groupName
|
||||||
|
|
||||||
@ -48,6 +53,26 @@ VERSION HISTORY
|
|||||||
1.5.0 - removeWhenDestroyed()
|
1.5.0 - removeWhenDestroyed()
|
||||||
- autoRemove()
|
- autoRemove()
|
||||||
- readConfigZone creates default config zone so we get correct defaulting
|
- readConfigZone creates default config zone so we get correct defaulting
|
||||||
|
2.0.0 - DML integration prio+-->prio! detect+ --> detect!
|
||||||
|
and method
|
||||||
|
- changed access to prio and blacklist to hash
|
||||||
|
- dynamic option for prio and black
|
||||||
|
- trigger zones for designating prio and blacklist
|
||||||
|
- reworked stringInList to also include dynamics
|
||||||
|
- Report in SALT format: size, action, loc, time.
|
||||||
|
- Marks add size, action info
|
||||||
|
- LatLon or MGRS
|
||||||
|
- MGRS option in config
|
||||||
|
- filter onEvent for helo and aircraft
|
||||||
|
- allowedScouts and blind
|
||||||
|
- stronger scout filtering at startup
|
||||||
|
- better filtering on startup when autorecon and playeronly
|
||||||
|
- player lazy late checking, zone saving
|
||||||
|
- correct checks when not autorecon
|
||||||
|
- ability to add special flags to recon prio group
|
||||||
|
- event guard in onEvent
|
||||||
|
- <t> wildcard
|
||||||
|
- <lat>, <lon>, <mgrs> wildcards
|
||||||
|
|
||||||
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
|
||||||
@ -79,7 +104,9 @@ cfxReconMode.playerOnlyRecon = false -- only players can do recon
|
|||||||
cfxReconMode.reportNumbers = true -- also add unit count in report
|
cfxReconMode.reportNumbers = true -- also add unit count in report
|
||||||
cfxReconMode.prioFlag = nil
|
cfxReconMode.prioFlag = nil
|
||||||
cfxReconMode.detectFlag = nil
|
cfxReconMode.detectFlag = nil
|
||||||
|
cfxReconMode.method = "inc"
|
||||||
cfxReconMode.applyMarks = true
|
cfxReconMode.applyMarks = true
|
||||||
|
cfxReconMode.mgrs = false
|
||||||
|
|
||||||
cfxReconMode.ups = 1 -- updates per second.
|
cfxReconMode.ups = 1 -- updates per second.
|
||||||
cfxReconMode.scouts = {} -- units that are performing scouting.
|
cfxReconMode.scouts = {} -- units that are performing scouting.
|
||||||
@ -111,38 +138,80 @@ function cfxReconMode.invokeCallbacks(reason, theSide, theScout, theGroup, theNa
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- add a priority/blackList group name to prio list
|
-- add a priority/blackList group name to prio list
|
||||||
function cfxReconMode.addToPrioList(aGroup)
|
function cfxReconMode.addToPrioList(aGroup, dynamic)
|
||||||
|
if not dynamic then dynamic = false end
|
||||||
if not aGroup then return end
|
if not aGroup then return end
|
||||||
if type(aGroup) == "table" and aGroup.getName then
|
if type(aGroup) == "table" and aGroup.getName then
|
||||||
aGroup = aGroup:getName()
|
aGroup = aGroup:getName()
|
||||||
end
|
end
|
||||||
if type(aGroup) == "string" then
|
if type(aGroup) == "string" then
|
||||||
table.insert(cfxReconMode.prioList, aGroup)
|
-- table.insert(cfxReconMode.prioList, aGroup)
|
||||||
|
cfxReconMode.prioList[aGroup] = aGroup
|
||||||
|
cfxReconMode.dynamics[aGroup] = dynamic
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconMode.addToBlackList(aGroup)
|
function cfxReconMode.addToBlackList(aGroup, dynamic)
|
||||||
|
if not dynamic then dynamic = false end
|
||||||
if not aGroup then return end
|
if not aGroup then return end
|
||||||
if type(aGroup) == "table" and aGroup.getName then
|
if type(aGroup) == "table" and aGroup.getName then
|
||||||
aGroup = aGroup:getName()
|
aGroup = aGroup:getName()
|
||||||
end
|
end
|
||||||
if type(aGroup) == "string" then
|
if type(aGroup) == "string" then
|
||||||
table.insert(cfxReconMode.blackList, aGroup)
|
--table.insert(cfxReconMode.blackList, aGroup)
|
||||||
|
cfxReconMode.blackList[aGroup] = aGroup
|
||||||
|
cfxReconMode.dynamics[aGroup] = dynamic
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.addToAllowedScoutList(aGroup, dynamic)
|
||||||
|
if not dynamic then dynamic = false end
|
||||||
|
if not aGroup then return end
|
||||||
|
if type(aGroup) == "table" and aGroup.getName then
|
||||||
|
aGroup = aGroup:getName()
|
||||||
|
end
|
||||||
|
if type(aGroup) == "string" then
|
||||||
|
cfxReconMode.allowedScouts[aGroup] = aGroup
|
||||||
|
cfxReconMode.dynamics[aGroup] = dynamic
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.addToBlindScoutList(aGroup, dynamic)
|
||||||
|
if not dynamic then dynamic = false end
|
||||||
|
if not aGroup then return end
|
||||||
|
if type(aGroup) == "table" and aGroup.getName then
|
||||||
|
aGroup = aGroup:getName()
|
||||||
|
end
|
||||||
|
if type(aGroup) == "string" then
|
||||||
|
cfxReconMode.blindScouts[aGroup] = aGroup
|
||||||
|
cfxReconMode.dynamics[aGroup] = dynamic
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function cfxReconMode.isStringInList(theString, theList)
|
function cfxReconMode.isStringInList(theString, theList)
|
||||||
if not theString then return false end
|
-- returns two values: inList, and original group name (if exist)
|
||||||
if not theList then return false end
|
if not theString then return false, nil end
|
||||||
if type(theString) == "string" then
|
if type(theString) ~= "string" then return false, nil end
|
||||||
for idx,anItem in pairs(theList) do
|
if not theList then return false, nil end
|
||||||
if anItem == theString then return true end
|
|
||||||
|
-- first, try a direct look-up. if this produces a hit
|
||||||
|
-- we directly return true
|
||||||
|
if theList[theString] then return true, theString end
|
||||||
|
|
||||||
|
-- now try the more involved retrieval with string starts with
|
||||||
|
for idx, aName in pairs(theList) do
|
||||||
|
if dcsCommon.stringStartsWith(theString, aName) then
|
||||||
|
-- they start the same. are dynamics allowed?
|
||||||
|
if cfxReconMode.dynamics[aName] then
|
||||||
|
return true, aName
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- addScout directly adds a scout unit. Use from external
|
-- addScout directly adds a scout unit. Use from external
|
||||||
-- to manually add a unit (e.g. via GUI when autoscout isExist
|
-- to manually add a unit (e.g. via GUI when autoscout isExist
|
||||||
-- off, or to force a scout unit (e.g. when scouts for a side
|
-- off, or to force a scout unit (e.g. when scouts for a side
|
||||||
@ -205,7 +274,7 @@ end
|
|||||||
|
|
||||||
function cfxReconMode.removeScout(theUnit)
|
function cfxReconMode.removeScout(theUnit)
|
||||||
if not theUnit then
|
if not theUnit then
|
||||||
trigger.action.outText("+++cfxRecon: WARNING - nil Unit on remove", 30)
|
trigger.action.outText("+++rcn: WARNING - nil Unit on remove", 30)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -265,7 +334,8 @@ function cfxReconMode.placeMarkForUnit(location, theSide, theGroup)
|
|||||||
local theID = cfxReconMode.uuid()
|
local theID = cfxReconMode.uuid()
|
||||||
local theDesc = "Contact: "..theGroup:getName()
|
local theDesc = "Contact: "..theGroup:getName()
|
||||||
if cfxReconMode.reportNumbers then
|
if cfxReconMode.reportNumbers then
|
||||||
theDesc = theDesc .. " (" .. theGroup:getSize() .. " units)"
|
-- theDesc = theDesc .. " (" .. theGroup:getSize() .. " units)"
|
||||||
|
theDesc = theDesc .. " - " .. cfxReconMode.getSit(theGroup) .. ", " .. cfxReconMode.getAction(theGroup) .. "."
|
||||||
end
|
end
|
||||||
trigger.action.markToCoalition(
|
trigger.action.markToCoalition(
|
||||||
theID,
|
theID,
|
||||||
@ -296,6 +366,121 @@ function cfxReconMode.removeMarkForArgs(args)
|
|||||||
cfxReconMode.detectedGroups[theName] = nil -- some housekeeping.
|
cfxReconMode.detectedGroups[theName] = nil -- some housekeeping.
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.getSit(theGroup)
|
||||||
|
local msg = ""
|
||||||
|
-- analyse the group we just discovered. We know it's a ground troop, so simply differentiate between vehicles and infantry
|
||||||
|
local theUnits = theGroup:getUnits()
|
||||||
|
local numInf = 0
|
||||||
|
local numVehicles = 0
|
||||||
|
for idx, aUnit in pairs(theUnits) do
|
||||||
|
if dcsCommon.unitIsInfantry(aUnit) then
|
||||||
|
numInf = numInf + 1
|
||||||
|
else
|
||||||
|
numVehicles = numVehicles + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if numInf > 0 and numVehicles > 0 then
|
||||||
|
-- mixed infantry and vehicles
|
||||||
|
msg = numInf .. " infantry and " .. numVehicles .. " vehicles"
|
||||||
|
elseif numInf > 0 then
|
||||||
|
-- only infantry
|
||||||
|
msg = numInf .. " infantry"
|
||||||
|
else
|
||||||
|
-- only vehicles
|
||||||
|
msg = numVehicles .. " vehicles"
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.getAction(theGroup)
|
||||||
|
local msg = ""
|
||||||
|
-- simply get the first unit and get velocity vector.
|
||||||
|
-- if it's smaller than 1 m/s (= 3.6 kmh), it's "Guarding", if it's faster, it's
|
||||||
|
-- moving with direction
|
||||||
|
local theUnit = theGroup:getUnit(1)
|
||||||
|
local vvel = theUnit:getVelocity()
|
||||||
|
local vel = dcsCommon.vMag(vvel)
|
||||||
|
if vel < 1 then
|
||||||
|
msg = "apparently guarding"
|
||||||
|
else
|
||||||
|
local speed = ""
|
||||||
|
if vel < 3 then speed = "slowly"
|
||||||
|
elseif vel < 6 then speed = "deliberately"
|
||||||
|
else speed = "briskly" end
|
||||||
|
local heading = dcsCommon.getUnitHeading(theUnit) -- in rad
|
||||||
|
msg = speed .. " moving " .. dcsCommon.bearing2compass(heading)
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.getLocation(theGroup)
|
||||||
|
local msg = ""
|
||||||
|
local theUnit = theGroup:getUnit(1)
|
||||||
|
local currPoint = theUnit:getPoint()
|
||||||
|
if cfxReconMode.mgrs then
|
||||||
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
||||||
|
msg = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||||
|
else
|
||||||
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
||||||
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
|
msg = "Lat " .. lat .. " Lon " .. lon
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.getTimeData()
|
||||||
|
local msg = ""
|
||||||
|
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
||||||
|
while absSecs > 86400 do
|
||||||
|
absSecs = absSecs - 86400 -- subtract out all days
|
||||||
|
end
|
||||||
|
msg = dcsCommon.processHMS("<:h>:<:m>:<:s>", absSecs)
|
||||||
|
return "at " .. msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.generateSALT(theScout, theGroup)
|
||||||
|
local msg = theScout:getName() .. " reports new ground contact " .. theGroup:getName() .. ":\n"
|
||||||
|
-- SALT: S = Situation or number of units A = action they are doing L = Location T = Time
|
||||||
|
msg = msg .. cfxReconMode.getSit(theGroup) .. ", "-- S
|
||||||
|
msg = msg .. cfxReconMode.getAction(theGroup) .. ", " -- A
|
||||||
|
msg = msg .. cfxReconMode.getLocation(theGroup) .. ", " -- L
|
||||||
|
msg = msg .. cfxReconMode.getTimeData() -- T
|
||||||
|
|
||||||
|
return msg
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.processZoneMessage(inMsg, theZone)
|
||||||
|
if not inMsg then return "<nil inMsg>" end
|
||||||
|
local formerType = type(inMsg)
|
||||||
|
if formerType ~= "string" then inMsg = tostring(inMsg) end
|
||||||
|
if not inMsg then inMsg = "<inMsg is incompatible type " .. formerType .. ">" end
|
||||||
|
local outMsg = ""
|
||||||
|
-- replace line feeds
|
||||||
|
outMsg = inMsg:gsub("<n>", "\n")
|
||||||
|
if theZone then
|
||||||
|
outMsg = outMsg:gsub("<z>", theZone.name)
|
||||||
|
end
|
||||||
|
-- replace <t> with current mission time HMS
|
||||||
|
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
||||||
|
while absSecs > 86400 do
|
||||||
|
absSecs = absSecs - 86400 -- subtract out all days
|
||||||
|
end
|
||||||
|
local timeString = dcsCommon.processHMS("<:h>:<:m>:<:s>", absSecs)
|
||||||
|
outMsg = outMsg:gsub("<t>", timeString)
|
||||||
|
|
||||||
|
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
||||||
|
-- and <mgrs> with mgrs coords of zone point
|
||||||
|
local currPoint = cfxZones.getPoint(theZone)
|
||||||
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
||||||
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
|
outMsg = outMsg:gsub("<lat>", lat)
|
||||||
|
outMsg = outMsg:gsub("<lon>", lon)
|
||||||
|
currPoint = cfxZones.getPoint(theZone)
|
||||||
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
||||||
|
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||||
|
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
||||||
|
return outMsg
|
||||||
|
end
|
||||||
|
|
||||||
function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
||||||
-- put a mark on the map
|
-- put a mark on the map
|
||||||
@ -312,26 +497,51 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
|
|
||||||
-- say something
|
-- say something
|
||||||
if cfxReconMode.announcer then
|
if cfxReconMode.announcer then
|
||||||
trigger.action.outTextForCoalition(mySide, theScout:getName() .. " reports new ground contact " .. theGroup:getName(), 30)
|
local msg = cfxReconMode.generateSALT(theScout, theGroup)
|
||||||
trigger.action.outText("+++recon: announced for side " .. mySide, 30)
|
trigger.action.outTextForCoalition(mySide, msg, 30)
|
||||||
|
-- trigger.action.outTextForCoalition(mySide, theScout:getName() .. " reports new ground contact " .. theGroup:getName(), 30)
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: announced for side " .. mySide, 30)
|
||||||
|
end
|
||||||
-- play a sound
|
-- play a sound
|
||||||
trigger.action.outSoundForCoalition(mySide, cfxReconMode.reconSound)
|
trigger.action.outSoundForCoalition(mySide, cfxReconMode.reconSound)
|
||||||
else
|
else
|
||||||
--trigger.action.outText("+++recon: announcer off", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- see if it was a prio target
|
-- see if it was a prio target
|
||||||
if cfxReconMode.isStringInList(theGroup:getName(), cfxReconMode.prioList) then
|
local inList, gName = cfxReconMode.isStringInList(theGroup:getName(), cfxReconMode.prioList)
|
||||||
if cfxReconMode.announcer then
|
if inList then
|
||||||
trigger.action.outTextForCoalition(mySide, "Priority target confirmed", 30)
|
-- if cfxReconMode.announcer then
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: Priority target spotted", 30)
|
||||||
end
|
end
|
||||||
-- invoke callbacks
|
-- invoke callbacks
|
||||||
cfxReconMode.invokeCallbacks("priotity", mySide, theScout, theGroup, theGroup:getName())
|
cfxReconMode.invokeCallbacks("priority", mySide, theScout, theGroup, theGroup:getName())
|
||||||
|
|
||||||
-- increase prio flag
|
-- increase prio flag
|
||||||
if cfxReconMode.prioFlag then
|
if cfxReconMode.prioFlag then
|
||||||
local currVal = trigger.misc.getUserFlag(cfxReconMode.prioFlag)
|
cfxZones.pollFlag(cfxReconMode.prioFlag, cfxReconMode.method, cfxReconMode.theZone)
|
||||||
trigger.action.setUserFlag(cfxReconMode.prioFlag, currVal + 1)
|
end
|
||||||
|
|
||||||
|
-- see if we were passed additional info in zInfo
|
||||||
|
if gName and cfxReconMode.zoneInfo[gName] then
|
||||||
|
local zInfo = cfxReconMode.zoneInfo[gName]
|
||||||
|
if zInfo.prioMessage then
|
||||||
|
-- prio message displays even when announcer is off
|
||||||
|
local msg = zInfo.prioMessage
|
||||||
|
msg = cfxReconMode.processZoneMessage(msg, zInfo.theZone)
|
||||||
|
trigger.action.outTextForCoalition(mySide, msg, 30)
|
||||||
|
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
||||||
|
trigger.action.outText("+++rcn: prio message sent for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if zInfo.theFlag then
|
||||||
|
cfxZones.pollFlag(zInfo.theFlag, cfxReconMode.method, zInfo.theZone)
|
||||||
|
if cfxReconMode.verbose or zInfo.theZone.verbose then
|
||||||
|
trigger.action.outText("+++rcn: banging <" .. zInfo.theFlag .. "> for prio target zone <" .. zInfo.theZone.name .. ">",30)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- invoke callbacks
|
-- invoke callbacks
|
||||||
@ -339,8 +549,7 @@ function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
|
|||||||
|
|
||||||
-- increase normal flag
|
-- increase normal flag
|
||||||
if cfxReconMode.detectFlag then
|
if cfxReconMode.detectFlag then
|
||||||
local currVal = trigger.misc.getUserFlag(cfxReconMode.detectFlag)
|
cfxZones.pollFlag(cfxReconMode.detectFlag, cfxReconMode.method, cfxReconMode.theZone)
|
||||||
trigger.action.setUserFlag(cfxReconMode.detectFlag, currVal + 1)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -371,7 +580,8 @@ function cfxReconMode.performReconForUnit(theScout)
|
|||||||
local groupName = theGroup:getName()
|
local groupName = theGroup:getName()
|
||||||
if cfxReconMode.detectedGroups[groupName] == nil then
|
if cfxReconMode.detectedGroups[groupName] == nil then
|
||||||
-- only now check against blackList
|
-- only now check against blackList
|
||||||
if not cfxReconMode.isStringInList(groupName, cfxReconMode.blackList) then
|
local inList, gName = cfxReconMode.isStringInList(groupName, cfxReconMode.blackList)
|
||||||
|
if not inList then
|
||||||
-- visible and not yet seen
|
-- visible and not yet seen
|
||||||
-- perhaps add some percent chance now
|
-- perhaps add some percent chance now
|
||||||
-- remember that we know this group
|
-- remember that we know this group
|
||||||
@ -464,11 +674,62 @@ function cfxReconMode.autoRemove()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- late eval player
|
||||||
|
function cfxReconMode.lateEvalPlayerUnit(theUnit)
|
||||||
|
-- check if a player is inside one of the scout zones
|
||||||
|
-- first: quick check if the player is already in a list
|
||||||
|
local aGroup = theUnit:getGroup()
|
||||||
|
local gName = aGroup:getName()
|
||||||
|
if cfxReconMode.allowedScouts[gName] then return end
|
||||||
|
if cfxReconMode.blindScouts[gName] then return end
|
||||||
|
|
||||||
|
-- get location
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
|
||||||
|
-- iterate all scoutZones
|
||||||
|
for idx, theZone in pairs (cfxReconMode.scoutZones) do
|
||||||
|
local isScout = theZone.isScout
|
||||||
|
local dynamic = theZone.dynamic
|
||||||
|
local inZone = cfxZones.pointInZone(p, theZone)
|
||||||
|
if inZone then
|
||||||
|
if isScout then
|
||||||
|
cfxReconMode.addToAllowedScoutList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then
|
||||||
|
trigger.action.outText("+++rcn: added LATE DYNAMIC PLAYER" .. gName .. " to allowed scouts", 30)
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++rcn: added LATE PLAYER " .. gName .. " to allowed scouts", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cfxReconMode.addToBlindScoutList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then
|
||||||
|
trigger.action.outText("+++rcn: added LATE DYNAMIC PLAYER" .. gName .. " to BLIND scouts list", 30)
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++rcn: added LATE PLAYER " .. gName .. " to BLIND scouts list", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return -- we stop after first found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- event handler
|
-- event handler
|
||||||
function cfxReconMode:onEvent(event)
|
function cfxReconMode:onEvent(event)
|
||||||
if not event then return end
|
if not event then return end
|
||||||
if not event.initiator then return end
|
if not event.initiator then return end
|
||||||
|
if not (event.id == 15 or event.id == 3) then return end
|
||||||
|
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
|
if not theUnit:isExist() then return end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
-- trigger.action.outText("+++rcn-ENTER onEvent: " .. event.id .. " for <" .. theUnit:getName() .. ">", 30)
|
||||||
|
if not theGroup then return end
|
||||||
|
local gCat = theGroup:getCategory()
|
||||||
|
-- only continue if cat = 0 (aircraft) or 1 (helo)
|
||||||
|
if gCat > 1 then return end
|
||||||
|
|
||||||
-- we simply add scouts as they are garbage-collected
|
-- we simply add scouts as they are garbage-collected
|
||||||
-- every so often when they do not exist
|
-- every so often when they do not exist
|
||||||
@ -481,6 +742,20 @@ function cfxReconMode:onEvent(event)
|
|||||||
-- scout when they are on that side. in that case
|
-- scout when they are on that side. in that case
|
||||||
-- you must add manually
|
-- you must add manually
|
||||||
local theSide = theUnit:getCoalition()
|
local theSide = theUnit:getCoalition()
|
||||||
|
|
||||||
|
local isPlayer = theUnit:getPlayerName()
|
||||||
|
if isPlayer then
|
||||||
|
-- since players wake up late, we lazy-eval their group
|
||||||
|
-- and add it to the blind/scout lists
|
||||||
|
cfxReconMode.lateEvalPlayerUnit(theUnit)
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: late player check complete for <" .. theUnit:getName() .. ">", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
isPlayer = false -- safer than sorry
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfxReconMode.autoRecon then
|
||||||
if theSide == 0 and not cfxReconMode.greyScouts then
|
if theSide == 0 and not cfxReconMode.greyScouts then
|
||||||
return -- grey scouts are not allowed
|
return -- grey scouts are not allowed
|
||||||
end
|
end
|
||||||
@ -492,15 +767,46 @@ function cfxReconMode:onEvent(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if cfxReconMode.playerOnlyRecon then
|
if cfxReconMode.playerOnlyRecon then
|
||||||
if not theUnit:getPlayerName() then
|
if not isPlayer then
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: <" .. theUnit:getName() .. "> filtered: no player unit", 30)
|
||||||
|
end
|
||||||
return -- only players can do recon. this unit is AI
|
return -- only players can do recon. this unit is AI
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if cfxReconMode.autoRecon is enabled
|
||||||
|
-- otherwise, abort the aircraft is not in
|
||||||
|
-- scourlist
|
||||||
|
local gName = theGroup:getName()
|
||||||
|
if not cfxReconMode.autoRecon then
|
||||||
|
-- no auto-recon. plane must be in scouts list
|
||||||
|
local inList, ignored = cfxReconMode.isStringInList(gName, cfxReconMode.allowedScouts)
|
||||||
|
if not inList then
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: <" .. theUnit:getName() .. "> filtered: not in scout list", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if aircraft is in blindlist
|
||||||
|
-- abort if so
|
||||||
|
local inList, ignored = cfxReconMode.isStringInList(gName, cfxReconMode.blindScouts)
|
||||||
|
if inList then
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: <" .. theUnit:getName() .. "> filtered: unit cannot scout", 30)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if cfxReconMode.verbose then
|
if cfxReconMode.verbose then
|
||||||
trigger.action.outText("+++rcn: event " .. event.id .. " for unit " .. theUnit:getName(), 30)
|
trigger.action.outText("+++rcn: event " .. event.id .. " for unit " .. theUnit:getName(), 30)
|
||||||
end
|
end
|
||||||
cfxReconMode.addScout(theUnit)
|
cfxReconMode.addScout(theUnit)
|
||||||
end
|
end
|
||||||
|
-- trigger.action.outText("+++rcn-onEvent: " .. event.id .. " for <" .. theUnit:getName() .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -512,9 +818,38 @@ function cfxReconMode.processScoutGroups(theGroups)
|
|||||||
-- we are very early in the mission, only few groups really
|
-- we are very early in the mission, only few groups really
|
||||||
-- exist now, the rest of the units come in with 15 event
|
-- exist now, the rest of the units come in with 15 event
|
||||||
if aGroup:isExist() then
|
if aGroup:isExist() then
|
||||||
|
-- see if we want to add these aircraft to the
|
||||||
|
-- active scout list
|
||||||
|
|
||||||
|
local gName = aGroup:getName()
|
||||||
|
local isBlind, ignored = cfxReconMode.isStringInList(gName, cfxReconMode.blindScouts)
|
||||||
|
local isScout, ignored = cfxReconMode.isStringInList(gName, cfxReconMode.allowedScouts)
|
||||||
|
|
||||||
|
local doAdd = cfxReconMode.autoRecon
|
||||||
|
if cfxReconMode.autoRecon then
|
||||||
|
local theSide = aGroup:getCoalition()
|
||||||
|
if theSide == 0 and not cfxReconMode.greyScouts then
|
||||||
|
doAdd = false
|
||||||
|
elseif theSide == 1 and not cfxReconMode.redScouts then
|
||||||
|
doAdd = false
|
||||||
|
elseif theSide == 2 and not cfxReconMode.blueScouts then
|
||||||
|
doAdd = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if isBlind then doAdd = false end
|
||||||
|
if isScout then doAdd = true end -- overrides all
|
||||||
|
|
||||||
|
if doAdd then
|
||||||
local allUnits = Group.getUnits(aGroup)
|
local allUnits = Group.getUnits(aGroup)
|
||||||
for idy, aUnit in pairs (allUnits) do
|
for idy, aUnit in pairs (allUnits) do
|
||||||
if aUnit:isExist() then
|
if aUnit:isExist() then
|
||||||
|
if cfxReconMode.autoRecon and cfxReconMode.playerOnlyRecon and (aUnit:getPlayerName() == nil)
|
||||||
|
then
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: skipped unit " ..aUnit:getName() .. " because not player unit", 30)
|
||||||
|
end
|
||||||
|
else
|
||||||
cfxReconMode.addScout(aUnit)
|
cfxReconMode.addScout(aUnit)
|
||||||
if cfxReconMode.verbose then
|
if cfxReconMode.verbose then
|
||||||
trigger.action.outText("+++rcn: added unit " ..aUnit:getName() .. " to pool at startup", 30)
|
trigger.action.outText("+++rcn: added unit " ..aUnit:getName() .. " to pool at startup", 30)
|
||||||
@ -522,25 +857,34 @@ function cfxReconMode.processScoutGroups(theGroups)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if cfxReconMode.verbose then
|
||||||
|
trigger.action.outText("+++rcn: filtered group " .. gName .. " from being entered into scout pool at startup", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function cfxReconMode.initScouts()
|
function cfxReconMode.initScouts()
|
||||||
-- get all groups of aircraft. Unrolled loop 0..2
|
-- get all groups of aircraft. Unrolled loop 0..2
|
||||||
|
-- added helicopters, removed check for grey/red/bluescouts,
|
||||||
|
-- as that happens in processScoutGroups
|
||||||
local theAirGroups = {}
|
local theAirGroups = {}
|
||||||
if cfxReconMode.greyScouts then
|
|
||||||
theAirGroups = coalition.getGroups(0, 0) -- 0 = aircraft
|
theAirGroups = coalition.getGroups(0, 0) -- 0 = aircraft
|
||||||
cfxReconMode.processScoutGroups(theAirGroups)
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
end
|
theAirGroups = coalition.getGroups(0, 1) -- 1 = helicopter
|
||||||
if cfxReconMode.redScouts then
|
|
||||||
theAirGroups = coalition.getGroups(1, 0) -- 1 = red, 0 = aircraft
|
|
||||||
cfxReconMode.processScoutGroups(theAirGroups)
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
end
|
|
||||||
|
|
||||||
if cfxReconMode.blueScouts then
|
theAirGroups = coalition.getGroups(1, 0) -- 0 = aircraft
|
||||||
theAirGroups = coalition.getGroups(2, 0) -- 2 = blue, 0 = aircraft
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
|
theAirGroups = coalition.getGroups(1, 1) -- 1 = helicopter
|
||||||
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
|
|
||||||
|
theAirGroups = coalition.getGroups(2, 0) -- 0 = aircraft
|
||||||
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
|
theAirGroups = coalition.getGroups(2, 1) -- 1 = helicopter
|
||||||
cfxReconMode.processScoutGroups(theAirGroups)
|
cfxReconMode.processScoutGroups(theAirGroups)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -575,12 +919,20 @@ function cfxReconMode.readConfigZone()
|
|||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "prio+") then
|
if cfxZones.hasProperty(theZone, "prio+") then
|
||||||
cfxReconMode.prioFlag = cfxZones.getStringFromZoneProperty(theZone, "prio+", "none")
|
cfxReconMode.prioFlag = cfxZones.getStringFromZoneProperty(theZone, "prio+", "none")
|
||||||
|
elseif cfxZones.hasProperty(theZone, "prio!") then
|
||||||
|
cfxReconMode.prioFlag = cfxZones.getStringFromZoneProperty(theZone, "prio!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "detect+") then
|
if cfxZones.hasProperty(theZone, "detect+") then
|
||||||
cfxReconMode.detectFlag = cfxZones.getStringFromZoneProperty(theZone, "detect+", "none")
|
cfxReconMode.detectFlag = cfxZones.getStringFromZoneProperty(theZone, "detect+", "none")
|
||||||
|
elseif cfxZones.hasProperty(theZone, "detect!") then
|
||||||
|
cfxReconMode.detectFlag = cfxZones.getStringFromZoneProperty(theZone, "detect!", "*<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
cfxReconMode.method = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
|
||||||
|
if cfxZones.hasProperty(theZone, "reconMethod") then
|
||||||
|
cfxReconMode.method = cfxZones.getStringFromZoneProperty(theZone, "reconMethod", "inc")
|
||||||
|
end
|
||||||
|
|
||||||
cfxReconMode.applyMarks = cfxZones.getBoolFromZoneProperty(theZone, "applyMarks", true)
|
cfxReconMode.applyMarks = cfxZones.getBoolFromZoneProperty(theZone, "applyMarks", true)
|
||||||
cfxReconMode.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
cfxReconMode.announcer = cfxZones.getBoolFromZoneProperty(theZone, "announcer", true)
|
||||||
@ -591,6 +943,101 @@ function cfxReconMode.readConfigZone()
|
|||||||
|
|
||||||
cfxReconMode.removeWhenDestroyed = cfxZones.getBoolFromZoneProperty(theZone, "autoRemove", true)
|
cfxReconMode.removeWhenDestroyed = cfxZones.getBoolFromZoneProperty(theZone, "autoRemove", true)
|
||||||
|
|
||||||
|
cfxReconMode.mgrs = cfxZones.getBoolFromZoneProperty(theZone, "mgrs", false)
|
||||||
|
|
||||||
|
cfxReconMode.theZone = theZone -- save this zone
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- read blackList and prio list groups
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
function cfxReconMode.processReconZone(theZone)
|
||||||
|
local theList = cfxZones.getStringFromZoneProperty(theZone, "recon", "prio")
|
||||||
|
theList = string.upper(theList)
|
||||||
|
local isBlack = dcsCommon.stringStartsWith(theList, "BLACK")
|
||||||
|
|
||||||
|
local zInfo = {}
|
||||||
|
zInfo.theZone = theZone
|
||||||
|
zInfo.isBlack = isBlack
|
||||||
|
if cfxZones.hasProperty(theZone, "spotted!") then
|
||||||
|
zInfo.theFlag = cfxZones.getStringFromZoneProperty(theZone, "spotted!", "*<none>")
|
||||||
|
end
|
||||||
|
|
||||||
|
if cfxZones.hasProperty(theZone, "prioMessage") then
|
||||||
|
zInfo.prioMessage = cfxZones.getStringFromZoneProperty(theZone, "prioMessage", "<none>")
|
||||||
|
end
|
||||||
|
|
||||||
|
local dynamic = cfxZones.getBoolFromZoneProperty(theZone, "dynamic", false)
|
||||||
|
zInfo.dynamic = dynamic
|
||||||
|
local categ = 2 -- ground troops only
|
||||||
|
local allGroups = cfxZones.allGroupsInZone(theZone, categ)
|
||||||
|
for idx, aGroup in pairs(allGroups) do
|
||||||
|
local gName = aGroup:getName()
|
||||||
|
cfxReconMode.zoneInfo[gName] = zInfo
|
||||||
|
if isBlack then
|
||||||
|
cfxReconMode.addToBlackList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then trigger.action.outText("+++rcn: added DYNAMIC " .. aGroup:getName() .. " to blacklist", 30)
|
||||||
|
else trigger.action.outText("+++rcn: added " .. aGroup:getName() .. " to blacklist", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cfxReconMode.addToPrioList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then trigger.action.outText("+++rcn: added DYNAMIC " .. aGroup:getName() .. " to priority target list", 30)
|
||||||
|
else trigger.action.outText("+++rcn: added " .. aGroup:getName() .. " to priority target list", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.processScoutZone(theZone)
|
||||||
|
local isScout = cfxZones.getBoolFromZoneProperty(theZone, "scout", true)
|
||||||
|
local dynamic = cfxZones.getBoolFromZoneProperty(theZone, "dynamic")
|
||||||
|
theZone.dynamic = dynamic
|
||||||
|
theZone.isScout = isScout
|
||||||
|
|
||||||
|
local categ = 0 -- aircraft
|
||||||
|
local allFixed = cfxZones.allGroupsInZone(theZone, categ)
|
||||||
|
local categ = 1 -- helos
|
||||||
|
local allRotor = cfxZones.allGroupsInZone(theZone, categ)
|
||||||
|
local allGroups = dcsCommon.combineTables(allFixed, allRotor)
|
||||||
|
for idx, aGroup in pairs(allGroups) do
|
||||||
|
if isScout then
|
||||||
|
cfxReconMode.addToAllowedScoutList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then trigger.action.outText("+++rcn: added DYNAMIC " .. aGroup:getName() .. " to allowed scouts", 30)
|
||||||
|
else trigger.action.outText("+++rcn: added " .. aGroup:getName() .. " to allowed scouts", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cfxReconMode.addToBlindScoutList(aGroup, dynamic)
|
||||||
|
if cfxReconMode.verbose or theZone.verbose then
|
||||||
|
if dynamic then trigger.action.outText("+++rcn: added DYNAMIC " .. aGroup:getName() .. " to BLIND scouts list", 30)
|
||||||
|
else trigger.action.outText("+++rcn: added " .. aGroup:getName() .. " to BLIND scouts list", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(cfxReconMode.scoutZones, theZone)
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.readReconGroups()
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("recon")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
cfxReconMode.processReconZone(aZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxReconMode.readScoutGroups()
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("scout")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
cfxReconMode.processScoutZone(aZone)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -606,6 +1053,12 @@ function cfxReconMode.start()
|
|||||||
-- read config
|
-- read config
|
||||||
cfxReconMode.readConfigZone()
|
cfxReconMode.readConfigZone()
|
||||||
|
|
||||||
|
-- gather prio and blacklist groups
|
||||||
|
cfxReconMode.readReconGroups()
|
||||||
|
|
||||||
|
-- gather allowed and forbidden scouts
|
||||||
|
cfxReconMode.readScoutGroups()
|
||||||
|
|
||||||
-- gather exiting planes
|
-- gather exiting planes
|
||||||
cfxReconMode.initScouts()
|
cfxReconMode.initScouts()
|
||||||
|
|
||||||
@ -618,7 +1071,7 @@ function cfxReconMode.start()
|
|||||||
cfxReconMode.autoRemove()
|
cfxReconMode.autoRemove()
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxReconMode.autoRecon then
|
if true or cfxReconMode.autoRecon then
|
||||||
-- install own event handler to detect
|
-- install own event handler to detect
|
||||||
-- when a unit takes off and add it to scout
|
-- when a unit takes off and add it to scout
|
||||||
-- roster
|
-- roster
|
||||||
@ -651,8 +1104,9 @@ ideas:
|
|||||||
- renew lease. when already sighted, simply renew lease, maybe update location.
|
- renew lease. when already sighted, simply renew lease, maybe update location.
|
||||||
- update marks and renew lease
|
- update marks and renew lease
|
||||||
TODO: red+ and blue+ - flags to increase when a plane of the other side is detected
|
TODO: red+ and blue+ - flags to increase when a plane of the other side is detected
|
||||||
|
TODO: recon: scout and blind for aircraft in group to add / remove scouts, maybe use scout keyword
|
||||||
|
|
||||||
|
allow special bangs per priority group
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1516,7 +1516,7 @@ function cfxZones.testZoneFlag(theZone, theFlagName, theMethod, latchName)
|
|||||||
-- get last value from latch
|
-- get last value from latch
|
||||||
local lastVal = theZone[latchName]
|
local lastVal = theZone[latchName]
|
||||||
if not lastVal then
|
if not lastVal then
|
||||||
trigger.action.outText("+++Zne: latch <" .. latchName .. "> not valid for zone " .. theZone.name, 30)
|
trigger.action.outText("+++Zne: latch <" .. latchName .. "> not valid for zone " .. theZone.name, 30) -- intentional break here
|
||||||
return nil, nil
|
return nil, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.6.4"
|
dcsCommon.version = "2.6.5"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
2.2.6 - compassPositionOfARelativeToB
|
||||||
- clockPositionOfARelativeToB
|
- clockPositionOfARelativeToB
|
||||||
@ -74,6 +74,9 @@ dcsCommon.version = "2.6.4"
|
|||||||
2.6.2 - new combineTables()
|
2.6.2 - new combineTables()
|
||||||
2.6.3 - new tacan2freq()
|
2.6.3 - new tacan2freq()
|
||||||
2.6.4 - new processHMS()
|
2.6.4 - new processHMS()
|
||||||
|
2.6.5 - new bearing2compass()
|
||||||
|
- new bearingdegrees2compass()
|
||||||
|
- new latLon2Text() - based on mist
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -530,6 +533,25 @@ dcsCommon.version = "2.6.4"
|
|||||||
return "North"
|
return "North"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.bearing2compass(inrad)
|
||||||
|
local bearing = math.floor(inrad / math.pi * 180)
|
||||||
|
if bearing < 0 then bearing = bearing + 360 end
|
||||||
|
if bearing > 360 then bearing = bearing - 360 end
|
||||||
|
return dcsCommon.bearingdegrees2compass(bearing)
|
||||||
|
end
|
||||||
|
|
||||||
|
function dcsCommon.bearingdegrees2compass(bearing)
|
||||||
|
if bearing < 23 then return "North" end
|
||||||
|
if bearing < 68 then return "NE" end
|
||||||
|
if bearing < 112 then return "East" end
|
||||||
|
if bearing < 158 then return "SE" end
|
||||||
|
if bearing < 202 then return "South" end
|
||||||
|
if bearing < 248 then return "SW" end
|
||||||
|
if bearing < 292 then return "West" end
|
||||||
|
if bearing < 338 then return "NW" end
|
||||||
|
return "North"
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.clockPositionOfARelativeToB(A, B, headingOfBInDegrees)
|
function dcsCommon.clockPositionOfARelativeToB(A, B, headingOfBInDegrees)
|
||||||
-- o'clock notation
|
-- o'clock notation
|
||||||
if not A then return "***error:A***" end
|
if not A then return "***error:A***" end
|
||||||
@ -2212,7 +2234,7 @@ function dcsCommon.getUnitHeading(theUnit)
|
|||||||
local pos = theUnit:getPosition() -- returns three vectors, p is location
|
local pos = theUnit:getPosition() -- returns three vectors, p is location
|
||||||
|
|
||||||
local heading = math.atan2(pos.x.z, pos.x.x)
|
local heading = math.atan2(pos.x.z, pos.x.x)
|
||||||
-- make sure positive only, add 260 degrees
|
-- make sure positive only, add 360 degrees
|
||||||
if heading < 0 then
|
if heading < 0 then
|
||||||
heading = heading + 2 * math.pi -- put heading in range of 0 to 2*pi
|
heading = heading + 2 * math.pi -- put heading in range of 0 to 2*pi
|
||||||
end
|
end
|
||||||
@ -2255,6 +2277,50 @@ function dcsCommon.coalition2county(inCoalition)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.latLon2Text(lat, lon)
|
||||||
|
-- inspired by mist, thanks Grimes!
|
||||||
|
-- returns two strings: lat and lon
|
||||||
|
|
||||||
|
-- determine hemispheres by sign
|
||||||
|
local latHemi, lonHemi
|
||||||
|
if lat > 0 then latHemi = 'N' else latHemi = 'S' end
|
||||||
|
if lon > 0 then lonHemi = 'E' else lonHemi = 'W' end
|
||||||
|
|
||||||
|
-- remove sign since we have hemi
|
||||||
|
lat = math.abs(lat)
|
||||||
|
lon = math.abs(lon)
|
||||||
|
|
||||||
|
-- calc deg / mins
|
||||||
|
local latDeg = math.floor(lat)
|
||||||
|
local latMin = (lat - latDeg) * 60
|
||||||
|
local lonDeg = math.floor(lon)
|
||||||
|
local lonMin = (lon - lonDeg) * 60
|
||||||
|
|
||||||
|
-- calc seconds
|
||||||
|
local rawLatMin = latMin
|
||||||
|
latMin = math.floor(latMin)
|
||||||
|
local latSec = (rawLatMin - latMin) * 60
|
||||||
|
local rawLonMin = lonMin
|
||||||
|
lonMin = math.floor(lonMin)
|
||||||
|
local lonSec = (rawLonMin - lonMin) * 60
|
||||||
|
|
||||||
|
-- correct for rounding errors
|
||||||
|
if latSec >= 60 then
|
||||||
|
latSec = latSec - 60
|
||||||
|
latMin = latMin + 1
|
||||||
|
end
|
||||||
|
if lonSec >= 60 then
|
||||||
|
lonSec = lonSec - 60
|
||||||
|
lonMin = lonMin + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- prepare string output
|
||||||
|
local secFrmtStr = '%06.3f'
|
||||||
|
local lat = string.format('%02d', latDeg) .. '°' .. string.format('%02d', latMin) .. "'" .. string.format(secFrmtStr, latSec) .. '"' .. latHemi
|
||||||
|
local lon = string.format('%02d', lonDeg) .. '°' .. string.format('%02d', lonMin) .. "'" .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
||||||
|
return lat, lon
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- INIT
|
-- INIT
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
guardianAngel = {}
|
guardianAngel = {}
|
||||||
guardianAngel.version = "2.0.3"
|
guardianAngel.version = "3.0.0"
|
||||||
guardianAngel.ups = 10
|
guardianAngel.ups = 10
|
||||||
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
guardianAngel.launchWarning = true -- detect launches and warn pilot
|
||||||
guardianAngel.intervention = true -- remove missiles just before hitting
|
guardianAngel.intervention = true -- remove missiles just before hitting
|
||||||
@ -9,6 +9,10 @@ guardianAngel.announcer = true -- angel talks to you
|
|||||||
guardianAngel.private = false -- angel only talks to group
|
guardianAngel.private = false -- angel only talks to group
|
||||||
guardianAngel.autoAddPlayers = true
|
guardianAngel.autoAddPlayers = true
|
||||||
|
|
||||||
|
guardianAngel.active = true -- can be turned on / off
|
||||||
|
|
||||||
|
guardianAngel.angelicZones = {}
|
||||||
|
|
||||||
guardianAngel.requiredLibs = {
|
guardianAngel.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -37,6 +41,18 @@ guardianAngel.requiredLibs = {
|
|||||||
- can be dangerous
|
- can be dangerous
|
||||||
2.0.3 - fxDistance
|
2.0.3 - fxDistance
|
||||||
- mea cupa capability
|
- mea cupa capability
|
||||||
|
3.0.0 - on/off and switch monitoring
|
||||||
|
- active flag
|
||||||
|
- zones to designate protected aircraft
|
||||||
|
- zones to designate unprotected aircraft
|
||||||
|
- improved gA logging
|
||||||
|
- missilesAndTargets log
|
||||||
|
- re-targeting detection
|
||||||
|
- removed bubble check
|
||||||
|
- retarget Item code
|
||||||
|
- hardened missile disappear code
|
||||||
|
- all missiles are now tracked regardless whom they aim for
|
||||||
|
- removed item.wp
|
||||||
|
|
||||||
|
|
||||||
This script detects missiles launched against protected aircraft an
|
This script detects missiles launched against protected aircraft an
|
||||||
@ -51,7 +67,7 @@ guardianAngel.safetyFactor = 1.8 -- for calculating dealloc range
|
|||||||
guardianAngel.unitsToWatchOver = {} -- I'll watch over these
|
guardianAngel.unitsToWatchOver = {} -- I'll watch over these
|
||||||
|
|
||||||
guardianAngel.missilesInTheAir = {} -- missiles in the air
|
guardianAngel.missilesInTheAir = {} -- missiles in the air
|
||||||
|
guardianAngel.missilesAndTargets = {} -- permanent log which missile was aimed at whom
|
||||||
guardianAngel.callBacks = {} -- callbacks
|
guardianAngel.callBacks = {} -- callbacks
|
||||||
-- callback signature: callBack(reason, targetUnitName, weaponName)
|
-- callback signature: callBack(reason, targetUnitName, weaponName)
|
||||||
-- reasons (string): "launch", "miss", "reacquire", "trackloss", "disappear", "intervention"
|
-- reasons (string): "launch", "miss", "reacquire", "trackloss", "disappear", "intervention"
|
||||||
@ -77,9 +93,14 @@ function guardianAngel.addUnitToWatch(aUnit)
|
|||||||
end
|
end
|
||||||
if not aUnit then return end
|
if not aUnit then return end
|
||||||
local unitName = aUnit:getName()
|
local unitName = aUnit:getName()
|
||||||
|
local isNew = guardianAngel.unitsToWatchOver[unitName] == nil
|
||||||
guardianAngel.unitsToWatchOver[unitName] = aUnit
|
guardianAngel.unitsToWatchOver[unitName] = aUnit
|
||||||
if guardianAngel.verbose then
|
if guardianAngel.verbose then
|
||||||
trigger.action.outText("+++gA: now watching unit " .. aUnit:getName(), 30)
|
if isNew then
|
||||||
|
trigger.action.outText("+++gA: now watching unit " .. unitName, 30)
|
||||||
|
else
|
||||||
|
trigger.action.outText("+++gA: updating unit " .. unitName, 30)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -104,14 +125,17 @@ end
|
|||||||
--
|
--
|
||||||
-- watch q items
|
-- watch q items
|
||||||
--
|
--
|
||||||
function guardianAngel.createQItem(theWeapon, theTarget, detectProbability)
|
function guardianAngel.createQItem(theWeapon, theTarget, threat)
|
||||||
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
|
||||||
if not detectProbability then detectProbability = 1.0 end
|
if not threat then threat = false end
|
||||||
|
-- if an item is not a 'threat' it means that we merely
|
||||||
|
-- watch it for re-targeting purposes
|
||||||
|
|
||||||
local theItem = {}
|
local theItem = {}
|
||||||
theItem.theWeapon = theWeapon
|
theItem.theWeapon = theWeapon -- weapon that we are tracking
|
||||||
theItem.wP = theWeapon:getPoint() -- save location
|
--theItem.wP = theWeapon:getPoint() -- save location
|
||||||
theItem.weaponName = theWeapon:getName()
|
theItem.weaponName = theWeapon:getName()
|
||||||
theItem.theTarget = theTarget
|
theItem.theTarget = theTarget
|
||||||
theItem.tGroup = theTarget:getGroup()
|
theItem.tGroup = theTarget:getGroup()
|
||||||
@ -119,14 +143,63 @@ function guardianAngel.createQItem(theWeapon, theTarget, detectProbability)
|
|||||||
|
|
||||||
theItem.targetName = theTarget:getName()
|
theItem.targetName = theTarget:getName()
|
||||||
theItem.launchTimeStamp = timer.getTime()
|
theItem.launchTimeStamp = timer.getTime()
|
||||||
theItem.lastCheckTimeStamp = -1000
|
--theItem.lastCheckTimeStamp = -1000
|
||||||
theItem.lastDistance = math.huge
|
theItem.lastDistance = math.huge
|
||||||
theItem.detected = false
|
theItem.detected = false
|
||||||
theItem.lostTrack = false -- so we can detect sneakies!
|
--theItem.lostTrack = false -- so we can detect sneakies!
|
||||||
theItem.missed = false -- just keep watching for re-ack
|
theItem.missed = false -- just keep watching for re-ack
|
||||||
|
theItem.threat = threat
|
||||||
|
theItem.lastDesc = "(new)"
|
||||||
|
theItem.timeStamp = timer.getTime()
|
||||||
return theItem
|
return theItem
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function guardianAngel.retargetItem(theItem, theTarget, threat)
|
||||||
|
theItem.theTarget = nil -- may cause trouble
|
||||||
|
if not theTarget or not theTarget:isExist() then
|
||||||
|
theItem.threat = false
|
||||||
|
theItem.timeStamp = timer.getTime()
|
||||||
|
theItem.target = nil
|
||||||
|
theItem.targetName = "(substitute)"
|
||||||
|
theItem.lastDistance = math.huge
|
||||||
|
-- theItem.lostTrack = false
|
||||||
|
theItem.missed = false
|
||||||
|
theItem.lastDesc = "(retarget)"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not threat then threat = false end
|
||||||
|
theItem.timeStamp = timer.getTime()
|
||||||
|
theItem.threat = threat
|
||||||
|
|
||||||
|
theItem.theTarget = theTarget
|
||||||
|
if not theTarget.getGroup then
|
||||||
|
local theCat = theTarget:getCategory()
|
||||||
|
if theCat ~= 2 then
|
||||||
|
-- not a weapon / flare
|
||||||
|
trigger.action.outText("*** gA: WARNING: <" .. theTarget:getName() .. "> has no getGroup and is of category <" .. theCat .. ">!!!", 30)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- target is a weapon (flare/chaff/decoy), all is well
|
||||||
|
end
|
||||||
|
else
|
||||||
|
theItem.tGroup = theTarget:getGroup()
|
||||||
|
theItem.tID = theItem.tGroup:getID()
|
||||||
|
end
|
||||||
|
theItem.targetName = theTarget:getName()
|
||||||
|
theItem.lastDistance = math.huge
|
||||||
|
--theItem.lostTrack = false
|
||||||
|
theItem.missed = false
|
||||||
|
theItem.lastDesc = "(retarget)"
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.getQItemForWeaponNamed(theName)
|
||||||
|
for idx, theItem in pairs (guardianAngel.missilesInTheAir) do
|
||||||
|
if theItem.weaponName == theName then
|
||||||
|
return theItem
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- calculate a point in direction from plane (pln) to weapon (wpn), dist meters
|
-- calculate a point in direction from plane (pln) to weapon (wpn), dist meters
|
||||||
function guardianAngel.calcSafeExplosionPoint(wpn, pln, dist)
|
function guardianAngel.calcSafeExplosionPoint(wpn, pln, dist)
|
||||||
@ -138,6 +211,7 @@ function guardianAngel.calcSafeExplosionPoint(wpn, pln, dist)
|
|||||||
return newPoint
|
return newPoint
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
function guardianAngel.bubbleCheck(wPos, w)
|
function guardianAngel.bubbleCheck(wPos, w)
|
||||||
if true then return false end
|
if true then return false end
|
||||||
for idx, aProtectee in pairs (guardianAngel.unitsToWatchOver) do
|
for idx, aProtectee in pairs (guardianAngel.unitsToWatchOver) do
|
||||||
@ -154,41 +228,102 @@ function guardianAngel.bubbleCheck(wPos, w)
|
|||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
function guardianAngel.monitorItem(theItem)
|
function guardianAngel.monitorItem(theItem)
|
||||||
local w = theItem.theWeapon
|
local w = theItem.theWeapon
|
||||||
local ID = theItem.tID
|
local ID = theItem.tID
|
||||||
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"
|
local desc = theItem.weaponName .. ": DISAPPEARED"
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer and theItem.threat then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
trigger.action.outTextForGroup(ID, desc, 30)
|
trigger.action.outTextForGroup(ID, desc, 30)
|
||||||
else
|
else
|
||||||
trigger.action.outText(desc, 30)
|
trigger.action.outText(desc, 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
guardianAngel.invokeCallbacks("disappear", theItem.targetName, theItem.weaponName)
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: missile disappeared: <" .. theItem.weaponName .. ">, aimed at <" .. theItem.targetName .. ">",30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
guardianAngel.invokeCallbacks("disappear", theItem.targetName, theItem.weaponName)
|
||||||
|
-- end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local t = theItem.theTarget
|
local t = theItem.theTarget
|
||||||
local currentTarget = w:getTarget()
|
local currentTarget = w:getTarget()
|
||||||
local oldWPos = theItem.wP
|
|
||||||
|
-- Re-target check. did missile pick a new target?
|
||||||
|
-- this can happen with any missile, even threat missiles,
|
||||||
|
-- so do this always!
|
||||||
|
local ctName = nil
|
||||||
|
if currentTarget then
|
||||||
|
-- get current name to check against last target name
|
||||||
|
ctName = currentTarget:getName()
|
||||||
|
else
|
||||||
|
-- currentTarget has disappeared, kill the 'threat flag'
|
||||||
|
-- theItem.threat = false
|
||||||
|
ctName = "***guardianangel.not.set"
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctName and ctName ~= theItem.targetName then
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
--trigger.action.outText("+++gA: RETARGETING for <" .. theItem.weaponName .. ">: from <" .. theItem.targetName .. "> to <" .. ctName .. ">", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- see if it's a threat to us now
|
||||||
|
local watchedUnit = guardianAngel.getWatchedUnitByName(ctName)
|
||||||
|
|
||||||
|
-- update the db who's seeking who
|
||||||
|
guardianAngel.missilesAndTargets[theItem.weaponName] = ctName
|
||||||
|
|
||||||
|
-- should now update theItem to new target info
|
||||||
|
isThreat = false
|
||||||
|
if guardianAngel.getWatchedUnitByName(ctName) then
|
||||||
|
isThreat = true
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: <" .. theItem.weaponName .. "> now targeting protected <" .. ctName .. ">!", 30)
|
||||||
|
end
|
||||||
|
|
||||||
|
if isThreat and guardianAngel.announcer and guardianAngel.active then
|
||||||
|
local desc = "Missile, missile, missile - now heading for " .. ctName .. "!"
|
||||||
|
if guardianAngel.private then
|
||||||
|
trigger.action.outTextForGroup(ID, desc, 30)
|
||||||
|
else
|
||||||
|
trigger.action.outText(desc, 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
guardianAngel.retargetItem(theItem, currentTarget, isThreat)
|
||||||
|
t = currentTarget
|
||||||
|
else
|
||||||
|
-- not ctName, or name as before.
|
||||||
|
-- go on.
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we only progress here is the missile is a threat.
|
||||||
|
-- if not, we keep it and check next time if it has
|
||||||
|
-- retargeted a protegee
|
||||||
|
if not theItem.threat then return true end
|
||||||
|
|
||||||
|
-- local oldWPos = theItem.wP
|
||||||
local A = w:getPoint() -- A is new point of weapon
|
local A = w:getPoint() -- A is new point of weapon
|
||||||
theItem.wp = A -- update new position, old is in oldWPos
|
-- theItem.wp = A -- update new position, old is in oldWPos
|
||||||
|
|
||||||
-- new code: safety check with ALL protected wings
|
-- new code: safety check with ALL protected wings
|
||||||
local bubbleThreat = guardianAngel.bubbleCheck(A, w)
|
-- local bubbleThreat = guardianAngel.bubbleCheck(A, w)
|
||||||
|
-- safety check removed, no benefit after new code
|
||||||
|
|
||||||
local B
|
local B
|
||||||
if currentTarget then B = currentTarget:getPoint() else B = A end
|
if currentTarget then B = currentTarget:getPoint() else B = A end
|
||||||
|
|
||||||
local d = math.floor(dcsCommon.dist(A, B))
|
local d = math.floor(dcsCommon.dist(A, B))
|
||||||
|
theItem.lastDistance = d -- save it for post mortem
|
||||||
local desc = theItem.weaponName .. ": "
|
local desc = theItem.weaponName .. ": "
|
||||||
if t == currentTarget then
|
if true or t == currentTarget then
|
||||||
desc = desc .. "tracking " .. theItem.targetName .. ", d = " .. d .. "m"
|
desc = desc .. "tracking " .. theItem.targetName .. ", d = " .. d .. "m"
|
||||||
local vcc = dcsCommon.getClosingVelocity(t, w)
|
local vcc = dcsCommon.getClosingVelocity(t, w)
|
||||||
desc = desc .. ", Vcc = " .. math.floor(vcc) .. "m/s"
|
desc = desc .. ", Vcc = " .. math.floor(vcc) .. "m/s"
|
||||||
@ -200,13 +335,15 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
-- destroy the missile
|
-- destroy the missile
|
||||||
local lethalRange = math.abs(vcc / guardianAngel.ups) * guardianAngel.safetyFactor
|
local lethalRange = math.abs(vcc / guardianAngel.ups) * guardianAngel.safetyFactor
|
||||||
desc = desc .. ", LR= " .. math.floor(lethalRange) .. "m"
|
desc = desc .. ", LR= " .. math.floor(lethalRange) .. "m"
|
||||||
|
theItem.lastDesc = desc
|
||||||
|
theItem.timeStamp = timer.getTime()
|
||||||
|
|
||||||
if guardianAngel.intervention and
|
if guardianAngel.intervention and
|
||||||
d <= lethalRange + 10
|
d <= lethalRange + 10
|
||||||
then
|
then
|
||||||
desc = desc .. " ANGEL INTERVENTION"
|
desc = desc .. " ANGEL INTERVENTION"
|
||||||
if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
||||||
if theItem.missed then desc = desc .. " (missed you!)" end
|
--if theItem.missed then desc = desc .. " (missed you!)" end
|
||||||
|
|
||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
@ -232,8 +369,8 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
d <= guardianAngel.minMissileDist -- god's override
|
d <= guardianAngel.minMissileDist -- god's override
|
||||||
then
|
then
|
||||||
desc = desc .. " GOD INTERVENTION"
|
desc = desc .. " GOD INTERVENTION"
|
||||||
if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
--if theItem.lostTrack then desc = desc .. " (little sneak!)" end
|
||||||
if theItem.missed then desc = desc .. " (missed you!)" end
|
--if theItem.missed then desc = desc .. " (missed you!)" end
|
||||||
|
|
||||||
if guardianAngel.announcer then
|
if guardianAngel.announcer then
|
||||||
if guardianAngel.private then
|
if guardianAngel.private then
|
||||||
@ -251,6 +388,7 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
return false -- remove from list
|
return false -- remove from list
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
--[[--
|
||||||
if not theItem.lostTrack then
|
if not theItem.lostTrack then
|
||||||
desc = desc .. "Missile LOST TRACK"
|
desc = desc .. "Missile LOST TRACK"
|
||||||
|
|
||||||
@ -264,10 +402,12 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
guardianAngel.invokeCallbacks("trackloss", theItem.targetName, theItem.weaponName)
|
guardianAngel.invokeCallbacks("trackloss", theItem.targetName, theItem.weaponName)
|
||||||
theItem.lostTrack = true
|
theItem.lostTrack = true
|
||||||
end
|
end
|
||||||
theItem.lastDistance = d
|
--]]--
|
||||||
return true -- true because they can re-acquire!
|
-- theItem.lastDistance = d
|
||||||
|
-- return true -- true because they can re-acquire!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
if d > theItem.lastDistance then
|
if d > theItem.lastDistance then
|
||||||
-- this can be wrong because if a missile is launched
|
-- this can be wrong because if a missile is launched
|
||||||
-- at an angle, it can initially look as if it missed
|
-- at an angle, it can initially look as if it missed
|
||||||
@ -287,7 +427,8 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
theItem.lastDistance = d
|
theItem.lastDistance = d
|
||||||
return true -- better not disregard - they can re-acquire!
|
return true -- better not disregard - they can re-acquire!
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
--[[--
|
||||||
if theItem.missed and d < theItem.lastDistance then
|
if theItem.missed and d < theItem.lastDistance then
|
||||||
desc = desc .. " Missile RE-ACQUIRED!"
|
desc = desc .. " Missile RE-ACQUIRED!"
|
||||||
|
|
||||||
@ -301,8 +442,9 @@ function guardianAngel.monitorItem(theItem)
|
|||||||
theItem.missed = false
|
theItem.missed = false
|
||||||
guardianAngel.invokeCallbacks("reacquire", theItem.targetName, theItem.weaponName)
|
guardianAngel.invokeCallbacks("reacquire", theItem.targetName, theItem.weaponName)
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
theItem.lastDistance = d
|
-- theItem.lastDistance = d
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -316,7 +458,7 @@ function guardianAngel.monitorMissiles()
|
|||||||
-- guardianAngel.detectItem(anItem)
|
-- guardianAngel.detectItem(anItem)
|
||||||
|
|
||||||
-- see if the weapon is still in existence
|
-- see if the weapon is still in existence
|
||||||
stillAlive = guardianAngel.monitorItem(anItem)
|
local stillAlive = guardianAngel.monitorItem(anItem)
|
||||||
if stillAlive then
|
if stillAlive then
|
||||||
table.insert(newArray, anItem)
|
table.insert(newArray, anItem)
|
||||||
end
|
end
|
||||||
@ -324,6 +466,31 @@ function guardianAngel.monitorMissiles()
|
|||||||
guardianAngel.missilesInTheAir = newArray
|
guardianAngel.missilesInTheAir = newArray
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function guardianAngel.filterItem(theItem)
|
||||||
|
local w = theItem.theWeapon
|
||||||
|
if not w then return false end
|
||||||
|
if not w:isExist() then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true -- missile still alive
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.filterMissiles()
|
||||||
|
local newArray = {} -- we collect all still existing missiles here
|
||||||
|
-- and replace missilesInTheAir with that for next round
|
||||||
|
for idx, anItem in pairs (guardianAngel.missilesInTheAir) do
|
||||||
|
-- we now have an item
|
||||||
|
-- see about detection
|
||||||
|
-- guardianAngel.detectItem(anItem)
|
||||||
|
|
||||||
|
-- see if the weapon is still in existence
|
||||||
|
local stillAlive = guardianAngel.filterItem(anItem)
|
||||||
|
if stillAlive then
|
||||||
|
table.insert(newArray, anItem)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
guardianAngel.missilesInTheAir = newArray
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- E V E N T P R O C E S S I N G
|
-- E V E N T P R O C E S S I N G
|
||||||
--
|
--
|
||||||
@ -348,22 +515,79 @@ function guardianAngel.postProcessor(event)
|
|||||||
-- don't do anything for now
|
-- don't do anything for now
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
for idx, theZone in pairs(guardianAngel.angelicZones) do
|
||||||
|
if cfxZones.unitInZone(theUnit, theZone) then
|
||||||
|
return theZone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- event callback from dcsCommon event handler. preProcessor has returned true
|
-- event callback from dcsCommon event handler. preProcessor has returned true
|
||||||
function guardianAngel.somethingHappened(event)
|
function guardianAngel.somethingHappened(event)
|
||||||
-- when this is invoked, the preprocessor guarantees that
|
-- when this is invoked, the preprocessor guarantees that
|
||||||
-- it's an interesting event and has initiator
|
-- it's an interesting event and has initiator
|
||||||
local ID = event.id
|
local ID = event.id
|
||||||
local theUnit = event.initiator
|
local theUnit = event.initiator
|
||||||
local playerName = theUnit:getPlayerName() -- nil if not a player
|
-- make sure that this is a cat 0 or cat 1
|
||||||
|
|
||||||
|
local playerName = nil
|
||||||
|
if theUnit.getPlayerName then
|
||||||
|
playerName = theUnit:getPlayerName() -- nil if not a player
|
||||||
|
end
|
||||||
|
|
||||||
|
local mustProtect = false
|
||||||
if ID == 15 and playerName then
|
if ID == 15 and playerName then
|
||||||
-- this is a player created unit
|
-- this is a player created unit
|
||||||
if guardianAngel.verbose then
|
if guardianAngel.verbose then
|
||||||
trigger.action.outText("+++gA: unit born " .. theUnit:getName(), 30)
|
trigger.action.outText("+++gA: player unit born " .. theUnit:getName(), 30)
|
||||||
end
|
end
|
||||||
if guardianAngel.autoAddPlayers then
|
if guardianAngel.autoAddPlayers then
|
||||||
|
mustProtect = true
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
if theZone then
|
||||||
|
mustProtect = theZone.angelic
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mustProtect then
|
||||||
guardianAngel.addUnitToWatch(theUnit)
|
guardianAngel.addUnitToWatch(theUnit)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return
|
||||||
|
elseif ID == 15 then
|
||||||
|
-- AI spawn. check if it is an aircraft and in an angelic zone
|
||||||
|
-- docs say that initiator is object. so let's see if when we
|
||||||
|
-- get cat, this returns 1 for unit (as it should, so we can get
|
||||||
|
-- group, or if it's really a unit, which returns 0 for aircraft
|
||||||
|
local cat = theUnit:getCategory()
|
||||||
|
--trigger.action.outText("birth event for " .. theUnit:getName() .. " with cat = " .. cat, 30)
|
||||||
|
if cat ~= 1 then
|
||||||
|
-- not a unit, bye bye
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local theGroup = theUnit:getGroup()
|
||||||
|
local gCat = theGroup:getCategory()
|
||||||
|
if gCat == 0 or gCat == 1 then
|
||||||
|
--trigger.action.outText("is aircraft cat " .. gCat, 30)
|
||||||
|
|
||||||
|
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
if theZone then
|
||||||
|
mustProtect = theZone.angelic
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: angelic zone <" .. theZone.name .."> contains unit <" .. theUnit:getName() .. ">, protect it: " .. dcsCommon.bool2YesNo(mustProtect) .. ".", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mustProtect then
|
||||||
|
guardianAngel.addUnitToWatch(theUnit)
|
||||||
|
end
|
||||||
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -372,9 +596,23 @@ function guardianAngel.somethingHappened(event)
|
|||||||
if guardianAngel.verbose then
|
if guardianAngel.verbose then
|
||||||
trigger.action.outText("+++gA: player seated in unit " .. theUnit:getName(), 30)
|
trigger.action.outText("+++gA: player seated in unit " .. theUnit:getName(), 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
if guardianAngel.autoAddPlayers then
|
if guardianAngel.autoAddPlayers then
|
||||||
|
mustProtect = true
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
if theZone then
|
||||||
|
mustProtect = theZone.angelic
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mustProtect then
|
||||||
guardianAngel.addUnitToWatch(theUnit)
|
guardianAngel.addUnitToWatch(theUnit)
|
||||||
end
|
end
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -390,6 +628,7 @@ function guardianAngel.somethingHappened(event)
|
|||||||
|
|
||||||
|
|
||||||
if ID == 1 then
|
if ID == 1 then
|
||||||
|
-- even if not active, we collect missile data
|
||||||
-- someone shot something. see if it is fire directed at me
|
-- someone shot something. see if it is fire directed at me
|
||||||
local theWeapon = event.weapon
|
local theWeapon = event.weapon
|
||||||
local theTarget
|
local theTarget
|
||||||
@ -406,11 +645,22 @@ 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)
|
||||||
if not watchedUnit then return end -- fired at some other poor sucker, we don't care
|
guardianAngel.missilesAndTargets[theWeapon:getName()] = targetName
|
||||||
|
if not watchedUnit then
|
||||||
|
-- we may still want to watch this if the missile
|
||||||
|
-- can be re-targeted
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: missile <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">, not a threat", 30)
|
||||||
|
end
|
||||||
|
-- add it as no threat
|
||||||
|
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, false) -- this is not a threat, simply watch for re-target
|
||||||
|
table.insert(guardianAngel.missilesInTheAir, theQItem)
|
||||||
|
return
|
||||||
|
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) -- prob 100
|
local theQItem = guardianAngel.createQItem(theWeapon, theTarget, true) -- 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)
|
||||||
|
|
||||||
@ -420,13 +670,17 @@ function guardianAngel.somethingHappened(event)
|
|||||||
local oclock = dcsCommon.clockPositionOfARelativeToB(A, B, unitHeading)
|
local oclock = dcsCommon.clockPositionOfARelativeToB(A, B, unitHeading)
|
||||||
|
|
||||||
local grpID = theTarget:getGroup():getID()
|
local grpID = theTarget:getGroup():getID()
|
||||||
if guardianAngel.launchWarning then
|
local vbInfo = ""
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
vbInfo = ", <" .. theWeapon:getName() .. "> targeting <" .. targetName .. ">"
|
||||||
|
end
|
||||||
|
if guardianAngel.launchWarning and guardianAngel.active then
|
||||||
-- 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", 30)
|
trigger.action.outTextForGroup(grpID, "Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
|
||||||
else
|
else
|
||||||
trigger.action.outText("Missile, missile, missile, " .. oclock .. " o clock", 30)
|
trigger.action.outText("Missile, missile, missile, " .. oclock .. " o clock" .. vbInfo, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
theQItem.detected = true -- remember: we detected and warned already
|
theQItem.detected = true -- remember: we detected and warned already
|
||||||
@ -435,6 +689,7 @@ function guardianAngel.somethingHappened(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if ID == 2 then
|
if ID == 2 then
|
||||||
|
if not guardianAngel.active then return end -- we aren't on watch.
|
||||||
if not guardianAngel.intervention then return end -- we don't intervene
|
if not guardianAngel.intervention then return end -- we don't intervene
|
||||||
if not event.weapon then return end -- no weapon, no interest
|
if not event.weapon then return end -- no weapon, no interest
|
||||||
local theWeapon = event.weapon
|
local theWeapon = event.weapon
|
||||||
@ -445,16 +700,37 @@ function guardianAngel.somethingHappened(event)
|
|||||||
|
|
||||||
local theProtegee = nil
|
local theProtegee = nil
|
||||||
for idx, aProt in pairs(guardianAngel.unitsToWatchOver) do
|
for idx, aProt in pairs(guardianAngel.unitsToWatchOver) do
|
||||||
|
if aProt:isExist() then
|
||||||
if tName == aProt:getName() then
|
if tName == aProt:getName() then
|
||||||
theProtegee = aProt
|
theProtegee = aProt
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: whoops. Looks like I lost a wing there... sorry", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not theProtegee then return end
|
if not theProtegee then return end
|
||||||
|
|
||||||
-- one of our protegees was hit
|
-- one of our protegees was hit
|
||||||
--trigger.action.outText("+++gA: Protegee " .. tName .. " was hit", 30)
|
--trigger.action.outText("+++gA: Protegee " .. tName .. " was hit", 30)
|
||||||
trigger.action.outText("+++gA: I:" .. theUnit:getName() .. " hit " .. tName .. " with " .. wName, 30)
|
trigger.action.outText("+++gA: I:" .. theUnit:getName() .. " hit " .. tName .. " with " .. wName, 30) -- note: theUnit is the LAUNCHER or the weapon!!!
|
||||||
|
if guardianAngel.missilesAndTargets[wName] and guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: <" .. wName .. "> was originally aimed at <" .. guardianAngel.missilesAndTargets[wName] .. ">", 30)
|
||||||
|
local qName = guardianAngel.missilesAndTargets[wName]
|
||||||
|
if qName ~= tName then
|
||||||
|
trigger.action.outText("+++gA: RETARGET DETECTED", 30)
|
||||||
|
local wpnTgt = theWeapon:getTarget()
|
||||||
|
local wpnTgtName = "(none???)"
|
||||||
|
if wpnTgt then wpnTgtName = wpnTgt:getName() end
|
||||||
|
trigger.action.outText("+++gA: *current* weapon's target is <" .. wpnTgtName .. ">", 30)
|
||||||
|
if wpnTgtName ~= tName then
|
||||||
|
trigger.action.outText("+++gA: COLLATERAL DAMAGE!", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("***gA: no missile in the air for <" .. wName .. ">!!!!")
|
||||||
|
end
|
||||||
-- let's see if the victim was in our list of protected
|
-- let's see if the victim was in our list of protected
|
||||||
-- units
|
-- units
|
||||||
local thePerp = nil
|
local thePerp = nil
|
||||||
@ -479,6 +755,18 @@ function guardianAngel.somethingHappened(event)
|
|||||||
|
|
||||||
-- if we should have protected: mea maxima culpa
|
-- if we should have protected: mea maxima culpa
|
||||||
trigger.action.outText("[+++gA: Angel hangs her head in shame. Mea Culpa, " .. tName.."]", 30)
|
trigger.action.outText("[+++gA: Angel hangs her head in shame. Mea Culpa, " .. tName.."]", 30)
|
||||||
|
-- see if we can find the q item
|
||||||
|
local missedItem = guardianAngel.getQItemForWeaponNamed(wName)
|
||||||
|
if not missedItem then
|
||||||
|
trigger.action.outText("Cannot retrieve item for <" .. wName .. ">", 30)
|
||||||
|
else
|
||||||
|
local now = timer.getTime()
|
||||||
|
local delta = now - missedItem.timeStamp
|
||||||
|
local wasThreat = dcsCommon.bool2YesNo(missedItem.threat)
|
||||||
|
|
||||||
|
trigger.action.outText("post: target was <" .. missedItem.targetName .. "> with last dist <" .. missedItem.lastDistance .. "> for weapon <" .. missedItem.weaponName .. ">, with dast desc = <" .. missedItem.lastDesc .. ">, <" .. delta .. "> s ago, Threat:(" .. wasThreat .. ")", 30)
|
||||||
|
end
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -495,21 +783,102 @@ end
|
|||||||
|
|
||||||
function guardianAngel.update()
|
function guardianAngel.update()
|
||||||
timer.scheduleFunction(guardianAngel.update, {}, timer.getTime() + 1/guardianAngel.ups)
|
timer.scheduleFunction(guardianAngel.update, {}, timer.getTime() + 1/guardianAngel.ups)
|
||||||
|
-- and break off if nothing to do
|
||||||
|
if not guardianAngel.active then
|
||||||
|
guardianAngel.filterMissiles()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
guardianAngel.monitorMissiles()
|
guardianAngel.monitorMissiles()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function guardianAngel.doActivate()
|
||||||
|
guardianAngel.active = true
|
||||||
|
if guardianAngel.verbose or guardianAngel.announcer then
|
||||||
|
trigger.action.outText("Guardian Angel has activated", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.doDeActivate()
|
||||||
|
guardianAngel.active = false
|
||||||
|
if guardianAngel.verbose or guardianAngel.announcer then
|
||||||
|
trigger.action.outText("Guardian Angel NO LONGER ACTIVE", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.flagUpdate()
|
||||||
|
timer.scheduleFunction(guardianAngel.flagUpdate, {}, timer.getTime() + 1) -- once every second
|
||||||
|
|
||||||
|
-- check the flags for on/off
|
||||||
|
if guardianAngel.activate then
|
||||||
|
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.activate, "change","lastActivate") then
|
||||||
|
guardianAngel.doActivate()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if guardianAngel.deactivate then
|
||||||
|
if cfxZones.testZoneFlag(guardianAngel, guardianAngel.deactivate, "change","lastDeActivate") then
|
||||||
|
guardianAngel.doDeActivate()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function guardianAngel.collectPlayerUnits()
|
function guardianAngel.collectPlayerUnits()
|
||||||
-- make sure we have all existing player units
|
-- make sure we have all existing player units
|
||||||
-- at start of game
|
-- at start of game
|
||||||
if not guardianAngel.autoAddPlayer then return end
|
-- if not guardianAngel.autoAddPlayer then return end
|
||||||
|
|
||||||
for i=1, 2 do
|
for i=1, 2 do
|
||||||
-- currently only two factions in dcs
|
-- currently only two factions in dcs
|
||||||
factionUnits = coalition.getPlayers(i)
|
local factionUnits = coalition.getPlayers(i)
|
||||||
for idx, aPlayerUnit in pairs(factionUnits) do
|
for idx, theUnit in pairs(factionUnits) do
|
||||||
-- add all existing faction units
|
local mustProtect = false
|
||||||
guardianAngel.addUnitToWatch(aPlayerUnit)
|
if guardianAngel.autoAddPlayers then
|
||||||
|
mustProtect = true
|
||||||
|
end
|
||||||
|
|
||||||
|
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
if theZone then
|
||||||
|
mustProtect = theZone.angelic
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: angelic zone " .. theZone.name .." contains player unit <" .. theUnit:getName() .. "> -- protect: (" .. dcsCommon.bool2YesNo(mustProtect) .. ")", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mustProtect then
|
||||||
|
guardianAngel.addUnitToWatch(theUnit)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.collectAIUnits()
|
||||||
|
-- make sure we have all existing AI units
|
||||||
|
-- at start of game
|
||||||
|
for i=1, 2 do
|
||||||
|
-- currently only two factions in dcs
|
||||||
|
local factionGroups = coalition.getGroups(i)
|
||||||
|
for idg, aGroup in pairs(factionGroups) do
|
||||||
|
local factionUnits = aGroup:getUnits()
|
||||||
|
for idx, theUnit in pairs(factionUnits) do
|
||||||
|
local mustProtect = false
|
||||||
|
|
||||||
|
local gCat = aGroup:getCategory()
|
||||||
|
if gCat == 0 or gCat == 1 then
|
||||||
|
theZone = guardianAngel.getAngelicZoneForUnit(theUnit)
|
||||||
|
if theZone then
|
||||||
|
mustProtect = theZone.angelic
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: angelic zone <" .. theZone.name .."> contains AI unit <" .. theUnit:getName() .. ">, protect it: " .. dcsCommon.bool2YesNo(mustProtect) .. ".", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if mustProtect then
|
||||||
|
guardianAngel.addUnitToWatch(theUnit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -521,13 +890,10 @@ function guardianAngel.readConfigZone()
|
|||||||
-- note: must match exactly!!!!
|
-- note: must match exactly!!!!
|
||||||
local theZone = cfxZones.getZoneByName("guardianAngelConfig")
|
local theZone = cfxZones.getZoneByName("guardianAngelConfig")
|
||||||
if not theZone then
|
if not theZone then
|
||||||
trigger.action.outText("+++gA: no config zone!", 30)
|
theZone = cfxZones.createSimpleZone("guardianAngelConfig")
|
||||||
return
|
|
||||||
end
|
|
||||||
if guardianAngel.verbose then
|
|
||||||
trigger.action.outText("+++gA: found config zone!", 30)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
guardianAngel.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
guardianAngel.verbose = cfxZones.getBoolFromZoneProperty(theZone, "verbose", false)
|
||||||
|
|
||||||
guardianAngel.autoAddPlayer = cfxZones.getBoolFromZoneProperty(theZone, "autoAddPlayer", true)
|
guardianAngel.autoAddPlayer = cfxZones.getBoolFromZoneProperty(theZone, "autoAddPlayer", true)
|
||||||
@ -537,8 +903,52 @@ function guardianAngel.readConfigZone()
|
|||||||
guardianAngel.private = cfxZones.getBoolFromZoneProperty(theZone, "private", false)
|
guardianAngel.private = cfxZones.getBoolFromZoneProperty(theZone, "private", false)
|
||||||
guardianAngel.explosion = cfxZones.getNumberFromZoneProperty(theZone, "explosion", -1)
|
guardianAngel.explosion = cfxZones.getNumberFromZoneProperty(theZone, "explosion", -1)
|
||||||
guardianAngel.fxDistance = cfxZones.getNumberFromZoneProperty(theZone, "fxDistance", 500)
|
guardianAngel.fxDistance = cfxZones.getNumberFromZoneProperty(theZone, "fxDistance", 500)
|
||||||
|
|
||||||
|
guardianAngel.active = cfxZones.getBoolFromZoneProperty(theZone, "active", true)
|
||||||
|
|
||||||
|
if cfxZones.hasProperty(theZone, "activate?") then
|
||||||
|
guardianAngel.activate = cfxZones.getStringFromZoneProperty(theZone, "activate?", "*<none>")
|
||||||
|
guardianAngel.lastActivate = cfxZones.getFlagValue(guardianAngel.activate, theZone)
|
||||||
|
elseif cfxZones.hasProperty(theZone, "on?") then
|
||||||
|
guardianAngel.activate = cfxZones.getStringFromZoneProperty(theZone, "on?", "*<none>")
|
||||||
|
guardianAngel.lastActivate = cfxZones.getFlagValue(guardianAngel.activate, theZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if cfxZones.hasProperty(theZone, "deactivate?") then
|
||||||
|
guardianAngel.deactivate = cfxZones.getStringFromZoneProperty(theZone, "deactivate?", "*<none>")
|
||||||
|
guardianAngel.lastDeActivate = cfxZones.getFlagValue(guardianAngel.deactivate, theZone)
|
||||||
|
elseif cfxZones.hasProperty(theZone, "off?") then
|
||||||
|
guardianAngel.deactivate = cfxZones.getStringFromZoneProperty(theZone, "off?", "*<none>")
|
||||||
|
guardianAngel.lastDeActivate = cfxZones.getFlagValue(guardianAngel.deactivate, theZone)
|
||||||
|
end
|
||||||
|
|
||||||
|
guardianAngel.configZone = theZone
|
||||||
|
if guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: processed config zone", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- guardian zones
|
||||||
|
--
|
||||||
|
|
||||||
|
function guardianAngel.processGuardianZone(theZone)
|
||||||
|
theZone.angelic = cfxZones.getBoolFromZoneProperty(theZone, "guardian", true)
|
||||||
|
|
||||||
|
|
||||||
|
if theZone.verbose or guardianAngel.verbose then
|
||||||
|
trigger.action.outText("+++gA: processed 'guardian' zone <" .. theZone.name .. ">", 30)
|
||||||
|
end
|
||||||
|
-- add it to my angelicZones
|
||||||
|
table.insert(guardianAngel.angelicZones, theZone)
|
||||||
|
end
|
||||||
|
|
||||||
|
function guardianAngel.readGuardianZones()
|
||||||
|
local attrZones = cfxZones.getZonesWithAttributeNamed("guardian")
|
||||||
|
for k, aZone in pairs(attrZones) do
|
||||||
|
guardianAngel.processGuardianZone(aZone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- start
|
-- start
|
||||||
@ -553,6 +963,9 @@ function guardianAngel.start()
|
|||||||
-- read config
|
-- read config
|
||||||
guardianAngel.readConfigZone()
|
guardianAngel.readConfigZone()
|
||||||
|
|
||||||
|
-- read guarded zones
|
||||||
|
guardianAngel.readGuardianZones()
|
||||||
|
|
||||||
-- install event monitor
|
-- install event monitor
|
||||||
dcsCommon.addEventHandler(guardianAngel.somethingHappened,
|
dcsCommon.addEventHandler(guardianAngel.somethingHappened,
|
||||||
guardianAngel.preProcessor,
|
guardianAngel.preProcessor,
|
||||||
@ -560,10 +973,14 @@ function guardianAngel.start()
|
|||||||
|
|
||||||
-- collect all units that are already in the game at this point
|
-- collect all units that are already in the game at this point
|
||||||
guardianAngel.collectPlayerUnits()
|
guardianAngel.collectPlayerUnits()
|
||||||
|
guardianAngel.collectAIUnits()
|
||||||
|
|
||||||
-- start update
|
-- start update
|
||||||
guardianAngel.update()
|
guardianAngel.update()
|
||||||
|
|
||||||
|
-- start flag check
|
||||||
|
guardianAngel.flagUpdate()
|
||||||
|
|
||||||
trigger.action.outText("Guardian Angel v" .. guardianAngel.version .. " running", 30)
|
trigger.action.outText("Guardian Angel v" .. guardianAngel.version .. " running", 30)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -581,3 +998,9 @@ end
|
|||||||
-- test callback
|
-- test callback
|
||||||
--guardianAngel.addCallback(guardianAngel.testCB)
|
--guardianAngel.addCallback(guardianAngel.testCB)
|
||||||
--guardianAngel.invokeCallbacks("A", "B", "C")
|
--guardianAngel.invokeCallbacks("A", "B", "C")
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
to do
|
||||||
|
- turn on and off via flags
|
||||||
|
- zones that designate protected/unprotected aircraft
|
||||||
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
messenger = {}
|
messenger = {}
|
||||||
messenger.version = "1.3.1"
|
messenger.version = "1.3.2"
|
||||||
messenger.verbose = false
|
messenger.verbose = false
|
||||||
messenger.requiredLibs = {
|
messenger.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
@ -22,7 +22,9 @@ messenger.messengers = {}
|
|||||||
1.2.1 - qoL: <n> = newline, <z> = zone name, <v> = value
|
1.2.1 - qoL: <n> = newline, <z> = zone name, <v> = value
|
||||||
1.3.0 - messenger? saves messageOut? attribute
|
1.3.0 - messenger? saves messageOut? attribute
|
||||||
1.3.1 - message now can interpret value as time with <h> <m> <s> <:h> <:m> <:s>
|
1.3.1 - message now can interpret value as time with <h> <m> <s> <:h> <:m> <:s>
|
||||||
|
1.3.2 - message interprets <t> as time in HH:MM:SS of current time
|
||||||
|
- can interpret <lat>, <lon>, <mgrs>
|
||||||
|
- zone-local verbosity
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function messenger.addMessenger(theZone)
|
function messenger.addMessenger(theZone)
|
||||||
@ -54,6 +56,24 @@ function messenger.preProcMessage(inMsg, theZone)
|
|||||||
if theZone then
|
if theZone then
|
||||||
outMsg = outMsg:gsub("<z>", theZone.name)
|
outMsg = outMsg:gsub("<z>", theZone.name)
|
||||||
end
|
end
|
||||||
|
-- replace <t> with current mission time HMS
|
||||||
|
local absSecs = timer.getAbsTime()-- + env.mission.start_time
|
||||||
|
while absSecs > 86400 do
|
||||||
|
absSecs = absSecs - 86400 -- subtract out all days
|
||||||
|
end
|
||||||
|
local timeString = dcsCommon.processHMS("<:h>:<:m>:<:s>", absSecs)
|
||||||
|
outMsg = outMsg:gsub("<t>", timeString)
|
||||||
|
|
||||||
|
-- replace <lat> with lat of zone point and <lon> with lon of zone point
|
||||||
|
-- and <mgrs> with mgrs coords of zone point
|
||||||
|
local currPoint = cfxZones.getPoint(theZone)
|
||||||
|
local lat, lon, alt = coord.LOtoLL(currPoint)
|
||||||
|
lat, lon = dcsCommon.latLon2Text(lat, lon)
|
||||||
|
outMsg = outMsg:gsub("<lat>", lat)
|
||||||
|
outMsg = outMsg:gsub("<lon>", lon)
|
||||||
|
local grid = coord.LLtoMGRS(coord.LOtoLL(currPoint))
|
||||||
|
local mgrs = grid.UTMZone .. ' ' .. grid.MGRSDigraph .. ' ' .. grid.Easting .. ' ' .. grid.Northing
|
||||||
|
outMsg = outMsg:gsub("<mgrs>", mgrs)
|
||||||
return outMsg
|
return outMsg
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -133,7 +153,7 @@ function messenger.createMessengerWithZone(theZone)
|
|||||||
theZone.messageValue = cfxZones.getStringFromZoneProperty(theZone, "messageValue?", "<none>")
|
theZone.messageValue = cfxZones.getStringFromZoneProperty(theZone, "messageValue?", "<none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if messenger.verbose then
|
if messenger.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++Msg: new zone <".. theZone.name .."> will say <".. theZone.message .. ">", 30)
|
trigger.action.outText("+++Msg: new zone <".. theZone.name .."> will say <".. theZone.message .. ">", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -167,7 +187,7 @@ end
|
|||||||
function messenger.isTriggered(theZone)
|
function messenger.isTriggered(theZone)
|
||||||
-- this module has triggered
|
-- this module has triggered
|
||||||
if theZone.messageOff then
|
if theZone.messageOff then
|
||||||
if messenger.verbose then
|
if messenger.verbose or theZone.verbose then
|
||||||
trigger.action.outFlag("msg: message for <".. theZone.name .."> is OFF",30)
|
trigger.action.outFlag("msg: message for <".. theZone.name .."> is OFF",30)
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
@ -175,7 +195,7 @@ function messenger.isTriggered(theZone)
|
|||||||
|
|
||||||
local fileName = "l10n/DEFAULT/" .. theZone.soundFile
|
local fileName = "l10n/DEFAULT/" .. theZone.soundFile
|
||||||
local msg = messenger.getMessage(theZone)
|
local msg = messenger.getMessage(theZone)
|
||||||
if messenger.verbose then
|
if messenger.verbose or theZone.verbose then
|
||||||
trigger.action.outText("+++Msg: <".. theZone.name .."> will say <".. msg .. ">", 30)
|
trigger.action.outText("+++Msg: <".. theZone.name .."> will say <".. msg .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -200,7 +220,7 @@ function messenger.update()
|
|||||||
-- make sure to re-start before reading time limit
|
-- make sure to re-start before reading time limit
|
||||||
-- new trigger code
|
-- new trigger code
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.triggerMessagerFlag, aZone.msgTriggerMethod, "lastMessageTriggerValue") then
|
if cfxZones.testZoneFlag(aZone, aZone.triggerMessagerFlag, aZone.msgTriggerMethod, "lastMessageTriggerValue") then
|
||||||
if messenger.verbose then
|
if messenger.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++msgr: triggered on in? for <".. aZone.name ..">", 30)
|
trigger.action.outText("+++msgr: triggered on in? for <".. aZone.name ..">", 30)
|
||||||
end
|
end
|
||||||
messenger.isTriggered(aZone)
|
messenger.isTriggered(aZone)
|
||||||
@ -209,14 +229,14 @@ function messenger.update()
|
|||||||
-- old trigger code
|
-- old trigger code
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then
|
if cfxZones.testZoneFlag(aZone, aZone.messageOffFlag, aZone.msgTriggerMethod, "lastMessageOff") then
|
||||||
aZone.messageOff = true
|
aZone.messageOff = true
|
||||||
if messenger.verbose then
|
if messenger.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ***OFF***", 30)
|
trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ***OFF***", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if cfxZones.testZoneFlag(aZone, aZone.messageOnFlag, aZone.msgTriggerMethod, "lastMessageOn") then
|
if cfxZones.testZoneFlag(aZone, aZone.messageOnFlag, aZone.msgTriggerMethod, "lastMessageOn") then
|
||||||
aZone.messageOff = false
|
aZone.messageOff = false
|
||||||
if messenger.verbose then
|
if messenger.verbose or aZone.verbose then
|
||||||
trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30)
|
trigger.action.outText("+++msg: messenger <" .. aZone.name .. "> turned ON", 30)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
radioMenu = {}
|
radioMenu = {}
|
||||||
radioMenu.version = "1.0.0"
|
radioMenu.version = "1.0.1"
|
||||||
radioMenu.verbose = false
|
radioMenu.verbose = false
|
||||||
radioMenu.ups = 1
|
radioMenu.ups = 1
|
||||||
radioMenu.requiredLibs = {
|
radioMenu.requiredLibs = {
|
||||||
@ -11,6 +11,7 @@ radioMenu.menus = {}
|
|||||||
--[[--
|
--[[--
|
||||||
Version History
|
Version History
|
||||||
1.0.0 Initial version
|
1.0.0 Initial version
|
||||||
|
1.0.1 spelling corrections
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
function radioMenu.addRadioMenu(theZone)
|
function radioMenu.addRadioMenu(theZone)
|
||||||
@ -181,7 +182,7 @@ function radioMenu.doMenuX(args)
|
|||||||
|
|
||||||
cfxZones.pollFlag(theFlag, theZone.radioMethod, theZone)
|
cfxZones.pollFlag(theFlag, theZone.radioMethod, theZone)
|
||||||
if theZone.verbose or radioMenu.verbose then
|
if theZone.verbose or radioMenu.verbose then
|
||||||
trigger.action.outText("+++menu: banging D! with <" .. theZone.radioMethod .. "> on <" .. theFlag .. "> for " .. theZone.name, 30)
|
trigger.action.outText("+++menu: banging with <" .. theZone.radioMethod .. "> on <" .. theFlag .. "> for " .. theZone.name, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
Binary file not shown.
BIN
tutorial & demo missions/demo - artillery recon.miz
Normal file
BIN
tutorial & demo missions/demo - artillery recon.miz
Normal file
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - recon mode - reloaded.miz
Normal file
BIN
tutorial & demo missions/demo - recon mode - reloaded.miz
Normal file
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user