From 902c001aa4c28bc4087d18c85f8ec5cc9acb8a73 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 28 Oct 2021 10:04:01 +0200 Subject: [PATCH] OPS Improved OPSTRANSPORT --- Moose Development/Moose/DCS.lua | 5 + Moose Development/Moose/Ops/FlightGroup.lua | 63 ++++++++++- Moose Development/Moose/Ops/NavyGroup.lua | 5 +- Moose Development/Moose/Ops/OpsGroup.lua | 36 ++++-- Moose Development/Moose/Ops/OpsTransport.lua | 7 +- .../Moose/Wrapper/Identifiable.lua | 26 ++--- .../Moose/Wrapper/Positionable.lua | 105 ++++++++++++++---- 7 files changed, 184 insertions(+), 63 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 7c9328cc9..24d7862d0 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1150,6 +1150,11 @@ do -- Unit -- @function [parent=#Unit] getAmmo -- @param #Unit self -- @return #Unit.Ammo + + --- Returns the number of infantry that can be embark onto the aircraft. Only returns a value if run on airplanes or helicopters. Returns nil if run on ground or ship units. + -- @function [parent=#Unit] getDescentCapacity + -- @param #Unit self + -- @return #number Number of soldiers that embark. --- Returns the unit sensors. -- @function [parent=#Unit] getSensors diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 877457b99..65aa7c3f5 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -2248,7 +2248,7 @@ function FLIGHTGROUP:onbeforeRTB(From, Event, To, airbase, SpeedTo, SpeedHold) if not self.group:IsAirborne(true) then -- this should really not happen, either the AUFTRAG is cancelled before the group was airborne or it is stuck at the ground for some reason - self:I(self.lid..string.format("WARNING: Group is not AIRBORNE ==> RTB event is suspended for 20 sec")) + self:I(self.lid..string.format("WARNING: Group [%s] is not AIRBORNE ==> RTB event is suspended for 20 sec", self:GetState())) allowed=false Tsuspend=-20 local groupspeed = self.group:GetVelocityMPS() @@ -2256,7 +2256,7 @@ function FLIGHTGROUP:onbeforeRTB(From, Event, To, airbase, SpeedTo, SpeedHold) self.RTBRecallCount = self.RTBRecallCount+1 end if self.RTBRecallCount>6 then - self:I(self.lid..string.format("WARNING: Group is not moving and was called RTB %d times. Assuming a problem and despawning!", self.RTBRecallCount)) + self:I(self.lid..string.format("WARNING: Group [%s] is not moving and was called RTB %d times. Assuming a problem and despawning!", self:GetState(), self.RTBRecallCount)) self.RTBRecallCount=0 self:Despawn(5) return @@ -2342,6 +2342,65 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp end + +--- On before "LandAtAirbase" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. +function FLIGHTGROUP:onbeforeLandAtAirbase(From, Event, To, airbase) + + if self:IsAlive() then + + local allowed=true + local Tsuspend=nil + + if airbase==nil then + self:E(self.lid.."ERROR: Airbase is nil in LandAtAirase() call!") + allowed=false + end + + -- Check that coaliton is okay. We allow same (blue=blue, red=red) or landing on neutral bases. + if airbase and airbase:GetCoalition()~=self.group:GetCoalition() and airbase:GetCoalition()>0 then + self:E(self.lid..string.format("ERROR: Wrong airbase coalition %d in LandAtAirbase() call! We allow only same as group %d or neutral airbases 0", airbase:GetCoalition(), self.group:GetCoalition())) + return false + end + + if self.currbase and self.currbase:GetName()==airbase:GetName() then + self:E(self.lid.."WARNING: Currbase is already same as LandAtAirbase airbase. LandAtAirbase canceled!") + return false + end + + -- Check if the group has landed at an airbase. If so, we lost control and RTBing is not possible (only after a respawn). + if self:IsLanded() then + self:E(self.lid.."WARNING: Flight has already landed. LandAtAirbase canceled!") + return false + end + + if self:IsParking() then + allowed=false + Tsuspend=-30 + self:E(self.lid.."WARNING: Flight is parking. LandAtAirbase call delayed by 30 sec") + elseif self:IsTaxiing() then + allowed=false + Tsuspend=-1 + self:E(self.lid.."WARNING: Flight is parking. LandAtAirbase call delayed by 1 sec") + end + + if Tsuspend and not allowed then + self:__LandAtAirbase(Tsuspend, airbase) + end + + return allowed + else + self:E(self.lid.."WARNING: Group is not alive! LandAtAirbase call not allowed") + return false + end + +end + + --- On after "LandAtAirbase" event. -- @param #FLIGHTGROUP self -- @param #string From From state. diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index fee9c4a9f..d76cd10b4 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1462,7 +1462,7 @@ end --- Add an a waypoint to the route. -- @param #NAVYGROUP self --- @param Core.Point#COORDINATE Coordinate The coordinate of the waypoint. Use COORDINATE:SetAltitude(altitude) to define the altitude. +-- @param Core.Point#COORDINATE Coordinate The coordinate of the waypoint. Use `COORDINATE:SetAltitude()` to define the altitude. -- @param #number Speed Speed in knots. Default is default cruise speed or 70% of max speed. -- @param #number AfterWaypointWithID Insert waypoint after waypoint given ID. Default is to insert as last waypoint. -- @param #number Depth Depth at waypoint in meters. Only for submarines. @@ -1514,9 +1514,6 @@ function NAVYGROUP:_InitGroup(Template) -- Get template of group. local template=Template or self:_GetTemplate() - --TODO: Submarine check - --self.isSubmarine=self.group:IsSubmarine() - -- Ships are always AI. self.isAI=true diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7596646bc..f93519b40 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -6846,7 +6846,7 @@ function OPSGROUP:AddWeightCargo(UnitName, Weight) -- For airborne units, we set the weight in game. if self.isFlightgroup then - --trigger.action.setUnitInternalCargo(element.name, element.weightCargo) --https://wiki.hoggitworld.com/view/DCS_func_setUnitInternalCargo + trigger.action.setUnitInternalCargo(element.name, element.weightCargo) --https://wiki.hoggitworld.com/view/DCS_func_setUnitInternalCargo end end @@ -7515,10 +7515,18 @@ function OPSGROUP:onafterUnloading(From, Event, To) if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then -- Disembark to carrier. - local needscarrier=false - local carrier=nil - local carrierGroup=nil + local needscarrier=false --#boolean + local carrier=nil --Ops.OpsGroup#OPSGROUP.Element + local carrierGroup=nil --Ops.OpsGroup#OPSGROUP + -- Try to get the OPSGROUP if deploy zone is a ship. + if zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then + local shipname=zone:GetAirbase():GetName() + local ship=UNIT:FindByName(shipname) + local group=ship:GetGroup() + carrierGroup=_DATABASE:GetOpsGroup(group:GetName()) + carrier=carrierGroup:GetElementByName(shipname) + end if self.cargoTZC.DisembarkCarriers and #self.cargoTZC.DisembarkCarriers>0 then @@ -7554,8 +7562,10 @@ function OPSGROUP:onafterUnloading(From, Event, To) -- Issue warning. self:E(self.lid.."ERROR: Deploy/disembark zone is a ZONE_AIRBASE of a ship! Where to put the cargo? Dumping into the sea, sorry!") - --TODO: Dumb into sea. - + + -- Unload but keep "in utero" (no coordinate provided). + self:Unload(cargo.opsgroup) + else --- @@ -7800,9 +7810,6 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) -- Check if this was the current transport. if self.cargoTransport and self.cargoTransport.uid==CargoTransport.uid then - -- This is not a carrier anymore. - self:_NewCarrierStatus(OPSGROUP.CarrierStatus.NOTCARRIER) - -- Checks if self:IsPickingup() then -- Delete pickup waypoint? @@ -7810,6 +7817,8 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) if wpindex then self:RemoveWaypoint(wpindex) end + -- Remove landing airbase. + self.isLandingAtAirbase=nil elseif self:IsLoading() then -- Nothing to do? elseif self:IsTransporting() then @@ -7818,6 +7827,9 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) -- Nothing to do? end + -- This is not a carrier anymore. + self:_NewCarrierStatus(OPSGROUP.CarrierStatus.NOTCARRIER) + -- Startup uncontrolled aircraft to allow it to go back. if self:IsFlightgroup() then @@ -7939,7 +7951,7 @@ function OPSGROUP:onafterTransportCancel(From, Event, To, Transport) -- Transport delivered. if calldelivered then - self:Delivered(Transport) + self:__Delivered(-2, Transport) end else @@ -9067,7 +9079,7 @@ function OPSGROUP._PassingWaypoint(opsgroup, uid) -- Land at current pos and wait for 60 min max. local coordinate=nil if opsgroup.cargoTZC then - coordinate=opsgroup.cargoTZC.PickupZone:GetCoordinate() + coordinate=opsgroup.cargoTZC.PickupZone:GetRandomCoordinate(nil, nil, {land.SurfaceType.LAND}) else coordinate=opsgroup:GetCoordinate() end @@ -9088,7 +9100,7 @@ function OPSGROUP._PassingWaypoint(opsgroup, uid) -- Land at current pos and wait for 60 min max. local coordinate=nil if opsgroup.cargoTZC then - coordinate=opsgroup.cargoTZC.DeployZone:GetCoordinate() + coordinate=opsgroup.cargoTZC.DeployZone:GetRandomCoordinate(nil, nil, {land.SurfaceType.LAND}) else coordinate=opsgroup:GetCoordinate() end diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index ec7f9ac44..d515ae44b 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -385,13 +385,11 @@ function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone) --- Triggers the FSM event "Cancel". -- @function [parent=#OPSTRANSPORT] Cancel -- @param #OPSTRANSPORT self - -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "Cancel" after a delay. -- @function [parent=#OPSTRANSPORT] __Cancel -- @param #OPSTRANSPORT self -- @param #number delay Delay in seconds. - -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- On after "Cancel" event. -- @function [parent=#OPSTRANSPORT] OnAfterCancel @@ -399,7 +397,6 @@ function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. --- Triggers the FSM event "Loaded". @@ -1789,7 +1786,7 @@ function OPSTRANSPORT:onafterCancel(From, Event, To) local Ngroups = #self.carriers -- Debug info. - self:I(self.lid..string.format("CANCELLING transport in status %s. Will wait for %d carrier groups to report DONE before evaluation", self.status, Ngroups)) + self:I(self.lid..string.format("CANCELLING transport in status %s. Will wait for %d carrier groups to report DONE before evaluation", self:GetState(), Ngroups)) -- Time stamp. self.Tover=timer.getAbsTime() @@ -1848,7 +1845,7 @@ function OPSTRANSPORT:onafterCancel(From, Event, To) -- Special mission states. if self:IsPlanned() or self:IsQueued() or self:IsRequested() or Ngroups==0 then - self:T(self.lid..string.format("Cancelled transport was in %s stage with %d carrier groups assigned and alive. Call it DELIVERED!", self.status, Ngroups)) + self:T(self.lid..string.format("Cancelled transport was in %s stage with %d carrier groups assigned and alive. Call it DELIVERED!", self:GetState(), Ngroups)) self:Delivered() end diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 5b340aec8..4f3fabeae 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -56,8 +56,7 @@ end -- If the Identifiable is not alive, nil is returned. -- If the Identifiable is alive, true is returned. -- @param #IDENTIFIABLE self --- @return #boolean true if Identifiable is alive. --- @return #nil if the Identifiable is not existing or is not alive. +-- @return #boolean true if Identifiable is alive or `#nil` if the Identifiable is not existing or is not alive. function IDENTIFIABLE:IsAlive() self:F3( self.IdentifiableName ) @@ -77,11 +76,8 @@ end --- Returns DCS Identifiable object name. -- The function provides access to non-activated objects too. -- @param #IDENTIFIABLE self --- @return #string The name of the DCS Identifiable. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return #string The name of the DCS Identifiable or `#nil`. function IDENTIFIABLE:GetName() - self:F2( self.IdentifiableName ) - local IdentifiableName = self.IdentifiableName return IdentifiableName end @@ -148,8 +144,7 @@ end --- Returns coalition of the Identifiable. -- @param #IDENTIFIABLE self --- @return DCS#coalition.side The side of the coalition. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return DCS#coalition.side The side of the coalition or `#nil` The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetCoalition() self:F2( self.IdentifiableName ) @@ -190,8 +185,7 @@ end --- Returns country of the Identifiable. -- @param #IDENTIFIABLE self --- @return DCS#country.id The country identifier. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return DCS#country.id The country identifier or `#nil` The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetCountry() self:F2( self.IdentifiableName ) @@ -222,8 +216,7 @@ end --- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. -- @param #IDENTIFIABLE self --- @return DCS#Object.Desc The Identifiable descriptor. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return DCS#Object.Desc The Identifiable descriptor or `#nil` The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetDesc() self:F2( self.IdentifiableName ) @@ -242,8 +235,7 @@ end --- Check if the Object has the attribute. -- @param #IDENTIFIABLE self -- @param #string AttributeName The attribute name. --- @return #boolean true if the attribute exists. --- @return #nil The DCS Identifiable is not existing or alive. +-- @return #boolean true if the attribute exists or `#nil` The DCS Identifiable is not existing or alive. function IDENTIFIABLE:HasAttribute( AttributeName ) self:F2( self.IdentifiableName ) @@ -266,8 +258,10 @@ function IDENTIFIABLE:GetCallsign() return '' end - +--- Gets the threat level. +-- @param #IDENTIFIABLE self +-- @return #number Threat level. +-- @return #string Type. function IDENTIFIABLE:GetThreatLevel() - return 0, "Scenery" end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 49172b5a9..6dc5ddf7b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1368,8 +1368,6 @@ do -- Cargo return self.__.Cargo end - - --- Remove cargo. -- @param #POSITIONABLE self -- @param Core.Cargo#CARGO Cargo @@ -1414,6 +1412,16 @@ do -- Cargo return ItemCount end + --- Get the number of infantry soldiers that can be embarked into an aircraft (airplane or helicopter). + -- Returns `nil` for ground or ship units. + -- @param #POSITIONABLE self + -- @return #number Descent number of soldiers that fit into the unit. Returns `#nil` for ground and ship units. + function POSITIONABLE:GetTroopCapacity() + local DCSunit=self:GetDCSObject() --DCS#Unit + local capacity=DCSunit:getDescentCapacity() + return capacity + end + --- Get Cargo Bay Free Weight in kg. -- @param #POSITIONABLE self -- @return #number CargoBayFreeWeight @@ -1433,43 +1441,78 @@ do -- Cargo --- Set Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self - -- @param #number WeightLimit + -- @param #number WeightLimit (Optional) Weight limit in kg. If not given, the value is taken from the descriptors or hard coded. function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) - if WeightLimit then + if WeightLimit then + --- + -- User defined value + --- self.__.CargoBayWeightLimit = WeightLimit elseif self.__.CargoBayWeightLimit~=nil then -- Value already set ==> Do nothing! else - -- If weightlimit is not provided, we will calculate it depending on the type of unit. + --- + -- Weightlimit is not provided, we will calculate it depending on the type of unit. + --- + + -- Descriptors that contain the type name and for aircraft also weights. + local Desc = self:GetDesc() + self:F({Desc=Desc}) + + -- Unit type name. + local TypeName=Desc.typeName or "Unknown Type" -- When an airplane or helicopter, we calculate the weightlimit based on the descriptor. if self:IsAir() then - local Desc = self:GetDesc() - self:F({Desc=Desc}) + -- Max takeoff weight if DCS descriptors have unrealstic values. local Weights = { - ["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry. - ["C-130"] = 22000 --The real value cannot be used, because it loads way too much apcs and infantry. + -- C-17A + -- Wiki says: max=265,352, empty=128,140, payload=77,516 (134 troops, 1 M1 Abrams tank, 2 M2 Bradley or 3 Stryker) + -- DCS says: max=265,350, empty=125,645, fuel=132,405 ==> Cargo Bay=7300 kg with a full fuel load (lot of fuel!) and 73300 with half a fuel load. + --["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry. + -- C-130: + -- DCS says: max=79,380, empty=36,400, fuel=10,415 kg ==> Cargo Bay=32,565 kg with fuel load. + -- Wiki says: max=70,307, empty=34,382, payload=19,000 kg (92 passengers, 2-3 Humvees or 2 M113s), max takeoff weight 70,037 kg. + -- Here we say two M113s should be transported. Each one weights 11,253 kg according to DCS. So the cargo weight should be 23,000 kg with a full load of fuel. + -- This results in a max takeoff weight of 69,815 kg (23,000+10,415+36,400), which is very close to the Wiki value of 70,037 kg. + ["C-130"] = 70000, } - local Weight=Weights[Desc.typeName] + -- Max (takeoff) weight (empty+fuel+cargo weight). + local massMax= Desc.massMax or 0 - if not Weight then - local fuelrel=self:GetFuel() or 1.0 - local Mmax=Desc.massMax or 0 - local Mempty=Desc.massEmpty or 0 - local Mfuel=Desc.fuelMassMax and Desc.fuelMassMax*fuelrel or 0 - Weight=Mmax-(Mempty+Mfuel) - self:I(string.format("Setting Cargo bay weight limit [%s]=%d kg (Mass max=%d, empty=%d, fuel=%d kg, fuelrel=%.3f)", Desc.typeName or "unknown type", Weight, Mmax, Mempty, Mfuel, fuelrel)) + -- Adjust value if set above. + local maxTakeoff=Weights[TypeName] + if maxTakeoff then + massMax=maxTakeoff end - - self.__.CargoBayWeightLimit = Weight + + -- Empty weight. + local massEmpty=Desc.massEmpty or 0 + + -- Fuel. The descriptor provides the max fuel mass in kg. This needs to be multiplied by the relative fuel amount to calculate the actual fuel mass on board. + local massFuelMax=Desc.fuelMassMax or 0 + local relFuel=self:GetFuel() or 1.0 + local massFuel=massFuelMax*relFuel + + -- Number of soldiers according to DCS function + local troopcapacity=self:GetTroopCapacity() or 0 + + -- Calculate max cargo weight, which is the max (takeoff) weight minus the empty weight minus the actual fuel weight. + local CargoWeight=massMax-(massEmpty+massFuel) + + -- Debug info. + self:I(string.format("Setting Cargo bay weight limit [%s]=%d kg (Mass max=%d, empty=%d, fuelMax=%d kg (rel=%.3f), fuel=%d kg", TypeName, CargoWeight, massMax, massEmpty, massFuelMax, relFuel, massFuel)) + self:I(string.format("Descent Troop Capacity=%d ==> %d kg (for 95 kg soldier)", troopcapacity, troopcapacity*95)) + + -- Set value. + self.__.CargoBayWeightLimit = CargoWeight elseif self:IsShip() then - local Desc = self:GetDesc() - self:F({Desc=Desc}) + -- Hard coded cargo weights in kg. local Weights = { ["Type_071"] = 245000, ["LHA_Tarawa"] = 500000, @@ -1482,11 +1525,11 @@ do -- Cargo ["speedboat"] = 500, -- 500 kg ~ 5 persons ["Seawise_Giant"] =261000000, -- Gross tonnage is 261,000 tonns. } - self.__.CargoBayWeightLimit = ( Weights[Desc.typeName] or 50000 ) + self.__.CargoBayWeightLimit = ( Weights[TypeName] or 50000 ) else - local Desc = self:GetDesc() + -- Hard coded number of soldiers. local Weights = { ["AAV7"] = 25, ["Bedford_MWD"] = 8, -- new by kappa @@ -1532,7 +1575,8 @@ do -- Cargo ["VAB_Mephisto"] = 8, -- new by Apple } - local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 + -- Assuming that each passenger weighs 95 kg on average. + local CargoBayWeightLimit = ( Weights[TypeName] or 0 ) * 95 self.__.CargoBayWeightLimit = CargoBayWeightLimit end @@ -1540,6 +1584,19 @@ do -- Cargo self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit}) end + + --- Get Cargo Bay Weight Limit in kg. + -- @param #POSITIONABLE self + -- @return #number Max cargo weight in kg. + function POSITIONABLE:GetCargoBayWeightLimit() + + if self.__.CargoBayWeightLimit==nil then + self:SetCargoBayWeightLimit() + end + + return self.__.CargoBayWeightLimit + end + end --- Cargo --- Signal a flare at the position of the POSITIONABLE.