OPS Cargo

This commit is contained in:
Frank 2021-02-07 01:24:13 +01:00
parent 8bb9f0d7c0
commit c5a4776b3a
5 changed files with 289 additions and 128 deletions

View File

@ -1057,24 +1057,48 @@ end
--- Returns a random Vec2 location within the zone. --- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
-- @return DCS#Vec2 The random location within the zone. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
self:F( self.ZoneName, inner, outer )
local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; if surfacetypes and type(surfacetypes)~="table" then
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); surfacetypes={surfacetypes}
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); end
self:T( { Point } ) local function _getpoint()
local point = {}
local angle = math.random() * math.pi * 2
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
return point
end
return Point local function _checkSurface(point)
for _,sf in pairs(surfacetypes) do
if sf==land.getSurfaceType(point) then
return true
end
end
return false
end
local point=_getpoint()
if surfacetypes then
local N=1 ; local Nmax=1000
while _checkSurface(point)==false and N<=Nmax do
point=_getpoint()
N=N+1
end
end
return point
end end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
@ -1126,15 +1150,15 @@ end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE -- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) -- @return Core.Point#COORDINATE The random coordinate.
self:F( self.ZoneName, inner, outer ) function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
self:T3( { Coordinate = Coordinate } ) local Coordinate = COORDINATE:NewFromVec2(vec2)
return Coordinate return Coordinate
end end

View File

@ -1176,6 +1176,8 @@ function ARMYGROUP:_InitGroup()
text=text..string.format("Unit type = %s\n", self.actype) text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal())
text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay())
text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))

View File

@ -358,6 +358,14 @@ function FLIGHTGROUP:SetAirwing(airwing)
return self return self
end end
--- Set if aircraft is VTOL capable. Unfortunately, there is no DCS way to determine this via scripting.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetVTOL()
self.isVTOL=true
return self
end
--- Get airwing the flight group belongs to. --- Get airwing the flight group belongs to.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
-- @return Ops.AirWing#AIRWING The AIRWING object. -- @return Ops.AirWing#AIRWING The AIRWING object.
@ -1674,13 +1682,17 @@ function FLIGHTGROUP:onafterAirborne(From, Event, To)
if self.isAI then if self.isAI then
if self:IsTransporting() then if self:IsTransporting() then
env.info("FF transporting land at airbase ") if self.cargoTransport and self.cargoTransport.deployzone and self.cargoTransport.deployzone:IsInstanceOf("ZONE_AIRBASE") then
local airbase=self.cargoTransport.deployzone:GetAirbase() env.info("FF transporting land at airbase ")
self:LandAtAirbase(airbase) local airbase=self.cargoTransport.deployzone:GetAirbase()
self:LandAtAirbase(airbase)
end
elseif self:IsPickingup() then elseif self:IsPickingup() then
env.info("FF pickingup land at airbase ") if self.cargoTransport and self.cargoTransport.pickupzone and self.cargoTransport.pickupzone:IsInstanceOf("ZONE_AIRBASE") then
local airbase=self.cargoTransport.pickupzone:GetAirbase() env.info("FF pickingup land at airbase ")
self:LandAtAirbase(airbase) local airbase=self.cargoTransport.pickupzone:GetAirbase()
self:LandAtAirbase(airbase)
end
else else
self:_CheckGroupDone(1) self:_CheckGroupDone(1)
end end
@ -2919,12 +2931,14 @@ function FLIGHTGROUP:_InitGroup()
self.refueltype=select(2, unit:IsRefuelable()) self.refueltype=select(2, unit:IsRefuelable())
-- Debug info. -- Debug info.
if self.verbose>=1 then if self.verbose>=0 then
local text=string.format("Initialized Flight Group %s:\n", self.groupname) local text=string.format("Initialized Flight Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype) text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Range max = %.1f km\n", self.rangemax/1000) text=text..string.format("Range max = %.1f km\n", self.rangemax/1000)
text=text..string.format("Ceiling = %.1f feet\n", UTILS.MetersToFeet(self.ceiling)) text=text..string.format("Ceiling = %.1f feet\n", UTILS.MetersToFeet(self.ceiling))
text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal())
text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay())
text=text..string.format("Tanker type = %s\n", tostring(self.tankertype)) text=text..string.format("Tanker type = %s\n", tostring(self.tankertype))
text=text..string.format("Refuel type = %s\n", tostring(self.refueltype)) text=text..string.format("Refuel type = %s\n", tostring(self.refueltype))
text=text..string.format("AI = %s\n", tostring(self.isAI)) text=text..string.format("AI = %s\n", tostring(self.isAI))
@ -3295,6 +3309,7 @@ function FLIGHTGROUP:InitWaypoints()
-- Get home and destination airbases from waypoints. -- Get home and destination airbases from waypoints.
self.homebase=self.homebase or self:GetHomebaseFromWaypoints() self.homebase=self.homebase or self:GetHomebaseFromWaypoints()
self.destbase=self.destbase or self:GetDestinationFromWaypoints() self.destbase=self.destbase or self:GetDestinationFromWaypoints()
self.currbase=self:GetHomebaseFromWaypoints()
-- Remove the landing waypoint. We use RTB for that. It makes adding new waypoints easier as we do not have to check if the last waypoint is the landing waypoint. -- Remove the landing waypoint. We use RTB for that. It makes adding new waypoints easier as we do not have to check if the last waypoint is the landing waypoint.
if self.destbase then if self.destbase then

View File

@ -677,7 +677,8 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
-- Set radio. -- Set radio.
if self.radioDefault then if self.radioDefault then
self:SwitchRadio() -- CAREFUL: This makes DCS crash for some ships like speed boats or Higgins boats! (On a respawn for example). Looks like the command SetFrequency is causing this.
--self:SwitchRadio()
else else
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, false) self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, false)
end end
@ -1186,6 +1187,8 @@ function NAVYGROUP:_InitGroup()
text=text..string.format("Unit type = %s\n", self.actype) text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal())
text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay())
text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))

View File

@ -19,13 +19,14 @@
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #string groupname Name of the group. -- @field #string groupname Name of the group.
-- @field Wrapper.Group#GROUP group Group object. -- @field Wrapper.Group#GROUP group Group object.
-- @field #table template Template of the group. -- @field DCS#Template template Template table of the group.
-- @field #boolean isLateActivated Is the group late activated. -- @field #boolean isLateActivated Is the group late activated.
-- @field #boolean isUncontrolled Is the group uncontrolled. -- @field #boolean isUncontrolled Is the group uncontrolled.
-- @field #boolean isFlightgroup Is a FLIGHTGROUP. -- @field #boolean isFlightgroup Is a FLIGHTGROUP.
-- @field #boolean isArmygroup Is an ARMYGROUP. -- @field #boolean isArmygroup Is an ARMYGROUP.
-- @field #boolean isNavygroup Is a NAVYGROUP. -- @field #boolean isNavygroup Is a NAVYGROUP.
-- @field #boolean isHelo If true, the is a helicopter group. -- @field #boolean isHelo If true, the is a helicopter group.
-- @field #boolean isVTOL If true, the is capable of Vertical TakeOff and Landing (VTOL).
-- @field #table elements Table of elements, i.e. units of the group. -- @field #table elements Table of elements, i.e. units of the group.
-- @field #boolean isAI If true, group is purely AI. -- @field #boolean isAI If true, group is purely AI.
-- @field #boolean isAircraft If true, group is airplane or helicopter. -- @field #boolean isAircraft If true, group is airplane or helicopter.
@ -108,6 +109,7 @@
-- @field #OPSGROUP.CargoTransport cargoTransport Current cargo transport assignment. -- @field #OPSGROUP.CargoTransport cargoTransport Current cargo transport assignment.
-- @field #string cargoStatus Cargo status of this group acting as cargo. -- @field #string cargoStatus Cargo status of this group acting as cargo.
-- @field #string carrierStatus Carrier status of this group acting as cargo carrier. -- @field #string carrierStatus Carrier status of this group acting as cargo carrier.
-- @field #number cargocounter Running number of cargo UIDs.
-- --
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@ -165,6 +167,7 @@ OPSGROUP = {
weaponData = {}, weaponData = {},
cargoqueue = {}, cargoqueue = {},
cargoBay = {}, cargoBay = {},
cargocounter = 1,
} }
@ -499,6 +502,7 @@ function OPSGROUP:New(group)
self:SetLaser(1688, true, false, 0.5) self:SetLaser(1688, true, false, 0.5)
self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO
self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER
self.cargocounter=1
-- Init task counter. -- Init task counter.
self.taskcurrent=0 self.taskcurrent=0
@ -582,7 +586,8 @@ function OPSGROUP:New(group)
self:AddTransition("*", "Transport", "*") -- Carrier is transporting cargo. self:AddTransition("*", "Transport", "*") -- Carrier is transporting cargo.
self:AddTransition("*", "Deploy", "*") -- Carrier is dropping off cargo. self:AddTransition("*", "Deploy", "*") -- Carrier is dropping off cargo.
self:AddTransition("*", "Unload", "*") -- Carrier unload a cargo group. self:AddTransition("*", "Unload", "*") -- Carrier unload a cargo group.
self:AddTransition("*", "Unloaded", "*") -- Carrier unloaded all its cargo. self:AddTransition("*", "Unloaded", "*") -- Carrier unloaded all its current cargo.
self:AddTransition("*", "Delivered", "*") -- Carrier delivered ALL cargo of the transport assignment.
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
@ -1292,6 +1297,7 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
if unit then if unit then
local name=unit:getName() local name=unit:getName()
if name then if name then
-- Despawn the unit.
self:DespawnUnit(name, 0, NoEventRemoveUnit) self:DespawnUnit(name, 0, NoEventRemoveUnit)
end end
end end
@ -3002,6 +3008,11 @@ end
-- @return Ops.Auftrag#AUFTRAG Next mission or *nil*. -- @return Ops.Auftrag#AUFTRAG Next mission or *nil*.
function OPSGROUP:_GetNextMission() function OPSGROUP:_GetNextMission()
-- Check if group is acting as carrier or cargo at the moment.
if self:IsTransporting() or self:IsPickingup() or self:IsLoading() or self:IsLoaded() then
return nil
end
-- Number of missions. -- Number of missions.
local Nmissions=#self.missionqueue local Nmissions=#self.missionqueue
@ -4376,7 +4387,7 @@ end
--- Respawn the group. --- Respawn the group.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before respawn happens. Default 0. -- @param #number Delay Delay in seconds before respawn happens. Default 0.
-- @param #table Template (optional) The template of the Group retrieved with GROUP:GetTemplate(). If the template is not provided, the template will be retrieved of the group itself. -- @param DCS#Template Template (optional) The template of the Group retrieved with GROUP:GetTemplate(). If the template is not provided, the template will be retrieved of the group itself.
-- @param #boolean Reset Reset positions if TRUE. -- @param #boolean Reset Reset positions if TRUE.
-- @return #OPSGROUP self -- @return #OPSGROUP self
function OPSGROUP:_Respawn(Delay, Template, Reset) function OPSGROUP:_Respawn(Delay, Template, Reset)
@ -4390,17 +4401,6 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Given template or get old. -- Given template or get old.
Template=Template or UTILS.DeepCopy(self.template) Template=Template or UTILS.DeepCopy(self.template)
-- Get correct heading.
local function _Heading(course)
local h
if course<=180 then
h=math.rad(course)
else
h=-math.rad(360-course)
end
return h
end
if self:IsAlive() then if self:IsAlive() then
--- ---
@ -4467,6 +4467,10 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Spawn new group. -- Spawn new group.
_DATABASE:Spawn(Template) _DATABASE:Spawn(Template)
-- Set activation and controlled state.
self.isLateActivated=Template.lateActivation
self.isUncontrolled=Template.uncontrolled
-- Reset events. -- Reset events.
--self:ResetEvents() --self:ResetEvents()
@ -4577,69 +4581,15 @@ function OPSGROUP:_CheckCargoTransport()
end end
self:I(self.lid..text) self:I(self.lid..text)
-- Loop over cargo queue and check if everything was delivered.
if self.cargoTransport then for i=#self.cargoqueue,1,-1 do
-- TODO: Check if this group can actually transport any cargo. local transport=self.cargoqueue[i] --#OPSGROUP.CargoTransport
self:_CheckDelivered(transport)
local done=true
for _,_cargo in pairs(self.cargoTransport.cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.delivered then
-- This one is delivered.
elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then
-- This one is dead.
else
done=false --Someone is not done!
end
--TODO: check if cargo is too heavy for this carrier group ==> elseif
end
if done then
self.cargoTransport.status=OPSGROUP.TransportStatus.DELIVERED
end
end end
-- Check if there is anything in the queue. -- Check if there is anything in the queue.
if not self.cargoTransport then if not self.cargoTransport then
self.cargoTransport=self:_GetNextCargoTransport()
-- Current position.
local coord=self:GetCoordinate()
-- Sort results table wrt prio and distance to pickup zone.
local function _sort(a, b)
local transportA=a --#OPSGROUP.CargoTransport
local transportB=b --#OPSGROUP.CargoTransport
local distA=transportA.pickupzone:GetCoordinate():Get2DDistance(coord)
local distB=transportB.pickupzone:GetCoordinate():Get2DDistance(coord)
return (transportA.prio<transportB.prio) or (transportA.prio==transportB.prio and distA<distB)
end
table.sort(self.cargoqueue, _sort)
-- Look for first mission that is SCHEDULED.
local vip=math.huge
for _,_cargotransport in pairs(self.cargoqueue) do
local cargotransport=_cargotransport --#OPSGROUP.CargoTransport
if cargotransport.importance and cargotransport.importance<vip then
vip=cargotransport.importance
end
end
-- Find next transport assignment.
for _,_cargotransport in pairs(self.cargoqueue) do
local cargotransport=_cargotransport --#OPSGROUP.CargoTransport
if Time>=cargotransport.Tstart and cargotransport.status==OPSGROUP.TransportStatus.SCHEDULED and (cargotransport.importance==nil or cargotransport.importance<=vip) then
cargotransport.status=OPSGROUP.TransportStatus.EXECUTING
self.cargoTransport=cargotransport
break
end
end
end end
-- Now handle the transport. -- Now handle the transport.
@ -4718,6 +4668,7 @@ function OPSGROUP:_CheckCargoTransport()
elseif self:IsUnloading() then elseif self:IsUnloading() then
-- Debug info.
self:I(self.lid.."Unloading ==> Checking if all cargo was delivered") self:I(self.lid.."Unloading ==> Checking if all cargo was delivered")
local delivered=true local delivered=true
@ -4731,7 +4682,7 @@ function OPSGROUP:_CheckCargoTransport()
end end
-- Boarding finished ==> Transport cargo. -- Unloading finished ==> pickup next batch or call it a day.
if delivered then if delivered then
self:I(self.lid.."Unloading finished ==> Unloaded") self:I(self.lid.."Unloading finished ==> Unloaded")
self:Unloaded() self:Unloaded()
@ -4746,6 +4697,83 @@ function OPSGROUP:_CheckCargoTransport()
return self return self
end end
--- Get cargo transport from cargo queue.
-- @param #OPSGROUP self
-- @return #OPSGROUP.CargoTransport The next due cargo transport or `nil`.
function OPSGROUP:_GetNextCargoTransport()
-- Abs. mission time in seconds.
local Time=timer.getAbsTime()
-- Current position.
local coord=self:GetCoordinate()
-- Sort results table wrt prio and distance to pickup zone.
local function _sort(a, b)
local transportA=a --#OPSGROUP.CargoTransport
local transportB=b --#OPSGROUP.CargoTransport
local distA=transportA.pickupzone:GetCoordinate():Get2DDistance(coord)
local distB=transportB.pickupzone:GetCoordinate():Get2DDistance(coord)
return (transportA.prio<transportB.prio) or (transportA.prio==transportB.prio and distA<distB)
end
table.sort(self.cargoqueue, _sort)
-- Look for first mission that is SCHEDULED.
local vip=math.huge
for _,_cargotransport in pairs(self.cargoqueue) do
local cargotransport=_cargotransport --#OPSGROUP.CargoTransport
if cargotransport.importance and cargotransport.importance<vip then
vip=cargotransport.importance
end
end
-- Find next transport assignment.
for _,_cargotransport in pairs(self.cargoqueue) do
local cargotransport=_cargotransport --#OPSGROUP.CargoTransport
if Time>=cargotransport.Tstart and cargotransport.status==OPSGROUP.TransportStatus.SCHEDULED and (cargotransport.importance==nil or cargotransport.importance<=vip) then
return cargotransport
end
end
return nil
end
--- Check if all cargo of this transport assignment was delivered.
-- @param #OPSGROUP self
-- @param #OPSGROUP.CargoTransport The next due cargo transport or `nil`.
-- @return #boolean If true, all cargo was delivered.
function OPSGROUP:_CheckDelivered(CargoTransport)
-- TODO: Check if this group can actually transport any cargo.
local done=true
for _,_cargo in pairs(CargoTransport.cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.delivered then
-- This one is delivered.
elseif cargo.opsgroup==nil or cargo.opsgroup:IsDead() or cargo.opsgroup:IsStopped() then
-- This one is dead.
else
done=false --Someone is not done!
end
--TODO: check if ALL remaining cargo is too heavy for this carrier group ==> el
end
if done then
self:Delivered(CargoTransport)
end
-- Debug info.
self:I(self.lid..string.format("Cargotransport UID=%d Status=%s: delivered=%s", CargoTransport.uid, CargoTransport.status, tostring(done)))
return done
end
--- Create a cargo transport assignment. --- Create a cargo transport assignment.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be a single @{Wrapper.Group#GROUP} object. -- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be a single @{Wrapper.Group#GROUP} object.
@ -4763,11 +4791,15 @@ function OPSGROUP:AddCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, Impo
-- Create a new cargo transport assignment. -- Create a new cargo transport assignment.
local cargotransport=self:CreateCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, Importance, ClockStart, Embarkzone, Disembarkzone, NewCarrierGroup) local cargotransport=self:CreateCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, Importance, ClockStart, Embarkzone, Disembarkzone, NewCarrierGroup)
-- Set state to SCHEDULED. if cargotransport then
cargotransport.status=OPSGROUP.TransportStatus.SCHEDULED
--Add to cargo queue -- Set state to SCHEDULED.
table.insert(self.cargoqueue, cargotransport) cargotransport.status=OPSGROUP.TransportStatus.SCHEDULED
--Add to cargo queue
table.insert(self.cargoqueue, cargotransport)
end
return cargotransport return cargotransport
end end
@ -4816,7 +4848,7 @@ function OPSGROUP:CreateCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, I
-- Data structure. -- Data structure.
local transport={} --#OPSGROUP.CargoTransport local transport={} --#OPSGROUP.CargoTransport
transport.uid=1 transport.uid=self.cargocounter
transport.status=OPSGROUP.TransportStatus.PLANNING transport.status=OPSGROUP.TransportStatus.PLANNING
transport.pickupzone=Pickupzone transport.pickupzone=Pickupzone
transport.deployzone=Deployzone transport.deployzone=Deployzone
@ -4830,15 +4862,27 @@ function OPSGROUP:CreateCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, I
-- Check type of GroupSet provided. -- Check type of GroupSet provided.
if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then
-- We got a single GROUP or OPSGROUP objectg. -- We got a single GROUP or OPSGROUP objectg.
local cargo=self:CreateCargoGroupData(GroupSet, Pickupzone, Deployzone) local cargo=self:CreateCargoGroupData(GroupSet, Pickupzone, Deployzone)
table.insert(transport.cargos, cargo)
else if cargo and self:CanCargo(cargo.opsgroup) then
-- We got a SET_GROUP object.
for _,group in pairs(GroupSet.Set) do
local cargo=self:CreateCargoGroupData(group, Pickupzone, Deployzone)
table.insert(transport.cargos, cargo) table.insert(transport.cargos, cargo)
end end
else
-- We got a SET_GROUP object.
for _,group in pairs(GroupSet.Set) do
local cargo=self:CreateCargoGroupData(group, Pickupzone, Deployzone)
if cargo and self:CanCargo(cargo.opsgroup) then
table.insert(transport.cargos, cargo)
end
end
end end
-- Debug info. -- Debug info.
@ -4856,7 +4900,13 @@ function OPSGROUP:CreateCargoTransport(GroupSet, Pickupzone, Deployzone, Prio, I
self:I(self.lid..text) self:I(self.lid..text)
end end
return transport if #transport.cargos>0 then
self.cargocounter=self.cargocounter+1
return transport
else
self:E(self.lid.."ERROR: Cargo too heavy for this carrier group!")
return nil
end
end end
--- Create a cargo group data structure. --- Create a cargo group data structure.
@ -4884,7 +4934,7 @@ function OPSGROUP:CreateCargoGroupData(group, Pickupzone, Deployzone)
opsgroup=ARMYGROUP:New(group) opsgroup=ARMYGROUP:New(group)
end end
else else
env.info("FF found opsgroup in createcargo") --env.info("FF found opsgroup in createcargo")
end end
end end
@ -4997,6 +5047,25 @@ function OPSGROUP:RedWeightCargo(UnitName, Weight)
return self return self
end end
--- Check if the group can be carrier of a cargo group.
-- **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 CargoGroup Cargo group, which needs a carrier.
-- @return #boolean If `true`, there is an element of the group that can load the whole cargo group.
function OPSGROUP:CanCargo(CargoGroup)
local weight=CargoGroup:GetWeightTotal()
for _,element in pairs(self.elements) do
local can=element.weightMaxCargo>=weight
if can then
return true
end
end
return false
end
--- Add weight to the internal cargo of an element of the group. --- Add weight to the internal cargo of an element of the group.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. -- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier.
@ -5033,6 +5102,7 @@ function OPSGROUP:onafterPickup(From, Event, To, Zone)
local airbasePickup=nil --Wrapper.Airbase#AIRBASE local airbasePickup=nil --Wrapper.Airbase#AIRBASE
if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then
env.info("FF 001")
airbasePickup=Zone:GetAirbase() airbasePickup=Zone:GetAirbase()
end end
@ -5041,17 +5111,22 @@ function OPSGROUP:onafterPickup(From, Event, To, Zone)
if self:IsArmygroup() or self:IsNavygroup() then if self:IsArmygroup() or self:IsNavygroup() then
ready4loading=inzone ready4loading=inzone
else else
env.info("FF 002 currbase="..(self.currbase and self.currbase:GetName() or "unknown"))
-- Aircraft is already parking at the pickup airbase. -- Aircraft is already parking at the pickup airbase.
ready4loading=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking() ready4loading=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking()
-- If a helo is landed in the zone, we also are ready for loading. -- If a helo is landed in the zone, we also are ready for loading.
if ready4loading==false and self.isHelo and self:IsLandedAt() and inzone then if ready4loading==false and (self.isHelo or self.isVTOL) and self:IsLandedAt() and inzone then
ready4loading=true ready4loading=true
env.info("FF 003")
end end
end end
if ready4loading then if ready4loading then
env.info("FF 004")
-- We are already in the pickup zone ==> initiate loading. -- We are already in the pickup zone ==> initiate loading.
self:Loading() self:Loading()
@ -5063,23 +5138,27 @@ function OPSGROUP:onafterPickup(From, Event, To, Zone)
-- Add waypoint. -- Add waypoint.
if self.isFlightgroup then if self.isFlightgroup then
if airbasePickup then
--- ---
-- Pickup at airbase -- Pickup at airbase
--- ---
if airbasePickup then
local airbaseCurrent=self.currbase local airbaseCurrent=self.currbase
if airbaseCurrent then if airbaseCurrent then
env.info("FF 100")
-- Activate uncontrolled group. -- Activate uncontrolled group.
if self:IsParking() then if self:IsParking() then
env.info("FF 200")
self:StartUncontrolled() self:StartUncontrolled()
end end
else else
-- Order group to land at an airbase. -- Order group to land at an airbase.
env.info("FF 300")
self:LandAtAirbase(airbasePickup) self:LandAtAirbase(airbasePickup)
end end
@ -5286,7 +5365,7 @@ function OPSGROUP:onafterTransport(From, Event, To, Zone)
ready2deploy=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking() ready2deploy=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking()
-- If a helo is landed in the zone, we also are ready for loading. -- If a helo is landed in the zone, we also are ready for loading.
if ready2deploy==false and self.isHelo and self:IsLandedAt() and inzone then if ready2deploy==false and (self.isHelo or self.isVTOL) and self:IsLandedAt() and inzone then
ready2deploy=true ready2deploy=true
end end
end end
@ -5454,6 +5533,7 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Heading)
--- ---
OpsGroup:Unboard(Coordinate, Heading) OpsGroup:Unboard(Coordinate, Heading)
else else
--- ---
@ -5480,7 +5560,6 @@ end
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param Core.Zone#ZONE Zone Deploy zone.
function OPSGROUP:onafterUnloaded(From, Event, To) function OPSGROUP:onafterUnloaded(From, Event, To)
-- Debug info -- Debug info
self:I(self.lid.."Cargo unloaded..") self:I(self.lid.."Cargo unloaded..")
@ -5519,8 +5598,8 @@ function OPSGROUP:onafterUnloaded(From, Event, To)
-- This is not a carrier anymore. -- This is not a carrier anymore.
self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER
-- No current transport assignment. -- Everything delivered.
self.cargoTransport=nil self:Delivered(self.cargoTransport)
-- Startup uncontrolled aircraft to allow it to go back. -- Startup uncontrolled aircraft to allow it to go back.
if self:IsFlightgroup() then if self:IsFlightgroup() then
@ -5537,6 +5616,27 @@ function OPSGROUP:onafterUnloaded(From, Event, To)
end end
--- On after "Delivered" event.
-- @param #OPSGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #OPSGROUP.CargoTransport CargoTransport
function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport)
-- Set cargo status.
CargoTransport.status=OPSGROUP.TransportStatus.DELIVERED
-- Check if this was the current transport.
if self.cargoTransport and self.cargoTransport.uid==CargoTransport.uid then
self.cargoTransport=nil
end
-- Remove cargo transport from cargo queue.
self:DelCargoTransport(CargoTransport)
end
--- ---
-- Cargo Group Functions -- Cargo Group Functions
@ -5569,7 +5669,7 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier)
local Coordinate=Carrier.unit:GetCoordinate() local Coordinate=Carrier.unit:GetCoordinate()
if self.isArmygroup then if self.isArmygroup then
local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation, Updateroute) local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate)
waypoint.detour=true waypoint.detour=true
else else
local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate) local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate)
@ -5643,6 +5743,9 @@ function OPSGROUP:onafterUnboard(From, Event, To, Coordinate, Heading, Delay)
-- Template for the respawned group. -- Template for the respawned group.
local Template=UTILS.DeepCopy(self.template) --DCS#Template local Template=UTILS.DeepCopy(self.template) --DCS#Template
-- No late activation.
Template.lateActivation=false
-- Loop over template units. -- Loop over template units.
for _,Unit in pairs(Template.units) do for _,Unit in pairs(Template.units) do
@ -7869,11 +7972,25 @@ function OPSGROUP:_AddElementByName(unitname)
-- Weight and cargo. -- Weight and cargo.
element.weightEmpty=element.descriptors.massEmpty or 666 element.weightEmpty=element.descriptors.massEmpty or 666
element.weightMaxTotal=element.descriptors.massMax or element.weightEmpty+2*95 --If max mass is not given, we assume 10 soldiers.
element.weightMaxCargo=math.max(element.weightMaxTotal-element.weightEmpty, 0)
element.weightCargo=0 element.weightCargo=0
element.weight=element.weightEmpty+element.weightCargo element.weight=element.weightEmpty+element.weightCargo
-- Looks like only aircraft have a massMax value in the descriptors.
element.weightMaxTotal=element.descriptors.massMax or element.weightEmpty+2*95 --If max mass is not given, we assume 10 soldiers.
if self.isArmygroup then
element.weightMaxTotal=element.weightEmpty+10*95 --If max mass is not given, we assume 10 soldiers.
elseif self.isNavygroup then
element.weightMaxTotal=element.weightEmpty+10*1000
end
-- Max cargo weight
element.weightMaxCargo=math.max(element.weightMaxTotal-element.weightEmpty, 0)
-- FLIGHTGROUP specific. -- FLIGHTGROUP specific.
if self.isFlightgroup then if self.isFlightgroup then