Zeus - Add spectator module (#6202)

Co-authored-by: Ozan Eğitmen <ozan@egitmen.net>
Co-authored-by: johnb432 <58661205+johnb432@users.noreply.github.com>
This commit is contained in:
mharis001 2024-05-29 14:48:34 -04:00 committed by GitHub
parent 0034f4b9cc
commit 440b9d5721
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 571 additions and 0 deletions

View File

@ -256,6 +256,12 @@ class CfgVehicles {
displayName = CSTRING(ModuleSimulation_DisplayName); displayName = CSTRING(ModuleSimulation_DisplayName);
function = QFUNC(moduleSimulation); function = QFUNC(moduleSimulation);
}; };
class GVAR(moduleSpectator): GVAR(moduleBase) {
curatorCanAttach = 1;
category = QGVAR(Utility);
displayName = ECSTRING(spectator,Module_DisplayName);
curatorInfoType = QGVAR(RscSpectator);
};
class GVAR(moduleSuicideBomber): GVAR(moduleBase) { class GVAR(moduleSuicideBomber): GVAR(moduleBase) {
curatorCanAttach = 1; curatorCanAttach = 1;
category = QGVAR(AI); category = QGVAR(AI);

View File

@ -34,6 +34,7 @@ PREP(moduleSetMedicalVehicle);
PREP(moduleSetRepairFacility); PREP(moduleSetRepairFacility);
PREP(moduleSetRepairVehicle); PREP(moduleSetRepairVehicle);
PREP(moduleSimulation); PREP(moduleSimulation);
PREP(moduleSpectator);
PREP(moduleSuicideBomber); PREP(moduleSuicideBomber);
PREP(moduleSuppressiveFire); PREP(moduleSuppressiveFire);
PREP(moduleSuppressiveFireLocal); PREP(moduleSuppressiveFireLocal);
@ -56,6 +57,7 @@ PREP(ui_groupSide);
PREP(ui_patrolArea); PREP(ui_patrolArea);
PREP(ui_searchArea); PREP(ui_searchArea);
PREP(ui_setEngineer); PREP(ui_setEngineer);
PREP(ui_spectator);
PREP(ui_suicideBomber); PREP(ui_suicideBomber);
PREP(ui_teleportPlayers); PREP(ui_teleportPlayers);
PREP(ui_toggleFlashlight); PREP(ui_toggleFlashlight);

View File

@ -11,6 +11,7 @@ QGVAR(GlobalSkillAI) addPublicVariableEventHandler FUNC(moduleGlobalSetSkill);
[QGVAR(moduleSearchNearby), CBA_fnc_searchNearby] call CBA_fnc_addEventHandler; [QGVAR(moduleSearchNearby), CBA_fnc_searchNearby] call CBA_fnc_addEventHandler;
[QGVAR(moduleSearchArea), CBA_fnc_taskSearchArea] call CBA_fnc_addEventHandler; [QGVAR(moduleSearchArea), CBA_fnc_taskSearchArea] call CBA_fnc_addEventHandler;
[QGVAR(suppressiveFire), LINKFUNC(moduleSuppressiveFireLocal)] call CBA_fnc_addEventHandler; [QGVAR(suppressiveFire), LINKFUNC(moduleSuppressiveFireLocal)] call CBA_fnc_addEventHandler;
[QGVAR(moduleSpectator), LINKFUNC(moduleSpectator)] call CBA_fnc_addEventHandler;
// Editable object commands must be ran on server, this events are used in the respective module // Editable object commands must be ran on server, this events are used in the respective module
if (isServer) then { if (isServer) then {

View File

@ -99,6 +99,11 @@ class CfgPatches {
QGVAR(moduleLayTrench) QGVAR(moduleLayTrench)
}; };
}; };
class GVAR(spectator): ADDON {
units[] = {
QGVAR(moduleSpectator)
};
};
}; };
class ACE_Curator { class ACE_Curator {
@ -112,6 +117,7 @@ class ACE_Curator {
GVAR(arsenal) = "ace_arsenal"; GVAR(arsenal) = "ace_arsenal";
GVAR(fire) = "ace_fire"; GVAR(fire) = "ace_fire";
GVAR(trenches) = "ace_trenches"; GVAR(trenches) = "ace_trenches";
GVAR(spectator) = "ace_spectator";
}; };
#include "CfgFactionClasses.hpp" #include "CfgFactionClasses.hpp"

View File

@ -0,0 +1,39 @@
#include "..\script_component.hpp"
/*
* Author: mharis001
* Zeus module function to make the local player an ACE Spectator.
*
* Arguments:
* 0: Force interface <BOOL>
* 1: Hide player <BOOL>
* 2: Sides available to spectate <ARRAY>
* 3: Camera modes available <ARRAY>
* 4: Vision modes available <ARRAY>
*
* Return Value:
* None
*
* Example:
* [true, true, [west], [0, 1, 2], [-2, -1, 0, 1]] call ace_zeus_fnc_moduleSpectator
*
* Public: No
*/
params ["_force", "_hide", "_sides", "_modes", "_visions"];
TRACE_1("params",_this);
// Update sides available to spectate
[_sides, [west, east, independent, civilian] - _sides] call EFUNC(spectator,updateSides);
// Update available camera modes
[_modes, [0, 1, 2] - _modes] call EFUNC(spectator,updateCameraModes);
// Update available vision modes
[_visions, [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7] - _visions] call EFUNC(spectator,updateVisionModes);
// Make unit spectator (close Zeus camera if open)
if (!isNull curatorCamera) then {
(findDisplay 312) closeDisplay 2;
};
[true, _force, _hide] call EFUNC(spectator,setSpectator);

View File

@ -0,0 +1,265 @@
#include "..\script_component.hpp"
/*
* Author: mharis001
* Initializes the "Spectator" Zeus module display.
*
* Arguments:
* 0: spectator controls group <CONTROL>
*
* Return Value:
* None
*
* Example:
* [CONTROL] call ace_zeus_fnc_ui_spectator
*
* Public: No
*/
#define SIDE_IDCs [92540, 92541, 92542, 92543]
#define CAMERA_IDCs [92550, 92551, 92552]
#define VISION_IDCs [92558, 92559, 92560, 92561]
params ["_control"];
private _display = ctrlParent _control;
private _ctrlButtonOK = _display displayCtrl 1; // IDC_OK
private _logic = missionNamespace getVariable ["BIS_fnc_initCuratorAttributes_target", objNull];
TRACE_1("Logic Object",_logic);
_control ctrlRemoveAllEventHandlers "SetFocus";
// Validate module target
private _unit = attachedTo _logic;
TRACE_1("Unit",_unit);
scopeName "Main";
private _fnc_errorAndClose = {
params ["_msg"];
_display closeDisplay 0;
deleteVehicle _logic;
[_msg] call FUNC(showMessage);
breakOut "Main";
};
switch (false) do {
case (["ace_spectator"] call EFUNC(common,isModLoaded)): {
[LSTRING(RequiresAddon)] call _fnc_errorAndClose;
};
case (!isNull _unit): {
[LSTRING(NothingSelected)] call _fnc_errorAndClose;
};
case (_unit isKindOf "CAManBase"): {
[LSTRING(OnlyInfantry)] call _fnc_errorAndClose;
};
case (alive _unit): {
[LSTRING(OnlyAlive)] call _fnc_errorAndClose;
};
case ([_unit, true] call EFUNC(common,isPlayer)): {
[LSTRING(OnlyPlayers)] call _fnc_errorAndClose;
};
};
// Specific onLoad stuff
private _side = side _unit;
// Spectate sides
private _fnc_onSideSelection = {
params ["_ctrl"];
private _display = ctrlParent _ctrl;
if (isNull _display) exitWith {};
private _color = _ctrl getVariable "color";
private _scale = 1;
private _sides = _display getVariable [QGVAR(spectateSides), []];
private _selectedSide = (ctrlIDC _ctrl) - 92540;
// Add or remove from spectatable sides and update color and scale
if (_selectedSide in _sides) then {
_display setVariable [QGVAR(spectateSides), _sides - [_selectedSide]];
_color set [3, 0.5];
} else {
_display setVariable [QGVAR(spectateSides), _sides + [_selectedSide]];
_color set [3, 1];
_scale = 1.2;
};
_ctrl ctrlSetTextColor _color;
[_ctrl, _scale, 0.1] call BIS_fnc_ctrlSetScale;
};
// Use the unit's side as default
private _activeSide = [east, west, independent, civilian] find _side;
// Handle sides other than default four (sideEnemy)
if (_activeSide != -1) then {
_display setVariable [QGVAR(spectateSides), [_activeSide]];
};
{
private _ctrl = _display displayCtrl _x;
private _side = _x - 92540;
private _color = [_side] call BIS_fnc_sideColor;
_ctrl setVariable ["color", _color];
_ctrl ctrlSetActiveColor _color;
_color set [3, 0.5];
if (_side == _activeSide) then {
[_ctrl, 1.2, 0] call BIS_fnc_ctrlSetScale;
_color set [3, 1];
};
_ctrl ctrlSetTextColor _color;
_ctrl ctrlAddEventHandler ["ButtonClick", _fnc_onSideSelection];
} forEach SIDE_IDCs;
// Camera modes
private _fnc_onModesSelection = {
params ["_ctrl"];
private _display = ctrlParent _ctrl;
if (isNull _display) exitWith {};
private _color = [1, 1, 1, 0.5];
private _scale = 1;
private _modes = _display getVariable [QGVAR(cameraModes), []];
private _selectedMode = (ctrlIDC _ctrl) - 92550;
// Add or remove from camera modes and update color and scale
if (_selectedMode in _modes) then {
_display setVariable [QGVAR(cameraModes), _modes - [_selectedMode]];
} else {
_display setVariable [QGVAR(cameraModes), _modes + [_selectedMode]];
_color set [3, 1];
_scale = 1.2;
};
_ctrl ctrlSetTextColor _color;
[_ctrl, _scale, 0.1] call BIS_fnc_ctrlSetScale;
};
// Use setting as default since global variable will change
private _availableModes = [[0, 1, 2], [1, 2], [0], [1], [2]] select EGVAR(spectator,restrictModes);
_display setVariable [QGVAR(cameraModes), _availableModes];
{
private _ctrl = _display displayCtrl _x;
private _color = [1, 1, 1, 0.5];
if ((_x - 92550) in _availableModes) then {
[_ctrl, 1.2, 0] call BIS_fnc_ctrlSetScale;
_color set [3, 1];
};
_ctrl ctrlSetTextColor _color;
_ctrl ctrlAddEventHandler ["ButtonClick", _fnc_onModesSelection];
} forEach CAMERA_IDCs;
// Vision Modes
private _fnc_onVisionSelection = {
params ["_ctrl", "_state"];
private _display = ctrlParent _ctrl;
if (isNull _display) exitwith {};
// Convert to boolean since EH returns state as 0 or 1
private _state = [false, true] select _state;
private _visions = _display getVariable [QGVAR(visionModes), []];
private _selectedVision = (ctrlIDC _ctrl) - 92560;
// Add or remove from vision modes
if (_state) then {
_display setVariable [QGVAR(visionModes), _visions + [_selectedVision]];
} else {
_display setVariable [QGVAR(visionModes), _visions - [_selectedVision]];
};
// Handle all checked/unchecked
private _allCheckboxes = VISION_IDCs apply {cbChecked (_display displayCtrl _x)};
if (_allCheckboxes isEqualTo [_state, _state, _state, _state]) then {
(_display displayCtrl 92557) cbSetChecked _state;
};
};
// Use setting as default since global variable will change
private _availableVisions = [[-2,-1,0,1], [-2,-1], [-2,0,1], [-2]] select EGVAR(spectator,restrictVisions);
_display setVariable [QGVAR(visionModes), _availableVisions];
{
private _ctrl = _display displayCtrl _x;
if ((_x - 92560) in _availableVisions) then {
_ctrl cbSetChecked true;
};
_ctrl ctrlAddEventHandler ["CheckedChanged", _fnc_onVisionSelection];
} forEach VISION_IDCs;
// Init all visions checkbox
private _fnc_onVisionsAll = {
params ["_ctrl", "_state"];
private _display = ctrlParent _ctrl;
if (isNull _display) exitWith {};
// Convert to boolean since EH returns state as 0 or 1
_state = _state == 1;
// Set state of all checkboxes
{
(_display displayCtrl _x) cbSetChecked _state;
} forEach VISION_IDCs;
// Store new visions mode setting
private _setting = [[], [-2, -1, 0, 1]] select _state;
_display setVariable [QGVAR(visionModes), _setting];
};
private _allCheckbox = _display displayCtrl 92557;
// Set to checked by default if setting is all vision modes
if (_availableVisions isEqualTo [-2, -1, 0, 1]) then {
_allCheckbox cbSetChecked true;
};
_allCheckbox ctrlAddEventHandler ["CheckedChanged", _fnc_onVisionsAll];
// Confirm and Cancel
private _fnc_onUnload = {
private _logic = missionNamespace getVariable ["BIS_fnc_initCuratorAttributes_target", objNull];
if (isNull _logic) exitWith {};
deleteVehicle _logic;
};
private _fnc_onConfirm = {
params [["_ctrlButtonOK", controlNull, [controlNull]]];
private _display = ctrlParent _ctrlButtonOK;
if (isNull _display) exitWith {};
private _logic = missionNamespace getVariable ["BIS_fnc_initCuratorAttributes_target", objNull];
if (isNull _logic) exitWith {};
private _unit = attachedTo _logic;
if (isNull _unit) exitWith {};
private _force = lbCurSel (_display displayCtrl 92531) > 0;
private _hide = lbCurSel (_display displayCtrl 92532) > 0;
private _sides = (_display getVariable [QGVAR(spectateSides), []]) apply {_x call BIS_fnc_sideType};
private _modes = _display getVariable [QGVAR(cameraModes), []];
private _visions = _display getVariable [QGVAR(visionModes), []];
[QGVAR(moduleSpectator), [_force, _hide, _sides, _modes, _visions], _unit] call CBA_fnc_targetEvent;
deleteVehicle _logic;
};
_display displayAddEventHandler ["Unload", _fnc_onUnload];
_ctrlButtonOK ctrlAddEventHandler ["ButtonClick", _fnc_onConfirm];

View File

@ -1097,6 +1097,9 @@
<Chinese>需要一個不存在的插件</Chinese> <Chinese>需要一個不存在的插件</Chinese>
<Korean>현재 없는 애드온을 필요로 합니다</Korean> <Korean>현재 없는 애드온을 필요로 합니다</Korean>
</Key> </Key>
<Key ID="STR_ACE_Zeus_OnlyPlayers">
<English>Only Players</English>
</Key>
<Key ID="STR_ACE_Zeus_None"> <Key ID="STR_ACE_Zeus_None">
<English>None</English> <English>None</English>
<German>Keiner</German> <German>Keiner</German>
@ -1993,5 +1996,26 @@
<Russian>+SHIFT на принудительное (может укладываться только на Север/Юг или Восток/Запад)</Russian> <Russian>+SHIFT на принудительное (может укладываться только на Север/Юг или Восток/Запад)</Russian>
<Spanish>+SHIFT para forzar (Puede solo colocar en N/S or E/O)</Spanish> <Spanish>+SHIFT para forzar (Puede solo colocar en N/S or E/O)</Spanish>
</Key> </Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_ForceInterface_Tooltip">
<English>Forces the spectator interface preventing the player from closing it with the Escape key</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_HidePlayer">
<English>Hide player</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_HidePlayer_Tooltip">
<English>Hides the player by making them invisible, invulnerable, muted, and removing them from their group</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_SpectableSides_Tooltip">
<English>Sets the sides that are available to spectate</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_WhiteHot">
<English>White Hot</English>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSpectator_BlackHot">
<English>Black Hot</English>
</Key>
<Key ID="STR_ACE_Zeus_ToggleAll">
<English>Toggle All</English>
</Key>
</Package> </Package>
</Project> </Project>

View File

@ -915,3 +915,231 @@ class GVAR(RscSuicideBomber): RscDisplayAttributes {
class ButtonCancel: ButtonCancel {}; class ButtonCancel: ButtonCancel {};
}; };
}; };
class GVAR(RscSpectator): RscDisplayAttributes {
onLoad = QUOTE([ARR_3('onLoad',_this,QQGVAR(RscSpectator))] call FUNC(zeusAttributes));
onUnload = QUOTE([ARR_3('onUnload',_this,QQGVAR(RscSpectator))] call FUNC(zeusAttributes));
class Controls: Controls {
class Background: Background {};
class Title: Title {};
class Content: Content {
class Controls {
class spectator: RscControlsGroupNoScrollbars {
onSetFocus = QUOTE(_this call FUNC(ui_spectator));
idc = 92530;
x = 0;
y = 0;
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(10.7));
class controls {
class ForceInterfaceLabel: RscText {
idc = -1;
text = "$STR_a3_cfgvehicles_modulecurator_f_arguments_forced";
tooltip = CSTRING(ModuleSpectator_ForceInterface_Tooltip);
x = 0;
y = 0;
w = QUOTE(W_PART(10));
h = QUOTE(H_PART(1));
colorBackground[] = {0, 0, 0, 0.5};
};
class ForceInterface: ctrlToolbox {
idc = 92531;
x = QUOTE(W_PART(10.1));
y = 0;
w = QUOTE(W_PART(15.9));
h = QUOTE(H_PART(1));
rows = 1;
columns = 2;
strings[] = {ECSTRING(common,No), ECSTRING(common,Yes)};
};
class HidePlayerLabel: ForceInterfaceLabel {
text = CSTRING(ModuleSpectator_HidePlayer);
tooltip = CSTRING(ModuleSpectator_HidePlayer_Tooltip);
y = QUOTE(H_PART(1.1));
};
class HidePlayer: ForceInterface {
idc = 92532;
y = QUOTE(H_PART(1.1));
};
class SpectateSides: RscControlsGroupNoScrollbars {
idc = 92533;
x = 0;
y = QUOTE(H_PART(2.2));
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(2.5));
class controls {
class Label: RscText {
idc = -1;
text = "$STR_A3_Spectator_Eden_WhitelistedSides_Name";
tooltip = CSTRING(ModuleSpectator_SpectableSides_Tooltip);
x = 0;
y = 0;
w = QUOTE(W_PART(10));
h = QUOTE(H_PART(2.5));
colorBackground[] = {0, 0, 0, 0.5};
};
class Background: RscText {
idc = -1;
x = QUOTE(W_PART(10));
y = 0;
w = QUOTE(W_PART(16));
h = QUOTE(H_PART(2.5));
colorBackground[] = {1, 1, 1, 0.1};
};
class BLUFOR: RscActivePicture {
idc = 92541;
text = "\a3\Ui_F_Curator\Data\Displays\RscDisplayCurator\side_west_ca.paa";
x = QUOTE(W_PART(12.5));
y = QUOTE(H_PART(0.25));
w = QUOTE(W_PART(2));
h = QUOTE(H_PART(2));
tooltip = "$STR_WEST";
};
class OPFOR: BLUFOR {
idc = 92540;
text = "\a3\Ui_F_Curator\Data\Displays\RscDisplayCurator\side_east_ca.paa";
x = QUOTE(W_PART(15.5));
tooltip = "$STR_EAST";
};
class Independent: BLUFOR {
idc = 92542;
text = "\a3\Ui_F_Curator\Data\Displays\RscDisplayCurator\side_guer_ca.paa";
x = QUOTE(W_PART(18.5));
tooltip = "$STR_guerrila";
};
class Civilian: BLUFOR {
idc = 92543;
text = "\a3\Ui_F_Curator\Data\Displays\RscDisplayCurator\side_civ_ca.paa";
x = QUOTE(W_PART(21.5));
tooltip = "$STR_Civilian";
};
};
};
class CameraModes: RscControlsGroupNoScrollbars {
idc = 92534;
x = 0;
y = QUOTE(H_PART(4.8));
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(2.5));
class controls {
class Label: RscText {
idc = -1;
text = ECSTRING(spectator,modes_DisplayName);
tooltip = ECSTRING(spectator,modes_Description);
x = 0;
y = 0;
w = QUOTE(W_PART(10));
h = QUOTE(H_PART(2.5));
colorBackground[] = {0, 0, 0, 0.5};
};
class Background: RscText {
idc = -1;
x =QUOTE(W_PART(10));
y = 0;
w = QUOTE(W_PART(16));
h = QUOTE(H_PART(2.5));
colorBackground[] = {1, 1, 1, 0.1};
};
class Free: RscActivePicture {
idc = 92550;
text = "a3\Ui_f\data\GUI\Rsc\RscDisplayEGSpectator\Free.paa";
x = QUOTE(W_PART(13.375));
y = QUOTE(H_PART(0.375));
w = QUOTE(W_PART(1.75));
h = QUOTE(H_PART(1.75));
tooltip = "$STR_A3_Spectator_free_camera_tooltip";
};
class Follow: Free {
idc = 92552;
text = "a3\Ui_f\data\GUI\Rsc\RscDisplayEGSpectator\Follow.paa";
x = QUOTE(W_PART(17.125));
tooltip = "$STR_A3_Spectator_3pp_camera_tooltip";
};
class FirstPerson: Free {
idc = 92551;
text = "a3\Ui_f\data\GUI\Rsc\RscDisplayEGSpectator\Fps.paa";
x = QUOTE(W_PART(20.875));
tooltip = "$STR_A3_Spectator_1pp_camera_tooltip";
};
};
};
class VisionModes: RscControlsGroupNoScrollbars {
idc = 92535;
x = 0;
y = QUOTE(H_PART(7.4));
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(3.3));
class controls {
class Label: RscText {
idc = -1;
text = ECSTRING(spectator,visions_DisplayName);
tooltip = ECSTRING(spectator,visions_Description);
x = 0;
y = 0;
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(1));
colorBackground[] = {0, 0, 0, 0.5};
};
class Background: RscText {
idc = -1;
x = 0;
y = QUOTE(H_PART(1));
w = QUOTE(W_PART(26));
h = QUOTE(H_PART(2.3));
colorBackground[] = {1, 1, 1, 0.1};
};
class AllCheckBox: RscCheckBox {
idc = 92557;
tooltip = CSTRING(ToggleAll);
x = QUOTE(W_PART(25));
y = 0;
w = QUOTE(W_PART(1));
h = QUOTE(H_PART(1));
};
class NormalLabel: Label {
text = "$STR_speed_normal";
tooltip = "";
x = QUOTE(W_PART(1));
y = QUOTE(H_PART(1.1));
w = QUOTE(W_PART(10.8));
colorBackground[] = {0, 0, 0, 0.6};
};
class Normal: AllCheckBox {
idc = 92558;
x = QUOTE(W_PART(11.9));
y = QUOTE(H_PART(1.1));
};
class NightVisionLabel: NormalLabel {
text = "$STR_usract_night_vision";
y = QUOTE(H_PART(2.2));
};
class NightVision: Normal {
idc = 92559;
y = QUOTE(H_PART(2.2));
};
class WhiteHotLabel: NormalLabel {
text = CSTRING(ModuleSpectator_WhiteHot);
x = QUOTE(W_PART(13.1));
};
class WhiteHot: Normal {
idc = 92560;
x = QUOTE(W_PART(24));
};
class BlackHotLabel: WhiteHotLabel {
text = CSTRING(ModuleSpectator_BlackHot);
y = QUOTE(Y_PART(2.2));
};
class BlackHot: WhiteHot {
idc = 92561;
y = QUOTE(H_PART(2.2));
};
};
};
};
};
};
};
class ButtonOK: ButtonOK {};
class ButtonCancel: ButtonCancel {};
};
};