From c5edc00ee94977b342823c910926a6ee605a6883 Mon Sep 17 00:00:00 2001 From: jonpas Date: Fri, 22 Jul 2016 18:06:34 +0200 Subject: [PATCH 1/4] Add Tagging framework and improvements --- addons/tagging/ACE_Tags.hpp | 26 ++++++ addons/tagging/CfgVehicles.hpp | 34 ++------ addons/tagging/XEH_PREP.hpp | 4 +- addons/tagging/XEH_postInit.sqf | 56 +++++++++++++ addons/tagging/XEH_preInit.sqf | 3 + addons/tagging/config.cpp | 1 + addons/tagging/functions/fnc_addCustomTag.sqf | 54 ++++++++++++ .../tagging/functions/fnc_addTagActions.sqf | 45 ++++++++++ .../tagging/functions/fnc_checkTaggable.sqf | 19 +++-- addons/tagging/functions/fnc_getTexture.sqf | 26 ------ addons/tagging/functions/fnc_tagRandom.sqf | 23 +++++ addons/tagging/stringtable.xml | 84 +++++++++++-------- 12 files changed, 278 insertions(+), 97 deletions(-) create mode 100644 addons/tagging/ACE_Tags.hpp create mode 100644 addons/tagging/functions/fnc_addCustomTag.sqf create mode 100644 addons/tagging/functions/fnc_addTagActions.sqf delete mode 100644 addons/tagging/functions/fnc_getTexture.sqf create mode 100644 addons/tagging/functions/fnc_tagRandom.sqf diff --git a/addons/tagging/ACE_Tags.hpp b/addons/tagging/ACE_Tags.hpp new file mode 100644 index 0000000000..78762cf888 --- /dev/null +++ b/addons/tagging/ACE_Tags.hpp @@ -0,0 +1,26 @@ +class ACE_Tags { + class ACE_XBlack { + displayName = CSTRING(XBlack); + requiredItem = "ACE_SpraypaintBlack"; + textures[] = {QPATHTOF(UI\tags\black\0.paa), QPATHTOF(UI\tags\black\1.paa), QPATHTOF(UI\tags\black\2.paa)}; + icon = QPATHTOF(UI\icons\iconTaggingBlack.paa); + }; + class ACE_XRed { + displayName = CSTRING(XRed); + requiredItem = "ACE_SpraypaintRed"; + textures[] = {QPATHTOF(UI\tags\red\0.paa), QPATHTOF(UI\tags\red\1.paa), QPATHTOF(UI\tags\red\2.paa)}; + icon = QPATHTOF(UI\icons\iconTaggingRed.paa); + }; + class ACE_XGreen { + displayName = CSTRING(XGreen); + requiredItem = "ACE_SpraypaintGreen"; + textures[] = {QPATHTOF(UI\tags\green\0.paa), QPATHTOF(UI\tags\green\1.paa), QPATHTOF(UI\tags\green\2.paa)}; + icon = QPATHTOF(UI\icons\iconTaggingGreen.paa); + }; + class ACE_XBlue { + displayName = CSTRING(XBlue); + requiredItem = "ACE_SpraypaintBlue"; + textures[] = {QPATHTOF(UI\tags\blue\0.paa), QPATHTOF(UI\tags\blue\1.paa), QPATHTOF(UI\tags\blue\2.paa)}; + icon = QPATHTOF(UI\icons\iconTaggingBlue.paa); + }; +}; diff --git a/addons/tagging/CfgVehicles.hpp b/addons/tagging/CfgVehicles.hpp index 2a71c773a0..f73a2ed3ab 100644 --- a/addons/tagging/CfgVehicles.hpp +++ b/addons/tagging/CfgVehicles.hpp @@ -2,37 +2,17 @@ class CfgVehicles { class Man; class CAManBase: Man { class ACE_SelfActions { - class ACE_Equipment { - class ACE_TagBlack { - displayName = CSTRING(TagBlack); - condition = QUOTE(('ACE_SpraypaintBlack' in items ACE_player) && {[] call FUNC(checkTaggable)}); - statement = QUOTE([ARR_2(ACE_player,'black' call FUNC(getTexture))] call FUNC(tag)); - showDisabled = 0; - priority = 3; - icon = QPATHTOF(UI\icons\iconTaggingBlack.paa); - }; - class ACE_TagRed: ACE_TagBlack { - displayName = CSTRING(TagRed); - condition = QUOTE(('ACE_SpraypaintRed' in items ACE_player) && {[] call FUNC(checkTaggable)}); - statement = QUOTE([ARR_2(ACE_player,'red' call FUNC(getTexture))] call FUNC(tag)); - icon = QPATHTOF(UI\icons\iconTaggingRed.paa); - }; - class ACE_TagGreen: ACE_TagBlack { - displayName = CSTRING(TagGreen); - condition = QUOTE(('ACE_SpraypaintGreen' in items ACE_player) && {[] call FUNC(checkTaggable)}); - statement = QUOTE([ARR_2(ACE_player,'green' call FUNC(getTexture))] call FUNC(tag)); - icon = QPATHTOF(UI\icons\iconTaggingGreen.paa); - }; - class ACE_TagBlue: ACE_TagBlack { - displayName = CSTRING(TagBlue); - condition = QUOTE(('ACE_SpraypaintBlue' in items ACE_player) && {[] call FUNC(checkTaggable)}); - statement = QUOTE([ARR_2(ACE_player,'blue' call FUNC(getTexture))] call FUNC(tag)); - icon = QPATHTOF(UI\icons\iconTaggingBlue.paa); - }; + class ACE_Tags { + displayName = CSTRING(Tag); + condition = QUOTE(_player call FUNC(checkTaggable)); + statement = QUOTE(_player call FUNC(tagRandom)); + icon = QPATHTOF(UI\icons\iconTaggingBlack.paa); + insertChildren = QUOTE(_player call FUNC(addTagActions)); }; }; }; + class Item_Base_F; class ACE_Item_SpraypaintBlack: Item_Base_F { author = "jokoho48"; diff --git a/addons/tagging/XEH_PREP.hpp b/addons/tagging/XEH_PREP.hpp index f8a8598cd7..d794d2717a 100644 --- a/addons/tagging/XEH_PREP.hpp +++ b/addons/tagging/XEH_PREP.hpp @@ -1,5 +1,7 @@ +PREP(addCustomTag); +PREP(addTagActions); PREP(checkTaggable); PREP(createTag); -PREP(getTexture); PREP(tag); +PREP(tagRandom); PREP(tagTestingThread); diff --git a/addons/tagging/XEH_postInit.sqf b/addons/tagging/XEH_postInit.sqf index 055cb59cfd..15b1f66b67 100644 --- a/addons/tagging/XEH_postInit.sqf +++ b/addons/tagging/XEH_postInit.sqf @@ -39,6 +39,62 @@ for "_index" from 0 to (_countOptions - 1) do { }; }; +if (hasInterface) then { + // Cache tags + { + private _failure = false; + private _class = configName _x; + + private _displayName = getText (_x >> "displayName"); + if (_displayName == "") then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing displayName",_class); + _failure = true; + }; + + private _requiredItem = toLower (getText (_x >> "requiredItem")); + if (_requiredItem == "") then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing requiredItem",_class); + _failure = true; + } else { + if (!isClass (configFile >> "CfgWeapons" >> _requiredItem)) then { + ACE_LOGERROR_2("Failed compiling ACE_Tags for tag: %1 - requiredItem %2 does not exist",_class,_requiredItem); + _failure = true; + }; + }; + + private _textures = getArray (_x >> "textures"); + if (_textures isEqualTo []) then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing textures",_class); + _failure = true; + }; + + private _icon = getText (_x >> "icon"); + + if (!_failure) then { + GVAR(cachedTags) pushBack [_class, _displayName, _requiredItem, _textures, _icon]; + if !(_requiredItem in GVAR(cachedRequiredItems)) then { + GVAR(cachedRequiredItems) pushBack _requiredItem; + }; + }; + } forEach ("true" configClasses (configFile >> "ACE_Tags")); + + // Scripted tag adding + [QGVAR(addCustomTag), { + params ["_identifier", "_displayName", "_requiredItem"]; + + // Add only if tag not already added (compare identifiers) + if (GVAR(cachedTags) select {_x select 0 == _identifier} isEqualTo []) then { + GVAR(cachedTags) pushBack _this; + if !(_requiredItem in GVAR(cachedRequiredItems)) then { + GVAR(cachedRequiredItems) pushBack _requiredItem; + }; + TRACE_1("Added custom script tag",_this); + } else { + ACE_LOGINFO_2("Tag with selected identifier already exists: %1 (%2)",_identifier,_displayName) + }; + }] call CBA_fnc_addEventHandler; +}; + if (!isServer) exitWith {}; GVAR(testingThread) = false; diff --git a/addons/tagging/XEH_preInit.sqf b/addons/tagging/XEH_preInit.sqf index a7feade1c3..66458c6995 100644 --- a/addons/tagging/XEH_preInit.sqf +++ b/addons/tagging/XEH_preInit.sqf @@ -4,4 +4,7 @@ ADDON = false; #include "XEH_PREP.hpp" +GVAR(cachedTags) = []; +GVAR(cachedRequiredItems) = []; + ADDON = true; diff --git a/addons/tagging/config.cpp b/addons/tagging/config.cpp index 634f7c57af..f894808ed7 100644 --- a/addons/tagging/config.cpp +++ b/addons/tagging/config.cpp @@ -17,6 +17,7 @@ class CfgPatches { #include "CfgEventHandlers.hpp" #include "CfgVehicles.hpp" #include "CfgWeapons.hpp" +#include "ACE_Tags.hpp" class ACE_newEvents { createTag = QGVAR(createTag); diff --git a/addons/tagging/functions/fnc_addCustomTag.sqf b/addons/tagging/functions/fnc_addCustomTag.sqf new file mode 100644 index 0000000000..9cd17e475a --- /dev/null +++ b/addons/tagging/functions/fnc_addCustomTag.sqf @@ -0,0 +1,54 @@ +/* + * Author: Jonpas + * Adds custom tag. Has to be executed on one machine only. + * + * Arguments: + * 0: Unique Identifier + * 1: Display Name + * 2: Required Item + * 3: Textures Paths + * 4: Icon Path (default: "") + * + * Return Value: + * Sucessfully Added Tag + * + * Example: + * ["ace_victoryRed", "Victory Red", "ACE_SpraypaintRed", ["path\to\texture1.paa", "path\to\texture2.paa"], "path\to\icon.paa"] call ace_tagging_fnc_addCustomTag + * + * Public: Yes + */ +#include "script_component.hpp" + +params [ + ["_identifier", "", [""]], + ["_displayName", "", [""]], + ["_requiredItem", "", [""]], + ["_textures", [], [[]]], + ["_icon", "", [""]] +]; + +// Verify +if (_identifier == "") exitWith { + ACE_LOGERROR("Failed adding custom tag - missing identifier"); +}; + +if (_displayName == "") exitWith { + ACE_LOGERROR_1("Failed adding custom tag: %1 - missing displayName",_identifier); +}; + +if (_requiredItem == "") exitWith { + ACE_LOGERROR_1("Failed adding custom tag: %1 - missing requiredItem",_identifier); +}; +if (!isClass (configFile >> "CfgWeapons" >> _requiredItem)) exitWith { + ACE_LOGERROR_2("Failed adding custom tag: %1 - requiredItem %2 does not exist",_identifier,_requiredItem); +}; + +if (_textures isEqualTo []) exitWith { + ACE_LOGERROR_1("Failed adding custom tag: %1 - missing textures",_identifier); +}; + +_identifier = [_identifier] call EFUNC(common,stringRemoveWhiteSpace); +_requiredItem = toLower _requiredItem; + +// Add +[QGVAR(addCustomTag), [_identifier, _displayName, _requiredItem, _textures, _icon]] call CBA_fnc_globalEventJIP; diff --git a/addons/tagging/functions/fnc_addTagActions.sqf b/addons/tagging/functions/fnc_addTagActions.sqf new file mode 100644 index 0000000000..35624d6d05 --- /dev/null +++ b/addons/tagging/functions/fnc_addTagActions.sqf @@ -0,0 +1,45 @@ +/* + * Author: Jonpas + * Compiles tags from ACE_Tags and returns children actions. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [unit] call ace_tagging_fnc_addTagActions + * + * Public: No + */ +#include "script_component.hpp" + +params ["_unit"]; + +private _actions = []; +{ + _x params ["_class", "_displayName", "_requiredItem", "_textures", "_icon"]; + + _actions pushBack [ + [ + format ["ACE_ConfigTag_%1", _class], + _displayName, + _icon, + { + (_this select 2) params ["_unit", "_textures"]; + [_unit, selectRandom _textures] call FUNC(tag); + }, + { + (_this select 2) params ["_unit", "", "_requiredItem"]; + _requiredItem in ((items _unit) apply {toLower _x}) + }, + {}, + [_unit, _textures, _requiredItem] + ] call EFUNC(interact_menu,createAction), + [], + _unit + ]; +} forEach GVAR(cachedTags); + +_actions diff --git a/addons/tagging/functions/fnc_checkTaggable.sqf b/addons/tagging/functions/fnc_checkTaggable.sqf index b77473f25b..d739bbfec5 100644 --- a/addons/tagging/functions/fnc_checkTaggable.sqf +++ b/addons/tagging/functions/fnc_checkTaggable.sqf @@ -3,26 +3,33 @@ * Checks if there is a taggable surface within 2.5m in front of the player. * * Arguments: - * None + * 0: Unit * * Return Value: - * Is wall taggable + * Is surface taggable * * Example: - * [] call ace_tagging_fnc_checkTaggable + * [unit] call ace_tagging_fnc_checkTaggable * * Public: No */ #include "script_component.hpp" -[[], { - private _startPosASL = eyePos ACE_player; +params ["_unit"]; + +[[_unit], { + params ["_unit"]; + + // Exit if no required item in inventory + if ((GVAR(cachedRequiredItems) arrayIntersect ((items _unit) apply {toLower _x})) isEqualTo []) exitWith {false}; + + private _startPosASL = eyePos _unit; private _cameraPosASL = AGLToASL positionCameraToWorld [0, 0, 0]; private _cameraDir = (AGLToASL positionCameraToWorld [0, 0, 1]) vectorDiff _cameraPosASL; private _endPosASL = _startPosASL vectorAdd (_cameraDir vectorMultiply 2.5); - private _intersections = lineIntersectsSurfaces [_startPosASL, _endPosASL, ACE_player, objNull, true, 1, "FIRE", "GEOM"]; + private _intersections = lineIntersectsSurfaces [_startPosASL, _endPosASL, _unit, objNull, true, 1, "FIRE", "GEOM"]; // If there's no intersections if (_intersections isEqualTo []) exitWith {false}; diff --git a/addons/tagging/functions/fnc_getTexture.sqf b/addons/tagging/functions/fnc_getTexture.sqf deleted file mode 100644 index f7f6d7e5e2..0000000000 --- a/addons/tagging/functions/fnc_getTexture.sqf +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Author: BaerMitUmlaut, esteldunedain, Jonpas - * Puts together a full path to the given tag color texture. Internal ACE3 textures only. - * - * Arguments: - * 0: The colour of the tag (valid colours are black, red, green and blue) - * - * Return Value: - * Texture (full path), "" if not found - * - * Example: - * texture = ["blue"] call ace_tagging_fnc_getTexture - * - * Public: No - */ - -#include "script_component.hpp" - -params ["_color"]; - -if !((toLower _color) in ["black", "red", "green", "blue"]) exitWith { - ACE_LOGERROR_1("%1 is not a valid tag colour.",_color); - "" -}; - -QUOTE(PATHTOF(UI)) + "\tags\" + _color + "\" + str (floor (random 3)) + ".paa" diff --git a/addons/tagging/functions/fnc_tagRandom.sqf b/addons/tagging/functions/fnc_tagRandom.sqf new file mode 100644 index 0000000000..2aa2f66222 --- /dev/null +++ b/addons/tagging/functions/fnc_tagRandom.sqf @@ -0,0 +1,23 @@ +/* + * Author: Jonpas + * Selects random tag and applies it. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call ace_tagging_fnc_tagRandom + * + * Public: No + */ + +#include "script_component.hpp" + +params ["_unit"]; + +private _possibleTags = GVAR(cachedTags) select {(_x select 2) in ((items _unit) apply {toLower _x})}; + +[_unit, selectRandom ((selectRandom _possibleTags) select 3)] call FUNC(tag); diff --git a/addons/tagging/stringtable.xml b/addons/tagging/stringtable.xml index 43f6a1e2d4..2edddb2bf8 100644 --- a/addons/tagging/stringtable.xml +++ b/addons/tagging/stringtable.xml @@ -1,45 +1,55 @@  - - Tag black - Schwarz markieren - Marcar en negro - Oznakuj na czarno - Tag noir - Marca nero - Označit černě - Marcar em preto + + Tag + Markieren + Marcar + Oznakuj + Tag + Marca + Označit + Marcar - - Tag red - Rot markieren - Marcar en rojo - Oznakuj na czerwono - Tag rouge - Marca rosso - Označit červeně - Marcar em vermelho + + X Black + Schwarz X + X en negro + X na czarno + X noir + X nero + X černě + X em preto - - Tag green - Grün markieren - Marcar en verde - Oznakuj na zielono - Tag vert - Marca verde - Označit zeleně - Marcar em verde + + X red + Rot X + X en rojo + X na czerwono + X rouge + X rosso + X červeně + X em vermelho - - Tag blue - Blau markieren - Marcar en azul - Oznakuj na niebiesko - Tag bleu - Marca blu - Označit modře - Marcar em azul + + X green + Grün X + X en verde + X na zielono + X vert + X verde + X zeleně + X em verde + + + X blue + Blau X + X en azul + X na niebiesko + X bleu + X blu + X modře + X em azul Black spray paint @@ -92,4 +102,4 @@ Uma lata de tinta spray para marcar paredes. - \ No newline at end of file + From 9b4f565cc56c6a08495705de4540e4a1d16e3fd0 Mon Sep 17 00:00:00 2001 From: jonpas Date: Sat, 23 Jul 2016 15:52:20 +0200 Subject: [PATCH 2/4] Split cache and EH to functions --- addons/tagging/XEH_PREP.hpp | 2 + addons/tagging/XEH_postInit.sqf | 56 ++----------------- addons/tagging/functions/fnc_addCustomTag.sqf | 2 +- .../tagging/functions/fnc_applyCustomTag.sqf | 33 +++++++++++ .../functions/fnc_compileConfigTags.sqf | 53 ++++++++++++++++++ 5 files changed, 93 insertions(+), 53 deletions(-) create mode 100644 addons/tagging/functions/fnc_applyCustomTag.sqf create mode 100644 addons/tagging/functions/fnc_compileConfigTags.sqf diff --git a/addons/tagging/XEH_PREP.hpp b/addons/tagging/XEH_PREP.hpp index d794d2717a..d5dd3571d0 100644 --- a/addons/tagging/XEH_PREP.hpp +++ b/addons/tagging/XEH_PREP.hpp @@ -1,6 +1,8 @@ PREP(addCustomTag); PREP(addTagActions); +PREP(applyCustomTag); PREP(checkTaggable); +PREP(compileConfigTags); PREP(createTag); PREP(tag); PREP(tagRandom); diff --git a/addons/tagging/XEH_postInit.sqf b/addons/tagging/XEH_postInit.sqf index 15b1f66b67..57b35f2e59 100644 --- a/addons/tagging/XEH_postInit.sqf +++ b/addons/tagging/XEH_postInit.sqf @@ -40,59 +40,11 @@ for "_index" from 0 to (_countOptions - 1) do { }; if (hasInterface) then { - // Cache tags - { - private _failure = false; - private _class = configName _x; + // Compile and cache config tags + call FUNC(compileConfigTags); - private _displayName = getText (_x >> "displayName"); - if (_displayName == "") then { - ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing displayName",_class); - _failure = true; - }; - - private _requiredItem = toLower (getText (_x >> "requiredItem")); - if (_requiredItem == "") then { - ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing requiredItem",_class); - _failure = true; - } else { - if (!isClass (configFile >> "CfgWeapons" >> _requiredItem)) then { - ACE_LOGERROR_2("Failed compiling ACE_Tags for tag: %1 - requiredItem %2 does not exist",_class,_requiredItem); - _failure = true; - }; - }; - - private _textures = getArray (_x >> "textures"); - if (_textures isEqualTo []) then { - ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing textures",_class); - _failure = true; - }; - - private _icon = getText (_x >> "icon"); - - if (!_failure) then { - GVAR(cachedTags) pushBack [_class, _displayName, _requiredItem, _textures, _icon]; - if !(_requiredItem in GVAR(cachedRequiredItems)) then { - GVAR(cachedRequiredItems) pushBack _requiredItem; - }; - }; - } forEach ("true" configClasses (configFile >> "ACE_Tags")); - - // Scripted tag adding - [QGVAR(addCustomTag), { - params ["_identifier", "_displayName", "_requiredItem"]; - - // Add only if tag not already added (compare identifiers) - if (GVAR(cachedTags) select {_x select 0 == _identifier} isEqualTo []) then { - GVAR(cachedTags) pushBack _this; - if !(_requiredItem in GVAR(cachedRequiredItems)) then { - GVAR(cachedRequiredItems) pushBack _requiredItem; - }; - TRACE_1("Added custom script tag",_this); - } else { - ACE_LOGINFO_2("Tag with selected identifier already exists: %1 (%2)",_identifier,_displayName) - }; - }] call CBA_fnc_addEventHandler; + // Scripted tag adding EH + [QGVAR(applyCustomTag), FUNC(applyCustomTag)] call CBA_fnc_addEventHandler; }; if (!isServer) exitWith {}; diff --git a/addons/tagging/functions/fnc_addCustomTag.sqf b/addons/tagging/functions/fnc_addCustomTag.sqf index 9cd17e475a..455b33ffd8 100644 --- a/addons/tagging/functions/fnc_addCustomTag.sqf +++ b/addons/tagging/functions/fnc_addCustomTag.sqf @@ -51,4 +51,4 @@ _identifier = [_identifier] call EFUNC(common,stringRemoveWhiteSpace); _requiredItem = toLower _requiredItem; // Add -[QGVAR(addCustomTag), [_identifier, _displayName, _requiredItem, _textures, _icon]] call CBA_fnc_globalEventJIP; +[QGVAR(applyCustomTag), [_identifier, _displayName, _requiredItem, _textures, _icon]] call CBA_fnc_globalEventJIP; diff --git a/addons/tagging/functions/fnc_applyCustomTag.sqf b/addons/tagging/functions/fnc_applyCustomTag.sqf new file mode 100644 index 0000000000..2dd694c7d8 --- /dev/null +++ b/addons/tagging/functions/fnc_applyCustomTag.sqf @@ -0,0 +1,33 @@ +/* + * Author: Jonpas + * Applies custom tag to the cache. + * + * Arguments: + * 0: Unique Identifier + * 1: Display Name + * 2: Required Item + * 3: Textures Paths + * 4: Icon Path (default: "") + * + * Return Value: + * None + * + * Example: + * ["ace_victoryRed", "Victory Red", "ACE_SpraypaintRed", ["path\to\texture1.paa", "path\to\texture2.paa"], "path\to\icon.paa"] call ace_tagging_fnc_addCustomTagLocal + * + * Public: No + */ +#include "script_component.hpp" + +params ["_identifier", "_displayName", "_requiredItem"]; + +// Add only if tag not already added (compare identifiers) +if (GVAR(cachedTags) select {_x select 0 == _identifier} isEqualTo []) then { + GVAR(cachedTags) pushBack _this; + if !(_requiredItem in GVAR(cachedRequiredItems)) then { + GVAR(cachedRequiredItems) pushBack _requiredItem; + }; + TRACE_1("Added custom script tag",_this); +} else { + ACE_LOGINFO_2("Tag with selected identifier already exists: %1 (%2)",_identifier,_displayName) +}; diff --git a/addons/tagging/functions/fnc_compileConfigTags.sqf b/addons/tagging/functions/fnc_compileConfigTags.sqf new file mode 100644 index 0000000000..cfc90ba413 --- /dev/null +++ b/addons/tagging/functions/fnc_compileConfigTags.sqf @@ -0,0 +1,53 @@ +/* + * Author: Jonpas + * Compiles and caches tags from ACE_Tags config. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call ace_tagging_fnc_compileConfigTags + * + * Public: No + */ +#include "script_component.hpp" + +{ + private _failure = false; + private _class = configName _x; + + private _displayName = getText (_x >> "displayName"); + if (_displayName == "") then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing displayName",_class); + _failure = true; + }; + + private _requiredItem = toLower (getText (_x >> "requiredItem")); + if (_requiredItem == "") then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing requiredItem",_class); + _failure = true; + } else { + if (!isClass (configFile >> "CfgWeapons" >> _requiredItem)) then { + ACE_LOGERROR_2("Failed compiling ACE_Tags for tag: %1 - requiredItem %2 does not exist",_class,_requiredItem); + _failure = true; + }; + }; + + private _textures = getArray (_x >> "textures"); + if (_textures isEqualTo []) then { + ACE_LOGERROR_1("Failed compiling ACE_Tags for tag: %1 - missing textures",_class); + _failure = true; + }; + + private _icon = getText (_x >> "icon"); + + if (!_failure) then { + GVAR(cachedTags) pushBack [_class, _displayName, _requiredItem, _textures, _icon]; + if !(_requiredItem in GVAR(cachedRequiredItems)) then { + GVAR(cachedRequiredItems) pushBack _requiredItem; + }; + }; +} forEach ("true" configClasses (configFile >> "ACE_Tags")); From 4b8f203f9b1f29a4058933c02c701cc6d0514dfd Mon Sep 17 00:00:00 2001 From: jonpas Date: Sun, 24 Jul 2016 20:00:33 +0200 Subject: [PATCH 3/4] Add quick tag keybind and setting --- addons/tagging/ACE_Settings.hpp | 11 ++++ addons/tagging/CfgVehicles.hpp | 43 +++++++++++++++- addons/tagging/XEH_PREP.hpp | 3 +- addons/tagging/XEH_postInit.sqf | 10 ++++ addons/tagging/config.cpp | 5 +- .../tagging/functions/fnc_addTagActions.sqf | 7 +-- addons/tagging/functions/fnc_moduleInit.sqf | 25 +++++++++ addons/tagging/functions/fnc_quickTag.sqf | 51 +++++++++++++++++++ addons/tagging/functions/fnc_tagRandom.sqf | 23 --------- addons/tagging/script_component.hpp | 10 ++-- addons/tagging/stringtable.xml | 23 ++++++++- 11 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 addons/tagging/ACE_Settings.hpp create mode 100644 addons/tagging/functions/fnc_moduleInit.sqf create mode 100644 addons/tagging/functions/fnc_quickTag.sqf delete mode 100644 addons/tagging/functions/fnc_tagRandom.sqf diff --git a/addons/tagging/ACE_Settings.hpp b/addons/tagging/ACE_Settings.hpp new file mode 100644 index 0000000000..a536718795 --- /dev/null +++ b/addons/tagging/ACE_Settings.hpp @@ -0,0 +1,11 @@ +class ACE_Settings { + class GVAR(quickTag) { + category = CSTRING(Tagging); + displayName = CSTRING(QuickTag); + description = CSTRING(QuickTagDesc); + typeName = "SCALAR"; + value = 1; + values[] = {ECSTRING(Common,Disabled), CSTRING(LastUsed), CSTRING(RandomX), CSTRING(Random)}; + isClientSettable = 1; + }; +}; diff --git a/addons/tagging/CfgVehicles.hpp b/addons/tagging/CfgVehicles.hpp index f73a2ed3ab..038e0a3f85 100644 --- a/addons/tagging/CfgVehicles.hpp +++ b/addons/tagging/CfgVehicles.hpp @@ -1,11 +1,52 @@ class CfgVehicles { + class ACE_Module; + class ACE_ModuleTagging: ACE_Module { + author = ECSTRING(common,ACETeam); + category = "ACE"; + displayName = CSTRING(Tagging); + function = QFUNC(moduleInit); + scope = 2; + isGlobal = 1; + //icon = QPATHTOF(UI\Icon_Module_Tagging_ca.paa);//@todo + class Arguments { + class quickTag { + displayName = CSTRING(QuickTag); + description = CSTRING(QuickTagDesc); + typeName = "NUMBER"; + class values { + class disabled { + name = ECSTRING(Common,Disabled); + value = 0; + }; + class lastUsed { + name = CSTRING(LastUsed); + value = 1; + default = 1; + }; + class randomX { + name = CSTRING(RandomX); + value = 2; + }; + class random { + name = CSTRING(Random); + value = 3; + }; + }; + }; + }; + class ModuleDescription { + description = CSTRING(ModuleDescription); + }; + }; + + class Man; class CAManBase: Man { class ACE_SelfActions { class ACE_Tags { displayName = CSTRING(Tag); condition = QUOTE(_player call FUNC(checkTaggable)); - statement = QUOTE(_player call FUNC(tagRandom)); + statement = QUOTE(_player call FUNC(quickTag)); icon = QPATHTOF(UI\icons\iconTaggingBlack.paa); insertChildren = QUOTE(_player call FUNC(addTagActions)); }; diff --git a/addons/tagging/XEH_PREP.hpp b/addons/tagging/XEH_PREP.hpp index d5dd3571d0..44bd74b992 100644 --- a/addons/tagging/XEH_PREP.hpp +++ b/addons/tagging/XEH_PREP.hpp @@ -4,6 +4,7 @@ PREP(applyCustomTag); PREP(checkTaggable); PREP(compileConfigTags); PREP(createTag); +PREP(moduleInit); +PREP(quickTag); PREP(tag); -PREP(tagRandom); PREP(tagTestingThread); diff --git a/addons/tagging/XEH_postInit.sqf b/addons/tagging/XEH_postInit.sqf index 57b35f2e59..ddac9b9ab9 100644 --- a/addons/tagging/XEH_postInit.sqf +++ b/addons/tagging/XEH_postInit.sqf @@ -45,6 +45,16 @@ if (hasInterface) then { // Scripted tag adding EH [QGVAR(applyCustomTag), FUNC(applyCustomTag)] call CBA_fnc_addEventHandler; + + // Keybind + ["ACE3 Equipment", QGVAR(quickTag), localize LSTRING(QuickTag), { + // Conditions + if !(ACE_player call FUNC(checkTaggable)) exitWith {false}; + + // Statement + ACE_player call FUNC(quickTag); + true + }, {false}, [0, [false, false, false]], false] call CBA_fnc_addKeybind; // Unbound }; if (!isServer) exitWith {}; diff --git a/addons/tagging/config.cpp b/addons/tagging/config.cpp index f894808ed7..e066bcafe5 100644 --- a/addons/tagging/config.cpp +++ b/addons/tagging/config.cpp @@ -8,16 +8,17 @@ class CfgPatches { requiredVersion = REQUIRED_VERSION; requiredAddons[] = {"ace_interaction"}; author = ECSTRING(common,ACETeam); - authors[] = {"BaerMitUmlaut","esteldunedain"}; + authors[] = {"BaerMitUmlaut", "esteldunedain", "Jonpas"}; url = ECSTRING(main,URL); VERSION_CONFIG; }; }; +#include "ACE_Settings.hpp" +#include "ACE_Tags.hpp" #include "CfgEventHandlers.hpp" #include "CfgVehicles.hpp" #include "CfgWeapons.hpp" -#include "ACE_Tags.hpp" class ACE_newEvents { createTag = QGVAR(createTag); diff --git a/addons/tagging/functions/fnc_addTagActions.sqf b/addons/tagging/functions/fnc_addTagActions.sqf index 35624d6d05..82acf253ad 100644 --- a/addons/tagging/functions/fnc_addTagActions.sqf +++ b/addons/tagging/functions/fnc_addTagActions.sqf @@ -27,15 +27,16 @@ private _actions = []; _displayName, _icon, { - (_this select 2) params ["_unit", "_textures"]; + (_this select 2) params ["_unit", "_class", "_textures"]; [_unit, selectRandom _textures] call FUNC(tag); + _unit setVariable [QGVAR(lastUsedTag), _class]; }, { - (_this select 2) params ["_unit", "", "_requiredItem"]; + (_this select 2) params ["_unit", "", "", "_requiredItem"]; _requiredItem in ((items _unit) apply {toLower _x}) }, {}, - [_unit, _textures, _requiredItem] + [_unit, _class, _textures, _requiredItem] ] call EFUNC(interact_menu,createAction), [], _unit diff --git a/addons/tagging/functions/fnc_moduleInit.sqf b/addons/tagging/functions/fnc_moduleInit.sqf new file mode 100644 index 0000000000..c9d3172d57 --- /dev/null +++ b/addons/tagging/functions/fnc_moduleInit.sqf @@ -0,0 +1,25 @@ +/* + * Author: Jonpas + * Initializes the Tagging module. + * + * Arguments: + * 0: The module logic + * 1: Units + * 2: Activated + * + * Return Value: + * None + * + * Public: No + */ +#include "script_component.hpp" + +if (!isServer) exitWith {}; + +params ["_logic", "", "_activated"]; + +if (!_activated) exitWith {}; + +[_logic, QGVAR(quickTag), "quickTag"] call EFUNC(common,readSettingFromModule); + +ACE_LOGINFO("Tagging Module Initialized."); diff --git a/addons/tagging/functions/fnc_quickTag.sqf b/addons/tagging/functions/fnc_quickTag.sqf new file mode 100644 index 0000000000..7f0a1d706e --- /dev/null +++ b/addons/tagging/functions/fnc_quickTag.sqf @@ -0,0 +1,51 @@ +/* + * Author: Jonpas + * Selects random tag and applies it. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call ace_tagging_fnc_quickTag + * + * Public: No + */ + +#include "script_component.hpp" + +// Exit if Quick Tag disabled +if (GVAR(quickTag) == 0) exitWith {}; + +params ["_unit"]; + +private _possibleTags = []; + +// Last Used +if (GVAR(quickTag) == 1) then { + private _lastUsedTagClass = _unit getVariable [QGVAR(lastUsedTag), nil]; + + if (!isNil "_lastUsedTagClass") then { + private _lastUsedTag = GVAR(cachedTags) select {(_x select 0) == _lastUsedTagClass}; + _possibleTags = _lastUsedTag; + }; +}; + +// Random X +if (GVAR(quickTag == 2)) then { + private _xTags = GVAR(cachedTags) select {(_x select 0) in ["ACE_XBlack", "ACE_XRed", "ACE_XGreen", "ACE_XBlue"]}; + _possibleTags = _xTags; +}; + +// Random +if (GVAR(quickTag) == 3) then { + _possibleTags = GVAR(cachedTags); +}; + +// Tag +if !(_possibleTags isEqualTo []) then { + private _availableTags = _possibleTags select {(_x select 2) in ((items _unit) apply {toLower _x})}; + [_unit, selectRandom ((selectRandom _availableTags) select 3)] call FUNC(tag); +}; diff --git a/addons/tagging/functions/fnc_tagRandom.sqf b/addons/tagging/functions/fnc_tagRandom.sqf deleted file mode 100644 index 2aa2f66222..0000000000 --- a/addons/tagging/functions/fnc_tagRandom.sqf +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Author: Jonpas - * Selects random tag and applies it. - * - * Arguments: - * 0: Unit - * - * Return Value: - * None - * - * Example: - * [player] call ace_tagging_fnc_tagRandom - * - * Public: No - */ - -#include "script_component.hpp" - -params ["_unit"]; - -private _possibleTags = GVAR(cachedTags) select {(_x select 2) in ((items _unit) apply {toLower _x})}; - -[_unit, selectRandom ((selectRandom _possibleTags) select 3)] call FUNC(tag); diff --git a/addons/tagging/script_component.hpp b/addons/tagging/script_component.hpp index 18066d4c0d..35f973d2fc 100644 --- a/addons/tagging/script_component.hpp +++ b/addons/tagging/script_component.hpp @@ -2,17 +2,17 @@ #define COMPONENT_BEAUTIFIED Tagging #include "\z\ace\addons\main\script_mod.hpp" -// #define DEBUG_MODE_FULL -// #define DISABLE_COMPILE_CACHE +#define DEBUG_MODE_FULL +#define DISABLE_COMPILE_CACHE // #define CBA_DEBUG_SYNCHRONOUS // #define ENABLE_PERFORMANCE_COUNTERS -#ifdef DEBUG_ENABLED_BLANK +#ifdef DEBUG_ENABLED_TAGGING #define DEBUG_MODE_FULL #endif -#ifdef DEBUG_SETTINGS_BLANK - #define DEBUG_SETTINGS DEBUG_SETTINGS_BLANK +#ifdef DEBUG_SETTINGS_TAGGING + #define DEBUG_SETTINGS DEBUG_SETTINGS_TAGGING #endif #include "\z\ace\addons\main\script_macros.hpp" diff --git a/addons/tagging/stringtable.xml b/addons/tagging/stringtable.xml index 2edddb2bf8..29eee8771f 100644 --- a/addons/tagging/stringtable.xml +++ b/addons/tagging/stringtable.xml @@ -1,6 +1,27 @@  + + Tagging + + + Configure how the tagging system will operate by default. + + + Quick Tag + + + Action performed on main tag interaction point. + + + Last Used + + + Random X + + + Random + Tag Markieren @@ -12,7 +33,7 @@ Marcar - X Black + X black Schwarz X X en negro X na czarno From e3a70c6d3b4c56461c9edbee1c3f607147412e31 Mon Sep 17 00:00:00 2001 From: jonpas Date: Sun, 24 Jul 2016 20:08:53 +0200 Subject: [PATCH 4/4] Add Tagging module icon --- addons/tagging/CfgVehicles.hpp | 2 +- addons/tagging/UI/Icon_Module_Tagging_ca.paa | Bin 0 -> 5625 bytes addons/tagging/script_component.hpp | 4 ++-- .../Icon_Module_Tagging_ca.png | Bin 0 -> 3669 bytes extras/assets/icons/Icons_Modules.psd | Bin 2371272 -> 2392621 bytes 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 addons/tagging/UI/Icon_Module_Tagging_ca.paa create mode 100644 extras/assets/icons/Icon_Module_png/Icon_Module_Tagging_ca.png diff --git a/addons/tagging/CfgVehicles.hpp b/addons/tagging/CfgVehicles.hpp index 038e0a3f85..80072ab1fc 100644 --- a/addons/tagging/CfgVehicles.hpp +++ b/addons/tagging/CfgVehicles.hpp @@ -7,7 +7,7 @@ class CfgVehicles { function = QFUNC(moduleInit); scope = 2; isGlobal = 1; - //icon = QPATHTOF(UI\Icon_Module_Tagging_ca.paa);//@todo + icon = QPATHTOF(UI\Icon_Module_Tagging_ca.paa); class Arguments { class quickTag { displayName = CSTRING(QuickTag); diff --git a/addons/tagging/UI/Icon_Module_Tagging_ca.paa b/addons/tagging/UI/Icon_Module_Tagging_ca.paa new file mode 100644 index 0000000000000000000000000000000000000000..6f3a4a3552b5ae24eade2ad634505abefe34f523 GIT binary patch literal 5625 zcmeHLZ)jUp6hBF`Hd0q>m#u?RmqG@e)D}ip23B+Hu7?w_}Tb#%1t-_z0E<^q6BCKFQefwsLIGi)4pv^~)_8A8&8(BKS=4 z9BCF65AKNaOUG+$KZ>Ki z&Ow7di29^a6Lv{aS1$bp{YM6~KX&653slj;e7S4<=-q1 zQ)ZH%7xMrbKRG_m($N-x|7gJia6c@0Sn1%h(?a1SowVHI$i-bY5cYTC5xB!LlQA?WN=4 z#7lE^Zl9;QamQDg*w;5coLNX$9#1BdPYC_;`PcU0^K$~Pm3_sQw+V1^P}j@mNrEk_ zZ$1_{x$%Ps$K-IL6vM+jlPl@>opb;(EVG_e{X?Hj0eFuh9>~n|b)&DZ?`U?sSV^>= z|9(8qjS;FS-GxnLQRuhfi()_1pHi^>d-e)TG>yW( zTi0*(zdC-~|J(T&RVM1ohSyE~&&L#nK-IVMk;Rr!g0cBnQxl$2`P66FZ&~rgGP~iI zdXazkECnK7zoN~5w)krsNL}B0r+dK*-~u|0X^AX-fxl`1R1BdSfvv}Ipqz4To1m-M zm{wZ$O43o0G|}%`%7&FER^28DeUT-zek_bU`q4;!>@~=|K-^K4E*X>={+evYu>y(?ft97 zPl5Q6ktmD>yOx6@O>{aPnMfb=3+$zlb9M4t4FV(ownSHzUM55taDc>Tdrd4+jyVy3 z!`M4>Xx+0!IpG3nC(?PDLMc&j&M@#}pW4KDbR+iii8r?{qywj$kA!}BAYG% literal 0 HcmV?d00001 diff --git a/addons/tagging/script_component.hpp b/addons/tagging/script_component.hpp index 35f973d2fc..5eca5d92d7 100644 --- a/addons/tagging/script_component.hpp +++ b/addons/tagging/script_component.hpp @@ -2,8 +2,8 @@ #define COMPONENT_BEAUTIFIED Tagging #include "\z\ace\addons\main\script_mod.hpp" -#define DEBUG_MODE_FULL -#define DISABLE_COMPILE_CACHE +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE // #define CBA_DEBUG_SYNCHRONOUS // #define ENABLE_PERFORMANCE_COUNTERS diff --git a/extras/assets/icons/Icon_Module_png/Icon_Module_Tagging_ca.png b/extras/assets/icons/Icon_Module_png/Icon_Module_Tagging_ca.png new file mode 100644 index 0000000000000000000000000000000000000000..653fe60f462b8c0eed790f6ae1c62e1c242e7459 GIT binary patch literal 3669 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 literal 0 HcmV?d00001 diff --git a/extras/assets/icons/Icons_Modules.psd b/extras/assets/icons/Icons_Modules.psd index 0da44ea68106022ee9fa90f40c6db5af943219c0..ac07ae7c777fe35f934517502cba9211f5e4c822 100644 GIT binary patch delta 24073 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 delta 5211 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