Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IR marking laser for aircraft #7761

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
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
23 changes: 23 additions & 0 deletions addons/markinglaser/CfgEventHandlers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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 {
ADDON = QUOTE(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 a marking laser for aircraft.

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

// Events
[QGVAR(laserOn), LINKFUNC(onLaserOn)] call CBA_fnc_addEventHandler;
[QGVAR(laserOff), LINKFUNC(onLaserOff)] call CBA_fnc_addEventHandler;

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this because previously there was nothing, but we might want to add a CBA registered camera check.


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

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

private _canTurnOn = if (_vehicle getVariable [QGVAR(useTurret), false]) then {
private _turretInfo = _vehicle getVariable [QGVAR(turretInfo), []];
_controlledUnit == _vehicle turretUnit _turretInfo # 0
} else {
_controlledUnit == driver _vehicle
};

if (_canTurnOn) then {
if (_vehicle getVariable [QGVAR(laserOn), false]) then {
[QGVAR(laserOff), [_vehicle]] call CBA_fnc_globalEvent;
} else {
[QGVAR(laserOn), [_vehicle]] call CBA_fnc_globalEvent;
};

true
} else {
false
};
}, "", [DIK_L, [false, false, true]]] call CBA_fnc_addKeybind;

// JIP init
if (didJIP) then {
{
if (_x getVariable [QGVAR(laserOn), false]) then {
[_x] call FUNC(onLaserOn);
};
} forEach (allMissionObjects "Air");
};
Comment on lines +39 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not store all lasers on the server, then send them to connecting clients when requested? See the dogtags component for an implementation.

16 changes: 16 additions & 0 deletions addons/markinglaser/XEH_preInit.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "script_component.hpp"

ADDON = false;

PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;

#include "initSettings.inc.sqf"

GVAR(lasers) = [];
GVAR(localLasers) = [];
GVAR(renderPFH) = -1;
GVAR(updatePFH) = -1;

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"};
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:
* None
*
* 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]
};

[]
81 changes: 81 additions & 0 deletions addons/markinglaser/functions/fnc_getPilotCamVector.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the directional vector of a pilot camera mounted marking laser.
* Uses interpolation of regular network updates because camera direction is local.
*
* Arguments:
* 0: Aircraft <OBJECT>
* 1: Laser <OBJECT>
*
* Return Value:
* Directional vector of laser in world space <ARRAY>
*
* Example:
* [plane, laser] call ace_markinglaser_fnc_getPilotCamVector
*
* Public: No
*/

params ["_aircraft"];

#ifndef DEBUG_MODE_FULL
// If player is controlling the camera, no need to interpolate
// Interpolate local AI vehicles, getPilotCameraDirection does not work with AI
if (local _aircraft && {cameraOn == _aircraft}) exitWith {
_aircraft vectorModelToWorldVisual getPilotCameraDirection _aircraft
};
#endif

// _type - Target type
// _target - Locked target, either an object, a position or a vector
// _time - When this info was last synched
// _isNewInfo - NIL when this info was just updated, otherwise true (not synched)
private _laser = _aircraft getVariable [QGVAR(laserInfo), []];
_laser params ["_type", "_target", "_time", ["_isNewInfo", true]];

private _originModel = _aircraft getVariable [QGVAR(laserOrigin), ""];
private _origin = _aircraft modelToWorldVisualWorld (_aircraft selectionPosition _originModel);
private _deltaTime = CBA_missionTime - _time;

// If an update is older than 2s, the laser movement is stopped
// This would indicate a lot of lag and cause the laser to go off into nowhere
if (_deltaTime > UPDATE_INTERVAL * 2) exitWith {
_aircraft vectorModelToWorld vectorUp _laser;
};

private _interpolationInfo = _aircraft getVariable [QGVAR(interpolationInfo), []];
_interpolationInfo params ["_lastTarget", "_cachedTarget", ["_cachedType", ""]];

// If laser info was just synched, use previous position/vector for interpolation start
if (_isNewInfo) then {
// TYPE_VECTOR doesn't clip interpolation, use current orientation instead
_cachedTarget = [_cachedTarget, _aircraft vectorWorldToModelVisual vectorUp _laser] select (_type == TYPE_VECTOR);

// Do not interpolate if type changed - might cause a snap when locking but should be OK
_lastTarget = [_target, _cachedTarget] select (_type == _cachedType);

// Update info arrays
_aircraft setVariable [QGVAR(interpolationInfo), [_lastTarget, _target, _type]];
};

private _vector = [0, 0, 0];
switch (_type) do {
case TYPE_OBJECT: {
// Don't interpolate because laser should always directly point at object
_vector = _origin vectorFromTo getPosASLVisual _target;
};
case TYPE_GROUND: {
// Interpolate between last position and current position
private _interpolatedPos = vectorLinearConversion [0, UPDATE_INTERVAL, _deltaTime, _lastTarget, _target, true];
_vector = _origin vectorFromTo _interpolatedPos;
};
case TYPE_VECTOR: {
// Interpolate between last vector and current vector
// Interpolation is deliberately not clipped to smoothen movement with delayed updates
private _interpolatedVec = vectorLinearConversion [0, UPDATE_INTERVAL, _deltaTime, _lastTarget, _target, false];
_vector = _aircraft vectorModelToWorldVisual _interpolatedVec;
};
};

_vector
38 changes: 38 additions & 0 deletions addons/markinglaser/functions/fnc_getTurretVector.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the directional vector of a turret mounted marking laser.
*
* Arguments:
* 0: Aircraft <OBJECT>
*
* Return Value:
* Directional vector of laser in world space <ARRAY>
*
* Example:
* [plane] call ace_markinglaser_fnc_getTurretVector
*
* Public: No
*/

params ["_aircraft"];

private _turretInfo = _aircraft getVariable [QGVAR(turretInfo), []];
_turretInfo params ["_turretPath", "_animationSources", "_followFreeLook"];

private _controlledTurret = if (unitIsUAV cameraOn) then {
ACE_controlledUAV # 2
} else {
cameraOn unitTurret ACE_player
};

if ((cameraOn == _aircraft) && {(_followFreeLook && {cameraView == "INTERNAL"}) || {cameraView == "GUNNER"}} && {!freeLook} && {_turretPath isEqualTo _controlledTurret}) then {
// Camera has some inertia which the turret does not have, use center of screen instead
(AGLToASL positionCameraToWorld [0, 0, 0]) vectorFromTo (AGLToASL positionCameraToWorld [0, 0, 1])
} else {
// Get turret dir through animation source
private _angleBody = -deg (_aircraft animationPhase _animationSources # 0);
private _angleGun = deg (_aircraft animationPhase _animationSources # 1);

_aircraft vectorModelToWorld ([1, _angleBody, _angleGun] call CBA_fnc_polar2vect)
};
Loading
Loading