Skip to content

Commit

Permalink
Marking Laser - IR marking laser for aircraft (#7761)
Browse files Browse the repository at this point in the history
Co-authored-by: PabstMirror <[email protected]>
Co-authored-by: Jouni Järvinen <[email protected]>
Co-authored-by: johnb432 <[email protected]>
Co-authored-by: Grim <[email protected]>
Co-authored-by: BrettMayson <[email protected]>
  • Loading branch information
6 people authored Jan 13, 2025
1 parent a54c4da commit f5a838e
Show file tree
Hide file tree
Showing 16 changed files with 421 additions and 0 deletions.
1 change: 1 addition & 0 deletions addons/markinglaser/$PBOPREFIX$
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
z\ace\addons\markinglaser
25 changes: 25 additions & 0 deletions addons/markinglaser/CfgEventHandlers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};

class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};

class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_postInit));
};
};

class Extended_InitPost_EventHandlers {
class Air {
class ADDON {
init = QUOTE(_this call FUNC(onAircraftInit));
};
};
};
4 changes: 4 additions & 0 deletions addons/markinglaser/CfgIRLaserSettings.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class CfgIRLaserSettings {
laserMaxRange = LASER_MAX; // 4000 is max range of the laser in engine
maxViewDistance = 6000;
};
17 changes: 17 additions & 0 deletions addons/markinglaser/CfgVehicles.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class CfgVehicles {
class AllVehicles;
class Air: AllVehicles {
class Attributes {
class GVAR(enabled) {
displayName = CSTRING(Attribute_Enabled_DisplayName);
tooltip = CSTRING(Attribute_Enabled_Tooltip);
property = QGVAR(enabled);
control = "Checkbox";
typeName = "BOOL";
expression = QUOTE(_this setVariable [ARR_3(QQGVAR(enabled),_value,true)]);
defaultValue = "true";
condition = "objectVehicle";
};
};
};
};
5 changes: 5 additions & 0 deletions addons/markinglaser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ace_markinglaser
===================

Adds an IR marking laser for aircraft.

4 changes: 4 additions & 0 deletions addons/markinglaser/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PREP(findTurret);
PREP(onAircraftInit);
PREP(onLaserOn);
PREP(renderPFH);
41 changes: 41 additions & 0 deletions addons/markinglaser/XEH_postInit.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "script_component.hpp"
#include "\a3\ui_f\hpp\defineDIKCodes.inc"

if (!hasInterface) exitWith {};

// Keybinds
["ACE3 Vehicles", QGVAR(toggleLaser), LLSTRING(ToggleLaser), {
// Ignore when in Zeus
if (!isNull curatorCamera) exitWith {false};

private _vehicle = cameraOn;
if !(_vehicle getVariable [QGVAR(enabled), false]) exitWith {false};

private _controlledUnit = [ACE_player, ACE_controlledUAV # 1] select (unitIsUAV _vehicle);

private _turretInfo = _vehicle getVariable [QGVAR(turretInfo), []];
private _canTurnOn = _controlledUnit == _vehicle turretUnit _turretInfo;
if (!_canTurnOn) exitWith { false };

playSound "ACE_Sound_Click";
private _currentMode = _vehicle getVariable [QGVAR(laserMode), MODE_OFF];
private _newMode = (_currentMode + 1) % 3;
_vehicle setVariable [QGVAR(laserMode), _newMode, true];
true
}, "", [DIK_L, [false, false, true]]] call CBA_fnc_addKeybind; // ALT-L

["CBA_settingsInitialized", {
TRACE_1("settingsInitialized",1);

GVAR(pfEH) = -1;
["visionMode", LINKFUNC(onLaserOn), true] call CBA_fnc_addPlayerEventHandler;
["ace_laserOn", {
params ["", "_args"];
_args params ["_object"];
if !(_object getVariable [QGVAR(enabled), false]) exitWith {};
_object setVariable [QGVAR(smoothing), []];
_object setVariable [QGVAR(flashOffset), random 1]; // make flashes not synchronized

[] call LINKFUNC(onLaserOn);
}] call CBA_fnc_addEventHandler;
}] call CBA_fnc_addEventHandler;
9 changes: 9 additions & 0 deletions addons/markinglaser/XEH_preInit.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "script_component.hpp"

ADDON = false;

PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;

ADDON = true;
3 changes: 3 additions & 0 deletions addons/markinglaser/XEH_preStart.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "script_component.hpp"

#include "XEH_PREP.hpp"
19 changes: 19 additions & 0 deletions addons/markinglaser/config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "script_component.hpp"

class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_common", "ace_laser"};
author = ECSTRING(common,ACETeam);
authors[] = {"BaerMitUmlaut"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};

#include "CfgIRLaserSettings.hpp"
#include "CfgEventHandlers.hpp"
#include "CfgVehicles.hpp"
75 changes: 75 additions & 0 deletions addons/markinglaser/functions/fnc_findTurret.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Finds the turret that has control over the marking laser.
*
* Arguments:
* 0: Aircraft config <OBJECT>
*
* Return Value:
* <ARRAY>
*
* Example:
* [configOf _plane] call ace_markinglaser_fnc_findTurret
*
* Public: No
*/

params ["_config"];

private _copilotPath = nil;
private _copilotConfig = configNull;
private _primaryPath = nil;
private _primaryConfig = configNull;
private _isUAV = getNumber (_config >> "isUAV") == 1;

private _walkTurrets = {
params ["_path", "_turrets"];

{
// Check turret rotation for symmetry, filters door gunner turrets
if (abs getNumber (_x >> "minTurn") != abs getNumber (_x >> "maxTurn")) then {
continue;
};

// Check if turret has a optics with night or thermal vision
private _visionModes = flatten (("true" configClasses (_x >> "OpticsIn")) apply {
(getArray (_x >> "visionMode")) apply {toLowerANSI _x}
});

if !("nvg" in _visionModes || {"ti" in _visionModes}) then {
continue;
};

// Use copilot turret if possible
// Not all helicopter gun turrets use this flag (for example the Kajman)
if (getNumber (_x >> "isCopilot") == 1) then {
_copilotPath = _path + [_forEachIndex];
_copilotConfig = _x;
break;
};

// Fallback to primary gunner
if (isNil "_primaryPath" && {getNumber (_x >> "primaryGunner") == 1}) then {
_primaryPath = _path + [_forEachIndex];
_primaryConfig = _x;
};

// Search subturrets
if (isClass (_x >> "Turrets")) then {
private _turrets = "true" configClasses (_x >> "Turrets");
[_path + [_forEachIndex], _turrets] call _walkTurrets;
};
} forEach _turrets;
};

[[], "true" configClasses (_config >> "Turrets")] call _walkTurrets;

if (!isNil "_copilotPath") exitWith {
[_copilotPath, _copilotConfig]
};
if (!isNil "_primaryPath") exitWith {
[_primaryPath, _primaryConfig]
};

[]
68 changes: 68 additions & 0 deletions addons/markinglaser/functions/fnc_onAircraftInit.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Equips an aircraft with a marking laser.
*
* Arguments:
* 0: Aircraft <OBJECT>
*
* Return Value:
* None
*
* Example:
* [plane] call ace_markinglaser_fnc_onAircraftInit
*
* Public: No
*/

params ["_aircraft"];
TRACE_2("onAircraftInit",_aircraft,typeOf _aircraft);

// Assume enabled by default
if !(_aircraft getVariable [QGVAR(enabled), true]) exitWith {};

private _config = configOf _aircraft;
private _turretData = [_config] call FUNC(findTurret);
private _hasPilotCamera = getNumber (_config >> "PilotCamera" >> "controllable") > 0;

if ((_turretData isEqualTo []) && {!_hasPilotCamera}) exitWith {
_aircraft setVariable [QGVAR(enabled), false];
WARNING_1("Class %1 does not have a pilot camera nor a turret that could be equipped with an IR marking laser.",configName _config);
};

_aircraft setVariable [QGVAR(enabled), true];
_aircraft setVariable [QGVAR(smoothing), []];

if (_turretData isEqualTo []) then {
TRACE_1("pilot",_turretData);
// Use pilot camera if no turrets are available
_aircraft setVariable [QGVAR(turretInfo), [-1]];
} else {
TRACE_1("turret",_turretData);
_turretData params ["_turretPath"];
_aircraft setVariable [QGVAR(turretInfo), _turretPath];
};


TRACE_1("Add interaction",_aircraft);
private _condition = {
if !(_target getVariable [QGVAR(enabled), false]) exitWith {false};
private _controlledUnit = [ACE_player, ACE_controlledUAV # 1] select (unitIsUAV _target);
private _turretInfo = _target getVariable [QGVAR(turretInfo), []];
private _canTurnOn = _controlledUnit == _target turretUnit _turretInfo;
_canTurnOn
};

private _getChildren = {
private _statement = {
_target setVariable [QGVAR(laserMode), _actionParams, true];
};
private _current = _target getVariable [QGVAR(laserMode), 0];
[[MODE_OFF, LELSTRING(common,disabled)], [MODE_ON, LELSTRING(common,enabled)], [MODE_FLASH, LLSTRING(Flashing)]] apply {
_x params ["_mode", "_text"];
if (_current == _mode) then { _text = ">" + _text };
[[str _mode, _text, "", _statement, {true}, {}, _mode] call EFUNC(interact_menu,createAction), [], _target]
}
};
private _actionBase = [QGVAR(actionBase), LLSTRING(Name), "", {}, _condition, _getChildren] call EFUNC(interact_menu,createAction);
private _basePath = [_aircraft, 1, ["ACE_SelfActions"], _actionBase] call EFUNC(interact_menu,addActionToObject);
22 changes: 22 additions & 0 deletions addons/markinglaser/functions/fnc_onLaserOn.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut, PabstMirror
* Starts render PFEH
*
* Arguments:
* 0: Aircraft <OBJECT>
*
* Return Value:
* None
*
* Example:
* [] call ace_markinglaser_fnc_onLaserOn
*
* Public: No
*/

if (GVAR(pfEH) != -1) exitWith {};
if (((currentVisionMode focusOn) != 1) || {EGVAR(laser,laserEmitters) isEqualTo []}) exitWith {};

GVAR(pfEH) = [LINKFUNC(renderPFH), 0, []] call CBA_fnc_addPerFrameHandler;
TRACE_1("start PFEH",GVAR(pfEH));
72 changes: 72 additions & 0 deletions addons/markinglaser/functions/fnc_renderPFH.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut, PabstMirror
* Renders all marking lasers.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_markinglaser_fnc_renderPFH
*
* Public: No
*/

if (((currentVisionMode focusOn) != 1) || {EGVAR(laser,laserEmitters) isEqualTo []}) exitWith {
GVAR(pfEH) call CBA_fnc_removePerFrameHandler;
GVAR(pfEH) = -1;
TRACE_1("end PFEH",GVAR(pfEH));
};

{
_y params ["_aircraft", "", "_laserMethod"];

private _currentMode = _aircraft getVariable [QGVAR(laserMode), MODE_OFF];
if (_currentMode == MODE_OFF) then { continue };
if (_laserMethod != QEFUNC(laser,findLaserSource)) then { continue }; // Normal vanilla laserTarget func

if ((_currentMode == MODE_FLASH) && {
private _cycle = (CBA_missionTime + (_aircraft getVariable [QGVAR(flashOffset), 0])) % (1/3);
_cycle > 1/6
}) then { continue };

(_y call EFUNC(laser,findLaserSource)) params ["_laserPosASL", "_laserDir"];

#ifdef DEBUG_MODE_FULL
private _targetObject = _aircraft getVariable [QEGVAR(laser,targetObject), objNull];
private _targetPosASL = getPosASL _targetObject;

drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa", [1,0,1,1], (ASLToAGL _targetPosASL), 0.5, 0.5, 0, "Laser", 0.5, 0.025, "TahomaB"];
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa", [1,0,1,1], (ASLToAGL _laserPosASL), 0.5, 0.5, 0, "Origin", 0.5, 0.025, "TahomaB"];
#endif

// If our camera is the laser source, offset it just a bit so it isn't dead center
if (((AGLToASL positionCameraToWorld [0,0,0]) distance _laserPosASL) < 0.09) then {
_laserPosASL = AGLToASL positionCameraToWorld [-0.02, -0.05, 0];
};

private _smoothing = _aircraft getVariable [QGVAR(smoothing), []];
_smoothing pushBack _laserDir;
if (count _smoothing > 5) then { _smoothing deleteAt 0 };
private _smoothDir = [0,0,0];
{ _smoothDir = _smoothDir vectorAdd _x } forEach _smoothing;
_smoothDir = _smoothDir vectorMultiply (1/count _smoothing);

private _startPos = _laserPosASL vectorAdd (_smoothDir vectorMultiply 0.25); // go forward a bit so first drawLaser doesn't hit aircraft
private _endPos = _laserPosASL vectorAdd (_smoothDir vectorMultiply LASER_MAX);
private _intersects = [];
while { _intersects isEqualTo [] } do {
// drawLaser has an internal maximum distance it can draw, so we may need to draw multiple segments
drawLaser [_startPos, _smoothDir, [250, 0, 0, 1], [], 0, 1, LASER_MAX, true]; // Draw a segment
if ((_startPos distance _laserPosASL) > 9999) exitWith {}; // just exit loop if we've drawn far enough
_intersects = lineIntersectsSurfaces [_startPos, _endPos, _aircraft];
if (_intersects isEqualTo []) then { // Check if we hit anything
_startPos = _endPos; // if we didn't then move up the startpos to where the last draw ended
_endPos = _endPos vectorAdd (_smoothDir vectorMultiply LASER_MAX);
};
};
drawLaser [_laserPosASL, _smoothDir, [250, 0, 0, 1], [], 0.5, 1, LASER_MAX, true]; // final draw from actual origin
} forEach EGVAR(laser,laserEmitters);
Loading

0 comments on commit f5a838e

Please sign in to comment.