diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 0bd2d336f..f3eba25c2 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -77,11 +77,11 @@ jobs: - name: Install markdown (prereq for LuaDocumentor) run: | - sudo luarocks install markdown 0.32-2 + sudo luarocks install markdown 0.33-1 - name: Install penlight (prereq for LuaDocumentor) run: | - sudo luarocks install penlight 1.11.0-1 + sudo luarocks install penlight 1.14.0-3 - name: Install metalua-compiler (prereq for LuaDocumentor) run: | diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index e86b88267..a4448d4af 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -833,6 +833,16 @@ do -- FSM return self._handler( self, EventName, ... ) end end + + --- Clear scheduled FSM event. + -- @param #FSM self + -- @param #string EventName Event name. + function FSM:_ClearFSMEvent( EventName ) + if self._EventSchedules[EventName] then + self.CallScheduler:Remove( self._EventSchedules[EventName] ) + self._EventSchedules[EventName]=nil + end + end --- Go sub. -- @param #FSM self diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 0d567c472..b3e4f82cf 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -979,6 +979,28 @@ do -- SET_BASE return ObjectNames end + + --- Checks whether all or optionally any objects is inside a given zone. + -- @param #SET_BASE self + -- @param Core.Zone#ZONE Zone The zone. + -- @param #boolean Any If `true`, at least one object has to be inside the zone. If `false` or `nil`, all objects need to be in the zone. + -- @return #boolean Retruns `true` if objects are in the zone and `false` otherwise. + function SET_BASE:IsInZone(Zone, Any) + + for ObjectName, Object in pairs(self.Set) do + local object=Object --Wrapper.Positionable#POSITIONABLE + local inzone=object:IsInZone(Zone) + if inzone and Any then + -- We want at least one and this one is + return true + elseif not inzone then + -- We want all but at least one is not + return false + end + end + + return true + end --- Flushes the current SET_BASE contents in the log ... (for debugging reasons). -- @param #SET_BASE self diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index a3ac50a46..23bf9f822 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -458,6 +458,7 @@ _AUFTRAGSNR=0 -- @field #string NOTHING Nothing. -- @field #string PATROLRACETRACK Patrol Racetrack. -- @field #string STRAFING Strafing run. +-- @field #string FREIGHTTRANSPORT Freight transport. AUFTRAG.Type={ ANTISHIP="Anti Ship", AWACS="AWACS", @@ -506,6 +507,7 @@ AUFTRAG.Type={ NOTHING="Nothing", PATROLRACETRACK="Patrol Racetrack", STRAFING="Strafing", + FREIGHTTRANSPORT="FREIGHTTRANSPORT", } --- Special task description. @@ -674,7 +676,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="1.3.0" +AUFTRAG.version="1.4.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2141,6 +2143,7 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC end --- **[AIR ROTARY]** Create a CARGO TRANSPORT mission. +-- This mission is for helicopters only, which transport cargo externally via slingload. -- **Important Note:** -- The dropoff zone has to be a zone defined in the Mission Editor. This is due to a restriction in the used DCS task, which takes the zone ID as input. -- Only ME zones have an ID that can be referenced. @@ -2172,6 +2175,63 @@ function AUFTRAG:NewCARGOTRANSPORT(StaticCargo, DropZone) return mission end +--- **[AIR]** Create a FREIGHT TRANSPORT mission. +-- This mission type can be used to transport cargo items internally via suitable transport aircraft (planes and helicopters), e.g. C-130 or CH-47. +-- It supports transporting one or multiple cargos (weight limits are not checked). +-- +-- All cargo must be within a 40 meter radius around the transport aircraft for the mission to start. +-- The mission is successful if any cargo item is delivered to the destination. +-- +-- This mission type uses the underlying DCS tasks: "Cargo Transportation (internal)", "Cargo Unload" +-- @param #AUFTRAG self +-- @param Wrapper.Static#STATIC StaticCargo Static cargo object. Can also be passed as a `SET_STATIC` object. +-- @param Wrapper.Airbase#AIRBASE Destination Destination airbase, where the cargo is unloaded. +-- @return #AUFTRAG self +function AUFTRAG:NewFREIGHTTRANSPORT(StaticCargo, Destination) + + -- Check if Destination is given + if Destination==nil then + self:E(self.lid..string.format("ERROR: Destination is nil for AUFTRAG:NewFREIGHTTRANSPORT! You must specify the destination airbase")) + return nil + elseif type(Destination)=="string" then + Destination=AIRBASE:FindByName(Destination) + end + + -- Check if Cargo is given + if StaticCargo==nil then + self:E(self.lid..string.format("ERROR: StaticCargo is nil for AUFTRAG:NewFREIGHTTRANSPORT! You must specify the static object that represents the cargo")) + return nil + elseif type(StaticCargo)=="string" then + StaticCargo=STATIC:FindByName(StaticCargo) + end + + -- Convert static to a set if necessary + if StaticCargo:IsInstanceOf("STATIC") then + local StaticCargoSet=SET_STATIC:New() --Core.Set#SET_STATIC + StaticCargoSet:AddCargo(StaticCargo) + StaticCargo=StaticCargoSet + end + + local mission=AUFTRAG:New(AUFTRAG.Type.FREIGHTTRANSPORT) + + mission:_TargetFromObject(StaticCargo) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.FREIGHTTRANSPORT) + + -- Set ROE and ROT. + mission.optionROE=ENUMS.ROE.ReturnFire + mission.optionROT=ENUMS.ROT.PassiveDefense + + mission.categories={AUFTRAG.Category.HELICOPTER, AUFTRAG.Category.AIRCRAFT} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.cargo=StaticCargo + mission.DCStask.params.destination=Destination + + return mission +end + --[[ --- **[AIR, GROUND, NAVAL]** Create a OPS TRANSPORT mission. @@ -3060,7 +3120,7 @@ end --- **[LEGION, COMMANDER, CHIEF]** Set the repeat delay in seconds after a mission is successful/failed. Only valid if the mission is handled by a LEGION (AIRWING, BRIGADE, FLEET) or higher level. -- @param #AUFTRAG self --- @param #number Nrepeat Repeat delay in seconds. Default 1. +-- @param #number RepeatDelay Repeat delay in seconds. Default 1. -- @return #AUFTRAG self function AUFTRAG:SetRepeatDelay(RepeatDelay) self.repeatDelay = RepeatDelay @@ -4173,6 +4233,28 @@ function AUFTRAG:IsReadyToGo() if not startme then return false end + + if self.type==AUFTRAG.Type.FREIGHTTRANSPORT then + + local cargoset=self.DCStask.params.cargo --Core.Set#SET_STATIC + + for _,_opsgroup in pairs(self:GetOpsGroups()) do + local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP + + local vec2=opsgroup.group:GetFirstUnitAlive():GetVec2() + + local zone=ZONE_RADIUS:New("Freighttransport", vec2, 40, true) + + local inzone=cargoset:IsInZone(zone) + + if not inzone then + self:T(self.lid.."FREIGHTTRANSPORT: cargo is not inside zone ==> mission not ready to start yet!") + return false + end + + end + + end -- We're good to go! @@ -4517,6 +4599,22 @@ function AUFTRAG:Evaluate() else failed=true end + + elseif self.type==AUFTRAG.Type.FREIGHTTRANSPORT then + + local cargoset=self.DCStask.params.cargo --Core.Set#SET_STATIC + + -- Get the destination airbase zone + local dest=self.DCStask.params.destination --Wrapper.Airbase#AIRBASE + local zone=dest:GetZone() + + -- Check if ANY cargo is inside the zone (might want to make it optional that all cargo needs to be) + local inzone=cargoset:IsInZone(zone, true) + + if not inzone then + self:I(self.lid.."FF Freight/cargo not delivered to airbase zone") + failed=true + end elseif self.type==AUFTRAG.Type.RESCUEHELO then @@ -5613,6 +5711,27 @@ function AUFTRAG:GetTargetLife() end end +--- Get cargo items as set SET object. +-- This returns the cargo item(s) as set `SET` object for mission types `CARGOTRANSPORT`, `TROOPTRANSPORT` and `FREIGHTTRANSPORT`. +-- @param #AUFTRAG self +-- @return Core.Set#SET_BASE The cargo set. +function AUFTRAG:GetCargoSet() + + if self.type==AUFTRAG.Type.CARGOTRANSPORT then + local set=SET_STATIC:New() + set:AddObject(self.DCStask.params.cargo) + return set + elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then + return self.transportGroupSet + elseif self.type==AUFTRAG.Type.FREIGHTTRANSPORT then + return self.DCStask.params.cargo + else + self:E(self.lid.."ERROR: GetCargoSet() is only for transport types!") + return nil + end + +end + --- Get target. -- @param #AUFTRAG self -- @return Ops.Target#TARGET The target object. Could be many things. @@ -6201,8 +6320,9 @@ end --- Get DCS task table for the given mission. -- @param #AUFTRAG self +-- @param Wrapper.Group#GROUP MissionGroup (Optional) Group that is supposed to carry out the mission. This might not exist when the AUFTRAG is created but only when the AUFTRAG is passed to a certain group. -- @return DCS#Task The DCS task table. If multiple tasks are necessary, this is returned as a combo task. -function AUFTRAG:GetDCSMissionTask() +function AUFTRAG:GetDCSMissionTask(MissionGroup) local DCStasks={} @@ -6545,6 +6665,34 @@ function AUFTRAG:GetDCSMissionTask() table.insert(DCStasks, TaskCargoTransportation) + elseif self.type==AUFTRAG.Type.FREIGHTTRANSPORT then + + ------------------------------ + -- FREIGHTTRANSPORT Mission -- + ------------------------------ + + local statics=self.engageTarget:GetObjects() + + for _, StaticObject in pairs(statics) do + local static=StaticObject --Wrapper.Static#STATIC + + self:T(static) + + -- Task to unload the cargo + local TaskCargoUnload={ + ["id"] = "CargoUnloadPlane", + ["params"] = + { + ["groupId"] = static:GetID(), + ["unitId"] = static:GetID(), + } + } + + + table.insert(DCStasks, TaskCargoUnload) + + end + elseif self.type==AUFTRAG.Type.RESCUEHELO then ------------------------- @@ -7109,6 +7257,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType) mtask=ENUMS.MissionTask.TRANSPORT elseif MissionType==AUFTRAG.Type.CARGOTRANSPORT then mtask=ENUMS.MissionTask.TRANSPORT + elseif MissionType==AUFTRAG.Type.FREIGHTTRANSPORT then + mtask=ENUMS.MissionTask.TRANSPORT elseif MissionType==AUFTRAG.Type.ARMORATTACK then mtask=ENUMS.MissionTask.NOTHING elseif MissionType==AUFTRAG.Type.HOVER then diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7a1d8d3df..c5617c94c 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5946,6 +5946,9 @@ function OPSGROUP:RouteToMission(mission, delay) -- Debug info. self:T(self.lid..string.format("Route To Mission")) + + -- Delay in seconds before cruise or updateroute is called. + local delayGo=-1 -- Catch dead or stopped groups. if self:IsDead() or self:IsStopped() then @@ -6150,7 +6153,65 @@ function OPSGROUP:RouteToMission(mission, delay) end end - + + elseif mission.type==AUFTRAG.Type.FREIGHTTRANSPORT then + + --- + -- FREIGHTTRANSPORT + --- + + local destination=mission.DCStask.params.destination + local cargo=mission.DCStask.params.cargo + + -- Set the waypoint coordinate directly above the airbase. + -- The only way to ensure the cargo is delivered there, because when the task is executed, the cargo is delivered to the closest airbase. + -- Hopefully, ED will change the behaviour of this task but at the moment, it is what it is. + waypointcoord=destination:GetCoordinate() + + -- Get additional parameters + mission.DCStask.params.destination=destination --Wrapper.Airbase#AIRBASE + mission.DCStask.params.cargo=cargo --Core.Set#SET_STATIC + + -- Get transport unit + local unit=self.group:GetFirstUnit() + local unitIdTransport=unit:GetID() + local vec2=unit:GetVec2() + + -- Create tasks to load/transport statics cargos + local tasks={} + for StaticName, StaticObject in pairs(cargo:GetSet()) do + local static=StaticObject --Wrapper.Static#STATIC + + -- Task to transport cargo. + local TaskCargoTransportation={ + id = "CargoTransportationPlane", + params = { + x=vec2.x, + y=vec2.y, + unitIdTransport=unitIdTransport, + groupId=static:GetID(), + unitId=static:GetID(), + } + } + + table.insert(tasks, TaskCargoTransportation) + end + + -- If we have multiple tasks, we create a combo task + local TaskCargo=nil + if #tasks==1 then + TaskCargo=tasks[1] + else + TaskCargo=CONTROLLABLE.TaskCombo(nil, tasks) + end + + -- We set the task to load the cargo into the aircraft. + -- We must be careful when calling updateroute because there the task is overwritten. + -- We also clear present "UpdateRoute" FSM events + self:_ClearFSMEvent( "UpdateRoute" ) + delayGo=-30 + self.group:SetTask(TaskCargo) + elseif mission.type==AUFTRAG.Type.ARTY then --- @@ -6309,7 +6370,7 @@ function OPSGROUP:RouteToMission(mission, delay) elseif self:IsNavygroup() then self:Cruise(SpeedToMission) elseif self:IsFlightgroup() then - self:UpdateRoute() + self:__UpdateRoute(delayGo) end end diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index f1d499109..1281bbf64 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -131,14 +131,24 @@ ENUMS.WeaponFlag={ Cannons = 805306368, -- GUN_POD + BuiltInCannon --- Torpedo Torpedo = 4294967296, + --- Decoy + Decoys = 8589934592, + --- Shell + SmokeShell = 17179869184, + IlluminationShell = 34359738368, + MarkerShell = 51539607552, + MarkerWeapon = 51539620864, + SubmunitionDispenserShell = 68719476736, + ConventionalShell = 206963736576, --- -- Even More Genral - Auto = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) - AutoDCS = 1073741822, -- Something if often see - AnyAG = 2956984318, -- Any Air-To-Ground Weapon - AnyAA = 264241152, -- Any Air-To-Air Weapon - AnyUnguided = 2952822768, -- Any Unguided Weapon - AnyGuided = 268402702, -- Any Guided Weapon + Auto = 265214230526, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons + Torpedos) + AutoDCS = 1073741822, -- Something if often see + AnyAG = 2956984318, -- Any Air-To-Ground Weapon + AnyAA = 264241152, -- Any Air-To-Air Weapon + AnyUnguided = 2952822768, -- Any Unguided Weapon + AnyGuided = 268402702, -- Any Guided Weapon + AnyShell = 258503344128, -- Any Shell } --- Weapon types by category. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerator on hoggit wiki. @@ -220,11 +230,11 @@ ENUMS.WeaponType.Torpedo={ } ENUMS.WeaponType.Any={ -- General combinations - Weapon = 3221225470, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons) - AG = 2956984318, -- Any Air-To-Ground Weapon - AA = 264241152, -- Any Air-To-Air Weapon - Unguided = 2952822768, -- Any Unguided Weapon - Guided = 268402702, -- Any Guided Weapon + Weapon = 265214230526, -- Any Weapon (AnyBomb + AnyRocket + AnyMissile + Cannons + Torpedos) + AG = 2956984318, -- Any Air-To-Ground Weapon + AA = 264241152, -- Any Air-To-Air Weapon + Unguided = 2952822768, -- Any Unguided Weapon + Guided = 268402702, -- Any Guided Weapon }