Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions addons/quickmount/CfgVehicles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ class CfgVehicles {
};
};

// because Get In action has its own statement
// we have to cache subactions in args and reuse them in insertChildren code
#define GETIN_ACTIONS \
class ACE_Actions { \
class ACE_MainActions { \
class GVAR(GetIn) { \
displayName = "$STR_rscMenu.hppRscGroupRootMenu_Items_GetIn1"; \
condition = QUOTE(call DFUNC(canShowFreeSeats)); \
condition = QUOTE(call DFUNC(canShowFreeSeats) && {_actionParams set [ARR_2(0,call DFUNC(addFreeSeatsActions))];_actionParams select 0 isNotEqualTo []}); \
statement = QUOTE(call DFUNC(getInNearest)); \
exceptions[] = {"isNotSwimming"}; \
insertChildren = QUOTE((_this select 2) param [ARR_2(0,[])]); \
insertChildren = QUOTE(_actionParams param [ARR_2(0,[])]); \
}; \
}; \
}; \
Expand Down
3 changes: 3 additions & 0 deletions addons/quickmount/XEH_PREP.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
PREP(addFreeSeatsActions);
PREP(addGetInActions);
PREP(canShowFreeSeats);
PREP(getInNearest);
PREP(getSeatProxies);
PREP(getSeatUnit);
PREP(moduleInit);
2 changes: 2 additions & 0 deletions addons/quickmount/XEH_postInitClient.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ if (!hasInterface) exitWith {};
[] call FUNC(getInNearest);
};
}] call CBA_fnc_addKeybind;

GVAR(initializedVehicleClasses) = [];
73 changes: 73 additions & 0 deletions addons/quickmount/dev/drawSeatProxies.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "..\script_component.hpp"

// call compileScript ["z\ace\addons\quickmount\dev\drawSeatProxies.sqf"]
// [_showSingleProxies, _minProxyDistance, _showAnyProxy] call compileScript ["z\ace\addons\quickmount\dev\drawSeatProxies.sqf"]

params [["_showSingleProxies", false], ["_minProxyDistance", 0.1], ["_showAnyProxy", false]];

GVAR(showSingleProxies) = _showSingleProxies; // show seats that have only one proxy point
GVAR(minProxyDistance) = _minProxyDistance; // minimum distance between nearby proxies to show both
GVAR(showAnyProxy) = _showAnyProxy; // show any proxy not only seat

if (!isNil QGVAR(draw3DEH)) then {
removeMissionEventHandler ["Draw3D", GVAR(draw3DEH)];
};

GVAR(draw3DEH) = addMissionEventHandler ["Draw3D", {
{
private _vehicle = _x;
if (!(_vehicle isKindOf "AllVehicles") || {_vehicle isKindOf "Man"}) then {continue};

// make unique var name to recalc proxies
private _seatProxies = _vehicle getVariable format ["%1%2", QGVAR(seatProxies), GVAR(draw3DEH)];
if (isNil "_seatProxies") then {
// see fnc_getSeatProxies
_seatProxies = createHashMap;
private _proxyRolePrefixes = ["driver", "gunner", "commander", "cargo", "pilot"];
private _proxyRoleMappings = ["driver", "gunner", "commander", "cargo", "driver", ""];
private _allLODsNumbers = allLODs _vehicle apply {_x select 2};
{
private _lodNumber = _x;
private _proxyPaths = _vehicle selectionNames _lodNumber select {
_x select [0,6] == "proxy:"
};
private _color = [random 1, random 1, random 1, 1];
{
private _proxyPath = _x;
private _substrings = _proxyPath splitString ":\.";
if (count _substrings < 3) then {continue};
private _proxyIndex = parseNumber (_substrings select -1);
if (_proxyIndex < 1) then {continue};
private _proxyRole = toLower (_substrings select -2);
private _proxyGroup = _proxyRoleMappings select (_proxyRolePrefixes findIf {_proxyRole find _x == 0});
if (_proxyGroup == "" && {!GVAR(showAnyProxy)}) then {continue};

private _proxyHashMap = _seatProxies getOrDefault [_proxyGroup, createHashMap, true];
private _proxies = _proxyHashMap getOrDefault [_proxyIndex, [], true];
private _pos = _vehicle selectionPosition [_proxyPath, _lodNumber, "AveragePoint"];
// skip nearby proxies
if (-1 < _proxies findIf {GVAR(minProxyDistance) > _vehicle selectionPosition (_x select 0) vectorDistance _pos}) then {continue};
_proxies pushBack [[_proxyPath, _lodNumber, "AveragePoint"], format ["%1:%2.%3", _lodNumber, _proxyRole, _proxyIndex], _color];
} forEach _proxyPaths;
} forEach _allLODsNumbers;
_vehicle setVariable [format ["%1%2", QGVAR(seatProxies), GVAR(draw3DEH)], _seatProxies];
};

{
{
if (!GVAR(showSingleProxies) && {count _y < 2}) then {continue}; // skip seats with only one proxy
{
drawIcon3D [
"\a3\ui_f\data\gui\cfg\hints\icon_text\group_1_ca.paa",
_x select 2,
_vehicle modelToWorldVisual (_vehicle selectionPosition (_x select 0)),
0.5, 0.5, 0,
_x select 1
];
} forEach _y;
} forEach _y;
} forEach _seatProxies;
} forEach [cursorObject, objectParent ACE_player];
}];

GVAR(draw3DEH)
8 changes: 0 additions & 8 deletions addons/quickmount/functions/fnc_addFreeSeatsActions.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@

#define TAKEN_SEAT_TIMEOUT 0.5

#define ICON_DRIVER "A3\ui_f\data\IGUI\RscIngameUI\RscUnitInfo\role_driver_ca.paa"
#define ICON_PILOT "A3\ui_f\data\IGUI\Cfg\Actions\getinpilot_ca.paa"
#define ICON_CARGO "A3\ui_f\data\IGUI\RscIngameUI\RscUnitInfo\role_cargo_ca.paa"
#define ICON_GUNNER "A3\ui_f\data\IGUI\Cfg\Actions\getingunner_ca.paa"
#define ICON_COMMANDER "A3\ui_f\data\IGUI\RscIngameUI\RscUnitInfo\role_commander_ca.paa"
#define ICON_TURRET "A3\ui_f\data\IGUI\RscIngameUI\RscUnitInfo\role_gunner_ca.paa"
#define ICON_FFV "A3\ui_f\data\IGUI\Cfg\CrewAimIndicator\gunnerAuto_ca.paa"

#define TO_COMPARTMENT_STRING(var) if !(var isEqualType "") then {var = format [ARR_2("Compartment%1",var)]}

// if unit isn't moved to new seat in TAKEN_SEAT_TIMEOUT, we move him back to his seat
Expand Down
166 changes: 166 additions & 0 deletions addons/quickmount/functions/fnc_addGetInActions.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#include "..\script_component.hpp"
/*
* Author: Dystopian
* Adds Get In actions for vehicle seats and Passenger Actions for vehicle crew.
*
* Arguments:
* 0: Vehicle <OBJECT>
*
* Return Value:
* None
*
* Example:
* cursorObject call ace_quickmount_fnc_addGetInActions
*
* Public: No
*/

#define ACTION_DISTANCE 4
#define CREW_HEIGHT_ABOVE_SEAT 0.3

params ["_vehicle"];

private _vehicleClass = typeOf _vehicle;
private _vehicleConfig = configOf _vehicle;
private _cargoProxyIndexes = getArray (_vehicleConfig >> "cargoProxyIndexes");
private _seatProxies = _vehicle call FUNC(getSeatProxies);

private _seatPositions = GETVAR(_vehicle,GVAR(seatPositions),[]);
if (_seatPositions isEqualType []) then {
_seatPositions = createHashMapFromArray _seatPositions;
};
private _seatPositionsConfig = getArray (_vehicleConfig >> QGVAR(seatPositions));
_seatPositionsConfig params [["_model", ""], ["_seatPositionsArray", []]];
// handle inherited classes with different model
if (_model == getText (_vehicleConfig >> "model")) then {
_seatPositions merge createHashMapFromArray _seatPositionsArray;
};

TRACE_4("addGetInActions",_vehicleClass,_cargoProxyIndexes,_seatPositions,_seatProxies);

private _conditionCrew = {
call FUNC(canShowFreeSeats)
&& {!isNull ([_target, _actionParams select 0] call FUNC(getSeatUnit))}
};
private _insertChildrenCrew = {
private _unit = [_target, _actionParams select 0] call FUNC(getSeatUnit);
[nil, nil, _unit] call EFUNC(interaction,addPassengerActions)
};
private _modifierFunctionCrew = {
params ["_target", "", "_params", "_actionData"];
_params params ["_seat"];
private _unit = [_target, _seat] call FUNC(getSeatUnit);
if (isNull _unit) exitWith {}; // skip empty seats
_actionData set [1, [_unit, true] call EFUNC(common,getName)];
if (["ace_medical_gui"] call EFUNC(common,isModLoaded)) then {
_this set [0, _unit]; // == _target
call EFUNC(medical_gui,modifyActionTriageLevel);
};
};

private _allSeats = fullCrew [_vehicle, "", true];
private _cargoPositionNumber = -1;

{
_x params ["", "_role", "_cargoIndex", "_turretPath", "_isPersonTurret", "", "_positionName"];
private _name = if (isLocalized _positionName) then {localize _positionName} else {_positionName};

private ["_proxyGroup", "_proxyIndex", "_icon", "_condition", "_statement", "_params", "_position", "_positionCrew"];
switch (_role) do {
case "driver": {
if (
unitIsUAV _vehicle
|| {getNumber (_vehicleConfig >> "hasDriver") == 0}
) then {continue};
_proxyGroup = "driver";
_proxyIndex = 1;
_params = [_turretPath];
_condition = {
call FUNC(canShowFreeSeats)
&& {!lockedDriver _target}
&& {!alive driver _target}
};
_statement = {_player action ["GetInDriver", _target]};
_icon = [ICON_DRIVER, ICON_PILOT] select (_vehicle isKindOf "Air");
};
case "cargo": {
INC(_cargoPositionNumber);
_proxyGroup = "cargo";
_proxyIndex = _cargoProxyIndexes param [_cargoPositionNumber, _cargoPositionNumber + 1];
_params = [_cargoIndex, _cargoPositionNumber];
_condition = {
call FUNC(canShowFreeSeats)
&& {
private _cargoIndex = _actionParams select 0;
!(_target lockedCargo _cargoIndex)
&& {!alive ([_target, _cargoIndex] call FUNC(getSeatUnit))}
}
};
_statement = {_player action ["GetInCargo", _target, _actionParams select 1]};
_name = format ["%1 %2", _name, _cargoPositionNumber + 1];
_icon = ICON_CARGO;
};
default {
if (_role == "gunner" && {unitIsUAV _vehicle}) then {continue};
private _turretConfig = [_vehicleConfig, _turretPath] call CBA_fnc_getTurret;
private _proxyType = getText (_turretConfig >> "proxyType");
_proxyGroup = switch (_proxyType) do {
case "CPCommander": {"commander"};
case "CPGunner": {"gunner"};
default {"cargo"};
};
_proxyIndex = getNumber (_turretConfig >> "proxyIndex");
_params = [_turretPath];
_condition = {
call FUNC(canShowFreeSeats)
&& {
private _turretPath = _actionParams select 0;
!(_target lockedTurret _turretPath)
&& {!alive ([_target, _turretPath] call FUNC(getSeatUnit))}
}
};
_statement = {_player action ["GetInTurret", _target, _actionParams select 0]};
_icon = switch true do {
case (getNumber (_turretConfig >> "isCopilot") > 0): {ICON_PILOT};
case (_role == "gunner"): {ICON_GUNNER};
case (_role == "commander"): {ICON_COMMANDER};
case (_isPersonTurret): {ICON_FFV};
case (getText (_turretConfig >> "gun") == ""): {ICON_CARGO};
default {ICON_TURRET};
};
};
};

private _positionString = _seatPositions getOrDefault [_params select 0, ""];
if (_positionString != "") then {
_position = compile _positionString;
_positionCrew = compile format ["(%1) vectorAdd [0, 0, %2]", _positionString, CREW_HEIGHT_ABOVE_SEAT];
TRACE_6("seat position",_role,_cargoIndex,_turretPath,_proxyGroup,_proxyIndex,_position);
} else {
private _seatProxy = _seatProxies getOrDefault [_proxyGroup, createHashMap] getOrDefault [_proxyIndex, []];
TRACE_6("seat proxy",_role,_cargoIndex,_turretPath,_proxyGroup,_proxyIndex,_seatProxy);
if (_seatProxy isEqualTo []) then {continue};
// cannot use static position because some proxy positions move with turret rotation
_position = compile format ["_target selectionPosition ['%1', %2, 'AveragePoint']", _seatProxy select 0, _seatProxy select 1];
_positionCrew = compile format [
"_target selectionPosition ['%1', %2, 'AveragePoint'] vectorAdd [0, 0, %3]",
_seatProxy select 0,
_seatProxy select 1,
CREW_HEIGHT_ABOVE_SEAT
];
};

private _action = [
format ["%1%2%3empty", _role, _cargoIndex, _turretPath],
_name, _icon, _statement, _condition, {}, _params, _position, ACTION_DISTANCE
] call EFUNC(interact_menu,createAction);
[_vehicleClass, 0, [], _action] call EFUNC(interact_menu,addActionToClass);

// modifier function needs icon color
private _actionCrew = [
format ["%1%2%3crew", _role, _cargoIndex, _turretPath],
"", [_icon, "#FFFFFF"], {}, _conditionCrew, _insertChildrenCrew,
_params, _positionCrew, ACTION_DISTANCE, nil, _modifierFunctionCrew
] call EFUNC(interact_menu,createAction);
[_vehicleClass, 0, [], _actionCrew] call EFUNC(interact_menu,addActionToClass);
} forEach _allSeats;
30 changes: 19 additions & 11 deletions addons/quickmount/functions/fnc_canShowFreeSeats.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
/*
* Author: Dystopian
* Checks if Free Seats menu can be shown.
* Result is cached for 1 second for reuse in several visible ATM actions.
*
* Arguments:
* 0: Vehicle <OBJECT>
* 1: Unit <OBJECT>
* 2: Args <ARRAY>
* 3: Use cache <BOOL> (default: true)
*
* Return Value:
* Can show menu <BOOL>
Expand All @@ -17,34 +19,40 @@
* Public: No
*/

params ["_vehicle", "_unit", "_args"];
params ["_vehicle", "_unit", ["_args", []], ["_useCache", true]];

private _isInVehicle = _unit in _vehicle;

// function is called by multiple actions and MainAction
if (!_isInVehicle && {_useCache}) exitWith {
_this set [3, false];
[_this, LINKFUNC(canShowFreeSeats), _vehicle, QGVAR(canShowFreeSeats), 1] call EFUNC(common,cachedCall) // return
};

TRACE_6("canShowFreeSeats",_vehicle,typeOf _vehicle,_unit,_isInVehicle,_args,_useCache);

GVAR(enabled)
&& {
GVAR(enableMenu) == 3
|| {_isInVehicle && {GVAR(enableMenu) == 2}}
|| {!_isInVehicle && {GVAR(enableMenu) == 1}}
}
&& {alive _vehicle}
&& {2 > locked _vehicle}
&& {locked _vehicle < 2}
&& {isNull getConnectedUAVUnit _unit}
&& {simulationEnabled _vehicle}
&& {[_unit, _vehicle] call EFUNC(interaction,canInteractWithVehicleCrew)}
&& {
[_unit, _vehicle] call EFUNC(interaction,canInteractWithVehicleCrew)
}
&& {
0.3 < vectorUp _vehicle select 2 // moveIn* and GetIn* don't work for flipped vehicles
vectorUp _vehicle select 2 > 0.3 // moveIn* and GetIn* don't work for flipped vehicles
|| {_vehicle isKindOf "Air"} // except Air
}
&& {
_isInVehicle
|| {typeOf _vehicle in GVAR(initializedVehicleClasses)}
|| {
// because Get In action has its own statement
// we have to cache subactions in args and reuse them in insertChildren code
private _subActions = call FUNC(addFreeSeatsActions);
_args set [0, _subActions];
[] isNotEqualTo _subActions
// init vehicle actions here to skip useless checks on clients with disabled quickmount
GVAR(initializedVehicleClasses) pushBack typeOf _vehicle;
_vehicle call FUNC(addGetInActions);
true
}
}
Loading
Loading