OPS Cargo

This commit is contained in:
Frank 2021-03-07 23:47:29 +01:00
parent bab097958f
commit 56e2b06e9d
5 changed files with 332 additions and 112 deletions

View File

@ -1728,26 +1728,28 @@ end
-- @param #ZONE_POLYGON_BASE self
-- @return DCS#Vec2 The Vec2 coordinate.
function ZONE_POLYGON_BASE:GetRandomVec2()
self:F2()
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
local Vec2Found = false
local Vec2
-- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
-- Get the bounding square.
local BS = self:GetBoundingSquare()
self:T2( BS )
local Nmax=1000 ; local n=0
while n<Nmax do
while Vec2Found == false do
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) }
self:T2( Vec2 )
if self:IsVec2InZone( Vec2 ) then
Vec2Found = true
-- Random point in the bounding square.
local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
-- Check if this is in the polygon.
if self:IsVec2InZone(Vec2) then
return Vec2
end
n=n+1
end
self:T2( Vec2 )
return Vec2
self:E("Could not find a random point in the polygon zone!")
return nil
end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.

View File

@ -115,6 +115,8 @@
-- @field #string cargoStatus Cargo status of this group acting as cargo.
-- @field #string carrierStatus Carrier status of this group acting as cargo carrier.
-- @field #number cargocounter Running number of cargo UIDs.
-- @field #OPSGROUP.CarrierLoader carrierLoader Carrier loader parameters.
-- @field #OPSGROUP.CarrierLoader carrierUnloader Carrier unloader parameters.
--
-- @extends Core.Fsm#FSM
@ -174,6 +176,8 @@ OPSGROUP = {
cargoBay = {},
cargocounter = 1,
mycarrier = {},
carrierLoader = {},
carrierUnloader = {},
}
@ -187,9 +191,9 @@ OPSGROUP = {
-- @field #boolean ai If true, element is AI.
-- @field #string skill Skill level.
--
-- @field Core.Zone#ZONE zoneBoundingbox Bounding box zone of the
-- @field Core.Zone#ZONE zoneLoad Loading zone.
-- @field Core.Zone#ZONE zoneUnload Unloading zone.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneBoundingbox Bounding box zone of the element unit.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneLoad Loading zone.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneUnload Unloading zone.
--
-- @field #string typename Type name.
-- @field #number category Aircraft category.
@ -409,18 +413,11 @@ OPSGROUP.CargoStatus={
LOADED="loaded",
}
--- Cargo transport status.
-- @type OPSGROUP.TransportStatus
-- @field #string PLANNING Planning state.
-- @field #string SCHEDULED Transport is scheduled in the cargo queue.
-- @field #string EXECUTING Transport is being executed.
-- @field #string DELIVERED Transport was delivered.
OPSGROUP.TransportStatus={
PLANNING="planning",
SCHEDULED="scheduled",
EXECUTING="executing",
DELIVERED="delivered",
}
--- Cargo carrier loader parameters.
-- @type OPSGROUP.CarrierLoader
-- @field #string type Loader type "Front", "Back", "Left", "Right", "All".
-- @field #number length Length of (un-)loading zone in meters.
-- @field #number width Width of (un-)loading zone in meters.
--- Cargo transport data.
-- @type OPSGROUP.MyCarrier
@ -503,6 +500,9 @@ function OPSGROUP:New(group)
self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO
self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER
self.cargocounter=1
self:SetCarrierLoaderAllAspect()
self:SetCarrierUnloaderAllAspect()
-- Init task counter.
self.taskcurrent=0
@ -1436,8 +1436,8 @@ end
--- Self destruction of group. An explosion is created at the position of each element.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds. Default now.
-- @param #number ExplosionPower (Optional) Explosion power in kg TNT. Default 500 kg.
-- @return #number Relative fuel in percent.
-- @param #number ExplosionPower (Optional) Explosion power in kg TNT. Default 100 kg.
-- @return #OPSGROUP self
function OPSGROUP:SelfDestruction(Delay, ExplosionPower)
if Delay and Delay>0 then
@ -1451,13 +1451,136 @@ function OPSGROUP:SelfDestruction(Delay, ExplosionPower)
local unit=element.unit
if unit and unit:IsAlive() then
unit:Explode(ExplosionPower)
unit:Explode(ExplosionPower or 100)
end
end
end
return self
end
--- Set that this carrier is an all aspect loader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierLoaderAllAspect(Length, Width)
self.carrierLoader.type="front"
self.carrierLoader.length=Length or 50
self.carrierLoader.width=Width or 20
return self
end
--- Set that this carrier is a front loader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierLoaderFront(Length, Width)
self.carrierLoader.type="front"
self.carrierLoader.length=Length or 50
self.carrierLoader.width=Width or 20
return self
end
--- Set that this carrier is a back loader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierLoaderBack(Length, Width)
self.carrierLoader.type="back"
self.carrierLoader.length=Length or 50
self.carrierLoader.width=Width or 20
return self
end
--- Set that this carrier is a starboard (right side) loader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierLoaderStarboard(Length, Width)
self.carrierLoader.type="right"
self.carrierLoader.length=Length or 50
self.carrierLoader.width=Width or 20
return self
end
--- Set that this carrier is a port (left side) loader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierLoaderPort(Length, Width)
self.carrierLoader.type="left"
self.carrierLoader.length=Length or 50
self.carrierLoader.width=Width or 20
return self
end
--- Set that this carrier is an all aspect unloader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierUnloaderAllAspect(Length, Width)
self.carrierUnloader.type="front"
self.carrierUnloader.length=Length or 50
self.carrierUnloader.width=Width or 20
return self
end
--- Set that this carrier is a front unloader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierUnloaderFront(Length, Width)
self.carrierUnloader.type="front"
self.carrierUnloader.length=Length or 50
self.carrierUnloader.width=Width or 20
return self
end
--- Set that this carrier is a back unloader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierUnloaderBack(Length, Width)
self.carrierUnloader.type="back"
self.carrierUnloader.length=Length or 50
self.carrierUnloader.width=Width or 20
return self
end
--- Set that this carrier is a starboard (right side) unloader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierUnloaderStarboard(Length, Width)
self.carrierUnloader.type="right"
self.carrierUnloader.length=Length or 50
self.carrierUnloader.width=Width or 20
return self
end
--- Set that this carrier is a port (left side) unloader.
-- @param #OPSGROUP self
-- @param #number Length Length of loading zone in meters. Default 50 m.
-- @param #number Width Width of loading zone in meters. Default 20 m.
-- @return #OPSGROUP self
function OPSGROUP:SetCarrierUnloaderPort(Length, Width)
self.carrierUnloader.type="left"
self.carrierUnloader.length=Length or 50
self.carrierUnloader.width=Width or 20
return self
end
--- Check if this is a FLIGHTGROUP.
-- @param #OPSGROUP self
-- @return #boolean If true, this is an airplane or helo group.
@ -5767,27 +5890,12 @@ function OPSGROUP:onafterUnloading(From, Event, To)
Coordinate=self.cargoTransport.disembarkzone:GetRandomCoordinate()
else
-- TODO: Optimize with random Vec2
-- Create a zone around the carrier.
local zoneCarrier=ZONE_RADIUS:New("Carrier", self:GetVec2(), 100)
local d={}
d.p1={x=vec2.x-l/2, y=vec2.y-w/2} --DCS#Vec2
d.p2={x=vec2.x-l/2, y=vec2.y+w/2} --DCS#Vec2
d.p3={x=d2.x+20, y=d2.y+20}
d.p4={x=d1.x+20, y=d1.y+20}
for _,_p in pairs(d) do
local p=_p --#DCSVec2
end
local zoneCarrier=ZONE_POLYGON_BASE:New("Carrier", {d1, d2, d3, d4})
-- Get random point in disembark zone.
local zoneCarrier=self:GetElementZoneUnload(cargo.opsgroup:_GetMyCarrierElement().name)
-- Random coordinate/heading in the zone.
Coordinate=zoneCarrier:GetRandomCoordinate(50)
Coordinate=zoneCarrier:GetRandomCoordinate()
end
@ -5800,8 +5908,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
end
-- Trigger "Unloaded" event for current cargo transport
self.cargoTransport:Unloaded(cargo.opsgroup)
self.cargoTransport:Unloaded(cargo.opsgroup)
end
@ -5898,13 +6005,16 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated
-- Respawn group.
OpsGroup:_Respawn(0, Template)
if self:IsNavygroup() then
self.currentwp=1
NAVYGROUP.AddWaypoint(self, Coordinate, nil, nil, nil, false)
elseif self:IsArmygroup() then
self.currentwp=1
ARMYGROUP.AddWaypoint(self, Coordinate, nil, nil, nil, false)
end
-- Add current waypoint. These have been cleard on loading.
if OpsGroup:IsNavygroup() then
OpsGroup.currentwp=1
OpsGroup.passedfinalwp=true
NAVYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false)
elseif OpsGroup:IsArmygroup() then
OpsGroup.currentwp=1
OpsGroup.passedfinalwp=true
ARMYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false)
end
else
@ -5919,11 +6029,10 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated
end
-- Trigger "Disembarked" event.
OpsGroup:Disembarked(OpsGroup.carrierGroup, OpsGroup.carrier)
OpsGroup:Disembarked(OpsGroup:_GetMyCarrierGroup(), OpsGroup:_GetMyCarrierElement())
-- No carrier any more.
OpsGroup.carrier=nil
OpsGroup.carrierGroup=nil
-- Remove my carrier.
OpsGroup:_RemoveMyCarrier()
end
@ -5966,11 +6075,8 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsTransport#OPSTRANSPORT CargoTransport
-- @param Ops.OpsTransport#OPSTRANSPORT CargoTransport The cargo transport assignment.
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
@ -7995,44 +8101,173 @@ end
-- @param #OPSGROUP self
-- @param #string UnitName Name of unit.
-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone.
function OPSGROUP:GetElementBoundingBox(UnitName)
function OPSGROUP:GetElementZoneBoundingBox(UnitName)
local element=self:GetElementByName(UnitName)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-- Create a new zone if necessary.
element.zoneBoundingbox=element.zoneBoundingbox or ZONE_POLYGON_BASE:New(element.name.." Zone Bounding Box", {})
local l=element.length
local w=element.width
local heading=element.unit:GetHeading()
local X=self:GetOrientationX(element.name)
local heading=math.deg(math.atan2(X.z, X.x))
env.info(string.format("FF l=%d w=%d h=%d", l, w, heading))
local vec2=self:GetVec2(element.name)
-- Set of
-- Set of edges facing "North" at the origin of the map.
local b={}
b[1]={y=l/2, x=-w/2} --DCS#Vec2
b[2]={y=l/2, x=w/2} --DCS#Vec2
b[3]={y=-l/2, x=w/2} --DCS#Vec2
b[4]={y=-l/2, x=-w/2} --DCS#Vec2
b[1]={x=l/2, y=-w/2} --DCS#Vec2
b[2]={x=l/2, y=w/2} --DCS#Vec2
b[3]={x=-l/2, y=w/2} --DCS#Vec2
b[4]={x=-l/2, y=-w/2} --DCS#Vec2
-- Rotate box to match current heading of the unit.
for i,p in pairs(b) do
b[i]=UTILS.Vec2Rotate2D(p, heading)
end
-- Translate the zone to the positon of the unit.
local vec2=self:GetVec2(element.name)
local d=UTILS.Vec2Norm(vec2)
local h=UTILS.Vec2Hdg(vec2)
for i,p in pairs(b) do
--b[i]=UTILS.Vec2Translate(p, d, h)
b[i]=UTILS.Vec2Translate(p, d, h)
end
-- Update existing zone.
element.zoneBoundingbox:UpdateFromVec2(b)
return ZONE_POLYGON_BASE:New(element.name, b)
return element.zoneBoundingbox
end
return nil
end
--- Get the loading zone of the element.
-- @param #OPSGROUP self
-- @param #string UnitName Name of unit.
-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone.
function OPSGROUP:GetElementZoneLoad(UnitName)
local element=self:GetElementByName(UnitName)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
element.zoneLoad=element.zoneLoad or ZONE_POLYGON_BASE:New(element.name.." Zone Load", {})
self:_GetElementZoneLoader(element, element.zoneLoad, self.carrierLoader)
return element.zoneLoad
end
return nil
end
--- Get the unloading zone of the element.
-- @param #OPSGROUP self
-- @param #string UnitName Name of unit.
-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone.
function OPSGROUP:GetElementZoneUnload(UnitName)
local element=self:GetElementByName(UnitName)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
element.zoneUnload=element.zoneUnload or ZONE_POLYGON_BASE:New(element.name.." Zone Unload", {})
self:_GetElementZoneLoader(element, element.zoneUnload, self.carrierUnloader)
return element.zoneUnload
end
return nil
end
--- Get/update the (un-)loading zone of the element.
-- @param #OPSGROUP self
-- @param #OPSGROUP.Element Element Element.
-- @param Core.Zone#ZONE_POLYGON Zone The zone.
-- @param #OPSGROUP.CarrierLoader Loader Loader parameters.
-- @return Core.Zone#ZONE_POLYGON Bounding box polygon zone.
function OPSGROUP:_GetElementZoneLoader(Element, Zone, Loader)
if Element.status~=OPSGROUP.ElementStatus.DEAD then
local l=Element.length
local w=Element.width
-- Orientation 3D vector where the "nose" is pointing.
local X=self:GetOrientationX(Element.name)
-- Heading in deg.
local heading=math.deg(math.atan2(X.z, X.x))
env.info(string.format("FF l=%d w=%d h=%d", l, w, heading))
-- Bounding box at the origin of the map facing "North".
local b={}
-- Create polygon rectangles.
if Loader.type:lower()=="front" then
table.insert(b, {x= l/2, y=-Loader.width/2}) -- left, low
table.insert(b, {x= l/2+Loader.length, y=-Loader.width/2}) -- left, up
table.insert(b, {x= l/2+Loader.length, y= Loader.width/2}) -- right, up
table.insert(b, {x= l/2, y= Loader.width/2}) -- right, low
elseif Loader.type:lower()=="back" then
table.insert(b, {x=-l/2, y=-Loader.width/2}) -- left, low
table.insert(b, {x=-l/2-Loader.length, y=-Loader.width/2}) -- left, up
table.insert(b, {x=-l/2-Loader.length, y= Loader.width/2}) -- right, up
table.insert(b, {x=-l/2, y= Loader.width/2}) -- right, low
elseif Loader.type:lower()=="left" then
table.insert(b, {x= Loader.length/2, y= -w/2}) -- right, up
table.insert(b, {x= Loader.length/2, y= -w/2-Loader.width}) -- left, up
table.insert(b, {x=-Loader.length/2, y= -w/2-Loader.width}) -- left, down
table.insert(b, {x=-Loader.length/2, y= -w/2}) -- right, down
elseif Loader.type:lower()=="right" then
table.insert(b, {x= Loader.length/2, y= w/2}) -- right, up
table.insert(b, {x= Loader.length/2, y= w/2+Loader.width}) -- left, up
table.insert(b, {x=-Loader.length/2, y= w/2+Loader.width}) -- left, down
table.insert(b, {x=-Loader.length/2, y= w/2}) -- right, down
else
-- All aspect. Rectangle around the unit but need to cut out the area of the unit itself.
b[1]={x= l/2, y=-w/2} --DCS#Vec2
b[2]={x= l/2, y= w/2} --DCS#Vec2
b[3]={x=-l/2, y= w/2} --DCS#Vec2
b[4]={x=-l/2, y=-w/2} --DCS#Vec2
table.insert(b, {x=b[1].x+Loader.length, y=b[1].y-Loader.width})
table.insert(b, {x=b[2].x+Loader.length, y=b[2].y+Loader.width})
table.insert(b, {x=b[3].x-Loader.length, y=b[3].y+Loader.width})
table.insert(b, {x=b[4].x-Loader.length, y=b[4].y-Loader.width})
end
-- Rotate edges to match the current heading of the unit.
for i,p in pairs(b) do
b[i]=UTILS.Vec2Rotate2D(p, heading)
end
-- Translate box to the current position of the unit.
local vec2=self:GetVec2(Element.name)
local d=UTILS.Vec2Norm(vec2)
local h=UTILS.Vec2Hdg(vec2)
for i,p in pairs(b) do
b[i]=UTILS.Vec2Translate(p, d, h)
end
-- Update existing zone.
Zone:UpdateFromVec2(b)
return Zone
end
return nil
end
--- Get the first element of a group, which is alive.
-- @param #OPSGROUP self
-- @return #OPSGROUP.Element The element or `#nil` if no element is alive any more.
@ -8399,9 +8634,7 @@ function OPSGROUP:_AddElementByName(unitname)
end
-- Max cargo weight
--element.weightMaxCargo=math.max(element.weightMaxTotal-element.weightEmpty, 0)
-- Max cargo weight:
unit:SetCargoBayWeightLimit()
element.weightMaxCargo=unit.__.CargoBayWeightLimit

View File

@ -97,7 +97,7 @@ _OPSTRANSPORTID=0
--- Army Group version.
-- @field #string version
OPSTRANSPORT.version="0.0.3"
OPSTRANSPORT.version="0.0.5"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list

View File

@ -1127,13 +1127,13 @@ function UTILS.Vec2Rotate2D(a, angle)
local phi=math.rad(angle)
local x=a.y
local y=a.x
local x=a.x
local y=a.y
local Z=x*math.cos(phi)-y*math.sin(phi)
local X=x*math.sin(phi)+y*math.cos(phi)
local X=x*math.cos(phi)-y*math.sin(phi)
local Y=x*math.sin(phi)+y*math.cos(phi)
local A={x=X, y=Z}
local A={x=X, y=Y}
return A
end

View File

@ -1394,18 +1394,6 @@ do -- Cargo
return ItemCount
end
-- --- Get Cargo Bay Free Volume in m3.
-- -- @param #POSITIONABLE self
-- -- @return #number CargoBayFreeVolume
-- function POSITIONABLE:GetCargoBayFreeVolume()
-- local CargoVolume = 0
-- for CargoName, Cargo in pairs( self.__.Cargo ) do
-- CargoVolume = CargoVolume + Cargo:GetVolume()
-- end
-- return self.__.CargoBayVolumeLimit - CargoVolume
-- end
--
--- Get Cargo Bay Free Weight in kg.
-- @param #POSITIONABLE self
-- @return #number CargoBayFreeWeight
@ -1423,13 +1411,6 @@ do -- Cargo
return self.__.CargoBayWeightLimit - CargoWeight
end
-- --- Get Cargo Bay Volume Limit in m3.
-- -- @param #POSITIONABLE self
-- -- @param #number VolumeLimit
-- function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit )
-- self.__.CargoBayVolumeLimit = VolumeLimit
-- end
--- Set Cargo Bay Weight Limit in kg.
-- @param #POSITIONABLE self
-- @param #number WeightLimit
@ -1458,11 +1439,13 @@ do -- Cargo
self:F({Desc=Desc})
local Weights = {
["Type_071"] = 245000,
["LHA_Tarawa"] = 500000,
["Ropucha-class"] = 150000,
["Dry-cargo ship-1"] = 70000,
["Dry-cargo ship-2"] = 70000,
["Type_071"] = 245000,
["LHA_Tarawa"] = 500000,
["Ropucha-class"] = 150000,
["Dry-cargo ship-1"] = 70000,
["Dry-cargo ship-2"] = 70000,
["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia).
["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops.
}
self.__.CargoBayWeightLimit = ( Weights[Desc.typeName] or 50000 )
@ -1507,9 +1490,11 @@ do -- Cargo
}
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95
self.__.CargoBayWeightLimit = CargoBayWeightLimit
end
end
self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit})
end
end --- Cargo