diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 069fdee0f..6b292f2fd 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -516,7 +516,7 @@ RANGE.MenuF10Root=nil --- Range script version. -- @field #string version -RANGE.version="2.2.1" +RANGE.version="2.2.2" --TODO list: --TODO: Verbosity level for messages. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 4ab4d0ea4..3a6317736 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1031,6 +1031,186 @@ function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, Atta end +--- (AIR) Delivering weapon via CarpetBombing (all bombers in formation release at same time) at the point on the ground. +-- @param #CONTROLLABLE self +-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. +-- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param #number Altitude (optional) The altitude from where to attack. +-- @param #number WeaponType (optional) The WeaponType. +-- @param #number CarpetLength (optional) default to 500 m. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:TaskCarpetBombing(Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, CarpetLength) + self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, CarpetLength } ) + + local _groupattack=false + if GroupAttack then + _groupattack=GroupAttack + end + + local _direction=0 + local _directionenabled=false + if Direction then + _direction=math.rad(Direction) + _directionenabled=true + end + + local _altitude=0 + local _altitudeenabled=false + if Altitude then + _altitude=Altitude + _altitudeenabled=true + end + + -- default to 500m + local _carpetLength = 500 + if CarpetLength then + _carpetLength = CarpetLength + end + + local _weaponexpend = "Auto" + if WeaponExpend then + _weaponexpend = WeaponExpend + end + + -- Build Task Structure + local DCSTask + DCSTask = { + id = 'CarpetBombing', + params = { + attackType = "Carpet", + point = Vec2, + x = Vec2.x, + y = Vec2.y, + groupAttack = _groupattack, + carpetLength = _carpetLength, + weaponType = WeaponType, + expend = "All", + attackQtyLimit = false, --AttackQty and true or false, + attackQty = AttackQty or 1, + directionEnabled = _directionenabled, + direction = _direction, + altitudeEnabled = _altitudeenabled, + altitude = _altitude + } + } + + return DCSTask +end + + + +--- (AIR) Following another airborne controllable. +-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders. +-- Used to support CarpetBombing Task +-- @param #CONTROLLABLE self +-- @param #CONTROLLABLE FollowControllable The controllable to be followed. +-- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. +-- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:TaskFollowBigFormation(FollowControllable, Vec3, LastWaypointIndex ) + + local DCSTask = { + id = 'FollowBigFormation', + params = { + groupId = FollowControllable:GetID(), + pos = Vec3, + lastWptIndexFlag = LastWaypointIndex and true or false, + lastWptIndex = LastWaypointIndex + } + } + + return DCSTask +end + + +--- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. +-- @param #CONTROLLABLE self +-- @param DCS#Vec2 Vec2 The point where to wait. Needs to have x and y components. +-- @param Core.Set#SET_GROUP GroupSetForEmparking Set of groups to embark. +-- @param #number Duration (Optional) The maximum duration in seconds to wait until all groups have embarked. +-- @param Core.Set#SET_GROUP (Optional) DistributionGroupSet Set of groups identifying the groups needing to board specific helicopters. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:TaskEmbarking(Vec2, GroupSetForEmbarking, Duration, DistributionGroupSet) + + -- Table of group IDs for embarking. + local g4e={} + + if GroupSetForEmbarking then + for _,_group in pairs(GroupSetForEmbarking:GetSet()) do + local group=_group --Wrapper.Group#GROUP + table.insert(g4e, group:GetID()) + end + else + self:E("ERROR: No groups for embarking specified!") + return nil + end + + + + local DCSTask = { + id = 'Embarking', + params = { + Vec2 = Vec2, + x = Vec2.x, + y = Vec2.y, + groupsForEmbarking = g4e, + durationFlag = Duration and true or false, + duration = Duration, + distributionFlag = DistributionGroupSet and true or false, + distribution = Distribution, + } + } + + self:T3( { DCSTask } ) + return DCSTask +end + + + +--- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. +-- @param #CONTROLLABLE self +-- @param DCS#Vec2 Vec2 The point where to wait. +-- @param #number Duration The duration in seconds to wait. +-- @param #CONTROLLABLE EmbarkingControllable The controllable to be embarked. +-- @return DCS#Task The DCS task structure +function CONTROLLABLE:TaskDisembarking(Vec2, Duration, EmbarkingControllable) + + -- Table of group IDs for embarking. + local g4e={} + + if GroupSetForEmbarking then + for _,_group in pairs(GroupSetForEmbarking:GetSet()) do + local group=_group --Wrapper.Group#GROUP + table.insert(g4e, group:GetID()) + end + else + self:E("ERROR: No groups for embarking specified!") + return nil + end + + + local DCSTask = { + id = 'Disembarking', + params = { + point = Vec2, + x = Vec2.x, + y = Vec2.y, + duration = Duration, + groupsForEmbarking = { EmbarkingControllable:GetID() }, + durationFlag = durationflag, + distributionFlag = false, + distribution = {}, + } + } + + return DCSTask +end + +---- + --- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- @param #CONTROLLABLE self -- @param DCS#Vec2 Point The point to hold the position. @@ -1040,39 +1220,20 @@ end function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed ) self:F2( { self.ControllableName, Point, Altitude, Speed } ) - -- pattern = enum AI.Task.OribtPattern, - -- point = Vec2, - -- point2 = Vec2, - -- speed = Distance, - -- altitude = Distance - local LandHeight = land.getHeight( Point ) self:T3( { LandHeight } ) - local DCSTask = { id = 'Orbit', - params = { pattern = AI.Task.OrbitPattern.CIRCLE, - point = Point, - speed = Speed, + local DCSTask = { + id = 'Orbit', + params = { + pattern = AI.Task.OrbitPattern.CIRCLE, + point = Point, + speed = Speed, altitude = Altitude + LandHeight } } - - -- local AITask = { id = 'ControlledTask', - -- params = { task = { id = 'Orbit', - -- params = { pattern = AI.Task.OrbitPattern.CIRCLE, - -- point = Point, - -- speed = Speed, - -- altitude = Altitude + LandHeight - -- } - -- }, - -- stopCondition = { duration = Duration - -- } - -- } - -- } - -- ) - return DCSTask end @@ -1112,7 +1273,7 @@ end -- @param #CONTROLLABLE self -- @param #number Altitude The altitude [m] to hold the position. -- @param #number Speed The speed [m/s] flying when holding the position. --- @param Core.Point#COORDINATE Coordinate (optional) The coordinate where to orbit. If the coordinate is not given, then the current position of the controllable is used. +-- @param Core.Point#COORDINATE Coordinate (Optional) The coordinate where to orbit. If the coordinate is not given, then the current position of the controllable is used. -- @return #CONTROLLABLE self function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate ) self:F2( { self.ControllableName, Altitude, Speed } ) @@ -1140,10 +1301,6 @@ function CONTROLLABLE:TaskHoldPosition() end - - - - --- (AIR) Delivering weapon on the runway. See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_bombingRunway) -- -- Make sure the aircraft has the following role: @@ -1166,40 +1323,18 @@ end function CONTROLLABLE:TaskBombingRunway(Airbase, WeaponType, WeaponExpend, AttackQty, Direction, GroupAttack) self:F2( { self.ControllableName, Airbase, WeaponType, WeaponExpend, AttackQty, Direction, GroupAttack } ) --- BombingRunway = { --- id = 'BombingRunway', --- params = { --- runwayId = AirdromeId, --- weaponType = number, --- expend = enum AI.Task.WeaponExpend, --- attackQty = number, --- direction = Azimuth, --- groupAttack = boolean, --- } --- } - - -- Defaults. - WeaponType=WeaponType or 2147485694 - WeaponExpend=WeaponExpend or AI.Task.WeaponExpend.ALL - AttackQty=AttackQty or 1 - - if Direction then - Direction=math.rad(Direction) - end - - local DCSTask - DCSTask = { id = 'BombingRunway', + local DCSTask = { + id = 'BombingRunway', params = { - runwayId = Airbase:GetID(), - weaponType = WeaponType, - expend = WeaponExpend, - attackQty = AttackQty, - direction = Direction, + runwayId = Airbase:GetID(), + weaponType = WeaponType or ENUMS.WeaponFlag.AnyBomb, + expend = WeaponExpend or AI.Task.WeaponExpend.ALL, + attackQty = AttackQty or 1, + direction = Direction and math.rad(Direction) or nil, groupAttack = GroupAttack, }, - }, + } - self:T3( { DCSTask } ) return DCSTask end @@ -1208,16 +1343,9 @@ end -- @param #CONTROLLABLE self -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskRefueling() - self:F2( { self.ControllableName } ) - --- Refueling = { --- id = 'Refueling', --- params = {} --- } local DCSTask={id='Refueling', params={}} - self:T3( { DCSTask } ) return DCSTask end @@ -1227,37 +1355,16 @@ end -- @param DCS#Vec2 Point The point where to land. -- @param #number Duration The duration in seconds to stay on the ground. -- @return #CONTROLLABLE self -function CONTROLLABLE:TaskLandAtVec2( Point, Duration ) - self:F2( { self.ControllableName, Point, Duration } ) +function CONTROLLABLE:TaskLandAtVec2(Vec2, Duration) --- Land = { --- id= 'Land', --- params = { --- point = Vec2, --- durationFlag = boolean, --- duration = Time --- } --- } - - local DCSTask - if Duration and Duration > 0 then - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = true, - duration = Duration, - }, - } - else - DCSTask = { id = 'Land', - params = { - point = Point, - durationFlag = false, - }, - } - end - - self:T3( DCSTask ) + local DCSTask = { + id = 'Land', + params = { + point = Vec2, + durationFlag = Duration and true or false, + duration = Duration, + }, + } return DCSTask end @@ -1428,21 +1535,7 @@ end -- @param #CONTROLLABLE self -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskHold() - self:F2( { self.ControllableName } ) - --- Hold = { --- id = 'Hold', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Hold', - params = { - } - } - - self:T3( { DCSTask } ) + local DCSTask = {id = 'Hold', params = {}} return DCSTask end @@ -1461,23 +1554,13 @@ end function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink ) self:F2( { self.ControllableName, AttackGroup, WeaponType, Designation, Datalink } ) --- FAC_AttackGroup = { --- id = 'FAC_AttackGroup', --- params = { --- groupId = Group.ID, --- weaponType = number, --- designation = enum AI.Task.Designation, --- datalink = boolean --- } --- } - - local DCSTask - DCSTask = { id = 'FAC_AttackGroup', + local DCSTask = { + id = 'FAC_AttackGroup', params = { - groupId = AttackGroup:GetID(), - weaponType = WeaponType, + groupId = AttackGroup:GetID(), + weaponType = WeaponType, designation = Designation, - datalink = Datalink, + datalink = Datalink, } } @@ -1491,26 +1574,18 @@ end -- @param #CONTROLLABLE self -- @param DCS#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored. -- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. --- @param #number Priority All enroute tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. +-- @param #number Priority All enroute tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default 0. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEngageTargets( Distance, TargetTypes, Priority ) self:F2( { self.ControllableName, Distance, TargetTypes, Priority } ) --- EngageTargets ={ --- id = 'EngageTargets', --- params = { --- maxDist = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargets', + local DCSTask = { + id = 'EngageTargets', params = { - maxDist = Distance, - targetTypes = TargetTypes, - priority = Priority + maxDistEnabled = Distance and true or false, + maxDist = Distance, + targetTypes = TargetTypes or {"Air"}, + priority = Priority or 0, } } @@ -1530,23 +1605,13 @@ end function CONTROLLABLE:EnRouteTaskEngageTargetsInZone( Vec2, Radius, TargetTypes, Priority ) self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } ) --- EngageTargetsInZone = { --- id = 'EngageTargetsInZone', --- params = { --- point = Vec2, --- zoneRadius = Distance, --- targetTypes = array of AttributeName, --- priority = number --- } --- } - - local DCSTask - DCSTask = { id = 'EngageTargetsInZone', + local DCSTask = { + id = 'EngageTargetsInZone', params = { - point = Vec2, - zoneRadius = Radius, + point = Vec2, + zoneRadius = Radius, targetTypes = TargetTypes or {"Air"}, - priority = Priority or 0 + priority = Priority or 0 } } @@ -1675,17 +1740,7 @@ end function CONTROLLABLE:EnRouteTaskAWACS( ) self:F2( { self.ControllableName } ) --- AWACS = { --- id = 'AWACS', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'AWACS', - params = { - } - } + local DCSTask = {id = 'AWACS', params = {}} self:T3( { DCSTask } ) return DCSTask @@ -1698,17 +1753,7 @@ end function CONTROLLABLE:EnRouteTaskTanker( ) self:F2( { self.ControllableName } ) --- Tanker = { --- id = 'Tanker', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'Tanker', - params = { - } - } + local DCSTask = {id = 'Tanker', params = {}} self:T3( { DCSTask } ) return DCSTask @@ -1723,17 +1768,7 @@ end function CONTROLLABLE:EnRouteTaskEWR( ) self:F2( { self.ControllableName } ) --- EWR = { --- id = 'EWR', --- params = { --- } --- } - - local DCSTask - DCSTask = { id = 'EWR', - params = { - } - } + local DCSTask = {id = 'EWR', params = {}} self:T3( { DCSTask } ) return DCSTask @@ -2155,12 +2190,12 @@ do -- Patrol methods if not self:IsInstanceOf( "GROUP" ) then PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP end - + DelayMin=DelayMin or 1 if not DelayMax or DelayMax we take the short cut. - + table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) @@ -2549,7 +2584,7 @@ do -- Route methods table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) end - + -- Add passing waypoint function. if WaypointFunction then local N=#route @@ -2557,10 +2592,10 @@ do -- Route methods waypoint.task = {} waypoint.task.id = "ComboTask" waypoint.task.params = {} - waypoint.task.params.tasks = {self:TaskFunction("CONTROLLABLE.___PassingWaypoint", n, N, WaypointFunction, unpack(WaypointFunctionArguments or {}))} + waypoint.task.params.tasks = {self:TaskFunction("CONTROLLABLE.___PassingWaypoint", n, N, WaypointFunction, unpack(WaypointFunctionArguments or {}))} end end - + return route, canroad end @@ -2596,7 +2631,7 @@ do -- Route methods table.insert(route, PathOnRail[2]:WaypointGround(Speed, "On Railroad")) end - + -- Add passing waypoint function. if WaypointFunction then local N=#route @@ -2604,7 +2639,7 @@ do -- Route methods waypoint.task = {} waypoint.task.id = "ComboTask" waypoint.task.params = {} - waypoint.task.params.tasks = {self:TaskFunction("CONTROLLABLE.___PassingWaypoint", n, N, WaypointFunction, unpack(WaypointFunctionArguments or {}))} + waypoint.task.params.tasks = {self:TaskFunction("CONTROLLABLE.___PassingWaypoint", n, N, WaypointFunction, unpack(WaypointFunctionArguments or {}))} end end @@ -2869,9 +2904,9 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad self:F2( self.ControllableName ) local DCSControllable = self:GetDCSObject() - + if DCSControllable then - + local DetectionVisual = ( DetectVisual and DetectVisual == true ) and Controller.Detection.VISUAL or nil local DetectionOptical = ( DetectOptical and DetectOptical == true ) and Controller.Detection.OPTICAL or nil local DetectionRadar = ( DetectRadar and DetectRadar == true ) and Controller.Detection.RADAR or nil @@ -3003,9 +3038,9 @@ function CONTROLLABLE:IsGroupDetected( Group, DetectVisual, DetectOptical, Detec for _,_unit in pairs(Group:GetUnits()) do local unit=_unit --Wrapper.Unit#UNIT if unit and unit:IsAlive() then - + local isdetected=self:IsUnitDetected(unit, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) - + if isdetected then return true end @@ -3036,23 +3071,23 @@ function CONTROLLABLE:GetDetectedUnitSet(DetectVisual, DetectOptical, DetectRada local detectedtargets=self:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) local unitset=SET_UNIT:New() - + for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object if DetectedObject and DetectedObject:isExist() and DetectedObject.id_<50000000 then local unit=UNIT:Find(DetectedObject) - + if unit and unit:IsAlive() then - + if not unitset:FindUnit(unit:GetName()) then - unitset:AddUnit(unit) + unitset:AddUnit(unit) end - + end end end - + return unitset end @@ -3073,24 +3108,24 @@ function CONTROLLABLE:GetDetectedGroupSet(DetectVisual, DetectOptical, DetectRad local detectedtargets=self:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK) local groupset=SET_GROUP:New() - + for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object if DetectedObject and DetectedObject:isExist() and DetectedObject.id_<50000000 then local unit=UNIT:Find(DetectedObject) - + if unit and unit:IsAlive() then local group=unit:GetGroup() - + if group and not groupset:FindGroup(group:GetName()) then groupset:AddGroup(group) end - + end end end - + return groupset end @@ -3714,30 +3749,30 @@ end -- @return #boolean true if Controllable contains Helicopters. function CONTROLLABLE:IsHelicopter() self:F2() - + local DCSObject = self:GetDCSObject() - + if DCSObject then local Category = DCSObject:getDesc().category return Category == Unit.Category.HELICOPTER end - + return nil end --- Sets Controllable Option for Restriction of Afterburner. -- @param #CONTROLLABLE self --- @param #boolean RestrictBurner If true, restrict burner. If false or nil, allow (unrestrict) burner. +-- @param #boolean RestrictBurner If true, restrict burner. If false or nil, allow (unrestrict) burner. function CONTROLLABLE:OptionRestrictBurner(RestrictBurner) self:F2({self.ControllableName}) - + local DCSControllable = self:GetDCSObject() - + if DCSControllable then local Controller = self:_GetController() - + if Controller then - + -- Issue https://github.com/FlightControl-Master/MOOSE/issues/1216 if RestrictBurner == true then if self:IsAir() then @@ -3748,8 +3783,8 @@ function CONTROLLABLE:OptionRestrictBurner(RestrictBurner) Controller:setOption(16, false) end end - + end end - + end