diff --git a/Moose Development/Moose/AI/AI_Air.lua b/Moose Development/Moose/AI/AI_Air.lua index 213e58757..6c8c9579c 100644 --- a/Moose Development/Moose/AI/AI_Air.lua +++ b/Moose Development/Moose/AI/AI_Air.lua @@ -595,19 +595,24 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To ) --- Calculate the target route point. local FromCoord = AIGroup:GetCoordinate() - local ToTargetCoord = self.HomeAirbase:GetCoordinate() - - if not self.RTBMinSpeed and not self.RTBMaxSpeed then + local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!) + local ToTargetVec3 = ToTargetCoord:GetVec3() + ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground + local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 ) + + if not self.RTBMinSpeed or not self.RTBMaxSpeed then local RTBSpeedMax = AIGroup:GetSpeedMax() - self:SetRTBSpeed( RTBSpeedMax * 0.25, RTBSpeedMax * 0.25 ) + self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 ) end local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed ) - local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord ) ) + --local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord2 ) ) - local Distance = FromCoord:Get2DDistance( ToTargetCoord ) + local Distance = FromCoord:Get2DDistance( ToTargetCoord2 ) - local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle ) + --local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle ) + local ToAirbaseCoord = ToTargetCoord2 + if Distance < 5000 then self:I( "RTB and near the airbase!" ) self:Home() diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 3dcf432de..f8a7313ef 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -1,24 +1,24 @@ --- **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** --- +-- -- === --- +-- -- @module Functional.Sead -- @image SEAD.JPG @@ -26,25 +26,26 @@ -- @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 = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } , - Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } , - High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } , - Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } } - }, + Average = { Evade = 30, DelayOn = { 40, 60 } } , + Good = { Evade = 20, DelayOn = { 30, 50 } } , + High = { Evade = 15, DelayOn = { 20, 40 } } , + Excellent = { Evade = 10, DelayOn = { 10, 30 } } + }, SEADGroupPrefixes = {}, + SuppressedGroups = {}, EngagementRange = 75 -- default 75% engagement range Feature Request #1355 } @@ -61,7 +62,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 @@ -69,15 +70,15 @@ function SEAD:New( SEADGroupPrefixes ) else self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes end - + self:HandleEvent( EVENTS.Shot ) - + self:I("*** SEAD - Started Version 0.2.0") return self end --- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355 -- @param #SEAD self --- @param #number range Gives the engagement range in percent, e.g. 50 +-- @param #number range Set the engagement range in percent, e.g. 50 -- @return self function SEAD:SetEngagementRange(range) self:F( { range } ) @@ -86,6 +87,7 @@ function SEAD:SetEngagementRange(range) range = 75 end self.EngagementRange = range + self:T(string.format("*** SEAD - Engagement range set to %s",range)) return self end @@ -94,18 +96,19 @@ end -- @param #SEAD -- @param Core.Event#EVENTDATA EventData function SEAD:OnEventShot( EventData ) - self:F( { EventData } ) + self:T( { EventData } ) local SEADUnit = EventData.IniDCSUnit local SEADUnitName = EventData.IniDCSUnitName - local SEADWeapon = EventData.Weapon -- Identify the weapon fired + local SEADWeapon = EventData.Weapon -- Identify the weapon fired local SEADWeaponName = EventData.WeaponName -- return weapon type - -- Start of the 2nd loop - self:T( "Missile Launched = " .. SEADWeaponName ) - - --if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD + + 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 @@ -122,27 +125,35 @@ function SEAD:OnEventShot( EventData ) or SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired or + SEADWeaponName == "weapons.missiles.LD-10" --LD-10 anti-radiation missiles fired + or SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired + or + SEADWeaponName == "weapons.missiles.AGM_84E" --AGM84 anti-radiation missiles fired + or + SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired + or + SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired 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) - local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) - local _targetMimgroupName = _targetMimgroup:getName() - local _targetMimcont= _targetMimgroup:getController() + local _targetMimname = Unit.getName(_targetMim) -- Unit name + local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) --targeted grouo + local _targetMimgroupName = _targetMimgroup:getName() -- group name local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill self:T( self.SEADGroupPrefixes ) self:T( _targetMimgroupName ) + -- see if we are shot at local SEADGroupFound = false for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then SEADGroupFound = true - self:T( 'Group Found' ) + self:T( '*** SEAD - Group Found' ) break end - end - if SEADGroupFound == true then + 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" } _targetskill = Skills[ math.random(1,4) ] @@ -150,44 +161,36 @@ function SEAD:OnEventShot( EventData ) self:T( _targetskill ) if self.TargetSkill[_targetskill] then if (_evade > self.TargetSkill[_targetskill].Evade) then - - self:T( string.format("Evading, target skill " ..string.format(_targetskill)) ) - - local _targetMim = Weapon.getTarget(SEADWeapon) - local _targetMimname = Unit.getName(_targetMim) + + 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 - - local SuppressedGroups1 = {} -- unit suppressed radar off for a random time - - local function SuppressionEnd1(id) - id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN) - SuppressedGroups1[id.groupName] = nil - end - + + --tracker ID table to switch groups off and on again 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 range = self.EngagementRange -- Feature Request #1355 --env.info(string.format("*** SEAD - Engagement Range is %d", range)) @@ -195,22 +198,16 @@ function SEAD:OnEventShot( EventData ) id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355 SuppressedGroups[id.groupName] = nil end - - local id = { - groupName = _targetMimgroup, - ctrl = _targetMimcont - } - + -- randomize switch-on time local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) - - if SuppressedGroups[id.groupName] == nil then - SuppressedGroups[id.groupName] = { - SuppressionEndTime = timer.getTime() + delay, - SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function - } - - timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function - --trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20) + 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) + timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function end end end diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 573b8c4b0..76c7225a7 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -253,7 +253,12 @@ do -- TASK_A2A_DISPATCHER return self end - + --- Set flashing player messages on or off + -- @param #TASK_A2A_DISPATCHER self + -- @param #boolean onoff Set messages on (true) or off (false) + function TASK_A2A_DISPATCHER:SetSendMessages( onoff ) + self.FlashNewTask = onoff + end --- Creates an INTERCEPT task when there are targets for it. -- @param #TASK_A2A_DISPATCHER self @@ -610,7 +615,7 @@ do -- TASK_A2A_DISPATCHER local TaskText = TaskReport:Text(", ") for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and ( not not self.FlashNewTask) then + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and (self.FlashNewTask) then Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) end end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index b7ae6ba17..448d7f545 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -451,6 +451,7 @@ do -- TASK_A2G_DISPATCHER self.Detection = Detection self.Mission = Mission + self.FlashNewTask = true --set to false to suppress flash messages self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } ) @@ -471,6 +472,12 @@ do -- TASK_A2G_DISPATCHER return self end + --- Set flashing player messages on or off + -- @param #TASK_A2G_DISPATCHER self + -- @param #boolean onoff Set messages on (true) or off (false) + function TASK_A2G_DISPATCHER:SetSendMessages( onoff ) + self.FlashNewTask = onoff + end --- Creates a SEAD task when there are targets for it. -- @param #TASK_A2G_DISPATCHER self @@ -616,7 +623,9 @@ do -- TASK_A2G_DISPATCHER if not DetectedItem then local TaskText = Task:GetName() for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) + if self.FlashNewTask then + Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) + end end Task = self:RemoveTask( TaskIndex ) end @@ -686,7 +695,7 @@ do -- TASK_A2G_DISPATCHER -- Now we send to each group the changes, if any. for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do local TargetsText = TargetsReport:Text(", ") - if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then + if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup ) end end @@ -805,7 +814,7 @@ do -- TASK_A2G_DISPATCHER local TaskText = TaskReport:Text(", ") for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do - if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and self.FlashNewTask then Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) end end @@ -815,4 +824,4 @@ do -- TASK_A2G_DISPATCHER return true end -end \ No newline at end of file +end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 1d31fe206..298b24008 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -175,7 +175,7 @@ -- -- ## 5.5) Air-2-Air missile attack range: -- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets . --- +-- -- @field #CONTROLLABLE CONTROLLABLE = { ClassName = "CONTROLLABLE", @@ -503,7 +503,7 @@ function CONTROLLABLE:TaskCombo( DCSTasks ) tasks = DCSTasks } } - + return DCSTaskCombo end @@ -801,12 +801,12 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay) - local CommandSetFrequency = { - id = 'SetFrequency', - params = { - frequency = Frequency*1000000, - modulation = Modulation or radio.modulation.AM, - } + local CommandSetFrequency = { + id = 'SetFrequency', + params = { + frequency = Frequency*1000000, + modulation = Modulation or radio.modulation.AM, + } } if Delay and Delay>0 then @@ -880,7 +880,7 @@ function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, At groupId = AttackGroup:GetID(), weaponType = WeaponType or 1073741822, expend = WeaponExpend or "Auto", - attackQtyLimit = AttackQty and true or false, + attackQtyLimit = AttackQty and true or false, attackQty = AttackQty or 1, directionEnabled = Direction and true or false, direction = Direction and math.rad(Direction) or 0, @@ -920,7 +920,7 @@ function CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, Atta weaponType = WeaponType or 1073741822, } } - + return DCSTask end @@ -1084,7 +1084,7 @@ function CONTROLLABLE:TaskEmbarking(Coordinate, GroupSetForEmbarking, Duration, -- Distribution --local distribution={} --distribution[id]=gids - + local groupID=self and self:GetID() local DCSTask = { @@ -1313,7 +1313,7 @@ function CONTROLLABLE:TaskLandAtVec2(Vec2, Duration) duration = Duration, }, } - + return DCSTask end @@ -1795,7 +1795,7 @@ function CONTROLLABLE:TaskFunction( FunctionString, ... ) -- DCS task. local DCSTask = self:TaskWrappedAction(self:CommandDoScript(table.concat( DCSScript ))) - + return DCSTask end @@ -1972,7 +1972,7 @@ function CONTROLLABLE:TaskRoute( Points ) id = 'Mission', params = { airborne = self:IsAir(), - route = {points = Points}, + route = {points = Points}, }, } @@ -2898,9 +2898,9 @@ end function CONTROLLABLE:OptionROE(ROEvalue) local DCSControllable = self:GetDCSObject() - + if DCSControllable then - + local Controller = self:_GetController() if self:IsAir() then @@ -3464,13 +3464,13 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:OptionProhibitAfterburner(Prohibit) self:F2( { self.ControllableName } ) - + if Prohibit==nil then Prohibit=true end if self:IsAir() then - self:SetOption(AI.Option.Air.id.PROHIBIT_AB, Prohibit) + self:SetOption(AI.Option.Air.id.PROHIBIT_AB, Prohibit) end return self @@ -3481,9 +3481,9 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_Never() self:F2( { self.ControllableName } ) - + if self:IsAir() then - self:SetOption(AI.Option.Air.id.ECM_USING, 0) + self:SetOption(AI.Option.Air.id.ECM_USING, 0) end return self @@ -3494,9 +3494,9 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_OnlyLockByRadar() self:F2( { self.ControllableName } ) - + if self:IsAir() then - self:SetOption(AI.Option.Air.id.ECM_USING, 1) + self:SetOption(AI.Option.Air.id.ECM_USING, 1) end return self @@ -3508,9 +3508,9 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_DetectedLockByRadar() self:F2( { self.ControllableName } ) - + if self:IsAir() then - self:SetOption(AI.Option.Air.id.ECM_USING, 2) + self:SetOption(AI.Option.Air.id.ECM_USING, 2) end return self @@ -3521,9 +3521,9 @@ end -- @return #CONTROLLABLE self function CONTROLLABLE:OptionECM_AlwaysOn() self:F2( { self.ControllableName } ) - + if self:IsAir() then - self:SetOption(AI.Option.Air.id.ECM_USING, 3) + self:SetOption(AI.Option.Air.id.ECM_USING, 3) end return self @@ -3667,18 +3667,18 @@ end -- @param #CONTROLLABLE self -- @param #number Defines the range: MAX_RANGE = 0, NEZ_RANGE = 1, HALF_WAY_RMAX_NEZ = 2, TARGET_THREAT_EST = 3, RANDOM_RANGE = 4. Defaults to 3. See: https://wiki.hoggitworld.com/view/DCS_option_missileAttack function CONTROLLABLE:OptionAAAttackRange(range) - self:F2( { self.ControllableName } ) + self:F2( { self.ControllableName } ) -- defaults to 3 local range = range or 3 - if range < 0 or range > 4 then - range = 3 + if range < 0 or range > 4 then + range = 3 end local DCSControllable = self:GetDCSObject() if DCSControllable then local Controller = self:_GetController() - if Controller then + if Controller then if self:IsAir() then - self:SetOption(AI.Option.Air.val.MISSILE_ATTACK, range) + self:SetOption(AI.Option.Air.val.MISSILE_ATTACK, range) end end return self