diff --git a/addons/plotting/$PBOPREFIX$ b/addons/plotting/$PBOPREFIX$ new file mode 100644 index 000000000..d5de66d5e --- /dev/null +++ b/addons/plotting/$PBOPREFIX$ @@ -0,0 +1 @@ +x\zen\addons\plotting diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp new file mode 100644 index 000000000..5ee677e95 --- /dev/null +++ b/addons/plotting/CfgContext.hpp @@ -0,0 +1,39 @@ +class EGVAR(context_menu,actions) { + class Plots { + displayName = CSTRING(DisplayName); + icon = QPATHTOF(ui\ruler.paa); + statement = QUOTE(call FUNC(selectPosition)); + args = "LINE"; + priority = 15; + + class MeasureDistance { + displayName = CSTRING(MeasureDistance); + icon = QPATHTOF(ui\ruler.paa); + statement = QUOTE(call FUNC(selectPosition)); + args = "LINE"; + }; + class MeasureDistanceFromCamera { + displayName = CSTRING(MeasureDistanceFromCamera); + icon = QPATHTOF(ui\ruler.paa); + statement = QUOTE([ARR_2(_args,curatorCamera)] call FUNC(setActivePlot)); + args = "LINE"; + }; + class MeasureRadius { + displayName = CSTRING(MeasureRadius); + icon = QPATHTOF(ui\radius.paa); + statement = QUOTE(call FUNC(selectPosition)); + args = "RADIUS"; + }; + class MeasureOffset { + displayName = CSTRING(MeasureOffset); + icon = QPATHTOF(ui\cuboid.paa); + statement = QUOTE(call FUNC(selectPosition)); + args = "RECTANGLE"; + }; + class ClearPlots { + displayName = CSTRING(ClearPlots); + icon = "A3\3den\Data\Displays\Display3DEN\PanelLeft\entityList_delete_ca.paa"; + statement = QUOTE([QQGVAR(plotsCleared)] call CBA_fnc_localEvent); + }; + }; +}; diff --git a/addons/plotting/CfgEventHandlers.hpp b/addons/plotting/CfgEventHandlers.hpp new file mode 100644 index 000000000..f6503c247 --- /dev/null +++ b/addons/plotting/CfgEventHandlers.hpp @@ -0,0 +1,17 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; + +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; + +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp new file mode 100644 index 000000000..085dd3556 --- /dev/null +++ b/addons/plotting/CfgFormatters.hpp @@ -0,0 +1,44 @@ +class GVAR(formatters) { + // Formats distance: _this is distance in meters + class Distance { + class Meter { + formatter = QUOTE(FORMAT_1('%1 m',[ARR_3(_this,1,parseNumber (abs _this < 100))] call CBA_fnc_formatNumber)); + priority = 100; + }; + class Feet { + formatter = QUOTE(FORMAT_1('%1 ft',[ARR_3(_this * 3.281,1,0)] call CBA_fnc_formatNumber)); + priority = 90; + }; + }; + + // Formats custom speeds: _this is [distance (m), speed (m/s)] + class Speed { + class kmh { + formatter = QUOTE(call FUNC(formatSpeedKmh)); + priority = 100; + + // Defines static speeds for this format to cycle through when plot is not attached to object + // Must have at least one entry + speeds[] = { + 3.333, // Unit with weapon lowered running in fast combat pace: takes ~12 km/h -> 3.333 m/s. This is with ACE mod loaded. Without ACE, the unit is a bit faster. + 8.333, // Vehicle at 30 km/h + 13.889, // Vehicle at 50 km/h + 27.778, // Vehicle at 100 km/h + 69.444, // Helicopter at 250 km/h + 166.667, // Jet at 600 km/h + }; + }; + }; + + // Formats direction: _this is direction in the range 0..360 degrees. + class Azimuth { + class Degree { + formatter = QUOTE(FORMAT_1('%1°',_this toFixed 0)); + priority = 100; + }; + class NATOMil { + formatter = QUOTE(FORMAT_1('%1 mil',(_this * 17.7778) toFixed 0)); + priority = 90; + }; + }; +}; diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp new file mode 100644 index 000000000..6d70988a2 --- /dev/null +++ b/addons/plotting/XEH_PREP.hpp @@ -0,0 +1,19 @@ +PREP(addPlot); +PREP(clearPlots); +PREP(compileFormatters); +PREP(drawLine); +PREP(drawPlots); +PREP(drawRadius); +PREP(drawRectangle); +PREP(formatSpeedKmh); +PREP(formatTravelTime); +PREP(isPosInCylinder); +PREP(isValidPlot); +PREP(onDraw); +PREP(onDraw3D); +PREP(onKeyDown); +PREP(onLoad); +PREP(onMouseButtonDown); +PREP(onUnload); +PREP(selectPosition); +PREP(setActivePlot); diff --git a/addons/plotting/XEH_postInit.sqf b/addons/plotting/XEH_postInit.sqf new file mode 100644 index 000000000..10087eff4 --- /dev/null +++ b/addons/plotting/XEH_postInit.sqf @@ -0,0 +1,9 @@ +#include "script_component.hpp" + +if (hasInterface) then { + ["zen_curatorDisplayLoaded", LINKFUNC(onLoad)] call CBA_fnc_addEventHandler; + ["zen_curatorDisplayUnloaded", LINKFUNC(onUnload)] call CBA_fnc_addEventHandler; + + [QGVAR(plotAdded), LINKFUNC(addPlot)] call CBA_fnc_addEventHandler; + [QGVAR(plotsCleared), LINKFUNC(clearPlots)] call CBA_fnc_addEventHandler; +}; diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf new file mode 100644 index 000000000..64fd9570b --- /dev/null +++ b/addons/plotting/XEH_preInit.sqf @@ -0,0 +1,38 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +if (hasInterface) then { + GVAR(draw3DAdded) = false; + + GVAR(plotTypes) = createHashMapFromArray [ + ["LINE", LINKFUNC(drawLine)], + ["RADIUS", LINKFUNC(drawRadius)], + ["RECTANGLE", LINKFUNC(drawRectangle)] + ]; + + GVAR(currentDistanceFormatter) = 0; + GVAR(currentSpeedFormatter) = 0; + GVAR(currentSpeedIndex) = 0; + GVAR(currentAzimuthFormatter) = 0; + + // Unique ID for creating plots + GVAR(nextID) = 0; + // Currently active plot shown at cursor in format [type, start] + GVAR(activePlot) = []; + // All plots, keys are IDs, values [type, start, end] + GVAR(plots) = createHashMap; + // All plot IDs ordered by creation time, last is newest + GVAR(history) = []; +}; + +call FUNC(compileFormatters); + +#include "initSettings.inc.sqf" +#include "initKeybinds.inc.sqf" + +ADDON = true; diff --git a/addons/plotting/XEH_preStart.sqf b/addons/plotting/XEH_preStart.sqf new file mode 100644 index 000000000..022888575 --- /dev/null +++ b/addons/plotting/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/plotting/config.cpp b/addons/plotting/config.cpp new file mode 100644 index 000000000..991023a13 --- /dev/null +++ b/addons/plotting/config.cpp @@ -0,0 +1,21 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"zen_common", "zen_context_menu"}; + author = ECSTRING(main,Author); + authors[] = {"Timi007"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +PRELOAD_ADDONS; + +#include "CfgEventHandlers.hpp" +#include "CfgContext.hpp" +#include "CfgFormatters.hpp" diff --git a/addons/plotting/functions/fnc_addPlot.sqf b/addons/plotting/functions/fnc_addPlot.sqf new file mode 100644 index 000000000..e13344556 --- /dev/null +++ b/addons/plotting/functions/fnc_addPlot.sqf @@ -0,0 +1,40 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Adds a new plot to be drawn. + * + * Arguments: + * 0: Type of plot + * 1: Start position ASL or attached object + * 2: End position ASL or attached object + * + * Return Value: + * ID of new plot or -1 on error + * + * Example: + * ["RADIUS", player, [100, 100, 10]] call zen_plotting_fnc_addPlot + * + * Public: No + */ + +params [ + ["_type", "LINE", [""]], + ["_startPos", [0, 0, 0], [[], objNull], [3]], + ["_endPos", [0, 0, 0], [[], objNull], [3]] +]; + +if (!(_type in GVAR(plotTypes)) || {_startPos isEqualTo objNull} || {_endPos isEqualTo objNull}) exitWith {-1}; + +private _id = GVAR(nextID); +GVAR(plots) set [_id, [_type, _startPos, _endPos]]; +GVAR(history) pushBack _id; + +GVAR(nextID) = GVAR(nextID) + 1; + +if (_endPos isEqualType objNull) then { + _endPos setVariable [QGVAR(attachedPlot), _id]; +}; + +TRACE_4("Plot added",_id,_type,_startPos,_endPos); + +_id diff --git a/addons/plotting/functions/fnc_clearPlots.sqf b/addons/plotting/functions/fnc_clearPlots.sqf new file mode 100644 index 000000000..254590e1f --- /dev/null +++ b/addons/plotting/functions/fnc_clearPlots.sqf @@ -0,0 +1,19 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Clears of plots. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call zen_plotting_fnc_clearPlots + * + * Public: No + */ + +GVAR(plots) = createHashMap; +GVAR(history) = []; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf new file mode 100644 index 000000000..11f94a1c4 --- /dev/null +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -0,0 +1,50 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Compiles formatters from config and saves them in missionNamespace. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call zen_plotting_fnc_compileFormatters + * + * Public: No + */ + +private _fnc_getFormatters = { + params ["_cfgFormatters"]; + + private _formatters = []; + { + private _entryConfig = _x; + + private _formatterName = configName _entryConfig; + private _formatter = compile getText (_entryConfig >> "formatter"); + private _priority = getNumber (_entryConfig >> "priority"); + private _speeds = if (isArray (_entryConfig >> "speeds")) then {getArray (_entryConfig >> "speeds")} else {[]}; + + private _formatterEntry = [ + _formatterName, + _formatter, + _priority, + _speeds + ]; + + _formatters pushBack _formatterEntry; + } forEach configProperties [_cfgFormatters, "isClass _x", true]; + + [_formatters, 2, false] call CBA_fnc_sortNestedArray +}; + +private _cfgFormatters = configFile >> QGVAR(formatters); +private _distanceFormatters = [_cfgFormatters >> "Distance"] call _fnc_getFormatters; +private _speedFormatters = [_cfgFormatters >> "Speed"] call _fnc_getFormatters; +private _azimuthFormatters = [_cfgFormatters >> "Azimuth"] call _fnc_getFormatters; + +missionNamespace setVariable [QGVAR(distanceFormatters), _distanceFormatters]; +missionNamespace setVariable [QGVAR(speedFormatters), _speedFormatters]; +missionNamespace setVariable [QGVAR(azimuthFormatters), _azimuthFormatters]; diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf new file mode 100644 index 000000000..eaa0ad142 --- /dev/null +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -0,0 +1,85 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a line plot in 3D or on the map. Must be called every frame. + * + * Arguments: + * 0: Start position ASL or attached object + * 1: End position ASL or attached object + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 1: Travel time formatter + * 2: Azimuth formatter + * 4: Additional arguments (default: []) + * 5: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {[_this, 3.333] call FUNC(formatTravelTime)}, {_this toFixed 1}]] call zen_plotting_fnc_drawLine + * + * Public: No + */ + +params ["_startPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_args params [["_speed", 0, [0]]]; + +// Don't draw start icon and line in 3D if Zeus camera is start pos +// Also don't use speed of camera +private _startPosIsNotCamera = curatorCamera isNotEqualTo _startPosOrObj; + +private _startPos = _startPosOrObj; +if (_startPosOrObj isEqualType objNull) then { + _startPos = getPosASLVisual _startPosOrObj; + + if (_startPosIsNotCamera) then { + _speed = velocityModelSpace _startPosOrObj select 1; + }; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; + +private _distance = _startPos vectorDistance _endPos; +private _azimuth = _startPos getDir _endPos; + +private _fnc_format = { + params ["_distance", "_speed", "_azimuth", "_formatters"]; + _formatters params ["_fnc_formatDistance", "_fnc_formatTime", "_fnc_formatAzimuth"]; + + format ["%1 (%2) - %3", + _distance call _fnc_formatDistance, + [_distance, _speed] call _fnc_formatTime, + _azimuth call _fnc_formatAzimuth + ] +}; + +if (isNull _ctrlMap) then { // 3D + private _camPos = getPosASL curatorCamera; + + if (_startPosIsNotCamera && {CAN_RENDER_ICON(_camPos,_startPos)}) then { + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; + }; + + if (_startPosIsNotCamera && {CAN_RENDER_LINE(_camPos,_startPos,_endPos)}) then { + drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; + }; + + if (CAN_RENDER_ICON(_camPos,_endPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_distance, _speed, _azimuth, _formatters] call _fnc_format]; + }; +} else { // Map + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; + _ctrlMap drawLine [_startPos, _endPos, _color]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_distance, _speed, _azimuth, _formatters] call _fnc_format]; +}; diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf new file mode 100644 index 000000000..76fb99b04 --- /dev/null +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -0,0 +1,66 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws all placed plots and currently active one in 3D or on the map. Must be called every frame. + * + * Arguments: + * 0: Permanent plots in format [type, startPosASLOrObj, endPosASLOrObj] + * 1: Currently active plot in format [type, startPosASLOrObj] + * 2: Zeus map (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[["LINE", [0, 0, 0], player]], ["LINE", [100, 100, 0]]] call zen_plotting_fnc_drawPlots + * + * Public: No + */ + +params ["_plots", ["_activePlot", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; + +private _drawIn3D = isNull _ctrlMap; + +(GVAR(speedFormatters) select GVAR(currentSpeedFormatter)) params ["", "_fnc_formatTime", "", "_speeds"]; + +private _formatters = [ + (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 1, + _fnc_formatTime, + (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 1 +]; + +private _args = [ + _speeds select GVAR(currentSpeedIndex) +]; + +// Format active plot as permanent plot with mouse position as end position +private _activePlotWithMouseEndPos = []; +if (_activePlot isNotEqualTo []) then { + _activePlot params ["_type", "_startPosOrObj"]; + + private _endPos = if (_drawIn3D) then { + [EGVAR(common,mousePos), 2] call EFUNC(common,getPosFromScreen) + } else { + private _pos2D = _ctrlMap ctrlMapScreenToWorld getMousePosition; + _pos2D set [2, getTerrainHeightASL _pos2D]; + _pos2D + }; + + _activePlotWithMouseEndPos = [[_type, _startPosOrObj, _endPos]]; +}; + +// Draw all plots +{ + _x params ["_type", "_startPosOrObj", "_endPosOrObj"]; + + // Skip invalid plots + if (_startPosOrObj isEqualTo objNull || {_endPosOrObj isEqualTo objNull}) then {continue}; + + private _scale = [MAP_ICON_SCALE, ICON_SCALE] select _drawIn3D; + private _visualProperties = [ICON, GVAR(color), _scale, ICON_ANGLE, LINEWIDTH]; + private _drawArgs = [_startPosOrObj, _endPosOrObj, _visualProperties, _formatters, _args, _ctrlMap]; + + if (_type in GVAR(plotTypes)) then { + _drawArgs call (GVAR(plotTypes) get _type); + }; +} forEach (_activePlotWithMouseEndPos + _plots); diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf new file mode 100644 index 000000000..803d697c3 --- /dev/null +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -0,0 +1,97 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a radius circle plot in 3D or on the map. Must be called every frame. + * + * Arguments: + * 0: Circle center position ASL or attached object + * 1: End position ASL or attached object + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 1: Travel time formatter (unused) + * 2: Azimuth formatter + * 4: Additional arguments (default: []) + * 5: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {[_this, 3.333] call FUNC(formatTravelTime)}, {_this toFixed 1}]] call zen_plotting_fnc_drawRadius + * + * Public: No + */ + +params ["_centerPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_args params [["_speed", 0, [0]]]; + +private _centerPos = _centerPosOrObj; +if (_centerPosOrObj isEqualType objNull) then { + _centerPos = getPosASLVisual _centerPosOrObj; + _speed = velocityModelSpace _centerPosOrObj select 1; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; + +private _radius = _centerPos vectorDistance _endPos; +private _azimuth = _centerPos getDir _endPos; + +private _fnc_format = { + params ["_distance", "_speed", "_azimuth", "_formatters"]; + _formatters params ["_fnc_formatDistance", "_fnc_formatTime", "_fnc_formatAzimuth"]; + + format ["%1 (%2) - %3", + _distance call _fnc_formatDistance, + [_distance, _speed] call _fnc_formatTime, + _azimuth call _fnc_formatAzimuth + ] +}; + +if (isNull _ctrlMap) then { // 3D + private _camPos = getPosASL curatorCamera; + + private _centerAGL = ASLToAGL _centerPos; + private _endPosAGL = ASLToAGL _endPos; + + if (CAN_RENDER_ICON(_camPos,_centerPos)) then { + drawIcon3D [_icon, _color, _centerAGL, _scale, _scale, _angle]; + }; + + if (_camPos vectorDistance _centerPos <= _radius + MAX_RENDER_DISTANCE) then { + drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; + drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, [_radius, _speed, _azimuth, _formatters] call _fnc_format]; + }; + + private _count = CIRCLE_EDGES_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_RESOLUTION); + private _factor = 360 / _count; + private _offsets = []; + + for "_i" from 0 to (_count - 1) do { + private _phi = _i * _factor - _azimuth; + _offsets pushBack [_radius * cos _phi, _radius * sin _phi, 0]; + }; + + for "_i" from 0 to (_count - 1) do { + private _pos1 = _centerPos vectorAdd (_offsets select _i); + private _pos2 = _centerPos vectorAdd (_offsets select ((_i + 1) % _count)); + + if (CAN_RENDER_LINE(_camPos,_pos1,_pos2)) then { + drawLine3D [ASLToAGL _pos1, ASLToAGL _pos2, _color, _lineWidth]; + }; + }; +} else { // Map + _ctrlMap drawIcon [_icon, _color, _centerPos, _scale, _scale, _angle]; + _ctrlMap drawLine [_centerPos, _endPos, _color]; + _ctrlMap drawEllipse [_centerPos, _radius, _radius, 0, _color, ""]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_radius, _speed, _azimuth, _formatters] call _fnc_format]; +}; diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf new file mode 100644 index 000000000..be829fdcb --- /dev/null +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -0,0 +1,97 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a rectangle plot in 3D or on the map. Must be called every frame. + * + * Arguments: + * 0: Start position ASL or attached object + * 1: End position ASL or attached object + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 4: Additional arguments (default: []) + * 5: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}]] call zen_plotting_fnc_drawRectangle + * + * Public: No + */ + +params ["_startPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; + +private _startPos = _startPosOrObj; +if (_startPosOrObj isEqualType objNull) then { + _startPos = getPosASLVisual _startPosOrObj; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; + +private _offset = _endPos vectorDiff _startPos; +_offset params ["_a", "_b", "_c"]; + +private _fnc_format = { + params ["_offset", "_formatters"]; + _offset params ["_a", "_b", "_c"]; + _formatters params ["_fnc_formatDistance"]; + + format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_formatDistance, _b call _fnc_formatDistance, _c call _fnc_formatDistance] +}; + +if (isNull _ctrlMap) then { // 3D + private _camPos = getPosASL curatorCamera; + + if (CAN_RENDER_ICON(_camPos,_startPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; + }; + + _startPos params ["_x1", "_y1", "_z1"]; + _endPos params ["_x2", "_y2", "_z2"]; + + private _edges = if ((abs (_z2 - _z1)) > CUBOID_HEIGHT_THRESHOLD) then { + [ + [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]], // Rectangle same height as start pos + [[_x1, _y1, _z2], [_x2, _y1, _z2], [_x2, _y2, _z2], [_x1, _y2, _z2], [_x1, _y1, _z2]], // Rectangle same height as end pos + // Connections from start to end height + [[_x1, _y1, _z1], [_x1, _y1, _z2]], + [[_x2, _y1, _z1], [_x2, _y1, _z2]], + [[_x2, _y2, _z1], [_x2, _y2, _z2]], + [[_x1, _y2, _z1], [_x1, _y2, _z2]] + ] + } else { + // Don't draw cuboid if height difference is small + [[[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]]] + }; + + { + for "_i" from 0 to (count _x - 2) do { + private _pos1 = _x select _i; + private _pos2 = _x select (_i + 1); + + if (CAN_RENDER_LINE(_camPos,_pos1,_pos2)) then { + drawLine3D [ASLToAGL _pos1, ASLToAGL _pos2, _color, _lineWidth]; + }; + }; + } forEach _edges; + + if (CAN_RENDER_ICON(_camPos,_endPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_offset, _formatters] call _fnc_format]; + }; +} else { // Map + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; + private _center = (_startPos vectorAdd _endPos) vectorMultiply 0.5; + _ctrlMap drawRectangle [_center, _a / 2, _b / 2, 0, _color, ""]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_offset, _formatters] call _fnc_format]; +}; diff --git a/addons/plotting/functions/fnc_formatSpeedKmh.sqf b/addons/plotting/functions/fnc_formatSpeedKmh.sqf new file mode 100644 index 000000000..a877c1035 --- /dev/null +++ b/addons/plotting/functions/fnc_formatSpeedKmh.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Formats the given distance and speed. + * + * Arguments: + * 0: Distance traveled in meters + * 1: Absolute speed in meters per second + * + * Return Value: + * Formatted travel time + * + * Example: + * [100, 3.3] call zen_plotting_fnc_formatTravelTime + * + * Public: No + */ + +params [["_distance", 0, [0]], ["_speed", 0, [0]]]; + +private _kmh = round (abs _speed * 3.6); + +// return +if (_kmh > 0) then { + format ["%1 @ %2 km/h", [_distance, _speed] call FUNC(formatTravelTime), _kmh toFixed 0] +} else { + "∞ @ 0 km/h" +} diff --git a/addons/plotting/functions/fnc_formatTravelTime.sqf b/addons/plotting/functions/fnc_formatTravelTime.sqf new file mode 100644 index 000000000..fd80f5378 --- /dev/null +++ b/addons/plotting/functions/fnc_formatTravelTime.sqf @@ -0,0 +1,33 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Formats the travel time to an appropriate format given by distance and speed. + * + * Arguments: + * 0: Distance traveled in meters + * 1: Speed in meters per second + * + * Return Value: + * Formatted travel time in format H:MM:SS or M:SS + * + * Example: + * [100, 3.3] call zen_plotting_fnc_formatTravelTime + * + * Public: No + */ + +params ["_distance", "_speed"]; + +private _time = _distance / _speed; + +private _hours = floor (_time / 3600); +private _seconds = _time - (_hours * 3600); +private _minutes = floor (_seconds / 60); +_seconds = _seconds - (_minutes * 60); + +// return +if (_hours > 0) then { + format ["%1:%2:%3", _hours, [_minutes, 2] call CBA_fnc_formatNumber, [floor _seconds, 2] call CBA_fnc_formatNumber] +} else { + format ["%1:%2", _minutes, [floor _seconds, 2] call CBA_fnc_formatNumber] +} diff --git a/addons/plotting/functions/fnc_isPosInCylinder.sqf b/addons/plotting/functions/fnc_isPosInCylinder.sqf new file mode 100644 index 000000000..16607f42a --- /dev/null +++ b/addons/plotting/functions/fnc_isPosInCylinder.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Checks if the given point is inside the defined cylinder. + * + * Arguments: + * 0: Start position of cylinder + * 1: End position of cylinder + * 2: Radius of cylinder + * 3: Point to check + * + * Return Value: + * Position is in cylinder + * + * Example: + * [[0, 0, 0], [100, 0, 0], 50, [50, 50, 0]] call zen_plotting_fnc_isPosInCylinder + * + * Public: No + */ + +params ["_p1", "_p2", "_r", "_q"]; +// https://stackoverflow.com/a/47933302 + +private _diff = _p2 vectorDiff _p1; + +((_q vectorDiff _p1) vectorDotProduct _diff >= 0) // First test (q - p1) * (p2 - p1) >= 0 +&& {(_q vectorDiff _p2) vectorDotProduct _diff <= 0} // Second test (q - p2) * (p2 - p1) <= 0 +&& {(vectorMagnitude ((_q vectorDiff _p1) vectorCrossProduct _diff)) <= (_r * vectorMagnitude _diff)} // Third test |(q - p1) x (p2 - p1)| <= r * |p2 - p1| diff --git a/addons/plotting/functions/fnc_isValidPlot.sqf b/addons/plotting/functions/fnc_isValidPlot.sqf new file mode 100644 index 000000000..09420cb94 --- /dev/null +++ b/addons/plotting/functions/fnc_isValidPlot.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Checks if the given plot identified by an ID is still valid. + * + * Arguments: + * 0: Plot ID + * + * Return Value: + * Plot is valid + * + * Example: + * [1] call zen_plotting_fnc_isValidPlot + * + * Public: No + */ + +params ["_id"]; + +if !(_id in GVAR(plots)) exitWith {false}; + +(GVAR(plots) get _id) params ["", "_startPos", "_endPos"]; + +(_startPos isNotEqualTo objNull) +&& {_endPos isNotEqualTo objNull} diff --git a/addons/plotting/functions/fnc_onDraw.sqf b/addons/plotting/functions/fnc_onDraw.sqf new file mode 100644 index 000000000..19ab03139 --- /dev/null +++ b/addons/plotting/functions/fnc_onDraw.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Author: Timi007 + * Handles drawing the plots on the Zeus map. Is only called when map is open. + * + * Arguments: + * 0: Zeus map + * + * Return Value: + * None + * + * Example: + * [_ctrlMap] call zen_plotting_fnc_onDraw + * + * Public: No + */ + +BEGIN_COUNTER(onDraw); + +params ["_ctrlMap"]; + +if (dialog || {call EFUNC(common,isInScreenshotMode)}) exitWith {}; // Dialog is open or HUD is hidden + +[values GVAR(plots), GVAR(activePlot), _ctrlMap] call FUNC(drawPlots); + +END_COUNTER(onDraw); diff --git a/addons/plotting/functions/fnc_onDraw3D.sqf b/addons/plotting/functions/fnc_onDraw3D.sqf new file mode 100644 index 000000000..65eb8d046 --- /dev/null +++ b/addons/plotting/functions/fnc_onDraw3D.sqf @@ -0,0 +1,29 @@ +#include "script_component.hpp" +/* + * Author: Timi007 + * Handles drawing the plots in Zeus 3D. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call zen_plotting_fnc_onDraw3D + * + * Public: No + */ + +BEGIN_COUNTER(onDraw3D); + +if ( + isNull (findDisplay IDD_RSCDISPLAYCURATOR) // We are in not Zeus + || {!isNull (findDisplay IDD_INTERRUPT)} // Pause menu is opened + || {dialog} // We have a dialog open + || {call EFUNC(common,isInScreenshotMode)} // HUD is hidden +) exitWith {}; + +[values GVAR(plots), GVAR(activePlot)] call FUNC(drawPlots); + +END_COUNTER(onDraw3D); diff --git a/addons/plotting/functions/fnc_onKeyDown.sqf b/addons/plotting/functions/fnc_onKeyDown.sqf new file mode 100644 index 000000000..86117c264 --- /dev/null +++ b/addons/plotting/functions/fnc_onKeyDown.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles canceling the currently active plot. + * + * Arguments: + * 0: Display or control the EH is attached to (ignored) + * 1: Key code + * + * Return Value: + * Handled + * + * Example: + * [_display, 1] call zen_plotting_fnc_onKeyDown + * + * Public: No + */ + +params ["", "_keyCode"]; + +if (GVAR(activePlot) isEqualTo [] || {_keyCode != DIK_ESCAPE}) exitWith {false}; + +TRACE_1("Cancel adding plot",_this); +GVAR(activePlot) = []; + +true diff --git a/addons/plotting/functions/fnc_onLoad.sqf b/addons/plotting/functions/fnc_onLoad.sqf new file mode 100644 index 000000000..e041165a8 --- /dev/null +++ b/addons/plotting/functions/fnc_onLoad.sqf @@ -0,0 +1,37 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Function triggered every time the Zeus/Curator display is opened. + * Adds the draw event handlers to display static and currently active plots in 3D and on the map. + * + * Arguments: + * 0: Zeus Display + * + * Return Value: + * None + * + * Example: + * [_display] call zen_plotting_fnc_onLoad + * + * Public: No + */ + +params ["_display"]; +TRACE_1("Zeus display opened",_display); + +GVAR(activePlot) = []; + +if (!GVAR(draw3DAdded)) then { + LOG("Adding Draw3D."); + addMissionEventHandler ["Draw3D", {call FUNC(onDraw3D)}]; + GVAR(draw3DAdded) = true; +}; + +LOG("Adding input handlers."); +_display displayAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; +_display displayAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; + +// MapDraw EH needs to be added every time the Zeus display is opened. +LOG("Adding map draw."); +private _ctrlMap = _display displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; +_ctrlMap ctrlAddEventHandler ["Draw", {call FUNC(onDraw)}]; diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf new file mode 100644 index 000000000..8f392efd6 --- /dev/null +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -0,0 +1,56 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles the mouse button event when user wants to add a plot in 3D or on the map. + * + * Arguments: + * 0: Zeus display + * 1: Mouse button pressed + * + * Return Value: + * None + * + * Example: + * [_display, 1] call zen_plotting_fnc_onMouseButtonDown + * + * Public: No + */ + +params ["_display", "_button"]; + +if (_button != 0 || {GVAR(activePlot) isEqualTo []}) exitWith {}; +TRACE_1("onMouseButtonDown",_this); + +if (call EFUNC(common,isCursorOnMouseArea)) then { + curatorMouseOver params ["_mouseOverType", "_object"]; + + private _endPosOrObj = switch (true) do { + case (_mouseOverType isEqualTo "OBJECT"): {_object}; + case (visibleMap): { + private _ctrlMap = _display displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; + + private _pos2D = _ctrlMap ctrlMapScreenToWorld getMousePosition; + _pos2D set [2, getTerrainHeightASL _pos2D]; + _pos2D + }; + default {[EGVAR(common,mousePos), 2] call EFUNC(common,getPosFromScreen)}; + }; + + // Check if end point already has a valid plot attached to it + private _attachedPlot = -1; + if (_endPosOrObj isEqualType objNull) then { + _attachedPlot = _endPosOrObj getVariable [QGVAR(attachedPlot), -1]; + }; + + if (_attachedPlot isNotEqualTo -1 && {[_attachedPlot] call FUNC(isValidPlot)}) then { + [LSTRING(ErrorCannotAttachPlot)] call EFUNC(common,showMessage); + } else { + // Add current active plot to permanent ones + GVAR(activePlot) params ["_type", "_startPosOrObj"]; + + TRACE_4("Add plot",_type,_startPosOrObj,_endPosOrObj,_this); + [QGVAR(plotAdded), [_type, _startPosOrObj, _endPosOrObj]] call CBA_fnc_localEvent; + }; +}; + +GVAR(activePlot) = []; diff --git a/addons/plotting/functions/fnc_onUnload.sqf b/addons/plotting/functions/fnc_onUnload.sqf new file mode 100644 index 000000000..e49cba9eb --- /dev/null +++ b/addons/plotting/functions/fnc_onUnload.sqf @@ -0,0 +1,18 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles unloading the Zeus Display. + * + * Arguments: + * 0: Display + * + * Return Value: + * None + * + * Example: + * [DISPLAY] call zen_plotting_fnc_onUnload + * + * Public: No + */ + +GVAR(activePlot) = []; diff --git a/addons/plotting/functions/fnc_selectPosition.sqf b/addons/plotting/functions/fnc_selectPosition.sqf new file mode 100644 index 000000000..8faf08df6 --- /dev/null +++ b/addons/plotting/functions/fnc_selectPosition.sqf @@ -0,0 +1,34 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Called from context menu to select the start position of the plot. + * + * Arguments: + * 0: Context menu position ASL + * 1: Selected objects + * 2: Selected groups + * 3: Selected waypoints + * 4: Selected markers + * 5: Hovered entity + * 6: Plot type + * + * Return Value: + * None + * + * Example: + * [[100, 100, 5], [], [], [], [], objNull, "LINE"] call zen_plotting_fnc_selectPosition + * + * Public: No + */ + +params ["_position", "_objects", "_groups", "_waypoints", "_markers", "_hoveredEntity", "_type"]; + +private _posOrObj = switch (true) do { + case (_hoveredEntity isEqualType objNull && {!isNull _hoveredEntity}): {_hoveredEntity}; + case (count _objects isEqualTo 1): {_objects select 0}; + default {_position}; +}; + +TRACE_1("start pos",_posOrObj); + +[_type, _posOrObj] call FUNC(setActivePlot); diff --git a/addons/plotting/functions/fnc_setActivePlot.sqf b/addons/plotting/functions/fnc_setActivePlot.sqf new file mode 100644 index 000000000..eefb648fb --- /dev/null +++ b/addons/plotting/functions/fnc_setActivePlot.sqf @@ -0,0 +1,22 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Sets the currently active plot where the end position is attached to the cursor. + * + * Arguments: + * 0: Type of plot + * 1: Start position ASL or attached object + * + * Return Value: + * None + * + * Example: + * ["LINE", curatorCamera] call zen_plotting_fnc_setActivePlot + * + * Public: No + */ + +params [["_type", "LINE", [""]], ["_startPos", [0, 0, 0], [[], objNull], [3]]]; + +GVAR(activePlot) = [_type, _startPos]; +[QGVAR(selectingEndPos), [_type, _startPos]] call CBA_fnc_localEvent; diff --git a/addons/plotting/functions/script_component.hpp b/addons/plotting/functions/script_component.hpp new file mode 100644 index 000000000..2680773dd --- /dev/null +++ b/addons/plotting/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\x\zen\addons\plotting\script_component.hpp" diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf new file mode 100644 index 000000000..92c7bc5db --- /dev/null +++ b/addons/plotting/initKeybinds.inc.sqf @@ -0,0 +1,99 @@ +private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; + +[ + _category, + QGVAR(toggleDistanceFormat), + [LSTRING(ToggleDistanceFormat), LSTRING(ToggleDistanceFormat_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(distanceFormatters); + GVAR(currentDistanceFormatter) = (GVAR(currentDistanceFormatter) + 1) % _numFormatters; + + true + }; + }, + {}, + [DIK_R, [false, true, false]] // Default: CTRL + R +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(toggleSpeed), + [LSTRING(ToggleSpeed), LSTRING(ToggleSpeed_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numSpeeds = count (GVAR(speedFormatters) select GVAR(currentSpeedFormatter) select 3); + GVAR(currentSpeedIndex) = (GVAR(currentSpeedIndex) + 1) % _numSpeeds; + + true + }; + }, + {}, + [DIK_R, [true, false, false]] // Default: SHIFT + R +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(toggleSpeedFormat), + [LSTRING(ToggleSpeedFormat), LSTRING(ToggleSpeedFormat_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(speedFormatters); + GVAR(currentSpeedFormatter) = (GVAR(currentSpeedFormatter) + 1) % _numFormatters; + + GVAR(currentSpeedIndex) = 0; + + true + }; + }, + {}, + [DIK_R, [false, false, true]] // Default: ALT + R +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(toggleAzimuthFormat), + [LSTRING(ToggleAzimuthFormat), LSTRING(ToggleAzimuthFormat_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(azimuthFormatters); + GVAR(currentAzimuthFormatter) = (GVAR(currentAzimuthFormatter) + 1) % _numFormatters; + + true + }; + }, + {}, + [DIK_T, [false, true, false]] // Default: CTRL + T +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(deleteLastPlot), + [LSTRING(DeleteLastPlot), LSTRING(DeleteLastPlot_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + if (GVAR(history) isNotEqualTo []) then { + private _lastPlotId = (GVAR(history) deleteAt [-1]) select 0; + private _lastPlot = GVAR(plots) deleteAt _lastPlotId; + [QGVAR(plotDeleted), [_lastPlotId] + _lastPlot] call CBA_fnc_localEvent; + TRACE_2("Removed last plot",_lastPlotId,_lastPlot); + + // Automatically delete invalid plots (e.g. when attached object does not exist anymore) + while {GVAR(history) isNotEqualTo []} do { + _lastPlotId = GVAR(history) select -1; + + if !([_lastPlotId] call FUNC(isValidPlot)) then { + GVAR(history) deleteAt [-1]; + _lastPlot = GVAR(plots) deleteAt _lastPlotId; + [QGVAR(plotDeleted), [_lastPlotId] + _lastPlot] call CBA_fnc_localEvent; + TRACE_2("Automatically removed next invalid plot",_lastPlotId,_lastPlot); + }; + }; + }; + + true + }; + }, + {}, + [DIK_Y, [false, true, false]] // Default: Ctrl + Y +] call CBA_fnc_addKeybind; diff --git a/addons/plotting/initSettings.inc.sqf b/addons/plotting/initSettings.inc.sqf new file mode 100644 index 000000000..bbb620817 --- /dev/null +++ b/addons/plotting/initSettings.inc.sqf @@ -0,0 +1,10 @@ +private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; + +[ + QGVAR(color), + "COLOR", + localize "str_3den_marker_attribute_color_displayname", + _category, + [0.9, 0.9, 0, 1], + 0 +] call CBA_fnc_addSetting; diff --git a/addons/plotting/script_component.hpp b/addons/plotting/script_component.hpp new file mode 100644 index 000000000..bcb73cdbf --- /dev/null +++ b/addons/plotting/script_component.hpp @@ -0,0 +1,37 @@ +#define COMPONENT plotting +#define COMPONENT_BEAUTIFIED Plotting +#include "\x\zen\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_PLOTTING + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_PLOTTING + #define DEBUG_SETTINGS DEBUG_SETTINGS_PLOTTING +#endif + +#include "\x\zen\addons\main\script_macros.hpp" + +#include "\a3\ui_f\hpp\defineDIKCodes.inc" +#include "\x\zen\addons\common\defineResinclDesign.inc" + +#define ICON "\a3\ui_f\data\map\markerbrushes\cross_ca.paa" +#define ICON_ANGLE 0 +#define ICON_SCALE 1 +#define MAP_ICON_SCALE 24 +#define LINEWIDTH 5 + +#define CIRCLE_EDGES_MIN 12 +#define CIRCLE_RESOLUTION 5 + +#define CUBOID_HEIGHT_THRESHOLD 0.1 + +#define MAX_RENDER_DISTANCE 3000 +#define MAX_RENDER_DISTANCE_SQR 9000000 + +#define CAN_RENDER_ICON(CAMPOS,POS) (CAMPOS vectorDistanceSqr POS <= MAX_RENDER_DISTANCE_SQR) +#define CAN_RENDER_LINE(CAMPOS,POS1,POS2) ((CAMPOS vectorDistanceSqr POS1 <= MAX_RENDER_DISTANCE_SQR) || {CAMPOS vectorDistanceSqr POS2 <= MAX_RENDER_DISTANCE_SQR} || {[POS1, POS2, MAX_RENDER_DISTANCE, CAMPOS] call FUNC(isPosInCylinder)}) diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml new file mode 100644 index 000000000..1386b2eb3 --- /dev/null +++ b/addons/plotting/stringtable.xml @@ -0,0 +1,73 @@ + + + + + Plotting + Plotten + + + Measure distance + Entfernung messen + + + Measure distance from camera + Entfernung von Kamera messen + + + Measure radius + Radius messen + + + Measure offset + Versatz messen + + + Clear plots + Plots entfernen + + + Switch Distance Unit + Distanzeinheit durchschalten + + + Switches to the next unit for distances. + Wechselt zur nächsten Einheit für Entfernungen. + + + Switch Speed for Travel Time + Geschwindigkeit für Fahrzeit durchschalten + + + Switches to the next speed which determines travel time. + Wechselt zur nächsten Geschwindigkeit, die die Fahrzeit bestimmt. + + + Switch Speed Unit + Geschwindigkeitseinheit durchschalten + + + Switches to the next unit for speeds. + Wechselt zur nächsten Einheit für Geschwindigkeiten. + + + Switch Azimuth Unit + Richtungseinheit durchschalten + + + Switches to the next unit for directions. + Wechselt zur nächsten Einheit für Richtungen. + + + Delete Last Plot + Letzten Plot entfernen + + + Deletes the last plot created. + Entfernt den zuletzt erstellten Plot. + + + Cannot attach more than one plot endpoint to a unit + Einer Einheit kann nicht mehr als ein Plot-Endpunkt angehängt werden + + + diff --git a/addons/plotting/ui/cuboid.paa b/addons/plotting/ui/cuboid.paa new file mode 100644 index 000000000..b1d997f20 Binary files /dev/null and b/addons/plotting/ui/cuboid.paa differ diff --git a/addons/plotting/ui/radius.paa b/addons/plotting/ui/radius.paa new file mode 100644 index 000000000..5f6b732b2 Binary files /dev/null and b/addons/plotting/ui/radius.paa differ diff --git a/addons/plotting/ui/ruler.paa b/addons/plotting/ui/ruler.paa new file mode 100644 index 000000000..96d03a192 Binary files /dev/null and b/addons/plotting/ui/ruler.paa differ diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 1529f158f..bd303bdca 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -6,6 +6,7 @@ - [ Compositions](/user_guide/compositions.md) - [ Vehicle Garage](/user_guide/garage.md) - [ Area Markers](/user_guide/area_markers.md) + - [ Plotting](/user_guide/plotting.md) - **Frameworks** - [ Context Menu](/frameworks/context_menu.md) - [ Custom Modules](/frameworks/custom_modules.md) diff --git a/docs/user_guide/context_actions.md b/docs/user_guide/context_actions.md index 803ad90b1..5cddd642d 100644 --- a/docs/user_guide/context_actions.md +++ b/docs/user_guide/context_actions.md @@ -60,6 +60,10 @@ Sub-actions allow for copying and pasting the hovered unit's loadout onto anothe Furthermore, the unit's current weapon can be switched between their rifle, handgun, or binoculars. The main action acts as a shortcut for the "Edit" sub-action. +## Plotting + +Creates a [plot](/user_guide/plotting.md) at the context menu's position or attached object. + ## Remote Control Starts the remote control process on the hovered unit or vehicle. diff --git a/docs/user_guide/plotting.md b/docs/user_guide/plotting.md new file mode 100644 index 000000000..6d538e6c5 --- /dev/null +++ b/docs/user_guide/plotting.md @@ -0,0 +1,34 @@ +# Plotting + +This feature helps you measure distances and directions with 3D and map plots. +You can set the starting and ending points to be either fixed locations or attached to an object, allowing the values to update dynamically. +A unit can only have one end point connected to it at a time. + +You can cycle between different distance, speed, and azimuth formats using the Ctrl+R, Alt+R, and Ctrl+T keys, which can be customized in the CBA Keybinding settings. +You can also change the plot colors in the CBA settings. + +## Measure Distance + +This action creates a simple line. It calculates the 3D distance between the starting and ending points and shows it at the end of the line. + +Next to the distance, the travel time is shown in the format M:SS or H:MM:SS, based on the currently selected speed. +You can change the speed by pressing Shift+R by default. +If the start point is attached to a unit, the speed of the unit is used instead. + +The azimuth at the end point indicates the direction from the start point to the end point. + +## Measure Radius + +This action draws a circle with the starting point at the center. +The radius of the circle is determined by the 3D distance between the start and end points. +Same as in [Measure Distance](#measure-distance), the travel time is shown next to the distance. +The azimuth at the end point indicates the direction from the center to the end point. + +## Measure Offset + +This action creates a rectangular cuboid, using the starting point as one corner and the ending point as the opposite corner. +The end point displays the offsets in the X, Y, and Z axes relative to the starting point. + +## Clear Plots + +This action removes all measurement plots from the display.