|
|
|
|
@@ -17,7 +17,7 @@
|
|
|
|
|
--
|
|
|
|
|
-- ### Authors: **FlightControl**, **applevangelist**
|
|
|
|
|
--
|
|
|
|
|
-- Last Update: July 2021
|
|
|
|
|
-- Last Update: Aug 2021
|
|
|
|
|
--
|
|
|
|
|
-- ===
|
|
|
|
|
--
|
|
|
|
|
@@ -59,7 +59,7 @@ SEAD = {
|
|
|
|
|
["AGM_122"] = "AGM_122",
|
|
|
|
|
["AGM_84"] = "AGM_84",
|
|
|
|
|
["AGM_45"] = "AGM_45",
|
|
|
|
|
["ALARN"] = "ALARM",
|
|
|
|
|
["ALARM"] = "ALARM",
|
|
|
|
|
["LD-10"] = "LD-10",
|
|
|
|
|
["X_58"] = "X_58",
|
|
|
|
|
["X_28"] = "X_28",
|
|
|
|
|
@@ -68,6 +68,23 @@ SEAD = {
|
|
|
|
|
["Kh25"] = "Kh25",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--- Missile enumerators
|
|
|
|
|
-- @field HarmData
|
|
|
|
|
SEAD.HarmData = {
|
|
|
|
|
-- km and mach
|
|
|
|
|
["AGM_88"] = { 150, 3},
|
|
|
|
|
["AGM_45"] = { 12, 2},
|
|
|
|
|
["AGM_122"] = { 16.5, 2.3},
|
|
|
|
|
["AGM_84"] = { 280, 0.85},
|
|
|
|
|
["ALARM"] = { 45, 2},
|
|
|
|
|
["LD-10"] = { 60, 4},
|
|
|
|
|
["X_58"] = { 70, 4},
|
|
|
|
|
["X_28"] = { 80, 2.5},
|
|
|
|
|
["X_25"] = { 25, 0.76},
|
|
|
|
|
["X_31"] = {150, 3},
|
|
|
|
|
["Kh25"] = {25, 0.8},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
|
|
|
|
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
|
|
|
|
-- Chances are big that the missile will miss.
|
|
|
|
|
@@ -92,7 +109,7 @@ function SEAD:New( SEADGroupPrefixes )
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
|
|
|
|
self:I("*** SEAD - Started Version 0.2.9")
|
|
|
|
|
self:I("*** SEAD - Started Version 0.2.10")
|
|
|
|
|
return self
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@@ -134,22 +151,56 @@ end
|
|
|
|
|
-- @param #SEAD self
|
|
|
|
|
-- @param #string WeaponName
|
|
|
|
|
-- @return #boolean Returns true for a match
|
|
|
|
|
-- @return #string name Name of hit in table
|
|
|
|
|
function SEAD:_CheckHarms(WeaponName)
|
|
|
|
|
self:T( { WeaponName } )
|
|
|
|
|
local hit = false
|
|
|
|
|
local name = ""
|
|
|
|
|
for _,_name in pairs (SEAD.Harms) do
|
|
|
|
|
if string.find(WeaponName,_name,1) then hit = true end
|
|
|
|
|
if string.find(WeaponName,_name,1) then
|
|
|
|
|
hit = true
|
|
|
|
|
name = _name
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return hit
|
|
|
|
|
return hit, name
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
|
|
|
|
-- @param #SEAD self
|
|
|
|
|
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
|
|
|
|
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
|
|
|
|
-- @return #number Distance in meters
|
|
|
|
|
function SEAD:_GetDistance(_point1, _point2)
|
|
|
|
|
self:T("_GetDistance")
|
|
|
|
|
if _point1 and _point2 then
|
|
|
|
|
local distance1 = _point1:Get2DDistance(_point2)
|
|
|
|
|
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
|
|
|
|
self:I({dist1=distance1, dist2=distance2})
|
|
|
|
|
if distance1 and type(distance1) == "number" then
|
|
|
|
|
return distance1
|
|
|
|
|
elseif distance2 and type(distance2) == "number" then
|
|
|
|
|
return distance2
|
|
|
|
|
else
|
|
|
|
|
self:E("*****Cannot calculate distance!")
|
|
|
|
|
self:E({_point1,_point2})
|
|
|
|
|
return -1
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
self:E("******Cannot calculate distance!")
|
|
|
|
|
self:E({_point1,_point2})
|
|
|
|
|
return -1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
|
|
|
|
-- @see SEAD
|
|
|
|
|
-- @param #SEAD
|
|
|
|
|
-- @param Core.Event#EVENTDATA EventData
|
|
|
|
|
function SEAD:HandleEventShot( EventData )
|
|
|
|
|
self:T( { EventData } )
|
|
|
|
|
|
|
|
|
|
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
|
|
|
|
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
|
|
|
|
local SEADUnit = EventData.IniDCSUnit
|
|
|
|
|
local SEADUnitName = EventData.IniDCSUnitName
|
|
|
|
|
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
|
|
|
|
@@ -163,11 +214,14 @@ function SEAD:HandleEventShot( EventData )
|
|
|
|
|
local _targetMimgroupName = "none"
|
|
|
|
|
local _evade = math.random (1,100) -- random number for chance of evading action
|
|
|
|
|
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
|
|
|
|
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
|
|
|
|
local _targetUnit = UNIT:Find(_targetMim) -- Wrapper.Unit#UNIT
|
|
|
|
|
local _targetMimgroup = nil -- Wrapper.Group#GROUP
|
|
|
|
|
if _targetUnit and _targetUnit:IsAlive() then
|
|
|
|
|
local _targetMimgroup = _targetUnit:GetGroup()
|
|
|
|
|
_targetMimgroup = _targetUnit:GetGroup()
|
|
|
|
|
_targetMimgroupName = _targetMimgroup:GetName() -- group name
|
|
|
|
|
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
|
|
|
|
local _targetUnitName = _targetUnit:GetName()
|
|
|
|
|
_targetUnit:GetSkill()
|
|
|
|
|
_targetskill = _targetUnit:GetSkill()
|
|
|
|
|
self:T( self.SEADGroupPrefixes )
|
|
|
|
|
self:T( _targetMimgroupName )
|
|
|
|
|
end
|
|
|
|
|
@@ -189,39 +243,67 @@ function SEAD:HandleEventShot( 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 = {
|
|
|
|
|
groupName = _targetMimgroup,
|
|
|
|
|
ctrl = _targetMimcont
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local function SuppressionEnd(id) --switch group back on
|
|
|
|
|
local range = self.EngagementRange -- Feature Request #1355
|
|
|
|
|
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
|
|
|
|
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
|
|
|
|
--id.groupName:enableEmission(true)
|
|
|
|
|
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
|
|
|
|
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
|
|
|
|
-- calculate distance of attacker
|
|
|
|
|
local _targetpos = _targetMimgroup:GetCoordinate()
|
|
|
|
|
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
|
|
|
|
|
-- weapon speed
|
|
|
|
|
local hit, data = self:_CheckHarms(SEADWeaponName)
|
|
|
|
|
local wpnpeed = 666
|
|
|
|
|
local reach = 10
|
|
|
|
|
if hit then
|
|
|
|
|
local wpndata = SEAD.HarmData[data]
|
|
|
|
|
reach = wpndata[1] * 1,1
|
|
|
|
|
local mach = wpndata[2]
|
|
|
|
|
wpnpeed = math.floor(mach * 340.29)
|
|
|
|
|
end
|
|
|
|
|
-- randomize switch-on time
|
|
|
|
|
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
|
|
|
|
local SuppressionEndTime = timer.getTime() + delay
|
|
|
|
|
--create entry
|
|
|
|
|
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
|
|
|
|
self.SuppressedGroups[id.groupName] = {
|
|
|
|
|
SuppressionEndTime = delay
|
|
|
|
|
}
|
|
|
|
|
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
|
|
|
|
--_targetMimgroup:enableEmission(false)
|
|
|
|
|
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
|
|
|
|
-- time to impact
|
|
|
|
|
local _tti = math.floor(_distance / wpnpeed) -- estimated impact time
|
|
|
|
|
if _distance > 0 then
|
|
|
|
|
_distance = math.floor(_distance / 1000) -- km
|
|
|
|
|
else
|
|
|
|
|
_distance = 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
|
|
|
|
|
|
|
|
|
|
local _targetMimgroup1 = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
|
|
|
|
local _targetMimcont1 = _targetMimgroup1:getController()
|
|
|
|
|
|
|
|
|
|
if reach >= _distance then
|
|
|
|
|
self:T("*** SEAD - Relocating")
|
|
|
|
|
_targetMimgroup:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
|
|
|
|
|
--routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
|
|
|
|
|
|
|
|
|
--tracker ID table to switch groups off and on again
|
|
|
|
|
local id = {
|
|
|
|
|
groupName = _targetMimgroup1,
|
|
|
|
|
ctrl = _targetMimcont1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local function SuppressionEnd(id) --switch group back on
|
|
|
|
|
local range = self.EngagementRange -- Feature Request #1355
|
|
|
|
|
--self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
|
|
|
|
self:T("*** SEAD - Radar On")
|
|
|
|
|
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
|
|
|
|
--id.groupName:enableEmission(true)
|
|
|
|
|
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
|
|
|
|
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
|
|
|
|
end
|
|
|
|
|
-- randomize switch-on time
|
|
|
|
|
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
|
|
|
|
if delay < _tti then delay = _tti * 1,1 end
|
|
|
|
|
local SuppressionEndTime = timer.getTime() + delay
|
|
|
|
|
--create entry
|
|
|
|
|
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
|
|
|
|
self.SuppressedGroups[id.groupName] = {
|
|
|
|
|
SuppressionEndTime = delay
|
|
|
|
|
}
|
|
|
|
|
self:T(string.format("*** SEAD - Radar Off for %dsecs",delay))
|
|
|
|
|
Controller.setOption(_targetMimcont1, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
|
|
|
|
--_targetMimgroup:enableEmission(false)
|
|
|
|
|
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|