mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
commit
4ffdf9e536
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,26 @@
|
|||||||
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Features:
|
-- ## Features:
|
||||||
--
|
--
|
||||||
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
-- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible.
|
||||||
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
-- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Missions:
|
-- ## Missions:
|
||||||
--
|
--
|
||||||
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Authors: **FlightControl**, **applevangelist**
|
-- ### Authors: **FlightControl**, **applevangelist**
|
||||||
--
|
--
|
||||||
-- Last Update: April 2021
|
-- Last Update: July 2021
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- @module Functional.Sead
|
-- @module Functional.Sead
|
||||||
-- @image SEAD.JPG
|
-- @image SEAD.JPG
|
||||||
|
|
||||||
@ -28,49 +28,32 @@
|
|||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||||
--
|
--
|
||||||
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
||||||
--
|
--
|
||||||
-- # Constructor:
|
-- # Constructor:
|
||||||
--
|
--
|
||||||
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
-- Use the @{#SEAD.New}() constructor to create a new SEAD object.
|
||||||
--
|
--
|
||||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||||
--
|
--
|
||||||
-- @field #SEAD
|
-- @field #SEAD
|
||||||
SEAD = {
|
SEAD = {
|
||||||
ClassName = "SEAD",
|
ClassName = "SEAD",
|
||||||
TargetSkill = {
|
TargetSkill = {
|
||||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||||
},
|
},
|
||||||
SEADGroupPrefixes = {},
|
SEADGroupPrefixes = {},
|
||||||
SuppressedGroups = {},
|
SuppressedGroups = {},
|
||||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||||
}
|
}
|
||||||
|
|
||||||
-- TODO Complete list?
|
|
||||||
--- Missile enumerators
|
--- Missile enumerators
|
||||||
-- @field Harms
|
-- @field Harms
|
||||||
SEAD.Harms = {
|
SEAD.Harms = {
|
||||||
--[[
|
|
||||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
|
||||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
|
||||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
|
||||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
|
||||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
|
||||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
|
||||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
|
||||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
|
||||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
|
||||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
|
||||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
["AGM_88"] = "AGM_88",
|
["AGM_88"] = "AGM_88",
|
||||||
["AGM_45"] = "AGM_45",
|
["AGM_45"] = "AGM_45",
|
||||||
["AGM_122"] = "AGM_122",
|
["AGM_122"] = "AGM_122",
|
||||||
@ -84,7 +67,7 @@ SEAD = {
|
|||||||
["X_31"] = "X_31",
|
["X_31"] = "X_31",
|
||||||
["Kh25"] = "Kh25",
|
["Kh25"] = "Kh25",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||||
-- Chances are big that the missile will miss.
|
-- Chances are big that the missile will miss.
|
||||||
@ -97,20 +80,20 @@ SEAD = {
|
|||||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||||
function SEAD:New( SEADGroupPrefixes )
|
function SEAD:New( SEADGroupPrefixes )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:F( SEADGroupPrefixes )
|
self:F( SEADGroupPrefixes )
|
||||||
|
|
||||||
if type( SEADGroupPrefixes ) == 'table' then
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||||
end
|
end
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Shot )
|
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||||
self:I("*** SEAD - Started Version 0.2.7")
|
self:I("*** SEAD - Started Version 0.2.8")
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Update the active SEAD Set
|
--- Update the active SEAD Set
|
||||||
@ -120,7 +103,7 @@ end
|
|||||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||||
|
|
||||||
self:F( SEADGroupPrefixes )
|
self:F( SEADGroupPrefixes )
|
||||||
|
|
||||||
if type( SEADGroupPrefixes ) == 'table' then
|
if type( SEADGroupPrefixes ) == 'table' then
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||||
@ -164,112 +147,83 @@ end
|
|||||||
-- @see SEAD
|
-- @see SEAD
|
||||||
-- @param #SEAD
|
-- @param #SEAD
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SEAD:OnEventShot( EventData )
|
function SEAD:HandleEventShot( EventData )
|
||||||
self:T( { EventData } )
|
self:T( { EventData } )
|
||||||
|
|
||||||
local SEADUnit = EventData.IniDCSUnit
|
local SEADUnit = EventData.IniDCSUnit
|
||||||
local SEADUnitName = EventData.IniDCSUnitName
|
local SEADUnitName = EventData.IniDCSUnitName
|
||||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||||
|
|
||||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||||
self:T({ SEADWeapon })
|
self:T({ SEADWeapon })
|
||||||
|
|
||||||
--[[check for SEAD missiles
|
|
||||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_28" --Kh-28 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.X_31P" --Kh-31P anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45A" --AGM-45A anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_45" --AGM-45B anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_88" --AGM-88C anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.LD-10" --LD-10 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84E" --AGM84 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired
|
|
||||||
or
|
|
||||||
SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
if self:_CheckHarms(SEADWeaponName) then
|
if self:_CheckHarms(SEADWeaponName) then
|
||||||
local _targetskill = "Random"
|
local _targetskill = "Random"
|
||||||
local _targetMimgroupName = "none"
|
local _targetMimgroupName = "none"
|
||||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||||
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||||
if _targetUnit and _targetUnit:IsAlive() then
|
if _targetUnit and _targetUnit:IsAlive() then
|
||||||
local _targetMimgroup = _targetUnit:GetGroup()
|
local _targetMimgroup = _targetUnit:GetGroup()
|
||||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||||
self:T( self.SEADGroupPrefixes )
|
self:T( self.SEADGroupPrefixes )
|
||||||
self:T( _targetMimgroupName )
|
self:T( _targetMimgroupName )
|
||||||
end
|
end
|
||||||
-- see if we are shot at
|
-- see if we are shot at
|
||||||
local SEADGroupFound = false
|
local SEADGroupFound = false
|
||||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||||
SEADGroupFound = true
|
SEADGroupFound = true
|
||||||
self:T( '*** SEAD - Group Found' )
|
self:T( '*** SEAD - Group Found' )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if SEADGroupFound == true then -- yes we are being attacked
|
if SEADGroupFound == true then -- yes we are being attacked
|
||||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||||
_targetskill = Skills[ math.random(1,4) ]
|
_targetskill = Skills[ math.random(1,4) ]
|
||||||
end
|
end
|
||||||
self:T( _targetskill )
|
self:T( _targetskill )
|
||||||
if self.TargetSkill[_targetskill] then
|
if self.TargetSkill[_targetskill] then
|
||||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||||
|
|
||||||
|
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||||
|
|
||||||
|
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||||
|
local _targetMimcont= _targetMimgroup:getController()
|
||||||
|
|
||||||
|
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||||
|
|
||||||
|
--tracker ID table to switch groups off and on again
|
||||||
|
local id = {
|
||||||
|
groupName = _targetMimgroup,
|
||||||
|
ctrl = _targetMimcont
|
||||||
|
}
|
||||||
|
|
||||||
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
local function SuppressionEnd(id) --switch group back on
|
||||||
|
local range = self.EngagementRange -- Feature Request #1355
|
||||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
||||||
local _targetMimcont= _targetMimgroup:getController()
|
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||||
|
--id.groupName:enableEmission(true)
|
||||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
||||||
|
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
||||||
--tracker ID table to switch groups off and on again
|
end
|
||||||
local id = {
|
-- randomize switch-on time
|
||||||
groupName = _targetMimgroup,
|
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||||
ctrl = _targetMimcont
|
local SuppressionEndTime = timer.getTime() + delay
|
||||||
}
|
--create entry
|
||||||
|
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||||
local function SuppressionEnd(id) --switch group back on
|
self.SuppressedGroups[id.groupName] = {
|
||||||
local range = self.EngagementRange -- Feature Request #1355
|
SuppressionEndTime = delay
|
||||||
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
}
|
||||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||||
--id.groupName:enableEmission(true)
|
--_targetMimgroup:enableEmission(false)
|
||||||
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||||
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
end
|
||||||
end
|
end
|
||||||
-- randomize switch-on time
|
end
|
||||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
end
|
||||||
local SuppressionEndTime = timer.getTime() + delay
|
end
|
||||||
--create entry
|
|
||||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
|
||||||
self.SuppressedGroups[id.groupName] = {
|
|
||||||
SuppressionEndTime = delay
|
|
||||||
}
|
|
||||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
|
||||||
--_targetMimgroup:enableEmission(false)
|
|
||||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
--- **Functional** -- Short Range Air Defense System
|
--- **Functional** -- Short Range Air Defense System
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- **SHORAD** - Short Range Air Defense System
|
-- **SHORAD** - Short Range Air Defense System
|
||||||
-- Controls a network of short range air/missile defense groups.
|
-- Controls a network of short range air/missile defense groups.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## Missions:
|
-- ## Missions:
|
||||||
--
|
--
|
||||||
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
|
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author : **applevangelist**
|
-- ### Author : **applevangelist **
|
||||||
--
|
--
|
||||||
-- @module Functional.Shorad
|
-- @module Functional.Shorad
|
||||||
-- @image Functional.Shorad.jpg
|
-- @image Functional.Shorad.jpg
|
||||||
--
|
--
|
||||||
-- Date: May 2021
|
-- Date: July 2021
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **SHORAD** class, extends Core.Base#BASE
|
--- **SHORAD** class, extends Core.Base#BASE
|
||||||
@ -26,7 +26,7 @@
|
|||||||
-- @field #string ClassName
|
-- @field #string ClassName
|
||||||
-- @field #string name Name of this Shorad
|
-- @field #string name Name of this Shorad
|
||||||
-- @field #boolean debug Set the debug state
|
-- @field #boolean debug Set the debug state
|
||||||
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP}
|
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP}
|
||||||
-- @field #number Radius Shorad defense radius in meters
|
-- @field #number Radius Shorad defense radius in meters
|
||||||
-- @field Core.Set#SET_GROUP Groupset The set of Shorad groups
|
-- @field Core.Set#SET_GROUP Groupset The set of Shorad groups
|
||||||
-- @field Core.Set#SET_GROUP Samset The set of SAM groups to defend
|
-- @field Core.Set#SET_GROUP Samset The set of SAM groups to defend
|
||||||
@ -41,10 +41,10 @@
|
|||||||
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
|
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
|
||||||
--
|
--
|
||||||
-- Simple Class for a more intelligent Short Range Air Defense System
|
-- Simple Class for a more intelligent Short Range Air Defense System
|
||||||
--
|
--
|
||||||
-- #SHORAD
|
-- #SHORAD
|
||||||
-- Moose derived missile intercepting short range defense system.
|
-- Moose derived missile intercepting short range defense system.
|
||||||
-- Protects a network of SAM sites. Uses events to switch on the defense groups closest to the enemy.
|
-- Protects a network of SAM sites. Uses events to switch on the defense groups closest to the enemy.
|
||||||
@ -52,26 +52,26 @@
|
|||||||
--
|
--
|
||||||
-- ## Usage
|
-- ## Usage
|
||||||
--
|
--
|
||||||
-- Set up a #SET_GROUP for the SAM sites to be protected:
|
-- Set up a #SET_GROUP for the SAM sites to be protected:
|
||||||
--
|
--
|
||||||
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Red SAM"):FilterCoalitions("red"):FilterStart()`
|
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Red SAM"):FilterCoalitions("red"):FilterStart()`
|
||||||
--
|
--
|
||||||
-- By default, SHORAD will defense against both HARMs and AG-Missiles with short to medium range. The default defense probability is 70-90%.
|
-- By default, SHORAD will defense against both HARMs and AG-Missiles with short to medium range. The default defense probability is 70-90%.
|
||||||
-- When a missile is detected, SHORAD will activate defense groups in the given radius around the target for 10 minutes. It will *not* react to friendly fire.
|
-- When a missile is detected, SHORAD will activate defense groups in the given radius around the target for 10 minutes. It will *not* react to friendly fire.
|
||||||
--
|
--
|
||||||
-- ### Start a new SHORAD system, parameters are:
|
-- ### Start a new SHORAD system, parameters are:
|
||||||
|
--
|
||||||
|
-- * Name: Name of this SHORAD.
|
||||||
|
-- * ShoradPrefix: Filter for the Shorad #SET_GROUP.
|
||||||
|
-- * Samset: The #SET_GROUP of SAM sites to defend.
|
||||||
|
-- * Radius: Defense radius in meters.
|
||||||
|
-- * ActiveTimer: Determines how many seconds the systems stay on red alert after wake-up call.
|
||||||
|
-- * Coalition: Coalition, i.e. "blue", "red", or "neutral".*
|
||||||
|
--
|
||||||
|
-- `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")`
|
||||||
--
|
--
|
||||||
-- * Name: Name of this SHORAD.
|
-- ## Customize options
|
||||||
-- * ShoradPrefix: Filter for the Shorad #SET_GROUP.
|
--
|
||||||
-- * Samset: The #SET_GROUP of SAM sites to defend.
|
|
||||||
-- * Radius: Defense radius in meters.
|
|
||||||
-- * ActiveTimer: Determines how many seconds the systems stay on red alert after wake-up call.
|
|
||||||
-- * Coalition: Coalition, i.e. "blue", "red", or "neutral".*
|
|
||||||
--
|
|
||||||
-- `myshorad = SHORAD:New("RedShorad", "Red SHORAD", SamSet, 25000, 600, "red")`
|
|
||||||
--
|
|
||||||
-- ## Customize options
|
|
||||||
--
|
|
||||||
-- * SHORAD:SwitchDebug(debug)
|
-- * SHORAD:SwitchDebug(debug)
|
||||||
-- * SHORAD:SwitchHARMDefense(onoff)
|
-- * SHORAD:SwitchHARMDefense(onoff)
|
||||||
-- * SHORAD:SwitchAGMDefense(onoff)
|
-- * SHORAD:SwitchAGMDefense(onoff)
|
||||||
@ -94,9 +94,9 @@ SHORAD = {
|
|||||||
lid = "",
|
lid = "",
|
||||||
DefendHarms = true,
|
DefendHarms = true,
|
||||||
DefendMavs = true,
|
DefendMavs = true,
|
||||||
DefenseLowProb = 75,
|
DefenseLowProb = 70,
|
||||||
DefenseHighProb = 90,
|
DefenseHighProb = 90,
|
||||||
UseEmOnOff = false,
|
UseEmOnOff = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
@ -108,22 +108,6 @@ do
|
|||||||
--- Missile enumerators
|
--- Missile enumerators
|
||||||
-- @field Harms
|
-- @field Harms
|
||||||
SHORAD.Harms = {
|
SHORAD.Harms = {
|
||||||
--[[
|
|
||||||
["X58"] = "weapons.missiles.X_58", --Kh-58X anti-radiation missiles fired
|
|
||||||
["Kh25"] = "weapons.missiles.Kh25MP_PRGS1VP", --Kh-25MP anti-radiation missiles fired
|
|
||||||
["X25"] = "weapons.missiles.X_25MP", --Kh-25MPU anti-radiation missiles fired
|
|
||||||
["X28"] = "weapons.missiles.X_28", --Kh-28 anti-radiation missiles fired
|
|
||||||
["X31"] = "weapons.missiles.X_31P", --Kh-31P anti-radiation missiles fired
|
|
||||||
["AGM45A"] = "weapons.missiles.AGM_45A", --AGM-45A anti-radiation missiles fired
|
|
||||||
["AGM45"] = "weapons.missiles.AGM_45", --AGM-45B anti-radiation missiles fired
|
|
||||||
["AGM88"] = "weapons.missiles.AGM_88", --AGM-88C anti-radiation missiles fired
|
|
||||||
["AGM122"] = "weapons.missiles.AGM_122", --AGM-122 Sidearm anti-radiation missiles fired
|
|
||||||
["LD10"] = "weapons.missiles.LD-10", --LD-10 anti-radiation missiles fired
|
|
||||||
["ALARM"] = "weapons.missiles.ALARM", --ALARM anti-radiation missiles fired
|
|
||||||
["AGM84E"] = "weapons.missiles.AGM_84E", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84A"] = "weapons.missiles.AGM_84A", --AGM84 anti-radiation missiles fired
|
|
||||||
["AGM84H"] = "weapons.missiles.AGM_84H", --AGM84 anti-radiation missiles fired
|
|
||||||
--]]
|
|
||||||
["AGM_88"] = "AGM_88",
|
["AGM_88"] = "AGM_88",
|
||||||
["AGM_45"] = "AGM_45",
|
["AGM_45"] = "AGM_45",
|
||||||
["AGM_122"] = "AGM_122",
|
["AGM_122"] = "AGM_122",
|
||||||
@ -137,7 +121,7 @@ do
|
|||||||
["X_31"] = "X_31",
|
["X_31"] = "X_31",
|
||||||
["Kh25"] = "Kh25",
|
["Kh25"] = "Kh25",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- TODO complete list?
|
--- TODO complete list?
|
||||||
-- @field Mavs
|
-- @field Mavs
|
||||||
SHORAD.Mavs = {
|
SHORAD.Mavs = {
|
||||||
@ -148,7 +132,7 @@ do
|
|||||||
["Kh31"] = "Kh31",
|
["Kh31"] = "Kh31",
|
||||||
["Kh66"] = "Kh66",
|
["Kh66"] = "Kh66",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Instantiates a new SHORAD object
|
--- Instantiates a new SHORAD object
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string Name Name of this SHORAD
|
-- @param #string Name Name of this SHORAD
|
||||||
@ -157,10 +141,12 @@ do
|
|||||||
-- @param #number Radius Defense radius in meters, used to switch on groups
|
-- @param #number Radius Defense radius in meters, used to switch on groups
|
||||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||||
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition)
|
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||||
|
-- @retunr #SHORAD self
|
||||||
|
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||||
local self = BASE:Inherit( self, BASE:New() )
|
local self = BASE:Inherit( self, BASE:New() )
|
||||||
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||||
|
|
||||||
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
|
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
|
||||||
|
|
||||||
self.name = Name or "MyShorad"
|
self.name = Name or "MyShorad"
|
||||||
@ -171,22 +157,23 @@ do
|
|||||||
self.ActiveTimer = ActiveTimer or 600
|
self.ActiveTimer = ActiveTimer or 600
|
||||||
self.ActiveGroups = {}
|
self.ActiveGroups = {}
|
||||||
self.Groupset = GroupSet
|
self.Groupset = GroupSet
|
||||||
self:HandleEvent( EVENTS.Shot )
|
|
||||||
self.DefendHarms = true
|
self.DefendHarms = true
|
||||||
self.DefendMavs = true
|
self.DefendMavs = true
|
||||||
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||||
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||||
self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||||
self:I("*** SHORAD - Started Version 0.2.5")
|
self:I("*** SHORAD - Started Version 0.2.8")
|
||||||
-- Set the string id for output to DCS.log file.
|
-- Set the string id for output to DCS.log file.
|
||||||
self.lid=string.format("SHORAD %s | ", self.name)
|
self.lid=string.format("SHORAD %s | ", self.name)
|
||||||
self:_InitState()
|
self:_InitState()
|
||||||
|
self:HandleEvent(EVENTS.Shot, self.HandleEventShot)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Initially set all groups to alarm state GREEN
|
--- Initially set all groups to alarm state GREEN
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
function SHORAD:_InitState()
|
function SHORAD:_InitState()
|
||||||
|
self:T(self.lid .. " _InitState")
|
||||||
local table = {}
|
local table = {}
|
||||||
local set = self.Groupset
|
local set = self.Groupset
|
||||||
self:T({set = set})
|
self:T({set = set})
|
||||||
@ -195,33 +182,50 @@ do
|
|||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
--_group:SetAIOff()
|
--_group:SetAIOff()
|
||||||
_group:EnableEmission(false)
|
_group:EnableEmission(false)
|
||||||
|
_group:OptionAlarmStateRed() --Wrapper.Group#GROUP
|
||||||
else
|
else
|
||||||
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
|
||||||
end
|
end
|
||||||
|
_group:OptionDisperseOnAttack(30)
|
||||||
end
|
end
|
||||||
-- gather entropy
|
-- gather entropy
|
||||||
for i=1,10 do
|
for i=1,100 do
|
||||||
math.random()
|
math.random()
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Switch debug state
|
--- Switch debug state on
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean debug Switch debug on (true) or off (false)
|
-- @param #boolean debug Switch debug on (true) or off (false)
|
||||||
function SHORAD:SwitchDebug(debug)
|
function SHORAD:SwitchDebug(onoff)
|
||||||
self:T( { debug } )
|
self:T( { onoff } )
|
||||||
local onoff = debug or false
|
if onoff then
|
||||||
if debug then
|
self:SwitchDebugOn()
|
||||||
self.debug = true
|
|
||||||
--tracing
|
|
||||||
BASE:TraceOn()
|
|
||||||
BASE:TraceClass("SHORAD")
|
|
||||||
else
|
else
|
||||||
self.debug = false
|
self.SwitchDebugOff()
|
||||||
BASE:TraceOff()
|
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Switch debug state on
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOn()
|
||||||
|
self.debug = true
|
||||||
|
--tracing
|
||||||
|
BASE:TraceOn()
|
||||||
|
BASE:TraceClass("SHORAD")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Switch debug state off
|
||||||
|
-- @param #SHORAD self
|
||||||
|
function SHORAD:SwitchDebugOff()
|
||||||
|
self.debug = false
|
||||||
|
BASE:TraceOff()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Switch defense for HARMs
|
--- Switch defense for HARMs
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean onoff
|
-- @param #boolean onoff
|
||||||
@ -229,8 +233,9 @@ do
|
|||||||
self:T( { onoff } )
|
self:T( { onoff } )
|
||||||
local onoff = onoff or true
|
local onoff = onoff or true
|
||||||
self.DefendHarms = onoff
|
self.DefendHarms = onoff
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Switch defense for AGMs
|
--- Switch defense for AGMs
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean onoff
|
-- @param #boolean onoff
|
||||||
@ -238,8 +243,9 @@ do
|
|||||||
self:T( { onoff } )
|
self:T( { onoff } )
|
||||||
local onoff = onoff or true
|
local onoff = onoff or true
|
||||||
self.DefendMavs = onoff
|
self.DefendMavs = onoff
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set defense probability limits
|
--- Set defense probability limits
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #number low Minimum detection limit, integer 1-100
|
-- @param #number low Minimum detection limit, integer 1-100
|
||||||
@ -256,42 +262,50 @@ do
|
|||||||
end
|
end
|
||||||
self.DefenseLowProb = low
|
self.DefenseLowProb = low
|
||||||
self.DefenseHighProb = high
|
self.DefenseHighProb = high
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the number of seconds a SHORAD site will stay active
|
--- Set the number of seconds a SHORAD site will stay active
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #number seconds Number of seconds systems stay active
|
-- @param #number seconds Number of seconds systems stay active
|
||||||
function SHORAD:SetActiveTimer(seconds)
|
function SHORAD:SetActiveTimer(seconds)
|
||||||
|
self:T(self.lid .. " SetActiveTimer")
|
||||||
local timer = seconds or 600
|
local timer = seconds or 600
|
||||||
if timer < 0 then
|
if timer < 0 then
|
||||||
timer = 600
|
timer = 600
|
||||||
end
|
end
|
||||||
self.ActiveTimer = timer
|
self.ActiveTimer = timer
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set the number of meters for the SHORAD defense zone
|
--- Set the number of meters for the SHORAD defense zone
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
||||||
function SHORAD:SetDefenseRadius(meters)
|
function SHORAD:SetDefenseRadius(meters)
|
||||||
|
self:T(self.lid .. " SetDefenseRadius")
|
||||||
local radius = meters or 20000
|
local radius = meters or 20000
|
||||||
if radius < 0 then
|
if radius < 0 then
|
||||||
radius = 20000
|
radius = 20000
|
||||||
end
|
end
|
||||||
self.Radius = radius
|
self.Radius = radius
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set using Emission on/off instead of changing alarm state
|
--- Set using Emission on/off instead of changing alarm state
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
||||||
function SHORAD:SetUsingEmOnOff(switch)
|
function SHORAD:SetUsingEmOnOff(switch)
|
||||||
|
self:T(self.lid .. " SetUsingEmOnOff")
|
||||||
self.UseEmOnOff = switch or false
|
self.UseEmOnOff = switch or false
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a HARM was fired
|
--- Check if a HARM was fired
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string WeaponName
|
-- @param #string WeaponName
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #boolean Returns true for a match
|
||||||
function SHORAD:_CheckHarms(WeaponName)
|
function SHORAD:_CheckHarms(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckHarms")
|
||||||
self:T( { WeaponName } )
|
self:T( { WeaponName } )
|
||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendHarms then
|
if self.DefendHarms then
|
||||||
@ -301,12 +315,13 @@ do
|
|||||||
end
|
end
|
||||||
return hit
|
return hit
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if an AGM was fired
|
--- Check if an AGM was fired
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string WeaponName
|
-- @param #string WeaponName
|
||||||
-- @return #boolean Returns true for a match
|
-- @return #boolean Returns true for a match
|
||||||
function SHORAD:_CheckMavs(WeaponName)
|
function SHORAD:_CheckMavs(WeaponName)
|
||||||
|
self:T(self.lid .. " _CheckMavs")
|
||||||
self:T( { WeaponName } )
|
self:T( { WeaponName } )
|
||||||
local hit = false
|
local hit = false
|
||||||
if self.DefendMavs then
|
if self.DefendMavs then
|
||||||
@ -316,15 +331,16 @@ do
|
|||||||
end
|
end
|
||||||
return hit
|
return hit
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check the coalition of the attacker
|
--- Check the coalition of the attacker
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string Coalition name
|
-- @param #string Coalition name
|
||||||
-- @return #boolean Returns false for a match
|
-- @return #boolean Returns false for a match
|
||||||
function SHORAD:_CheckCoalition(Coalition)
|
function SHORAD:_CheckCoalition(Coalition)
|
||||||
|
self:T(self.lid .. " _CheckCoalition")
|
||||||
local owncoalition = self.Coalition
|
local owncoalition = self.Coalition
|
||||||
local othercoalition = ""
|
local othercoalition = ""
|
||||||
if Coalition == 0 then
|
if Coalition == 0 then
|
||||||
othercoalition = "neutral"
|
othercoalition = "neutral"
|
||||||
elseif Coalition == 1 then
|
elseif Coalition == 1 then
|
||||||
othercoalition = "red"
|
othercoalition = "red"
|
||||||
@ -338,12 +354,13 @@ do
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if the missile is aimed at a SHORAD
|
--- Check if the missile is aimed at a SHORAD
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string TargetGroupName Name of the target group
|
-- @param #string TargetGroupName Name of the target group
|
||||||
-- @return #boolean Returns true for a match, else false
|
-- @return #boolean Returns true for a match, else false
|
||||||
function SHORAD:_CheckShotAtShorad(TargetGroupName)
|
function SHORAD:_CheckShotAtShorad(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtShorad")
|
||||||
local tgtgrp = TargetGroupName
|
local tgtgrp = TargetGroupName
|
||||||
local shorad = self.Groupset
|
local shorad = self.Groupset
|
||||||
local shoradset = shorad:GetAliveSet() --#table
|
local shoradset = shorad:GetAliveSet() --#table
|
||||||
@ -352,17 +369,18 @@ do
|
|||||||
local groupname = _groups:GetName()
|
local groupname = _groups:GetName()
|
||||||
if string.find(groupname, tgtgrp, 1) then
|
if string.find(groupname, tgtgrp, 1) then
|
||||||
returnname = true
|
returnname = true
|
||||||
_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return returnname
|
return returnname
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if the missile is aimed at a SAM site
|
--- Check if the missile is aimed at a SAM site
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string TargetGroupName Name of the target group
|
-- @param #string TargetGroupName Name of the target group
|
||||||
-- @return #boolean Returns true for a match, else false
|
-- @return #boolean Returns true for a match, else false
|
||||||
function SHORAD:_CheckShotAtSams(TargetGroupName)
|
function SHORAD:_CheckShotAtSams(TargetGroupName)
|
||||||
|
self:T(self.lid .. " _CheckShotAtSams")
|
||||||
local tgtgrp = TargetGroupName
|
local tgtgrp = TargetGroupName
|
||||||
local shorad = self.Samset
|
local shorad = self.Samset
|
||||||
--local shoradset = shorad:GetAliveSet() --#table
|
--local shoradset = shorad:GetAliveSet() --#table
|
||||||
@ -376,11 +394,12 @@ do
|
|||||||
end
|
end
|
||||||
return returnname
|
return returnname
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculate if the missile shot is detected
|
--- Calculate if the missile shot is detected
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @return #boolean Returns true for a detection, else false
|
-- @return #boolean Returns true for a detection, else false
|
||||||
function SHORAD:_ShotIsDetected()
|
function SHORAD:_ShotIsDetected()
|
||||||
|
self:T(self.lid .. " _ShotIsDetected")
|
||||||
local IsDetected = false
|
local IsDetected = false
|
||||||
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
|
||||||
local ActualDetection = math.random(1,100) -- value for this shot
|
local ActualDetection = math.random(1,100) -- value for this shot
|
||||||
@ -389,15 +408,15 @@ do
|
|||||||
end
|
end
|
||||||
return IsDetected
|
return IsDetected
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
|
--- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param #string TargetGroup Name of the target group used to build the #ZONE
|
-- @param #string TargetGroup Name of the target group used to build the #ZONE
|
||||||
-- @param #number Radius Radius of the #ZONE
|
-- @param #number Radius Radius of the #ZONE
|
||||||
-- @param #number ActiveTimer Number of seconds to stay active
|
-- @param #number ActiveTimer Number of seconds to stay active
|
||||||
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
||||||
-- @usage Use this function to integrate with other systems, example
|
-- @usage Use this function to integrate with other systems, example
|
||||||
--
|
--
|
||||||
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
||||||
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
|
-- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")
|
||||||
-- myshorad:SwitchDebug(true)
|
-- myshorad:SwitchDebug(true)
|
||||||
@ -405,6 +424,7 @@ do
|
|||||||
-- mymantis:AddShorad(myshorad,720)
|
-- mymantis:AddShorad(myshorad,720)
|
||||||
-- mymantis:Start()
|
-- mymantis:Start()
|
||||||
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer, TargetCat)
|
||||||
|
self:T(self.lid .. " WakeUpShorad")
|
||||||
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
self:T({TargetGroup, Radius, ActiveTimer, TargetCat})
|
||||||
local targetcat = TargetCat or Object.Category.UNIT
|
local targetcat = TargetCat or Object.Category.UNIT
|
||||||
local targetgroup = TargetGroup
|
local targetgroup = TargetGroup
|
||||||
@ -442,7 +462,7 @@ do
|
|||||||
self:T(text)
|
self:T(text)
|
||||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||||
if self.UseEmOnOff then
|
if self.UseEmOnOff then
|
||||||
_group:SetAIOn()
|
--_group:SetAIOn()
|
||||||
_group:EnableEmission(true)
|
_group:EnableEmission(true)
|
||||||
end
|
end
|
||||||
_group:OptionAlarmStateRed()
|
_group:OptionAlarmStateRed()
|
||||||
@ -454,14 +474,15 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Main function - work on the EventData
|
--- Main function - work on the EventData
|
||||||
-- @param #SHORAD self
|
-- @param #SHORAD self
|
||||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||||
function SHORAD:OnEventShot( EventData )
|
function SHORAD:HandleEventShot( EventData )
|
||||||
self:T( { EventData } )
|
self:T( { EventData } )
|
||||||
|
self:T(self.lid .. " HandleEventShot")
|
||||||
--local ShootingUnit = EventData.IniDCSUnit
|
--local ShootingUnit = EventData.IniDCSUnit
|
||||||
--local ShootingUnitName = EventData.IniDCSUnitName
|
--local ShootingUnitName = EventData.IniDCSUnitName
|
||||||
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
||||||
@ -473,7 +494,7 @@ do
|
|||||||
local IsDetected = self:_ShotIsDetected()
|
local IsDetected = self:_ShotIsDetected()
|
||||||
-- convert to text
|
-- convert to text
|
||||||
local DetectedText = "false"
|
local DetectedText = "false"
|
||||||
if IsDetected then
|
if IsDetected then
|
||||||
DetectedText = "true"
|
DetectedText = "true"
|
||||||
end
|
end
|
||||||
local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
|
local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
|
||||||
@ -490,7 +511,7 @@ do
|
|||||||
targetunit = UNIT:Find(targetdata)
|
targetunit = UNIT:Find(targetdata)
|
||||||
elseif targetcat == Object.Category.STATIC then -- STATIC
|
elseif targetcat == Object.Category.STATIC then -- STATIC
|
||||||
targetunit = STATIC:Find(targetdata)
|
targetunit = STATIC:Find(targetdata)
|
||||||
end
|
end
|
||||||
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
||||||
if targetunit and targetunit:IsAlive() then
|
if targetunit and targetunit:IsAlive() then
|
||||||
local targetunitname = targetunit:GetName()
|
local targetunitname = targetunit:GetName()
|
||||||
@ -507,7 +528,7 @@ do
|
|||||||
local text = string.format("%s Missile Target = %s", self.lid, tostring(targetgroupname))
|
local text = string.format("%s Missile Target = %s", self.lid, tostring(targetgroupname))
|
||||||
self:T( text )
|
self:T( text )
|
||||||
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
|
||||||
-- check if we or a SAM site are the target
|
-- check if we or a SAM site are the target
|
||||||
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
|
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
|
||||||
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
|
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
|
||||||
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
|
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
|
||||||
@ -516,12 +537,12 @@ do
|
|||||||
self:T({shotatsams=shotatsams,shotatus=shotatus})
|
self:T({shotatsams=shotatsams,shotatus=shotatus})
|
||||||
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
|
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer, targetcat)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
end
|
end
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- SHORAD end
|
-- SHORAD end
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
@ -11639,7 +11639,7 @@ end
|
|||||||
|
|
||||||
--- Get wind speed on carrier deck parallel and perpendicular to runway.
|
--- Get wind speed on carrier deck parallel and perpendicular to runway.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param #number alt Altitude in meters. Default 50 m.
|
-- @param #number alt Altitude in meters. Default 15 m. (change made from 50m from Discord discussion from Sickdog)
|
||||||
-- @return #number Wind component parallel to runway im m/s.
|
-- @return #number Wind component parallel to runway im m/s.
|
||||||
-- @return #number Wind component perpendicular to runway in m/s.
|
-- @return #number Wind component perpendicular to runway in m/s.
|
||||||
-- @return #number Total wind strength in m/s.
|
-- @return #number Total wind strength in m/s.
|
||||||
@ -11662,7 +11662,7 @@ function AIRBOSS:GetWindOnDeck(alt)
|
|||||||
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
|
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
|
||||||
|
|
||||||
-- Wind (from) vector
|
-- Wind (from) vector
|
||||||
local vw=cv:GetWindWithTurbulenceVec3(alt or 50)
|
local vw=cv:GetWindWithTurbulenceVec3(alt or 15)
|
||||||
|
|
||||||
-- Total wind velocity vector.
|
-- Total wind velocity vector.
|
||||||
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
|
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
--- **Ops** -- Combat Search and Rescue.
|
--- **Ops** -- Combat Search and Rescue.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
@ -212,8 +213,11 @@ CSAR = {
|
|||||||
-- @field #string player Player name if applicable.
|
-- @field #string player Player name if applicable.
|
||||||
-- @field Wrapper.Group#GROUP group Spawned group object.
|
-- @field Wrapper.Group#GROUP group Spawned group object.
|
||||||
-- @field #number timestamp Timestamp for approach process
|
-- @field #number timestamp Timestamp for approach process
|
||||||
|
-- @field #boolean alive Group is alive or dead/rescued
|
||||||
|
--
|
||||||
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
|
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
|
||||||
|
|
||||||
|
--[[ Moved to Utils
|
||||||
-- @field #CSAR.SkipFrequencies
|
-- @field #CSAR.SkipFrequencies
|
||||||
CSAR.SkipFrequencies = {
|
CSAR.SkipFrequencies = {
|
||||||
214,274,291.5,295,297.5,
|
214,274,291.5,295,297.5,
|
||||||
@ -229,7 +233,8 @@ CSAR.SkipFrequencies = {
|
|||||||
905,907,920,935,942,950,995,
|
905,907,920,935,942,950,995,
|
||||||
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
||||||
}
|
}
|
||||||
|
--]]
|
||||||
|
|
||||||
--- All slot / Limit settings
|
--- All slot / Limit settings
|
||||||
-- @type CSAR.AircraftType
|
-- @type CSAR.AircraftType
|
||||||
-- @field #string typename Unit type name.
|
-- @field #string typename Unit type name.
|
||||||
@ -239,13 +244,14 @@ CSAR.AircraftType["SA342Minigun"] = 2
|
|||||||
CSAR.AircraftType["SA342L"] = 4
|
CSAR.AircraftType["SA342L"] = 4
|
||||||
CSAR.AircraftType["SA342M"] = 4
|
CSAR.AircraftType["SA342M"] = 4
|
||||||
CSAR.AircraftType["UH-1H"] = 8
|
CSAR.AircraftType["UH-1H"] = 8
|
||||||
CSAR.AircraftType["Mi-8MTV2"] = 12
|
CSAR.AircraftType["Mi-8MTV2"] = 12
|
||||||
|
CSAR.AircraftType["Mi-8MT"] = 12
|
||||||
CSAR.AircraftType["Mi-24P"] = 8
|
CSAR.AircraftType["Mi-24P"] = 8
|
||||||
CSAR.AircraftType["Mi-24V"] = 8
|
CSAR.AircraftType["Mi-24V"] = 8
|
||||||
|
|
||||||
--- CSAR class version.
|
--- CSAR class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CSAR.version="0.1.8r1"
|
CSAR.version="0.1.8r3"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@ -317,6 +323,7 @@ function CSAR:New(Coalition, Template, Alias)
|
|||||||
self:AddTransition("*", "Boarded", "*") -- Pilot boarded.
|
self:AddTransition("*", "Boarded", "*") -- Pilot boarded.
|
||||||
self:AddTransition("*", "Returning", "*") -- CSAR able to return to base.
|
self:AddTransition("*", "Returning", "*") -- CSAR able to return to base.
|
||||||
self:AddTransition("*", "Rescued", "*") -- Pilot at MASH.
|
self:AddTransition("*", "Rescued", "*") -- Pilot at MASH.
|
||||||
|
self:AddTransition("*", "KIA", "*") -- Pilot killed in action.
|
||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
-- tables, mainly for tracking actions
|
-- tables, mainly for tracking actions
|
||||||
@ -361,7 +368,7 @@ function CSAR:New(Coalition, Template, Alias)
|
|||||||
self.mashprefix = {"MASH"} -- prefixes used to find MASHes
|
self.mashprefix = {"MASH"} -- prefixes used to find MASHes
|
||||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also?
|
self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also?
|
||||||
self.autosmoke = false -- automatically smoke location when heli is near
|
self.autosmoke = false -- automatically smoke location when heli is near
|
||||||
self.autosmokedistance = 1000 -- distance for autosmoke
|
self.autosmokedistance = 2000 -- distance for autosmoke
|
||||||
-- added 0.1.4
|
-- added 0.1.4
|
||||||
self.limitmaxdownedpilots = true
|
self.limitmaxdownedpilots = true
|
||||||
self.maxdownedpilots = 25
|
self.maxdownedpilots = 25
|
||||||
@ -458,6 +465,14 @@ function CSAR:New(Coalition, Template, Alias)
|
|||||||
-- @param #string HeliName Name of the helicopter group.
|
-- @param #string HeliName Name of the helicopter group.
|
||||||
-- @param #number PilotsSaved Number of the saved pilots on board when landing.
|
-- @param #number PilotsSaved Number of the saved pilots on board when landing.
|
||||||
|
|
||||||
|
--- On After "KIA" event. Pilot is dead.
|
||||||
|
-- @function [parent=#CSAR] OnAfterKIA
|
||||||
|
-- @param #CSAR self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #string Pilotname Name of the pilot KIA.
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -491,6 +506,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
|
|||||||
DownedPilot.typename = Typename or ""
|
DownedPilot.typename = Typename or ""
|
||||||
DownedPilot.group = Group
|
DownedPilot.group = Group
|
||||||
DownedPilot.timestamp = 0
|
DownedPilot.timestamp = 0
|
||||||
|
DownedPilot.alive = true
|
||||||
|
|
||||||
-- Add Pilot
|
-- Add Pilot
|
||||||
local PilotTable = self.downedPilots
|
local PilotTable = self.downedPilots
|
||||||
@ -609,7 +625,8 @@ end
|
|||||||
-- @param #number _freq Frequency
|
-- @param #number _freq Frequency
|
||||||
-- @param #boolean noMessage
|
-- @param #boolean noMessage
|
||||||
-- @param #string _description Description
|
-- @param #string _description Description
|
||||||
function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description )
|
-- @param #boolean forcedesc Use the description only for the pilot track entry
|
||||||
|
function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description, forcedesc )
|
||||||
self:T(self.lid .. " _AddCsar")
|
self:T(self.lid .. " _AddCsar")
|
||||||
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
|
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
|
||||||
|
|
||||||
@ -622,11 +639,10 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
|||||||
|
|
||||||
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq)
|
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq)
|
||||||
|
|
||||||
local _typeName = _typeName or "PoW"
|
local _typeName = _typeName or "Pilot"
|
||||||
|
|
||||||
if not noMessage then
|
if not noMessage then
|
||||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10)
|
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10)
|
||||||
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _typeName .. " is down. ",10,"INFO"):ToCoalition(self.coalition)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if _freq then
|
if _freq then
|
||||||
@ -635,15 +651,14 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
|||||||
|
|
||||||
self:_AddSpecialOptions(_spawnedGroup)
|
self:_AddSpecialOptions(_spawnedGroup)
|
||||||
|
|
||||||
local _text = " "
|
local _text = _description
|
||||||
if _playerName ~= nil then
|
if not forcedesc then
|
||||||
_text = "Pilot " .. _playerName .. " of " .. _unitName .. " - " .. _typeName
|
if _playerName ~= nil then
|
||||||
elseif _typeName ~= nil then
|
_text = "Pilot " .. _playerName
|
||||||
_text = "AI Pilot of " .. _unitName .. " - " .. _typeName
|
elseif _unitName ~= nil then
|
||||||
else
|
_text = "AI Pilot of " .. _unitName
|
||||||
_text = _description
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:T({_spawnedGroup, _alias})
|
self:T({_spawnedGroup, _alias})
|
||||||
|
|
||||||
local _GroupName = _spawnedGroup:GetName() or _alias
|
local _GroupName = _spawnedGroup:GetName() or _alias
|
||||||
@ -662,7 +677,10 @@ end
|
|||||||
-- @param #string _description (optional) Description.
|
-- @param #string _description (optional) Description.
|
||||||
-- @param #boolean _randomPoint (optional) Random yes or no.
|
-- @param #boolean _randomPoint (optional) Random yes or no.
|
||||||
-- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
|
-- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
|
||||||
function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage)
|
-- @param #string unitname (optional) Name of the lost unit.
|
||||||
|
-- @param #string typename (optional) Type of plane.
|
||||||
|
-- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names.
|
||||||
|
function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc)
|
||||||
self:T(self.lid .. " _SpawnCsarAtZone")
|
self:T(self.lid .. " _SpawnCsarAtZone")
|
||||||
local freq = self:_GenerateADFFrequency()
|
local freq = self:_GenerateADFFrequency()
|
||||||
local _triggerZone = ZONE:New(_zone) -- trigger to use as reference position
|
local _triggerZone = ZONE:New(_zone) -- trigger to use as reference position
|
||||||
@ -671,7 +689,9 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local _description = _description or "Unknown"
|
local _description = _description or "PoW"
|
||||||
|
local unitname = unitname or "Old Rusty"
|
||||||
|
local typename = typename or "Phantom II"
|
||||||
|
|
||||||
local pos = {}
|
local pos = {}
|
||||||
if _randomPoint then
|
if _randomPoint then
|
||||||
@ -690,7 +710,7 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _
|
|||||||
_country = country.id.UN_PEACEKEEPERS
|
_country = country.id.UN_PEACEKEEPERS
|
||||||
end
|
end
|
||||||
|
|
||||||
self:_AddCsar(_coalition, _country, pos, "PoW", _description, nil, freq, _nomessage, _description)
|
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -702,12 +722,15 @@ end
|
|||||||
-- @param #string Description (optional) Description.
|
-- @param #string Description (optional) Description.
|
||||||
-- @param #boolean RandomPoint (optional) Random yes or no.
|
-- @param #boolean RandomPoint (optional) Random yes or no.
|
||||||
-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
|
-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
|
||||||
-- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys works, they can do this like so:
|
-- @param #string Unitname (optional) Name of the lost unit.
|
||||||
|
-- @param #string Typename (optional) Type of plane.
|
||||||
|
-- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names.
|
||||||
|
-- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys work, they can do this like so:
|
||||||
--
|
--
|
||||||
-- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition
|
-- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition
|
||||||
-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true )
|
-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Wagner", true, false, "Charly-1-1", "F5E" )
|
||||||
function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessage)
|
function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessage, Unitname, Typename, Forcedesc)
|
||||||
self:_SpawnCsarAtZone(Zone, Coalition, Description, RandomPoint, Nomessage)
|
self:_SpawnCsarAtZone(Zone, Coalition, Description, RandomPoint, Nomessage, Unitname, Typename, Forcedesc)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -865,7 +888,11 @@ function CSAR:_EventHandler(EventData)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
|
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
|
||||||
self:_RescuePilots(_unit)
|
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
|
||||||
|
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
|
||||||
|
else
|
||||||
|
self:_RescuePilots(_unit)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
|
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
|
||||||
end
|
end
|
||||||
@ -916,7 +943,7 @@ function CSAR:_CheckNameInDownedPilots(name)
|
|||||||
local found = false
|
local found = false
|
||||||
local table = nil
|
local table = nil
|
||||||
for _,_pilot in pairs(PilotTable) do
|
for _,_pilot in pairs(PilotTable) do
|
||||||
if _pilot.name == name then
|
if _pilot.name == name and _pilot.alive == true then
|
||||||
found = true
|
found = true
|
||||||
table = _pilot
|
table = _pilot
|
||||||
break
|
break
|
||||||
@ -933,25 +960,10 @@ end
|
|||||||
function CSAR:_RemoveNameFromDownedPilots(name,force)
|
function CSAR:_RemoveNameFromDownedPilots(name,force)
|
||||||
local PilotTable = self.downedPilots --#CSAR.DownedPilot
|
local PilotTable = self.downedPilots --#CSAR.DownedPilot
|
||||||
local found = false
|
local found = false
|
||||||
for _,_pilot in pairs(PilotTable) do
|
for _index,_pilot in pairs(PilotTable) do
|
||||||
if _pilot.name == name then
|
if _pilot.name == name then
|
||||||
local group = _pilot.group -- Wrapper.Group#GROUP
|
self.downedPilots[_index].alive = false
|
||||||
if group then
|
|
||||||
if (not group:IsAlive()) or ( force == true) then -- don\'t delete groups which still exist
|
|
||||||
found = true
|
|
||||||
_pilot.desc = nil
|
|
||||||
_pilot.frequency = nil
|
|
||||||
_pilot.index = nil
|
|
||||||
_pilot.name = nil
|
|
||||||
_pilot.originalUnit = nil
|
|
||||||
_pilot.player = nil
|
|
||||||
_pilot.side = nil
|
|
||||||
_pilot.typename = nil
|
|
||||||
_pilot.group = nil
|
|
||||||
_pilot.timestamp = nil
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return found
|
return found
|
||||||
end
|
end
|
||||||
@ -974,7 +986,7 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local _woundedGroup = _downedpilot.group
|
local _woundedGroup = _downedpilot.group
|
||||||
if _woundedGroup ~= nil then
|
if _woundedGroup ~= nil and _woundedGroup:IsAlive() then
|
||||||
local _heliUnit = self:_GetSARHeli(_heliName) -- Wrapper.Unit#UNIT
|
local _heliUnit = self:_GetSARHeli(_heliName) -- Wrapper.Unit#UNIT
|
||||||
|
|
||||||
local _lookupKeyHeli = _heliName .. "_" .. _woundedGroupName --lookup key for message state tracking
|
local _lookupKeyHeli = _heliName .. "_" .. _woundedGroupName --lookup key for message state tracking
|
||||||
@ -986,11 +998,15 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
|
|||||||
self:T("...helinunit nil!")
|
self:T("...helinunit nil!")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
--if self:_CheckGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _heliName) then
|
|
||||||
local _heliCoord = _heliUnit:GetCoordinate()
|
local _heliCoord = _heliUnit:GetCoordinate()
|
||||||
local _leaderCoord = _woundedGroup:GetCoordinate()
|
local _leaderCoord = _woundedGroup:GetCoordinate()
|
||||||
local _distance = self:_GetDistance(_heliCoord,_leaderCoord)
|
local _distance = self:_GetDistance(_heliCoord,_leaderCoord)
|
||||||
|
-- autosmoke
|
||||||
|
if (self.autosmoke == true) and (_distance < self.autosmokedistance) and (_distance ~= -1) then
|
||||||
|
self:_PopSmokeForGroup(_woundedGroupName, _woundedGroup)
|
||||||
|
end
|
||||||
|
|
||||||
if _distance < self.approachdist_near and _distance > 0 then
|
if _distance < self.approachdist_near and _distance > 0 then
|
||||||
if self:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then
|
if self:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then
|
||||||
-- we\'re close, reschedule
|
-- we\'re close, reschedule
|
||||||
@ -998,14 +1014,35 @@ function CSAR:_CheckWoundedGroupStatus(heliname,woundedgroupname)
|
|||||||
self:__Approach(-5,heliname,woundedgroupname)
|
self:__Approach(-5,heliname,woundedgroupname)
|
||||||
end
|
end
|
||||||
elseif _distance >= self.approachdist_near and _distance < self.approachdist_far then
|
elseif _distance >= self.approachdist_near and _distance < self.approachdist_far then
|
||||||
self.heliVisibleMessage[_lookupKeyHeli] = nil
|
-- message once
|
||||||
|
if self.heliVisibleMessage[_lookupKeyHeli] == nil then
|
||||||
|
local _pilotName = _downedpilot.desc
|
||||||
|
if self.autosmoke == true then
|
||||||
|
local dist = self.autosmokedistance / 1000
|
||||||
|
local disttext = string.format("%.0fkm",dist)
|
||||||
|
if _SETTINGS:IsImperial() then
|
||||||
|
local dist = UTILS.MetersToNM(self.autosmokedistance)
|
||||||
|
disttext = string.format("%.0fnm",dist)
|
||||||
|
end
|
||||||
|
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nI'll pop a smoke when you are %s away.\nLand or hover by the smoke.", _heliName, _pilotName, disttext), self.messageTime,false,true)
|
||||||
|
else
|
||||||
|
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud!\nRequest a flare or smoke if you need.", _heliName, _pilotName), self.messageTime,false,true)
|
||||||
|
end
|
||||||
|
--mark as shown for THIS heli and THIS group
|
||||||
|
self.heliVisibleMessage[_lookupKeyHeli] = true
|
||||||
|
end
|
||||||
|
self.heliCloseMessage[_lookupKeyHeli] = nil
|
||||||
|
self.landedStatus[_lookupKeyHeli] = nil
|
||||||
--reschedule as units aren\'t dead yet , schedule for a bit slower though as we\'re far away
|
--reschedule as units aren\'t dead yet , schedule for a bit slower though as we\'re far away
|
||||||
_downedpilot.timestamp = timer.getAbsTime()
|
_downedpilot.timestamp = timer.getAbsTime()
|
||||||
self:__Approach(-10,heliname,woundedgroupname)
|
self:__Approach(-10,heliname,woundedgroupname)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:T("...Downed Pilot KIA?!")
|
self:T("...Downed Pilot KIA?!")
|
||||||
self:_RemoveNameFromDownedPilots(_downedpilot.name)
|
if not _downedpilot.alive then
|
||||||
|
--self:__KIA(1,_downedpilot.name)
|
||||||
|
self:_RemoveNameFromDownedPilots(_downedpilot.name, true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1106,27 +1143,27 @@ function CSAR:_IsLoadingDoorOpen( unit_name )
|
|||||||
local type_name = unit:getTypeName()
|
local type_name = unit:getTypeName()
|
||||||
|
|
||||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
||||||
self:I(unit_name .. " Cargo doors are open or cargo door not present")
|
self:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||||
ret_val = true
|
ret_val = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
||||||
self:I(unit_name .. " a side door is open")
|
self:T(unit_name .. " a side door is open")
|
||||||
ret_val = true
|
ret_val = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
||||||
self:I(unit_name .. " a side door is open ")
|
self:T(unit_name .. " a side door is open ")
|
||||||
ret_val = true
|
ret_val = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
||||||
self:I(unit_name .. " front door(s) are open")
|
self:T(unit_name .. " front door(s) are open")
|
||||||
ret_val = true
|
ret_val = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ret_val == false then
|
if ret_val == false then
|
||||||
self:I(unit_name .. " all doors are closed")
|
self:T(unit_name .. " all doors are closed")
|
||||||
end
|
end
|
||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
@ -1155,27 +1192,13 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
|||||||
|
|
||||||
local _reset = true
|
local _reset = true
|
||||||
|
|
||||||
if (self.autosmoke == true) and (_distance < self.autosmokedistance) then
|
|
||||||
self:_PopSmokeForGroup(_woundedGroupName, _woundedLeader)
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.heliVisibleMessage[_lookupKeyHeli] == nil then
|
|
||||||
if self.autosmoke == true then
|
|
||||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud! Land or hover by the smoke.", _heliName, _pilotName), self.messageTime,true,true)
|
|
||||||
else
|
|
||||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn, that thing is loud! Request a Flare or Smoke if you need", _heliName, _pilotName), self.messageTime,true,true)
|
|
||||||
end
|
|
||||||
--mark as shown for THIS heli and THIS group
|
|
||||||
self.heliVisibleMessage[_lookupKeyHeli] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if (_distance < 500) then
|
if (_distance < 500) then
|
||||||
|
|
||||||
if self.heliCloseMessage[_lookupKeyHeli] == nil then
|
if self.heliCloseMessage[_lookupKeyHeli] == nil then
|
||||||
if self.autosmoke == true then
|
if self.autosmoke == true then
|
||||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", _heliName, _pilotName), self.messageTime,true,true)
|
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land or hover at the smoke.", _heliName, _pilotName), self.messageTime,false,true)
|
||||||
else
|
else
|
||||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,true,true)
|
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true)
|
||||||
end
|
end
|
||||||
--mark as shown for THIS heli and THIS group
|
--mark as shown for THIS heli and THIS group
|
||||||
self.heliCloseMessage[_lookupKeyHeli] = true
|
self.heliCloseMessage[_lookupKeyHeli] = true
|
||||||
@ -1192,14 +1215,14 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
|||||||
self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 )
|
self.landedStatus[_lookupKeyHeli] = math.floor( (_distance - self.loadDistance) / 3.6 )
|
||||||
_time = self.landedStatus[_lookupKeyHeli]
|
_time = self.landedStatus[_lookupKeyHeli]
|
||||||
self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate())
|
self:_OrderGroupToMoveToPoint(_woundedGroup, _heliUnit:GetCoordinate())
|
||||||
self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, true)
|
self:_DisplayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. " gets in. \nETA " .. _time .. " more seconds.", self.messageTime, false)
|
||||||
else
|
else
|
||||||
_time = self.landedStatus[_lookupKeyHeli] - 10
|
_time = self.landedStatus[_lookupKeyHeli] - 10
|
||||||
self.landedStatus[_lookupKeyHeli] = _time
|
self.landedStatus[_lookupKeyHeli] = _time
|
||||||
end
|
end
|
||||||
if _time <= 0 or _distance < self.loadDistance then
|
if _time <= 0 or _distance < self.loadDistance then
|
||||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
||||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, bugger!", self.messageTime, true)
|
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self.landedStatus[_lookupKeyHeli] = nil
|
self.landedStatus[_lookupKeyHeli] = nil
|
||||||
@ -1211,7 +1234,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
|||||||
else
|
else
|
||||||
if (_distance < self.loadDistance) then
|
if (_distance < self.loadDistance) then
|
||||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
||||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, honk!", self.messageTime, true)
|
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
|
self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName)
|
||||||
@ -1252,7 +1275,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
|||||||
self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true)
|
self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true)
|
||||||
else
|
else
|
||||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
|
||||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, noob!", self.messageTime, true)
|
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self.hoverStatus[_lookupKeyHeli] = nil
|
self.hoverStatus[_lookupKeyHeli] = nil
|
||||||
@ -1281,36 +1304,6 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (Internal) Check if group not KIA.
|
|
||||||
-- @param #CSAR self
|
|
||||||
-- @param Wrapper.Group#GROUP _woundedGroup
|
|
||||||
-- @param #string _woundedGroupName
|
|
||||||
-- @param Wrapper.Unit#UNIT _heliUnit
|
|
||||||
-- @param #string _heliName
|
|
||||||
-- @return #boolean Outcome
|
|
||||||
function CSAR:_CheckGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _heliName)
|
|
||||||
self:T(self.lid .. " _CheckGroupNotKIA")
|
|
||||||
-- check if unit has died or been picked up
|
|
||||||
local inTransit = false
|
|
||||||
if _woundedGroup and _heliUnit then
|
|
||||||
for _currentHeli, _groups in pairs(self.inTransitGroups) do
|
|
||||||
if _groups[_woundedGroupName] then
|
|
||||||
inTransit = true
|
|
||||||
self:_DisplayToAllSAR(string.format("%s has been picked up by %s", _woundedGroupName, _currentHeli), self.coalition, self.messageTime)
|
|
||||||
break
|
|
||||||
end -- end name check
|
|
||||||
end -- end loop
|
|
||||||
if not inTransit then
|
|
||||||
-- KIA
|
|
||||||
self:_DisplayToAllSAR(string.format("%s is KIA ", _woundedGroupName), self.coalition, self.messageTime)
|
|
||||||
end
|
|
||||||
--stops the message being displayed again
|
|
||||||
self:_RemoveNameFromDownedPilots(_woundedGroupName)
|
|
||||||
end
|
|
||||||
--continue
|
|
||||||
return inTransit
|
|
||||||
end
|
|
||||||
|
|
||||||
--- (Internal) Monitor in-flight returning groups.
|
--- (Internal) Monitor in-flight returning groups.
|
||||||
-- @param #CSAR self
|
-- @param #CSAR self
|
||||||
-- @param #string heliname Heli name
|
-- @param #string heliname Heli name
|
||||||
@ -1339,8 +1332,12 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if _dist < 200 and _heliUnit:InAir() == false then
|
if _dist < 200 and _heliUnit:InAir() == false then
|
||||||
|
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then
|
||||||
|
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
|
||||||
|
else
|
||||||
self:_RescuePilots(_heliUnit)
|
self:_RescuePilots(_heliUnit)
|
||||||
return
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--queue up
|
--queue up
|
||||||
@ -1428,12 +1425,8 @@ function CSAR:_GetPositionOfWounded(_woundedGroup)
|
|||||||
_coordinatesText = _coordinate:ToStringLLDMS()
|
_coordinatesText = _coordinate:ToStringLLDMS()
|
||||||
elseif self.coordtype == 2 then -- MGRS
|
elseif self.coordtype == 2 then -- MGRS
|
||||||
_coordinatesText = _coordinate:ToStringMGRS()
|
_coordinatesText = _coordinate:ToStringMGRS()
|
||||||
elseif self.coordtype == 3 then -- Bullseye Imperial
|
else -- Bullseye Metric --(medevac.coordtype == 4 or 3)
|
||||||
local Settings = _SETTINGS:SetImperial()
|
_coordinatesText = _coordinate:ToStringBULLS(self.coalition)
|
||||||
_coordinatesText = _coordinate:ToStringBULLS(self.coalition,Settings)
|
|
||||||
else -- Bullseye Metric --(medevac.coordtype == 4)
|
|
||||||
local Settings = _SETTINGS:SetMetric()
|
|
||||||
_coordinatesText = _coordinate:ToStringBULLS(self.coalition,Settings)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return _coordinatesText
|
return _coordinatesText
|
||||||
@ -1461,18 +1454,17 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
|||||||
self:T({Table=_value})
|
self:T({Table=_value})
|
||||||
--local _woundedGroup = GROUP:FindByName(_groupName)
|
--local _woundedGroup = GROUP:FindByName(_groupName)
|
||||||
local _woundedGroup = _value.group
|
local _woundedGroup = _value.group
|
||||||
if _woundedGroup then
|
if _woundedGroup and _value.alive then
|
||||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
||||||
local _helicoord = _heli:GetCoordinate()
|
local _helicoord = _heli:GetCoordinate()
|
||||||
local _woundcoord = _woundedGroup:GetCoordinate()
|
local _woundcoord = _woundedGroup:GetCoordinate()
|
||||||
local _distance = self:_GetDistance(_helicoord, _woundcoord)
|
local _distance = self:_GetDistance(_helicoord, _woundcoord)
|
||||||
self:T({_distance = _distance})
|
self:T({_distance = _distance})
|
||||||
-- change distance to miles if self.coordtype < 4
|
|
||||||
local distancetext = ""
|
local distancetext = ""
|
||||||
if self.coordtype < 4 then
|
if _SETTINGS:IsImperial() then
|
||||||
distancetext = string.format("%.3fnm",UTILS.MetersToNM(_distance))
|
distancetext = string.format("%.1fnm",UTILS.MetersToNM(_distance))
|
||||||
else
|
else
|
||||||
distancetext = string.format("%.3fkm", _distance/1000.0)
|
distancetext = string.format("%.1fkm", _distance/1000.0)
|
||||||
end
|
end
|
||||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
||||||
end
|
end
|
||||||
@ -1537,15 +1529,16 @@ function CSAR:_SignalFlare(_unitName)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local _closest = self:_GetClosestDownedPilot(_heli)
|
local _closest = self:_GetClosestDownedPilot(_heli)
|
||||||
|
local smokedist = 8000
|
||||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < 8000.0 then
|
if self.approachdist_far > smokedist then smokedist = self.approachdist_far end
|
||||||
|
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then
|
||||||
|
|
||||||
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
||||||
local _distance = 0
|
local _distance = 0
|
||||||
if self.coordtype < 4 then
|
if _SETTINGS:IsImperial() then
|
||||||
_distance = string.format("%.3fnm",UTILS.MetersToNM(_closest.distance))
|
_distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance))
|
||||||
else
|
else
|
||||||
_distance = string.format("%.3fkm",_closest.distance)
|
_distance = string.format("%.1fkm",_closest.distance)
|
||||||
end
|
end
|
||||||
local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
||||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
||||||
@ -1553,11 +1546,13 @@ function CSAR:_SignalFlare(_unitName)
|
|||||||
local _coord = _closest.pilot:GetCoordinate()
|
local _coord = _closest.pilot:GetCoordinate()
|
||||||
_coord:FlareRed(_clockDir)
|
_coord:FlareRed(_clockDir)
|
||||||
else
|
else
|
||||||
local disttext = "4.3nm"
|
local _distance = smokedist
|
||||||
if self.coordtype == 4 then
|
if _SETTINGS:IsImperial() then
|
||||||
disttext = "8km"
|
_distance = string.format("%.1fnm",UTILS.MetersToNM(smokedist))
|
||||||
end
|
else
|
||||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",disttext), self.messageTime)
|
_distance = string.format("%.1fkm",smokedist/1000)
|
||||||
|
end
|
||||||
|
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1589,14 +1584,16 @@ function CSAR:_Reqsmoke( _unitName )
|
|||||||
if _heli == nil then
|
if _heli == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
local smokedist = 8000
|
||||||
|
if smokedist < self.approachdist_far then smokedist = self.approachdist_far end
|
||||||
local _closest = self:_GetClosestDownedPilot(_heli)
|
local _closest = self:_GetClosestDownedPilot(_heli)
|
||||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < 8000.0 then
|
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance < smokedist then
|
||||||
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
||||||
local _distance = 0
|
local _distance = 0
|
||||||
if self.coordtype < 4 then
|
if _SETTINGS:IsImperial() then
|
||||||
_distance = string.format("%.3fnm",UTILS.MetersToNM(_closest.distance))
|
_distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance))
|
||||||
else
|
else
|
||||||
_distance = string.format("%.3fkm",_closest.distance)
|
_distance = string.format("%.1fkm",_closest.distance)
|
||||||
end
|
end
|
||||||
local _msg = string.format("%s - Popping signal smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
local _msg = string.format("%s - Popping signal smoke at your %s o\'clock. Distance %s", _unitName, _clockDir, _distance)
|
||||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true)
|
||||||
@ -1604,11 +1601,13 @@ function CSAR:_Reqsmoke( _unitName )
|
|||||||
local color = self.smokecolor
|
local color = self.smokecolor
|
||||||
_coord:Smoke(color)
|
_coord:Smoke(color)
|
||||||
else
|
else
|
||||||
local disttext = "4.3nm"
|
local _distance = 0
|
||||||
if self.coordtype == 4 then
|
if _SETTINGS:IsImperial() then
|
||||||
disttext = "8km"
|
_distance = string.format("%.1fnm",UTILS.MetersToNM(smokedist))
|
||||||
end
|
else
|
||||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",disttext), self.messageTime)
|
_distance = string.format("%.1fkm",smokedist/1000)
|
||||||
|
end
|
||||||
|
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1753,70 +1752,10 @@ end
|
|||||||
-- @param #CSAR self
|
-- @param #CSAR self
|
||||||
function CSAR:_GenerateVHFrequencies()
|
function CSAR:_GenerateVHFrequencies()
|
||||||
self:T(self.lid .. " _GenerateVHFrequencies")
|
self:T(self.lid .. " _GenerateVHFrequencies")
|
||||||
local _skipFrequencies = self.SkipFrequencies
|
--local _skipFrequencies = self.SkipFrequencies
|
||||||
|
|
||||||
local FreeVHFFrequencies = {}
|
local FreeVHFFrequencies = {}
|
||||||
local UsedVHFFrequencies = {}
|
FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
|
||||||
|
|
||||||
-- first range
|
|
||||||
local _start = 200000
|
|
||||||
while _start < 400000 do
|
|
||||||
|
|
||||||
-- skip existing NDB frequencies
|
|
||||||
local _found = false
|
|
||||||
for _, value in pairs(_skipFrequencies) do
|
|
||||||
if value * 1000 == _start then
|
|
||||||
_found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if _found == false then
|
|
||||||
table.insert(FreeVHFFrequencies, _start)
|
|
||||||
end
|
|
||||||
|
|
||||||
_start = _start + 10000
|
|
||||||
end
|
|
||||||
|
|
||||||
-- second range
|
|
||||||
_start = 400000
|
|
||||||
while _start < 850000 do
|
|
||||||
|
|
||||||
-- skip existing NDB frequencies
|
|
||||||
local _found = false
|
|
||||||
for _, value in pairs(_skipFrequencies) do
|
|
||||||
if value * 1000 == _start then
|
|
||||||
_found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if _found == false then
|
|
||||||
table.insert(FreeVHFFrequencies, _start)
|
|
||||||
end
|
|
||||||
|
|
||||||
_start = _start + 10000
|
|
||||||
end
|
|
||||||
|
|
||||||
-- third range
|
|
||||||
_start = 850000
|
|
||||||
while _start <= 999000 do -- updated for Gazelle
|
|
||||||
|
|
||||||
-- skip existing NDB frequencies
|
|
||||||
local _found = false
|
|
||||||
for _, value in pairs(_skipFrequencies) do
|
|
||||||
if value * 1000 == _start then
|
|
||||||
_found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if _found == false then
|
|
||||||
table.insert(FreeVHFFrequencies, _start)
|
|
||||||
end
|
|
||||||
|
|
||||||
_start = _start + 50000
|
|
||||||
end
|
|
||||||
self.FreeVHFFrequencies = FreeVHFFrequencies
|
self.FreeVHFFrequencies = FreeVHFFrequencies
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1853,7 +1792,8 @@ function CSAR:_GetClockDirection(_heli, _group)
|
|||||||
if _heading then
|
if _heading then
|
||||||
local Aspect = Angle - _heading
|
local Aspect = Angle - _heading
|
||||||
if Aspect == 0 then Aspect = 360 end
|
if Aspect == 0 then Aspect = 360 end
|
||||||
clock = math.floor(Aspect / 30)
|
--clock = math.floor(Aspect / 30)
|
||||||
|
clock = math.abs(UTILS.Round((Aspect / 30),0))
|
||||||
if clock == 0 then clock = 12 end
|
if clock == 0 then clock = 12 end
|
||||||
end
|
end
|
||||||
return clock
|
return clock
|
||||||
@ -1913,8 +1853,8 @@ function CSAR:_CountActiveDownedPilots()
|
|||||||
self:T(self.lid .. " _CountActiveDownedPilots")
|
self:T(self.lid .. " _CountActiveDownedPilots")
|
||||||
local PilotsInFieldN = 0
|
local PilotsInFieldN = 0
|
||||||
for _, _unitName in pairs(self.downedPilots) do
|
for _, _unitName in pairs(self.downedPilots) do
|
||||||
self:T({_unitName})
|
self:T({_unitName.desc})
|
||||||
if _unitName.name ~= nil then
|
if _unitName.alive == true then
|
||||||
PilotsInFieldN = PilotsInFieldN + 1
|
PilotsInFieldN = PilotsInFieldN + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1965,6 +1905,26 @@ function CSAR:onafterStart(From, Event, To)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Function called before Status() event.
|
||||||
|
-- @param #CSAR self
|
||||||
|
function CSAR:_CheckDownedPilotTable()
|
||||||
|
local pilots = self.downedPilots
|
||||||
|
for _,_entry in pairs (pilots) do
|
||||||
|
self:T("Checking for " .. _entry.name)
|
||||||
|
self:T({entry=_entry})
|
||||||
|
local group = _entry.group
|
||||||
|
if not group:IsAlive() then
|
||||||
|
self:T("Group is dead")
|
||||||
|
if _entry.alive == true then
|
||||||
|
self:T("Switching .alive to false")
|
||||||
|
self:__KIA(1,_entry.desc)
|
||||||
|
self:_RemoveNameFromDownedPilots(_entry.name,true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function called before Status() event.
|
--- (Internal) Function called before Status() event.
|
||||||
-- @param #CSAR self.
|
-- @param #CSAR self.
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
@ -1975,15 +1935,18 @@ function CSAR:onbeforeStatus(From, Event, To)
|
|||||||
-- housekeeping
|
-- housekeeping
|
||||||
self:_AddMedevacMenuItem()
|
self:_AddMedevacMenuItem()
|
||||||
self:_RefreshRadioBeacons()
|
self:_RefreshRadioBeacons()
|
||||||
|
self:_CheckDownedPilotTable()
|
||||||
for _,_sar in pairs (self.csarUnits) do
|
for _,_sar in pairs (self.csarUnits) do
|
||||||
local PilotTable = self.downedPilots
|
local PilotTable = self.downedPilots
|
||||||
for _,_entry in pairs (PilotTable) do
|
for _,_entry in pairs (PilotTable) do
|
||||||
local entry = _entry -- #CSAR.DownedPilot
|
if _entry.alive then
|
||||||
local name = entry.name
|
local entry = _entry -- #CSAR.DownedPilot
|
||||||
local timestamp = entry.timestamp or 0
|
local name = entry.name
|
||||||
local now = timer.getAbsTime()
|
local timestamp = entry.timestamp or 0
|
||||||
if now - timestamp > 17 then -- only check if we\'re not in approach mode, which is iterations of 5 and 10.
|
local now = timer.getAbsTime()
|
||||||
self:_CheckWoundedGroupStatus(_sar,name)
|
if now - timestamp > 17 then -- only check if we\'re not in approach mode, which is iterations of 5 and 10.
|
||||||
|
self:_CheckWoundedGroupStatus(_sar,name)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -56,30 +56,30 @@
|
|||||||
--
|
--
|
||||||
-- ## set up a detection SET_GROUP
|
-- ## set up a detection SET_GROUP
|
||||||
--
|
--
|
||||||
-- `Red_DetectionSetGroup = SET_GROUP:New()`
|
-- `Red_DetectionSetGroup = SET_GROUP:New()`
|
||||||
-- `Red_DetectionSetGroup:FilterPrefixes( { "Red EWR" } )`
|
-- `Red_DetectionSetGroup:FilterPrefixes( { "Red EWR" } )`
|
||||||
-- `Red_DetectionSetGroup:FilterOnce()`
|
-- `Red_DetectionSetGroup:FilterOnce()`
|
||||||
--
|
--
|
||||||
-- ## New Intel type detection for the red side, logname "KGB"
|
-- ## New Intel type detection for the red side, logname "KGB"
|
||||||
--
|
--
|
||||||
-- `RedIntel = INTEL:New(Red_DetectionSetGroup,"red","KGB")`
|
-- `RedIntel = INTEL:New(Red_DetectionSetGroup,"red","KGB")`
|
||||||
-- `RedIntel:SetClusterAnalysis(true,true)`
|
-- `RedIntel:SetClusterAnalysis(true,true)`
|
||||||
-- `RedIntel:SetVerbosity(2)`
|
-- `RedIntel:SetVerbosity(2)`
|
||||||
-- `RedIntel:Start()`
|
-- `RedIntel:__Start(2)`
|
||||||
--
|
--
|
||||||
-- ## Hook into new contacts found
|
-- ## Hook into new contacts found
|
||||||
--
|
--
|
||||||
-- `function RedIntel:OnAfterNewContact(From, Event, To, Contact)`
|
-- `function RedIntel:OnAfterNewContact(From, Event, To, Contact)`
|
||||||
-- `local text = string.format("NEW contact %s detected by %s", Contact.groupname, Contact.recce or "unknown")`
|
-- `local text = string.format("NEW contact %s detected by %s", Contact.groupname, Contact.recce or "unknown")`
|
||||||
-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()`
|
-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()`
|
||||||
-- `end`
|
-- `end`
|
||||||
--
|
--
|
||||||
-- ## And/or new clusters found
|
-- ## And/or new clusters found
|
||||||
--
|
--
|
||||||
-- `function RedIntel:OnAfterNewCluster(From, Event, To, Contact, Cluster)`
|
-- `function RedIntel:OnAfterNewCluster(From, Event, To, Contact, Cluster)`
|
||||||
-- `local text = string.format("NEW cluster %d size %d with contact %s", Cluster.index, Cluster.size, Contact.groupname)`
|
-- `local text = string.format("NEW cluster %d size %d with contact %s", Cluster.index, Cluster.size, Contact.groupname)`
|
||||||
-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()`
|
-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()`
|
||||||
-- `end`
|
-- `end`
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- @field #INTEL
|
-- @field #INTEL
|
||||||
@ -116,7 +116,7 @@ INTEL = {
|
|||||||
-- @field #number speed Last known speed in m/s.
|
-- @field #number speed Last known speed in m/s.
|
||||||
-- @field #boolean isship
|
-- @field #boolean isship
|
||||||
-- @field #boolean ishelo
|
-- @field #boolean ishelo
|
||||||
-- @field #boolean isgrund
|
-- @field #boolean isground
|
||||||
-- @field Ops.Auftrag#AUFTRAG mission The current Auftrag attached to this contact
|
-- @field Ops.Auftrag#AUFTRAG mission The current Auftrag attached to this contact
|
||||||
-- @field #string recce The name of the recce unit that detected this contact
|
-- @field #string recce The name of the recce unit that detected this contact
|
||||||
|
|
||||||
@ -135,13 +135,13 @@ INTEL = {
|
|||||||
|
|
||||||
--- INTEL class version.
|
--- INTEL class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
INTEL.version="0.2.2"
|
INTEL.version="0.2.6"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Filter detection methods.
|
-- DONE: Filter detection methods.
|
||||||
-- TODO: process detected set asynchroniously for better performance.
|
-- TODO: process detected set asynchroniously for better performance.
|
||||||
-- DONE: Accept zones.
|
-- DONE: Accept zones.
|
||||||
-- DONE: Reject zones.
|
-- DONE: Reject zones.
|
||||||
@ -203,7 +203,14 @@ function INTEL:New(DetectionSet, Coalition, Alias)
|
|||||||
self.alias="CIA"
|
self.alias="CIA"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.DetectVisual = true
|
||||||
|
self.DetectOptical = true
|
||||||
|
self.DetectRadar = true
|
||||||
|
self.DetectIRST = true
|
||||||
|
self.DetectRWR = true
|
||||||
|
self.DetectDLINK = true
|
||||||
|
|
||||||
-- Set some string id for output to DCS.log file.
|
-- Set some string id for output to DCS.log file.
|
||||||
self.lid=string.format("INTEL %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
self.lid=string.format("INTEL %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||||
@ -222,7 +229,8 @@ function INTEL:New(DetectionSet, Coalition, Alias)
|
|||||||
self:AddTransition("*", "LostContact", "*") -- Contact could not be detected any more.
|
self:AddTransition("*", "LostContact", "*") -- Contact could not be detected any more.
|
||||||
|
|
||||||
self:AddTransition("*", "NewCluster", "*") -- New cluster has been detected.
|
self:AddTransition("*", "NewCluster", "*") -- New cluster has been detected.
|
||||||
self:AddTransition("*", "LostCluster", "*") -- Cluster could not be detected any more.
|
self:AddTransition("*", "LostCluster", "*") -- Cluster could not be detected any more.
|
||||||
|
self:AddTransition("*", "Stop", "Stopped")
|
||||||
|
|
||||||
-- Defaults
|
-- Defaults
|
||||||
self:SetForgetTime()
|
self:SetForgetTime()
|
||||||
@ -475,6 +483,47 @@ function INTEL:SetClusterRadius(radius)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set detection types for this #INTEL - all default to true.
|
||||||
|
-- @param #INTEL self
|
||||||
|
-- @param #boolean DetectVisual Visual detection
|
||||||
|
-- @param #boolean DetectOptical Optical detection
|
||||||
|
-- @param #boolean DetectRadar Radar detection
|
||||||
|
-- @param #boolean DetectIRST IRST detection
|
||||||
|
-- @param #boolean DetectRWR RWR detection
|
||||||
|
-- @param #boolean DetectDLINK Data link detection
|
||||||
|
-- @return self
|
||||||
|
function INTEL:SetDetectionTypes(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK)
|
||||||
|
self.DetectVisual = DetectVisual and true
|
||||||
|
self.DetectOptical = DetectOptical and true
|
||||||
|
self.DetectRadar = DetectRadar and true
|
||||||
|
self.DetectIRST = DetectIRST and true
|
||||||
|
self.DetectRWR = DetectRWR and true
|
||||||
|
self.DetectDLINK = DetectDLINK and true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get table of #INTEL.Contact objects
|
||||||
|
-- @param #INTEL self
|
||||||
|
-- @return #table Contacts or nil if not running
|
||||||
|
function INTEL:GetContactTable()
|
||||||
|
if self:Is("Running") then
|
||||||
|
return self.Contacts
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get table of #INTEL.Cluster objects
|
||||||
|
-- @param #INTEL self
|
||||||
|
-- @return #table Clusters or nil if not running
|
||||||
|
function INTEL:GetClusterTable()
|
||||||
|
if self:Is("Running") and self.clusteranalysis then
|
||||||
|
return self.Clusters
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Start & Status
|
-- Start & Status
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -530,7 +579,7 @@ function INTEL:onafterStatus(From, Event, To)
|
|||||||
text=text..string.format("\n- %s (%s): %s, units=%d, T=%d sec", contact.categoryname, contact.attribute, contact.groupname, contact.group:CountAliveUnits(), dT)
|
text=text..string.format("\n- %s (%s): %s, units=%d, T=%d sec", contact.categoryname, contact.attribute, contact.groupname, contact.group:CountAliveUnits(), dT)
|
||||||
if contact.mission then
|
if contact.mission then
|
||||||
local mission=contact.mission --Ops.Auftrag#AUFTRAG
|
local mission=contact.mission --Ops.Auftrag#AUFTRAG
|
||||||
text=text..string.format(" mission name=%s type=%s target=%s", mission.name, mission.type, mission:GetTargetName() or "unkown")
|
text=text..string.format(" mission name=%s type=%s target=%s", mission.name, mission.type, mission:GetTargetName() or "unknown")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
@ -558,14 +607,13 @@ function INTEL:UpdateIntel()
|
|||||||
local recce=_recce --Wrapper.Unit#UNIT
|
local recce=_recce --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Get detected units.
|
-- Get detected units.
|
||||||
self:GetDetectedUnits(recce, DetectedUnits, RecceDetecting)
|
self:GetDetectedUnits(recce, DetectedUnits, RecceDetecting, self.DetectVisual, self.DetectOptical, self.DetectRadar, self.DetectIRST, self.DetectRWR, self.DetectDLINK)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Filter detection methods?
|
|
||||||
local remove={}
|
local remove={}
|
||||||
for unitname,_unit in pairs(DetectedUnits) do
|
for unitname,_unit in pairs(DetectedUnits) do
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
@ -700,7 +748,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, RecceDetecting)
|
|||||||
item.velocity=group:GetVelocityVec3()
|
item.velocity=group:GetVelocityVec3()
|
||||||
item.speed=group:GetVelocityMPS()
|
item.speed=group:GetVelocityMPS()
|
||||||
item.recce=RecceDetecting[groupname]
|
item.recce=RecceDetecting[groupname]
|
||||||
self:T(string.format("%s group detect by %s/%s", groupname, RecceDetecting[groupname] or "unknonw", item.recce or "unknown"))
|
self:T(string.format("%s group detect by %s/%s", groupname, RecceDetecting[groupname] or "unknown", item.recce or "unknown"))
|
||||||
-- Add contact to table.
|
-- Add contact to table.
|
||||||
self:AddContact(item)
|
self:AddContact(item)
|
||||||
|
|
||||||
@ -728,7 +776,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, RecceDetecting)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return the detected target groups of the controllable as a @{SET_GROUP}.
|
--- (Internal) Return the detected target groups of the controllable as a @{SET_GROUP}.
|
||||||
-- The optional parametes specify the detection methods that can be applied.
|
-- The optional parametes specify the detection methods that can be applied.
|
||||||
-- If no detection method is given, the detection will use all the available methods by default.
|
-- If no detection method is given, the detection will use all the available methods by default.
|
||||||
-- @param #INTEL self
|
-- @param #INTEL self
|
||||||
@ -815,7 +863,7 @@ function INTEL:onafterLostCluster(From, Event, To, Cluster, Mission)
|
|||||||
local text = self.lid..string.format("LOST cluster %d", Cluster.index)
|
local text = self.lid..string.format("LOST cluster %d", Cluster.index)
|
||||||
if Mission then
|
if Mission then
|
||||||
local mission=Mission --Ops.Auftrag#AUFTRAG
|
local mission=Mission --Ops.Auftrag#AUFTRAG
|
||||||
text=text..string.format(" mission name=%s type=%s target=%s", mission.name, mission.type, mission:GetTargetName() or "unkown")
|
text=text..string.format(" mission name=%s type=%s target=%s", mission.name, mission.type, mission:GetTargetName() or "unknown")
|
||||||
end
|
end
|
||||||
self:T(text)
|
self:T(text)
|
||||||
end
|
end
|
||||||
@ -1391,3 +1439,306 @@ end
|
|||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
-- Start INTEL_DLINK
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- **Ops_DLink** - Support for Office of Military Intelligence.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Overcome limitations of (non-available) datalinks between ground radars
|
||||||
|
-- * Detect and track contacts consistently across INTEL instances
|
||||||
|
-- * Use FSM events to link functionality into your scripts
|
||||||
|
-- * Easy setup
|
||||||
|
--
|
||||||
|
--- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
|
||||||
|
--- INTEL_DLINK class.
|
||||||
|
-- @type INTEL_DLINK
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #number verbose Make the logging verbose.
|
||||||
|
-- @field #string alias Alias name for logging.
|
||||||
|
-- @field #number cachetime Number of seconds to keep an object.
|
||||||
|
-- @field #number interval Number of seconds between collection runs.
|
||||||
|
-- @field #table contacts Table of Ops.Intelligence#INTEL.Contact contacts.
|
||||||
|
-- @field #table clusters Table of Ops.Intelligence#INTEL.Cluster clusters.
|
||||||
|
-- @field #table contactcoords Table of contacts' Core.Point#COORDINATE objects.
|
||||||
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
--- INTEL_DLINK data aggregator
|
||||||
|
-- @field #INTEL_DLINK
|
||||||
|
INTEL_DLINK = {
|
||||||
|
ClassName = "INTEL_DLINK",
|
||||||
|
verbose = 0,
|
||||||
|
lid = nil,
|
||||||
|
alias = nil,
|
||||||
|
cachetime = 300,
|
||||||
|
interval = 20,
|
||||||
|
contacts = {},
|
||||||
|
clusters = {},
|
||||||
|
contactcoords = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Version string
|
||||||
|
-- @field #string version
|
||||||
|
INTEL_DLINK.version = "0.0.1"
|
||||||
|
|
||||||
|
--- Function to instantiate a new object
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #table Intels Table of Ops.Intelligence#INTEL objects.
|
||||||
|
-- @param #string Alias (optional) Name of this instance. Default "SPECTRE"
|
||||||
|
-- @param #number Interval (optional) When to query #INTEL objects for detected items (default 20 seconds).
|
||||||
|
-- @param #number Cachetime (optional) How long to cache detected items (default 300 seconds).
|
||||||
|
-- @usage Use #INTEL_DLINK if you want to merge data from a number of #INTEL objects into one. This might be useful to simulate a
|
||||||
|
-- Data Link, e.g. for Russian-tech based EWR, realising a Star Topology @{https://en.wikipedia.org/wiki/Network_topology#Star}
|
||||||
|
-- in a basic setup. It will collect the contacts and clusters from the #INTEL objects.
|
||||||
|
-- Contact duplicates are removed. Clusters might contain duplicates (Might fix that later, WIP).
|
||||||
|
--
|
||||||
|
-- Basic setup:
|
||||||
|
-- local datalink = INTEL_DLINK:New({myintel1,myintel2}), "FSB", 20, 300)
|
||||||
|
-- datalink:__Start(2)
|
||||||
|
--
|
||||||
|
-- Add an Intel while running:
|
||||||
|
-- datalink:AddIntel(myintel3)
|
||||||
|
--
|
||||||
|
-- Gather the data:
|
||||||
|
-- datalink:GetContactTable() -- #table of #INTEL.Contact contacts.
|
||||||
|
-- datalink:GetClusterTable() -- #table of #INTEL.Cluster clusters.
|
||||||
|
-- datalink:GetDetectedItemCoordinates() -- #table of contact coordinates, to be compatible with @{Functional.Detection#DETECTION}.
|
||||||
|
--
|
||||||
|
-- Gather data with the event function:
|
||||||
|
-- function datalink:OnAfterCollected(From, Event, To, Contacts, Clusters)
|
||||||
|
-- ... <your code here> ...
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
function INTEL_DLINK:New(Intels, Alias, Interval, Cachetime)
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, FSM:New()) -- #INTEL
|
||||||
|
|
||||||
|
self.intels = Intels or {}
|
||||||
|
self.contacts = {}
|
||||||
|
self.clusters = {}
|
||||||
|
self.contactcoords = {}
|
||||||
|
|
||||||
|
-- Set alias.
|
||||||
|
if Alias then
|
||||||
|
self.alias=tostring(Alias)
|
||||||
|
else
|
||||||
|
self.alias="SPECTRE"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cache time
|
||||||
|
self.cachetime = Cachetime or 300
|
||||||
|
|
||||||
|
-- Interval
|
||||||
|
self.interval = Interval or 20
|
||||||
|
|
||||||
|
-- Set some string id for output to DCS.log file.
|
||||||
|
self.lid=string.format("INTEL_DLINK %s | ", self.alias)
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Stopped")
|
||||||
|
|
||||||
|
-- Add FSM transitions.
|
||||||
|
-- From State --> Event --> To State
|
||||||
|
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
||||||
|
self:AddTransition("*", "Collect", "*") -- Collect data.
|
||||||
|
self:AddTransition("*", "Collected", "*") -- Collection of data done.
|
||||||
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
-- Pseudo Functions
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
--- Triggers the FSM event "Start". Starts the INTEL_DLINK.
|
||||||
|
-- @function [parent=#INTEL_DLINK] Start
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Start" after a delay. Starts the INTEL_DLINK.
|
||||||
|
-- @function [parent=#INTEL_DLINK] __Start
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Stop". Stops the INTEL_DLINK.
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Stop" after a delay. Stops the INTEL_DLINK.
|
||||||
|
-- @function [parent=#INTEL_DLINK] __Stop
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Collect". Used internally to collect all data.
|
||||||
|
-- @function [parent=#INTEL_DLINK] Collect
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Collect" after a delay.
|
||||||
|
-- @function [parent=#INTEL_DLINK] __Status
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- On After "Collected" event. Data tables have been refreshed.
|
||||||
|
-- @function [parent=#INTEL_DLINK] OnAfterCollected
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #table Contacts Table of #INTEL.Contact Contacts.
|
||||||
|
-- @param #table Clusters Table of #INTEL.Cluster Clusters.
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
-- Helper & User Functions
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function to add an #INTEL object to the aggregator
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param Ops.Intelligence#INTEL Intel the #INTEL object to add
|
||||||
|
-- @return #INTEL_DLINK self
|
||||||
|
function INTEL_DLINK:AddIntel(Intel)
|
||||||
|
self:T(self.lid .. "AddIntel")
|
||||||
|
if Intel then
|
||||||
|
table.insert(self.intels,Intel)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
-- FSM Functions
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function to start the work.
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event triggering this call
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @return #INTEL_DLINK self
|
||||||
|
function INTEL_DLINK:onafterStart(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
local text = string.format("Version %s started.", self.version)
|
||||||
|
self:I(self.lid .. text)
|
||||||
|
self:__Collect(-math.random(1,10))
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to collect data from the various #INTEL
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event triggering this call
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @return #INTEL_DLINK self
|
||||||
|
function INTEL_DLINK:onbeforeCollect(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
-- run through our #INTEL objects and gather the contacts tables
|
||||||
|
self:T("Contacts Data Gathering")
|
||||||
|
local newcontacts = {}
|
||||||
|
local intels = self.intels -- #table
|
||||||
|
for _,_intel in pairs (intels) do
|
||||||
|
_intel = _intel -- #INTEL
|
||||||
|
if _intel:Is("Running") then
|
||||||
|
local ctable = _intel:GetContactTable() or {} -- #INTEL.Contact
|
||||||
|
for _,_contact in pairs (ctable) do
|
||||||
|
local _ID = string.format("%s-%d",_contact.groupname, _contact.Tdetected)
|
||||||
|
self:T(string.format("Adding %s",_ID))
|
||||||
|
newcontacts[_ID] = _contact
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- clean up for stale contacts and dupes
|
||||||
|
self:T("Cleanup")
|
||||||
|
local contacttable = {}
|
||||||
|
local coordtable = {}
|
||||||
|
local TNow = timer.getAbsTime()
|
||||||
|
local Tcache = self.cachetime
|
||||||
|
for _ind, _contact in pairs(newcontacts) do -- #string, #INTEL.Contact
|
||||||
|
if TNow - _contact.Tdetected < Tcache then
|
||||||
|
if (not contacttable[_contact.groupname]) or (contacttable[_contact.groupname] and contacttable[_contact.groupname].Tdetected < _contact.Tdetected) then
|
||||||
|
self:T(string.format("Adding %s",_contact.groupname))
|
||||||
|
contacttable[_contact.groupname] = _contact
|
||||||
|
table.insert(coordtable,_contact.position)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- run through our #INTEL objects and gather the clusters tables
|
||||||
|
self:T("Clusters Data Gathering")
|
||||||
|
local newclusters = {}
|
||||||
|
local intels = self.intels -- #table
|
||||||
|
for _,_intel in pairs (intels) do
|
||||||
|
_intel = _intel -- #INTEL
|
||||||
|
if _intel:Is("Running") then
|
||||||
|
local ctable = _intel:GetClusterTable() or {} -- #INTEL.Cluster
|
||||||
|
for _,_cluster in pairs (ctable) do
|
||||||
|
local _ID = string.format("%s-%d", _intel.alias, _cluster.index)
|
||||||
|
self:T(string.format("Adding %s",_ID))
|
||||||
|
table.insert(newclusters,_cluster)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- update self tables
|
||||||
|
self.contacts = contacttable
|
||||||
|
self.contactcoords = coordtable
|
||||||
|
self.clusters = newclusters
|
||||||
|
self:__Collected(1, contacttable, newclusters) -- make table available via FSM Event
|
||||||
|
-- schedule next round
|
||||||
|
local interv = self.interval * -1
|
||||||
|
self:__Collect(interv)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function called after collection is done
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event triggering this call
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @param #table Contacts The table of collected #INTEL.Contact contacts
|
||||||
|
-- @param #table Clusters The table of collected #INTEL.Cluster clusters
|
||||||
|
-- @return #INTEL_DLINK self
|
||||||
|
function INTEL_DLINK:onbeforeCollected(From, Event, To, Contacts, Clusters)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to stop
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @param #string From The From state
|
||||||
|
-- @param #string Event The Event triggering this call
|
||||||
|
-- @param #string To The To state
|
||||||
|
-- @return #INTEL_DLINK self
|
||||||
|
function INTEL_DLINK:onafterStop(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
local text = string.format("Version %s stopped.", self.version)
|
||||||
|
self:I(self.lid .. text)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to query the detected contacts
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @return #table Table of #INTEL.Contact contacts
|
||||||
|
function INTEL_DLINK:GetContactTable()
|
||||||
|
self:T(self.lid .. "GetContactTable")
|
||||||
|
return self.contacts
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to query the detected clusters
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @return #table Table of #INTEL.Cluster clusters
|
||||||
|
function INTEL_DLINK:GetClusterTable()
|
||||||
|
self:T(self.lid .. "GetClusterTable")
|
||||||
|
return self.clusters
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to query the detected contact coordinates
|
||||||
|
-- @param #INTEL_DLINK self
|
||||||
|
-- @return #table Table of the contacts' Core.Point#COORDINATE objects.
|
||||||
|
function INTEL_DLINK:GetDetectedItemCoordinates()
|
||||||
|
self:T(self.lid .. "GetDetectedItemCoordinates")
|
||||||
|
return self.contactcoords
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
-- End INTEL_DLINK
|
||||||
|
----------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@ -1668,4 +1668,149 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
|||||||
end -- nil
|
end -- nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid FM frequencies in mHz for radio beacons (FM).
|
||||||
|
-- @return #table Table of frequencies.
|
||||||
|
function UTILS.GenerateFMFrequencies()
|
||||||
|
local FreeFMFrequencies = {}
|
||||||
|
for _first = 3, 7 do
|
||||||
|
for _second = 0, 5 do
|
||||||
|
for _third = 0, 9 do
|
||||||
|
local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit
|
||||||
|
table.insert(FreeFMFrequencies, _frequency)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return FreeFMFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid VHF frequencies in kHz for radio beacons (FM).
|
||||||
|
-- @return #table VHFrequencies
|
||||||
|
function UTILS.GenerateVHFrequencies()
|
||||||
|
|
||||||
|
-- known and sorted map-wise NDBs in kHz
|
||||||
|
local _skipFrequencies = {
|
||||||
|
214,274,291.5,295,297.5,
|
||||||
|
300.5,304,307,309.5,311,312,312.5,316,
|
||||||
|
320,324,328,329,330,332,336,337,
|
||||||
|
342,343,348,351,352,353,358,
|
||||||
|
363,365,368,372.5,374,
|
||||||
|
380,381,384,385,389,395,396,
|
||||||
|
414,420,430,432,435,440,450,455,462,470,485,
|
||||||
|
507,515,520,525,528,540,550,560,570,577,580,
|
||||||
|
602,625,641,662,670,680,682,690,
|
||||||
|
705,720,722,730,735,740,745,750,770,795,
|
||||||
|
822,830,862,866,
|
||||||
|
905,907,920,935,942,950,995,
|
||||||
|
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
||||||
|
}
|
||||||
|
|
||||||
|
local FreeVHFFrequencies = {}
|
||||||
|
|
||||||
|
-- first range
|
||||||
|
local _start = 200000
|
||||||
|
while _start < 400000 do
|
||||||
|
|
||||||
|
-- skip existing NDB frequencies#
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 10000
|
||||||
|
end
|
||||||
|
|
||||||
|
-- second range
|
||||||
|
_start = 400000
|
||||||
|
while _start < 850000 do
|
||||||
|
-- skip existing NDB frequencies
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 10000
|
||||||
|
end
|
||||||
|
|
||||||
|
-- third range
|
||||||
|
_start = 850000
|
||||||
|
while _start <= 999000 do -- adjusted for Gazelle
|
||||||
|
-- skip existing NDB frequencies
|
||||||
|
local _found = false
|
||||||
|
for _, value in pairs(_skipFrequencies) do
|
||||||
|
if value * 1000 == _start then
|
||||||
|
_found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if _found == false then
|
||||||
|
table.insert(FreeVHFFrequencies, _start)
|
||||||
|
end
|
||||||
|
_start = _start + 50000
|
||||||
|
end
|
||||||
|
|
||||||
|
return FreeVHFFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid UHF Frequencies in mHz (AM).
|
||||||
|
-- @return #table UHF Frequencies
|
||||||
|
function UTILS.GenerateUHFrequencies()
|
||||||
|
|
||||||
|
local FreeUHFFrequencies = {}
|
||||||
|
local _start = 220000000
|
||||||
|
|
||||||
|
while _start < 399000000 do
|
||||||
|
table.insert(FreeUHFFrequencies, _start)
|
||||||
|
_start = _start + 500000
|
||||||
|
end
|
||||||
|
|
||||||
|
return FreeUHFFrequencies
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to generate valid laser codes for JTAC.
|
||||||
|
-- @return #table Laser Codes.
|
||||||
|
function UTILS.GenerateLaserCodes()
|
||||||
|
local jtacGeneratedLaserCodes = {}
|
||||||
|
|
||||||
|
-- helper function
|
||||||
|
local function ContainsDigit(_number, _numberToFind)
|
||||||
|
local _thisNumber = _number
|
||||||
|
local _thisDigit = 0
|
||||||
|
while _thisNumber ~= 0 do
|
||||||
|
_thisDigit = _thisNumber % 10
|
||||||
|
_thisNumber = math.floor(_thisNumber / 10)
|
||||||
|
if _thisDigit == _numberToFind then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate list of laser codes
|
||||||
|
local _code = 1111
|
||||||
|
local _count = 1
|
||||||
|
while _code < 1777 and _count < 30 do
|
||||||
|
while true do
|
||||||
|
_code = _code + 1
|
||||||
|
if not self:_ContainsDigit(_code, 8)
|
||||||
|
and not ContainsDigit(_code, 9)
|
||||||
|
and not ContainsDigit(_code, 0) then
|
||||||
|
table.insert(jtacGeneratedLaserCodes, _code)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_count = _count + 1
|
||||||
|
end
|
||||||
|
return jtacGeneratedLaserCodes
|
||||||
end
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user