From 1fff02cdd2f7d3229ee7eb24643341e27e64fa29 Mon Sep 17 00:00:00 2001 From: Valmo Trindade Date: Sun, 24 May 2026 16:07:04 -0300 Subject: [PATCH] Added Draw Route function --- addons/main/CfgFunctions.hpp | 4 +- addons/main/functions/api/fn_draw_route.sqf | 161 ++++++++++++++++++++ 2 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 addons/main/functions/api/fn_draw_route.sqf diff --git a/addons/main/CfgFunctions.hpp b/addons/main/CfgFunctions.hpp index c683890..d96bb40 100644 --- a/addons/main/CfgFunctions.hpp +++ b/addons/main/CfgFunctions.hpp @@ -40,6 +40,9 @@ class CfgFunctions { class draw_polyline { file = "\armatak\armatak\addons\main\functions\api\fn_draw_polyline.sqf"; }; + class draw_route { + file = "\armatak\armatak\addons\main\functions\api\fn_draw_route.sqf"; + }; class draw_tactical_graphic { file = "\armatak\armatak\addons\main\functions\api\fn_draw_tactical_graphic.sqf"; }; @@ -170,4 +173,3 @@ class CfgFunctions { }; }; }; - diff --git a/addons/main/functions/api/fn_draw_route.sqf b/addons/main/functions/api/fn_draw_route.sqf new file mode 100644 index 0000000..87b1b7a --- /dev/null +++ b/addons/main/functions/api/fn_draw_route.sqf @@ -0,0 +1,161 @@ +// function name: armatak_fnc_draw_route +// function author: Valmo +// function description: Sends an ATAK navigable route CoT. +// +// Arguments: +// 0: Positions or objects +// 1: Callsign/title (default: "ArmaTAK Route") +// 2: Stale time in seconds (default: 86400) +// 3: Color as signed ARGB int (default: -1) +// 4: Stroke weight (default: 3) +// 5: Navigation method (default: "Driving") +// 6: Route type (default: "Primary") +// 7: Direction/planning method (default: "Infil") +// 8: Checkpoint interval among route points (default: 5) +// 9: Optional registration scope (default: "") +// +// Example: +// [[pos player, screenToWorld [0.5, 0.5]], "Patrol Route"] call armatak_fnc_draw_route; +// +// Public: Yes + +params [ + ["_points", [], [[]]], + ["_callsign", "ArmaTAK Route", [""]], + ["_staleSeconds", 86400, [0]], + ["_color", -1, [0]], + ["_strokeWeight", 3, [0]], + ["_method", "Driving", [""]], + ["_routeType", "Primary", [""]], + ["_direction", "Infil", [""]], + ["_checkpointInterval", 5, [0]], + ["_scope", "", [""]] +]; + +if ((count _points) < 2) exitWith {""}; + +private _pointStrings = []; + +{ + private _position = if (_x isEqualType objNull) then { + getPos _x + } else { + _x + }; + + if ((count _position) >= 2) then { + private _altitude = _position param [2, 0, [0]]; + private _realLocation = [_position select 0, _position select 1, _altitude] call armatak_client_fnc_convertClientLocation; + _pointStrings pushBack format ["%1,%2,%3", _realLocation select 0, _realLocation select 1, _realLocation select 2]; + }; +} forEach _points; + +if ((count _pointStrings) < 2) exitWith {""}; + +private _uuid = "armatak" callExtension ["uuid", []] select 0; +private _pad = { + params ["_value", "_digits"]; + + private _text = str (floor _value); + while {(count _text) < _digits} do { + _text = "0" + _text; + }; + + _text +}; + +private _nowDate = systemTimeUTC; +private _year = _nowDate param [0, 1970, [0]]; +private _month = _nowDate param [1, 1, [0]]; +private _day = _nowDate param [2, 1, [0]]; +private _hour = _nowDate param [3, 0, [0]]; +private _minute = _nowDate param [4, 0, [0]]; +private _second = _nowDate param [5, 0, [0]]; +private _millisecond = _nowDate param [6, 0, [0]]; +private _now = format [ + "%1-%2-%3T%4:%5:%6.%7Z", + _year, + [_month, 2] call _pad, + [_day, 2] call _pad, + [_hour, 2] call _pad, + [_minute, 2] call _pad, + [_second, 2] call _pad, + [_millisecond, 3] call _pad +]; +private _stale = "9999-12-31T23:59:59.999Z"; + +private _links = ""; +private _routeWaypoints = []; +private _lastIndex = (count _pointStrings) - 1; +private _checkpointNumber = 1; +private _checkpointIntervalSafe = _checkpointInterval max 1; + +{ + private _linkUuid = "armatak" callExtension ["uuid", []] select 0; + private _pointCallsign = ""; + if (_forEachIndex isEqualTo 0) then { + _pointCallsign = format ["%1 SP", _callsign]; + } else { + if (_forEachIndex isEqualTo _lastIndex) then { + _pointCallsign = "VDO"; + } else { + if ((_forEachIndex mod _checkpointIntervalSafe) isEqualTo 0) then { + _pointCallsign = format ["CP%1", _checkpointNumber]; + _checkpointNumber = _checkpointNumber + 1; + }; + }; + }; + + private _linkType = ["b-m-p-w", "b-m-p-c"] select (_pointCallsign isEqualTo ""); + _links = _links + format [ + "", + _linkUuid, + _pointCallsign, + _linkType, + _x + ]; + + if (_pointCallsign isNotEqualTo "") then { + _routeWaypoints pushBack [_linkUuid, _pointCallsign, _x]; + }; +} forEach _pointStrings; + +private _xml = format [ + "%4<__routeinfo><__navcues/>1", + _uuid, + _now, + _stale, + _links, + _direction, + _color, + _method, + _strokeWeight max 1, + _routeType, + _callsign +]; + +"armatak" callExtension ["log", [["info", format ["Sending ATAK route '%1' with %2 points (%3 bytes)", _callsign, count _pointStrings, count _xml]]]]; +"armatak" callExtension ["tcp_socket:send_payload", [_xml]]; +[_scope, _uuid, "b-m-r", 0, 0, 9999999] call armatak_fnc_register_cot; + +{ + _x params ["_waypointUid", "_waypointCallsign", "_pointString"]; + private _pointParts = _pointString splitString ","; + if ((count _pointParts) >= 3) then { + private _waypointXml = format [ + "", + _waypointUid, + _now, + _stale, + _pointParts select 0, + _pointParts select 1, + _pointParts select 2, + _waypointCallsign, + _uuid + ]; + "armatak" callExtension ["tcp_socket:send_payload", [_waypointXml]]; + [_scope, _waypointUid, "b-m-p-w", parseNumber (_pointParts select 0), parseNumber (_pointParts select 1), parseNumber (_pointParts select 2)] call armatak_fnc_register_cot; + }; +} forEach _routeWaypoints; + +_uuid