Merge pull request #1999 from FlightControl-Master/FF/Ops

OPSTRANSPORT
This commit is contained in:
Frank 2023-08-27 18:46:50 +02:00 committed by GitHub
commit 1337815f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 971 additions and 290 deletions

View File

@ -1517,6 +1517,36 @@ function FLIGHTGROUP:Status()
end end
---
-- Track flight
---
if false then
for _,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
local unit=element.unit
if unit and unit:IsAlive() then
local vec3=unit:GetVec3()
if vec3 and element.pos then
local id=UTILS.GetMarkID()
trigger.action.lineToAll(-1, id, vec3, element.pos, {1,1,1,0.5}, 1)
end
element.pos=vec3
end
end
end
--- ---
-- Fuel State -- Fuel State
--- ---

View File

@ -489,11 +489,17 @@ OPSGROUP.CargoStatus={
--- Element cargo bay data. --- Element cargo bay data.
-- @type OPSGROUP.MyCargo -- @type OPSGROUP.MyCargo
-- @field #OPSGROUP group The cargo group. -- @field #OPSGROUP group The cargo group.
-- @field #number storageType Type of storage.
-- @field #number storageAmount Amount of storage.
-- @field #number storageWeight Weight of storage item.
-- @field #boolean reserved If `true`, the cargo bay space is reserved but cargo has not actually been loaded yet. -- @field #boolean reserved If `true`, the cargo bay space is reserved but cargo has not actually been loaded yet.
--- Cargo group data. --- Cargo group data.
-- @type OPSGROUP.CargoGroup -- @type OPSGROUP.CargoGroup
-- @field #number uid Unique ID of this cargo data.
-- @field #string type Type of cargo: "OPSGROUP" or "STORAGE".
-- @field #OPSGROUP opsgroup The cargo opsgroup. -- @field #OPSGROUP opsgroup The cargo opsgroup.
-- @field Ops.OpsTransport#OPSTRANSPORT.Storage storage Storage data.
-- @field #boolean delivered If `true`, group was delivered. -- @field #boolean delivered If `true`, group was delivered.
-- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated. -- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated.
-- @field Core.Zone#ZONE disembarkZone Zone where this group is disembarked to. -- @field Core.Zone#ZONE disembarkZone Zone where this group is disembarked to.
@ -2268,22 +2274,27 @@ end
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #number Delay Delay in seconds. Default now. -- @param #number Delay Delay in seconds. Default now.
-- @param #number ExplosionPower (Optional) Explosion power in kg TNT. Default 100 kg. -- @param #number ExplosionPower (Optional) Explosion power in kg TNT. Default 100 kg.
-- @param #string ElementName Name of the element that should be destroyed. Default is all elements.
-- @return #OPSGROUP self -- @return #OPSGROUP self
function OPSGROUP:SelfDestruction(Delay, ExplosionPower) function OPSGROUP:SelfDestruction(Delay, ExplosionPower, ElementName)
if Delay and Delay>0 then if Delay and Delay>0 then
self:ScheduleOnce(Delay, OPSGROUP.SelfDestruction, self, 0, ExplosionPower) self:ScheduleOnce(Delay, OPSGROUP.SelfDestruction, self, 0, ExplosionPower, ElementName)
else else
-- Loop over all elements. -- Loop over all elements.
for i,_element in pairs(self.elements) do for i,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element local element=_element --#OPSGROUP.Element
if ElementName==nil or ElementName==element.name then
local unit=element.unit local unit=element.unit
if unit and unit:IsAlive() then if unit and unit:IsAlive() then
unit:Explode(ExplosionPower or 100) unit:Explode(ExplosionPower or 100)
end end
end
end end
end end
@ -5452,10 +5463,10 @@ function OPSGROUP:onafterMissionExecute(From, Event, To, Mission)
if self.isFlightgroup then if self.isFlightgroup then
if Mission.prohibitABExecute == true then if Mission.prohibitABExecute == true then
self:SetProhibitAfterburner() self:SetProhibitAfterburner()
self:I("Set prohibit AB") self:T(self.lid.."Set prohibit AB")
elseif Mission.prohibitABExecute == false then elseif Mission.prohibitABExecute == false then
self:SetAllowAfterburner() self:SetAllowAfterburner()
self:T2("Set allow AB") self:T2(self.lid.."Set allow AB")
end end
end end
@ -7446,36 +7457,55 @@ function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- Clear cargo bay of element. -- Clear cargo bay of element.
for i=#Element.cargoBay,1,-1 do for i=#Element.cargoBay,1,-1 do
local cargo=Element.cargoBay[i] --#OPSGROUP.MyCargo local mycargo=Element.cargoBay[i] --#OPSGROUP.MyCargo
if mycargo.group then
-- Remove from cargo bay. -- Remove from cargo bay.
self:_DelCargobay(cargo.group) self:_DelCargobay(mycargo.group)
if cargo.group and not (cargo.group:IsDead() or cargo.group:IsStopped()) then if mycargo.group and not (mycargo.group:IsDead() or mycargo.group:IsStopped()) then
-- Remove my carrier -- Remove my carrier
cargo.group:_RemoveMyCarrier() mycargo.group:_RemoveMyCarrier()
if cargo.reserved then if mycargo.reserved then
-- This group was not loaded yet ==> Not cargo any more. -- This group was not loaded yet ==> Not cargo any more.
cargo.group:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO) mycargo.group:_NewCargoStatus(OPSGROUP.CargoStatus.NOTCARGO)
else else
-- Carrier dead ==> cargo dead. -- Carrier dead ==> cargo dead.
for _,cargoelement in pairs(cargo.group.elements) do for _,cargoelement in pairs(mycargo.group.elements) do
-- Debug info. -- Debug info.
self:T2(self.lid.."Cargo element dead "..cargoelement.name) self:T2(self.lid.."Cargo element dead "..cargoelement.name)
-- Trigger dead event. -- Trigger dead event.
cargo.group:ElementDead(cargoelement) mycargo.group:ElementDead(cargoelement)
end end
end end
end end
else
-- Add cargo to lost.
if self.cargoTZC then
for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup
if cargo.uid==mycargo.cargoUID then
cargo.storage.cargoLost=cargo.storage.cargoLost+mycargo.storageAmount
end
end
end
-- Remove cargo from cargo bay.
self:_DelCargobayElement(Element, mycargo)
end
end end
end end
@ -7972,7 +8002,12 @@ function OPSGROUP:_CheckCargoTransport()
local element=_element --#OPSGROUP.Element local element=_element --#OPSGROUP.Element
for _,_cargo in pairs(element.cargoBay) do for _,_cargo in pairs(element.cargoBay) do
local cargo=_cargo --#OPSGROUP.MyCargo local cargo=_cargo --#OPSGROUP.MyCargo
if cargo.group then
text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved)) text=text..string.format("\n- %s in carrier %s, reserved=%s", tostring(cargo.group:GetName()), tostring(element.name), tostring(cargo.reserved))
else
text=text..string.format("\n- storage %s=%d kg in carrier %s [UID=%s]",
tostring(cargo.storageType), tostring(cargo.storageAmount*cargo.storageWeight), tostring(element.name), tostring(cargo.cargoUID))
end
end end
end end
if text=="" then if text=="" then
@ -7993,6 +8028,7 @@ function OPSGROUP:_CheckCargoTransport()
text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s", i, transport.uid, transport:GetState(), pickupname, deployname) text=text..string.format("\n[%d] UID=%d Status=%s: %s --> %s", i, transport.uid, transport:GetState(), pickupname, deployname)
for j,_cargo in pairs(transport:GetCargos()) do for j,_cargo in pairs(transport:GetCargos()) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local state=cargo.opsgroup:GetState() local state=cargo.opsgroup:GetState()
local status=cargo.opsgroup.cargoStatus local status=cargo.opsgroup.cargoStatus
local name=cargo.opsgroup.groupname local name=cargo.opsgroup.groupname
@ -8000,6 +8036,9 @@ function OPSGROUP:_CheckCargoTransport()
local carrierGroupname=carriergroup and carriergroup.groupname or "none" local carrierGroupname=carriergroup and carriergroup.groupname or "none"
local carrierElementname=carrierelement and carrierelement.name or "none" local carrierElementname=carrierelement and carrierelement.name or "none"
text=text..string.format("\n (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered)) text=text..string.format("\n (%d) %s [%s]: %s, carrier=%s(%s), delivered=%s", j, name, state, status, carrierGroupname, carrierElementname, tostring(cargo.delivered))
else
--TODO: STORAGE
end
end end
end end
if text~="" then if text~="" then
@ -8074,42 +8113,51 @@ function OPSGROUP:_CheckCargoTransport()
-- Current pickup time. -- Current pickup time.
local tloading=Time-self.Tloading local tloading=Time-self.Tloading
--TODO: Check max loading time. If exceeded ==> abort transport. --TODO: Check max loading time. If exceeded ==> abort transport. Time might depend on required cargos, because we need to give them time to arrive.
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Loading at %s [TZC UID=%d] for %s sec...", self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName() or "unknown", self.cargoTZC.uid, tloading)) self:T(self.lid..string.format("Loading at %s [TZC UID=%d] for %.1f sec...", self.cargoTZC.PickupZone and self.cargoTZC.PickupZone:GetName() or "unknown", self.cargoTZC.uid, tloading))
local boarding=false local boarding=false
local gotcargo=false local gotcargo=false
for _,_cargo in pairs(self.cargoTZC.Cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSTRANPORT then
-- Check if anyone is still boarding. -- Check if anyone is still boarding.
if cargo.opsgroup:IsBoarding(self.groupname) then if cargo.opsgroup and cargo.opsgroup:IsBoarding(self.groupname) then
boarding=true boarding=true
end end
-- Check if we have any cargo to transport. -- Check if we have any cargo to transport.
if cargo.opsgroup:IsLoaded(self.groupname) then if cargo.opsgroup and cargo.opsgroup:IsLoaded(self.groupname) then
gotcargo=true gotcargo=true
end end
else
-- Get cargo if it is in the cargo bay of any carrier element.
local mycargo=self:_GetMyCargoBayFromUID(cargo.uid)
if mycargo and mycargo.storageAmount>0 then
gotcargo=true
end
end
end end
-- Boarding finished ==> Transport cargo. -- Boarding finished ==> Transport cargo.
if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self) and not boarding then if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self) and not boarding then
self:T(self.lid.."Boarding finished ==> Loaded") self:T(self.lid.."Boarding/loading finished ==> Loaded")
self.Tloading=nil
self:LoadingDone() self:LoadingDone()
else else
-- No cargo and no one is boarding ==> check again if we can make anyone board. -- No cargo and no one is boarding ==> check again if we can make anyone board.
self:Loading() self:Loading()
end end
-- No cargo and no one is boarding ==> check again if we can make anyone board.
if not gotcargo and not boarding then
--self:Loading()
end
elseif self:IsTransporting() then elseif self:IsTransporting() then
-- Set time stamp. -- Set time stamp.
@ -8136,6 +8184,8 @@ function OPSGROUP:_CheckCargoTransport()
for _,_cargo in pairs(self.cargoTZC.Cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup() local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup()
-- Check that this group is -- Check that this group is
@ -8144,6 +8194,21 @@ function OPSGROUP:_CheckCargoTransport()
break break
end end
else
---
-- STORAGE
---
-- Get cargo if it is in the cargo bay of any carrier element.
local mycargo=self:_GetMyCargoBayFromUID(cargo.uid)
if mycargo and not cargo.delivered then
delivered=false
break
end
end
end end
-- Unloading finished ==> pickup next batch or call it a day. -- Unloading finished ==> pickup next batch or call it a day.
@ -8165,6 +8230,7 @@ function OPSGROUP:_CheckCargoTransport()
local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, pickupname, deployname) local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, pickupname, deployname)
for _,_cargo in pairs(self.cargoTransport:GetCargos(self.cargoTZC)) do for _,_cargo in pairs(self.cargoTransport:GetCargos(self.cargoTZC)) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local name=cargo.opsgroup:GetName() local name=cargo.opsgroup:GetName()
local gstatus=cargo.opsgroup:GetState() local gstatus=cargo.opsgroup:GetState()
local cstatus=cargo.opsgroup.cargoStatus local cstatus=cargo.opsgroup.cargoStatus
@ -8173,6 +8239,9 @@ function OPSGROUP:_CheckCargoTransport()
local carrierGroupname=carriergroup and carriergroup.groupname or "none" local carrierGroupname=carriergroup and carriergroup.groupname or "none"
local carrierElementname=carrierelement and carrierelement.name or "none" local carrierElementname=carrierelement and carrierelement.name or "none"
text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered)) text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered))
else
--TODO: Storage
end
end end
self:I(self.lid..text) self:I(self.lid..text)
end end
@ -8217,6 +8286,8 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved)
cargo.reserved=Reserved cargo.reserved=Reserved
else else
--cargo=self:_CreateMyCargo(CargoUID, CargoGroup)
cargo={} --#OPSGROUP.MyCargo cargo={} --#OPSGROUP.MyCargo
cargo.group=CargoGroup cargo.group=CargoGroup
cargo.reserved=Reserved cargo.reserved=Reserved
@ -8244,6 +8315,95 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved)
return self return self
end end
--- Add warehouse storage to cargo bay of a carrier.
-- @param #OPSGROUP self
-- @param #OPSGROUP.Element CarrierElement The element of the carrier.
-- @param #number CargoUID UID of the cargo data.
-- @param #string StorageType Storage type.
-- @param #number StorageAmount Storage amount.
-- @param #number StorageWeight Weight of a single storage item in kg.
function OPSGROUP:_AddCargobayStorage(CarrierElement, CargoUID, StorageType, StorageAmount, StorageWeight)
local MyCargo=self:_CreateMyCargo(CargoUID, nil, StorageType, StorageAmount, StorageWeight)
self:_AddMyCargoBay(MyCargo, CarrierElement)
end
--- Add OPSGROUP to cargo bay of a carrier.
-- @param #OPSGROUP self
-- @param #number CargoUID UID of the cargo data.
-- @param #OPSGROUP OpsGroup Cargo group.
-- @param #string StorageType Storage type.
-- @param #number StorageAmount Storage amount.
-- @param #number StorageWeight Weight of a single storage item in kg.
-- @return #OPSGROUP.MyCargo My cargo object.
function OPSGROUP:_CreateMyCargo(CargoUID, OpsGroup, StorageType, StorageAmount, StorageWeight)
local cargo={} --#OPSGROUP.MyCargo
cargo.cargoUID=CargoUID
cargo.group=OpsGroup
cargo.storageType=StorageType
cargo.storageAmount=StorageAmount
cargo.storageWeight=StorageWeight
cargo.reserved=false
return cargo
end
--- Add storage to cargo bay of a carrier.
-- @param #OPSGROUP self
-- @param #OPSGROUP.MyCargo MyCargo My cargo.
-- @param #OPSGROUP.Element CarrierElement The element of the carrier.
function OPSGROUP:_AddMyCargoBay(MyCargo, CarrierElement)
table.insert(CarrierElement.cargoBay, MyCargo)
if not MyCargo.reserved then
-- Cargo weight.
local weight=0
if MyCargo.group then
weight=MyCargo.group:GetWeightTotal()
else
weight=MyCargo.storageAmount*MyCargo.storageWeight
end
-- Add weight to carrier.
self:AddWeightCargo(CarrierElement.name, weight)
end
end
--- Get cargo bay data from a cargo data id.
-- @param #OPSGROUP self
-- @param #number uid Unique ID of cargo data.
-- @return #OPSGROUP.MyCargo Cargo My cargo.
-- @return #OPSGROUP.Element Element that has loaded the cargo.
function OPSGROUP:_GetMyCargoBayFromUID(uid)
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
for i,_mycargo in pairs(element.cargoBay) do
local mycargo=_mycargo --#OPSGROUP.MyCargo
if mycargo.cargoUID and mycargo.cargoUID==uid then
return mycargo, element, i
end
end
end
return nil, nil, nil
end
--- Get all groups currently loaded as cargo. --- Get all groups currently loaded as cargo.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string CarrierName (Optional) Only return cargo groups loaded into a particular carrier unit. -- @param #string CarrierName (Optional) Only return cargo groups loaded into a particular carrier unit.
@ -8291,6 +8451,51 @@ function OPSGROUP:_GetCargobay(CargoGroup)
return nil, nil, nil return nil, nil, nil
end end
--- Remove OPSGROUP from cargo bay of a carrier.
-- @param #OPSGROUP self
-- @param #OPSGROUP.Element Element Cargo group.
-- @param #number CargoUID Cargo UID.
-- @return #OPSGROUP.MyCargo MyCargo My cargo data.
function OPSGROUP:_GetCargobayElement(Element, CargoUID)
self:T3({Element=Element, CargoUID=CargoUID})
for i,_mycargo in pairs(Element.cargoBay) do
local mycargo=_mycargo --#OPSGROUP.MyCargo
if mycargo.cargoUID and mycargo.cargoUID==CargoUID then
return mycargo
end
end
return nil
end
--- Remove OPSGROUP from cargo bay of a carrier.
-- @param #OPSGROUP self
-- @param #OPSGROUP.Element Element Cargo group.
-- @param #OPSGROUP.MyCargo MyCargo My cargo data.
-- @return #boolean If `true`, cargo could be removed.
function OPSGROUP:_DelCargobayElement(Element, MyCargo)
for i,_mycargo in pairs(Element.cargoBay) do
local mycargo=_mycargo --#OPSGROUP.MyCargo
if mycargo.cargoUID and MyCargo.cargoUID and mycargo.cargoUID==MyCargo.cargoUID then
if MyCargo.group then
self:RedWeightCargo(Element.name, MyCargo.group:GetWeightTotal())
else
self:RedWeightCargo(Element.name, MyCargo.storageAmount*MyCargo.storageWeight)
end
table.remove(Element.cargoBay, i)
return true
end
end
return false
end
--- Remove OPSGROUP from cargo bay of a carrier. --- Remove OPSGROUP from cargo bay of a carrier.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #OPSGROUP CargoGroup Cargo group. -- @param #OPSGROUP CargoGroup Cargo group.
@ -8385,11 +8590,13 @@ function OPSGROUP:_CheckDelivered(CargoTransport)
for _,_cargo in pairs(CargoTransport:GetCargos()) do for _,_cargo in pairs(CargoTransport:GetCargos()) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if self:CanCargo(cargo.opsgroup) then if self:CanCargo(cargo) then
if cargo.delivered then if cargo.delivered then
-- This one is delivered. -- This one is delivered.
elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup==nil then
elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then
-- This one is dead. -- This one is dead.
else else
done=false --Someone is not done! done=false --Someone is not done!
@ -8419,13 +8626,13 @@ function OPSGROUP:_CheckGoPickup(CargoTransport)
for _,_cargo in pairs(CargoTransport:GetCargos()) do for _,_cargo in pairs(CargoTransport:GetCargos()) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if self:CanCargo(cargo.opsgroup) then if self:CanCargo(cargo) then
if cargo.delivered then if cargo.delivered then
-- This one is delivered. -- This one is delivered.
elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then
-- This one is dead. -- This one is dead.
elseif cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames()) then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsLoaded(CargoTransport:_GetCarrierNames())) then
-- This one is loaded into a(nother) carrier. -- This one is loaded into a(nother) carrier.
else else
done=false --Someone is not done! done=false --Someone is not done!
@ -8650,8 +8857,11 @@ function OPSGROUP:GetWeightCargo(UnitName, IncludeReserved)
for _,_cargo in pairs(element.cargoBay) do for _,_cargo in pairs(element.cargoBay) do
local cargo=_cargo --#OPSGROUP.MyCargo local cargo=_cargo --#OPSGROUP.MyCargo
if (not cargo.reserved) or (cargo.reserved==true and (IncludeReserved==true or IncludeReserved==nil)) then if (not cargo.reserved) or (cargo.reserved==true and (IncludeReserved==true or IncludeReserved==nil)) then
local cargoweight=cargo.group:GetWeightTotal() if cargo.group then
gewicht=gewicht+cargoweight gewicht=gewicht+cargo.group:GetWeightTotal()
else
gewicht=gewicht+cargo.storageAmount*cargo.storageWeight
end
--self:I(self.lid..string.format("unit=%s (reserved=%s): cargo=%s weight=%d, total weight=%d", tostring(UnitName), tostring(IncludeReserved), cargo.group:GetName(), cargoweight, weight)) --self:I(self.lid..string.format("unit=%s (reserved=%s): cargo=%s weight=%d, total weight=%d", tostring(UnitName), tostring(IncludeReserved), cargo.group:GetName(), cargoweight, weight))
end end
end end
@ -8745,16 +8955,46 @@ function OPSGROUP:RedWeightCargo(UnitName, Weight)
return self return self
end end
--- Get weight of warehouse storage to transport.
-- @param #OPSGROUP self
-- @param Ops.OpsTransport#OPSTRANSPORT.Storage Storage
-- @param #boolean Total Get total weight. Otherweise the amount left to deliver (total-loaded-lost-delivered).
-- @param #boolean Reserved Reduce weight that is reserved.
-- @param #boolean Amount Return amount not weight.
-- @return #number Weight of cargo in kg or amount in number of items, if `Amount=true`.
function OPSGROUP:_GetWeightStorage(Storage, Total, Reserved, Amount)
local weight=Storage.cargoAmount
if not Total then
weight=weight-Storage.cargoLost-Storage.cargoLoaded-Storage.cargoDelivered
end
if Reserved then
weight=weight-Storage.cargoReserved
end
if not Amount then
weight=weight*Storage.cargoWeight
end
return weight
end
--- Check if the group can *in principle* be carrier of a cargo group. This checks the max cargo capacity of the group but *not* how much cargo is already loaded (if any). --- Check if the group can *in principle* be carrier of a cargo group. This checks the max cargo capacity of the group but *not* how much cargo is already loaded (if any).
-- **Note** that the cargo group *cannot* be split into units, i.e. the largest cargo bay of any element of the group must be able to load the whole cargo group in one piece. -- **Note** that the cargo group *cannot* be split into units, i.e. the largest cargo bay of any element of the group must be able to load the whole cargo group in one piece.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. -- @param Ops.OpsGroup#OPSGROUP.CargoGroup Cargo Cargo data, which needs a carrier.
-- @return #boolean If `true`, there is an element of the group that can load the whole cargo group. -- @return #boolean If `true`, there is an element of the group that can load the whole cargo group.
function OPSGROUP:CanCargo(CargoGroup) function OPSGROUP:CanCargo(Cargo)
if CargoGroup then if Cargo then
local weight=CargoGroup:GetWeightTotal() local weight=math.huge
if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local weight=Cargo.opsgroup:GetWeightTotal()
for _,_element in pairs(self.elements) do for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element local element=_element --#OPSGROUP.Element
@ -8763,31 +9003,58 @@ function OPSGROUP:CanCargo(CargoGroup)
if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then if element and element.status~=OPSGROUP.ElementStatus.DEAD and element.weightMaxCargo>=weight then
return true return true
end end
end
else
---
-- STORAGE
---
-- Since storage cargo can be devided onto multiple carriers, we take the weight of a single cargo item (even 1 kg of fuel).
weight=Cargo.storage.cargoWeight
end end
-- Calculate cargo bay space.
local bay=0
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
-- Check that element is not dead and has
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
bay=bay+element.weightMaxCargo
end
end
-- Check if cargo fits into cargo bay(s) of carrier group.
if bay>=weight then
return true
end
end end
return false return false
end end
--- Add weight to the internal cargo of an element of the group. --- Find carrier for cargo by evaluating the free cargo bay storage.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. -- @param #number Weight Weight of cargo in kg.
-- @return #OPSGROUP.Element Carrier able to transport the cargo. -- @return #OPSGROUP.Element Carrier able to transport the cargo.
function OPSGROUP:FindCarrierForCargo(CargoGroup) function OPSGROUP:FindCarrierForCargo(Weight)
local weight=CargoGroup:GetWeightTotal()
for _,_element in pairs(self.elements) do for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element local element=_element --#OPSGROUP.Element
local free=self:GetFreeCargobay(element.name) local free=self:GetFreeCargobay(element.name)
if free>=weight then if free>=Weight then
return element return element
else else
self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, weight, free)) self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, Weight, free))
end end
end end
@ -9068,6 +9335,36 @@ function OPSGROUP:onafterPickup(From, Event, To)
end end
--- On after "Loading" event.
-- @param #OPSGROUP self
-- @param #table Cargos Table of cargos.
-- @return #table Table of sorted cargos.
function OPSGROUP:_SortCargo(Cargos)
-- Sort results table wrt descending weight.
local function _sort(a, b)
local cargoA=a --Ops.OpsGroup#OPSGROUP.CargoGroup
local cargoB=b --Ops.OpsGroup#OPSGROUP.CargoGroup
local weightA=0
local weightB=0
if cargoA.opsgroup then
weightA=cargoA.opsgroup:GetWeightTotal()
else
weightA=self:_GetWeightStorage(cargoA.storage)
end
if cargoB.opsgroup then
weightB=cargoB.opsgroup:GetWeightTotal()
else
weightB=self:_GetWeightStorage(cargoB.storage)
end
return weightA>weightB
end
table.sort(Cargos, _sort)
return Cargos
end
--- On after "Loading" event. --- On after "Loading" event.
-- @param #OPSGROUP self -- @param #OPSGROUP self
@ -9079,32 +9376,30 @@ function OPSGROUP:onafterLoading(From, Event, To)
-- Set carrier status. -- Set carrier status.
self:_NewCarrierStatus(OPSGROUP.CarrierStatus.LOADING) self:_NewCarrierStatus(OPSGROUP.CarrierStatus.LOADING)
-- Loading time stamp.
self.Tloading=timer.getAbsTime()
-- Get valid cargos of the TZC. -- Get valid cargos of the TZC.
local cargos={} local cargos={}
for _,_cargo in pairs(self.cargoTZC.Cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
-- Check if this group can carry the cargo. -- Check if this group can carry the cargo.
local canCargo=self:CanCargo(cargo.opsgroup) local canCargo=self:CanCargo(cargo)
-- Check if this group is currently acting as carrier. -- Check if this group is currently acting as carrier.
local isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading() local isCarrier=false
-- Check if cargo is not already cargo. -- Check if cargo is not already cargo.
local isNotCargo=cargo.opsgroup:IsNotCargo(true) local isNotCargo=true
-- Check if cargo is holding or loaded -- Check if cargo is holding or loaded
local isHolding=cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded() local isHolding=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsHolding() or cargo.opsgroup:IsLoaded()) or true
-- Check if cargo is in embark/pickup zone. -- Check if cargo is in embark/pickup zone.
-- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else! -- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else!
local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero() local inZone=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and (cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) or cargo.opsgroup:IsInUtero()) or true
-- Check if cargo is currently on a mission. -- Check if cargo is currently on a mission.
local isOnMission=cargo.opsgroup:IsOnMission() local isOnMission=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsOnMission() or false
-- Check if current mission is using this ops transport. -- Check if current mission is using this ops transport.
if isOnMission then if isOnMission then
@ -9114,39 +9409,114 @@ function OPSGROUP:onafterLoading(From, Event, To)
end end
end end
local isAvail=true
if cargo.type==OPSTRANSPORT.CargoType.STORAGE then
local nAvail=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType)
if nAvail>0 then
isAvail=true
else
isAvail=false
end
else
isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()
isNotCargo=cargo.opsgroup:IsNotCargo(true)
end
local isDead=cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead() or false
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s", self:T(self.lid..string.format("Loading: canCargo=%s, isCarrier=%s, isNotCargo=%s, isHolding=%s, isOnMission=%s",
tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission))) tostring(canCargo), tostring(isCarrier), tostring(isNotCargo), tostring(isHolding), tostring(isOnMission)))
-- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport()
if canCargo and inZone and isNotCargo and isHolding and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then if canCargo and inZone and isNotCargo and isHolding and isAvail and (not (cargo.delivered or isDead or isCarrier or isOnMission)) then
table.insert(cargos, cargo) table.insert(cargos, cargo)
end end
end end
-- Sort results table wrt descending weight. -- Sort cargos.
local function _sort(a, b) self:_SortCargo(cargos)
local cargoA=a --Ops.OpsGroup#OPSGROUP.CargoGroup
local cargoB=b --Ops.OpsGroup#OPSGROUP.CargoGroup
return cargoA.opsgroup:GetWeightTotal()>cargoB.opsgroup:GetWeightTotal()
end
table.sort(cargos, _sort)
-- Loop over all cargos. -- Loop over all cargos.
for _,_cargo in pairs(cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
-- Find a carrier for this cargo. local weight=nil
local carrier=self:FindCarrierForCargo(cargo.opsgroup) if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
if carrier then weight=cargo.opsgroup:GetWeightTotal()
-- Find a carrier for this cargo.
local carrier=self:FindCarrierForCargo(weight)
-- Order cargo group to board the carrier. -- Order cargo group to board the carrier.
if carrier then
cargo.opsgroup:Board(self, carrier) cargo.opsgroup:Board(self, carrier)
end
else
---
-- STORAGE
---
-- Get weight of cargo that needs to be transported.
weight=self:_GetWeightStorage(cargo.storage, false)
-- Get amount that the warehouse currently has.
local Amount=cargo.storage.storageFrom:GetAmount(cargo.storage.cargoType)
local Weight=Amount*cargo.storage.cargoWeight
-- Make sure, we do not take more than the warehouse can provide.
weight=math.min(weight, Weight)
-- Debug info.
self:T(self.lid..string.format("Loading storage weight=%d kg (warehouse has %d kg)!", weight, Weight))
-- Loop over all elements of the carrier group.
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
-- Get the free cargo space of the carrier.
local free=self:GetFreeCargobay(element.name)
-- Min of weight or bay.
local w=math.min(weight, free)
-- Check that weight is >0 and also greater that at least one item. We cannot transport half a missile.
if w>=cargo.storage.cargoWeight then
-- Calculate item amount.
local amount=math.floor(w/cargo.storage.cargoWeight)
-- Remove items from warehouse.
cargo.storage.storageFrom:RemoveAmount(cargo.storage.cargoType, amount)
-- Add amount to loaded cargo.
cargo.storage.cargoLoaded=cargo.storage.cargoLoaded+amount
-- Add cargo to cargo by of element.
self:_AddCargobayStorage(element, cargo.uid, cargo.storage.cargoType, amount, cargo.storage.cargoWeight)
-- Reduce weight for the next element (if any).
weight=weight-amount*cargo.storage.cargoWeight
-- Debug info.
local text=string.format("Element %s: loaded amount=%d (weight=%d) ==> left=%d kg", element.name, amount, amount*cargo.storage.cargoWeight, weight)
self:T(self.lid..text)
-- If no cargo left, break the loop.
if weight<=0 then
break
end
end
end end
end end
end
end end
--- Set (new) cargo status. --- Set (new) cargo status.
@ -9323,6 +9693,8 @@ function OPSGROUP:onafterTransport(From, Event, To)
end end
end end
--env.info(string.format("FF Transport: Zone=%s inzone=%s, ready2deploy=%s", Zone:GetName(), tostring(inzone), tostring(ready2deploy)))
if inzone then if inzone then
-- We are already in the deploy zone ==> wait and initiate unloading. -- We are already in the deploy zone ==> wait and initiate unloading.
@ -9480,6 +9852,8 @@ function OPSGROUP:onafterUnloading(From, Event, To)
for _,_cargo in pairs(self.cargoTZC.Cargos) do for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --#OPSGROUP.CargoGroup local cargo=_cargo --#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
-- Check that cargo is loaded into this group. -- Check that cargo is loaded into this group.
-- NOTE: Could be that the element carriing this cargo group is DEAD, which would mean that the cargo group is also DEAD. -- NOTE: Could be that the element carriing this cargo group is DEAD, which would mean that the cargo group is also DEAD.
if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then
@ -9617,6 +9991,67 @@ function OPSGROUP:onafterUnloading(From, Event, To)
-- Not loaded or dead -- Not loaded or dead
end end
else
---
-- STORAGE
---
-- TODO: should proabaly move this check to the top to include OPSGROUPS as well?!
if not cargo.delivered then
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
-- Get my cargo from cargo bay of element.
local mycargo=self:_GetCargobayElement(element, cargo.uid)
if mycargo then
-- Add cargo to warehouse storage.
cargo.storage.storageTo:AddAmount(mycargo.storageType, mycargo.storageAmount)
-- Add amount to delivered.
cargo.storage.cargoDelivered=cargo.storage.cargoDelivered+mycargo.storageAmount
-- Reduce loaded amount.
cargo.storage.cargoLoaded=cargo.storage.cargoLoaded-mycargo.storageAmount
-- Remove cargo from bay.
self:_DelCargobayElement(element, mycargo)
-- Debug info
self:T2(self.lid..string.format("Cargo loaded=%d, delivered=%d, lost=%d", cargo.storage.cargoLoaded, cargo.storage.cargoDelivered, cargo.storage.cargoLost))
end
end
-- Get amount that was delivered.
local amountToDeliver=self:_GetWeightStorage(cargo.storage, false, false, true)
-- Get total amount to be delivered.
local amountTotal=self:_GetWeightStorage(cargo.storage, true, false, true)
-- Debug info.
local text=string.format("Amount delivered=%d, total=%d", amountToDeliver, amountTotal)
self:T(self.lid..text)
if amountToDeliver<=0 then
-- Cargo was delivered (somehow).
cargo.delivered=true
-- Increase number of delivered cargos.
self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1
-- Debug info.
local text=string.format("Ndelivered=%d delivered=%s", self.cargoTransport.Ndelivered, tostring(cargo.delivered))
self:T(self.lid..text)
end
end
end
end -- loop over cargos end -- loop over cargos
end end
@ -10047,7 +10482,7 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier)
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!")) self:T(self.lid..string.format("Group is loaded currently ==> Moving directly to new carrier - No Unload(), Disembart() events triggered!"))
-- Remove my carrier. -- Remove old/current carrier.
self:_RemoveMyCarrier() self:_RemoveMyCarrier()
-- Trigger Load event. -- Trigger Load event.

View File

@ -1,8 +1,9 @@
--- **Ops** - Troop transport assignment for OPS groups. --- **Ops** - Transport assignment for OPS groups and storage.
-- --
-- ## Main Features: -- ## Main Features:
-- --
-- * Transport troops from A to B -- * Transport troops from A to B
-- * Transport of warehouse storage (fuel, weapons and equipment)
-- * Supports ground, naval and airborne (airplanes and helicopters) units as carriers -- * Supports ground, naval and airborne (airplanes and helicopters) units as carriers
-- * Use combined forces (ground, naval, air) to transport the troops -- * Use combined forces (ground, naval, air) to transport the troops
-- * Additional FSM events to hook into and customize your mission design -- * Additional FSM events to hook into and customize your mission design
@ -63,6 +64,7 @@
-- @field Ops.Chief#CHIEF chief Chief of the transport. -- @field Ops.Chief#CHIEF chief Chief of the transport.
-- @field Ops.OpsZone#OPSZONE opszone OPS zone. -- @field Ops.OpsZone#OPSZONE opszone OPS zone.
-- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed.
-- @field #number cargocounter Running number to generate cargo UIDs.
-- --
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@ -79,7 +81,7 @@
-- * Cargo groups are **not** split and distributed into different carrier *units*. That means that the whole cargo group **must fit** into one of the carrier units. -- * Cargo groups are **not** split and distributed into different carrier *units*. That means that the whole cargo group **must fit** into one of the carrier units.
-- * Cargo groups must be inside the pickup zones to be considered for loading. Groups not inside the pickup zone will not get the command to board. -- * Cargo groups must be inside the pickup zones to be considered for loading. Groups not inside the pickup zone will not get the command to board.
-- --
-- # Constructor -- # Troop Transport
-- --
-- A new cargo transport assignment is created with the @{#OPSTRANSPORT.New}() function -- A new cargo transport assignment is created with the @{#OPSTRANSPORT.New}() function
-- --
@ -101,6 +103,30 @@
-- --
-- You can also mix carrier types. For instance, you can assign the same transport to APCs and helicopters. Or to helicopters and airplanes. -- You can also mix carrier types. For instance, you can assign the same transport to APCs and helicopters. Or to helicopters and airplanes.
-- --
-- # Storage Transport
--
-- An instance of the OPSTRANSPORT class is created similarly to the troop transport case. However, the first parameter is `nil` as not troops
-- are transported.
--
-- local storagetransport=OPSTRANSPORT:New(nil, PickupZone, DeployZone)
--
-- ## Defining Storage
--
-- The storage warehouses from which the cargo is taken and to which the cargo is delivered have to be specified
--
-- storagetransport:AddCargoStorage(berlinStorage, batumiStorage, STORAGE.Liquid.JETFUEL, 1000)
--
-- Here `berlinStorage` and `batumiStorage` are @{Wrapper.Storage#STORAGE} objects of DCS warehouses.
--
-- Furthermore, that type of cargo (liquids or weapons/equipment) and the amount has to be specified. If weapons/equipment is the cargo,
-- we also need to specify the weight per storage item as this cannot be retrieved from the DCS API and is not stored in any MOOSE database.
--
-- storagetransport:AddCargoStorage(berlinStorage, batumiStorage, ENUMS.Storage.weapons.bombs.Mk_82, 9, 230)
--
-- Finally, the transport is assigned to one or multiple groups, which carry out the transport
--
-- myopsgroup:AddOpsTransport(storagetransport)
--
-- # Examples -- # Examples
-- --
-- A carrier group is assigned to transport infantry troops from zone "Zone Kobuleti X" to zone "Zone Alpha". -- A carrier group is assigned to transport infantry troops from zone "Zone Kobuleti X" to zone "Zone Alpha".
@ -131,6 +157,7 @@ OPSTRANSPORT = {
legions = {}, legions = {},
statusLegion = {}, statusLegion = {},
requestID = {}, requestID = {},
cargocounter = 0,
} }
--- Cargo transport status. --- Cargo transport status.
@ -186,6 +213,27 @@ OPSTRANSPORT.Status={
-- @field #number radius Radomization radius for waypoints in meters. Default 0 m. -- @field #number radius Radomization radius for waypoints in meters. Default 0 m.
-- @field #boolean reverse If `true`, path is used in reversed order. -- @field #boolean reverse If `true`, path is used in reversed order.
--- Storage data.
-- @type OPSTRANSPORT.Storage
-- @field Wrapper.Storage#STORAGE storageFrom Storage from.
-- @field Wrapper.Storage#STORAGE storageTo Storage To.
-- @field #string cargoType Type of cargo.
-- @field #number cargoAmount Amount of cargo that should be transported.
-- @field #number cargoReserved Amount of cargo that is reserved for a carrier group.
-- @field #number cargoDelivered Amount of cargo that has been delivered.
-- @field #number cargoLost Amount of cargo that was lost.
-- @field #number cargoLoaded Amount of cargo that is loading.
-- @field #number cargoWeight Weight of one single cargo item in kg. Default 1 kg.
--- Storage data.
-- @type OPSTRANSPORT.CargoType
-- @field #string OPSGROUP Cargo is an OPSGROUP.
-- @field #string STORAGE Cargo is storage of DCS warehouse.
OPSTRANSPORT.CargoType={
OPSGROUP="OPSGROUP",
STORAGE="STORAGE",
}
--- Generic transport condition. --- Generic transport condition.
-- @type OPSTRANSPORT.Condition -- @type OPSTRANSPORT.Condition
-- @field #function func Callback function to check for a condition. Should return a #boolean. -- @field #function func Callback function to check for a condition. Should return a #boolean.
@ -196,7 +244,7 @@ _OPSTRANSPORTID=0
--- Army Group version. --- Army Group version.
-- @field #string version -- @field #string version
OPSTRANSPORT.version="0.7.0" OPSTRANSPORT.version="0.8.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -205,6 +253,7 @@ OPSTRANSPORT.version="0.7.0"
-- TODO: Trains. -- TODO: Trains.
-- TODO: Stop transport. -- TODO: Stop transport.
-- TODO: Improve pickup and transport paths. -- TODO: Improve pickup and transport paths.
-- DONE: Storage.
-- DONE: Disembark parameters per cargo group. -- DONE: Disembark parameters per cargo group.
-- DONE: Special transport cohorts/legions. Similar to mission. -- DONE: Special transport cohorts/legions. Similar to mission.
-- DONE: Cancel transport. -- DONE: Cancel transport.
@ -576,6 +625,38 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActi
return self return self
end end
--- Add cargo warehouse storage to be transported. This adds items such as fuel, weapons and other equipment, which is to be transported
-- from one DCS warehouse to another.
-- For weapons and equipment, the weight per item has to be specified explicitly as these cannot be retrieved by the DCS API. For liquids the
-- default value of 1 kg per item should be used as the amount of liquid is already given in kg.
-- @param #OPSTRANSPORT self
-- @param Wrapper.Storage#STORAGE StorageFrom Storage warehouse from which the cargo is taken.
-- @param Wrapper.Storage#STORAGE StorageTo Storage warehouse to which the cargo is delivered.
-- @param #string CargoType Type of cargo, *e.g.* `"weapons.bombs.Mk_84"` or liquid type as #number.
-- @param #number CargoAmount Amount of cargo. Liquids in kg.
-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo if other than default.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-- Cargo data.
local cargo=self:_CreateCargoStorage(StorageFrom,StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo)
if cargo then
-- Add total amount of ever assigned cargos.
self.Ncargo=self.Ncargo+1
-- Add to TZC table.
table.insert(TransportZoneCombo.Cargos, cargo)
end
end
--- Set pickup zone. --- Set pickup zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
@ -1063,18 +1144,37 @@ end
-- @return #table Cargo Ops groups. Can be and empty table `{}`. -- @return #table Cargo Ops groups. Can be and empty table `{}`.
function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) function OPSTRANSPORT:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo)
local cargos=self:GetCargos(TransportZoneCombo) local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered)
local opsgroups={} local opsgroups={}
for _,_cargo in pairs(cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if Delivered==nil or cargo.delivered==Delivered then if cargo.type=="OPSGROUP" then
if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then if cargo.opsgroup and not (cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped()) then
if Carrier==nil or Carrier:CanCargo(cargo.opsgroup) then
table.insert(opsgroups, cargo.opsgroup) table.insert(opsgroups, cargo.opsgroup)
end end
end end
end end
return opsgroups
end
--- Get (all) cargo @{Ops.OpsGroup#OPSGROUP}s. Optionally, only delivered or undelivered groups can be returned.
-- @param #OPSTRANSPORT self
-- @param #boolean Delivered If `true`, only delivered groups are returned. If `false` only undelivered groups are returned. If `nil`, all groups are returned.
-- @param Ops.OpsGroup#OPSGROUP Carrier (Optional) Only count cargo groups that fit into the given carrier group. Current cargo is not a factor.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Cargo Ops groups. Can be and empty table `{}`.
function OPSTRANSPORT:GetCargoStorages(Delivered, Carrier, TransportZoneCombo)
local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered)
local opsgroups={}
for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.type=="STORAGE" then
table.insert(opsgroups, cargo.storage)
end
end end
return opsgroups return opsgroups
@ -1090,22 +1190,60 @@ end
--- Get cargos. --- Get cargos.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @param Ops.OpsGroup#OPSGROUP Carrier Specific carrier.
-- @param #boolean Delivered Delivered status.
-- @return #table Cargos. -- @return #table Cargos.
function OPSTRANSPORT:GetCargos(TransportZoneCombo) function OPSTRANSPORT:GetCargos(TransportZoneCombo, Carrier, Delivered)
local tczs=self.tzCombos
if TransportZoneCombo then if TransportZoneCombo then
return TransportZoneCombo.Cargos tczs={TransportZoneCombo}
else end
local cargos={} local cargos={}
for _,_tzc in pairs(self.tzCombos) do for _,_tcz in pairs(tczs) do
local tzc=_tzc --#OPSTRANSPORT.TransportZoneCombo local tcz=_tcz --#OPSTRANSPORT.TransportZoneCombo
for _,cargo in pairs(tzc.Cargos) do for _,_cargo in pairs(tcz.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if Delivered==nil or cargo.delivered==Delivered then
if Carrier==nil or Carrier:CanCargo(cargo) then
table.insert(cargos, cargo) table.insert(cargos, cargo)
end end
end end
return cargos end
end end
return cargos
end
--- Get total weight.
-- @param #OPSTRANSPORT self
-- @param Ops.OpsGroup#OPSGROUP.CargoGroup Cargo Cargo data.
-- @param #boolean IncludeReserved Include reserved cargo.
-- @return #number Weight in kg.
function OPSTRANSPORT:GetCargoTotalWeight(Cargo, IncludeReserved)
local weight=0
if Cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
weight=Cargo.opsgroup:GetWeightTotal(nil, IncludeReserved)
else
if type(Cargo.storage.cargoType)=="number" then
if IncludeReserved then
return Cargo.storage.cargoAmount+Cargo.storage.cargoReserved
else
return Cargo.storage.cargoAmount
end
else
if IncludeReserved then
return Cargo.storage.cargoAmount*100 -- Assume 100 kg per item
else
return (Cargo.storage.cargoAmount+Cargo.storage.cargoReserved)*100 -- Assume 100 kg per item
end
end
end
return weight
end end
--- Set transport start and stop time. --- Set transport start and stop time.
@ -1642,11 +1780,18 @@ function OPSTRANSPORT:onafterStatusUpdate(From, Event, To)
text=text..string.format("\nCargos:") text=text..string.format("\nCargos:")
for _,_cargo in pairs(self:GetCargos()) do for _,_cargo in pairs(self:GetCargos()) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local carrier=cargo.opsgroup:_GetMyCarrierElement() local carrier=cargo.opsgroup:_GetMyCarrierElement()
local name=carrier and carrier.name or "none" local name=carrier and carrier.name or "none"
local cstate=carrier and carrier.status or "N/A" local cstate=carrier and carrier.status or "N/A"
text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]",
cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID))
else
--TODO: Storage
local storage=cargo.storage
text=text..string.format("\n- storage type=%s: amount: total=%d loaded=%d, lost=%d, delivered=%d, delivered=%s [UID=%s]",
storage.cargoType, storage.cargoAmount, storage.cargoLoaded, storage.cargoLost, storage.cargoDelivered, tostring(cargo.delivered), tostring(cargo.uid))
end
end end
text=text..string.format("\nCarriers:") text=text..string.format("\nCarriers:")
@ -1923,14 +2068,14 @@ function OPSTRANSPORT:_CheckDelivered()
if cargo.delivered then if cargo.delivered then
-- This one is delivered. -- This one is delivered.
dead=false dead=false
elseif cargo.opsgroup==nil then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup==nil then
-- This one is nil?! -- This one is nil?!
dead=false dead=false
elseif cargo.opsgroup:IsDestroyed() then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDestroyed() then
-- This one was destroyed. -- This one was destroyed.
elseif cargo.opsgroup:IsDead() then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsDead() then
-- This one is dead. -- This one is dead.
elseif cargo.opsgroup:IsStopped() then elseif cargo.type==OPSTRANSPORT.CargoType.OPSGROUP and cargo.opsgroup:IsStopped() then
-- This one is stopped. -- This one is stopped.
dead=false dead=false
else else
@ -2116,10 +2261,12 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark
end end
end end
self.cargocounter=self.cargocounter+1
-- Create a new data item. -- Create a new data item.
local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup
cargo.uid=self.cargocounter
cargo.type="OPSGROUP"
cargo.opsgroup=opsgroup cargo.opsgroup=opsgroup
cargo.delivered=false cargo.delivered=false
cargo.status="Unknown" cargo.status="Unknown"
@ -2133,6 +2280,45 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark
return cargo return cargo
end end
--- Create a cargo group data structure.
-- @param #OPSTRANSPORT self
-- @param Wrapper.Storage#STORAGE StorageFrom Storage from.
-- @param Wrapper.Storage#STORAGE StorageTo Storage to.
-- @param #string CargoType Type of cargo.
-- @param #number CargoAmount Total amount of cargo that should be transported. Liquids in kg.
-- @param #number CargoWeight Weight of a single cargo item in kg. Default 1 kg.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data.
function OPSTRANSPORT:_CreateCargoStorage(StorageFrom, StorageTo, CargoType, CargoAmount, CargoWeight, TransportZoneCombo)
local storage={} --#OPSTRANSPORT.Storage
storage.storageFrom=StorageFrom
storage.storageTo=StorageTo
storage.cargoType=CargoType
storage.cargoAmount=CargoAmount
storage.cargoDelivered=0
storage.cargoLost=0
storage.cargoReserved=0
storage.cargoLoaded=0
storage.cargoWeight=CargoWeight or 1
self.cargocounter=self.cargocounter+1
-- Create a new data item.
local cargo={} --Ops.OpsGroup#OPSGROUP.CargoGroup
cargo.uid=self.cargocounter
cargo.type="STORAGE"
cargo.opsgroup=nil
cargo.storage=storage
cargo.delivered=false
cargo.status="Unknown"
cargo.tzcUID=TransportZoneCombo
cargo.disembarkZone=nil
cargo.disembarkCarriers=nil
return cargo
end
--- Count how many cargo groups are inside a zone. --- Count how many cargo groups are inside a zone.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE Zone The zone object. -- @param Core.Zone#ZONE Zone The zone object.
@ -2143,7 +2329,9 @@ end
function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZoneCombo) function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZoneCombo)
-- Get cargo ops groups. -- Get cargo ops groups.
local cargos=self:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo) --local cargos=self:GetCargoOpsGroups(Delivered, Carrier, TransportZoneCombo)
local cargos=self:GetCargos(TransportZoneCombo, Carrier, Delivered)
--- Function to check if carrier is supposed to be disembarked to. --- Function to check if carrier is supposed to be disembarked to.
local function iscarrier(_cargo) local function iscarrier(_cargo)
@ -2185,22 +2373,33 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZone
local N=0 local N=0
for _,_cargo in pairs(cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
local isNotCargo=true
local isInZone=true
local isInUtero=true
if cargo.type==OPSTRANSPORT.CargoType.OPSGROUP then
local opsgroup=cargo.opsgroup
-- Is not cargo? -- Is not cargo?
local isNotCargo=cargo:IsNotCargo(true) isNotCargo=opsgroup:IsNotCargo(true)
if not isNotCargo then if not isNotCargo then
isNotCargo=iscarrier(cargo) isNotCargo=iscarrier(opsgroup)
end end
-- Is in zone? -- Is in zone?
local isInZone=cargo:IsInZone(Zone) isInZone=opsgroup:IsInZone(Zone)
-- Is in utero? -- Is in utero?
local isInUtero=cargo:IsInUtero() isInUtero=opsgroup:IsInUtero()
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", cargo:GetName(), tostring(cargo:IsNotCargo(true)), tostring(iscarrier(cargo)), tostring(isInZone), tostring(isInUtero))) self:T(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", opsgroup:GetName(), tostring(opsgroup:IsNotCargo(true)), tostring(iscarrier(opsgroup)), tostring(isInZone), tostring(isInUtero)))
end
-- We look for groups that are not cargo, in the zone or in utero. -- We look for groups that are not cargo, in the zone or in utero.
if isNotCargo and (isInZone or isInUtero) then if isNotCargo and (isInZone or isInUtero) then

View File

@ -900,6 +900,23 @@ function MSRS:_ExecCommand(command)
timer.scheduleFunction(os.remove, filename, timer.getTime()+1) timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1) timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
elseif false then
-- Create a tmp file.
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..STTS.uuid()..".vbs"
-- VBS script
local script = io.open(filenvbs, "w+")
script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n'))
script:write(string.format('Dim strArgs\n'))
script:write(string.format('strArgs = "cmd /c %s"\n', filename))
script:write(string.format('oShell.Run strArgs, 0, false'))
script:close()
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
-- Play file in 0.01 seconds
res=os.execute(runvbs)
else else