ACE3/addons/maptools/functions/fnc_handlePlottingBoardMarkers.sqf

220 lines
7.4 KiB
Plaintext
Raw Normal View History

#include "..\script_component.hpp"
/*
* Author: LorenLuke, johnb43
* Handle map marker creation.
* If a marker is (partially) on the plotting board, the parts on the plotting board are attached to the plotting board
* and move with the board accordingly.
*
* Arguments:
* 0: Arguments <ARRAY>
* - 0: Marker name <STRING>
* - 1: Chat channel number <NUMBER>
* - 2: Marker owner <OBJECT>
* - 3: Local origin <BOOL>
* 1: Deleted <BOOL>
*
* Return Value:
* None
*
* Example:
* [CONTROL, [0, 5]] call ace_maptools_fnc_handlePlottingBoardMarkers
*
* Public: No
*/
params ["_args", "_deleted"];
_args params ["_marker", "_channelNumber", "_owner", "_local"];
if (_deleted) exitWith {
GVAR(plottingBoard_markers) deleteAt _marker;
};
// Do not process non-local or already processed markers, don't check if the plotting board isn't shown
if (!_local || {GVAR(plottingBoard_Shown) == 0} || {QUOTE(ADDON) in _marker}) exitWith {};
// Check if the channel the marker was made in can be marked on the plotting board
private _continue = true;
if (isMultiplayer) then {
switch (GVAR(plottingBoardAllowChannelDrawing)) do {
case 0: {
if (_channelNumber != 5) then {_continue = false};
};
case 1: {
if !(_channelNumber in [3, 5]) then {_continue = false};
};
};
};
if (!_continue) exitWith {};
private _boardPos = GVAR(plottingBoard_pos);
private _boardAng = GVAR(plottingBoard_acrylicAngle);
private _markerPolyline = markerPolyline _marker;
private _count = count _markerPolyline;
// If the marker is not a polyline marker
if (_count == 0) exitWith {
private _diffPos = (getMarkerPos _marker) vectorDiff _boardPos;
// If the marker is on the acrylic or ruler of the plotting board, save it
if (vectorMagnitude _diffPos < PLOTTINGBOARD_DRAWRANGE) then {
private _relPos = [[0, 0], _diffPos, _boardAng] call CBA_fnc_vectRotate2D;
GVAR(plottingBoard_markers) set [_marker, [_relPos, [], _boardAng, +_boardPos, 1]];
};
};
// If the marker is a polyline marker, but doesn't have enough components (happens when you ctrl-left click on the map), ignore
if (_count <= 4) exitWith {};
// Polyine markers (lines)
private _startPos = [];
private _endPos = [];
private _dir = [];
private _diffPos = [];
private _a = 0;
private _b = 0;
private _c = 0;
private _t1 = nil;
private _t2 = nil;
private _delta = 0;
private _intersectionValid1 = false;
private _intersectionValid2 = false;
private _intersectPoint1 = [];
private _intersectPoint2 = [];
private _intersectClose = [];
private _intersectFar = [];
private _polylineIndex = 0;
private _markerArray = [[]];
private _insideArray = [];
for "_i" from 0 to _count - 1 - 2 step 2 do {
_startPos = [_markerPolyline select _i, _markerPolyline select (_i + 1)];
_endPos = [_markerPolyline select (_i + 2), _markerPolyline select (_i + 3)];
_dir = _endPos vectorDiff _startPos;
_diffPos = _startPos vectorDiff _boardPos;
// Circle-line intersection: Check for intersections between plotting board and current piece of polyline
// https://stackoverflow.com/a/1084899
_a = _dir vectorDotProduct _dir;
_b = 2 * (_diffPos vectorDotProduct _dir);
_c = (_diffPos vectorDotProduct _diffPos) - PLOTTINGBOARD_DRAWRANGE^2;
_delta = _b^2 - 4 * _a * _c;
// Stretch factors
_t1 = nil;
_t2 = nil;
if (_delta > 0) then {
_t1 = (-_b + sqrt _delta) / (2 * _a);
_t2 = (-_b - sqrt _delta) / (2 * _a);
// Don't look for intersection points beyond the start or end points
if (_t1 < 0 || _t1 > 1) then {
_t1 = nil;
};
if (_t2 < 0 || _t2 > 1) then {
_t2 = nil;
};
};
// The current point is always part of a polyline
(_markerArray param [_polylineIndex, []]) append _startPos;
_insideArray set [_polylineIndex, vectorMagnitude _diffPos < PLOTTINGBOARD_DRAWRANGE]; // keep track if point is within plotting board
_intersectionValid1 = !isNil "_t1";
_intersectionValid2 = !isNil "_t2";
// If no valid intersection points, continue
if (!_intersectionValid1 && {!_intersectionValid2}) then {
continue;
};
// Extremely rare case if the marker is tangential to the plotting board: Ignore
if (_intersectionValid1 && {_intersectionValid2} && {_t1 == _t2}) then {
continue;
};
if (_intersectionValid1) then {
_intersectPoint1 = _startPos vectorAdd (_dir vectorMultiply _t1);
};
if (_intersectionValid2) then {
_intersectPoint2 = _startPos vectorAdd (_dir vectorMultiply _t2);
};
// When a marker crosses the plotting board entirely (one straight line through the plotting board)
if (_intersectionValid1 && {_intersectionValid2}) then {
// Take the closer point first
_intersectClose = [_intersectPoint1, _intersectPoint2] select (_t1 > _t2);
// Finish previous polyline with the last point being the intersection
(_markerArray select _polylineIndex) append _intersectClose;
// Create a new polyline, with the first point being the closest intersection
_polylineIndex = _polylineIndex + 1;
_markerArray set [_polylineIndex, _intersectClose];
// Now take the point further away
_intersectFar = [_intersectPoint1, _intersectPoint2] select (_t1 < _t2);
// Make a polyline between the intersection points
(_markerArray select _polylineIndex) append _intersectClose;
(_markerArray select _polylineIndex) append _intersectFar;
_insideArray set [_polylineIndex, true]; // with 2 intersections, this part of the polyline must be inside
// Create a new polyline, with the first point being the furthest intersection
_polylineIndex = _polylineIndex + 1;
_markerArray set [_polylineIndex, _intersectFar];
} else {
// Only 1 intersection (either point 1 or 2, exclusive or)
if (_intersectionValid2) then {
_intersectPoint1 = _intersectPoint2;
};
// Finish previous polyline with the last point being the intersection
(_markerArray select _polylineIndex) append _intersectPoint1;
// Create a new polyline, with the first point being the intersection
_polylineIndex = _polylineIndex + 1;
_markerArray set [_polylineIndex, _intersectPoint1];
};
};
// If there were no polyline intersections and the marker was not on the plotting board, don't create new markers
if (_insideArray isEqualTo [false]) exitWith {};
private _color = getMarkerColor _marker;
private _name = "";
private _polylineRelative = [];
private _relPos = [];
{
_name = format ["%1-%2-%3", _marker, _forEachIndex, QUOTE(ADDON)]; // adding an identifier allow to check if marker was already processed
createMarkerLocal [_name, [0, 0], _channelNumber, _owner];
_name setMarkerColorLocal _color;
_name setMarkerPolyline _x; // global marker broadcast
// If the marker was on the plotting board, take it's unrotated position and store it
if (_insideArray select _forEachIndex) then {
_polylineRelative = [];
for "_i" from 0 to count _x - 1 step 2 do {
_relPos = [[0, 0], [_x select _i, _x select (_i + 1)] vectorDiff _boardPos, _boardAng] call CBA_fnc_vectRotate2D;
_polylineRelative append _relPos;
};
GVAR(plottingBoard_markers) set [_name, [[0, 0], +_polylineRelative, _boardAng, +_boardPos, 1]];
};
} forEach _markerArray;
// Delete original marker
deleteMarker _marker;