diff --git a/.gitignore b/.gitignore index 25afacd0..cf8be52f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ hemtt hemtt.exe .hemtt/local +.hemtt/missions/~* symbols/* # Bikey diff --git a/.hemtt/launch.toml b/.hemtt/launch.toml index fe4bfeed..1468aff1 100644 --- a/.hemtt/launch.toml +++ b/.hemtt/launch.toml @@ -2,11 +2,14 @@ workshop = [ "450814997", # CBA_A3 "2369477168", # ADT + "1779063631", # ZEN ] parameters = [ "-name=dev", + "-noPause", "-window", ] +mission = "test.Stratis" [ace] extends = "default" diff --git a/.hemtt/missions/test.Stratis/cba_settings.sqf b/.hemtt/missions/test.Stratis/cba_settings.sqf new file mode 100644 index 00000000..a4bc22d7 --- /dev/null +++ b/.hemtt/missions/test.Stratis/cba_settings.sqf @@ -0,0 +1,4 @@ + +// ArmaForces - Respawn +force afm_respawn_enabled = true; +force afm_respawn_time = 60; diff --git a/.hemtt/missions/test.Stratis/description.ext b/.hemtt/missions/test.Stratis/description.ext new file mode 100644 index 00000000..d248bb4a --- /dev/null +++ b/.hemtt/missions/test.Stratis/description.ext @@ -0,0 +1,7 @@ + +cba_settings_hasSettingsFile = 1; + +respawn = 3; +respawnDelay = 1e10; +respawnOnStart = -1; +respawnTemplates[] = {"afm_respawn_default", "Counter"}; diff --git a/.hemtt/missions/test.Stratis/mission.sqm b/.hemtt/missions/test.Stratis/mission.sqm new file mode 100644 index 00000000..a6dbd895 --- /dev/null +++ b/.hemtt/missions/test.Stratis/mission.sqm @@ -0,0 +1,431 @@ +version=54; +class EditorData +{ + moveGridStep=1; + angleGridStep=0.2617994; + scaleGridStep=1; + autoGroupingDist=10; + toggles=1025; + class ItemIDProvider + { + nextID=84; + }; + class MarkerIDProvider + { + nextID=8; + }; + class LayerIndexProvider + { + nextID=18; + }; + class Camera + { + pos[]={2103.7285,49.199692,5677.5742}; + dir[]={-0.24344152,-0.74140024,-0.6253494}; + up[]={-0.26895773,0.67106307,-0.69089508}; + aside[]={-0.93187869,-1.4901161e-08,0.36276996}; + }; +}; +binarizationWanted=0; +sourceName="test"; +addons[]= +{ + "A3_Characters_F", + "afm_respawn", + "A3_Modules_F_Curator_Curator" +}; +class AddonsMetaData +{ + class List + { + items=3; + class Item0 + { + className="A3_Characters_F"; + name="Arma 3 Alpha - Characters and Clothing"; + author="Bohemia Interactive"; + url="https://www.arma3.com"; + }; + class Item1 + { + className="afm_respawn"; + name="AFM - respawn"; + author="ArmaForces"; + }; + class Item2 + { + className="A3_Modules_F_Curator"; + name="Arma 3 Zeus Update - Scripted Modules"; + author="Bohemia Interactive"; + url="https://www.arma3.com"; + }; + }; +}; +randomSeed=2670849; +class ScenarioData +{ + author="veteran29"; +}; +class CustomAttributes +{ + class Category0 + { + name="Scenario"; + class Attribute0 + { + property="cba_settings_hasSettingsFile"; + expression="false"; + class Value + { + class data + { + singleType="BOOL"; + value=1; + }; + }; + }; + nAttributes=1; + }; +}; +class Mission +{ + class Intel + { + timeOfChanges=1800.0002; + startWeather=0.30000001; + startWind=0.1; + startWaves=0.1; + forecastWeather=0.30000001; + forecastWind=0.1; + forecastWaves=0.1; + forecastLightnings=0.1; + wavesForced=1; + windForced=1; + year=2035; + month=7; + day=6; + hour=12; + minute=0; + startFogDecay=0.014; + forecastFogDecay=0.014; + }; + class Entities + { + items=6; + class Item0 + { + dataType="Group"; + side="West"; + class Entities + { + items=1; + class Item0 + { + dataType="Object"; + class PositionInfo + { + position[]={2093.6479,6.6836753,5649.085}; + angles[]={0,3.8437212,0}; + }; + side="West"; + flags=7; + class Attributes + { + name="zeus"; + isPlayer=1; + }; + id=75; + type="B_Soldier_SL_F"; + class CustomAttributes + { + class Attribute0 + { + property="speaker"; + expression="_this setspeaker _value;"; + class Value + { + class data + { + singleType="STRING"; + value="Male11ENG"; + }; + }; + }; + class Attribute1 + { + property="pitch"; + expression="_this setpitch _value;"; + class Value + { + class data + { + singleType="SCALAR"; + value=1; + }; + }; + }; + nAttributes=2; + }; + }; + }; + class Attributes + { + name="respawn_group"; + }; + id=74; + }; + class Item1 + { + dataType="Group"; + side="West"; + class Entities + { + items=1; + class Item0 + { + dataType="Object"; + class PositionInfo + { + position[]={2124.301,6.2714386,5664.3506}; + angles[]={0,3.4975924,0}; + }; + side="West"; + flags=7; + class Attributes + { + isPlayable=1; + }; + id=78; + type="B_Soldier_SL_F"; + class CustomAttributes + { + class Attribute0 + { + property="speaker"; + expression="_this setspeaker _value;"; + class Value + { + class data + { + singleType="STRING"; + value="Male06ENG"; + }; + }; + }; + class Attribute1 + { + property="pitch"; + expression="_this setpitch _value;"; + class Value + { + class data + { + singleType="SCALAR"; + value=1.01; + }; + }; + }; + nAttributes=2; + }; + }; + }; + class Attributes + { + }; + id=77; + }; + class Item2 + { + dataType="Logic"; + class PositionInfo + { + position[]={2085.355,6.6999998,5638.686}; + }; + name="module_respawnWest"; + id=79; + type="afm_respawn_moduleRespawn"; + class CustomAttributes + { + class Attribute0 + { + property="afm_respawn_applyTo"; + expression="_this setVariable ['afm_respawn_applyTo',_value,true];"; + class Value + { + class data + { + singleType="SCALAR"; + value=1; + }; + }; + }; + class Attribute1 + { + property="afm_respawn_filter"; + expression="_this setVariable ['afm_respawn_filter', compileFinal _value];"; + class Value + { + class data + { + singleType="STRING"; + value="(_this#0) in units respawn_group"; + }; + }; + }; + nAttributes=2; + }; + }; + class Item3 + { + dataType="Trigger"; + position[]={2085.5859,6.6999946,5621.6021}; + angle=0.21598445; + class Attributes + { + onActivation="systemChat ""Respawn trigger"";"; + sizeA=33.768002; + sizeB=40.047001; + sizeC=5; + timeout[]={5,5,5}; + interuptable=1; + activationType="NOT PRESENT"; + activationBy="WEST"; + isRectangle=1; + isServerOnly=1; + }; + id=81; + type="EmptyDetectorArea10x10"; + atlOffset=-0.026000023; + }; + class Item4 + { + dataType="Logic"; + class PositionInfo + { + position[]={2030.312,9.391324,5636.2998}; + angles[]={6.2033539,0,0.0075049158}; + }; + name="module_respawnEast"; + id=82; + type="afm_respawn_moduleRespawn"; + class CustomAttributes + { + class Attribute0 + { + property="afm_respawn_applyTo"; + expression="_this setVariable ['afm_respawn_applyTo',_value,true];"; + class Value + { + class data + { + singleType="SCALAR"; + value=0; + }; + }; + }; + class Attribute1 + { + property="afm_respawn_filter"; + expression="_this setVariable ['afm_respawn_filter', compileFinal _value];"; + class Value + { + class data + { + singleType="STRING"; + value="true"; + }; + }; + }; + nAttributes=2; + }; + }; + class Item5 + { + dataType="Logic"; + class PositionInfo + { + position[]={2030.788,6.292222,5623.8281}; + }; + id=83; + type="ModuleCurator_F"; + atlOffset=4.7683716e-07; + class CustomAttributes + { + class Attribute0 + { + property="ModuleCurator_F_Owner"; + expression="_this setVariable ['Owner',_value,true];"; + class Value + { + class data + { + singleType="STRING"; + value="zeus"; + }; + }; + }; + class Attribute1 + { + property="ModuleCurator_F_Forced"; + expression="_this setVariable ['Forced',_value,true];"; + class Value + { + class data + { + singleType="SCALAR"; + value=0; + }; + }; + }; + class Attribute2 + { + property="ModuleCurator_F_Name"; + expression="_this setVariable ['Name',_value,true];"; + class Value + { + class data + { + singleType="STRING"; + value=""; + }; + }; + }; + class Attribute3 + { + property="ModuleCurator_F_Addons"; + expression="_this setVariable ['Addons',_value,true];"; + class Value + { + class data + { + singleType="SCALAR"; + value=3; + }; + }; + }; + nAttributes=4; + }; + }; + }; + class Connections + { + class LinkIDProvider + { + nextID=1; + }; + class Links + { + items=1; + class Item0 + { + linkID=0; + item0=81; + item1=79; + class CustomData + { + type="Sync"; + }; + }; + }; + }; +}; diff --git a/.vscode/settings.json b/.vscode/settings.json index 568d9e46..dc2ecbfb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "sqf.enableCBA": true, "sqf.enableACE3": true, "files.associations": { - "*.hpp": "ext" + "*.hpp": "arma-config" }, "sqflint.ignoredVariables": [ "_input0", diff --git a/addons/common/CfgVehicles.hpp b/addons/common/CfgVehicles.hpp new file mode 100644 index 00000000..3b8d3eb6 --- /dev/null +++ b/addons/common/CfgVehicles.hpp @@ -0,0 +1,20 @@ +class CfgVehicles { + class Logic; + class Module_F: Logic { + class AttributesBase { + class Default; + class GVAR(codeBig): Default { + control = "EditCodeMulti5"; + typeName = "STRING"; + expression = "_this setVariable ['%s', compileFinal _value];"; + validate = "none"; + // these seem buggy, _this is a control during validation and breaks it + // validate = "expression"; + // validate = "condition"; + }; + class GVAR(codeSmall): GVAR(codeBig) { + control = "EditCodeMulti3"; + }; + }; + }; +}; diff --git a/addons/common/config.cpp b/addons/common/config.cpp index 422099fb..4b4833ca 100644 --- a/addons/common/config.cpp +++ b/addons/common/config.cpp @@ -15,4 +15,5 @@ class CfgPatches { }; #include "CfgEventHandlers.hpp" +#include "CfgVehicles.hpp" #include "ui\RscModal.hpp" diff --git a/addons/main/script_debug.hpp b/addons/main/script_debug.hpp index cea601d3..2f4f32f9 100644 --- a/addons/main/script_debug.hpp +++ b/addons/main/script_debug.hpp @@ -15,7 +15,7 @@ };\ PREFIX##_PREP_RECOMPILE_BUTTON = true;\ } - #define PREP_RECOMPILE_START if (isNil 'PREFIX##_PREP_RECOMPILE') then {PREFIX##_RECOMPILES = []; PREFIX##_PREP_RECOMPILE = {{call _x} forEach PREFIX##_RECOMPILES;}}; private _recomp = {INFO('Compiling'); + #define PREP_RECOMPILE_START if (isNil 'PREFIX##_PREP_RECOMPILE') then {PREFIX##_RECOMPILES = []; PREFIX##_PREP_RECOMPILE = {{call _x} forEach PREFIX##_RECOMPILES; diag_frameNo}}; private _recomp = {INFO('Compiling'); #define PREP_RECOMPILE_END }; call _recomp; PREFIX##_RECOMPILES pushBack _recomp; PREP_RECOMPILE_ADD_BUTTON; #else #define LINKFUNC(x) FUNC(x) diff --git a/addons/respawn/CfgVehicles.hpp b/addons/respawn/CfgVehicles.hpp new file mode 100644 index 00000000..efc58a38 --- /dev/null +++ b/addons/respawn/CfgVehicles.hpp @@ -0,0 +1,83 @@ +class CfgVehicles { + class Logic; + class Module_F: Logic { + class AttributesBase { + class Combo; + class EGVAR(common,codeSmall); + class ModuleDescription; + }; + + class ModuleDescription { + class EmptyDetector; + class Condition; + }; + }; + + class GVAR(moduleRespawn): Module_F { + scope = 2; + + displayName = CSTRING(ModuleRespawn_DisplayName); + category = QUOTE(PREFIX); + icon = "\a3\Modules_f\data\iconRespawn_ca.paa"; + portrait = "\a3\Modules_f\data\portraitRespawn_ca.paa"; + + function = QFUNC(moduleRespawn); + isGlobal = 0; + isTriggerActivated = 1; + isDisposable = 1; + + class Attributes: AttributesBase { + + class GVAR(applyTo): Combo { + displayName = CSTRING(ModuleRespawn_Attribute_ApplyTo_DisplayName); + + property = QGVAR(applyTo); + typeName = "NUMBER"; + + class Values { + class EAST { + name = "$STR_A3_CfgGroups_East0"; + value = 0; + }; + class WEST { + name = "$STR_A3_CfgGroups_West0"; + value = 1; + default = 1; + }; + class INDEP { + name = "$STR_A3_CfgGroups_Indep0"; + value = 2; + }; + class CIVILIAN { + name = "$STR_Civilian"; + value = 2; + }; + }; + }; + + class GVAR(filter): EGVAR(common,codeSmall) { + displayName = CSTRING(ModuleRespawn_Attribute_Filter_DisplayName); + tooltip = "params [""_unit""]"; + + property = QGVAR(filter); + defaultValue = "'true'"; + }; + + class ModuleDescription: ModuleDescription {}; + }; + + class ModuleDescription: ModuleDescription { + description[] = { + CSTRING(ModuleRespawn_Description_0), + CSTRING(ModuleRespawn_Description_1), + }; + sync[] = {"EmptyDetector"}; + + duplicate = 1; + direction = 0; + position = 0; + + class EmptyDetector: EmptyDetector {}; + }; + }; +}; diff --git a/addons/respawn/README.md b/addons/respawn/README.md index 506f4f07..5cf23118 100644 --- a/addons/respawn/README.md +++ b/addons/respawn/README.md @@ -2,6 +2,17 @@ Respawn template with live customization available through CBA settings. +### Enabling in a mission + +Following config needs to be added to a mission config to allow this system to work properly: + +```cpp +respawn = 3; +respawnDelay = 1e10; +respawnOnStart = -1; +respawnTemplates[] = {"afm_respawn_default", "Counter"}; +``` + ## Respawn delay change You can change respawn delay at any time during the mission. If someone was already waiting for respawn, his timer will get adjusted to account for already spent time waiting. diff --git a/addons/respawn/XEH_PREP.hpp b/addons/respawn/XEH_PREP.hpp index cfa63ed9..16b94b99 100644 --- a/addons/respawn/XEH_PREP.hpp +++ b/addons/respawn/XEH_PREP.hpp @@ -5,3 +5,6 @@ PREP(enable); PREP(onPlayerKilled); PREP(onPlayerRespawn); PREP(toggle); +// modules +PREP(addZeusModules); +PREP(moduleRespawn); diff --git a/addons/respawn/XEH_postInit.sqf b/addons/respawn/XEH_postInit.sqf index bcf50a62..50f35ea3 100644 --- a/addons/respawn/XEH_postInit.sqf +++ b/addons/respawn/XEH_postInit.sqf @@ -37,4 +37,6 @@ if (hasInterface) then { [QGVAR(adjustTimeLocal), { _this call FUNC(adjustTimeLocal); }] call CBA_fnc_addEventHandler; + + [] call FUNC(addZeusModules); }; diff --git a/addons/respawn/config.cpp b/addons/respawn/config.cpp index 2141ea08..07ed49a5 100644 --- a/addons/respawn/config.cpp +++ b/addons/respawn/config.cpp @@ -17,3 +17,4 @@ class CfgPatches { #include "CfgEventHandlers.hpp" #include "CfgRespawnTemplates.hpp" +#include "CfgVehicles.hpp" diff --git a/addons/respawn/functions/fnc_addZeusModules.sqf b/addons/respawn/functions/fnc_addZeusModules.sqf new file mode 100644 index 00000000..937e2d77 --- /dev/null +++ b/addons/respawn/functions/fnc_addZeusModules.sqf @@ -0,0 +1,80 @@ +#include "script_component.hpp" +/* + * Author: veteran29 + * Adds custom modules to Zeus via Zeus Enhanced framework. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Public: No + */ + +if (isNil "zen_custom_modules_fnc_register" || !isMultiplayer) exitWith {}; + +LOG("Adding ZEN modules"); + +private _iconRespawn = getText (configFile >> "CfgVehicles" >> QGVAR(moduleRespawn) >> "portrait"); + +[ELSTRING(Main,Category), LSTRING(ModuleRespawn_ZEN_Sides_DisplayName), { + [ + LSTRING(ModuleRespawn_ZEN_Sides_DisplayName), + [ + [ + "SIDES", + LSTRING(ModuleRespawn_ZEN_Sides_AttributeSides_DisplayName), + [], + false + ] + ], + { + params ["_dialogValues"]; + _dialogValues params ["_sides"]; + TRACE_1("",_dialogValues); + + private _players = allPlayers select {side group _x in _sides && !alive _x}; + + format [LLSTRING(ModuleRespawn_Notification_RespawningMany), count _players] call CBA_fnc_notify; + [QGVAR(force), nil, _players] call CBA_fnc_targetEvent; + } + ] call zen_dialog_fnc_create; +}, _iconRespawn] call zen_custom_modules_fnc_register; + +[ELSTRING(Main,Category), LSTRING(ModuleRespawn_ZEN_Player_DisplayName), { + private _deadPlayers = allPlayers select {!alive _x}; + private _deadPlayersNames = _deadPlayers apply {format ["%1 (%2)", name _x, side group _x]}; + + if (_deadPlayers isEqualTo []) exitWith { + [objNull, LLSTRING(ModuleRespawn_Notification_NoDead)] call BIS_fnc_showCuratorFeedbackMessage; + }; + + [ + LSTRING(ModuleRespawn_ZEN_Player_DisplayName), + [ + [ + "LIST", + LSTRING(ModuleRespawn_ZEN_Player_AttributeDead_DisplayName), + [ + _deadPlayers, + _deadPlayersNames, + 0, + 10 + ], + true + ] + ], + { + params ["_dialogValues", "_deadPlayers"]; + _dialogValues params ["_selected"]; + TRACE_1("",_dialogValues); + + format [LLSTRING(ModuleRespawn_Notification_RespawningSingle), name _selected] call CBA_fnc_notify; + + [QGVAR(force), nil, _selected] call CBA_fnc_targetEvent; + }, + {}, + _deadPlayers + ] call zen_dialog_fnc_create; +}, _iconRespawn] call zen_custom_modules_fnc_register; diff --git a/addons/respawn/functions/fnc_moduleRespawn.sqf b/addons/respawn/functions/fnc_moduleRespawn.sqf new file mode 100644 index 00000000..9df2881b --- /dev/null +++ b/addons/respawn/functions/fnc_moduleRespawn.sqf @@ -0,0 +1,41 @@ +#define DEBUG_SYNCHRONOUS +#include "script_component.hpp" +/* + * Author: veteran29 + * Respawn players module logic. + * + * Arguments: + * 0: Module + * + * Return Value: + * None + * + * Public: No + */ + +[{ + params ["_logic"]; + + private _synchronizedTriggers = synchronizedObjects _logic select {_x isKindOf "EmptyDetector"}; + if (_synchronizedTriggers isEqualTo []) exitWith { + WARNING_1("No synchronized triggers: %1",_logic); + }; + + private _side = (_logic getVariable QGVAR(applyTo)) call BIS_fnc_sideType; + private _filter = _logic getVariable QGVAR(filter); + + private _units = allPlayers select { + !alive _x + && side group _x == _side + && {[_x] call _filter} + }; + + TRACE_1("",_filter); + INFO_2("Respawning %2 players of side %1:",_side,count _units); + { + diag_log text format [" %1", name _x]; + } forEach _units; + + [QGVAR(force), nil, _units] call CBA_fnc_targetEvent; + +}, _this] call CBA_fnc_directCall; diff --git a/addons/respawn/stringtable.xml b/addons/respawn/stringtable.xml index 8d0ecb5c..389ff685 100644 --- a/addons/respawn/stringtable.xml +++ b/addons/respawn/stringtable.xml @@ -21,5 +21,58 @@ How much time must pass before player will respawn (if respawn is enabled). Jak dużo czasu musi upłynąć by gracz mógł się odrodzić (jeśli odrodzenie jest włączone). + + + Force respawn + Wymuś odrodzenie + + + Apply to + Zastosuj do + + + Filter units code + Kod filtru jednostek + + + Synchronize the module with a trigger to respawn players of the selected side once it's activated. + Zsynchronizuj moduł z triggerem aby odrodzić graczy wybranej strony po jego aktywacji. + + + The list of players can be filtered with filter code. + Lista graczy może zostać odfiltrowana za pomocą kodu filtru. + + + + Respawn sides + Odródź strony + + + Sides to respawn + Strony do odrodzenia + + + + Respawn player + Odródź gracza + + + Dead players: + Martwi gracze: + + + + Respawning %1 + Odradzanie %1 + + + Respawning %1 players + Odradzanie %1 graczy + + + No dead players + Brak martwych graczy + +