From 24608d36c546e5c7233fee179fee6aa01ae84246 Mon Sep 17 00:00:00 2001 From: dutchie031 Date: Sun, 16 Aug 2026 13:06:07 +0200 Subject: [PATCH] Updated cargo crates spawning and logic --- .../stageClasses/helpers/SupplyLoadConfig.lua | 2 +- .../helpers/SupplyUnitsTracker.lua | 99 ++++++++++++++++--- 2 files changed, 84 insertions(+), 17 deletions(-) diff --git a/src/classes/stageClasses/helpers/SupplyLoadConfig.lua b/src/classes/stageClasses/helpers/SupplyLoadConfig.lua index 83c651d..90b42db 100644 --- a/src/classes/stageClasses/helpers/SupplyLoadConfig.lua +++ b/src/classes/stageClasses/helpers/SupplyLoadConfig.lua @@ -20,7 +20,7 @@ local SupplyLoadConfig = { ["CH-47Fbl1"] = { maxInternalLoad = 10000, dropZones = { - { centerAngle = 180, angleWidth = 60, minRadius = 20, maxRadius = 75, spacing = 5 }, + { centerAngle = 180, angleWidth = 30, minRadius = 20, maxRadius = 75, spacing = 5 }, } }, ["Mi-24P"] = { diff --git a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua index f6376f1..d98d2e0 100644 --- a/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua +++ b/src/classes/stageClasses/helpers/SupplyUnitsTracker.lua @@ -297,9 +297,13 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe self._logger:debug("Unload requested for unit: " .. unitID .. " crateType: " .. crateType) 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() if group == nil then + self._logger:warn("Unload requested for unit with no group: " .. unit:getName()) return end @@ -329,9 +333,11 @@ function SupplyUnitsTracker:UnloadRequested(unitID, crateType, missionCommandsHe 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) self._droppedCrates[cargoSpawnObject.name] = spawned missionCommandsHelper:updateCommandsForGroup(group:getID()) + self._logger:debug("Cargo dropped for unit: " .. unit:getName() .. " crateType: " .. crateType) end ---@return table @@ -465,6 +471,7 @@ end ---@class SupplyUnitsBoundingBox ---@field min Vec3 World space minimum ---@field max Vec3 World space maximum +---@field heading number? Object heading in radians ---@param foundObject Object ---@return SupplyUnitsBoundingBox? @@ -481,20 +488,61 @@ function SupplyUnitsTracker:GetBoundingBoxes(foundObject) local objPos = foundObject:getPoint() 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 ---@type SupplyUnitsBoundingBox return { min = { - x = objPos.x + box.min.x, + x = objPos.x + minX, y = objPos.y + box.min.y, - z = objPos.z + box.min.z + z = objPos.z + minZ }, max = { - x = objPos.x + box.max.x, + x = objPos.x + maxX, y = objPos.y + box.max.y, - z = objPos.z + box.max.z - } + z = objPos.z + maxZ + }, + heading = heading } end @@ -530,6 +578,10 @@ end function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName) 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 local crateDesc = StaticObject.getDescByName(crateTypeName) --[[@as table]] @@ -566,14 +618,24 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName) local found = function(foundItem, val) local bbox = self:GetBoundingBoxes(foundItem) if bbox then + self._logger:debug("Found object: " .. foundItem:getTypeName() .. " at (" .. foundItem:getPoint().x .. ", " .. foundItem:getPoint().z .. ")") table.insert(occupiedObjects, { pos = foundItem:getPoint(), bbox = bbox }) + else + self._logger:debug("Found object without bbox: " .. foundItem:getTypeName()) 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 @@ -591,23 +653,26 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName) for angle = minAngle, maxAngle, angleStep do 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 - local candidateX = unitPos.p.x + distance * math.sin(radians) - local candidateY = unitPos.p.y + distance * math.cos(radians) - local candidateZ = land.getHeight({ x = candidateX, y = candidateY }) + -- Apply unit heading to make angles relative to unit orientation + local worldAngle = radians + unitHeading + + 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 local crateBBoxWorldSpace = { min = { x = candidateX + crateRelativeBBox.min.x, - y = candidateZ + crateRelativeBBox.min.y, - z = candidateY + crateRelativeBBox.min.z + y = candidateY + crateRelativeBBox.min.y, + z = candidateZ + crateRelativeBBox.min.z }, max = { x = candidateX + crateRelativeBBox.max.x, - y = candidateZ + crateRelativeBBox.max.y, - z = candidateY + crateRelativeBBox.max.z + y = candidateY + crateRelativeBBox.max.y, + z = candidateZ + crateRelativeBBox.max.z } } @@ -616,12 +681,14 @@ function SupplyUnitsTracker:GetCargoPlacePosition(unit, crateTypeName) for _, obj in ipairs(occupiedObjects) do if self:CheckBBoxCollision(crateBBoxWorldSpace, obj.bbox, safetyMargin) then collides = true + self._logger:debug("Collision at angle=" .. angle .. ", distance=" .. distance) break end end 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