diff --git a/RELEASENOTES.md b/RELEASENOTES.md index eaf720d..e8e479e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -8,22 +8,15 @@ ### Bug Fixes - Issue #11 - PR #17 Supply crate spawning now checks for free space and will not spawn if the area is too crowded. Additionally different units will spawn in different areas depending on loading side. -- PR #15 - Fixed command wiring for supply hubs for better and more accurate detection of units spawning and entering/exiting zone. - - -## [0.12.1] 2026-07 - -### Breaking Changes - -### New Features - -### Bug Fixes - -- Fixed stage drawing to only be checked when a stage completed. Now done on stage number changed. + PR #17 +- Fixed command wiring for supply hubs for better and more accurate detection of units spawning and entering/exiting zone. + PR #15 +- Fixed custom drawings not being drawn correctly. + PR #18 +- Fixed CAP Callbacks not working since the change to a transpiled script. Now a global callback circumvents this issue. + PR #18 ## [0.12.0] 2026-06 diff --git a/src/classes/capClasses/taskings/CAP.lua b/src/classes/capClasses/taskings/CAP.lua index ecc8f2b..cb53bd2 100644 --- a/src/classes/capClasses/taskings/CAP.lua +++ b/src/classes/capClasses/taskings/CAP.lua @@ -220,7 +220,7 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishOnStation, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishOnStation, \"" .. groupName .. "\")" } } } @@ -260,7 +260,7 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) }, stopCondition = { duration = durationBefore10, - condition = "return Spearhead.DcsUtil.NeedsRTBInTen(\"" .. groupName .. "\", 0.10)", + condition = "return GlobalCapCallBacks.NeedsRTBInTen(\"" .. groupName .. "\", 0.10)", } } }, @@ -273,7 +273,7 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTBInTen, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTBInTen, \"" .. groupName .. "\")" } } } @@ -299,7 +299,7 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) }, stopCondition = { duration = durationAfter10, - condition = "return Spearhead.DcsUtil.IsBingoFuel(\"" .. groupName .. "\")", + condition = "return GlobalCapCallBacks.IsBingoFuel(\"" .. groupName .. "\", 0.10)", } } }, @@ -312,7 +312,7 @@ function CAP.getAsTasking(groupName, airbase, capZone, capConfig) action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } } diff --git a/src/classes/capClasses/taskings/Callbacks/GlobalCallbacks.lua b/src/classes/capClasses/taskings/Callbacks/GlobalCallbacks.lua new file mode 100644 index 0000000..0c6096f --- /dev/null +++ b/src/classes/capClasses/taskings/Callbacks/GlobalCallbacks.lua @@ -0,0 +1,26 @@ + +local DcsUtil = require("classes.util.DcsUtil") +local Events = require("classes.spearhead_events") + + +GlobalCapCallBacks = {} + +function GlobalCapCallBacks.IsBingoFuel(groupName, fuelPercent) + return DcsUtil.IsBingoFuel(groupName, fuelPercent) +end + +function GlobalCapCallBacks.NeedsRTBInTen(groupName, fuelOffset) + return DcsUtil.NeedsRTBInTen(groupName, fuelOffset) +end + +function GlobalCapCallBacks.PublishRTBInTen(groupName) + return Events.PublishRTBInTen(groupName) +end + +function GlobalCapCallBacks.PublishRTB(groupName) + return Events.PublishRTB(groupName) +end + +function GlobalCapCallBacks.PublishOnStation(groupName) + return Events.PublishOnStation(groupName) +end diff --git a/src/classes/capClasses/taskings/INTERCEPT.lua b/src/classes/capClasses/taskings/INTERCEPT.lua index 8ed2600..e6cdc8d 100644 --- a/src/classes/capClasses/taskings/INTERCEPT.lua +++ b/src/classes/capClasses/taskings/INTERCEPT.lua @@ -247,7 +247,7 @@ function INTERCEPT.getInterceptTaskPoint(groupName, currentPoint, targetPoint, a action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } } @@ -379,7 +379,7 @@ function INTERCEPT.getUnitInterceptTaskPoint(groupName, currentPoint, targetPosi action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } } diff --git a/src/classes/capClasses/taskings/SWEEP.lua b/src/classes/capClasses/taskings/SWEEP.lua index ed2ee40..61e12aa 100644 --- a/src/classes/capClasses/taskings/SWEEP.lua +++ b/src/classes/capClasses/taskings/SWEEP.lua @@ -189,7 +189,7 @@ function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig) action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishOnStation, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishOnStation, \"" .. groupName .. "\")" } } } @@ -270,7 +270,7 @@ function SWEEP.getAsTasking(groupName, airbase, capZone, capConfig) action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } } diff --git a/src/classes/spearhead_db.lua b/src/classes/spearhead_db.lua index 85ed3d9..653d1b2 100644 --- a/src/classes/spearhead_db.lua +++ b/src/classes/spearhead_db.lua @@ -231,7 +231,7 @@ function Database.New(Logger) for key, layer_object in pairs(layer.objects) do if Util.startswith(layer_object.name, "drawing_", true) then local object = layer_object --[[@as DrawingObject]] - local customDrawing = CustomDrawing.New(object) + local customDrawing = CustomDrawing.New(object, nil, self._logger.LogLevel) table.insert(self._tables.CustomDrawings, customDrawing) end end diff --git a/src/classes/spearhead_routeutil.lua b/src/classes/spearhead_routeutil.lua index 6e5f143..717ee5b 100644 --- a/src/classes/spearhead_routeutil.lua +++ b/src/classes/spearhead_routeutil.lua @@ -92,7 +92,7 @@ do --setup route util action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishOnStation, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishOnStation, \"" .. groupName .. "\")" } } } @@ -122,7 +122,7 @@ do --setup route util }, stopCondition = { duration = durationBefore10, - condition = "return Spearhead.DcsUtil.IsBingoFuel(\"" .. groupName .. "\", 0.10)", + condition = "return GlobalCapCallBacks.IsBingoFuel(\"" .. groupName .. "\", 0.10)", } } }, @@ -135,7 +135,7 @@ do --setup route util action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTBInTen, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTBInTen, \"" .. groupName .. "\")" } } } @@ -156,7 +156,7 @@ do --setup route util }, stopCondition = { duration = durationAfter10, - condition = "return Spearhead.DcsUtil.IsBingoFuel(\"" .. groupName .. "\")", + condition = "return GlobalCapCallBacks.IsBingoFuel(\"" .. groupName .. "\", 0.10)", } } }, @@ -169,7 +169,7 @@ do --setup route util action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. groupName .. "\")" + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } } @@ -375,7 +375,7 @@ do --setup route util action = { id = "Script", params = { - command = "pcall(Spearhead.Events.PublishRTB, \"" .. + command = "pcall(GlobalCapCallBacks.PublishRTB, \"" .. groupName .. "\")" } } diff --git a/src/classes/stageClasses/GlobalStageManager.lua b/src/classes/stageClasses/GlobalStageManager.lua index ad232a3..71a636c 100644 --- a/src/classes/stageClasses/GlobalStageManager.lua +++ b/src/classes/stageClasses/GlobalStageManager.lua @@ -216,12 +216,15 @@ end ---@private function GlobalStageManager:UpdateDrawings(stageNumber) + self.logger:debug("Updating custom drawings for stage number: " .. tostring(stageNumber)) local drawings = self.database:getCustomDrawings() for _, drawing in pairs(drawings) do local startStage, stopStage = drawing:GetStartAndStop() if stageNumber >= startStage and stageNumber < stopStage then + self.logger:debug("Drawing " .. drawing:GetName() .. " is active for stage number: " .. tostring(stageNumber)) drawing:Draw() else + self.logger:debug("Drawing " .. drawing:GetName() .. " is not active for stage number: " .. tostring(stageNumber)) drawing:Remove() end end diff --git a/src/classes/stageClasses/drawings/CustomDrawing.lua b/src/classes/stageClasses/drawings/CustomDrawing.lua index fa97160..df577c1 100644 --- a/src/classes/stageClasses/drawings/CustomDrawing.lua +++ b/src/classes/stageClasses/drawings/CustomDrawing.lua @@ -1,23 +1,27 @@ local DrawingHelper = require("classes.stageClasses.drawings.helper.DrawingHelper") local Util = require("classes.util.Util") +local Logger = require("classes.util.Logger") ---@class CustomDrawing ---@field private _id integer? ---@field private _drawingObject DrawingObject ---@field private _startingStage number ---@field private _removeAtStage number +---@field private _logger Logger local CustomDrawing = {} CustomDrawing.__index = CustomDrawing ---@param drawingObject DrawingObject ---@param id integer? +---@param loglevel LogLevel ---@return CustomDrawing -function CustomDrawing.New(drawingObject, id) +function CustomDrawing.New(drawingObject, id, loglevel) local self = setmetatable({}, CustomDrawing) self._drawingObject = drawingObject self._id = id + self._logger = Logger.new("CustomDrawing", loglevel) local name = drawingObject.name local split = Util.split_string(name or "", "_") @@ -28,6 +32,11 @@ function CustomDrawing.New(drawingObject, id) return self end +---@return string +function CustomDrawing:GetName() + return self._drawingObject.name +end + ---@return number start ---@return number stop function CustomDrawing:GetStartAndStop() @@ -35,11 +44,18 @@ function CustomDrawing:GetStartAndStop() end function CustomDrawing:Draw() - self._id = DrawingHelper.Draw(self._drawingObject) + self._logger:debug("Drawing custom drawing with ID " .. tostring(self._id)) + + if self._id ~= nil then + DrawingHelper.Remove(self._id) + end + + self._id = DrawingHelper.Draw(self._drawingObject, self._logger) end function CustomDrawing:Remove() if self._id ~= nil then + self._logger:debug("Removing custom drawing with ID " .. tostring(self._id)) DrawingHelper.Remove(self._id) self._id = nil end diff --git a/src/classes/stageClasses/drawings/helper/DrawingHelper.lua b/src/classes/stageClasses/drawings/helper/DrawingHelper.lua index 99d6c93..fa2b179 100644 --- a/src/classes/stageClasses/drawings/helper/DrawingHelper.lua +++ b/src/classes/stageClasses/drawings/helper/DrawingHelper.lua @@ -1,25 +1,39 @@ - +local Util = require("classes.util.Util") ---@class DrawingHelper local DrawingHelper = {} DrawingHelper.__index = DrawingHelper + local customDrawingIdIncrementer = 4210 ---@param object DrawingObject +---@param logger Logger? ---@return integer? id -function DrawingHelper.Draw(object) +function DrawingHelper.Draw(object, logger) if object == nil then + if logger then + logger:warn("DrawingHelper.Draw called with nil object") + end return nil end local id = DrawingHelper.GetAndAddId() + + if logger then + logger:debug("Drawing object with ID " .. tostring(id) .. " and primitive type: " .. tostring(object.primitiveType)) + end + if(object.primitiveType == "Polygon") then - DrawingHelper.DrawPolygon(object--[[@as Polygon]], id) + DrawingHelper.DrawPolygon(object--[[@as Polygon]], id, logger) elseif(object.primitiveType == "Line") then - DrawingHelper.DrawLine(object--[[@as Line]], id) + DrawingHelper.DrawLine(object--[[@as Line]], id, logger) elseif(object.primitiveType == "TextBox") then DrawingHelper.DrawTextBox(object--[[@as TextBox]], id) + else + if logger then + logger:warn("Unknown primitive type: " .. tostring(object.primitiveType)) + end end return id @@ -36,16 +50,25 @@ end ---@param points Array ---@param fillColor table ---@param lineColor table -local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineStyle) +---@param lineStyle LineType +---@param lineThickness number +---@param logger Logger? +local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineStyle, lineThickness, logger) + + if lineThickness == nil or lineThickness <= 0 then + lineStyle = 0 + end local functionString = "trigger.action.markupToAll(" .. shapeID .. ", -1, " .. drawID .. "," - for _, point in ipairs(points) do + + for _, point in pairs(points) do functionString = functionString .. " { x=" .. point.x .. ", y=0,z=" .. point.z .. "}," end - functionString = functionString .. - "{ " .. lineColor[1] .. "," .. lineColor[2] .. "," .. lineColor[3] .. "," .. lineColor[4] .. "}, " .. - "{ " .. fillColor[1] .. "," .. fillColor[2] .. "," .. fillColor[3] .. "," .. fillColor[4] .. "}, " .. - lineStyle .. ")" + functionString = functionString .. "{0,1,0,1}, {0,1,0,1}, " .. lineStyle .. ")" + + if logger then + logger:debug("Drawing complex drawing with ID " .. tostring(drawID) .. " and function string: " .. functionString) + end ---@diagnostic disable-next-line: deprecated local f, err = loadstring(functionString) @@ -55,12 +78,21 @@ local function MarkupToAll(shapeID, drawID, points, fillColor, lineColor, lineS env.error("Something failed when drawing complex drawing" .. err) end + if logger then + logger:debug("Drawing with fill color: " .. table.concat(fillColor, ",") .. " and line color: " .. table.concat(lineColor, ",") .. " and line style: " .. tostring(lineStyle) .. " and line thickness: " .. tostring(lineThickness)) + end + + trigger.action.setMarkupColorFill(drawID, fillColor) + trigger.action.setMarkupColor(drawID, lineColor) + trigger.action.setMarkupTypeLine(drawID, lineStyle) + end ---@private ---@param object Polygon ---@param id integer -function DrawingHelper.DrawPolygon(object, id) +---@param logger Logger? +function DrawingHelper.DrawPolygon(object, id, logger) if object == nil then return end @@ -75,11 +107,12 @@ function DrawingHelper.DrawPolygon(object, id) end ---@param oval Oval - local function DrawOval(oval) + ---@param logger Logger? + local function DrawOval(oval, logger) ---@type Array local points = {} local pointsNo = 30 - local angleStep = (2 * math.pi) / points + local angleStep = (2 * math.pi) / pointsNo local fillColor = DrawingHelper.ColorToColorTable(oval.fillColorString) local color = DrawingHelper.ColorToColorTable(oval.colorString) @@ -91,20 +124,37 @@ function DrawingHelper.DrawPolygon(object, id) local y = oval.mapY + (oval.r2 * math.sin(angle)) table.insert(points, { x = x, y = 0, z = y } ) end - MarkupToAll(7, id, points, fillColor, color, lineStyle) + MarkupToAll(7, id, points, fillColor, color, lineStyle, oval.thickness, logger) end ---@param free Free - local function DrawFree(free) + ---@param logger Logger? + local function DrawFree(free, logger) local fillColor = DrawingHelper.ColorToColorTable(free.fillColorString) local color = DrawingHelper.ColorToColorTable(free.colorString) local lineStyle = DrawingHelper.ToLineStyleInteger(free.style) - local points = {} - for _, point in ipairs(free.points) do - table.insert(points, { x = point.x, y = 0, z = point.y } ) + local keys = {} + for k, _ in pairs(free.points) do + table.insert(keys, k) end - MarkupToAll(7, id, points, fillColor, color, lineStyle) + + table.sort(keys, function(a, b) return a < b end) + + local points = {} + for _, k in ipairs(keys) do + local point = free.points[k] + + local newPoint = { x = free.mapX + point.x, y = 0, z = free.mapY + point.y } + local firstPoint = points[1] + + if firstPoint == nil or newPoint.x ~= firstPoint.x or newPoint.z ~= firstPoint.z then + logger:debug("Drawing free polygon point " .. tostring(k) .. ": " .. tostring(free.points[k].x) .. ", " .. tostring(free.points[k].y)) + table.insert(points, newPoint) + end + end + + MarkupToAll(7, id, points, fillColor, color, lineStyle, free.thickness, logger) end ---@param rect Rect @@ -116,38 +166,49 @@ function DrawingHelper.DrawPolygon(object, id) local pointA = { x = rect.mapX, y = 0, z = rect.mapY } local pointB = { x = rect.mapX + rect.width, y = 0, z = rect.mapY + rect.height } trigger.action.rectToAll(-1, id, pointA, pointB, color, fillColor, lineStyle, true) + end ---@param arrow Arrow - local function DrawArrow(arrow) + ---@param logger Logger? + local function DrawArrow(arrow, logger) local fillColor = DrawingHelper.ColorToColorTable(arrow.fillColorString) local color = DrawingHelper.ColorToColorTable(arrow.colorString) local lineStyle = DrawingHelper.ToLineStyleInteger(arrow.style) - local startPoint = { x = arrow.mapX, y = 0, z = arrow.mapY } + if logger then + logger:debug("Drawing arrow with start point: " .. tostring(arrow.mapX) .. ", " .. tostring(arrow.mapY) .. " and angle: " .. tostring(arrow.angle) .. " and length: " .. tostring(arrow.length)) + end + + local endPoint = { x = arrow.mapX, y = 0, z = arrow.mapY } local rad = math.rad(arrow.angle or 0) local length = arrow.length or 100 - local endPoint = { x = arrow.mapX + length * math.cos(rad), y = 0, z = arrow.mapY + length * math.sin(rad) } + local startPoint = { x = arrow.mapX - length * math.sin(rad), y = 0, z = arrow.mapY + length * math.cos(rad) } trigger.action.arrowToAll(-1, id, startPoint, endPoint, color, fillColor, lineStyle, true) end + if logger then + logger:debug("Drawing polygon with ID " .. tostring(id) .. " and polygon mode: " .. tostring(object.polygonMode)) + end + if object.polygonMode == "circle" then DrawCircle(object--[[@as Circle]]) elseif object.polygonMode == "oval" then - DrawOval(object--[[@as Oval]]) + DrawOval(object--[[@as Oval]], logger) elseif object.polygonMode == "free" then - DrawFree(object--[[@as Free]]) + DrawFree(object--[[@as Free]], logger) elseif object.polygonMode == "rect" then DrawRect(object--[[@as Rect]]) elseif object.polygonMode == "arrow" then - DrawArrow(object--[[@as Arrow]]) + DrawArrow(object--[[@as Arrow]], logger) end end ---@private ---@param object Line ---@param id integer -function DrawingHelper.DrawLine(object, id) +---@param logger Logger? +function DrawingHelper.DrawLine(object, id, logger) ---@type Array local points = {} @@ -158,7 +219,7 @@ function DrawingHelper.DrawLine(object, id) local color = DrawingHelper.ColorToColorTable(object.colorString) local lineStyle = DrawingHelper.ToLineStyleInteger(object.style) - MarkupToAll(1, id, points, color, color, lineStyle) + MarkupToAll(1, id, points, color, color, lineStyle, object.thickness, logger) end ---@private @@ -177,16 +238,14 @@ function DrawingHelper.Remove(id) trigger.action.removeMark(id) end - - ---@param hexStr string ---@return table function DrawingHelper.ColorToColorTable(hexStr) hexStr = hexStr:gsub("0x", "") - local a = tonumber(hexStr:sub(1, 2), 16) / 255 - local r = tonumber(hexStr:sub(3, 4), 16) / 255 - local g = tonumber(hexStr:sub(5, 6), 16) / 255 - local b = tonumber(hexStr:sub(7, 8), 16) / 255 + local r = tonumber(hexStr:sub(1, 2), 16) / 255 + local g = tonumber(hexStr:sub(3, 4), 16) / 255 + local b = tonumber(hexStr:sub(5, 6), 16) / 255 + local a = tonumber(hexStr:sub(7, 8), 16) / 255 return { r, g , b , a } end diff --git a/src/classes/util/Util.lua b/src/classes/util/Util.lua index 41dc0ff..f825790 100644 --- a/src/classes/util/Util.lua +++ b/src/classes/util/Util.lua @@ -297,6 +297,49 @@ do -- INIT UTIL return false end + ---@param points Array + ---@return Array + function UTIL.getConvexHull3d(points) + if #points == 0 then + return {} + end + + ---comment + ---@param a Vec3 + ---@param b Vec3 + ---@param c Vec3 + ---@return boolean + local function ccw(a, b, c) + return (b.z - a.z) * (c.x - a.x) > (b.x - a.x) * (c.z - a.z) + end + + table.sort(points, function(left, right) + return left.z < right.z + end) + + local hull = {} + -- lower hull + for _, point in pairs(points) do + while #hull >= 2 and not ccw(hull[#hull - 1], hull[#hull], point) do + table.remove(hull, #hull) + end + table.insert(hull, point) + end + + -- upper hull + local t = #hull + 1 + for i = #points, 1, -1 do + local point = points[i] + while #hull >= t and not ccw(hull[#hull - 1], hull[#hull], point) do + table.remove(hull, #hull) + end + table.insert(hull, point) + end + table.remove(hull, #hull) + return hull + end + + ---comment ---@param points Array points ---@return Array hullPoints