Update DynamicCargo.lua

- Added C-130-specific transport state handling to avoid false unloads with `ropelength = 0`.
- Added state flow for C-130 cargo:
  - attach (grounded proximity)
  - detach (airborne + distance)
  - airborne confirm
  - landed-stable confirm
  - unload event only after landed-stable check
- Added owner re-resolve fallback for C-130 when owner/unit refs are stale.
- Added C-130 config values:
  - `C130AttachDistance = 10`
  - `C130DetachDistance = 14`
  - `C130AirborneAGL = 8`
  - `C130LandedAGL = 0.5`
  - `C130StabilityEpsilon = 0.05`
  - `C130RequireAirborne = true`
  - owner resolve thresholds (`Move2D`, `Near2D`, `Max3D`)
- Updated default scheduler interval to `5` seconds.
- Changed tracking to one global timer for all tracked DynamicCargo objects:
  - starts when cargo exists
  - stops when no tracked cargo remains
- Added/updated aircraft dimensions table entries:
  - `CH-47Fbl1`, `Mi-8MTV2`, `Mi-8MT`, `UH-1H`, `Mi-24P`, `UH-60L`, `UH-60L_DAP`, `C-130J-30`
- Added helper getters:
  - `IsAttached()`
  - `IsDetached()`
  - `WasAirborneTransport()`
  - `IsLandedStable()`
  - `GetCarrierUnitName()`
  - `GetCarrierTypeName()`
  - `GetCarrierGroupName()`

**Behavior note**

- Non-C130 behavior stays on the existing logic path.
- Existing DynamicCargo event IDs and flow are kept (`New`, `Loaded`, `Unloaded`, `Removed`).
This commit is contained in:
leka1986
2026-03-03 19:37:42 +01:00
committed by GitHub
parent 8968add126
commit 506073022b
+493 -13
View File
@@ -28,9 +28,9 @@
-- @field #string version. -- @field #string version.
-- @field #string CargoState. -- @field #string CargoState.
-- @field #table DCS#Vec3 LastPosition. -- @field #table DCS#Vec3 LastPosition.
-- @field #number Interval Check Interval. 20 secs default. -- @field #number Interval Check interval. 5 secs default.
-- @field #boolean testing -- @field #boolean testing
-- @field Core.Timer#TIMER timer Timmer to run intervals -- @field Core.Timer#TIMER timer Compatibility field; updates are handled by one class-wide scheduler.
-- @field #string Owner The playername who has created, loaded or unloaded this cargo. Depends on state. -- @field #string Owner The playername who has created, loaded or unloaded this cargo. Depends on state.
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
@@ -50,7 +50,16 @@ DYNAMICCARGO = {
ClassName = "DYNAMICCARGO", ClassName = "DYNAMICCARGO",
verbose = 0, verbose = 0,
testing = false, testing = false,
Interval = 10, Interval = 5,
C130AttachDistance = 10,
C130DetachDistance = 14,
C130AirborneAGL = 8,
C130LandedAGL = 0.5,
C130StabilityEpsilon = 0.05,
C130RequireAirborne = true,
C130OwnerResolveMove2D = 10,
C130OwnerResolveNear2D = 4,
C130OwnerResolveMax3D = 250,
} }
@@ -110,6 +119,10 @@ DYNAMICCARGO.AircraftTypes = {
["CH-47Fbl1"] = "CH-47Fbl1", ["CH-47Fbl1"] = "CH-47Fbl1",
["Mi-8MTV2"] = "Mi-8MTV2", ["Mi-8MTV2"] = "Mi-8MTV2",
["Mi-8MT"] = "Mi-8MT", ["Mi-8MT"] = "Mi-8MT",
["UH-1H"] = "UH-1H",
["Mi-24P"] = "Mi-24P",
["UH-60L"] = "UH-60L",
["UH-60L_DAP"] = "UH-60L_DAP",
["C-130J-30"] = "C-130J-30", ["C-130J-30"] = "C-130J-30",
} }
@@ -135,17 +148,46 @@ DYNAMICCARGO.AircraftDimensions = {
["length"] = 15, ["length"] = 15,
["ropelength"] = 30, ["ropelength"] = 30,
}, },
["UH-1H"] = {
["width"] = 4,
["height"] = 4,
["length"] = 9,
["ropelength"] = 25,
},
["Mi-24P"] = {
["width"] = 4,
["height"] = 5,
["length"] = 11,
["ropelength"] = 25,
},
["UH-60L"] = {
["width"] = 4,
["height"] = 5,
["length"] = 10,
["ropelength"] = 25,
},
["UH-60L_DAP"] = {
["width"] = 4,
["height"] = 5,
["length"] = 10,
["ropelength"] = 25,
},
["C-130J-30"] = { ["C-130J-30"] = {
["width"] = 4, ["width"] = 4,
["height"] = 12, ["height"] = 12,
["length"] = 35, ["length"] = 35,
["ropelength"] = 0, ["ropelength"] = 0,
["attach"] = 10,
["detach"] = 14,
}, },
} }
--- DYNAMICCARGO class version. --- DYNAMICCARGO class version.
-- @field #string version -- @field #string version
DYNAMICCARGO.version="0.1.0" DYNAMICCARGO.version="0.1.0"
DYNAMICCARGO._TrackedCargo = DYNAMICCARGO._TrackedCargo or {}
DYNAMICCARGO._GlobalTimer = DYNAMICCARGO._GlobalTimer or nil
DYNAMICCARGO._GlobalTimerInterval = DYNAMICCARGO._GlobalTimerInterval or nil
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -170,8 +212,17 @@ function DYNAMICCARGO:Register(CargoName)
self.StaticName = CargoName self.StaticName = CargoName
self.LastPosition = self:GetCoordinate() self.LastPosition = self:GetCoordinate()
self._spawnVec3 = self.LastPosition and self.LastPosition:GetVec3() or nil
self.CargoState = DYNAMICCARGO.State.NEW self.CargoState = DYNAMICCARGO.State.NEW
self._attached = false
self._detached = false
self._wasAirborne = false
self._landAglConfirm = nil
self._ownerResolved = false
self._carrierUnitName = nil
self._carrierGroupName = nil
self._carrierTypeName = nil
self.Interval = DYNAMICCARGO.Interval or 10 self.Interval = DYNAMICCARGO.Interval or 10
@@ -186,8 +237,9 @@ function DYNAMICCARGO:Register(CargoName)
self.Owner = string.match(CargoName,"^(.+)|%d%d:%d%d|PKG%d+") or "None" self.Owner = string.match(CargoName,"^(.+)|%d%d:%d%d|PKG%d+") or "None"
self.timer = TIMER:New(DYNAMICCARGO._UpdatePosition,self) -- Keep a compatibility field, while updates are driven by one class-wide scheduler.
self.timer:Start(self.Interval,self.Interval) self.timer = nil
DYNAMICCARGO._TrackCargo(self)
if not _DYNAMICCARGO_HELOS then if not _DYNAMICCARGO_HELOS then
_DYNAMICCARGO_HELOS = SET_CLIENT:New():FilterAlive():FilterFunction(DYNAMICCARGO._FilterHeloTypes):FilterStart() _DYNAMICCARGO_HELOS = SET_CLIENT:New():FilterAlive():FilterFunction(DYNAMICCARGO._FilterHeloTypes):FilterStart()
@@ -267,6 +319,55 @@ function DYNAMICCARGO:IsRemoved()
end end
end end
--- Returns true if this cargo is attached to a detected carrier.
-- @param #DYNAMICCARGO self
-- @return #boolean Outcome
function DYNAMICCARGO:IsAttached()
return self._attached == true
end
--- Returns true if this cargo was detached from a detected carrier.
-- @param #DYNAMICCARGO self
-- @return #boolean Outcome
function DYNAMICCARGO:IsDetached()
return self._detached == true
end
--- Returns true if this cargo has seen airborne transport in this cycle.
-- @param #DYNAMICCARGO self
-- @return #boolean Outcome
function DYNAMICCARGO:WasAirborneTransport()
return self._wasAirborne == true
end
--- Returns true if this cargo has reached landed stable confirmation.
-- @param #DYNAMICCARGO self
-- @return #boolean Outcome
function DYNAMICCARGO:IsLandedStable()
return self.CargoState == DYNAMICCARGO.State.UNLOADED and self._detached == true
end
--- Returns last known carrier unit name.
-- @param #DYNAMICCARGO self
-- @return #string Unit name
function DYNAMICCARGO:GetCarrierUnitName()
return self._carrierUnitName
end
--- Returns last known carrier type name.
-- @param #DYNAMICCARGO self
-- @return #string Type name
function DYNAMICCARGO:GetCarrierTypeName()
return self._carrierTypeName
end
--- Returns last known carrier group name.
-- @param #DYNAMICCARGO self
-- @return #string Group name
function DYNAMICCARGO:GetCarrierGroupName()
return self._carrierGroupName
end
--- [CTLD] Get number of crates this DYNAMICCARGO consists of. Always one. --- [CTLD] Get number of crates this DYNAMICCARGO consists of. Always one.
-- @param #DYNAMICCARGO self -- @param #DYNAMICCARGO self
-- @return #number crate number, always one -- @return #number crate number, always one
@@ -398,6 +499,372 @@ end
-- Private Functions -- Private Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- [Internal] Check whether an aircraft typename is a C-130J for special handling.
-- @param #DYNAMICCARGO self
-- @param #string TypeName
-- @return #boolean Outcome
function DYNAMICCARGO:_IsC130Type(TypeName)
return TypeName == "C-130J-30"
end
--- [Internal] Safe AGL calculation helper.
-- @param #DYNAMICCARGO self
-- @param Core.Point#COORDINATE Coord
-- @return #number AGL in meters
function DYNAMICCARGO:_GetAGL(Coord)
if not Coord then return -1 end
return (Coord.y or 0) - Coord:GetLandHeight()
end
--- [Internal] Resolve player name for a client.
-- @param #DYNAMICCARGO self
-- @param Wrapper.Client#CLIENT Client
-- @return #string Player name
function DYNAMICCARGO:_GetPlayerNameForClient(Client)
if not Client then return self.Owner or "None" end
return Client:GetPlayerName() or _DATABASE:_FindPlayerNameByUnitName(Client:GetName()) or self.Owner or "None"
end
--- [Internal] Store current carrier identity fields.
-- @param #DYNAMICCARGO self
-- @param Wrapper.Client#CLIENT Client
-- @param #string PlayerName
-- @return #DYNAMICCARGO self
function DYNAMICCARGO:_SetCarrierFromClient(Client, PlayerName)
if not Client then return self end
self._carrierUnitName = Client:GetName() or self._carrierUnitName
self._carrierTypeName = Client:GetTypeName() or self._carrierTypeName
local grp = Client:GetGroup()
if grp then
self._carrierGroupName = grp:GetName() or self._carrierGroupName
end
self.Owner = PlayerName or self:_GetPlayerNameForClient(Client)
return self
end
--- [Internal] Get scheduler interval for global dynamic cargo updates.
-- @return #number Interval in seconds
function DYNAMICCARGO._GetSchedulerInterval()
return DYNAMICCARGO.Interval or 5
end
--- [Internal] Count currently tracked dynamic cargo objects.
-- @return #number Count
function DYNAMICCARGO._CountTracked()
local n = 0
for _,_ in pairs(DYNAMICCARGO._TrackedCargo or {}) do
n = n + 1
end
return n
end
--- [Internal] Stop global scheduler when no tracked cargo remains.
function DYNAMICCARGO._StopGlobalSchedulerIfIdle()
if DYNAMICCARGO._CountTracked() > 0 then
return
end
if DYNAMICCARGO._GlobalTimer and DYNAMICCARGO._GlobalTimer:IsRunning() then
DYNAMICCARGO._GlobalTimer:Stop()
end
DYNAMICCARGO._GlobalTimer = nil
DYNAMICCARGO._GlobalTimerInterval = nil
end
--- [Internal] Ensure global scheduler is running for tracked cargo updates.
function DYNAMICCARGO._EnsureGlobalScheduler()
local interval = DYNAMICCARGO._GetSchedulerInterval()
if DYNAMICCARGO._GlobalTimer and DYNAMICCARGO._GlobalTimer:IsRunning() then
if DYNAMICCARGO._GlobalTimerInterval == interval then
return
end
DYNAMICCARGO._GlobalTimer:Stop()
DYNAMICCARGO._GlobalTimer = nil
DYNAMICCARGO._GlobalTimerInterval = nil
end
if DYNAMICCARGO._CountTracked() < 1 then
return
end
DYNAMICCARGO._GlobalTimer = TIMER:New(DYNAMICCARGO._UpdateAllTracked)
DYNAMICCARGO._GlobalTimer:Start(interval, interval)
DYNAMICCARGO._GlobalTimerInterval = interval
end
--- [Internal] Add one dynamic cargo object to global tracking.
-- @param #DYNAMICCARGO Cargo
function DYNAMICCARGO._TrackCargo(Cargo)
if not Cargo or not Cargo.StaticName then
return
end
DYNAMICCARGO._TrackedCargo = DYNAMICCARGO._TrackedCargo or {}
DYNAMICCARGO._TrackedCargo[Cargo.StaticName] = Cargo
DYNAMICCARGO._EnsureGlobalScheduler()
end
--- [Internal] Remove one dynamic cargo object from global tracking.
-- @param #string CargoName
function DYNAMICCARGO._UntrackCargo(CargoName)
if not CargoName or not DYNAMICCARGO._TrackedCargo then
DYNAMICCARGO._StopGlobalSchedulerIfIdle()
return
end
DYNAMICCARGO._TrackedCargo[CargoName] = nil
DYNAMICCARGO._StopGlobalSchedulerIfIdle()
end
--- [Internal] Update all tracked dynamic cargo objects in one scheduler tick.
function DYNAMICCARGO._UpdateAllTracked()
local tracked = DYNAMICCARGO._TrackedCargo or {}
local names = {}
for name,_ in pairs(tracked) do
names[#names + 1] = name
end
for _,name in ipairs(names) do
local cargo = tracked[name]
if cargo then
cargo:_UpdatePosition()
end
end
DYNAMICCARGO._StopGlobalSchedulerIfIdle()
end
--- [Internal] Find tracked client by unit name in current dynamic cargo helo set.
-- @param #DYNAMICCARGO self
-- @param #string UnitName
-- @return Wrapper.Client#CLIENT Client
function DYNAMICCARGO:_FindClientByUnitName(UnitName)
if not UnitName or UnitName == "" or not _DYNAMICCARGO_HELOS then return nil end
for _,_helo in pairs(_DYNAMICCARGO_HELOS:GetAliveSet() or {}) do
local helo = _helo -- Wrapper.Client#CLIENT
if helo and helo:IsAlive() and helo:GetName() == UnitName then
return helo
end
end
return nil
end
--- [Internal] Get best known carrier client from stored names/owner.
-- @param #DYNAMICCARGO self
-- @return Wrapper.Client#CLIENT Client
function DYNAMICCARGO:_GetKnownCarrierClient()
local client = nil
if self._carrierUnitName then
client = self:_FindClientByUnitName(self._carrierUnitName)
end
if (not client) and self.Owner and self.Owner ~= "None" then
local byPlayer = CLIENT:FindByPlayerName(self.Owner)
if byPlayer and byPlayer:IsAlive() then
client = byPlayer
end
end
return client
end
--- [Internal] Find nearest live C-130 client.
-- @param #DYNAMICCARGO self
-- @param Core.Point#COORDINATE Pos
-- @param #number Max3D Maximum 3D distance
-- @return Wrapper.Client#CLIENT Client
-- @return #string PlayerName
-- @return #number Dist2D
-- @return #number Dist3D
function DYNAMICCARGO:_FindNearestC130(Pos, Max3D)
if not Pos or not _DYNAMICCARGO_HELOS then return nil, nil, nil, nil end
local bestClient = nil
local bestName = nil
local best2D = math.huge
local best3D = math.huge
local bestOwnerMatch = false
local max3D = Max3D or DYNAMICCARGO.C130OwnerResolveMax3D
local preferredOwner = self.Owner
if preferredOwner == "" or preferredOwner == "None" then
preferredOwner = nil
end
for _,_helo in pairs(_DYNAMICCARGO_HELOS:GetAliveSet() or {}) do
local helo = _helo -- Wrapper.Client#CLIENT
if helo and helo:IsAlive() then
local typename = helo:GetTypeName()
if self:_IsC130Type(typename) then
local hpos = helo:GetCoordinate()
if hpos then
local d3 = hpos:Get3DDistance(Pos)
if d3 <= max3D then
local d2 = hpos:Get2DDistance(Pos)
local pname = self:_GetPlayerNameForClient(helo)
local ownerMatch = preferredOwner and pname and pname == preferredOwner or false
if (ownerMatch and not bestOwnerMatch) or ((ownerMatch == bestOwnerMatch) and d3 < best3D) then
bestClient = helo
bestName = pname
best2D = d2
best3D = d3
bestOwnerMatch = ownerMatch
end
end
end
end
end
end
return bestClient, bestName, best2D, best3D
end
--- [Internal] Resolve/rebind owner to nearest valid C-130 after movement from spawn.
-- @param #DYNAMICCARGO self
-- @param Core.Point#COORDINATE Pos
-- @return Wrapper.Client#CLIENT Client
function DYNAMICCARGO:_ResolveC130Owner(Pos)
if not Pos or not self._spawnVec3 then return nil end
local moved2D = UTILS.VecDist2D(Pos, self._spawnVec3)
if moved2D < (DYNAMICCARGO.C130OwnerResolveMove2D or 10) then
return nil
end
local max3D = DYNAMICCARGO.C130OwnerResolveMax3D or 250
local known = self:_GetKnownCarrierClient()
if known and known:IsAlive() and self:_IsC130Type(known:GetTypeName()) then
local kpos = known:GetCoordinate()
if kpos and kpos:Get3DDistance(Pos) <= max3D then
self:_SetCarrierFromClient(known)
return known
end
end
local nearest, playerName, d2 = self:_FindNearestC130(Pos, DYNAMICCARGO.C130OwnerResolveMax3D)
if nearest and d2 and d2 <= (DYNAMICCARGO.C130OwnerResolveNear2D or 4) then
self:_SetCarrierFromClient(nearest, playerName)
self._ownerResolved = true
self:T(self.lid.." C130 owner re-resolved to "..tostring(self._carrierUnitName).." / "..tostring(self.Owner))
return nearest
end
return nil
end
--- [Internal] Determine whether to run C-130 transport-state handling for this cargo tick.
-- @param #DYNAMICCARGO self
-- @param Core.Point#COORDINATE Pos
-- @return #boolean Outcome
function DYNAMICCARGO:_ShouldUseC130State(Pos)
if self:_IsC130Type(self._carrierTypeName) then
return true
end
local known = self:_GetKnownCarrierClient()
if known and self:_IsC130Type(known:GetTypeName()) then
self:_SetCarrierFromClient(known)
return true
end
if self._attached or self._detached or self._wasAirborne then
return true
end
if self.CargoState == DYNAMICCARGO.State.NEW or self.CargoState == DYNAMICCARGO.State.UNLOADED then
local nearest, _, d2 = self:_FindNearestC130(Pos, DYNAMICCARGO.C130AttachDistance + 50)
if nearest and d2 and d2 <= (DYNAMICCARGO.C130AttachDistance + 5) then
return true
end
end
return false
end
--- [Internal] C-130-specific transport state update.
-- @param #DYNAMICCARGO self
-- @param Core.Point#COORDINATE Pos
-- @return #DYNAMICCARGO self
function DYNAMICCARGO:_UpdatePositionC130(Pos)
local attachDist = DYNAMICCARGO.C130AttachDistance or 10
local detachDist = DYNAMICCARGO.C130DetachDistance or 14
local airborneAgl = DYNAMICCARGO.C130AirborneAGL or 8
local landedAgl = DYNAMICCARGO.C130LandedAGL or 0.5
local stableEps = DYNAMICCARGO.C130StabilityEpsilon or 0.05
local requireAirborne = DYNAMICCARGO.C130RequireAirborne ~= false
local cargoAgl = self:_GetAGL(Pos)
local carrier = self:_GetKnownCarrierClient()
if carrier and not self:_IsC130Type(carrier:GetTypeName()) then
carrier = nil
end
if not carrier then
carrier = self:_ResolveC130Owner(Pos)
end
-- Attach detection: grounded C-130 close to cargo.
if (self.CargoState == DYNAMICCARGO.State.NEW or self.CargoState == DYNAMICCARGO.State.UNLOADED) and (not self._attached) then
if not carrier then
local nearest, pname, d2 = self:_FindNearestC130(Pos, DYNAMICCARGO.C130OwnerResolveMax3D)
if nearest and d2 and d2 <= attachDist and not nearest:InAir() then
carrier = nearest
self:_SetCarrierFromClient(nearest, pname)
end
end
if carrier and carrier:IsAlive() then
local hpos = carrier:GetCoordinate()
if hpos and (not carrier:InAir()) and hpos:Get2DDistance(Pos) <= attachDist then
self._attached = true
self._detached = false
self._wasAirborne = false
self._landAglConfirm = nil
self:_SetCarrierFromClient(carrier)
if self.CargoState ~= DYNAMICCARGO.State.LOADED then
self.CargoState = DYNAMICCARGO.State.LOADED
self:T(self.lid.." C130 attach: "..tostring(self.Owner))
_DATABASE:CreateEventDynamicCargoLoaded(self)
end
end
end
end
if self.CargoState == DYNAMICCARGO.State.LOADED then
if not carrier then
carrier = self:_ResolveC130Owner(Pos)
end
local carrierInAir = false
local dist2D = math.huge
local carrierAgl = -1
if carrier and carrier:IsAlive() then
local hpos = carrier:GetCoordinate()
if hpos then
dist2D = hpos:Get2DDistance(Pos)
carrierAgl = self:_GetAGL(hpos)
end
carrierInAir = carrier:InAir()
self:_SetCarrierFromClient(carrier)
end
if cargoAgl >= airborneAgl or carrierAgl >= airborneAgl then
self._wasAirborne = true
end
-- Detach only when actually airborne and separated.
if self._attached and carrierInAir and dist2D > detachDist then
self._attached = false
self._detached = true
self._landAglConfirm = nil
self:T(self.lid.." C130 detach at d2="..tostring(UTILS.Round(dist2D,2)))
end
-- Fallback detach if carrier reference is stale but cargo already transitioned airborne.
if self._attached and (not carrier or not carrier:IsAlive()) and self._wasAirborne and cargoAgl <= airborneAgl then
self._attached = false
self._detached = true
self._landAglConfirm = nil
self:T(self.lid.." C130 detach fallback (carrier stale)")
end
local canUnload = self._detached and ((not requireAirborne) or self._wasAirborne)
if canUnload then
local moved3D = self.LastPosition and UTILS.VecDist3D(Pos, self.LastPosition) or math.huge
local stable = moved3D <= stableEps
if cargoAgl <= landedAgl and stable then
if self._landAglConfirm then
self.CargoState = DYNAMICCARGO.State.UNLOADED
self:T(self.lid.." C130 landed-stable unload by "..tostring(self.Owner))
_DATABASE:CreateEventDynamicCargoUnloaded(self)
else
self._landAglConfirm = true
end
else
self._landAglConfirm = nil
end
end
end
return self
end
--- [Internal] _Get helo hovering intel --- [Internal] _Get helo hovering intel
-- @param #DYNAMICCARGO self -- @param #DYNAMICCARGO self
-- @param Wrapper.Unit#UNIT Unit The Unit to test -- @param Wrapper.Unit#UNIT Unit The Unit to test
@@ -444,11 +911,12 @@ function DYNAMICCARGO:_GetPossibleHeloNearby(pos,loading)
local hpos = helo:GetCoordinate() local hpos = helo:GetCoordinate()
-- TODO Check unloading via sling load? -- TODO Check unloading via sling load?
local typename = helo:GetTypeName() local typename = helo:GetTypeName()
if not self:_IsC130Type(typename) then
local dimensions = DYNAMICCARGO.AircraftDimensions[typename] local dimensions = DYNAMICCARGO.AircraftDimensions[typename]
if hpos and typename and dimensions then
local hovering, height = self:_HeloHovering(helo,dimensions.ropelength) local hovering, height = self:_HeloHovering(helo,dimensions.ropelength)
local helolanded = not helo:InAir() local helolanded = not helo:InAir()
self:T(self.lid.." InAir: AGL/Hovering: "..hpos.y-hpos:GetLandHeight().."/"..tostring(hovering)) self:T(self.lid.." InAir: AGL/Hovering: "..hpos.y-hpos:GetLandHeight().."/"..tostring(hovering))
if hpos and typename and dimensions then
local delta2D = hpos:Get2DDistance(pos) local delta2D = hpos:Get2DDistance(pos)
local delta3D = hpos:Get3DDistance(pos) local delta3D = hpos:Get3DDistance(pos)
if self.testing then if self.testing then
@@ -476,6 +944,7 @@ function DYNAMICCARGO:_GetPossibleHeloNearby(pos,loading)
end end
end end
end end
end
return success,Helo,Playername return success,Helo,Playername
end end
@@ -490,17 +959,27 @@ function DYNAMICCARGO:_UpdatePosition()
self:T(string.format("Cargo position: x=%d, y=%d, z=%d",pos.x,pos.y,pos.z)) self:T(string.format("Cargo position: x=%d, y=%d, z=%d",pos.x,pos.y,pos.z))
self:T(string.format("Last position: x=%d, y=%d, z=%d",self.LastPosition.x,self.LastPosition.y,self.LastPosition.z)) self:T(string.format("Last position: x=%d, y=%d, z=%d",self.LastPosition.x,self.LastPosition.y,self.LastPosition.z))
end end
if UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5 then -- This checks if the cargo has moved more than 0.5m since last check. If so then the cargo is loaded local moved = UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5
if self:_ShouldUseC130State(pos) then
self:_UpdatePositionC130(pos)
self.LastPosition = pos
elseif moved then -- This checks if the cargo has moved more than 0.5m since last check. If so then the cargo is loaded
--------------- ---------------
-- LOAD Cargo -- LOAD Cargo
--------------- ---------------
if self.CargoState == DYNAMICCARGO.State.NEW or self.CargoState == DYNAMICCARGO.State.UNLOADED then if self.CargoState == DYNAMICCARGO.State.NEW or self.CargoState == DYNAMICCARGO.State.UNLOADED then
local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true) local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true)
if isloaded then
self:T(self.lid.." moved! NEW -> LOADED by "..tostring(playername)) self:T(self.lid.." moved! NEW -> LOADED by "..tostring(playername))
self.CargoState = DYNAMICCARGO.State.LOADED self.CargoState = DYNAMICCARGO.State.LOADED
self.Owner = playername self.Owner = playername
if client then
self:_SetCarrierFromClient(client, playername)
end
_DATABASE:CreateEventDynamicCargoLoaded(self) _DATABASE:CreateEventDynamicCargoLoaded(self)
end end
end
self.LastPosition = pos
--------------- ---------------
-- UNLOAD Cargo -- UNLOAD Cargo
--------------- ---------------
@@ -524,30 +1003,31 @@ function DYNAMICCARGO:_UpdatePosition()
self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername)) self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername))
self.CargoState = DYNAMICCARGO.State.UNLOADED self.CargoState = DYNAMICCARGO.State.UNLOADED
self.Owner = playername self.Owner = playername
if client then
self:_SetCarrierFromClient(client, playername)
end
_DATABASE:CreateEventDynamicCargoUnloaded(self) _DATABASE:CreateEventDynamicCargoUnloaded(self)
end end
end end
end end
self.LastPosition = pos
--end
else else
--------------- ---------------
-- REMOVED Cargo -- REMOVED Cargo
--------------- ---------------
if self.timer and self.timer:IsRunning() then if self.CargoState ~= DYNAMICCARGO.State.REMOVED then
self.timer:Stop() DYNAMICCARGO._UntrackCargo(self.StaticName)
self.timer=nil self.timer=nil
end
self:T(self.lid.." dead! " ..self.CargoState.."-> REMOVED") self:T(self.lid.." dead! " ..self.CargoState.."-> REMOVED")
self.CargoState = DYNAMICCARGO.State.REMOVED self.CargoState = DYNAMICCARGO.State.REMOVED
_DATABASE:CreateEventDynamicCargoRemoved(self) _DATABASE:CreateEventDynamicCargoRemoved(self)
end end
end
return self return self
end end
--- [USER] Destroy a DYNAMICCARGO object. --- [USER] Destroy a DYNAMICCARGO object.
-- @param #DYNAMICCARGO self -- @param #DYNAMICCARGO self
-- @param #boolean GenerateEvent (Optional) Set to false to remove an item silently. Defaults to true. -- @param #boolean GenerateEvent Set to false to remove an item silently. Defaults to true.
-- @return #boolean Return Returns nil if the object could not be found, else returns true. -- @return #boolean Return Returns nil if the object could not be found, else returns true.
function DYNAMICCARGO:Destroy(GenerateEvent) function DYNAMICCARGO:Destroy(GenerateEvent)
local DCSObject = self:GetDCSObject() local DCSObject = self:GetDCSObject()