Updated cargo crates spawning and logic
This commit is contained in:
@@ -20,7 +20,7 @@ local SupplyLoadConfig = {
|
|||||||
["CH-47Fbl1"] = {
|
["CH-47Fbl1"] = {
|
||||||
maxInternalLoad = 10000,
|
maxInternalLoad = 10000,
|
||||||
dropZones = {
|
dropZones = {
|
||||||
{ centerAngle = 180, angleWidth = 60, minRadius = 20, maxRadius = 75, spacing = 5 },
|
{ centerAngle = 180, angleWidth = 30, minRadius = 20, maxRadius = 75, spacing = 5 },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
["Mi-24P"] = {
|
["Mi-24P"] = {
|
||||||
|
|||||||
@@ -297,9 +297,13 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe
|
|||||||
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
|
self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType)
|
||||||
|
|
||||||
local unit = DcsUtil.GetPlayerUnitByID(unitID)
|
local unit = DcsUtil.GetPlayerUnitByID(unitID)
|
||||||
if unit == nil or unit:isExist() == false then return end
|
if unit == nil or unit:isExist() == false then
|
||||||
|
self._logger:warn("Unload requested for non-existent unit: " .. unitID)
|
||||||
|
return
|
||||||
|
end
|
||||||
local group = unit:getGroup()
|
local group = unit:getGroup()
|
||||||
if group == nil then
|
if group == nil then
|
||||||
|
self._logger:warn("Unload requested for unit with no group: " .. unit:getName())
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -329,9 +333,11 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe
|
|||||||
y = cargoPos.z,
|
y = cargoPos.z,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._logger:debug("Spawning crate #" .. cargoCount .. " at (" .. string.format("%.2f", cargoPos.x) .. ", " .. string.format("%.2f", cargoPos.y) .. ", " .. string.format("%.2f", cargoPos.z) .. ")")
|
||||||
local spawned = coalition.addStaticObject(unit:getCoalition(), cargoSpawnObject)
|
local spawned = coalition.addStaticObject(unit:getCoalition(), cargoSpawnObject)
|
||||||
self._droppedCrates[cargoSpawnObject.name] = spawned
|
self._droppedCrates[cargoSpawnObject.name] = spawned
|
||||||
missionCommandsHelper:updateCommandsForGroup(group:getID())
|
missionCommandsHelper:updateCommandsForGroup(group:getID())
|
||||||
|
self._logger:debug("Cargo dropped for unit: " .. unit:getName() .. " crateType: " .. crateType)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return table<string,StaticObject>
|
---@return table<string,StaticObject>
|
||||||
@@ -465,6 +471,7 @@ end
|
|||||||
---@class SupplyUnitsBoundingBox
|
---@class SupplyUnitsBoundingBox
|
||||||
---@field min Vec3 World space minimum
|
---@field min Vec3 World space minimum
|
||||||
---@field max Vec3 World space maximum
|
---@field max Vec3 World space maximum
|
||||||
|
---@field heading number? Object heading in radians
|
||||||
|
|
||||||
---@param foundObject Object
|
---@param foundObject Object
|
||||||
---@return SupplyUnitsBoundingBox?
|
---@return SupplyUnitsBoundingBox?
|
||||||
@@ -481,20 +488,61 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject)
|
|||||||
|
|
||||||
local objPos = foundObject:getPoint()
|
local objPos = foundObject:getPoint()
|
||||||
local box = desc.box
|
local box = desc.box
|
||||||
|
local heading = 0
|
||||||
|
|
||||||
|
-- Try to get object heading from position vector's forward direction
|
||||||
|
-- This works for units and other objects that support getPosition
|
||||||
|
pcall(function()
|
||||||
|
local objPosition = foundObject:getPosition()
|
||||||
|
if objPosition and objPosition.x then
|
||||||
|
heading = math.atan2(objPosition.x.z, objPosition.x.x)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- For rotated objects, we need to rotate the bounding box
|
||||||
|
local minX = box.min.x
|
||||||
|
local maxX = box.max.x
|
||||||
|
local minZ = box.min.z
|
||||||
|
local maxZ = box.max.z
|
||||||
|
|
||||||
|
-- If object has significant rotation, apply rotation to bbox corners
|
||||||
|
if math.abs(heading) > 0.1 then
|
||||||
|
-- Get all 4 corners of bbox in local space
|
||||||
|
local corners = {
|
||||||
|
{minX, minZ},
|
||||||
|
{minX, maxZ},
|
||||||
|
{maxX, minZ},
|
||||||
|
{maxX, maxZ}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Rotate corners and find new min/max
|
||||||
|
minX, maxX = math.huge, -math.huge
|
||||||
|
minZ, maxZ = math.huge, -math.huge
|
||||||
|
|
||||||
|
for _, corner in ipairs(corners) do
|
||||||
|
local rotX = corner[1] * math.cos(heading) - corner[2] * math.sin(heading)
|
||||||
|
local rotZ = corner[1] * math.sin(heading) + corner[2] * math.cos(heading)
|
||||||
|
minX = math.min(minX, rotX)
|
||||||
|
maxX = math.max(maxX, rotX)
|
||||||
|
minZ = math.min(minZ, rotZ)
|
||||||
|
maxZ = math.max(maxZ, rotZ)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Convert relative bbox to world space by adding object position
|
-- Convert relative bbox to world space by adding object position
|
||||||
---@type SupplyUnitsBoundingBox
|
---@type SupplyUnitsBoundingBox
|
||||||
return {
|
return {
|
||||||
min = {
|
min = {
|
||||||
x = objPos.x + box.min.x,
|
x = objPos.x + minX,
|
||||||
y = objPos.y + box.min.y,
|
y = objPos.y + box.min.y,
|
||||||
z = objPos.z + box.min.z
|
z = objPos.z + minZ
|
||||||
},
|
},
|
||||||
max = {
|
max = {
|
||||||
x = objPos.x + box.max.x,
|
x = objPos.x + maxX,
|
||||||
y = objPos.y + box.max.y,
|
y = objPos.y + box.max.y,
|
||||||
z = objPos.z + box.max.z
|
z = objPos.z + maxZ
|
||||||
}
|
},
|
||||||
|
heading = heading
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -530,6 +578,10 @@ end
|
|||||||
function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
||||||
|
|
||||||
local unitPos = unit:getPosition()
|
local unitPos = unit:getPosition()
|
||||||
|
|
||||||
|
-- Get unit's heading from the forward vector (x component)
|
||||||
|
-- Heading is calculated as: atan2(forward.z, forward.x)
|
||||||
|
local unitHeading = math.atan2(unitPos.x.z, unitPos.x.x)
|
||||||
|
|
||||||
-- Get crate bbox - relative to placement position
|
-- Get crate bbox - relative to placement position
|
||||||
local crateDesc = StaticObject.getDescByName(crateTypeName) --[[@as table]]
|
local crateDesc = StaticObject.getDescByName(crateTypeName) --[[@as table]]
|
||||||
@@ -566,14 +618,24 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
|||||||
local found = function(foundItem, val)
|
local found = function(foundItem, val)
|
||||||
local bbox = self:GetBoundingBoxes(foundItem)
|
local bbox = self:GetBoundingBoxes(foundItem)
|
||||||
if bbox then
|
if bbox then
|
||||||
|
self._logger:debug("Found object: " .. foundItem:getTypeName() .. " at (" .. foundItem:getPoint().x .. ", " .. foundItem:getPoint().z .. ")")
|
||||||
table.insert(occupiedObjects, {
|
table.insert(occupiedObjects, {
|
||||||
pos = foundItem:getPoint(),
|
pos = foundItem:getPoint(),
|
||||||
bbox = bbox
|
bbox = bbox
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
self._logger:debug("Found object without bbox: " .. foundItem:getTypeName())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
world.searchObjects(searchVolume.id, searchVolume, found)
|
local searchCategories = {}
|
||||||
|
for key, value in pairs(Object.Category) do
|
||||||
|
self._logger:debug("Adding category to search: " .. tostring(value) .. " (" .. tostring(key) .. ")")
|
||||||
|
table.insert(searchCategories, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: param-type-mismatch
|
||||||
|
world.searchObjects(searchCategories, searchVolume, found)
|
||||||
|
|
||||||
local safetyMargin = 3 -- Safety margin around objects
|
local safetyMargin = 3 -- Safety margin around objects
|
||||||
|
|
||||||
@@ -591,23 +653,26 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
|||||||
for angle = minAngle, maxAngle, angleStep do
|
for angle = minAngle, maxAngle, angleStep do
|
||||||
local radians = math.rad(angle)
|
local radians = math.rad(angle)
|
||||||
|
|
||||||
-- Calculate position at this angle and distance
|
-- Calculate position at this angle and distance, relative to unit's heading
|
||||||
-- Angle 0 = forward, 90 = right, 180 = rear, 270 = left
|
-- Angle 0 = forward, 90 = right, 180 = rear, 270 = left
|
||||||
local candidateX = unitPos.p.x + distance * math.sin(radians)
|
-- Apply unit heading to make angles relative to unit orientation
|
||||||
local candidateY = unitPos.p.y + distance * math.cos(radians)
|
local worldAngle = radians + unitHeading
|
||||||
local candidateZ = land.getHeight({ x = candidateX, y = candidateY })
|
|
||||||
|
local candidateX = unitPos.p.x + distance * math.sin(worldAngle)
|
||||||
|
local candidateZ = unitPos.p.z + distance * math.cos(worldAngle)
|
||||||
|
local candidateY = land.getHeight({ x = candidateX, y = candidateZ })
|
||||||
|
|
||||||
-- Convert crate's relative bbox to world space at this position
|
-- Convert crate's relative bbox to world space at this position
|
||||||
local crateBBoxWorldSpace = {
|
local crateBBoxWorldSpace = {
|
||||||
min = {
|
min = {
|
||||||
x = candidateX + crateRelativeBBox.min.x,
|
x = candidateX + crateRelativeBBox.min.x,
|
||||||
y = candidateZ + crateRelativeBBox.min.y,
|
y = candidateY + crateRelativeBBox.min.y,
|
||||||
z = candidateY + crateRelativeBBox.min.z
|
z = candidateZ + crateRelativeBBox.min.z
|
||||||
},
|
},
|
||||||
max = {
|
max = {
|
||||||
x = candidateX + crateRelativeBBox.max.x,
|
x = candidateX + crateRelativeBBox.max.x,
|
||||||
y = candidateZ + crateRelativeBBox.max.y,
|
y = candidateY + crateRelativeBBox.max.y,
|
||||||
z = candidateY + crateRelativeBBox.max.z
|
z = candidateZ + crateRelativeBBox.max.z
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,12 +681,14 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName)
|
|||||||
for _, obj in ipairs(occupiedObjects) do
|
for _, obj in ipairs(occupiedObjects) do
|
||||||
if self:CheckBBoxCollision(crateBBoxWorldSpace, obj.bbox, safetyMargin) then
|
if self:CheckBBoxCollision(crateBBoxWorldSpace, obj.bbox, safetyMargin) then
|
||||||
collides = true
|
collides = true
|
||||||
|
self._logger:debug("Collision at angle=" .. angle .. ", distance=" .. distance)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not collides then
|
if not collides then
|
||||||
return { x = candidateX, y = candidateZ, z = candidateY }
|
self._logger:debug("Valid position found at angle=" .. angle .. ", distance=" .. distance .. ", pos=(" .. string.format("%.2f", candidateX) .. ", " .. string.format("%.2f", candidateY) .. ", " .. string.format("%.2f", candidateZ) .. ")")
|
||||||
|
return { x = candidateX, y = candidateY, z = candidateZ }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user