mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Improve the consistency of the module intros to the most commonly used version (single dash). Add missing module information (abbreviated where none existed previously). Fix broken documentation links Make module names correspond to filenames (and fix links). Fix typos.
1904 lines
79 KiB
Lua
1904 lines
79 KiB
Lua
--- **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. Uses detection to switch on the AA site closest to the enemy.
|
|
-- Automatic mode (default since 0.8) can set-up your SAM site network automatically for you.
|
|
-- Leverage evasiveness from SEAD, leverage attack range setting.
|
|
--
|
|
-- ===
|
|
--
|
|
-- ## 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: Dec 2021
|
|
|
|
-------------------------------------------------------------------------
|
|
--- **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 #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 #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 #boolean debug Switch on extra messages
|
|
-- @field #boolean verbose Switch on extra logging
|
|
-- @field #number checkradius Radius of the SAM sites
|
|
-- @field #number grouping Radius to group detected objects
|
|
-- @field #number acceptrange Radius of the EWR detection
|
|
-- @field #number detectinterval Interval in seconds for the target detection
|
|
-- @field #number engagerange Firing engage range of the SAMs, see [https://wiki.hoggitworld.com/view/DCS_option_engagementRange]
|
|
-- @field #boolean autorelocate Relocate HQ and EWR groups in random intervals. Note: You need to select units for this which are *actually mobile*
|
|
-- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object
|
|
-- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100%
|
|
-- @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
|
|
-- @field #boolean UseEmOnOff Decide if we are using Emissions on/off (true) or AlarmState red/green (default)
|
|
-- @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
|
|
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
|
|
-- @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
|
|
--
|
|
-- 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. Uses detection to switch on the SAM site closest to the enemy.
|
|
-- * **Automatic mode** (default since 0.8) can set-up your SAM site network automatically for you
|
|
-- * **Classic mode** behaves like before
|
|
-- * Leverage evasiveness from SEAD, leverage attack range setting
|
|
-- * Automatic setup of SHORAD based on groups of the class "short-range"
|
|
--
|
|
-- # 0. Base considerations and naming conventions
|
|
--
|
|
-- **Before** you start to set up your SAM sites in the mission editor, please think of naming conventions. This is especially critical to make
|
|
-- eveything work as intended, also if you have both a blue and a red installation!
|
|
--
|
|
-- You need three **non-overlapping** "name spaces" for everything to work properly:
|
|
--
|
|
-- * SAM sites, e.g. each **group name** begins with "Red SAM"
|
|
-- * EWR network and AWACS, e.g. each **group name** begins with "Red EWR" and *not* e.g. "Red SAM EWR" (overlap with "Red SAM"), "Red EWR Awacs" will be found by "Red EWR"
|
|
-- * SHORAD, e.g. each **group name** begins with "Red SHORAD" and *not" e.g. just "SHORAD" because you might also have "Blue SHORAD"
|
|
--
|
|
-- It's important to get this right because of the nature of the filter-system in @{Core.Set#SET_GROUP}. Filters are "greedy", that is they
|
|
-- will match *any* string that contains the search string - hence we need to avoid that SAMs, EWR and SHORAD step on each other\'s toes.
|
|
--
|
|
-- Second, for auto-mode to work, the SAMs need the **SAM Type Name** in their group name, as MANTIS will determine their capabilities from this.
|
|
-- This is case-sensitive, so "sa-11" is not equal to "SA-11" is not equal to "Sa-11"!
|
|
--
|
|
-- Known SAM types at the time of writing are:
|
|
--
|
|
-- * Avenger
|
|
-- * Chaparrel
|
|
-- * Hawk
|
|
-- * Linebacker
|
|
-- * NASAMS
|
|
-- * Patriot
|
|
-- * Rapier
|
|
-- * Roland
|
|
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
|
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
|
|
-- * and from HDS (see note below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
|
|
--
|
|
-- Following the example started above, an SA-6 site group name should start with "Red SAM SA-6" then, or a blue Patriot installation with e.g. "Blue SAM Patriot".
|
|
-- **NOTE** If you are using the High-Digit-Sam Mod, please note that the **group name** for the following SAM types also needs to contain the keyword "HDS":
|
|
--
|
|
-- * SA-2 (with V759 missile, e.g. "Red SAM SA-2 HDS")
|
|
-- * SA-2 (with HQ-2 launcher, use HQ-2 in the group name, e.g. "Red SAM HQ-2" )
|
|
-- * SA-3 (with V601P missile, e.g. "Red SAM SA-3 HDS")
|
|
-- * SA-10B (overlap with other SA-10 types, e.g. "Red SAM SA-10B HDS")
|
|
-- * SA-10C (overlap with other SA-10 types, e.g. "Red SAM SA-10C HDS")
|
|
-- * SA-12 (launcher dependent range, e.g. "Red SAM SA-12 HDS")
|
|
-- * SA-23 (launcher dependent range, e.g. "Red SAM SA-23 HDS")
|
|
--
|
|
-- The other HDS types work like the rest of the known SAM systems.
|
|
--
|
|
-- # 0.1 Set-up in the mission editor
|
|
--
|
|
-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.
|
|
-- Set up your EWR system in the mission editor. Name the groups using a systematic approach like above. Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
|
|
-- Search Radars usually have "SR" or "STR" in their names. Use the encyclopedia in the mission editor to inform yourself.
|
|
-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one** group per SAM location. SA-15 TOR systems offer a good missile defense.
|
|
--
|
|
-- [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 importance 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.
|
|
--
|
|
-- ## 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-5/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 on, 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 mission 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:Start()
|
|
--
|
|
-- Use
|
|
--
|
|
-- * MANTIS:SetEWRGrouping(radius) [classic mode]
|
|
-- * MANTIS:SetSAMRadius(radius) [classic mode]
|
|
-- * MANTIS:SetDetectInterval(interval) [classic & auto modes]
|
|
-- * MANTIS:SetAutoRelocate(hq, ewr) [classic & auto modes]
|
|
--
|
|
-- before starting #MANTIS to fine-tune your setup.
|
|
--
|
|
-- If you want to use a separate AWACS unit 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:Start()
|
|
--
|
|
-- ## 2.1 Auto mode features
|
|
--
|
|
-- ### 2.1.1 You can now add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones:
|
|
--
|
|
-- -- Parameters are tables of Core.Zone#ZONE objects!
|
|
-- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
|
|
-- -- it is inside any AcceptZone. Then RejectZones are checked, which enforces both borders, but also overlaps of
|
|
-- -- Accept- and RejectZones. Last, if it is inside a conflict zone, it is accepted.
|
|
-- `mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)`
|
|
--
|
|
--
|
|
-- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target:
|
|
--
|
|
-- -- parameters are numbers. Defaults are 1,2,2,6 respectively
|
|
-- `mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)`
|
|
--
|
|
-- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range"
|
|
--
|
|
-- ### 2.1.4 Advanced features
|
|
--
|
|
-- -- switch off auto mode **before** you start MANTIS.
|
|
-- `mybluemantis.automode = false`
|
|
--
|
|
-- -- switch off auto shorad **before** you start MANTIS.
|
|
-- `mybluemantis.autoshorad = false`
|
|
--
|
|
-- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
|
-- -- also see engagerange below.
|
|
-- ` self.radiusscale[MANTIS.SamType.LONG] = 1.1`
|
|
-- ` self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2`
|
|
-- ` self.radiusscale[MANTIS.SamType.SHORT] = 1.3`
|
|
--
|
|
-- # 3. Default settings [both modes unless stated otherwise]
|
|
--
|
|
-- 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
|
|
-- * [classic mode] 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)`
|
|
-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)`
|
|
-- * engagerange = 95 (percent) - SAMs will only fire if flights are inside of a 95% 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)`
|
|
-- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)`
|
|
-- * 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.
|
|
--
|
|
-- # 5. Integrate SHORAD [classic mode]
|
|
--
|
|
-- 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()
|
|
--
|
|
-- If you systematically name your SHORAD groups starting with "Blue SHORAD" you'll need exactly **one** SHORAD instance to manage all SHORAD groups.
|
|
--
|
|
-- (Optionally) you can remove the link later on with
|
|
--
|
|
-- mymantis:RemoveShorad()
|
|
--
|
|
-- # 6. Integrated SEAD
|
|
--
|
|
-- MANTIS is using @{Functional.Sead#SEAD} internally to both detect and evade HARM attacks. No extra efforts needed to set this up!
|
|
-- Once a HARM attack is detected, MANTIS (via SEAD) will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
|
|
-- vehicles around (*if they are __drivable__*, that is). There's a component of randomness in detection and evasion, which is based on the
|
|
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
|
|
-- period of time to stay defensive, before it takes evasive actions.
|
|
--
|
|
-- You can link into the SEAD driven events of MANTIS like so:
|
|
--
|
|
-- function mymantis:OnAfterSeadSuppressionPlanned(From, Event, To, Group, Name, SuppressionStartTime, SuppressionEndTime)
|
|
-- -- your code here - SAM site shutdown and evasion planned, but not yet executed
|
|
-- -- Time entries relate to timer.getTime() - see https://wiki.hoggitworld.com/view/DCS_func_getTime
|
|
-- end
|
|
--
|
|
-- function mymantis:OnAfterSeadSuppressionStart(From, Event, To, Group, Name)
|
|
-- -- your code here - SAM site is emissions off and possibly moving
|
|
-- end
|
|
--
|
|
-- function mymantis:OnAfterSeadSuppressionEnd(From, Event, To, Group, Name)
|
|
-- -- your code here - SAM site is back online
|
|
-- end
|
|
--
|
|
-- @field #MANTIS
|
|
MANTIS = {
|
|
ClassName = "MANTIS",
|
|
name = "mymantis",
|
|
SAM_Templates_Prefix = "",
|
|
SAM_Group = nil,
|
|
EWR_Templates_Prefix = "",
|
|
EWR_Group = nil,
|
|
Adv_EWR_Group = nil,
|
|
HQ_Template_CC = "",
|
|
HQ_CC = nil,
|
|
SAM_Table = {},
|
|
SAM_Table_Long = {},
|
|
SAM_Table_Medium = {},
|
|
SAM_Table_Short = {},
|
|
lid = "",
|
|
Detection = nil,
|
|
AWACS_Detection = nil,
|
|
debug = false,
|
|
checkradius = 25000,
|
|
grouping = 5000,
|
|
acceptrange = 80000,
|
|
detectinterval = 30,
|
|
engagerange = 95,
|
|
autorelocate = false,
|
|
advanced = false,
|
|
adv_ratio = 100,
|
|
adv_state = 0,
|
|
AWACS_Prefix = "",
|
|
advAwacs = false,
|
|
verbose = false,
|
|
awacsrange = 250000,
|
|
Shorad = nil,
|
|
ShoradLink = false,
|
|
ShoradTime = 600,
|
|
ShoradActDistance = 25000,
|
|
UseEmOnOff = false,
|
|
TimeStamp = 0,
|
|
state2flag = false,
|
|
SamStateTracker = {},
|
|
DLink = false,
|
|
DLTimeStamp = 0,
|
|
Padding = 10,
|
|
SuppressedGroups = {},
|
|
automode = true,
|
|
autoshorad = true,
|
|
ShoradGroupSet = nil,
|
|
}
|
|
|
|
--- Advanced state enumerator
|
|
-- @type MANTIS.AdvancedState
|
|
MANTIS.AdvancedState = {
|
|
GREEN = 0,
|
|
AMBER = 1,
|
|
RED = 2,
|
|
}
|
|
|
|
--- SAM Type
|
|
-- @type MANTIS.SamType
|
|
MANTIS.SamType = {
|
|
SHORT = "Short",
|
|
MEDIUM = "Medium",
|
|
LONG = "Long",
|
|
}
|
|
|
|
--- SAM data
|
|
-- @type MANTIS.SamData
|
|
-- @field #number Range Max firing range in km
|
|
-- @field #number Blindspot no-firing range (green circle)
|
|
-- @field #number Height Max firing height in km
|
|
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
|
-- @field #string Radar Radar typename on unit level (used as key)
|
|
MANTIS.SamData = {
|
|
["Hawk"] = { Range=44, Blindspot=0, Height=9, Type="Medium", Radar="Hawk" }, -- measures in km
|
|
["NASAMS"] = { Range=14, Blindspot=0, Height=3, Type="Short", Radar="NSAMS" },
|
|
["Patriot"] = { Range=99, Blindspot=0, Height=9, Type="Long", Radar="Patriot" },
|
|
["Rapier"] = { Range=6, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
|
|
["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" },
|
|
["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" },
|
|
["SA-5"] = { Range=250, Blindspot=7, Height=40, Type="Long", Radar="5N62V" },
|
|
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" },
|
|
["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"},
|
|
["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" },
|
|
["Roland"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Roland" },
|
|
["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" },
|
|
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
|
|
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" },
|
|
["SA-19"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Tunguska" },
|
|
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" },
|
|
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
|
|
["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" },
|
|
["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
|
|
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" },
|
|
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
|
|
-- units from HDS Mod, multi launcher options is tricky
|
|
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
|
|
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },
|
|
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
|
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
|
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
|
}
|
|
|
|
--- SAM data HDS
|
|
-- @type MANTIS.SamDataHDS
|
|
-- @field #number Range Max firing range in km
|
|
-- @field #number Blindspot no-firing range (green circle)
|
|
-- @field #number Height Max firing height in km
|
|
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
|
-- @field #string Radar Radar typename on unit level (used as key)
|
|
MANTIS.SamDataHDS = {
|
|
-- units from HDS Mod, multi launcher options is tricky
|
|
-- group name MUST contain HDS to ID launcher type correctly!
|
|
["SA-2 HDS"] = { Range=56, Blindspot=7, Height=30, Type="Medium", Radar="V759" },
|
|
["SA-3 HDS"] = { Range=20, Blindspot=6, Height=30, Type="Short", Radar="V-601P" },
|
|
["SA-10C HDS 2"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85DE ln"}, -- V55RUD
|
|
["SA-10C HDS 1"] = { Range=90, Blindspot=5, Height=25, Type="Long" , Radar="5P85CE ln"}, -- V55RUD
|
|
["SA-12 HDS 2"] = { Range=100, Blindspot=10, Height=25, Type="Long" , Radar="S-300V 9A82 l"},
|
|
["SA-12 HDS 1"] = { Range=75, Blindspot=1, Height=25, Type="Long" , Radar="S-300V 9A83 l"},
|
|
["SA-23 HDS 2"] = { Range=200, Blindspot=5, Height=37, Type="Long", Radar="S-300VM 9A82ME" },
|
|
["SA-23 HDS 1"] = { Range=100, Blindspot=1, Height=50, Type="Long", Radar="S-300VM 9A83ME" },
|
|
["HQ-2 HDS"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
|
}
|
|
|
|
-----------------------------------------------------------------------
|
|
-- MANTIS System
|
|
-----------------------------------------------------------------------
|
|
|
|
do
|
|
--- Function to instantiate a new object of class MANTIS
|
|
--@param #MANTIS self
|
|
--@param #string name Name of this MANTIS for reporting
|
|
--@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..."
|
|
--@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..."
|
|
--@param #string hq Group name of your HQ (optional)
|
|
--@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 (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:Start()
|
|
--
|
|
-- [optional] Use
|
|
--
|
|
-- myredmantis:SetDetectInterval(interval)
|
|
-- myredmantis: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:Start()
|
|
--
|
|
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
|
|
-- DONE: Refresh SAM table in dynamic mode
|
|
-- DONE: Treat Awacs separately, since they might be >80km off site
|
|
-- DONE: Allow tables of prefixes for the setup
|
|
-- DONE: Auto-Mode with range setups for various known SAM types.
|
|
|
|
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
|
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
|
self.HQ_Template_CC = hq or nil
|
|
self.Coalition = coaltion or "red"
|
|
self.SAM_Table = {}
|
|
self.SAM_Table_Long = {}
|
|
self.SAM_Table_Medium = {}
|
|
self.SAM_Table_Short = {}
|
|
self.dynamic = dynamic or false
|
|
self.checkradius = 25000
|
|
self.grouping = 5000
|
|
self.acceptrange = 80000
|
|
self.detectinterval = 30
|
|
self.engagerange = 95
|
|
self.autorelocate = false
|
|
self.autorelocateunits = { HQ = false, EWR = false}
|
|
self.advanced = false
|
|
self.adv_ratio = 100
|
|
self.adv_state = 0
|
|
self.verbose = false
|
|
self.Adv_EWR_Group = nil
|
|
self.AWACS_Prefix = awacs or nil
|
|
self.awacsrange = 250000 --DONE: 250km, User Function to change
|
|
self.Shorad = nil
|
|
self.ShoradLink = false
|
|
self.ShoradTime = 600
|
|
self.ShoradActDistance = 25000
|
|
self.TimeStamp = timer.getAbsTime()
|
|
self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
|
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
|
|
self.SuppressedGroups = {}
|
|
-- 0.8 additions
|
|
self.automode = true
|
|
self.radiusscale = {}
|
|
self.radiusscale[MANTIS.SamType.LONG] = 1.1
|
|
self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
|
|
self.radiusscale[MANTIS.SamType.SHORT] = 1.3
|
|
--self.SAMCheckRanges = {}
|
|
self.usezones = false
|
|
self.AcceptZones = {}
|
|
self.RejectZones = {}
|
|
self.ConflictZones = {}
|
|
self.maxlongrange = 1
|
|
self.maxmidrange = 2
|
|
self.maxshortrange = 2
|
|
self.maxclassic = 6
|
|
self.autoshorad = true
|
|
self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP
|
|
|
|
self.UseEmOnOff = true
|
|
if EmOnOff == false then
|
|
self.UseEmOnOff = false
|
|
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)
|
|
BASE:TraceClass(self.ClassName)
|
|
--BASE:TraceClass("SEAD")
|
|
BASE:TraceLevel(1)
|
|
end
|
|
|
|
self.ewr_templates = {}
|
|
if type(samprefix) ~= "table" then
|
|
self.SAM_Templates_Prefix = {samprefix}
|
|
end
|
|
|
|
if type(ewrprefix) ~= "table" then
|
|
self.EWR_Templates_Prefix = {ewrprefix}
|
|
end
|
|
|
|
for _,_group in pairs (self.SAM_Templates_Prefix) do
|
|
table.insert(self.ewr_templates,_group)
|
|
end
|
|
|
|
for _,_group in pairs (self.EWR_Templates_Prefix) do
|
|
table.insert(self.ewr_templates,_group)
|
|
end
|
|
|
|
if self.advAwacs then
|
|
table.insert(self.ewr_templates,awacs)
|
|
end
|
|
|
|
self:T({self.ewr_templates})
|
|
|
|
if self.dynamic then
|
|
-- Set SAM SET_GROUP
|
|
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
|
-- Set EWR SET_GROUP
|
|
self.EWR_Group = SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition):FilterStart()
|
|
else
|
|
-- Set SAM SET_GROUP
|
|
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
|
-- Set EWR SET_GROUP
|
|
self.EWR_Group = SET_GROUP:New():FilterPrefixes(self.ewr_templates):FilterCoalitions(self.Coalition):FilterOnce()
|
|
end
|
|
|
|
-- set up CC
|
|
if self.HQ_Template_CC then
|
|
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
|
|
end
|
|
|
|
-- TODO Version
|
|
-- @field #string version
|
|
self.version="0.8.8"
|
|
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
|
|
|
--- FSM Functions ---
|
|
|
|
-- Start State.
|
|
self:SetStartState("Stopped")
|
|
|
|
-- Add FSM transitions.
|
|
-- From State --> Event --> To State
|
|
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
|
|
self:AddTransition("*", "Status", "*") -- MANTIS status update.
|
|
self:AddTransition("*", "Relocating", "*") -- MANTIS HQ and EWR are relocating.
|
|
self:AddTransition("*", "GreenState", "*") -- MANTIS A SAM switching to GREEN state.
|
|
self:AddTransition("*", "RedState", "*") -- MANTIS A SAM switching to RED state.
|
|
self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change.
|
|
self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD.
|
|
self:AddTransition("*", "SeadSuppressionStart", "*") -- SEAD has switched off one group.
|
|
self:AddTransition("*", "SeadSuppressionEnd", "*") -- SEAD has switched on one group.
|
|
self:AddTransition("*", "SeadSuppressionPlanned", "*") -- SEAD has planned a suppression.
|
|
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
|
|
|
|
--- Triggers the FSM event "Start" after a delay. Starts the MANTIS. Initializes parameters and starts event handlers.
|
|
-- @function [parent=#MANTIS] __Start
|
|
-- @param #MANTIS self
|
|
-- @param #number delay Delay in seconds.
|
|
|
|
--- Triggers the FSM event "Stop". Stops the MANTIS and all its event handlers.
|
|
-- @param #MANTIS self
|
|
|
|
--- Triggers the FSM event "Stop" after a delay. Stops the MANTIS and all its event handlers.
|
|
-- @function [parent=#MANTIS] __Stop
|
|
-- @param #MANTIS self
|
|
-- @param #number delay Delay in seconds.
|
|
|
|
--- Triggers the FSM event "Status".
|
|
-- @function [parent=#MANTIS] Status
|
|
-- @param #MANTIS self
|
|
|
|
--- Triggers the FSM event "Status" after a delay.
|
|
-- @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
|
|
-- @param #string From The From State
|
|
-- @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
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @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
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @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
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red
|
|
-- @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
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param #string Name Name of the GROUP which SHORAD shall protect
|
|
-- @param #number Radius Radius around the named group to find SHORAD groups
|
|
-- @param #number Ontime Seconds the SHORAD will stay active
|
|
|
|
--- On After "SeadSuppressionPlanned" event. Mantis has planned to switch off a site to defend SEAD attack.
|
|
-- @function [parent=#MANTIS] OnAfterSeadSuppressionPlanned
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
-- @param #number SuppressionStartTime Model start time of the suppression from `timer.getTime()`
|
|
-- @param #number SuppressionEndTime Model end time of the suppression from `timer.getTime()`
|
|
-- @param Wrapper.Group#GROUP Attacker The attacking GROUP object
|
|
|
|
--- On After "SeadSuppressionStart" event. Mantis has switched off a site to defend a SEAD attack.
|
|
-- @function [parent=#MANTIS] OnAfterSeadSuppressionStart
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
-- @param Wrapper.Group#GROUP Attacker The attacking GROUP object
|
|
|
|
--- On After "SeadSuppressionEnd" event. Mantis has switched on a site after a SEAD attack.
|
|
-- @function [parent=#MANTIS] OnAfterSeadSuppressionEnd
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
|
|
return self
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- MANTIS helper functions
|
|
-----------------------------------------------------------------------
|
|
|
|
--- [Internal] Function to get the self.SAM_Table
|
|
-- @param #MANTIS self
|
|
-- @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
|
|
function MANTIS:_SetSAMTable(table)
|
|
self:T(self.lid .. "SetSAMTable")
|
|
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
|
|
function MANTIS:SetEWRGrouping(radius)
|
|
self:T(self.lid .. "SetEWRGrouping")
|
|
local radius = radius or 5000
|
|
self.grouping = radius
|
|
return self
|
|
end
|
|
|
|
--- Function to set accept and reject zones.
|
|
-- @param #MANTIS self
|
|
-- @param #table AcceptZones Table of @{Core.Zone#ZONE} objects
|
|
-- @param #table RejectZones Table of @{Core.Zone#ZONE} objects
|
|
-- @param #table ConflictZones Table of @{Core.Zone#ZONE} objects
|
|
-- @return #MANTIS self
|
|
-- @usage
|
|
-- Parameters are **tables of Core.Zone#ZONE** objects!
|
|
-- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
|
|
-- it is inside any AcceptZone. Then RejectZones are checked, which enforces both borders, but also overlaps of
|
|
-- Accept- and RejectZones. Last, if it is inside a conflict zone, it is accepted.
|
|
function MANTIS:AddZones(AcceptZones,RejectZones, ConflictZones)
|
|
self:T(self.lid .. "AddZones")
|
|
self.AcceptZones = AcceptZones or {}
|
|
self.RejectZones = RejectZones or {}
|
|
self.ConflictZones = ConflictZones or {}
|
|
if #AcceptZones > 0 or #RejectZones > 0 or #ConflictZones > 0 then
|
|
self.usezones = true
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Function to set the detection radius of the EWR in meters. (Deprecated, SAM range is used)
|
|
-- @param #MANTIS self
|
|
-- @param #number radius Radius of the EWR detection zone
|
|
function MANTIS:SetEWRRange(radius)
|
|
self:T(self.lid .. "SetEWRRange")
|
|
--local radius = radius or 80000
|
|
-- self.acceptrange = radius
|
|
return self
|
|
end
|
|
|
|
--- Function to set switch-on/off zone for the SAM sites in meters. Overwritten per SAM in automode.
|
|
-- @param #MANTIS self
|
|
-- @param #number radius Radius of the firing zone in classic mode
|
|
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. 85
|
|
-- @param #MANTIS self
|
|
-- @param #number range Percent of the max fire range
|
|
function MANTIS:SetSAMRange(range)
|
|
self:T(self.lid .. "SetSAMRange")
|
|
local range = range or 95
|
|
if range < 0 or range > 100 then
|
|
range = 95
|
|
end
|
|
self.engagerange = range
|
|
return self
|
|
end
|
|
|
|
--- Function to set number of SAMs going active on a valid, detected thread
|
|
-- @param #MANTIS self
|
|
-- @param #number Short Number of short-range systems activated, defaults to 1.
|
|
-- @param #number Mid Number of mid-range systems activated, defaults to 2.
|
|
-- @param #number Long Number of long-range systems activated, defaults to 2.
|
|
-- @param #number Classic (non-automode) Number of overall systems activated, defaults to 6.
|
|
-- @return #MANTIS self
|
|
function MANTIS:SetMaxActiveSAMs(Short,Mid,Long,Classic)
|
|
self:T(self.lid .. "SetMaxActiveSAMs")
|
|
self.maxclassic = Classic or 6
|
|
self.maxlongrange = Long or 1
|
|
self.maxmidrange = Mid or 2
|
|
self.maxshortrange = Short or 2
|
|
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
|
|
function MANTIS:SetNewSAMRangeWhileRunning(range)
|
|
self:T(self.lid .. "SetNewSAMRangeWhileRunning")
|
|
local range = range or 95
|
|
if range < 0 or range > 100 then
|
|
range = 95
|
|
end
|
|
self.engagerange = range
|
|
self:_RefreshSAMTable()
|
|
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
|
|
function MANTIS:Debug(onoff)
|
|
self:T(self.lid .. "SetDebug")
|
|
local onoff = onoff or false
|
|
self.debug = onoff
|
|
if onoff then
|
|
-- Debug trace.
|
|
BASE:TraceOn()
|
|
BASE:TraceClass("MANTIS")
|
|
BASE:TraceLevel(1)
|
|
else
|
|
BASE:TraceOff()
|
|
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
|
|
function MANTIS:GetCommandCenter()
|
|
self:T(self.lid .. "GetCommandCenter")
|
|
if self.HQ_CC then
|
|
return self.HQ_CC
|
|
else
|
|
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
|
|
function MANTIS:SetAwacs(prefix)
|
|
self:T(self.lid .. "SetAwacs")
|
|
if prefix ~= nil then
|
|
if type(prefix) == "string" then
|
|
self.AWACS_Prefix = prefix
|
|
self.advAwacs = true
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Function to set AWACS detection range. Defaults to 250.000m (250km) - use **before** starting your Mantis!
|
|
-- @param #MANTIS self
|
|
-- @param #number range Detection range of the AWACS group
|
|
function MANTIS:SetAwacsRange(range)
|
|
self:T(self.lid .. "SetAwacsRange")
|
|
local range = range or 250000
|
|
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
|
|
function MANTIS:SetCommandCenter(group)
|
|
self:T(self.lid .. "SetCommandCenter")
|
|
local group = group or nil
|
|
if group ~= nil then
|
|
if type(group) == "string" then
|
|
self.HQ_CC = GROUP:FindByName(group)
|
|
self.HQ_Template_CC = group
|
|
else
|
|
self.HQ_CC = group
|
|
self.HQ_Template_CC = group:GetName()
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- Function to set the detection interval
|
|
-- @param #MANTIS self
|
|
-- @param #number interval The interval in seconds
|
|
function MANTIS:SetDetectInterval(interval)
|
|
self:T(self.lid .. "SetDetectInterval")
|
|
local interval = interval or 30
|
|
self.detectinterval = interval
|
|
return self
|
|
end
|
|
|
|
--- Function to set Advanded Mode
|
|
-- @param #MANTIS self
|
|
-- @param #boolean onoff If true, will activate Advanced Mode
|
|
-- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100%
|
|
-- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. 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)`
|
|
function MANTIS:SetAdvancedMode(onoff, ratio)
|
|
self:T(self.lid .. "SetAdvancedMode")
|
|
--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
|
|
self.adv_ratio = ratio
|
|
self.advanced = true
|
|
self.adv_state = 0
|
|
self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
|
self:I(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version))
|
|
else
|
|
local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
|
|
local m= MESSAGE:New(text,10,"MANTIS",true):ToAll()
|
|
self:E(text)
|
|
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
|
|
function MANTIS:SetUsingEmOnOff(switch)
|
|
self:T(self.lid .. "SetUsingEmOnOff")
|
|
self.UseEmOnOff = switch or false
|
|
return self
|
|
end
|
|
|
|
--- Set using your own #INTEL_DLINK object instead of #DETECTION
|
|
-- @param #MANTIS self
|
|
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
|
|
function MANTIS:SetUsingDLink(DLink)
|
|
self:T(self.lid .. "SetUsingDLink")
|
|
self.DLink = true
|
|
self.Detection = DLink
|
|
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
|
|
function MANTIS:_CheckHQState()
|
|
self:T(self.lid .. "CheckHQState")
|
|
local text = self.lid.." Checking HQ State"
|
|
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
|
if self.verbose then self:I(text) end
|
|
-- start check
|
|
if self.advanced then
|
|
local hq = self.HQ_Template_CC
|
|
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!")
|
|
return true
|
|
else
|
|
--self:T(self.lid.." HQ is dead!")
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function to check if EWR is (at least partially) alive
|
|
-- @param #MANTIS self
|
|
-- @return #boolean True if EWR is alive, else false
|
|
function MANTIS:_CheckEWRState()
|
|
self:T(self.lid .. "CheckEWRState")
|
|
local text = self.lid.." Checking EWR State"
|
|
--self:T(text)
|
|
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
|
if self.verbose then self:I(text) end
|
|
-- start check
|
|
if self.advanced then
|
|
local EWR_Group = self.Adv_EWR_Group
|
|
--local EWR_Set = EWR_Group.Set
|
|
local nalive = EWR_Group:CountAlive()
|
|
if self.advAwacs then
|
|
local awacs = GROUP:FindByName(self.AWACS_Prefix)
|
|
if awacs ~= nil then
|
|
if awacs:IsAlive() then
|
|
nalive = nalive+1
|
|
end
|
|
end
|
|
end
|
|
--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
|
|
end
|
|
|
|
--- [Internal] Function to determine state of the advanced mode
|
|
-- @param #MANTIS self
|
|
-- @return #number Newly calculated interval
|
|
-- @return #number Previous state for tracking 0, 1, or 2
|
|
function MANTIS:_CalcAdvState()
|
|
self:T(self.lid .. "CalcAdvState")
|
|
local m=MESSAGE:New(self.lid.." Calculating Advanced State",10,"MANTIS"):ToAllIf(self.debug)
|
|
if self.verbose then self:I(self.lid.." Calculating Advanced State") end
|
|
-- start check
|
|
local currstate = self.adv_state -- save curr state for comparison later
|
|
local EWR_State = self:_CheckEWRState()
|
|
local HQ_State = self:_CheckHQState()
|
|
-- set state
|
|
if EWR_State and HQ_State then -- both alive
|
|
self.adv_state = 0 --everything is fine
|
|
elseif EWR_State or HQ_State then -- one alive
|
|
self.adv_state = 1 --slow down level 1
|
|
else -- none alive
|
|
self.adv_state = 2 --slow down level 2
|
|
end
|
|
-- calculate new detectioninterval
|
|
local interval = self.detectinterval -- e.g. 30
|
|
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
|
|
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6
|
|
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)
|
|
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})
|
|
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})
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function to execute the relocation
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:_RelocateGroups()
|
|
self:T(self.lid .. "RelocateGroups")
|
|
local text = self.lid.." Relocating Groups"
|
|
local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
|
|
if self.verbose then self:I(text) end
|
|
if self.autorelocate then
|
|
-- relocate HQ
|
|
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")
|
|
local text = self.lid.." Relocating HQ"
|
|
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
|
|
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
|
|
end
|
|
--relocate EWR
|
|
-- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach
|
|
if self.autorelocateunits.EWR then
|
|
-- get EWR Group
|
|
local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
|
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())
|
|
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
|
|
_grp:RelocateGroundRandomInRadius(20,500,true,true)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function to check accept and reject zones
|
|
-- @param #MANTIS self
|
|
-- @param Core.Point#COORDINATE coord The coordinate to check
|
|
-- @return #boolean outcome
|
|
function MANTIS:_CheckCoordinateInZones(coord)
|
|
-- DEBUG
|
|
self:T(self.lid.."_CheckCoordinateInZones")
|
|
local inzone = false
|
|
-- acceptzones
|
|
if #self.AcceptZones > 0 then
|
|
for _,_zone in pairs(self.AcceptZones) do
|
|
local zone = _zone -- Core.Zone#ZONE
|
|
if zone:IsCoordinateInZone(coord) then
|
|
inzone = true
|
|
self:T(self.lid.."Target coord in Accept Zone!")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- rejectzones
|
|
if #self.RejectZones > 0 and inzone then -- maybe in accept zone, but check the overlaps
|
|
for _,_zone in pairs(self.RejectZones) do
|
|
local zone = _zone -- Core.Zone#ZONE
|
|
if zone:IsCoordinateInZone(coord) then
|
|
inzone = false
|
|
self:T(self.lid.."Target coord in Reject Zone!")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- conflictzones
|
|
if #self.ConflictZones > 0 and not inzone then -- if not already accepted, might be in conflict zones
|
|
for _,_zone in pairs(self.ConflictZones) do
|
|
local zone = _zone -- Core.Zone#ZONE
|
|
if zone:IsCoordinateInZone(coord) then
|
|
inzone = true
|
|
self:T(self.lid.."Target coord in Conflict Zone!")
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return inzone
|
|
end
|
|
|
|
--- [Internal] Function to prefilter height based
|
|
-- @param #MANTIS self
|
|
-- @param #number height
|
|
-- @return #table set
|
|
function MANTIS:_PreFilterHeight(height)
|
|
self:T(self.lid.."_PreFilterHeight")
|
|
local set = {}
|
|
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
|
|
local detectedgroups = dlink:GetContactTable()
|
|
for _,_contact in pairs(detectedgroups) do
|
|
local contact = _contact -- Ops.Intelligence#INTEL.Contact
|
|
local grp = contact.group -- Wrapper.Group#GROUP
|
|
if grp:IsAlive() then
|
|
if grp:GetHeight(true) < height then
|
|
local coord = grp:GetCoordinate()
|
|
table.insert(set,coord)
|
|
end
|
|
end
|
|
end
|
|
return set
|
|
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
|
|
-- @param Core.Point#COORDINATE samcoordinate Coordinate object.
|
|
-- @param #number radius Radius to check.
|
|
-- @param #number height Height to check.
|
|
-- @param #boolean dlink Data from DLINK.
|
|
-- @return #boolean True if in any zone, else false
|
|
-- @return #number Distance Target distance in meters or zero when no object is in zone
|
|
function MANTIS:_CheckObjectInZone(dectset, samcoordinate, radius, height, dlink)
|
|
self:T(self.lid.."_CheckObjectInZone")
|
|
-- check if non of the coordinate is in the given defense zone
|
|
local rad = radius or self.checkradius
|
|
local set = dectset
|
|
if dlink then
|
|
-- DEBUG
|
|
set = self:_PreFilterHeight(height)
|
|
end
|
|
for _,_coord in pairs (set) do
|
|
local coord = _coord -- get current coord to check
|
|
-- output for cross-check
|
|
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
|
if not targetdistance then
|
|
targetdistance = samcoordinate:Get2DDistance(coord)
|
|
end
|
|
-- check accept/reject zones
|
|
local zonecheck = true
|
|
if self.usezones then
|
|
-- DONE
|
|
zonecheck = self:_CheckCoordinateInZones(coord)
|
|
end
|
|
if self.verbose and self.debug then
|
|
local dectstring = coord:ToStringLLDMS()
|
|
local samstring = samcoordinate:ToStringLLDMS()
|
|
local inrange = "false"
|
|
if targetdistance <= rad then
|
|
inrange = "true"
|
|
end
|
|
local text = string.format("Checking SAM at %s | Targetdist %d | Rad %d | Inrange %s", samstring, targetdistance, rad, inrange)
|
|
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
|
self:T(self.lid..text)
|
|
end
|
|
-- end output to cross-check
|
|
if targetdistance <= rad and zonecheck then
|
|
return true, targetdistance
|
|
end
|
|
end
|
|
return false, 0
|
|
end
|
|
|
|
--- [Internal] Function to start the detection via EWR groups - if INTEL isn\'t available
|
|
-- @param #MANTIS self
|
|
-- @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 20
|
|
|
|
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) -- deprecated - in range of SAMs is used anyway
|
|
MANTISdetection:SetRefreshTimeInterval(interval)
|
|
MANTISdetection:__Start(2)
|
|
|
|
return MANTISdetection
|
|
end
|
|
|
|
--- [Internal] Function to start the detection with INTEL via EWR groups
|
|
-- @param #MANTIS self
|
|
-- @return Ops.Intel#INTEL_DLINK The running detection set
|
|
function MANTIS:StartIntelDetection()
|
|
self:T(self.lid.."Starting Intel Detection")
|
|
-- DEBUG
|
|
-- start detection
|
|
local groupset = self.EWR_Group
|
|
local samset = self.SAM_Group
|
|
|
|
self.intelset = {}
|
|
|
|
local IntelOne = INTEL:New(groupset,self.Coalition,self.name.." IntelOne")
|
|
--IntelOne:SetClusterAnalysis(true,true)
|
|
--IntelOne:SetClusterRadius(5000)
|
|
IntelOne:Start()
|
|
|
|
local IntelTwo = INTEL:New(samset,self.Coalition,self.name.." IntelTwo")
|
|
--IntelTwo:SetClusterAnalysis(true,true)
|
|
--IntelTwo:SetClusterRadius(5000)
|
|
IntelTwo:Start()
|
|
|
|
local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,300)
|
|
IntelDlink:__Start(1)
|
|
|
|
self:SetUsingDLink(IntelDlink)
|
|
|
|
table.insert(self.intelset, IntelOne)
|
|
table.insert(self.intelset, IntelTwo)
|
|
|
|
return IntelDlink
|
|
end
|
|
|
|
--- [Internal] Function to start the detection via AWACS if defined as separate (classic)
|
|
-- @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()
|
|
|
|
return MANTISAwacs
|
|
end
|
|
|
|
--- [Internal] Function to get SAM firing data from units types.
|
|
-- @param #MANTIS self
|
|
-- @param #string grpname Name of the group
|
|
-- @param #boolean mod HDS mod flag
|
|
-- @return #number range Max firing range
|
|
-- @return #number height Max firing height
|
|
-- @return #string type Long, medium or short range
|
|
-- @return #number blind "blind" spot
|
|
function MANTIS:_GetSAMDataFromUnits(grpname,mod)
|
|
self:T(self.lid.."_GetSAMRangeFromUnits")
|
|
local found = false
|
|
local range = self.checkradius
|
|
local height = 3000
|
|
local type = MANTIS.SamType.MEDIUM
|
|
local radiusscale = self.radiusscale[type]
|
|
local blind = 0
|
|
local group = GROUP:FindByName(grpname) -- Wrapper.Group#GROUP
|
|
local units = group:GetUnits()
|
|
local SAMData = self.SamData
|
|
if mod then
|
|
SAMData = self.SamDataHDS
|
|
end
|
|
--self:I("Looking to auto-match for "..grpname)
|
|
for _,_unit in pairs(units) do
|
|
local unit = _unit -- Wrapper.Unit#UNIT
|
|
local type = string.lower(unit:GetTypeName())
|
|
--self:I(string.format("Matching typename: %s",type))
|
|
for idx,entry in pairs(SAMData) do
|
|
local _entry = entry -- #MANTIS.SamData
|
|
local _radar = string.lower(_entry.Radar)
|
|
--self:I(string.format("Trying typename: %s",_radar))
|
|
if string.find(type,_radar,1,true) then
|
|
type = _entry.Type
|
|
radiusscale = self.radiusscale[type]
|
|
range = _entry.Range * 1000 * radiusscale -- max firing range used as switch-on
|
|
height = _entry.Height * 1000 -- max firing height
|
|
blind = _entry.Blindspot * 100 -- blind spot range
|
|
--self:I(string.format("Match: %s - %s",_radar,type))
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
if found then break end
|
|
end
|
|
if not found then
|
|
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
|
end
|
|
return range, height, type, blind
|
|
end
|
|
|
|
--- [Internal] Function to get SAM firing data
|
|
-- @param #MANTIS self
|
|
-- @param #string grpname Name of the group
|
|
-- @return #number range Max firing range
|
|
-- @return #number height Max firing height
|
|
-- @return #string type Long, medium or short range
|
|
-- @return #number blind "blind" spot
|
|
function MANTIS:_GetSAMRange(grpname)
|
|
self:T(self.lid.."_GetSAMRange")
|
|
local range = self.checkradius
|
|
local height = 3000
|
|
local type = MANTIS.SamType.MEDIUM
|
|
local radiusscale = self.radiusscale[type]
|
|
local blind = 0
|
|
local found = false
|
|
local HDSmod = false
|
|
if string.find(grpname,"HDS",1,true) then
|
|
HDSmod = true
|
|
end
|
|
if self.automode then
|
|
for idx,entry in pairs(self.SamData) do
|
|
--self:I("ID = " .. idx)
|
|
if string.find(grpname,idx,1,true) then
|
|
local _entry = entry -- #MANTIS.SamData
|
|
type = _entry.Type
|
|
radiusscale = self.radiusscale[type]
|
|
range = _entry.Range * 1000 * radiusscale -- max firing range
|
|
height = _entry.Height * 1000 -- max firing height
|
|
blind = _entry.Blindspot
|
|
--self:I("Matching Groupname = " .. grpname .. " Range= " .. range)
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- secondary filter if not found
|
|
if (not found and self.automode) or HDSmod then
|
|
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod)
|
|
elseif not found then
|
|
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
|
end
|
|
return range, height, type, blind
|
|
end
|
|
|
|
--- [Internal] Function to set the SAM start state
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:SetSAMStartState()
|
|
-- DONE: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406
|
|
-- DONE: Auto mode
|
|
self:T(self.lid.."Setting SAM Start States")
|
|
-- get SAM Group
|
|
local SAM_SET = self.SAM_Group
|
|
local SAM_Grps = SAM_SET.Set --table of objects
|
|
local SAM_Tbl = {} -- table of SAM defense zones
|
|
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
|
|
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
|
|
local SAM_Tbl_sh = {} -- table of short range SAM defense zones
|
|
local SEAD_Grps = {} -- table of SAM names to make evasive
|
|
local engagerange = self.engagerange -- firing range in % of max
|
|
--cycle through groups and set alarm state etc
|
|
for _i,_group in pairs (SAM_Grps) do
|
|
if _group:IsGround() and _group:IsAlive() then
|
|
local group = _group -- Wrapper.Group#GROUP
|
|
-- DONE: add emissions on/off
|
|
if self.UseEmOnOff then
|
|
group:OptionAlarmStateRed()
|
|
group:EnableEmission(false)
|
|
--group:SetAIOff()
|
|
else
|
|
group:OptionAlarmStateGreen() -- AI off
|
|
end
|
|
group:OptionEngageRange(engagerange) --default engagement will be 95% of firing range
|
|
local grpname = group:GetName()
|
|
local grpcoord = group:GetCoordinate()
|
|
local grprange,grpheight,type,blind = self:_GetSAMRange(grpname)
|
|
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind})
|
|
--table.insert( SEAD_Grps, grpname )
|
|
if type == MANTIS.SamType.LONG then
|
|
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
|
|
table.insert( SEAD_Grps, grpname )
|
|
--self:T("SAM "..grpname.." is type LONG")
|
|
elseif type == MANTIS.SamType.MEDIUM then
|
|
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
|
|
table.insert( SEAD_Grps, grpname )
|
|
--self:T("SAM "..grpname.." is type MEDIUM")
|
|
elseif type == MANTIS.SamType.SHORT then
|
|
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
|
|
--self:T("SAM "..grpname.." is type SHORT")
|
|
self.ShoradGroupSet:Add(grpname,group)
|
|
if not self.autoshorad then
|
|
table.insert( SEAD_Grps, grpname )
|
|
end
|
|
end
|
|
self.SamStateTracker[grpname] = "GREEN"
|
|
end
|
|
end
|
|
self.SAM_Table = SAM_Tbl
|
|
self.SAM_Table_Long = SAM_Tbl_lg
|
|
self.SAM_Table_Medium = SAM_Tbl_md
|
|
self.SAM_Table_Short = SAM_Tbl_sh
|
|
-- make SAMs evasive
|
|
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
|
|
mysead:SetEngagementRange(engagerange)
|
|
mysead:AddCallBack(self)
|
|
if self.UseEmOnOff then
|
|
mysead:SwitchEmissions(true)
|
|
end
|
|
self.mysead = mysead
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function to update SAM table and SEAD state
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:_RefreshSAMTable()
|
|
self:T(self.lid.."RefreshSAMTable")
|
|
-- Requires SEAD 0.2.2 or better
|
|
-- get SAM Group
|
|
local SAM_SET = self.SAM_Group
|
|
local SAM_Grps = SAM_SET.Set --table of objects
|
|
local SAM_Tbl = {} -- table of SAM defense zones
|
|
local SAM_Tbl_lg = {} -- table of long range SAM defense zones
|
|
local SAM_Tbl_md = {} -- table of mid range SAM defense zones
|
|
local SAM_Tbl_sh = {} -- table of short range SAM defense zon
|
|
local SEAD_Grps = {} -- table of SAM names to make evasive
|
|
local engagerange = self.engagerange -- firing range in % of max
|
|
--cycle through groups and set alarm state etc
|
|
for _i,_group in pairs (SAM_Grps) do
|
|
local group = _group -- Wrapper.Group#GROUP
|
|
group:OptionEngageRange(engagerange) --engagement will be 95% of firing range
|
|
if group:IsGround() and group:IsAlive() then
|
|
local grpname = group:GetName()
|
|
local grpcoord = group:GetCoordinate()
|
|
local grprange, grpheight,type,blind = self:_GetSAMRange(grpname)
|
|
table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind}) -- make the table lighter, as I don't really use the zone here
|
|
table.insert( SEAD_Grps, grpname )
|
|
if type == MANTIS.SamType.LONG then
|
|
table.insert( SAM_Tbl_lg, {grpname, grpcoord, grprange, grpheight, blind})
|
|
--self:I({grpname,grprange, grpheight})
|
|
elseif type == MANTIS.SamType.MEDIUM then
|
|
table.insert( SAM_Tbl_md, {grpname, grpcoord, grprange, grpheight, blind})
|
|
--self:I({grpname,grprange, grpheight})
|
|
elseif type == MANTIS.SamType.SHORT then
|
|
table.insert( SAM_Tbl_sh, {grpname, grpcoord, grprange, grpheight, blind})
|
|
-- self:I({grpname,grprange, grpheight})
|
|
self.ShoradGroupSet:Add(grpname,group)
|
|
if self.autoshorad then
|
|
self.Shorad.Groupset = self.ShoradGroupSet
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.SAM_Table = SAM_Tbl
|
|
self.SAM_Table_Long = SAM_Tbl_lg
|
|
self.SAM_Table_Medium = SAM_Tbl_md
|
|
self.SAM_Table_Short = SAM_Tbl_sh
|
|
-- make SAMs evasive
|
|
if self.mysead ~= nil then
|
|
local mysead = self.mysead
|
|
mysead:UpdateSet( SEAD_Grps )
|
|
end
|
|
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)
|
|
self:T(self.lid.."AddShorad")
|
|
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
|
|
return self
|
|
end
|
|
|
|
--- Function to unlink #MANTIS from a #SHORAD installation
|
|
-- @param #MANTIS self
|
|
function MANTIS:RemoveShorad()
|
|
self:T(self.lid.."RemoveShorad")
|
|
self.ShoradLink = false
|
|
return self
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- MANTIS main functions
|
|
-----------------------------------------------------------------------
|
|
|
|
--- [Internal] Check detection function
|
|
-- @param #MANTIS self
|
|
-- @param #table samset Table of SAM data
|
|
-- @param #table detset Table of COORDINATES
|
|
-- @param #boolean dlink Using DLINK
|
|
-- @param #number limit of SAM sites to go active on a contact
|
|
-- @return #MANTIS self
|
|
function MANTIS:_CheckLoop(samset,detset,dlink,limit)
|
|
self:T(self.lid .. "CheckLoop " .. #detset .. " Coordinates")
|
|
local switchedon = 0
|
|
for _,_data in pairs (samset) do
|
|
local samcoordinate = _data[2]
|
|
local name = _data[1]
|
|
local radius = _data[3]
|
|
local height = _data[4]
|
|
local blind = _data[5] * 1.25 + 1
|
|
local samgroup = GROUP:FindByName(name)
|
|
local IsInZone, Distance = self:_CheckObjectInZone(detset, samcoordinate, radius, height, dlink)
|
|
local suppressed = self.SuppressedGroups[name] or false
|
|
local activeshorad = self.Shorad.ActiveGroups[name] or false
|
|
if IsInZone and not suppressed and not activeshorad then --check any target in zone and not currently managed by SEAD
|
|
if samgroup:IsAlive() then
|
|
-- switch on SAM
|
|
local switch = false
|
|
if self.UseEmOnOff and switchedon < limit then
|
|
-- DONE: add emissions on/off
|
|
samgroup:EnableEmission(true)
|
|
switchedon = switchedon + 1
|
|
switch = true
|
|
elseif (not self.UseEmOnOff) and switchedon < limit then
|
|
samgroup:OptionAlarmStateRed()
|
|
switchedon = switchedon + 1
|
|
switch = true
|
|
end
|
|
if self.SamStateTracker[name] ~= "RED" and switch then
|
|
self:__RedState(1,samgroup)
|
|
self.SamStateTracker[name] = "RED"
|
|
end
|
|
-- link in to SHORAD if available
|
|
-- DONE: Test integration fully
|
|
if self.ShoradLink and (Distance < self.ShoradActDistance or Distance < blind ) then -- don't give SHORAD position away too early
|
|
local Shorad = self.Shorad
|
|
local radius = self.checkradius
|
|
local ontime = self.ShoradTime
|
|
Shorad:WakeUpShorad(name, radius, ontime)
|
|
self:__ShoradActivated(1,name, radius, ontime)
|
|
end
|
|
-- debug output
|
|
if (self.debug or self.verbose) and switch then
|
|
local text = string.format("SAM %s in alarm state RED!", name)
|
|
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
|
if self.verbose then self:I(self.lid..text) end
|
|
end
|
|
end --end alive
|
|
else
|
|
if samgroup:IsAlive() and not suppressed and not activeshorad then
|
|
-- switch off SAM
|
|
if self.UseEmOnOff then
|
|
samgroup:EnableEmission(false)
|
|
else
|
|
samgroup:OptionAlarmStateGreen()
|
|
end
|
|
if self.SamStateTracker[name] ~= "GREEN" then
|
|
self:__GreenState(1,samgroup)
|
|
self.SamStateTracker[name] = "GREEN"
|
|
end
|
|
if self.debug or self.verbose then
|
|
local text = string.format("SAM %s in alarm state GREEN!", name)
|
|
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
|
if self.verbose then self:I(self.lid..text) end
|
|
end
|
|
end --end alive
|
|
end --end check
|
|
end --for for loop
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Check detection function
|
|
-- @param #MANTIS self
|
|
-- @param Functional.Detection#DETECTION_AREAS detection Detection object
|
|
-- @param #boolean dlink
|
|
-- @return #MANTIS self
|
|
function MANTIS:_Check(detection,dlink)
|
|
self:T(self.lid .. "Check")
|
|
--get detected set
|
|
local detset = detection:GetDetectedItemCoordinates()
|
|
--self:T("Check:", {detset})
|
|
-- randomly update SAM Table
|
|
local rand = math.random(1,100)
|
|
if rand > 65 then -- 1/3 of cases
|
|
self:_RefreshSAMTable()
|
|
end
|
|
-- switch SAMs on/off if (n)one of the detected groups is inside their reach
|
|
if self.automode then
|
|
local samset = self.SAM_Table_Long -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
|
self:_CheckLoop(samset,detset,dlink,self.maxlongrange)
|
|
local samset = self.SAM_Table_Medium -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
|
self:_CheckLoop(samset,detset,dlink,self.maxmidrange)
|
|
local samset = self.SAM_Table_Short -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
|
self:_CheckLoop(samset,detset,dlink,self.maxshortrange)
|
|
else
|
|
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height
|
|
self:_CheckLoop(samset,detset,dlink,self.maxclassic)
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Relocation relay function
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:_Relocate()
|
|
self:T(self.lid .. "Relocate")
|
|
self:_RelocateGroups()
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Check advanced state
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:_CheckAdvState()
|
|
self:T(self.lid .. "CheckAdvSate")
|
|
local interval, oldstate = self:_CalcAdvState()
|
|
local newstate = self.adv_state
|
|
if newstate ~= oldstate then
|
|
-- deal with new state
|
|
self:__AdvStateChange(1,oldstate,newstate,interval)
|
|
if newstate == 2 then
|
|
-- switch alarm state RED
|
|
self.state2flag = true
|
|
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
|
for _,_data in pairs (samset) do
|
|
local name = _data[1]
|
|
local samgroup = GROUP:FindByName(name)
|
|
if samgroup:IsAlive() then
|
|
if self.UseEmOnOff then
|
|
-- DONE: add emissions on/off
|
|
--samgroup:SetAIOn()
|
|
samgroup:EnableEmission(true)
|
|
else
|
|
samgroup:OptionAlarmStateRed()
|
|
end
|
|
end -- end alive
|
|
end -- end for loop
|
|
elseif newstate <= 1 then
|
|
-- change MantisTimer to slow down or speed up
|
|
self.detectinterval = interval
|
|
self.state2flag = false
|
|
end
|
|
end -- end newstate vs oldstate
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Check DLink state
|
|
-- @param #MANTIS self
|
|
-- @return #MANTIS self
|
|
function MANTIS:_CheckDLinkState()
|
|
self:T(self.lid .. "_CheckDLinkState")
|
|
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
|
|
local TS = timer.getAbsTime()
|
|
if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then
|
|
self.DLink = false
|
|
self.Detection = self:StartDetection() -- fall back
|
|
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
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterStart(From, Event, To)
|
|
self:T({From, Event, To})
|
|
self:T(self.lid.."Starting MANTIS")
|
|
self:SetSAMStartState()
|
|
if not INTEL then
|
|
self.Detection = self:StartDetection()
|
|
else
|
|
self.Detection = self:StartIntelDetection()
|
|
end
|
|
--[[
|
|
if self.advAwacs and not self.automode then
|
|
self.AWACS_Detection = self:StartAwacsDetection()
|
|
end
|
|
--]]
|
|
if self.autoshorad then
|
|
self.Shorad = SHORAD:New(self.name.."-SHORAD",self.name.."-SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
|
|
self.Shorad:SetDefenseLimits(80,95)
|
|
self.ShoradLink = true
|
|
self.Shorad.Groupset=self.ShoradGroupSet
|
|
end
|
|
self:__Status(-math.random(1,10))
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Before status function for MANTIS
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @return #MANTIS self
|
|
function MANTIS:onbeforeStatus(From, Event, To)
|
|
self:T({From, Event, To})
|
|
-- check detection
|
|
if not self.state2flag then
|
|
self:_Check(self.Detection,self.DLink)
|
|
end
|
|
|
|
--[[ check Awacs
|
|
if self.advAwacs and not self.state2flag then
|
|
self:_Check(self.AWACS_Detection,false)
|
|
end
|
|
--]]
|
|
|
|
-- relocate HQ and EWR
|
|
if self.autorelocate then
|
|
local relointerval = self.relointerval
|
|
local thistime = timer.getAbsTime()
|
|
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
|
|
|
|
--- [Internal] Status function for MANTIS
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterStatus(From,Event,To)
|
|
self:T({From, Event, To})
|
|
-- Display some states
|
|
if self.debug and self.verbose then
|
|
self:I(self.lid .. "Status Report")
|
|
for _name,_state in pairs(self.SamStateTracker) do
|
|
self:I(string.format("Site %s\tStatus %s",_name,_state))
|
|
end
|
|
end
|
|
local interval = self.detectinterval * -1
|
|
self:__Status(interval)
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function to stop MANTIS
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterStop(From, Event, To)
|
|
self:T({From, Event, To})
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event Relocating
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterRelocating(From, Event, To)
|
|
self:T({From, Event, To})
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event GreenState
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterGreenState(From, Event, To, Group)
|
|
self:T({From, Event, To, Group:GetName()})
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event RedState
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed
|
|
-- @return #MANTIS self
|
|
function MANTIS:onafterRedState(From, Event, To, Group)
|
|
self:T({From, Event, To, Group:GetName()})
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event AdvStateChange
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red
|
|
-- @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
|
|
function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval)
|
|
self:T({From, Event, To, Oldstate, Newstate, Interval})
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event ShoradActivated
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param #string Name Name of the GROUP which SHORAD shall protect
|
|
-- @param #number Radius Radius around the named group to find SHORAD groups
|
|
-- @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
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event SeadSuppressionStart
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
-- @param Wrapper.Group#GROUP Attacker The attacking GROUP object
|
|
function MANTIS:onafterSeadSuppressionStart(From, Event, To, Group, Name, Attacker)
|
|
self:T({From, Event, To, Name})
|
|
self.SuppressedGroups[Name] = true
|
|
if self.ShoradLink then
|
|
local Shorad = self.Shorad
|
|
local radius = self.checkradius
|
|
local ontime = self.ShoradTime
|
|
Shorad:WakeUpShorad(Name, radius, ontime)
|
|
self:__ShoradActivated(1,Name, radius, ontime)
|
|
end
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event SeadSuppressionEnd
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
function MANTIS:onafterSeadSuppressionEnd(From, Event, To, Group, Name)
|
|
self:T({From, Event, To, Name})
|
|
self.SuppressedGroups[Name] = false
|
|
return self
|
|
end
|
|
|
|
--- [Internal] Function triggered by Event SeadSuppressionPlanned
|
|
-- @param #MANTIS self
|
|
-- @param #string From The From State
|
|
-- @param #string Event The Event
|
|
-- @param #string To The To State
|
|
-- @param Wrapper.Group#GROUP Group The suppressed GROUP object
|
|
-- @param #string Name Name of the suppressed group
|
|
-- @param #number SuppressionStartTime Model start time of the suppression from `timer.getTime()`
|
|
-- @param #number SuppressionEndTime Model end time of the suppression from `timer.getTime()`
|
|
-- @param Wrapper.Group#GROUP Attacker The attacking GROUP object
|
|
function MANTIS:onafterSeadSuppressionPlanned(From, Event, To, Group, Name, SuppressionStartTime, SuppressionEndTime, Attacker)
|
|
self:T({From, Event, To, Name})
|
|
return self
|
|
end
|
|
|
|
end
|
|
-----------------------------------------------------------------------
|
|
-- MANTIS end
|
|
-----------------------------------------------------------------------
|