diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index ae886d712..2c5abb451 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -1,26 +1,26 @@ --- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- +-- -- === --- +-- -- ## Features: --- +-- -- * When SAM sites are being fired upon, the SAMs will take evasive action will reposition themselves when possible. -- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars. --- +-- -- === --- +-- -- ## Missions: --- +-- -- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion) --- +-- -- === --- +-- -- ### Authors: **FlightControl**, **applevangelist** --- +-- -- Last Update: Feb 2021 --- +-- -- === --- +-- -- @module Functional.Sead -- @image SEAD.JPG @@ -28,24 +28,24 @@ -- @extends Core.Base#BASE --- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- +-- -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. --- +-- -- # Constructor: --- +-- -- Use the @{#SEAD.New}() constructor to create a new SEAD object. --- +-- -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) --- +-- -- @field #SEAD SEAD = { - ClassName = "SEAD", + ClassName = "SEAD", TargetSkill = { Average = { Evade = 30, DelayOn = { 40, 60 } } , Good = { Evade = 20, DelayOn = { 30, 50 } } , High = { Evade = 15, DelayOn = { 20, 40 } } , - Excellent = { Evade = 10, DelayOn = { 10, 30 } } - }, + Excellent = { Evade = 10, DelayOn = { 10, 30 } } + }, SEADGroupPrefixes = {}, SuppressedGroups = {}, EngagementRange = 75 -- default 75% engagement range Feature Request #1355 @@ -84,7 +84,7 @@ SEAD = { ["X_31"] = "X_31", ["Kh25"] = "Kh25", } - + --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. -- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... -- Chances are big that the missile will miss. @@ -99,7 +99,7 @@ function SEAD:New( SEADGroupPrefixes ) local self = BASE:Inherit( self, BASE:New() ) self:F( SEADGroupPrefixes ) - + if type( SEADGroupPrefixes ) == 'table' then for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix @@ -107,7 +107,7 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes end - + self:HandleEvent( EVENTS.Shot ) self:I("*** SEAD - Started Version 0.2.5") return self @@ -120,7 +120,7 @@ end function SEAD:UpdateSet( SEADGroupPrefixes ) self:F( SEADGroupPrefixes ) - + if type( SEADGroupPrefixes ) == 'table' then for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix @@ -174,10 +174,10 @@ function SEAD:OnEventShot( EventData ) self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) self:T({ SEADWeapon }) - + --[[check for SEAD missiles if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired - or + or SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired or SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired @@ -205,7 +205,7 @@ function SEAD:OnEventShot( EventData ) SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired --]] if self:_CheckHarms(SEADWeaponName) then - + local _evade = math.random (1,100) -- random number for chance of evading action local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetMimname = Unit.getName(_targetMim) -- Unit name @@ -222,7 +222,7 @@ function SEAD:OnEventShot( EventData ) self:T( '*** SEAD - Group Found' ) break end - end + end if SEADGroupFound == true then -- yes we are being attacked if _targetskill == "Random" then -- when skill is random, choose a skill local Skills = { "Average", "Good", "High", "Excellent" } @@ -231,42 +231,26 @@ function SEAD:OnEventShot( EventData ) self:T( _targetskill ) if self.TargetSkill[_targetskill] then if (_evade > self.TargetSkill[_targetskill].Evade) then - + self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) ) - + local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimcont= _targetMimgroup:getController() - + routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly - + --tracker ID table to switch groups off and on again - local id = { + local id = { groupName = _targetMimgroup, ctrl = _targetMimcont } - local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2]) - - if SuppressedGroups1[id.groupName] == nil then - - SuppressedGroups1[id.groupName] = { - SuppressionEndTime1 = timer.getTime() + delay1, - SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function - } - - Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20) - end - - local SuppressedGroups = {} - - local function SuppressionEnd(id) + local function SuppressionEnd(id) --switch group back on local range = self.EngagementRange -- Feature Request #1355 - --env.info(string.format("*** SEAD - Engagement Range is %d", range)) + self:T(string.format("*** SEAD - Engagement Range is %d", range)) id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED) id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355 - SuppressedGroups[id.groupName] = nil + self.SuppressedGroups[id.groupName] = nil --delete group id from table when done end -- randomize switch-on time local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 4080b6e32..ca95135eb 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -1,8 +1,11 @@ --- **Ops** - Office of Military Intelligence. -- --- **Main Features:** +-- ## Main Features: -- --- * Stuff +-- * Detect and track contacts consistently +-- * Detect and track clusters of contacts consistently +-- * Use FSM events to link functionality into your scripts +-- * Easy setup -- -- === -- @@ -41,9 +44,42 @@ -- ![Banner Image](..\Presentations\CarrierAirWing\INTEL_Main.jpg) -- -- # The INTEL Concept +-- +-- * Lightweight replacement for @{Functional.Detection#DETECTION} +-- * Detect and track contacts consistently +-- * Detect and track clusters of contacts consistently +-- * Once detected and still alive, planes will be tracked 10 minutes, helicopters 20 minutes, ships and trains 1 hour, ground units 2 hours +-- * Use FSM events to link functionality into your scripts +-- +-- # Basic Usage -- --- --- +-- ## set up a detection SET_GROUP +-- +-- `Red_DetectionSetGroup = SET_GROUP:New()` +-- `Red_DetectionSetGroup:FilterPrefixes( { "Red EWR" } )` +-- `Red_DetectionSetGroup:FilterOnce()` +-- +-- ## New Intel type detection for the red side, logname "KGB" +-- +-- `RedIntel = INTEL:New(Red_DetectionSetGroup,"red","KGB")` +-- `RedIntel:SetClusterAnalysis(true,true)` +-- `RedIntel:SetVerbosity(2)` +-- `RedIntel:Start()` +-- +-- ## Hook into new contacts found +-- +-- `function RedIntel:OnAfterNewContact(From, Event, To, Contact)` +-- `local text = string.format("NEW contact %s detected by %s", Contact.groupname, Contact.recce or "unknown")` +-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()` +-- `end` +-- +-- ## And/or new clusters found +-- +-- `function RedIntel:OnAfterNewCluster(From, Event, To, Contact, Cluster)` +-- `local text = string.format("NEW cluster %d size %d with contact %s", Cluster.index, Cluster.size, Contact.groupname)` +-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()` +-- `end` +-- -- @field #INTEL INTEL = { ClassName = "INTEL", @@ -57,7 +93,7 @@ INTEL = { ContactsUnknown = {}, Clusters = {}, clustercounter = 1, - clusterradius = 15, + clusterradius = 10, } --- Detected item info. @@ -314,7 +350,7 @@ function INTEL:RemoveRejectZone(RejectZone) return self end ---- Set forget contacts time interval. +--- Set forget contacts time interval. For unknown contacts only. -- Previously known contacts that are not detected any more, are "lost" after this time. -- This avoids fast oscillations between a contact being detected and undetected. -- @param #INTEL self @@ -429,7 +465,7 @@ end -- @param #number radius The radius of the clusters -- @return #INTEL self function INTEL:SetClusterRadius(radius) - local radius = radius or 15 + local radius = radius or 10 self.clusterradius = radius return self end @@ -1046,7 +1082,7 @@ function INTEL:CalcClusterThreatlevelSum(cluster) threatlevel=threatlevel+contact.threatlevel end - + cluster.threatlevelSum = threatlevel return threatlevel end @@ -1058,7 +1094,7 @@ function INTEL:CalcClusterThreatlevelAverage(cluster) local threatlevel=self:CalcClusterThreatlevelSum(cluster) threatlevel=threatlevel/cluster.size - + cluster.threatlevelAve = threatlevel return threatlevel end @@ -1078,7 +1114,7 @@ function INTEL:CalcClusterThreatlevelMax(cluster) end end - + cluster.threatlevelMax = threatlevel return threatlevel end @@ -1119,7 +1155,7 @@ function INTEL:IsContactConnectedToCluster(contact, cluster) --local dist=Contact.position:Get2DDistance(contact.position) local dist=Contact.position:DistanceFromPointVec2(contact.position) - local radius = self.clusterradius or 15 + local radius = self.clusterradius or 10 if dist 100 then + EngageRange = 100 + end + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + if Controller then + if self:IsGround() then + self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange) + end + end + return self + end + return nil +end + +--- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving. +-- @param #CONTROLLABLE self +-- @param #number speed Speed of the controllable, default 20 +-- @param #number radius Radius of the relocation zone, default 500 +-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true +-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false +function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) + self:F2( { self.ControllableName } ) + + local _coord = self:GetCoordinate() + local _radius = radius or 500 + local _speed = speed or 20 + local _tocoord = _coord:GetRandomCoordinateInRadius(_radius,100) + local _onroad = onroad or true + local _grptsk = {} + local _candoroad = false + local _shortcut = shortcut or false + + -- create a DCS Task an push it on the group + -- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments) + if onroad then + _grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut) + self:Route(_grptsk,5) + else + self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") + end + + return self +end