diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 008b9ae01..98b6fbdbd 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -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 diff --git a/Moose Development/Moose/Functional/Shorad.lua b/Moose Development/Moose/Functional/Shorad.lua index 958877ec8..ce2d93d20 100644 --- a/Moose Development/Moose/Functional/Shorad.lua +++ b/Moose Development/Moose/Functional/Shorad.lua @@ -113,7 +113,7 @@ do ["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", diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 01e3867ba..223eab9e6 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -3775,12 +3775,13 @@ 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 +-- @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 +-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road" -- @return #CONTROLLABLE self -function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) +function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation) self:F2( { self.ControllableName } ) local _coord = self:GetCoordinate() @@ -3791,14 +3792,14 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc local _grptsk = {} local _candoroad = false local _shortcut = shortcut or false + local _formation = formation or "Off Road" -- 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) + _grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut) self:Route(_grptsk,5) else - self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") + self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation) end return self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index a9c678ab5..a1ccdfa85 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2594,6 +2594,17 @@ function GROUP:SetCommandImmortal(switch) return self end +--- Get skill from Group. Effectively gets the skill from Unit 1 as the group holds no skill value. +-- @param #GROUP self +-- @return #string Skill String of skill name. +function GROUP:GetSkill() + self:F2( self.GroupName ) + local unit = self:GetUnit(1) + local name = unit:GetName() + local skill = _DATABASE.Templates.Units[name].Template.skill or "Random" + return skill +end + --do -- Smoke -- ----- Signal a flare at the position of the GROUP. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index efd02a03d..3fb557504 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1426,3 +1426,13 @@ function UNIT:EnableEmission(switch) return self end + +--- Get skill from Unit. +-- @param #UNIT self +-- @return #string Skill String of skill name. +function UNIT:GetSkill() + self:F2( self.UnitName ) + local name = self.UnitName + local skill = _DATABASE.Templates.Units[name].Template.skill or "Random" + return skill +end