This commit is contained in:
Applevangelist
2026-02-08 12:08:00 +01:00
parent 62dcfdb7a2
commit 5a9277de90
4 changed files with 99 additions and 199 deletions
+85 -192
View File
@@ -35,7 +35,6 @@
-- ===
--
-- ### Author: **funkyfranky**
-- ### Co-author: FlightControl (cargo dispatcher classes)
--
-- ===
--
@@ -132,14 +131,7 @@
-- a reasonable degree in DCS at the moment and hence cannot be used yet.
--
-- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modeled
-- in a realistic way by using the corresponding cargo dispatcher classes, i.e.
--
-- * @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}
-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER}
-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}
--
-- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. Howver, note that
-- the dispatchers as of yet cannot use user defined off road paths for example since they are classes of their own and use a different routing logic.
-- in a realistic way by using the @{Ops.OpsTransport#OPSTRANSPORT} class.
--
-- ===
--
@@ -230,18 +222,6 @@
-- of 630 kg. This is important as groups cannot be split between carrier units when transporting, i.e. the total weight of the whole group must be smaller than the
-- cargo bay of the transport carrier.
--
-- ### Setting the Load Radius
-- Boading and loading of cargo into a carrier is modeled in a realistic fashion in the AI\_CARGO\DISPATCHER classes, which are used inernally by the WAREHOUSE class.
-- Meaning that troops (cargo) will board, i.e. run or drive to the carrier, and only once they are in close proximity to the transporter they will be loaded (disappear).
--
-- Unfortunately, there are some situations where problems can occur. For example, in DCS tanks have the strong tentendcy not to drive around obstacles but rather to roll over them.
-- I have seen cases where an aircraft of the same coalition as the tank was in its way and the tank drove right through the plane waiting on a parking spot and destroying it.
--
-- As a workaround it is possible to set a larger load radius so that the cargo units are despawned further away from the carrier via the optional **loadradius** parameter:
--
-- warehouseBatumi:AddAsset("Leopard 2", nil, nil, nil, nil, 250)
--
-- Adding the asset like this will cause the units to be loaded into the carrier already at a distance of 250 meters.
--
-- ### Setting the AI Skill
--
@@ -486,7 +466,7 @@
-- and the road connection is less than 3 km.
--
-- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. This is only functional for self propelled assets at the moment
-- and not if using the AI dispatcher classes since these have a different logic to find the route.
-- and not if using the OPSTRANSPORT class since this has a different logic to find the route.
--
-- ## Off Road Connections
--
@@ -595,7 +575,7 @@
--
-- ## Cargo Bay and Weight Limitations
--
-- The transportation of cargo is handled by the AI\_Dispatcher classes. These take the cargo bay of a carrier and the weight of
-- The transportation of cargo is handled by the `OPSTRANSPORT` class. This takes the cargo bay of a carrier and the weight of
-- the cargo into account so that a carrier can only load a realistic amount of cargo.
--
-- However, if troops are supposed to be transported between warehouses, there is one important limitations one has to keep in mind.
@@ -1798,12 +1778,13 @@ _WAREHOUSEDB = {
--- Warehouse class version.
-- @field #string version
WAREHOUSE.version="1.0.2a"
WAREHOUSE.version="2.0.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Warehouse todo list.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DONE: Switch from AI Dispatchers to OPSTRANSPORT
-- TODO: Add check if assets "on the move" are stationary. Can happen if ground units get stuck in buildings. If stationary auto complete transport by adding assets to request warehouse? Time?
-- TODO: Optimize findpathonroad. Do it only once (first time) and safe paths between warehouses similar to off-road paths.
-- NOGO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number?
@@ -3743,12 +3724,15 @@ function WAREHOUSE:_JobDone()
-- Check conditions for being back home.
local ishome=false
if category==Group.Category.GROUND or category==Group.Category.HELICOPTER then
-- Units go back to the spawn zone, helicopters land and they should not move any more.
ishome=inspawnzone and onground and notmoving
if category==Group.Category.GROUND then
-- Ground units go back to the spawn zone and they should not move any more.
ishome=inspawnzone and notmoving
elseif category==Group.Category.AIRPLANE then
-- Planes need to be on ground at their home airbase and should not move any more.
ishome=athomebase and onground and notmoving
elseif category==Group.Category.HELICOPTER then
-- Helicopters go back to their airbase or spawn zone and should not move any more.
ishome=(athomebase or inspawnzone) and onground and notmoving
end
-- Debug text.
@@ -4605,14 +4589,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
end
function WAREHOUSE:_CallbackLoaded()
MESSAGE:New("TaskEmbarking",30,"Task"):ToAll()
end
function WAREHOUSE:_CallbackUnloaded()
MESSAGE:New("TaskDisembarking",30,"Task"):ToAll()
end
--- On after "RequestSpawned" event. Initiates the transport of the assets to the requesting warehouse.
-- @param #WAREHOUSE self
-- @param #string From From state.
@@ -4624,7 +4600,7 @@ end
function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet, TransportGroupSet)
-- General type and category.
local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute
local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute
local _cargocategory=Request.cargocategory --DCS#Group.Category
-- Add groups to pending item.
@@ -4705,112 +4681,48 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
------------------------------------------------------------------------------------------------------------------------------------
-- Prepare cargo groups for transport
------------------------------------------------------------------------------------------------------------------------------------
-- TODO: set asset.weight for cargos
-- Board radius, i.e. when the cargo will begin to board the carrier
local _boardradius=500
-------------------------
-- Create OPSTRANSPORT --
-------------------------
-- OPSTRANSPORT
local CargoTransport --Ops.OpsTransport#OPSTRANSPORT
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
_boardradius=5000
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, ZONE_AIRBASE:New(self.airbase:GetName()), ZONE_AIRBASE:New(Request.airbase:GetName()))
CargoTransport:SetEmbarkZone(self.spawnzone)
CargoTransport:SetDisembarkZone(Request.warehouse.spawnzone)
elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
--_loadradius=1000
--_boardradius=nil
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
--_boardradius=nil
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
_boardradius=6000
end
------------------------
-- Create Dispatchers --
------------------------
-- Cargo dispatcher.
local CargoTransport --Ops.Auftrag#AUFTRAG
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE or Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
-- Pickup and deploy zones.
local PickupZone = ZONE_AIRBASE:New(self.airbase:GetName())
local DeployZone = ZONE_AIRBASE:New(Request.airbase:GetName())
local DropoffCoordinate = DeployZone:GetRandomCoordinate(50,200,{land.SurfaceType.LAND, land.SurfaceType.ROAD})
local PickupCoordinate = PickupZone:GetRandomCoordinate(50,200,{land.SurfaceType.LAND, land.SurfaceType.ROAD})
local Auftrag = AUFTRAG:NewTROOPTRANSPORT(CargoGroupSet,DropoffCoordinate,PickupCoordinate,500)
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.spawnzone, Request.warehouse.spawnzone)
CargoGroupSet:SetProperty("WAREHOUSE_REQUEST",Request)
for _,_Group in pairs (TransportGroupSet.Set) do
local FlightGroup = FLIGHTGROUP:New(_Group)
FlightGroup:AddMission(Auftrag)
Auftrag:AddOpsGroup(FlightGroup)
FlightGroup:SetHomebase(self.airbase)
end
CargoTransport=Auftrag
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
-- Pickup and deploy zones.
local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone)
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone)
-- Define dispatcher for this task.
CargoTransport = AI_CARGO_DISPATCHER_APC:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, 0)
-- Set home zone.
CargoTransport:SetHomeZone(self.spawnzone)
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.spawnzone, Request.warehouse.spawnzone)
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
-- Pickup and deploy zones.
local PickupZoneSet = SET_ZONE:New():AddZone(self.portzone)
PickupZoneSet:AddZone(self.harborzone)
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.harborzone)
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
CargoTransport = OPSTRANSPORT:New(CargoGroupSet, self.portzone, Request.warehouse.portzone)
CargoTransport:SetEmbarkZone(self.spawnzone)
CargoTransport:SetDisembarkZone(Request.warehouse.spawnzone)
-- Get the shipping lane to use and pass it to the Dispatcher
local remotename = Request.warehouse.warehouse:GetName()
local ShippingLane = self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
-- Define dispatcher for this task.
CargoTransport = AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, ShippingLane)
-- Set home zone
CargoTransport:SetHomeZone(self.portzone)
-- TODO: Add shipping lane
-- CargoTransport:AddPathTransport(PathGroup)
else
self:E(self.lid.."ERROR: Unknown transporttype!")
end
-- Set pickup and deploy radii.
-- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone.
local pickupouter = 200
local pickupinner = 0
local deployouter = 200
local deployinner = 0
if Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
pickupouter=1000
pickupinner=20
deployouter=1000
deployinner=0
else
pickupouter=200
pickupinner=0
if self.spawnzone.Radius~=nil then
pickupouter=self.spawnzone.Radius
pickupinner=20
end
deployouter=200
deployinner=0
if self.spawnzone.Radius~=nil then
deployouter=Request.warehouse.spawnzone.Radius
deployinner=20
end
end
-- Adjust carrier units. This has to come AFTER the dispatchers have been defined because they set the cargobay free weight!
Request.carriercargo={}
@@ -4831,79 +4743,61 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
end
end
--------------------------------
-- Dispatcher Event Functions --
--------------------------------
----------------------------------
-- Opstransport Event Functions --
----------------------------------
CargoTransport.warehouse = self
--- Function called when a carrier unit has loaded a cargo group.
function CargoTransport:OnAfterLoaded(From, Event, To, OpsGroupCargo, OpsGroupCarrier, CarrierElement)
--- Function called when cargo is loading.
function CargoTransport:OnBeforeExecuting(From,Event,To)
-- Cargo loaded
-- Get warehouse state.
local opsgroups = CargoTransport:GetOpsGroups()
for _,_group in pairs(opsgroups) do
local Carrier = _group:GetGroup()
local Element = Carrier:GetUnit(1)
local warehouse=Carrier:GetProperty("WAREHOUSE") --#WAREHOUSE
for _,Cargo in pairs(CargoGroupSet.Set) do
-- Debug message.
local text=string.format("Carrier group %s loaded cargo %s into unit %s in pickup zone", Carrier:GetName(), Cargo:GetName(), Element.UnitName)
warehouse:T(warehouse.lid..text)
-- Get cargo group object.
local group=Cargo --Wrapper.Group#GROUP
-- Get request.
local request = CargoGroupSet:GetProperty("WAREHOUSE_REQUEST")
-- Add cargo group to this carrier.
table.insert(request.carriercargo[Element.UnitName], warehouse:_GetNameWithOut(Cargo:GetName()))
end
end
local warehouse=CargoTransport.warehouse --#WAREHOUSE
-- Get cargo group object.
local group=OpsGroupCargo:GetGroup() --Cargo:GetObject() --Wrapper.Group#GROUP
-- Get request.
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
-- Add cargo group to this carrier.
table.insert(request.carriercargo[CarrierElement.name], warehouse:_GetNameWithOut(group:GetName()))
end
--- Function called when cargo has arrived and was unloaded.
function CargoTransport:OnAfterUnloaded(From, Event, To, OpsGroupCargo, OpsGroupCarrier)
-- Get warehouse state.
local warehouse=CargoTransport.warehouse --Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE
-- Get group obejet.
local group=OpsGroupCargo:GetGroup() --Cargo:GetObject() --Wrapper.Group#GROUP
-- Debug message.
local text=string.format("Cargo group %s was unloaded from carrier group %s.", tostring(group:GetName()), tostring(OpsGroupCarrier:GetName()))
warehouse:T(warehouse.lid..text)
-- Trigger Arrived event.
warehouse:Arrived(group)
end
--- Function called when cargo has arrived and was unloaded.
function CargoTransport:OnBeforeDone(From,Event,To)
-- Cargo unloading
-- Get warehouse state.
local opsgroups = CargoTransport:GetOpsGroups()
for _,_group in pairs(opsgroups) do
local Carrier = _group:GetGroup()
local Element = Carrier:GetUnit(1)
local warehouse=Carrier:GetProperty("WAREHOUSE") --#WAREHOUSE
for _,Cargo in pairs(CargoGroupSet.Set) do
-- TODO: Probably can also add some cargo/carrier dead functions here to simplify things at other places
-- Debug message.
local text=string.format("Cargo group %s was unloaded from carrier unit", tostring(Element:GetName()))
warehouse:T(warehouse.lid..text)
-- Trigger Arrived event.
warehouse:Arrived(Cargo)
end
function _group:OnAfterLanded(From,Event,To,airbase)
local mybase = warehouse.airbasename
if airbase:GetName() == mybase then
-- Debug info.
local text=string.format("Carrier %s is back home at warehouse %s.", tostring(Carrier:GetName()), tostring(warehouse.warehouse:GetName()))
MESSAGE:New(text, 5):ToAllIf(warehouse.Debug)
warehouse:I(warehouse.lid..text)
-- Call arrived event for carrier.
warehouse:__Arrived(1, Carrier)
end
end
-- Assign cargo to carriers
for _,carriergroup in pairs(TransportGroupSet:GetSetObjects()) do
local opsgroup=nil
if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE or Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then
opsgroup=FLIGHTGROUP:New(carriergroup)
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
opsgroup=ARMYGROUP:New(carriergroup)
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
opsgroup=NAVYGROUP:New(carriergroup)
end
opsgroup:AddOpsTransport(CargoTransport)
end
end
@@ -5407,8 +5301,7 @@ function WAREHOUSE:onafterAssetSpawned(From, Event, To, group, asset, request)
end
-- Set warehouse state.
--group:SetState(group, "WAREHOUSE", self)
group:SetProperty("WAREHOUSE",self)
group:SetState(group, "WAREHOUSE", self)
-- Check if all assets groups are spawned and trigger events.
local n=0
+1 -1
View File
@@ -2782,7 +2782,7 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n, N)
-- Set current waypoint or we get problem that the _PassingWaypoint function is triggered too early, i.e. right now and not when passing the next WP.
local current=self:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO, waypointType, waypointAction, speed, true, nil, {}, "Current")
table.insert(wp, current)
-- Add remaining waypoints to route.
for i=n, N do
table.insert(wp, self.waypoints[i])
+8 -3
View File
@@ -514,7 +514,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version.
-- @field #string version
OPSGROUP.version="1.0.4"
OPSGROUP.version="1.0.5"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -5100,7 +5100,7 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task)
if Task.description=="Task_Land_At" then
self:T(self.lid.."Taske DONE Task_Land_At ==> Wait")
self:Cruise()
-- After the land task, we set the helo to wait. This is because of an issue that the passing waypoint function is triggered immidiately if we do not do this!
self:Wait(20, 100)
else
self:T(self.lid.."Task Done but NO mission found ==> _CheckGroupDone in 1 sec")
@@ -8331,7 +8331,8 @@ function OPSGROUP:_CheckCargoTransport()
end
-- Boarding finished ==> Transport cargo.
if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self) and not boarding then
local required=self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self)
if gotcargo and required and not boarding then
self:T(self.lid.."Boarding/loading finished ==> Loaded")
self.Tloading=nil
self:LoadingDone()
@@ -9967,6 +9968,10 @@ function OPSGROUP:onafterTransport(From, Event, To)
self:T(self.lid.."ERROR: No current task but landed at?!")
end
end
if self:IsWaiting() then
self:__Cruise(-10)
end
elseif self:IsArmygroup() then
+5 -3
View File
@@ -244,7 +244,7 @@ _OPSTRANSPORTID=0
--- Army Group version.
-- @field #string version
OPSTRANSPORT.version="0.8.0"
OPSTRANSPORT.version="0.9.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -511,7 +511,7 @@ function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone)
-- @param Ops.OpsGroup#OPSGROUP OpsGroupCarrier Carrier OPSGROUP that unloaded the cargo.
--TODO: Pseudofunctions
--TODO: Psydofunctions
-- Call status update.
self:__StatusUpdate(-1)
@@ -2127,7 +2127,9 @@ function OPSTRANSPORT:_CheckRequiredCargos(TransportZoneCombo, CarrierGroup)
requiredCargos={}
for _,_cargo in pairs(TransportZoneCombo.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
table.insert(requiredCargos, cargo.opsgroup)
if not cargo.delivered then
table.insert(requiredCargos, cargo.opsgroup)
end
end
end