Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank 2021-02-21 11:54:06 +01:00
commit a63ee3fa8b
13 changed files with 674 additions and 59 deletions

7
.gitignore vendored
View File

@ -26,6 +26,13 @@ local.properties
.buildpath
#####################
## Visual Studio Code
#####################
*.code-workspace
.vscode/
#################
## Visual Studio
#################

View File

@ -880,8 +880,8 @@ do -- SET_GROUP
--
-- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s).
-- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies).
-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies).
-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s).
-- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the groups belonging to the country(ies).
-- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*.
-- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set!
--
-- For the Category Filter, extra methods have been added:
@ -1241,10 +1241,10 @@ do -- SET_GROUP
end
--- Builds a set of groups of defined GROUP prefixes.
-- All the groups starting with the given prefixes will be included within the set.
--- Builds a set of groups that contain the given string in their group name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string.
-- @param #SET_GROUP self
-- @param #string Prefixes The prefix of which the group name starts with.
-- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings.
-- @return #SET_GROUP self
function SET_GROUP:FilterPrefixes( Prefixes )
if not self.Filter.GroupPrefixes then
@ -1843,7 +1843,7 @@ do -- SET_UNIT
-- * @{#SET_UNIT.FilterCategories}: Builds the SET_UNIT with the units belonging to the category(ies).
-- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s).
-- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies).
-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s).
-- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units sharing the same string(s) in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
-- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
--
-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using:
@ -2105,10 +2105,10 @@ do -- SET_UNIT
end
--- Builds a set of units of defined unit prefixes.
-- All the units starting with the given prefixes will be included within the set.
--- Builds a set of UNITs that contain a given string in their unit name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all units that **contain** the string.
-- @param #SET_UNIT self
-- @param #string Prefixes The prefix of which the unit name starts with.
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit name. Can also be passed as a `#table` of strings.
-- @return #SET_UNIT self
function SET_UNIT:FilterPrefixes( Prefixes )
if not self.Filter.UnitPrefixes then
@ -2963,7 +2963,7 @@ do -- SET_STATIC
-- * @{#SET_STATIC.FilterCategories}: Builds the SET_STATIC with the units belonging to the category(ies).
-- * @{#SET_STATIC.FilterTypes}: Builds the SET_STATIC with the units belonging to the unit type(s).
-- * @{#SET_STATIC.FilterCountries}: Builds the SET_STATIC with the units belonging to the country(ies).
-- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units starting with the same prefix string(s).
-- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **ATTENTION** bad naming convention as this *does not** only filter *prefixes*.
--
-- Once the filter criteria have been set for the SET_STATIC, you can start filtering using:
--
@ -3176,10 +3176,10 @@ do -- SET_STATIC
end
--- Builds a set of units of defined unit prefixes.
-- All the units starting with the given prefixes will be included within the set.
--- Builds a set of STATICs that contain the given string in their name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all statics that **contain** the string.
-- @param #SET_STATIC self
-- @param #string Prefixes The prefix of which the unit name starts with.
-- @param #string Prefixes The string pattern(s) that need to be contained in the static name. Can also be passed as a `#table` of strings.
-- @return #SET_STATIC self
function SET_STATIC:FilterPrefixes( Prefixes )
if not self.Filter.StaticPrefixes then
@ -3675,7 +3675,7 @@ do -- SET_CLIENT
-- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies).
-- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s).
-- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies).
-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s).
-- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients containing the same string(s) in their unit/pilot name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
-- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
--
-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using:
@ -3858,10 +3858,10 @@ do -- SET_CLIENT
end
--- Builds a set of clients of defined client prefixes.
-- All the clients starting with the given prefixes will be included within the set.
--- Builds a set of CLIENTs that contain the given string in their unit/pilot name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all clients that **contain** the string.
-- @param #SET_CLIENT self
-- @param #string Prefixes The prefix of which the client name starts with.
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
-- @return #SET_CLIENT self
function SET_CLIENT:FilterPrefixes( Prefixes )
if not self.Filter.ClientPrefixes then
@ -4114,7 +4114,7 @@ do -- SET_PLAYER
-- * @{#SET_PLAYER.FilterCategories}: Builds the SET_PLAYER with the clients belonging to the category(ies).
-- * @{#SET_PLAYER.FilterTypes}: Builds the SET_PLAYER with the clients belonging to the client type(s).
-- * @{#SET_PLAYER.FilterCountries}: Builds the SET_PLAYER with the clients belonging to the country(ies).
-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients starting with the same prefix string(s).
-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients sharing the same string(s) in their unit/pilot name. **ATTENTION** Bad naming convention as this *does not* only filter prefixes.
--
-- Once the filter criteria have been set for the SET_PLAYER, you can start filtering using:
--
@ -4293,10 +4293,10 @@ do -- SET_PLAYER
end
--- Builds a set of clients of defined client prefixes.
-- All the clients starting with the given prefixes will be included within the set.
--- Builds a set of PLAYERs that contain the given string in their unit/pilot name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all player clients that **contain** the string.
-- @param #SET_PLAYER self
-- @param #string Prefixes The prefix of which the client name starts with.
-- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings.
-- @return #SET_PLAYER self
function SET_PLAYER:FilterPrefixes( Prefixes )
if not self.Filter.ClientPrefixes then
@ -4874,7 +4874,7 @@ do -- SET_CARGO
-- Filter criteria are defined by:
--
-- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s).
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s).
-- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the same string(s). **ATTENTION** Bad naming convention as this *does not* only filter *prefixes*.
-- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s).
-- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies).
--
@ -5036,10 +5036,10 @@ do -- SET_CARGO
end
--- (R2.1) Builds a set of cargos of defined cargo prefixes.
-- All the cargos starting with the given prefixes will be included within the set.
--- Builds a set of CARGOs that contain a given string in their name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all cargos that **contain** the string.
-- @param #SET_CARGO self
-- @param #string Prefixes The prefix of which the cargo name starts with.
-- @param #string Prefixes The string pattern(s) that need to be in the cargo name. Can also be passed as a `#table` of strings.
-- @return #SET_CARGO self
function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1
if not self.Filter.CargoPrefixes then
@ -5315,7 +5315,7 @@ do -- SET_ZONE
-- You can set filter criteria to build the collection of zones in SET_ZONE.
-- Filter criteria are defined by:
--
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix.
-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
--
-- Once the filter criteria have been set for the SET_ZONE, you can start filtering using:
--
@ -5450,10 +5450,10 @@ do -- SET_ZONE
--- Builds a set of zones of defined zone prefixes.
-- All the zones starting with the given prefixes will be included within the set.
--- Builds a set of ZONEs that contain the given string in their name.
-- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string.
-- @param #SET_ZONE self
-- @param #string Prefixes The prefix of which the zone name starts with.
-- @param #string Prefixes The string pattern(s) that need to be contained in the zone name. Can also be passed as a `#table` of strings.
-- @return #SET_ZONE self
function SET_ZONE:FilterPrefixes( Prefixes )
if not self.Filter.Prefixes then
@ -5652,7 +5652,7 @@ do -- SET_ZONE_GOAL
-- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL.
-- Filter criteria are defined by:
--
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern of prefix.
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
--
-- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using:
--
@ -5768,10 +5768,10 @@ do -- SET_ZONE_GOAL
--- Builds a set of zones of defined zone prefixes.
-- All the zones starting with the given prefixes will be included within the set.
--- Builds a set of ZONE_GOALs that contain the given string in their name.
-- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string.
-- @param #SET_ZONE_GOAL self
-- @param #string Prefixes The prefix of which the zone name starts with.
-- @param #string Prefixes The string pattern(s) that needs to be contained in the zone name. Can also be passed as a `#table` of strings.
-- @return #SET_ZONE_GOAL self
function SET_ZONE_GOAL:FilterPrefixes( Prefixes )
if not self.Filter.Prefixes then

View File

@ -1001,7 +1001,13 @@ do -- DESIGNATE
local ID = self.Detection:GetDetectedItemID( DetectedItem )
local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup )
-- Use injected MenuName from TaskA2GDispatcher if using same Detection Object
if DetectedItem.DesignateMenuName then
MenuText = string.format( "(%3s) %s", Designating, DetectedItem.DesignateMenuName )
else
MenuText = string.format( "(%3s) %s", Designating, MenuText )
end
local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
-- Build the Lasing menu.

View File

@ -23,21 +23,21 @@
-- Date: Jan 2021
-------------------------------------------------------------------------
--- **MANTIS** class, extends @{#Core.Base#BASE}
-- @type MANTIS #MANTIS
--- **MANTIS** class, extends #Core.Base#BASE
-- @type MANTIS
-- @field #string Classname
-- @field #string name Name of this Mantis
-- @field #string SAM_Templates_Prefix Prefix to build the #GROUP_SET for SAM sites
-- @field @{#Core.Set#GROUP_SET} SAM_Group The SAM #GROUP_SET
-- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group
-- @field @{#Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET
-- @field @{#Core.Set#GROUP_SET} Adv_EWR_Group The EWR #GROUP_SET used for advanced mode
-- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites
-- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP
-- @field #string EWR_Templates_Prefix Prefix to build the #SET_GROUP for EWR group
-- @field Core.Set#SET_GROUP EWR_Group The EWR #SET_GROUP
-- @field #Core.Set#SET_GROUP Adv_EWR_Group The EWR #SET_GROUP used for advanced mode
-- @field #string HQ_Template_CC The ME name of the HQ object
-- @field @{#Wrapper.Group#GROUP} HQ_CC The #GROUP object of the HQ
-- @field Wrapper.Group#GROUP HQ_CC The #GROUP object of the HQ
-- @field #table SAM_Table Table of SAM sites
-- @field #string lid Prefix for logging
-- @field @{#Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object for EWR
-- @field @{#Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS
-- @field Functional.Detection#DETECTION_AREAS Detection The #DETECTION_AREAS object for EWR
-- @field Functional.Detection#DETECTION_AREAS AWACS_Detection The #DETECTION_AREAS object for AWACS
-- @field #boolean debug Switch on extra messages
-- @field #boolean verbose Switch on extra logging
-- @field #number checkradius Radius of the SAM sites
@ -51,7 +51,10 @@
-- @field #number adv_state Advanced mode state tracker
-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream
-- @field #number awacsrange Detection range of an optional Awacs unit
-- @extends @{#Core.Base#BASE}
-- @field Functional.Shorad#SHORAD Shorad SHORAD Object, if available
-- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled
-- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range
-- @extends Core.Base#BASE
--- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat
@ -163,7 +166,10 @@ MANTIS = {
AWACS_Prefix = "",
advAwacs = false,
verbose = false,
awacsrange = 250000
awacsrange = 250000,
Shorad = nil,
ShoradLink = false,
ShoradTime = 600,
}
-----------------------------------------------------------------------
@ -230,6 +236,9 @@ do
self.Adv_EWR_Group = nil
self.AWACS_Prefix = awacs or nil
self.awacsrange = 250000 --TODO: 250km, User Function to change
self.Shorad = nil
self.ShoradLink = false
self.ShoradTime = 600
if type(awacs) == "string" then
self.advAwacs = true
else
@ -373,7 +382,7 @@ do
--- Function to set the HQ object for further use
-- @param #MANTIS self
-- @param Wrapper.GROUP#GROUP The HQ #GROUP object to be set as HQ
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
function MANTIS:SetCommandCenter(group)
local group = group or nil
if group ~= nil then
@ -709,6 +718,27 @@ do
return self
end
--- Function to link up #MANTIS with a #SHORAD installation
-- @param #MANTIS self
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
-- @param #number Shoradtime Number of seconds #SHORAD stays active post wake-up
function MANTIS:AddShorad(Shorad,Shoradtime)
local Shorad = Shorad or nil
local ShoradTime = Shoradtime or 600
local ShoradLink = true
if Shorad:IsInstanceOf("SHORAD") then
self.ShoradLink = ShoradLink
self.Shorad = Shorad --#SHORAD
self.ShoradTime = Shoradtime -- #number
end
end
--- Function to unlink #MANTIS from a #SHORAD installation
-- @param #MANTIS self
function MANTIS:RemoveShorad()
self.ShoradLink = false
end
-----------------------------------------------------------------------
-- MANTIS main functions
-----------------------------------------------------------------------
@ -743,8 +773,15 @@ do
if samgroup:IsAlive() then
-- switch off SAM
samgroup:OptionAlarmStateRed()
--samgroup:OptionROEWeaponFree()
--samgroup:SetAIOn()
-- link in to SHORAD if available
-- TODO Test integration fully
if self.ShoradLink then
local Shorad = self.Shorad
local radius = self.checkradius
local ontime = self.ShoradTime
Shorad:WakeUpShorad(name, radius, ontime)
end
-- debug output
local text = string.format("SAM %s switched to alarm state RED!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then env.info(self.lid..text) end

View File

@ -17,7 +17,7 @@
--
-- ### Authors: **FlightControl**, **applevangelist**
--
-- Last Update: Dec 2020
-- Last Update: Feb 2021
--
-- ===
--
@ -51,9 +51,44 @@ SEAD = {
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
}
-- TODO Complete list?
--- Missile enumerators
-- @field 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_45"] = "AGM_45",
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",
["X_25"] = "X_25",
["X_31"] = "X_31",
["Kh25"] = "Kh25",
}
--- 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...
-- Chances are big that the missile will miss.
-- @param #SEAD self
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
-- @return SEAD
-- @usage
@ -74,7 +109,7 @@ function SEAD:New( SEADGroupPrefixes )
end
self:HandleEvent( EVENTS.Shot )
self:I("*** SEAD - Started Version 0.2.2")
self:I("*** SEAD - Started Version 0.2.5")
return self
end
@ -112,7 +147,20 @@ function SEAD:SetEngagementRange(range)
return self
end
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
--- Check if a known HARM was fired
-- @param #SEAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SEAD:_CheckHarms(WeaponName)
self:F( { WeaponName } )
local hit = false
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end
end
return hit
end
--- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @see SEAD
-- @param #SEAD
-- @param Core.Event#EVENTDATA EventData
@ -127,7 +175,7 @@ function SEAD:OnEventShot( EventData )
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
self:T({ SEADWeapon })
--check for SEAD missiles
--[[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
@ -155,12 +203,13 @@ function SEAD:OnEventShot( EventData )
SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired
or
SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
then
--]]
if self:_CheckHarms(SEADWeaponName) then
local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetMimname = Unit.getName(_targetMim) -- Unit name
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) --targeted grouo
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) --targeted group
local _targetMimgroupName = _targetMimgroup:getName() -- group name
local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill
self:T( self.SEADGroupPrefixes )

View File

@ -0,0 +1,460 @@
--- **Functional** -- Short Range Air Defense System
--
-- ===
--
-- **SHORAD** - Short Range Air Defense System
-- Controls a network of short range air/missile defense groups.
--
-- ===
--
-- ## Missions:
--
-- ### [SHORAD - Short Range Air Defense](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SRD%20-%20SHORAD%20Defense)
--
-- ===
--
-- ### Author : **applevangelist **
--
-- @module Functional.Shorad
-- @image Functional.Shorad.jpg
--
-- Date: Feb 2021
-------------------------------------------------------------------------
--- **SHORAD** class, extends Core.Base#BASE
-- @type SHORAD
-- @field #string ClassName
-- @field #string name Name of this Shorad
-- @field #boolean debug Set the debug state
-- @field #string Prefixes String to be used to build the @{#Core.Set#SET_GROUP}
-- @field #number Radius Shorad defense radius in meters
-- @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 #string Coalition The coalition of this Shorad
-- @field #number ActiveTimer How long a Shorad stays active after wake-up in seconds
-- @field #table ActiveGroups Table for the timer function
-- @field #string lid The log ID for the dcs.log
-- @field #boolean DefendHarms Default true, intercept incoming HARMS
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
-- @field #number DefenseLowProb Default 70, minimum detection limit
-- @field #number DefenseHighProb Default 90, maximim detection limit
-- @extends Core.Base#BASE
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
--
-- Simple Class for a more intelligent Short Range Air Defense System
--
-- #SHORAD
-- 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.
-- Easily integrated with @{Functional.Mantis#MANTIS} to complete the defensive system setup.
--
-- ## Usage
--
-- Set up a #SET_GROUP for the SAM sites to be protected:
--
-- `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%.
-- 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:
--
-- * 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")`
--
-- ## Customize options
--
-- * SHORAD:SwitchDebug(debug)
-- * SHORAD:SwitchHARMDefense(onoff)
-- * SHORAD:SwitchAGMDefense(onoff)
-- * SHORAD:SetDefenseLimits(low,high)
-- * SHORAD:SetActiveTimer(seconds)
-- * SHORAD:SetDefenseRadius(meters)
--
-- @field #SHORAD
SHORAD = {
ClassName = "SHORAD",
name = "MyShorad",
debug = false,
Prefixes = "",
Radius = 20000,
Groupset = nil,
Samset = nil,
Coalition = nil,
ActiveTimer = 600, --stay on 10 mins
ActiveGroups = {},
lid = "",
DefendHarms = true,
DefendMavs = true,
DefenseLowProb = 70,
DefenseHighProb = 90,
}
-----------------------------------------------------------------------
-- SHORAD System
-----------------------------------------------------------------------
do
-- TODO Complete list?
--- Missile enumerators
-- @field 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_45"] = "AGM_45",
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",
["X_25"] = "X_25",
["X_31"] = "X_31",
["Kh25"] = "Kh25",
}
--- TODO complete list?
-- @field Mavs
SHORAD.Mavs = {
["AGM"] = "AGM",
["C-701"] = "C-701",
["Kh25"] = "Kh25",
["Kh29"] = "Kh29",
["Kh31"] = "Kh31",
["Kh66"] = "Kh66",
}
--- Instantiates a new SHORAD object
-- @param #SHORAD self
-- @param #string Name Name of this SHORAD
-- @param #string ShoradPrefix Filter for the Shorad #SET_GROUP
-- @param Core.Set#SET_GROUP Samset The #SET_GROUP of SAM sites to defend
-- @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 #string Coalition Coalition, i.e. "blue", "red", or "neutral"
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition)
local self = BASE:Inherit( self, BASE:New() )
self:F({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
local GroupSet = SET_GROUP:New():FilterPrefixes(ShoradPrefix):FilterCoalitions(Coalition):FilterCategoryGround():FilterStart()
self.name = Name or "MyShorad"
self.Prefixes = ShoradPrefix or "SAM SHORAD"
self.Radius = Radius or 20000
self.Coalition = Coalition or "blue"
self.Samset = Samset or GroupSet
self.ActiveTimer = ActiveTimer or 600
self.ActiveGroups = {}
self.Groupset = GroupSet
self:HandleEvent( EVENTS.Shot )
self.DefendHarms = true
self.DefendMavs = true
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
self:I("*** SHORAD - Started Version 0.0.2")
-- Set the string id for output to DCS.log file.
self.lid=string.format("SHORAD %s | ", self.name)
self:_InitState()
return self
end
--- Initially set all groups to alarm state GREEN
-- @param #SHORAD self
function SHORAD:_InitState()
local table = {}
local set = self.Groupset
self:T({set = set})
local aliveset = set:GetAliveSet() --#table
for _,_group in pairs (aliveset) do
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
end
-- gather entropy
for i=1,10 do
math.random()
end
end
--- Switch debug state
-- @param #SHORAD self
-- @param #boolean debug Switch debug on (true) or off (false)
function SHORAD:SwitchDebug(debug)
self:F( { debug } )
local onoff = debug or false
if debug then
self.debug = true
--tracing
BASE:TraceOn()
BASE:TraceClass("SHORAD")
else
self.debug = false
BASE:TraceOff()
end
end
--- Switch defense for HARMs
-- @param #SHORAD self
-- @param #boolean onoff
function SHORAD:SwitchHARMDefense(onoff)
self:F( { onoff } )
local onoff = onoff or true
self.DefendHarms = onoff
end
--- Switch defense for AGMs
-- @param #SHORAD self
-- @param #boolean onoff
function SHORAD:SwitchAGMDefense(onoff)
self:F( { onoff } )
local onoff = onoff or true
self.DefendMavs = onoff
end
--- Set defense probability limits
-- @param #SHORAD self
-- @param #number low Minimum detection limit, integer 1-100
-- @param #number high Maximum detection limit integer 1-100
function SHORAD:SetDefenseLimits(low,high)
self:F( { low, high } )
local low = low or 70
local high = high or 90
if (low < 0) or (low > 100) or (low > high) then
low = 70
end
if (high < 0) or (high > 100) or (high < low ) then
high = 90
end
self.DefenseLowProb = low
self.DefenseHighProb = high
end
--- Set the number of seconds a SHORAD site will stay active
-- @param #SHORAD self
-- @param #number seconds Number of seconds systems stay active
function SHORAD:SetActiveTimer(seconds)
local timer = seconds or 600
if timer < 0 then
timer = 600
end
self.ActiveTimer = timer
end
--- Set the number of meters for the SHORAD defense zone
-- @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
function SHORAD:SetDefenseRadius(meters)
local radius = meters or 20000
if radius < 0 then
radius = 20000
end
self.Radius = radius
end
--- Check if a HARM was fired
-- @param #SHORAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SHORAD:_CheckHarms(WeaponName)
self:F( { WeaponName } )
local hit = false
if self.DefendHarms then
for _,_name in pairs (SHORAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end
end
end
return hit
end
--- Check if an AGM was fired
-- @param #SHORAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
function SHORAD:_CheckMavs(WeaponName)
self:F( { WeaponName } )
local hit = false
if self.DefendMavs then
for _,_name in pairs (SHORAD.Mavs) do
if string.find(WeaponName,_name,1) then hit = true end
end
end
return hit
end
--- Check the coalition of the attacker
-- @param #SHORAD self
-- @param #string Coalition name
-- @return #boolean Returns false for a match
function SHORAD:_CheckCoalition(Coalition)
local owncoalition = self.Coalition
local othercoalition = ""
if Coalition == 0 then
othercoalition = "neutral"
elseif Coalition == 1 then
othercoalition = "red"
else
othercoalition = "blue"
end
self:T({owncoalition = owncoalition, othercoalition = othercoalition})
if owncoalition ~= othercoalition then
return true
else
return false
end
end
--- Check if the missile is aimed at a SHORAD
-- @param #SHORAD self
-- @param #string TargetGroupName Name of the target group
-- @return #boolean Returns true for a match, else false
function SHORAD:_CheckShotAtShorad(TargetGroupName)
local tgtgrp = TargetGroupName
local shorad = self.Groupset
local shoradset = shorad:GetAliveSet() --#table
local returnname = false
for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then
returnname = true
end
end
return returnname
end
--- Check if the missile is aimed at a SAM site
-- @param #SHORAD self
-- @param #string TargetGroupName Name of the target group
-- @return #boolean Returns true for a match, else false
function SHORAD:_CheckShotAtSams(TargetGroupName)
local tgtgrp = TargetGroupName
local shorad = self.Samset
local shoradset = shorad:GetAliveSet() --#table
local returnname = false
for _,_groups in pairs (shoradset) do
local groupname = _groups:GetName()
if string.find(groupname, tgtgrp, 1) then
returnname = true
end
end
return returnname
end
--- Calculate if the missile shot is detected
-- @param #SHORAD self
-- @return #boolean Returns true for a detection, else false
function SHORAD:_ShotIsDetected()
local IsDetected = false
local DetectionProb = math.random(self.DefenseLowProb, self.DefenseHighProb) -- reference value
local ActualDetection = math.random(1,100) -- value for this shot
if ActualDetection <= DetectionProb then
IsDetected = true
end
return IsDetected
end
--- Wake up #SHORADs in a zone with diameter Radius for ActiveTimer seconds
-- @param #SHORAD self
-- @param #string TargetGroup Name of the target group used to build the #ZONE
-- @param #number Radius Radius of the #ZONE
-- @param #number ActiveTimer Number of seconds to stay active
-- @usage Use this function to integrate with other systems.
function SHORAD:WakeUpShorad(TargetGroup, Radius, ActiveTimer)
self:F({TargetGroup, Radius, ActiveTimer})
local targetgroup = GROUP:FindByName(TargetGroup)
local targetzone = ZONE_GROUP:New("Shorad",targetgroup,Radius) -- create a defense zone to check
local groupset = self.Groupset --Core.Set#SET_GROUP
local shoradset = groupset:GetAliveSet() --#table
-- local function to switch off shorad again
local function SleepShorad(group)
local groupname = group:GetName()
self.ActiveGroups[groupname] = nil
group:OptionAlarmStateGreen()
local text = string.format("Sleeping SHORAD %s", group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
end
-- go through set and find the one(s) to activate
for _,_group in pairs (shoradset) do
if _group:IsAnyInZone(targetzone) then
local text = string.format("Waking up SHORAD %s", _group:GetName())
self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
_group:OptionAlarmStateRed()
local groupname = _group:GetName()
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
timer.scheduleFunction(SleepShorad, _group, endtime)
end
end
end
end
--- Main function - work on the EventData
-- @param #SHORAD self
-- @param Core.Event#EVENTDATA EventData The event details table data set
function SHORAD:OnEventShot( EventData )
self:F( { EventData } )
--local ShootingUnit = EventData.IniDCSUnit
--local ShootingUnitName = EventData.IniDCSUnitName
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
local ShootingWeaponName = EventData.WeaponName -- return weapon type
-- get firing coalition
local weaponcoalition = EventData.IniGroup:GetCoalition()
-- get detection probability
if self:_CheckCoalition(weaponcoalition) then --avoid overhead on friendly fire
local IsDetected = self:_ShotIsDetected()
-- convert to text
local DetectedText = "false"
if IsDetected then
DetectedText = "true"
end
local text = string.format("%s Missile Launched = %s | Detected probability state is %s", self.lid, ShootingWeaponName, DetectedText)
self:T( text )
local m = MESSAGE:New(text,15,"Info"):ToAllIf(self.debug)
--
if (self:_CheckHarms(ShootingWeaponName) or self:_CheckMavs(ShootingWeaponName)) and IsDetected then
-- get target data
local targetdata = EventData.Weapon:getTarget() -- Identify target
local targetunitname = Unit.getName(targetdata) -- Unit name
local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group
local targetgroupname = targetgroup:getName() -- group name
-- check if we or a SAM site are the target
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
-- if being shot at, find closest SHORADs to activate
if shotatsams or shotatus then
self:T({shotatsams=shotatsams,shotatus=shotatus})
self:WakeUpShorad(targetgroupname, self.Radius, self.ActiveTimer)
end
end
end
end
--
end
-----------------------------------------------------------------------
-- SHORAD end
-----------------------------------------------------------------------

View File

@ -69,6 +69,7 @@ __Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Shorad.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )

View File

@ -688,7 +688,7 @@ end
-- @param #number Speed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type.
-- @param #number RefuelSystem Refueling system (1=boom, 0=probe). This info is *only* for AIRWINGs so they launch the right tanker type.
-- @return #AUFTRAG self
function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSystem)

View File

@ -753,6 +753,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "SEAD.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem )
end
@ -761,6 +762,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "CAS.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem )
end
@ -769,6 +771,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
DetectedItem.DesignateMenuName = string.format( "BAI.%03d", DetectedItemID ) --inject a name for DESIGNATE, if using same DETECTION object
Task:SetDetection( Detection, DetectedItem )
end
end

View File

@ -311,3 +311,52 @@ ENUMS.Morse.N8="- - - * *"
ENUMS.Morse.N9="- - - - *"
ENUMS.Morse.N0="- - - - -"
ENUMS.Morse[" "]=" "
--- ISO (639-1) 2-letter Language Codes. See the [Wikipedia](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
--
-- @type ENUMS.ISOLang
ENUMS.ISOLang =
{
Arabic = 'AR',
Chinese = 'ZH',
English = 'EN',
French = 'FR',
German = 'DE',
Russian = 'RU',
Spanish = 'ES',
Japanese = 'JA',
Italian = 'IT',
}
--- Phonetic Alphabet (NATO). See the [Wikipedia](https://en.wikipedia.org/wiki/NATO_phonetic_alphabet).
--
-- @type ENUMS.Phonetic
ENUMS.Phonetic =
{
A = 'Alpha',
B = 'Bravo',
C = 'Charlie',
D = 'Delta',
E = 'Echo',
F = 'Foxtrot',
G = 'Golf',
H = 'Hotel',
I = 'India',
J = 'Juliett',
K = 'Kilo',
L = 'Lima',
M = 'Mike',
N = 'November',
O = 'Oscar',
P = 'Papa',
Q = 'Quebec',
R = 'Romeo',
S = 'Sierra',
T = 'Tango',
U = 'Uniform',
V = 'Victor',
W = 'Whiskey',
X = 'Xray',
Y = 'Yankee',
Z = 'Zulu',
}

View File

@ -97,7 +97,7 @@ CALLSIGN={
JTAC={
Axeman=1,
Darknight=2,
Warrier=3,
Warrior=3,
Pointer=4,
Eyeball=5,
Moonbeam=6,

View File

@ -1467,6 +1467,7 @@ end
--- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
-- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC.
-- If the task is assigned to the controllable lead unit will be a FAC.
-- It's important to note that depending on the type of unit that is being assigned the task (AIR or GROUND), you must choose the correct type of callsign enumerator. For airborne controllables use CALLSIGN.Aircraft and for ground based use CALLSIGN.JTAC enumerators.
-- @param #CONTROLLABLE self
-- @param Wrapper.Group#GROUP AttackGroup Target GROUP object.
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
@ -1474,7 +1475,7 @@ end
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
-- @param #number CallsignName Callsign enumerator name of the FAC.
-- @param #number CallsignName Callsign enumerator name of the FAC. (CALLSIGN.Aircraft.{name} for airborne controllables, CALLSIGN.JTACS.{name} for ground units)
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
-- @return DCS#Task The DCS task structure.
function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber )

View File

@ -68,6 +68,8 @@ Functional/Suppression.lua
Functional/PseudoATC.lua
Functional/Warehouse.lua
Functional/Fox.lua
Functional/Mantis.lua
Functional/Shorad.lua
Ops/Airboss.lua
Ops/RecoveryTanker.lua