From 47d814e409996fbbbe4d5f3719c452cfd206f8f2 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Fri, 21 May 2021 12:35:32 +0200 Subject: [PATCH] CONTROLLABLE:PatrolRouteRandom fix for Subs (#1536) * CONTROLLABLE:PatrolRouteRandom fix for Subs fixes issue #1535 * Update Controllable.lua * Update Controllable.lua --- .../Moose/Wrapper/Controllable.lua | 165 +++++++++++------- 1 file changed, 105 insertions(+), 60 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index e2d3edca6..58e1a747e 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -175,11 +175,8 @@ -- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat} -- -- ## 5.5) Air-2-Air missile attack range: --- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets. --- --- ## 5.6) GROUND units attack range: --- * @{#CONTROLLABLE.OptionEngageRange}(): Engage range limit in percent (a number between 0 and 100). Default 100. Defines the range at which a GROUND unit/group (e.g. a SAM site) is allowed to use its weapons automatically. --- +-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets . +-- -- @field #CONTROLLABLE CONTROLLABLE = { ClassName = "CONTROLLABLE", @@ -507,7 +504,7 @@ function CONTROLLABLE:TaskCombo( DCSTasks ) tasks = DCSTasks } } - + return DCSTaskCombo end @@ -805,12 +802,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 @@ -884,7 +881,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, @@ -924,7 +921,7 @@ function CONTROLLABLE:TaskAttackUnit(AttackUnit, GroupAttack, WeaponExpend, Atta weaponType = WeaponType or 1073741822, } } - + return DCSTask end @@ -1088,7 +1085,7 @@ function CONTROLLABLE:TaskEmbarking(Coordinate, GroupSetForEmbarking, Duration, -- Distribution --local distribution={} --distribution[id]=gids - + local groupID=self and self:GetID() local DCSTask = { @@ -1317,7 +1314,7 @@ function CONTROLLABLE:TaskLandAtVec2(Vec2, Duration) duration = Duration, }, } - + return DCSTask end @@ -1432,7 +1429,7 @@ end -- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). -- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag -- @param #number Altitude (Optional) Altitude in meters. --- @param #number ASL Altitude is above mean sea level. Default is above ground level. +-- @param #number ASL Altitude is above mean sea level. Default is above ground level. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Altitude, ASL ) @@ -1454,7 +1451,7 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti DCSTask.params.expendQty = AmmoCount DCSTask.params.expendQtyEnabled = true end - + if Altitude then DCSTask.params.altitude=Altitude end @@ -1462,7 +1459,7 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti if WeaponType then DCSTask.params.weaponType=WeaponType end - + --self:I(DCSTask) return DCSTask @@ -1482,6 +1479,7 @@ end --- (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction. -- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. -- If the task is assigned to the controllable lead unit will be a FAC. +-- It's important to note that depending on the type of unit that is being assigned the task (AIR or GROUND), you must choose the correct type of callsign enumerator. For airborne controllables use CALLSIGN.Aircraft and for ground based use CALLSIGN.JTAC enumerators. -- @param #CONTROLLABLE self -- @param Wrapper.Group#GROUP AttackGroup Target GROUP object. -- @param #number WeaponType Bitmask of weapon types, which are allowed to use. @@ -1489,7 +1487,7 @@ end -- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. -- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz. -- @param #number Modulation Modulation of radio for communication. Default 0=AM. --- @param #number CallsignName Callsign enumerator name of the FAC. +-- @param #number CallsignName Callsign enumerator name of the FAC. (CALLSIGN.Aircraft.{name} for airborne controllables, CALLSIGN.JTACS.{name} for ground units) -- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink, Frequency, Modulation, CallsignName, CallsignNumber ) @@ -1811,7 +1809,7 @@ function CONTROLLABLE:TaskFunction( FunctionString, ... ) -- DCS task. local DCSTask = self:TaskWrappedAction(self:CommandDoScript(table.concat( DCSScript ))) - + return DCSTask end @@ -1853,8 +1851,27 @@ do -- Patrol methods -- Calculate the new Route. local FromCoord = PatrolGroup:GetCoordinate() - local From = FromCoord:WaypointGround( 120 ) - + + -- test for submarine + local depth = 0 + local IsSub = false + if PatrolGroup:IsShip() then + local navalvec3 = FromCoord:GetVec3() + if navalvec3.y < 0 then + depth = navalvec3.y + IsSub = true + end + end + + + local Waypoint = Waypoints[1] + local Speed = Waypoint.speed or (20 / 3.6) + local From = FromCoord:WaypointGround( Speed ) + + if IsSub then + From = FromCoord:WaypointNaval( Speed, Waypoint.alt ) + end + table.insert( Waypoints, 1, From ) local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" ) @@ -1894,7 +1911,16 @@ do -- Patrol methods if ToWaypoint then FromWaypoint = ToWaypoint end - + -- test for submarine + local depth = 0 + local IsSub = false + if PatrolGroup:IsShip() then + local navalvec3 = FromCoord:GetVec3() + if navalvec3.y < 0 then + depth = navalvec3.y + IsSub = true + end + end -- Loop until a waypoint has been found that is not the same as the current waypoint. -- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly -- what it is supposed to do, which is making groups drive around. @@ -1909,9 +1935,13 @@ do -- Patrol methods local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } ) -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task local Route = {} - Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) - Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) - + if IsSub then + Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth ) + Route[#Route+1] = ToCoord:WaypointNaval( Speed, Waypoint.alt ) + else + Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) + Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) + end local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint ) @@ -1952,9 +1982,20 @@ do -- Patrol methods self:F( { PatrolGroup = PatrolGroup:GetName() } ) if PatrolGroup:IsGround() or PatrolGroup:IsShip() then - + -- Calculate the new Route. local FromCoord = PatrolGroup:GetCoordinate() + + -- test for submarine + local depth = 0 + local IsSub = false + if PatrolGroup:IsShip() then + local navalvec3 = FromCoord:GetVec3() + if navalvec3.y < 0 then + depth = navalvec3.y + IsSub = true + end + end -- Select a random Zone and get the Coordinate of the new Zone. local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE @@ -1962,9 +2003,13 @@ do -- Patrol methods -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task local Route = {} - Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) - Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) - + if IsSub then + Route[#Route+1] = FromCoord:WaypointNaval( Speed, depth ) + Route[#Route+1] = ToCoord:WaypointNaval( Speed, depth ) + else + Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) + Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) + end local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation, DelayMin, DelayMax ) @@ -1988,7 +2033,7 @@ function CONTROLLABLE:TaskRoute( Points ) id = 'Mission', params = { airborne = self:IsAir(), - route = {points = Points}, + route = {points = Points}, }, } @@ -2914,9 +2959,9 @@ end function CONTROLLABLE:OptionROE(ROEvalue) local DCSControllable = self:GetDCSObject() - + if DCSControllable then - + local Controller = self:_GetController() if self:IsAir() then @@ -3480,13 +3525,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 @@ -3497,9 +3542,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 @@ -3510,9 +3555,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 @@ -3524,9 +3569,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 @@ -3537,9 +3582,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 @@ -3681,22 +3726,22 @@ end --- Sets Controllable Option for A2A attack range for AIR FIGHTER units. -- @param #CONTROLLABLE self --- @param #number range Defines the range +-- @param #number range Defines the range -- @return #CONTROLLABLE self -- @usage Range can be one of 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 - end + 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 @@ -3709,7 +3754,7 @@ end -- @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 } ) + self:F2( { self.ControllableName } ) -- Set default if not specified. EngageRange=EngageRange or 100 if EngageRange < 0 or EngageRange > 100 then @@ -3718,9 +3763,9 @@ function CONTROLLABLE:OptionEngageRange(EngageRange) local DCSControllable = self:GetDCSObject() if DCSControllable then local Controller = self:_GetController() - if Controller then + if Controller then if self:IsGround() then - self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange) + self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange) end end return self @@ -3736,9 +3781,9 @@ end -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false -- @return #CONTROLLABLE self function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) - self:F2( { self.ControllableName } ) + self:F2( { self.ControllableName } ) - local _coord = self:GetCoordinate() + local _coord = self:GetCoordinate() local _radius = radius or 500 local _speed = speed or 20 local _tocoord = _coord:GetRandomCoordinateInRadius(_radius,100) @@ -3746,7 +3791,7 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc 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 @@ -3756,23 +3801,23 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") end - return self + return self end --- Defines how long a GROUND unit/group will move to avoid an ongoing attack. -- @param #CONTROLLABLE self --- @param #number Seconds Any positive number: AI will disperse, but only for the specified time before continuing their route. 0: AI will not disperse. +-- @param #number Seconds Any positive number: AI will disperse, but only for the specified time before continuing their route. 0: AI will not disperse. -- @return #CONTROLLABLE self function CONTROLLABLE:OptionDisperseOnAttack(Seconds) - self:F2( { self.ControllableName } ) + self:F2( { self.ControllableName } ) -- Set default if not specified. local seconds = Seconds or 0 local DCSControllable = self:GetDCSObject() if DCSControllable then local Controller = self:_GetController() - if Controller then + if Controller then if self:IsGround() then - self:SetOption(AI.Option.GROUND.id.DISPERSE_ON_ATTACK, seconds) + self:SetOption(AI.Option.GROUND.id.DISPERSE_ON_ATTACK, seconds) end end return self