Merge pull request #3 from FlightControl-Master/develop

Develop
This commit is contained in:
Tony Goodale 2021-03-21 14:53:53 -07:00 committed by GitHub
commit c0cc960df5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 62 deletions

View File

@ -1,26 +1,26 @@
--- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- **Functional** -- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- === -- ===
-- --
-- ## Features: -- ## 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 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. -- * When SAM sites are being fired upon, the SAMs will take defensive action by shutting down their radars.
-- --
-- === -- ===
-- --
-- ## Missions: -- ## Missions:
-- --
-- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion) -- [SEV - SEAD Evasion](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SEV%20-%20SEAD%20Evasion)
-- --
-- === -- ===
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Feb 2021 -- Last Update: Feb 2021
-- --
-- === -- ===
-- --
-- @module Functional.Sead -- @module Functional.Sead
-- @image SEAD.JPG -- @image SEAD.JPG
@ -28,24 +28,24 @@
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- 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. -- 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: -- # Constructor:
-- --
-- Use the @{#SEAD.New}() constructor to create a new SEAD object. -- 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' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
-- --
-- @field #SEAD -- @field #SEAD
SEAD = { SEAD = {
ClassName = "SEAD", ClassName = "SEAD",
TargetSkill = { TargetSkill = {
Average = { Evade = 30, DelayOn = { 40, 60 } } , Average = { Evade = 30, DelayOn = { 40, 60 } } ,
Good = { Evade = 20, DelayOn = { 30, 50 } } , Good = { Evade = 20, DelayOn = { 30, 50 } } ,
High = { Evade = 15, DelayOn = { 20, 40 } } , High = { Evade = 15, DelayOn = { 20, 40 } } ,
Excellent = { Evade = 10, DelayOn = { 10, 30 } } Excellent = { Evade = 10, DelayOn = { 10, 30 } }
}, },
SEADGroupPrefixes = {}, SEADGroupPrefixes = {},
SuppressedGroups = {}, SuppressedGroups = {},
EngagementRange = 75 -- default 75% engagement range Feature Request #1355 EngagementRange = 75 -- default 75% engagement range Feature Request #1355
@ -84,7 +84,7 @@ SEAD = {
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- 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... -- 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. -- Chances are big that the missile will miss.
@ -99,7 +99,7 @@ function SEAD:New( SEADGroupPrefixes )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, BASE:New() )
self:F( SEADGroupPrefixes ) self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@ -107,7 +107,7 @@ function SEAD:New( SEADGroupPrefixes )
else else
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
end end
self:HandleEvent( EVENTS.Shot ) self:HandleEvent( EVENTS.Shot )
self:I("*** SEAD - Started Version 0.2.5") self:I("*** SEAD - Started Version 0.2.5")
return self return self
@ -120,7 +120,7 @@ end
function SEAD:UpdateSet( SEADGroupPrefixes ) function SEAD:UpdateSet( SEADGroupPrefixes )
self:F( SEADGroupPrefixes ) self:F( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
@ -174,10 +174,10 @@ function SEAD:OnEventShot( EventData )
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
self:T({ SEADWeapon }) self:T({ SEADWeapon })
--[[check for SEAD missiles --[[check for SEAD missiles
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired 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 SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
or or
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired 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 SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
--]] --]]
if self:_CheckHarms(SEADWeaponName) then if self:_CheckHarms(SEADWeaponName) then
local _evade = math.random (1,100) -- random number for chance of evading action local _evade = math.random (1,100) -- random number for chance of evading action
local _targetMim = EventData.Weapon:getTarget() -- Identify target local _targetMim = EventData.Weapon:getTarget() -- Identify target
local _targetMimname = Unit.getName(_targetMim) -- Unit name local _targetMimname = Unit.getName(_targetMim) -- Unit name
@ -222,7 +222,7 @@ function SEAD:OnEventShot( EventData )
self:T( '*** SEAD - Group Found' ) self:T( '*** SEAD - Group Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" } local Skills = { "Average", "Good", "High", "Excellent" }
@ -231,42 +231,26 @@ function SEAD:OnEventShot( EventData )
self:T( _targetskill ) self:T( _targetskill )
if self.TargetSkill[_targetskill] then if self.TargetSkill[_targetskill] then
if (_evade > self.TargetSkill[_targetskill].Evade) then if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) ) self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
local _targetMimcont= _targetMimgroup:getController() local _targetMimcont= _targetMimgroup:getController()
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
--tracker ID table to switch groups off and on again --tracker ID table to switch groups off and on again
local id = { local id = {
groupName = _targetMimgroup, groupName = _targetMimgroup,
ctrl = _targetMimcont ctrl = _targetMimcont
} }
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2]) local function SuppressionEnd(id) --switch group back on
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 range = self.EngagementRange -- Feature Request #1355 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.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355 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 end
-- randomize switch-on time -- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])

View File

@ -1,8 +1,11 @@
--- **Ops** - Office of Military Intelligence. --- **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) -- ![Banner Image](..\Presentations\CarrierAirWing\INTEL_Main.jpg)
-- --
-- # The INTEL Concept -- # 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 -- @field #INTEL
INTEL = { INTEL = {
ClassName = "INTEL", ClassName = "INTEL",
@ -57,7 +93,7 @@ INTEL = {
ContactsUnknown = {}, ContactsUnknown = {},
Clusters = {}, Clusters = {},
clustercounter = 1, clustercounter = 1,
clusterradius = 15, clusterradius = 10,
} }
--- Detected item info. --- Detected item info.
@ -314,7 +350,7 @@ function INTEL:RemoveRejectZone(RejectZone)
return self return self
end 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. -- 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. -- This avoids fast oscillations between a contact being detected and undetected.
-- @param #INTEL self -- @param #INTEL self
@ -429,7 +465,7 @@ end
-- @param #number radius The radius of the clusters -- @param #number radius The radius of the clusters
-- @return #INTEL self -- @return #INTEL self
function INTEL:SetClusterRadius(radius) function INTEL:SetClusterRadius(radius)
local radius = radius or 15 local radius = radius or 10
self.clusterradius = radius self.clusterradius = radius
return self return self
end end
@ -1046,7 +1082,7 @@ function INTEL:CalcClusterThreatlevelSum(cluster)
threatlevel=threatlevel+contact.threatlevel threatlevel=threatlevel+contact.threatlevel
end end
cluster.threatlevelSum = threatlevel
return threatlevel return threatlevel
end end
@ -1058,7 +1094,7 @@ function INTEL:CalcClusterThreatlevelAverage(cluster)
local threatlevel=self:CalcClusterThreatlevelSum(cluster) local threatlevel=self:CalcClusterThreatlevelSum(cluster)
threatlevel=threatlevel/cluster.size threatlevel=threatlevel/cluster.size
cluster.threatlevelAve = threatlevel
return threatlevel return threatlevel
end end
@ -1078,7 +1114,7 @@ function INTEL:CalcClusterThreatlevelMax(cluster)
end end
end end
cluster.threatlevelMax = threatlevel
return threatlevel return threatlevel
end end
@ -1119,7 +1155,7 @@ function INTEL:IsContactConnectedToCluster(contact, cluster)
--local dist=Contact.position:Get2DDistance(contact.position) --local dist=Contact.position:Get2DDistance(contact.position)
local dist=Contact.position:DistanceFromPointVec2(contact.position) local dist=Contact.position:DistanceFromPointVec2(contact.position)
local radius = self.clusterradius or 15 local radius = self.clusterradius or 10
if dist<radius*1000 then if dist<radius*1000 then
return true return true
end end

View File

@ -3687,3 +3687,57 @@ function CONTROLLABLE:OptionAAAttackRange(range)
end end
return nil return nil
end end
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
-- @param #CONTROLLABLE self
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionEngageRange(EngageRange)
self:F2( { self.ControllableName } )
-- Set default if not specified.
EngageRange=EngageRange or 100
if EngageRange < 0 or EngageRange > 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