diff --git a/Moose Development/Moose/AI/AI_Air_Engage.lua b/Moose Development/Moose/AI/AI_Air_Engage.lua index 2679d472d..03a693ddf 100644 --- a/Moose Development/Moose/AI/AI_Air_Engage.lua +++ b/Moose Development/Moose/AI/AI_Air_Engage.lua @@ -418,6 +418,7 @@ end -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. +-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked. function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit ) self:I( { DefenderGroup, From, Event, To, AttackSetUnit } ) @@ -425,7 +426,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air! - local AttackCount = AttackSetUnit:Count() + local AttackCount = AttackSetUnit:CountAlive() if AttackCount > 0 then @@ -510,6 +511,7 @@ end -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. +-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked. function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit ) self:F( { DefenderGroup, From, Event, To, AttackSetUnit} ) @@ -517,8 +519,8 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air! - local AttackCount = AttackSetUnit:Count() - self:I({AttackCount = AttackCount}) + local AttackCount = AttackSetUnit:CountAlive()() + self:T({AttackCount = AttackCount}) if AttackCount > 0 then diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 75bd2c96f..698b1c8fb 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -1,23 +1,27 @@ ------------------------------------------------------------------------ --- MANTIS System ------------------------------------------------------------------------ --- ---- **MANTIS** - Moose derived *M*odular, *A*utomatic and *N*etwork capable *T*argeting and *I*nterception *S*ystem +--- **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 +-- **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: -- --- ### Authors : **applevangelist ** +-- ### [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 2020 --- + ------------------------------------------------------------------------- --- **MANTIS** class, extends @{Core.Base#BASE} -- @type MANTIS @@ -28,17 +32,21 @@ -- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group -- @field @{Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET -- @field #string SEAD_Template_CC The ME name of the HQ object --- @field @{Tasking.CommandCenter#COMMANDCENTER} SEAD_CC The #COMMANDCENTER object +-- @field @{Wrapper.Group#GROUP} SEAD_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 -- @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* -- @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 @@ -48,38 +56,53 @@ -- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy. -- Leverage evasiveness from @{Functional.Sead#SEAD}. -- Leverage attack range setup added by DCS in 11/20. --- @usage +-- -- 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. -- -- Start up your MANTIS -- --- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` --- [optional] Use `MANTIS:SetEWRGrouping(radius)`, `MANTIS:SetEWRRange(radius)`, `MANTIS:SetSAMRadius(radius)`, `MANTIS:SetDetectInterval(interval)` to fine-tune your setup. --- --- `myredmantis:Start()` +-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` +-- +-- [optional] Use +-- * `MANTIS:SetEWRGrouping(radius)` +-- * `MANTIS:SetEWRRange(radius)` +-- * `MANTIS:SetSAMRadius(radius)` +-- * `MANTIS:SetDetectInterval(interval)` +-- * `MANTIS:SetAutoRelocate(hq, ewr)` +-- to fine-tune your setup. -- +-- `myredmantis:Start()` +-- +-- -- @field #MANTIS MANTIS = { - ClassName = "MANTIS", - name = "mymantis", - SAM_Templates_Prefix = "", - SAM_Group = nil, - EWR_Templates_Prefix = "", - EWR_Group = nil, - SEAD_Template_CC = "", - SEAD_CC = nil, - SAM_Table = {}, - lid = "", - Detection = nil, - debug = false, - checkradius = 25000, - grouping = 5000, - acceptrange = 80000, - detectinterval = 30 + ClassName = "MANTIS", + name = "mymantis", + SAM_Templates_Prefix = "", + SAM_Group = nil, + EWR_Templates_Prefix = "", + EWR_Group = nil, + SEAD_Template_CC = "", + SEAD_CC = nil, + SAM_Table = {}, + lid = "", + Detection = nil, + debug = false, + checkradius = 25000, + grouping = 5000, + acceptrange = 80000, + detectinterval = 30, + engagerange = 75, + autorelocate = false, + verbose = false } +----------------------------------------------------------------------- +-- MANTIS System +----------------------------------------------------------------------- + do --- Function instantiate new class --@param #MANTIS self @@ -88,11 +111,11 @@ do --@param #string ewrprefix Prefixes for the EWR and AWACS 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 filer once (false, default) + --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) --@return #MANTIS self function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic) - -- DONE: Create user functions for these + -- TODO: Create some user functions for these -- TODO: Make HQ useful -- TODO: Set SAMs to auto if EWR dies @@ -107,12 +130,16 @@ do self.grouping = 5000 self.acceptrange = 80000 self.detectinterval = 30 + self.engagerange = 75 + self.autorelocate = false + self.autorelocateunits = { HQ = false, EWR = false} + self.verbose = false -- @field #string version - self.version="0.2.1" + self.version="0.2.5" env.info(string.format("***** Starting MANTIS Version %s *****", self.version)) - -- Set some string id for output to DCS.log file. + -- Set the string id for output to DCS.log file. self.lid=string.format("MANTIS %s | ", self.name) -- Debug trace. @@ -124,20 +151,21 @@ do end if self.dynamic then - -- get SAM SET_GROUP + -- Set SAM SET_GROUP self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() - -- get EWR SET_GROUP + -- Set EWR SET_GROUP self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterStart() else - -- get SAM SET_GROUP + -- Set SAM SET_GROUP self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce() - -- get EWR SET_GROUP + -- 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.SEAD_Template_CC then - self.SEAD_CC = COMMANDCENTER:New(GROUP:FindByName(self.SEAD_Template_CC),self.SEAD_Template_CC) + self.SEAD_CC = GROUP:FindByName(self.SEAD_Template_CC) + --self.SEAD_CC = COMMANDCENTER:New(GROUP:FindByName(self.SEAD_Template_CC),self.SEAD_Template_CC) end -- Inherit everything from BASE class. local self = BASE:Inherit(self, BASE:New()) -- #MANTIS @@ -188,13 +216,35 @@ do self.checkradius = radius 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 + function MANTIS:SetSAMRadius(range) + local range = range or 75 + if range < 0 or range > 100 then + range = 75 + end + self.engagerange = range + 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) local onoff = onoff or false self.debug = onoff - end + 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() + if self.SEAD_CC then + return self.SEAD_CC + else + return nil + end + end --- Function to set the detection interval -- @param #MANTIS self @@ -202,9 +252,59 @@ do function MANTIS:SetDetectInterval(interval) local interval = interval or 30 self.detectinterval = interval + 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:F({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 end + + --- [Internal] Function to execute the relocation + -- @param #MANTIS self + function MANTIS:_RelocateGroups() + self:T(self.lid.." Relocating Groups") + local text = self.lid.." Relocating Groups" + local m= MESSAGE:New(text,15,"MANTIS",true):ToAllIf(self.debug) + if self.verbose then env.info(text) end + if self.autorelocate then + -- relocate HQ + if self.autorelocateunits.HQ and self.SEAD_CC then --only relocate if HQ exists + local _hqgrp = self.SEAD_CC + self:T(self.lid.." Relocating HQ") + local text = self.lid.." Relocating HQ" + local m= MESSAGE:New(text,15,"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:IsGround() then + self:T(self.lid.." Relocating EWR ".._grp:GetName()) + local text = self.lid.." Relocating EWR ".._grp:GetName() + local m= MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(text) end + _grp:RelocateGroundRandomInRadius(20,500,true,true) + end + end + end + end + end - --- Function to check if no object is in the given SAM zone + --- 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 samcoordinate Core.Point#COORDINATE Coordinate object. @@ -222,6 +322,7 @@ do local targetdistance = samcoordinate:DistanceFromPointVec2(coord) 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) + if self.verbose then env.info(self.lid..text) end -- end output to cross-check if targetdistance <= radius then return true @@ -237,12 +338,13 @@ do self:F(self.lid.."Starting Detection") -- start detection - groupset = self.EWR_Group + local groupset = self.EWR_Group local grouping = self.grouping or 5000 local acceptrange = self.acceptrange or 80000 local interval = self.detectinterval or 60 - _MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --group detected objects to 5000m zones + --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object + _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) @@ -250,10 +352,11 @@ do function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem) --BASE:I( { From, Event, To, DetectedItem }) - if DetectedItem.IsDetected then + local debug = false + if DetectedItem.IsDetected and debug then local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS() - m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) end end return _MANTISdetection @@ -269,6 +372,7 @@ do local SAM_Grps = SAM_SET.Set --table of objects local SAM_Tbl = {} -- table of 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 --for i=1,#SAM_Grps do @@ -276,7 +380,7 @@ do group:OptionAlarmStateGreen() --group:OptionROEHoldFire() --group:SetAIOn() - group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,75) --engagement will be 75% of firing range + group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range if group:IsGround() then local grpname = group:GetName() local grpcoord = group:GetCoordinate() @@ -307,15 +411,6 @@ do --get detected set local detset = detection:GetDetectedItemCoordinates() self:F("Check:", {detset}) - --[[report detections - if self.debug then - for _,_data in pairs (detset) do - local coord = _data - local text = "Target detect at " - text = text..coord:ToStringLLDMS() - m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) - end --end for - end --end if ]] -- switch SAMs on/off if (n)one of the detected groups is inside their reach local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates and i.3=zones of SAM sites for _,_data in pairs (samset) do @@ -328,8 +423,9 @@ do samgroup:OptionAlarmStateRed() --samgroup:OptionROEWeaponFree() --samgroup:SetAIOn() - text = string.format("SAM %s switched to alarm state RED!", name) - m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + local text = string.format("SAM %s switched to alarm state RED!", name) + local m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(self.lid..text) end end --end alive else if samgroup:IsAlive() then @@ -337,16 +433,25 @@ do samgroup:OptionAlarmStateGreen() --samgroup:OptionROEWeaponFree() --samgroup:SetAIOn() - text = string.format("SAM %s switched to alarm state GREEN!", name) - m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + local text = string.format("SAM %s switched to alarm state GREEN!", name) + local m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(self.lid..text) end end --end alive end --end check end --for for loop end --end function + -- relocation relay function + local function relocate() + self:_RelocateGroups() + end -- timer to run the system local interval = self.detectinterval self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) + -- relocate HQ and EWR + local relointerval = math.random(1800,3600) -- random between 30 and 60 mins + self.MantisReloTimer = TIMER:New(relocate) + self.MantisReloTimer:Start(relointerval,relointerval,nil) return self end @@ -357,6 +462,9 @@ do if self.MantisTimer then self.MantisTimer:Stop() end + if self.MantisReloTimer then + self.MantisReloTimer:Stop() + end return self end diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index d71f76d0e..1d64e7e22 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -2387,7 +2387,7 @@ function FLIGHTGROUP:onafterEngageTargets(From, Event, To, TargetUnitSet) local DCSTasks={} - for _,_unit in paris(TargetUnitSet:GetSet()) do + for _,_unit in pairs(TargetUnitSet:GetSet()) do --detected by =HRP= Zero local unit=_unit --Wrapper.Unit#UNIT local task=self.group:TaskAttackUnit(unit, true) table.insert(DCSTasks) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 928d6029d..59a1db182 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -269,31 +269,31 @@ AIRBASE.Normandy = { -- * AIRBASE.PersianGulf.Tunb_Kochak -- @field PersianGulf AIRBASE.PersianGulf = { - ["Abu_Dhabi_International_Airport"] = "Abu Dhabi International Airport", - ["Abu_Musa_Island_Airport"] = "Abu Musa Island Airport", - ["Al_Ain_International_Airport"] = "Al Ain International Airport", - ["Al_Bateen_Airport"] = "Al-Bateen Airport", - ["Al_Dhafra_AB"] = "Al Dhafra AB", + ["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl", + ["Abu_Musa_Island_Airport"] = "Abu Musa Island", + ["Al_Ain_International_Airport"] = "Al Ain Intl", + ["Al_Bateen_Airport"] = "Al-Bateen", + ["Al_Dhafra_AB"] = "Al Dhafra AFB", ["Al_Maktoum_Intl"] = "Al Maktoum Intl", - ["Al_Minhad_AB"] = "Al Minhad AB", + ["Al_Minhad_AB"] = "Al Minhad AFB", ["Bandar_Abbas_Intl"] = "Bandar Abbas Intl", ["Bandar_Lengeh"] = "Bandar Lengeh", - ["Bandar_e_Jask_airfield"] = "Bandar-e-Jask airfield", + ["Bandar_e_Jask_airfield"] = "Bandar-e-Jask", ["Dubai_Intl"] = "Dubai Intl", ["Fujairah_Intl"] = "Fujairah Intl", ["Havadarya"] = "Havadarya", - ["Jiroft_Airport"] = "Jiroft Airport", - ["Kerman_Airport"] = "Kerman Airport", + ["Jiroft_Airport"] = "Jiroft", + ["Kerman_Airport"] = "Kerman", ["Khasab"] = "Khasab", - ["Kish_International_Airport"] = "Kish International Airport", - ["Lar_Airbase"] = "Lar Airbase", - ["Lavan_Island_Airport"] = "Lavan Island Airport", - ["Liwa_Airbase"] = "Liwa Airbase", + ["Kish_International_Airport"] = "Kish Intl", + ["Lar_Airbase"] = "Lar", + ["Lavan_Island_Airport"] = "Lavan Island", + ["Liwa_Airbase"] = "Liwa AFB", ["Qeshm_Island"] = "Qeshm Island", - ["Ras_Al_Khaimah"] = "Ras Al Khaimah", - ["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel Airport", + ["Ras_Al_Khaimah"] = "Ras Al Khaimah Intl", + ["Sas_Al_Nakheel_Airport"] = "Sas Al Nakheel", ["Sharjah_Intl"] = "Sharjah Intl", - ["Shiraz_International_Airport"] = "Shiraz International Airport", + ["Shiraz_International_Airport"] = "Shiraz Intl", ["Sir_Abu_Nuayr"] = "Sir Abu Nuayr", ["Sirri_Island"] = "Sirri Island", ["Tunb_Island_AFB"] = "Tunb Island AFB", diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 298b24008..2361b7d37 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -129,6 +129,7 @@ -- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route. -- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate. -- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate. +-- * @{CONTROLLABLE.RelocateGroundRandomInRadius}(): Relocate the GROUND controllable to a random point in a given radius. -- -- # 5) Option methods --