From e8c3be80168d2471d6a6798edfed24e377ffc1d9 Mon Sep 17 00:00:00 2001 From: Dystopian Date: Mon, 19 Nov 2018 08:33:50 +0400 Subject: [PATCH] Quick Mount - Add Get In and Change seat action menu (#5745) * Add quickmount GetIn menu * Add compartment support * Check engine, check pilot, check static turret driver * Add doc, condition, translation * Add hybrid config entries, fix MP issue * Optimize condition * Ignore Enabled setting in vehicle * Work around SQF validator macro issue * Fix config macro entries * Add workaround for getting damage when seat changing while moving * Add setting for Get In menu disabling * Fix race when 2 players try to get the same seat * Convert if-else to switch * Decrease move-back timeout to 0.5s * Check if vehicle is flipped * Add getin statement for parent menu * Improve canShowFreeSeats * Apply latest trends * Improve fnc_addFreeSeatsActions * Change copilot and gunless turret icons * Fix macro name * Fix FFV icon * Optimize turret icon code * Extend setting to 4 values * Fix menu is shown when vehicle is full * Optimize UAV checking code * Fix bug with disabled FFV turrets * Remove bugged FFV, Add turret locality check, Add Failed message * Replace some macros with function * Fix validator * Remove global variables, Add debug --- addons/quickmount/CfgVehicles.hpp | 47 ++++ addons/quickmount/XEH_PREP.hpp | 2 + .../functions/fnc_addFreeSeatsActions.sqf | 247 ++++++++++++++++++ .../functions/fnc_canShowFreeSeats.sqf | 39 +++ addons/quickmount/initSettings.sqf | 34 ++- addons/quickmount/stringtable.xml | 4 + 6 files changed, 364 insertions(+), 9 deletions(-) create mode 100644 addons/quickmount/functions/fnc_addFreeSeatsActions.sqf create mode 100644 addons/quickmount/functions/fnc_canShowFreeSeats.sqf diff --git a/addons/quickmount/CfgVehicles.hpp b/addons/quickmount/CfgVehicles.hpp index 702f7cab4e..d24a207d4b 100644 --- a/addons/quickmount/CfgVehicles.hpp +++ b/addons/quickmount/CfgVehicles.hpp @@ -34,4 +34,51 @@ class CfgVehicles { description = CSTRING(KeybindDescription); }; }; + +#define GETIN_ACTIONS \ + class ACE_Actions { \ + class ACE_MainActions { \ + class GVAR(GetIn) { \ + displayName = "$STR_rscMenu.hppRscGroupRootMenu_Items_GetIn1"; \ + condition = QUOTE(call DFUNC(canShowFreeSeats)); \ + statement = QUOTE(call DFUNC(getInNearest)); \ + exceptions[] = {"isNotSwimming"}; \ + insertChildren = QUOTE(call DFUNC(addFreeSeatsActions)); \ + }; \ + }; \ + }; \ + class ACE_SelfActions { \ + class GVAR(ChangeSeat) { \ + displayName = CSTRING(ChangeSeat); \ + condition = QUOTE(call DFUNC(canShowFreeSeats)); \ + statement = ""; \ + insertChildren = QUOTE(call DFUNC(addFreeSeatsActions)); \ + }; \ + } + + class LandVehicle; + class Car: LandVehicle { + GETIN_ACTIONS; + }; + class Motorcycle: LandVehicle { + GETIN_ACTIONS; + }; + class StaticWeapon: LandVehicle { + GETIN_ACTIONS; + }; + class Tank: LandVehicle { + GETIN_ACTIONS; + }; + class Air; + class Helicopter: Air { + GETIN_ACTIONS; + }; + class Plane: Air { + GETIN_ACTIONS; + }; + class Ship; + class Ship_F: Ship { + GETIN_ACTIONS; + }; + }; diff --git a/addons/quickmount/XEH_PREP.hpp b/addons/quickmount/XEH_PREP.hpp index eba2f68296..5b0ec5a042 100644 --- a/addons/quickmount/XEH_PREP.hpp +++ b/addons/quickmount/XEH_PREP.hpp @@ -1,2 +1,4 @@ +PREP(addFreeSeatsActions); +PREP(canShowFreeSeats); PREP(getInNearest); PREP(moduleInit); diff --git a/addons/quickmount/functions/fnc_addFreeSeatsActions.sqf b/addons/quickmount/functions/fnc_addFreeSeatsActions.sqf new file mode 100644 index 0000000000..b49525d406 --- /dev/null +++ b/addons/quickmount/functions/fnc_addFreeSeatsActions.sqf @@ -0,0 +1,247 @@ +#include "script_component.hpp" +/* + * Author: Dystopian + * Creates actions for vehicle free seats. + * + * Arguments: + * 0: Vehicle + * 1: Unit + * + * Return Value: + * Child actions + * + * Example: + * [cursorObject, player] call ace_quickmount_fnc_addFreeSeatsActions + * + * Public: No + */ + +#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 +#define WAIT_IN_OR_MOVE_BACK \ + [ARR_5( \ + {!isNull objectParent (_this select 0)}, \ + { \ + params [ARR_2("_player","_damageBlocked")]; \ + if (_damageBlocked) then {_player allowDamage true}; \ + LOG_1("moved in in %1 frames",diag_frameno-GVAR(frame)); \ + }, \ + [ARR_4(_player,_damageBlocked,_moveBackCode,_moveBackParams)], \ + TAKEN_SEAT_TIMEOUT, \ + { \ + params [ARR_4("_player","_damageBlocked","_moveBackCode","_moveBackParams")]; \ + [ARR_2(_player,_moveBackParams)] call _moveBackCode; \ + if (_damageBlocked) then {_player allowDamage true}; \ + localize "str_mis_state_failed" call EFUNC(common,displayTextStructured); \ + } \ + )] call CBA_fnc_waitUntilAndExecute; + +#define IS_MOVED_OUT \ +( \ + isNull objectParent _player \ + && { \ + [] isEqualTo _currentTurret \ + || {local _target isEqualTo (_target turretLocal _currentTurret)} \ + } \ +) + +#define MOVE_IN_CODE(command) (_this select 0) command (_this select 1) + +private _fnc_move = { + (_this select 2) params ["_moveInCode", "_moveInParams", "_currentTurret", "_moveBackCode", "_moveBackParams", ["_enabledByAnimationSource", ""]]; + TRACE_7("fnc_move params",_moveInCode,_moveInParams,_currentTurret,_moveBackCode,_moveBackParams,_enabledByAnimationSource,call {GVAR(frame)=diag_frameno}); + + // check bugged FFV + if ( + !("" isEqualTo _enabledByAnimationSource) + && {1 > _target doorPhase _enabledByAnimationSource} + ) exitWith {}; + + // workaround getting damage when moveOut while vehicle is moving + private _damageBlocked = false; + if (isDamageAllowed _player) then { + _player allowDamage false; + _damageBlocked = true; + }; + private _preserveEngineOn = _player == driver _target && {isEngineOn _target}; + moveOut _player; + if (_preserveEngineOn) then {_target engineOn true}; + + // moveIn right after moveOut doesn't work in MP for non-local vehicles, player just stays out + // so we have to wait some time (e.g. until player is out and turret locality become vehicle locality) + // usually it's done in the same frame for local vehicles/turrets and takes 3-7 frames for non-local, so in MP can look a little lagging + if (IS_MOVED_OUT) exitWith { + LOG("moved out in current frame"); + [_player, _moveInParams] call _moveInCode; + WAIT_IN_OR_MOVE_BACK; + }; + [ + {params ["_target", "_player", "_currentTurret"]; IS_MOVED_OUT}, + { + params ["", "_player", "", "_moveInCode", "_moveInParams", "_moveBackCode", "_moveBackParams", "_damageBlocked"]; + LOG_2("moved out in %1 frames",diag_frameno-GVAR(frame),call {GVAR(frame)=diag_frameno; 0}); + [_player, _moveInParams] call _moveInCode; + WAIT_IN_OR_MOVE_BACK; + }, + [_target, _player, _currentTurret, _moveInCode, _moveInParams, _moveBackCode, _moveBackParams, _damageBlocked] + ] call CBA_fnc_waitUntilAndExecute; +}; + +scopeName "main"; + +params ["_vehicle", "_player"]; + +private _vehicleConfig = configFile >> "CfgVehicles" >> typeOf _vehicle; +private _isInVehicle = _player in _vehicle; +private _fullCrew = fullCrew [_vehicle, "", true]; + +private ["_driverCompartments", "_isDriverIsolated", "_cargoCompartments", "_cargoCompartmentsLast", "_compartment", "_currentTurret", "_moveBackCode", "_moveBackParams"]; + +if (_isInVehicle) then { + _driverCompartments = (_vehicleConfig >> "driverCompartments") call BIS_fnc_getCfgData; + // Air class by default has driverCompartments=0 and cargoCompartments[]={0}, so we have to disable them + _isDriverIsolated = _driverCompartments isEqualTo 0 && {_vehicle isKindOf "Air"}; + TO_COMPARTMENT_STRING(_driverCompartments); + + _cargoCompartments = getArray (_vehicleConfig >> "cargoCompartments"); + { + if !(_x isEqualType "") then { + _cargoCompartments set [_forEachIndex, format ["Compartment%1", _x]]; + }; + } forEach _cargoCompartments; + _cargoCompartmentsLast = count _cargoCompartments - 1; + + // find current compartment + ( + _fullCrew select (_fullCrew findIf {_player == _x select 0}) + ) params ["", "_role", "_cargoIndex", "_turretPath"]; + + _currentTurret = _turretPath; + + switch (_role) do { + case "driver": { + if (_isDriverIsolated) then { + [] breakOut "main"; + }; + _compartment = _driverCompartments; + _moveBackCode = {MOVE_IN_CODE(moveInDriver)}; + _moveBackParams = _vehicle; + }; + case "cargo": { + private _cargoNumber = fullCrew [_vehicle, "cargo", true] findIf {_player == _x select 0}; + _compartment = _cargoCompartments select (_cargoNumber min _cargoCompartmentsLast); + _moveBackCode = {MOVE_IN_CODE(moveInCargo)}; + _moveBackParams = [_vehicle, _cargoIndex]; + }; + default { + private _turretConfig = [_vehicleConfig, _turretPath] call CBA_fnc_getTurret; + _compartment = (_turretConfig >> "gunnerCompartments") call BIS_fnc_getCfgData; + TO_COMPARTMENT_STRING(_compartment); + _moveBackCode = {MOVE_IN_CODE(moveInTurret)}; + _moveBackParams = [_vehicle, _turretPath]; + }; + }; + TRACE_6("current",_role,_cargoIndex,_turretPath,_compartment,_driverCompartments,_cargoCompartments); +}; + +private _actions = []; +private _cargoNumber = -1; +{ + scopeName "crewLoop"; + _x params ["_unit", "_role", "_cargoIndex", "_turretPath", "_isPersonTurret"]; + if (!isNull _unit && {alive _unit}) then { + if (_role == "cargo") then { + INC(_cargoNumber); + }; + } else { + private ["_name", "_icon", "_statement", "_params"]; + switch (toLower _role) do { + case "driver": { + if ( + lockedDriver _vehicle + || {unitIsUAV _vehicle} + || {0 == getNumber (_vehicleConfig >> "hasDriver")} + ) then { + breakTo "crewLoop"; + }; + if (_isInVehicle) then { + if (_compartment != _driverCompartments || {_isDriverIsolated}) then {breakTo "crewLoop"}; + _params = [{MOVE_IN_CODE(moveInDriver)}, _vehicle, _currentTurret, _moveBackCode, _moveBackParams]; + _statement = _fnc_move; + } else { + _params = ["GetInDriver", _vehicle]; + _statement = {_player action (_this select 2)}; + }; + if (_vehicle isKindOf "Air") then { + _name = localize "str_getin_pos_pilot"; + _icon = ICON_PILOT; + } else { + _name = localize "str_driver"; + _icon = ICON_DRIVER; + }; + }; + case "cargo": { + INC(_cargoNumber); + if (_vehicle lockedCargo _cargoIndex) then {breakTo "crewLoop"}; + if (_isInVehicle) then { + if (_compartment != (_cargoCompartments select (_cargoNumber min _cargoCompartmentsLast))) then {breakTo "crewLoop"}; + _params = [{MOVE_IN_CODE(moveInCargo)}, [_vehicle, _cargoIndex], _currentTurret, _moveBackCode, _moveBackParams]; + _statement = _fnc_move; + } else { + _params = ["GetInCargo", _vehicle, _cargoNumber]; + _statement = {_player action (_this select 2)}; + }; + _name = format ["%1 %2", localize "str_getin_pos_passenger", _cargoNumber + 1]; + _icon = ICON_CARGO; + }; + default { // all turrets including FFV + if (_vehicle lockedTurret _turretPath) then {breakTo "crewLoop"}; + if (_role == "gunner" && {unitIsUAV _vehicle}) then {breakTo "crewLoop"}; + private _turretConfig = [_vehicleConfig, _turretPath] call CBA_fnc_getTurret; + if (!_isInVehicle) then { + _params = ["GetInTurret", _vehicle, _turretPath]; + _statement = {_player action (_this select 2)}; + } else { + private _gunnerCompartments = (_turretConfig >> "gunnerCompartments") call BIS_fnc_getCfgData; + TO_COMPARTMENT_STRING(_gunnerCompartments); + if (_compartment != _gunnerCompartments) then {breakTo "crewLoop"}; + // due to arma bug the unit is stuck in wrong anim when move in turret with configured enabledByAnimationSource + private _enabledByAnimationSource = getText (_turretConfig >> "enabledByAnimationSource"); + if ( + !("" isEqualTo _enabledByAnimationSource) + && {1 > _vehicle doorPhase _enabledByAnimationSource} + ) then {breakTo "crewLoop"}; + _params = [{MOVE_IN_CODE(moveInTurret)}, [_vehicle, _turretPath], _currentTurret, _moveBackCode, _moveBackParams, _enabledByAnimationSource]; + _statement = _fnc_move; + }; + _name = getText (_turretConfig >> "gunnerName"); + _icon = switch true do { + case (0 < getNumber (_turretConfig >> "isCopilot")): {ICON_PILOT}; + case (_role == "gunner"): {ICON_GUNNER}; + case (_role == "commander"): {ICON_COMMANDER}; + case (_isPersonTurret): {ICON_FFV}; + case ("" isEqualTo getText (_turretConfig >> "gun")): {ICON_CARGO}; + default {ICON_TURRET}; + }; + }; + }; + private _action = [ + format ["%1%2%3", _role, _cargoIndex, _turretPath], + _name, _icon, _statement, {true}, {}, _params + ] call EFUNC(interact_menu,createAction); + _actions pushBack [_action, [], _vehicle]; + }; +} forEach _fullCrew; + +_actions diff --git a/addons/quickmount/functions/fnc_canShowFreeSeats.sqf b/addons/quickmount/functions/fnc_canShowFreeSeats.sqf new file mode 100644 index 0000000000..560a036a50 --- /dev/null +++ b/addons/quickmount/functions/fnc_canShowFreeSeats.sqf @@ -0,0 +1,39 @@ +#include "script_component.hpp" +/* + * Author: Dystopian + * Checks if Free Seats menu can be shown. + * + * Arguments: + * 0: Vehicle + * 1: Unit + * + * Return Value: + * Can show menu + * + * Example: + * [cursorObject, player] call ace_quickmount_fnc_canShowFreeSeats + * + * Public: No + */ + +params ["_vehicle", "_unit"]; + +private _isInVehicle = _unit in _vehicle; + +GVAR(enabled) +&& { + GVAR(enableMenu) == 3 + || {_isInVehicle && {GVAR(enableMenu) == 2}} + || {!_isInVehicle && {GVAR(enableMenu) == 1}} +} +&& {alive _vehicle} +&& {2 > locked _vehicle} +&& { + -1 == crew _vehicle findIf {alive _x} + || {0.6 <= side group _unit getFriend side group _vehicle} +} +&& { + 0.3 < vectorUp _vehicle select 2 // moveIn* and GetIn* don't work for flipped vehicles + || {_vehicle isKindOf "Air"} // except Air +} +&& {!([] isEqualTo (_this call FUNC(addFreeSeatsActions)))} // this should be replaced with faster function diff --git a/addons/quickmount/initSettings.sqf b/addons/quickmount/initSettings.sqf index e8049aafc2..f213357167 100644 --- a/addons/quickmount/initSettings.sqf +++ b/addons/quickmount/initSettings.sqf @@ -1,8 +1,8 @@ [ QGVAR(enabled), "CHECKBOX", - localize ELSTRING(common,Enabled), - format ["ACE %1", localize LSTRING(Category)], + ELSTRING(common,Enabled), + format ["ACE %1", LLSTRING(Category)], true, true ] call CBA_settings_fnc_init; @@ -10,8 +10,8 @@ [ QGVAR(distance), "SLIDER", - [localize LSTRING(Distance), localize LSTRING(DistanceDescription)], - format ["ACE %1", localize LSTRING(Category)], + [LSTRING(Distance), LSTRING(DistanceDescription)], + format ["ACE %1", LLSTRING(Category)], [0, 10, DEFAULT_DISTANCE, 0], true ] call CBA_settings_fnc_init; @@ -19,8 +19,8 @@ [ QGVAR(speed), "SLIDER", - [localize LSTRING(Speed), localize LSTRING(SpeedDescription)], - format ["ACE %1", localize LSTRING(Category)], + [LSTRING(Speed), LSTRING(SpeedDescription)], + format ["ACE %1", LLSTRING(Category)], [0, 30, DEFAULT_SPEED, 0], true ] call CBA_settings_fnc_init; @@ -28,9 +28,25 @@ [ QGVAR(priority), "LIST", - [localize LSTRING(Priority), localize LSTRING(PriorityDescription)], - format ["ACE %1", localize LSTRING(Category)], - [[0, 1, 2, 3], [localize "str_getin_pos_driver", localize "str_getin_pos_gunn", localize "str_getin_pos_comm", localize "str_getin_pos_passenger"], DEFAULT_PRIORITY], + [LSTRING(Priority), LSTRING(PriorityDescription)], + format ["ACE %1", LLSTRING(Category)], + [[0, 1, 2, 3], ["str_getin_pos_driver", "str_getin_pos_gunn", "str_getin_pos_comm", "str_getin_pos_passenger"], DEFAULT_PRIORITY], false ] call CBA_settings_fnc_init; +[ + QGVAR(enableMenu), + "LIST", + ELSTRING(interact_menu,Category_InteractionMenu), + format ["ACE %1", LLSTRING(Category)], + [ + [0,1,2,3], + [ + "STR_A3_None", + "STR_rscMenu.hppRscGroupRootMenu_Items_GetIn1", + LSTRING(ChangeSeat), + "str_word_all" + ], + 3 + ] +] call CBA_settings_fnc_init; diff --git a/addons/quickmount/stringtable.xml b/addons/quickmount/stringtable.xml index 2e1a6b9743..941aad3f60 100644 --- a/addons/quickmount/stringtable.xml +++ b/addons/quickmount/stringtable.xml @@ -111,5 +111,9 @@ Priorytet pozycji w pojeździe 탑승할 좌석의 우선순위를 지정합니다. + + Change seat + Пересесть +