Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank
2021-09-29 09:55:20 +02:00
27 changed files with 3771 additions and 1816 deletions

View File

@@ -1707,8 +1707,8 @@ end
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
-- @param #FOX self
-- @param DCS#Weapon weapon The weapon.
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>.
-- @return #number Notching heading right, i.e. missile heading +90°.
-- @return #number Notching heading left, i.e. missile heading -90°.
function FOX:_GetNotchingHeadings(weapon)
if weapon then

View File

@@ -1,31 +1,31 @@
--- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses
--
--
-- ===
--
--
-- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy
-- Leverage evasiveness from SEAD
-- Leverage attack range setup added by DCS in 11/20
--
--
-- ===
--
--
-- ## Missions:
--
-- ### [MANTIS - Modular, Automatic and Network capable Targeting and Interception System](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MTS%20-%20Mantis/MTS-010%20-%20Basic%20Mantis%20Demo)
--
--
-- ===
--
--
-- ### Author : **applevangelist **
--
--
-- @module Functional.Mantis
-- @image Functional.Mantis.jpg
-- Date: July 2021
-------------------------------------------------------------------------
--- **MANTIS** class, extends #Core.Base#BASE
--- **MANTIS** class, extends Core.Base#BASE
-- @type MANTIS
-- @field #string Classname
-- @field #string ClassName
-- @field #string name Name of this Mantis
-- @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
@@ -59,10 +59,10 @@
-- @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
--
--- *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
--
-- Simple Class for a more intelligent Air Defense System
--
--
-- #MANTIS
-- Moose derived Modular, Automatic and Network capable Targeting and Interception System.
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy.
@@ -72,62 +72,62 @@
-- Set up your SAM sites in the mission editor. Name the groups with common prefix like "Red SAM".
-- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
--
--
-- # 1. Basic tactical considerations when setting up your SAM sites
--
--
-- ## 1.1 Radar systems and AWACS
--
--
-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track.
-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
--
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
--
-- ## 1.2 SAM sites
--
-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like
-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral,
--
-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like
-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral,
-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage.
--
--
-- ## 1.3 Typical problems
--
-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are
--
-- * bad placement of radar units,
-- * overestimation how far units can "see" and
-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing.
--
--
-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are
--
-- * bad placement of radar units,
-- * overestimation how far units can "see" and
-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing.
--
-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles
-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**.
--
--
-- # 2. Start up your MANTIS with a basic setting
--
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
--
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
-- `myredmantis:Start()`
--
-- [optional] Use
--
-- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)`
--
-- [optional] Use
--
-- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)`
-- * `MANTIS:SetDetectInterval(interval)`
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
--
--
-- before starting #MANTIS to fine-tune your setup.
--
--
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
--
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
--
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()`
--
-- # 3. Default settings
--
--
-- By default, the following settings are active:
--
-- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM"
-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit
-- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)`
-- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)`
-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)`
-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)`
-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)`
-- * engagerange = 85 (percent) - SAMs will only fire if flights are inside of a 85% radius of their max firerange - `MANTIS:SetSAMRange(range)`
-- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)`
@@ -135,28 +135,28 @@
-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)`
--
-- # 4. Advanced Mode
--
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
--
-- E.g. `mymantis:SetAdvancedMode( true, 90 )`
--
-- Use this option if you want to make use of or allow advanced SEAD tactics.
--
--
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
--
-- E.g. `mymantis:SetAdvancedMode( true, 90 )`
--
-- Use this option if you want to make use of or allow advanced SEAD tactics.
--
-- # 5. Integrate SHORAD
--
--
-- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in
-- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so:
--
--
-- `local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()`
-- `myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue")`
-- `-- now set up MANTIS`
-- `mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mymantis:AddShorad(myshorad,720)`
-- `mymantis:Start()`
--
--
-- and (optionally) remove the link later on with
--
-- `mymantis:RemoveShorad()`
--
-- `mymantis:RemoveShorad()`
--
-- @field #MANTIS
MANTIS = {
@@ -197,6 +197,7 @@ MANTIS = {
SamStateTracker = {},
DLink = false,
DLTimeStamp = 0,
Padding = 10,
}
--- Advanced state enumerator
@@ -221,30 +222,31 @@ do
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
--@param #string awacs Group name of your Awacs (optional)
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional)
--@param #number Padding For #SEAD - Extra number of seconds to add to radar switch-back-on time (optional)
--@return #MANTIS self
--@usage Start up your MANTIS with a basic setting
--
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
-- `myredmantis:Start()`
--
-- [optional] Use
--
-- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)`
--
-- [optional] Use
--
-- * `MANTIS:SetEWRGrouping(radius)`
-- * `MANTIS:SetEWRRange(radius)`
-- * `MANTIS:SetSAMRadius(radius)`
-- * `MANTIS:SetDetectInterval(interval)`
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
--
--
-- before starting #MANTIS to fine-tune your setup.
--
--
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
--
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
--
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()`
--
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff)
--
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding)
-- DONE: Create some user functions for these
-- DONE: Make HQ useful
-- DONE: Set SAMs to auto if EWR dies
@@ -267,7 +269,7 @@ do
self.autorelocateunits = { HQ = false, EWR = false}
self.advanced = false
self.adv_ratio = 100
self.adv_state = 0
self.adv_state = 0
self.verbose = false
self.Adv_EWR_Group = nil
self.AWACS_Prefix = awacs or nil
@@ -281,27 +283,28 @@ do
self.state2flag = false
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
self.DLink = false
self.Padding = Padding or 10
if EmOnOff then
if EmOnOff == false then
self.UseEmOnOff = false
else
self.UseEmOnOff = true
end
end
end
if type(awacs) == "string" then
self.advAwacs = true
else
self.advAwacs = false
end
-- Inherit everything from BASE class.
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
-- Set the string id for output to DCS.log file.
self.lid=string.format("MANTIS %s | ", self.name)
-- Debug trace.
if self.debug then
BASE:TraceOnOff(true)
@@ -309,7 +312,7 @@ do
--BASE:TraceClass("SEAD")
BASE:TraceLevel(1)
end
if self.dynamic then
-- Set SAM SET_GROUP
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
@@ -321,18 +324,18 @@ do
-- Set EWR SET_GROUP
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce()
end
-- set up CC
if self.HQ_Template_CC then
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
end
-- @field #string version
self.version="0.6.2"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions ---
-- Start State.
self:SetStartState("Stopped")
@@ -346,11 +349,11 @@ do
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers.
-- @function [parent=#MANTIS] Start
-- @param #MANTIS self
@@ -376,7 +379,7 @@ do
-- @function [parent=#MANTIS] __Status
-- @param #MANTIS self
-- @param #number delay Delay in seconds.
--- On After "Relocating" event. HQ and/or EWR moved.
-- @function [parent=#MANTIS] OnAfterRelocating
-- @param #MANTIS self
@@ -384,7 +387,7 @@ do
-- @param #string Event The Event
-- @param #string To The To State
-- @return #MANTIS self
--- On After "GreenState" event. A SAM group was switched to GREEN alert.
-- @function [parent=#MANTIS] OnAfterGreenState
-- @param #MANTIS self
@@ -393,7 +396,7 @@ do
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
-- @return #MANTIS self
--- On After "RedState" event. A SAM group was switched to RED alert.
-- @function [parent=#MANTIS] OnAfterRedState
-- @param #MANTIS self
@@ -402,7 +405,7 @@ do
-- @param #string To The To State
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
-- @return #MANTIS self
--- On After "AdvStateChange" event. Advanced state changed, influencing detection speed.
-- @function [parent=#MANTIS] OnAfterAdvStateChange
-- @param #MANTIS self
@@ -413,7 +416,7 @@ do
-- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red
-- @param #number Interval Calculated detection interval based on state and advanced feature setting
-- @return #MANTIS self
--- On After "ShoradActivated" event. Mantis has activated a SHORAD.
-- @function [parent=#MANTIS] OnAfterShoradActivated
-- @param #MANTIS self
@@ -424,21 +427,21 @@ do
-- @param #number Radius Radius around the named group to find SHORAD groups
-- @param #number Ontime Seconds the SHORAD will stay active
return self
return self
end
-----------------------------------------------------------------------
-- MANTIS helper functions
-----------------------------------------------------------------------
-----------------------------------------------------------------------
--- [Internal] Function to get the self.SAM_Table
-- @param #MANTIS self
-- @return #table table
-- @return #table table
function MANTIS:_GetSAMTable()
self:T(self.lid .. "GetSAMTable")
return self.SAM_Table
end
--- [Internal] Function to set the self.SAM_Table
-- @param #MANTIS self
-- @return #MANTIS self
@@ -447,7 +450,7 @@ do
self.SAM_Table = table
return self
end
--- Function to set the grouping radius of the detection in meters
-- @param #MANTIS self
-- @param #number radius Radius upon which detected objects will be grouped
@@ -467,17 +470,17 @@ do
self.acceptrange = radius
return self
end
--- Function to set switch-on/off zone for the SAM sites in meters
-- @param #MANTIS self
-- @param #number radius Radius of the firing zone
-- @param #number radius Radius of the firing zone
function MANTIS:SetSAMRadius(radius)
self:T(self.lid .. "SetSAMRadius")
local radius = radius or 25000
self.checkradius = radius
return self
end
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
-- @param #MANTIS self
-- @param #number range Percent of the max fire range
@@ -490,7 +493,7 @@ do
self.engagerange = range
return self
end
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
-- @param #MANTIS self
-- @param #number range Percent of the max fire range
@@ -505,7 +508,7 @@ do
self.mysead.EngagementRange = range
return self
end
--- Function to set switch-on/off the debug state
-- @param #MANTIS self
-- @param #boolean onoff Set true to switch on
@@ -523,7 +526,7 @@ do
end
return self
end
--- Function to get the HQ object for further use
-- @param #MANTIS self
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
@@ -532,10 +535,10 @@ do
if self.HQ_CC then
return self.HQ_CC
else
return nil
end
return nil
end
end
--- Function to set separate AWACS detection instance
-- @param #MANTIS self
-- @param #string prefix Name of the AWACS group in the mission editor
@@ -559,7 +562,7 @@ do
self.awacsrange = range
return self
end
--- Function to set the HQ object for further use
-- @param #MANTIS self
-- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ
@@ -577,7 +580,7 @@ do
end
return self
end
--- Function to set the detection interval
-- @param #MANTIS self
-- @param #number interval The interval in seconds
@@ -586,8 +589,8 @@ do
local interval = interval or 30
self.detectinterval = interval
return self
end
end
--- Function to set Advanded Mode
-- @param #MANTIS self
-- @param #boolean onoff If true, will activate Advanced Mode
@@ -596,7 +599,7 @@ do
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
function MANTIS:SetAdvancedMode(onoff, ratio)
self:T(self.lid .. "SetAdvancedMode")
--self.T({onoff, ratio})
--self:T({onoff, ratio})
local onoff = onoff or false
local ratio = ratio or 100
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
@@ -612,7 +615,7 @@ do
end
return self
end
--- Set using Emissions on/off instead of changing alarm state
-- @param #MANTIS self
-- @param #boolean switch Decide if we are changing alarm state or Emission state
@@ -621,7 +624,7 @@ do
self.UseEmOnOff = switch or false
return self
end
--- Set using an #INTEL_DLINK object instead of #DETECTION
-- @param #MANTIS self
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
@@ -632,7 +635,7 @@ do
self.DLTimeStamp = timer.getAbsTime()
return self
end
--- [Internal] Function to check if HQ is alive
-- @param #MANTIS self
-- @return #boolean True if HQ is alive, else false
@@ -647,15 +650,15 @@ do
local hqgrp = GROUP:FindByName(hq)
if hqgrp then
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
--self.T(self.lid.." HQ is alive!")
--self:T(self.lid.." HQ is alive!")
return true
else
--self.T(self.lid.." HQ is dead!")
return false
--self:T(self.lid.." HQ is dead!")
return false
end
end
end
return self
return self
end
--- [Internal] Function to check if EWR is (at least partially) alive
@@ -664,7 +667,7 @@ do
function MANTIS:_CheckEWRState()
self:T(self.lid .. "CheckEWRState")
local text = self.lid.." Checking EWR State"
--self.T(text)
--self:T(text)
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
-- start check
@@ -680,14 +683,14 @@ do
end
end
end
--self.T(self.lid..string.format(" No of EWR alive is %d", nalive))
--self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
if nalive > 0 then
return true
else
return false
end
end
return self
return self
end
--- [Internal] Function to determine state of the advanced mode
@@ -717,30 +720,30 @@ do
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
if self.debug or self.verbose then
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
--self.T(text)
--self:T(text)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
end
return newinterval, currstate
end
--- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS!
-- @param #MANTIS self
-- @param #boolean hq If true, will relocate HQ object
-- @param #boolean ewr If true, will relocate EWR objects
function MANTIS:SetAutoRelocate(hq, ewr)
self:T(self.lid .. "SetAutoRelocate")
--self.T({hq, ewr})
--self:T({hq, ewr})
local hqrel = hq or false
local ewrel = ewr or false
if hqrel or ewrel then
self.autorelocate = true
self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
--self.T({self.autorelocate, self.autorelocateunits})
--self:T({self.autorelocate, self.autorelocateunits})
end
return self
end
end
--- [Internal] Function to execute the relocation
-- @param #MANTIS self
function MANTIS:_RelocateGroups()
@@ -753,7 +756,7 @@ do
local HQGroup = self.HQ_CC
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
local _hqgrp = self.HQ_CC
--self.T(self.lid.." Relocating HQ")
--self:T(self.lid.." Relocating HQ")
local text = self.lid.." Relocating HQ"
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
@@ -766,7 +769,7 @@ do
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
for _,_grp in pairs (EWR_Grps) do
if _grp:IsAlive() and _grp:IsGround() then
--self.T(self.lid.." Relocating EWR ".._grp:GetName())
--self:T(self.lid.." Relocating EWR ".._grp:GetName())
local text = self.lid.." Relocating EWR ".._grp:GetName()
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
@@ -777,7 +780,7 @@ do
end
return self
end
--- [Internal] Function to check if any object is in the given SAM zone
-- @param #MANTIS self
-- @param #table dectset Table of coordinates of detected items
@@ -793,12 +796,12 @@ do
local coord = _coord -- get current coord to check
-- output for cross-check
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
if self.verbose or self.debug then
if self.verbose or self.debug then
local dectstring = coord:ToStringLLDMS()
local samstring = samcoordinate:ToStringLLDMS()
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
self:I(self.lid..text)
self:I(self.lid..text)
end
-- end output to cross-check
if targetdistance <= radius then
@@ -813,20 +816,20 @@ do
-- @return Functional.Detection #DETECTION_AREAS The running detection set
function MANTIS:StartDetection()
self:T(self.lid.."Starting Detection")
-- start detection
local groupset = self.EWR_Group
local grouping = self.grouping or 5000
local acceptrange = self.acceptrange or 80000
local interval = self.detectinterval or 60
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
local MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
MANTISdetection:SetAcceptRange(acceptrange)
MANTISdetection:SetRefreshTimeInterval(interval)
MANTISdetection:Start()
function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
--BASE:I( { From, Event, To, DetectedItem })
local debug = false
@@ -835,30 +838,30 @@ do
local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS()
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
end
end
end
return MANTISdetection
end
--- [Internal] Function to start the detection via AWACS if defined as separate
-- @param #MANTIS self
-- @return Functional.Detection #DETECTION_AREAS The running detection set
function MANTIS:StartAwacsDetection()
self:T(self.lid.."Starting Awacs Detection")
-- start detection
local group = self.AWACS_Prefix
local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart()
local grouping = self.grouping or 5000
--local acceptrange = self.acceptrange or 80000
local interval = self.detectinterval or 60
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object
local MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones
MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
MANTISAwacs:SetRefreshTimeInterval(interval)
MANTISAwacs:Start()
function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
--BASE:I( { From, Event, To, DetectedItem })
local debug = false
@@ -867,10 +870,10 @@ do
local text = "Awacs Detection at "..Coordinate:ToStringLLDMS()
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
end
end
end
return MANTISAwacs
end
--- [Internal] Function to set the SAM start state
-- @param #MANTIS self
-- @return #MANTIS self
@@ -904,12 +907,12 @@ do
end
self.SAM_Table = SAM_Tbl
-- make SAMs evasive
local mysead = SEAD:New( SEAD_Grps )
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
mysead:SetEngagementRange(engagerange)
self.mysead = mysead
return self
end
--- [Internal] Function to update SAM table and SEAD state
-- @param #MANTIS self
-- @return #MANTIS self
@@ -941,7 +944,7 @@ do
end
return self
end
--- Function to link up #MANTIS with a #SHORAD installation
-- @param #MANTIS self
-- @param Functional.Shorad#SHORAD Shorad The #SHORAD object
@@ -958,7 +961,7 @@ do
end
return self
end
--- Function to unlink #MANTIS from a #SHORAD installation
-- @param #MANTIS self
function MANTIS:RemoveShorad()
@@ -966,11 +969,11 @@ do
self.ShoradLink = false
return self
end
-----------------------------------------------------------------------
-- MANTIS main functions
-----------------------------------------------------------------------
-----------------------------------------------------------------------
--- [Internal] Check detection function
-- @param #MANTIS self
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
@@ -1021,7 +1024,7 @@ do
if self.verbose then self:I(self.lid..text) end
end
end --end alive
else
else
if samgroup:IsAlive() then
-- switch off SAM
if self.UseEmOnOff then
@@ -1032,7 +1035,7 @@ do
self:__GreenState(1,samgroup)
self.SamStateTracker[name] = "GREEN"
end
if self.debug or self.verbose then
if self.debug or self.verbose then
local text = string.format("SAM %s switched to alarm state GREEN!", name)
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(self.lid..text) end
@@ -1041,8 +1044,8 @@ do
end --end check
end --for for loop
return self
end
end
--- [Internal] Relocation relay function
-- @param #MANTIS self
-- @return #MANTIS self
@@ -1051,7 +1054,7 @@ do
self:_RelocateGroups()
return self
end
--- [Internal] Check advanced state
-- @param #MANTIS self
-- @return #MANTIS self
@@ -1086,7 +1089,7 @@ do
end -- end newstate vs oldstate
return self
end
--- [Internal] Check DLink state
-- @param #MANTIS self
-- @return #MANTIS self
@@ -1100,7 +1103,7 @@ do
self:I(self.lid .. "Intel DLink not running - switching back to single detection!")
end
end
--- [Internal] Function to set start state
-- @param #MANTIS self
-- @param #string From The From State
@@ -1117,10 +1120,10 @@ do
if self.advAwacs then
self.AWACS_Detection = self:StartAwacsDetection()
end
self:__Status(-math.random(1,10))
self:__Status(-math.random(1,10))
return self
end
--- [Internal] Before status function for MANTIS
-- @param #MANTIS self
-- @param #string From The From State
@@ -1138,7 +1141,7 @@ do
if self.advAwacs and not self.state2flag then
self:_Check(self.AWACS_Detection)
end
-- relocate HQ and EWR
if self.autorelocate then
local relointerval = self.relointerval
@@ -1146,26 +1149,26 @@ do
local timepassed = thistime - self.TimeStamp
local halfintv = math.floor(timepassed / relointerval)
--self:T({timepassed=timepassed, halfintv=halfintv})
if halfintv >= 1 then
self.TimeStamp = timer.getAbsTime()
self:_Relocate()
self:__Relocating(1)
end
end
-- advanced state check
if self.advanced then
self:_CheckAdvState()
end
-- check DLink state
if self.DLink then
self:_CheckDLinkState()
end
return self
end
@@ -1188,7 +1191,7 @@ do
self:__Status(interval)
return self
end
--- [Internal] Function to stop MANTIS
-- @param #MANTIS self
-- @param #string From The From State
@@ -1197,9 +1200,9 @@ do
-- @return #MANTIS self
function MANTIS:onafterStop(From, Event, To)
self:T({From, Event, To})
return self
return self
end
--- [Internal] Function triggered by Event Relocating
-- @param #MANTIS self
-- @param #string From The From State
@@ -1208,9 +1211,9 @@ do
-- @return #MANTIS self
function MANTIS:onafterRelocating(From, Event, To)
self:T({From, Event, To})
return self
return self
end
--- [Internal] Function triggered by Event GreenState
-- @param #MANTIS self
-- @param #string From The From State
@@ -1220,9 +1223,9 @@ do
-- @return #MANTIS self
function MANTIS:onafterGreenState(From, Event, To, Group)
self:T({From, Event, To, Group})
return self
return self
end
--- [Internal] Function triggered by Event RedState
-- @param #MANTIS self
-- @param #string From The From State
@@ -1232,9 +1235,9 @@ do
-- @return #MANTIS self
function MANTIS:onafterRedState(From, Event, To, Group)
self:T({From, Event, To, Group})
return self
return self
end
--- [Internal] Function triggered by Event AdvStateChange
-- @param #MANTIS self
-- @param #string From The From State
@@ -1246,9 +1249,9 @@ do
-- @return #MANTIS self
function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval)
self:T({From, Event, To, Oldstate, Newstate, Interval})
return self
return self
end
--- [Internal] Function triggered by Event ShoradActivated
-- @param #MANTIS self
-- @param #string From The From State
@@ -1259,7 +1262,7 @@ do
-- @param #number Ontime Seconds the SHORAD will stay active
function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime)
self:T({From, Event, To, Name, Radius, Ontime})
return self
return self
end
end
-----------------------------------------------------------------------

View File

@@ -1,28 +1,28 @@
--- **Functional** -- Train missile defence and deflection.
--
--
-- ===
--
-- ## Features:
--
--
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <20>
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range
-- * Provide alerts when a missile would have killed your aircraft.
-- * Provide alerts when the missile self destructs.
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
--
--
-- ===
--
--
-- ## Missions:
--
--
-- [MIT - Missile Trainer](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MIT%20-%20Missile%20Trainer)
--
--
-- ===
--
--
-- Uses the MOOSE messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
--
--
-- When running a mission where the missile trainer is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
--
--
-- * **Messages**: Menu to configure all messages.
-- * **Messages On**: Show all messages.
-- * **Messages Off**: Disable all messages.
@@ -45,23 +45,23 @@
-- * **Range Off**: Disable range information when a missile is fired to a target.
-- * **Bearing On**: Shows bearing information when a missile is fired to a target.
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
--
--
-- ===
--
--
-- ### Authors: **FlightControl**
--
--
-- ### Contributions:
--
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
-- Danny has shared his ideas and together we made a design.
--
-- * **Stuka (Danny)**: Who you can search on the Eagle Dynamics Forums. Working together with Danny has resulted in the MISSILETRAINER class.
-- Danny has shared his ideas and together we made a design.
-- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback!
-- * **132nd Squadron**: Testing and optimizing the logic.
--
--
-- ===
--
-- @module Functional.MissileTrainer
@@ -76,7 +76,7 @@
---
--
-- # Constructor:
--
--
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
--
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
@@ -84,7 +84,7 @@
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
--
-- # Initialization:
--
--
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
--
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
@@ -97,8 +97,8 @@
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
--
-- @field #MISSILETRAINER
--
-- @field #MISSILETRAINER
MISSILETRAINER = {
ClassName = "MISSILETRAINER",
TrackingMissiles = {},
@@ -167,7 +167,7 @@ end
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
-- @param #MISSILETRAINER self
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
-- @return #MISSILETRAINER
function MISSILETRAINER:New( Distance, Briefing )
local self = BASE:Inherit( self, BASE:New() )
@@ -194,8 +194,8 @@ function MISSILETRAINER:New( Distance, Briefing )
-- self:F( "ForEach:" .. Client.UnitName )
-- Client:Alive( self._Alive, self )
-- end
--
self.DBClients:ForEachClient(
--
self.DBClients:ForEachClient(
function( Client )
self:F( "ForEach:" .. Client.UnitName )
Client:Alive( self._Alive, self )
@@ -207,9 +207,9 @@ function MISSILETRAINER:New( Distance, Briefing )
-- self.DB:ForEachClient(
-- --- @param Wrapper.Client#CLIENT Client
-- function( Client )
--
--
-- ... actions ...
--
--
-- end
-- )
@@ -225,7 +225,7 @@ function MISSILETRAINER:New( Distance, Briefing )
self.DetailsRangeOnOff = true
self.DetailsBearingOnOff = true
self.MenusOnOff = true
self.TrackingMissiles = {}
@@ -293,7 +293,7 @@ end
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
-- @param #MISSILETRAINER self
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
-- @return #MISSILETRAINER self
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
self:F( TrackingFrequency )
@@ -478,30 +478,30 @@ function MISSILETRAINER:OnEventShot( EVentData )
if TrainerTargetDCSUnit then
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
self:T(TrainerTargetDCSUnitName )
local Client = self.DBClients:FindClient( TrainerTargetDCSUnitName )
if Client then
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
local Message = MESSAGE:New(
string.format( "%s launched a %s",
TrainerSourceUnit:GetTypeName(),
TrainerWeaponName
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ), 5, "Launch Alert" )
if self.AlertsToAll then
Message:ToAll()
else
Message:ToClient( Client )
end
end
local ClientID = Client:GetID()
self:T( ClientID )
local MissileData = {}
@@ -579,52 +579,52 @@ function MISSILETRAINER:_TrackMissiles()
end
-- ALERTS PART
-- Loop for all Player Clients to check the alerts and deletion of missiles.
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
if Client and Client:IsAlive() then
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000
if Distance <= self.Distance then
-- Hit alert
TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" )
local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData)
@@ -639,7 +639,7 @@ function MISSILETRAINER:_TrackMissiles()
TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName()
), 5, "Tracking" )
if self.AlertsToAll == true then
Message:ToAll()
else
@@ -660,41 +660,41 @@ function MISSILETRAINER:_TrackMissiles()
if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed.
-- TRACKING PART
-- For the current client, the missile range and bearing details are displayed To the Player Client.
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
-- Main Player Client loop
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
--self:T2( { Client:GetName() } )
ClientData.MessageToClient = ""
ClientData.MessageToAll = ""
-- Other Players Client loop
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
--self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
if ShowMessages == true then
local TrackingTo
TrackingTo = string.format( " -> %s",
TrainerWeaponTypeName
)
if ClientDataID == TrackingDataID then
if ClientData.MessageToClient == "" then
ClientData.MessageToClient = "Missiles to You:\n"
@@ -712,7 +712,7 @@ function MISSILETRAINER:_TrackMissiles()
end
end
end
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, 1, "Tracking" ):ToClient( Client )

File diff suppressed because it is too large Load Diff

View File

@@ -32,7 +32,9 @@
--
-- ===
--
-- ## Missions: Example missions will be added later.
-- ## Missions:
--
-- * [MAR - On the Range - MOOSE - SC](https://www.digitalcombatsimulator.com/en/files/3317765/) by shagrat
--
-- ===
--
@@ -2558,7 +2560,7 @@ function RANGE:_DisplayBombTargets(_unitname)
-- Get elevation
local elevation=coord:GetLandHeight()
local eltxt=string.format("%d m", elevation)
if _settings:IsImperial() then
if not _settings:IsMetric() then
elevation=UTILS.MetersToFeet(elevation)
eltxt=string.format("%d ft", elevation)
end
@@ -2829,6 +2831,7 @@ function RANGE:_CheckInZone(_unitName)
local accur=0
if shots>0 then
accur=_result.hits/shots*100
if accur > 100 then accur = 100 end
end
-- Message text.

View File

@@ -1,54 +1,56 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
--
--
-- ===
--
--
-- ## 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 defensive action by shutting down their radars.
--
--
-- ===
--
--
-- ## Missions:
--
--
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
--
--
-- ===
--
--
-- ### Authors: **FlightControl**, **applevangelist**
--
-- Last Update: July 2021
--
--
-- Last Update: Aug 2021
--
-- ===
--
--
-- @module Functional.Sead
-- @image SEAD.JPG
--- @type SEAD
---
-- @type SEAD
-- @extends Core.Base#BASE
--- 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.
--
--
-- # Constructor:
--
--
-- 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' } )
--
--
-- @field #SEAD
SEAD = {
ClassName = "SEAD",
ClassName = "SEAD",
TargetSkill = {
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
High = { Evade = 15, DelayOn = { 20, 40 } } ,
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
},
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
},
SEADGroupPrefixes = {},
SuppressedGroups = {},
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
Padding = 10,
}
--- Missile enumerators
@@ -59,7 +61,7 @@ SEAD = {
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM",
["ALARM"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",
@@ -67,22 +69,40 @@ SEAD = {
["X_31"] = "X_31",
["Kh25"] = "Kh25",
}
--- Missile enumerators - from DCS ME and Wikipedia
-- @field HarmData
SEAD.HarmData = {
-- km and mach
["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85},
["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4},
["X_58"] = { 70, 4},
["X_28"] = { 80, 2.5},
["X_25"] = { 25, 0.76},
["X_31"] = {150, 3},
["Kh25"] = {25, 0.8},
}
--- 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
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
-- @return #SEAD self
-- @usage
-- -- CCCP SEAD Defenses
-- -- Defends the Russian SA installations from SEAD attacks.
-- 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, Padding )
local self = BASE:Inherit( self, BASE:New() )
self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@@ -90,9 +110,14 @@ function SEAD:New( SEADGroupPrefixes )
else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.2.8")
self:I("*** SEAD - Started Version 0.3.1")
return self
end
@@ -102,8 +127,8 @@ end
-- @return #SEAD self
function SEAD:UpdateSet( SEADGroupPrefixes )
self:F( SEADGroupPrefixes )
self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@@ -118,9 +143,9 @@ end
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
-- @param #SEAD self
-- @param #number range Set the engagement range in percent, e.g. 50
-- @return self
-- @return #SEAD self
function SEAD:SetEngagementRange(range)
self:F( { range } )
self:T( { range } )
range = range or 75
if range < 0 or range > 100 then
range = 75
@@ -130,100 +155,175 @@ function SEAD:SetEngagementRange(range)
return self
end
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
-- @param #SEAD self
-- @param #number Padding Extra number of seconds to add for the switch-on
-- @return #SEAD self
function SEAD:SetPadding(Padding)
self:T( { Padding } )
local padding = Padding or 10
if padding < 10 then padding = 10 end
self.Padding = padding
return self
end
--- Check if a known HARM was fired
-- @param #SEAD self
-- @param #string WeaponName
-- @return #boolean Returns true for a match
-- @return #string name Name of hit in table
function SEAD:_CheckHarms(WeaponName)
self:F( { WeaponName } )
self:T( { WeaponName } )
local hit = false
local name = ""
for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then hit = true end
if string.find(WeaponName,_name,1) then
hit = true
name = _name
break
end
end
return hit
return hit, name
end
--- (Internal) Return distance in meters between two coordinates or -1 on error.
-- @param #SEAD self
-- @param Core.Point#COORDINATE _point1 Coordinate one
-- @param Core.Point#COORDINATE _point2 Coordinate two
-- @return #number Distance in meters
function SEAD:_GetDistance(_point1, _point2)
self:T("_GetDistance")
if _point1 and _point2 then
local distance1 = _point1:Get2DDistance(_point2)
local distance2 = _point1:DistanceFromPointVec2(_point2)
--self:T({dist1=distance1, dist2=distance2})
if distance1 and type(distance1) == "number" then
return distance1
elseif distance2 and type(distance2) == "number" then
return distance2
else
self:E("*****Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
else
self:E("******Cannot calculate distance!")
self:E({_point1,_point2})
return -1
end
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
-- @return #SEAD self
function SEAD:HandleEventShot( EventData )
self:T( { EventData } )
self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
self:T({ SEADWeapon })
--self:T({ SEADWeapon })
if self:_CheckHarms(SEADWeaponName) then
self:T( '*** SEAD - Weapon Match' )
local _targetskill = "Random"
local _targetMimgroupName = "none"
local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
local _targetgroup = nil -- Wrapper.Group#GROUP
if _targetUnit and _targetUnit:IsAlive() then
local _targetMimgroup = _targetUnit:GetGroup()
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
self:T( self.SEADGroupPrefixes )
self:T( _targetMimgroupName )
_targetgroup = _targetUnit:GetGroup()
_targetgroupname = _targetgroup:GetName() -- group name
local _targetUnitName = _targetUnit:GetName()
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end
-- see if we are shot at
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
self:T( _targetgroupname, SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Found' )
self:T( '*** SEAD - Group Match Found' )
break
end
end
end
if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
self:T( _targetskill )
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
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
}
local function SuppressionEnd(id) --switch group back on
local range = self.EngagementRange -- Feature Request #1355
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)
--id.groupName:enableEmission(true)
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
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
local SuppressionEndTime = timer.getTime() + delay
--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
-- time to impact
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateGreen()
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[args[2]] = false
end
-- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
end
end
end
end
end
end
return self
end

View File

@@ -113,7 +113,7 @@ do
["AGM_122"] = "AGM_122",
["AGM_84"] = "AGM_84",
["AGM_45"] = "AGM_45",
["ALARN"] = "ALARM",
["ALARM"] = "ALARM",
["LD-10"] = "LD-10",
["X_58"] = "X_58",
["X_28"] = "X_28",