diff --git a/addons/arsenal/XEH_PREP.hpp b/addons/arsenal/XEH_PREP.hpp index 3b14abe96be..0539c820b62 100644 --- a/addons/arsenal/XEH_PREP.hpp +++ b/addons/arsenal/XEH_PREP.hpp @@ -1,3 +1,4 @@ +PREP(addAction); PREP(addDefaultLoadout); PREP(addListBoxItem); PREP(addRightPanelButton); @@ -62,6 +63,7 @@ PREP(open3DEN); PREP(openBox); PREP(portVALoadouts); PREP(refresh); +PREP(removeAction); PREP(removeBox); PREP(removeDefaultLoadout); PREP(removeSort); diff --git a/addons/arsenal/functions/fnc_addAction.sqf b/addons/arsenal/functions/fnc_addAction.sqf new file mode 100644 index 00000000000..c867b9aea45 --- /dev/null +++ b/addons/arsenal/functions/fnc_addAction.sqf @@ -0,0 +1,122 @@ +#include "script_component.hpp" +/* + * Author: johnb43 + * Adds custom action buttons. + * + * Arguments: + * 0: Tabs to add action to + * 1: Action class (unique string for each action) + * 2: Title + * 3: Actions + * 4: Condition (default: {true}) + * 5: Scope editor (default: 2) + * + * Return Value: + * 0: Array of IDs + * + * Example: + * [[0, 5], "TAG_myActions", "My Actions", [ + * ["text", "Text", {true}, "Text"], + * ["statement", "Statement", {true}, "", {[_this select 0] call tag_fnc_myTextStatement}], + * ["button", "Button", {true}, "", {}, {_this call tag_fnc_myAction}] + * ]] call ace_arsenal_fnc_addAction + * + * Public: Yes + */ + +params [ + ["_tabs", [], [[]]], + ["_rootClass", "", [""]], + ["_title", "", [""]], + ["_actions", [], [[]]], + ["_rootCondition", {true}, [{}]], + ["_scopeEditor", 2, [0]] +]; + +// Compile actions from config (in case this is called before preInit) +call FUNC(compileActions); + +// Skip if not allowed in editor and in editor +if (is3DEN && {_scopeEditor != 2}) exitWith { + TRACE_1("Skipping action because in editor", _rootClass); + [] +}; + +// Class can't contain ~, because it's used for formatting result +if ("~" in _rootClass) exitWith { + TRACE_1("Classname can't contain '~'", _rootClass); + [] +}; + +private _return = []; + +private _fnc_addToGroup = { + params ["_group", "_tab"]; + + private _type = -1; + + { + _x params [["_class", "", [""]], ["_label", "", [""]], ["_condition", {true}, [{}]], ["_text", "", [""]], ["_textStatement", {}, [{}]], ["_statement", {}, [{}]]]; + + // Class can't contain ~, because it's used for formatting result + if (_class == "" || {"~" in _class}) then { + continue; + }; + + // Don't allow two of the same class + if (_group findIf {(_x select 0) == _class} != -1) then { + TRACE_1("An action with this ID already exists", _class); + continue; + }; + + _type = switch (false) do { + case (_text == ""): { + _statement = format ["{""%1""}", _text]; + ACTION_TYPE_TEXT + }; + case (_textStatement isEqualTo {}): { + _statement = _textStatement; + ACTION_TYPE_TEXT + }; + case (_statement isEqualTo {}): { + _statement = _statement; + ACTION_TYPE_BUTTON + }; + default { + -1 + }; + }; + + if (_type == -1) then { + continue; + }; + + _statement = compile format [QUOTE([GVAR(center)] call %1), _statement]; + + _group pushBack [_class, _type, _label, _statement, _condition]; + _return pushBack ([_rootClass, _class, _tab] joinString "~"); + } forEach _actions; +}; + +private _tab = []; +private _index = -1; +private _group = []; + +{ + _tab = GVAR(actionList) select _x; + _index = _tab findIf {(_x select 0) == _rootClass}; + + // Add to existing group + if (_index != -1) then { + [_tab select _index select 3, _x] call _fnc_addToGroup; + } else { + // Add to new group + _group = []; + + [_group, _x] call _fnc_addToGroup; + + _tab pushBack [_rootClass, _title, _rootCondition, _group]; + }; +} forEach _tabs; + +_return diff --git a/addons/arsenal/functions/fnc_addSort.sqf b/addons/arsenal/functions/fnc_addSort.sqf index 49bbc04c76b..41ad0c58476 100644 --- a/addons/arsenal/functions/fnc_addSort.sqf +++ b/addons/arsenal/functions/fnc_addSort.sqf @@ -7,10 +7,10 @@ * 0: Tabs to add sort to * - 0: Left Tab Indexes * - 1: Right Tab Indexes - * 1: Sort Class (a unique string for each algorithm) + * 1: Sort class (a unique string for each algorithm) * 2: Title * 3: Algorithm - * 4: Condition (default: true) + * 4: Condition (default: {true}) * * Return Value: * 0: Array of IDs @@ -27,7 +27,7 @@ * * _fireRate sort true; * _fireRate param [0, 0] - * }] call ace_arsenal_fnc_addSort; + * }] call ace_arsenal_fnc_addSort * * Public: Yes */ @@ -37,8 +37,7 @@ params [ ["_class", "", [""]], ["_title", "", [""]], ["_statement", {}, [{}]], - ["_condition", {true}, [{}]], - ["_overwrite", false, [false]] + ["_condition", {true}, [{}]] ]; _tabs params [ diff --git a/addons/arsenal/functions/fnc_buttonActionsPage.sqf b/addons/arsenal/functions/fnc_buttonActionsPage.sqf index 59f519799f8..7b30f42e4d0 100644 --- a/addons/arsenal/functions/fnc_buttonActionsPage.sqf +++ b/addons/arsenal/functions/fnc_buttonActionsPage.sqf @@ -6,7 +6,7 @@ * * Arguments: * 0: Arsenal display - * 1. Actions control + * 1: Actions control * 2: Previous or next (false = previous, true = next) * * Return Value: diff --git a/addons/arsenal/functions/fnc_compileActions.sqf b/addons/arsenal/functions/fnc_compileActions.sqf index 2bf4fe51b0e..1fec6005d1c 100644 --- a/addons/arsenal/functions/fnc_compileActions.sqf +++ b/addons/arsenal/functions/fnc_compileActions.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Author: Brett Mayson - * Create the internal actions arrays when needed for the first time + * Create the internal actions arrays when needed for the first time. * * Arguments: * None @@ -39,10 +39,12 @@ private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions) { private _scopeEditor = getNumber (_x >> "scopeEditor"); + if (is3DEN && {_scopeEditor != 2}) then {continue}; private _configActions = "true" configClasses _x; + private _rootClass = configName _x; private _rootDisplayName = getText (_x >> "displayName"); private _rootCondition = getText (_x >> "condition"); private _rootTabs = getArray (_x >> "tabs"); @@ -56,6 +58,7 @@ private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions) private _group = []; { + private _class = configName _x; private _label = getText (_x >> "label"); private _condition = getText (_x >> "condition"); private _statement = getText (_x >> "statement"); @@ -79,9 +82,11 @@ private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions) -1 }; }; + if (_type == -1) then { continue; }; + _statement = compile format [QUOTE([GVAR(center)] call {%1}), _statement]; if (_condition != "") then { @@ -90,12 +95,13 @@ private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions) _condition = {true}; }; - _group pushBack [_type, _label, _statement, _condition]; + // No duplicates are possible here + _group pushBack [_class, _type, _label, _statement, _condition]; } forEach _configActions; { - (_actionList select _x) pushBack [_rootDisplayName, _rootCondition, _group]; + (_actionList select _x) pushBack [_rootClass, _rootDisplayName, _rootCondition, _group]; } forEach _rootTabs; } forEach _configGroupEntries; -missionNamespace setVariable [QGVAR(actionList), _actionList]; +GVAR(actionList) = _actionList; diff --git a/addons/arsenal/functions/fnc_compileSorts.sqf b/addons/arsenal/functions/fnc_compileSorts.sqf index d38cbe6b2ef..de2fff4e4f9 100644 --- a/addons/arsenal/functions/fnc_compileSorts.sqf +++ b/addons/arsenal/functions/fnc_compileSorts.sqf @@ -63,7 +63,6 @@ private _sortListRightPanel = [ [] // Misc 7 ]; -//------------------------- Config handling private _class = ""; private _displayName = ""; private _statement = ""; diff --git a/addons/arsenal/functions/fnc_compileStats.sqf b/addons/arsenal/functions/fnc_compileStats.sqf index 9dd1abcf893..0462b13e03d 100644 --- a/addons/arsenal/functions/fnc_compileStats.sqf +++ b/addons/arsenal/functions/fnc_compileStats.sqf @@ -78,7 +78,6 @@ private _statsListRightPanel = [ [] // Misc 7 ]; -//------------------------- Config handling private _finalArray = []; private _class = ""; private _stats = []; @@ -127,6 +126,5 @@ private _priority = 0; [_statsListLeftPanel] call _fnc_sortLists; [_statsListRightPanel] call _fnc_sortLists; -//------------------------- Config Handling GVAR(statsListLeftPanel) = _statsListLeftPanel; GVAR(statsListRightPanel) = _statsListRightPanel; diff --git a/addons/arsenal/functions/fnc_handleActions.sqf b/addons/arsenal/functions/fnc_handleActions.sqf index f8ed2d092d8..757616e3a78 100644 --- a/addons/arsenal/functions/fnc_handleActions.sqf +++ b/addons/arsenal/functions/fnc_handleActions.sqf @@ -42,7 +42,7 @@ private _panel = [ ] find GVAR(currentLeftPanel); private _groups = (GVAR(actionList) select _panel) select { - [GVAR(center)] call (_x select 1) + [GVAR(center)] call (_x select 2) }; private _show = _groups isNotEqualTo []; @@ -57,9 +57,11 @@ private _actionsCurrentPageCtrl = _display displayCtrl IDC_actionsCurrentPage; private _currentPage = GVAR(currentActionPage); private _pages = count _groups; + if (_currentPage < 0) then { _currentPage = _pages - 1; }; + if (_currentPage >= _pages) then { _currentPage = 0; GVAR(currentActionPage) = _currentPage; @@ -72,25 +74,28 @@ if (_currentPage >= _pages) then { } forEach [IDC_actionsPreviousPage, IDC_actionsNextPage]; private _group = _groups select _currentPage; -private _items = _group select 2 select { - [GVAR(center)] call (_x select 3) +private _items = _group select 3 select { + [GVAR(center)] call (_x select 4) }; -_actionsCurrentPageCtrl ctrlSetText (_group select 0); +_actionsCurrentPageCtrl ctrlSetText (_group select 1); _actionsCurrentPageCtrl ctrlSetFade 0; _actionsCurrentPageCtrl ctrlShow true; _actionsCurrentPageCtrl ctrlCommit 0; { - _x params ["_type", "_label", "_statement"]; + _x params ["", "_type", "_label", "_statement"]; + private _idc = 9001 + _forEachIndex * 2; private _actionTextCtrl = _display displayCtrl _idc; private _actionButtonCtrl = _display displayCtrl (_idc + 1); + switch (_type) do { case ACTION_TYPE_BUTTON: { _actionButtonCtrl ctrlRemoveAllEventHandlers "ButtonClick"; _actionButtonCtrl ctrlAddEventHandler ["ButtonClick", { if (is3DEN) exitWith {call FUNC(refresh)}; + [{ call FUNC(refresh); }] call CBA_fnc_execNextFrame; @@ -104,10 +109,12 @@ _actionsCurrentPageCtrl ctrlCommit 0; _actionTextCtrl ctrlCommit 0; }; case ACTION_TYPE_TEXT: { - private _text = (call _statement); + private _text = call _statement; + if (isNil "_text") then { _text = ""; }; + _actionTextCtrl ctrlSetText _text; _actionTextCtrl ctrlSetFade 0; _actionTextCtrl ctrlCommit 0; @@ -129,6 +136,7 @@ private _actionCount = count _items; private _idc = 9001 + _x * 2; private _actionTextCtrl = _display displayCtrl _idc; private _actionButtonCtrl = _display displayCtrl (_idc + 1); + _actionTextCtrl ctrlSetFade 1; _actionTextCtrl ctrlCommit 0; _actionButtonCtrl ctrlSetFade 1; diff --git a/addons/arsenal/functions/fnc_removeAction.sqf b/addons/arsenal/functions/fnc_removeAction.sqf new file mode 100644 index 00000000000..1c11dce4a95 --- /dev/null +++ b/addons/arsenal/functions/fnc_removeAction.sqf @@ -0,0 +1,41 @@ +#include "script_component.hpp" +/* + * Author: johnb43 + * Remove a custom action button from ACE Arsenal. + * + * Arguments: + * 0: Array of IDs + * + * Return Value: + * None + * + * Example: + * [["TAG_myActions~text~0", "TAG_myActions~statement~0", "TAG_myActions~button~0"]] call ace_arsenal_fnc_removeAction + * + * Public: Yes +*/ + +params ["_IDList"]; + +// Compile sorts from config (in case this is called before preInit) +call FUNC(compileActions); + +// Remove entries (all names are unique, there are no duplicates) +{ + (_x splitString "~") params ["_rootClass", "_class", "_tab"]; + + _tab = parseNumber _tab; + + { + if ((_x select 0) == _rootClass) exitWith { + (_x select 3) deleteAt ((_x select 3) findIf {(_x select 0) == _class}); + + // If no entries left in group, remove group + if ((_x select 3) isEqualTo []) then { + (GVAR(actionList) select _tab) deleteAt _forEachIndex; + }; + }; + } forEach (GVAR(actionList) select _tab); +} forEach _IDList; + +nil // return diff --git a/addons/gunbag/ACE_Arsenal_Actions.hpp b/addons/gunbag/ACE_Arsenal_Actions.hpp index 32a16e99d37..6064384ad11 100644 --- a/addons/gunbag/ACE_Arsenal_Actions.hpp +++ b/addons/gunbag/ACE_Arsenal_Actions.hpp @@ -4,20 +4,20 @@ class EGVAR(arsenal,actions) { condition = QUOTE(_this call FUNC(hasGunbag)); scopeEditor = 0; // variables are reset between 3DEN and mission start tabs[] = {0,5}; - class status { + class GVAR(status) { textStatement = QUOTE([_this select 0] call FUNC(weaponName)); }; - class store { + class GVAR(store) { label = CSTRING(ToGunbag); condition = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(canInteract) == 0); statement = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(toGunbagCallback)); }; - class retrieve { + class GVAR(retrieve) { label = CSTRING(OffGunbag); condition = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(canInteract) == 1); statement = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(offGunbagCallback)); }; - class swap { + class GVAR(swap) { label = CSTRING(SwapGunbag); condition = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(canInteract) == 2); statement = QUOTE([ARR_2(_this select 0,_this select 0)] call FUNC(swapGunbagCallback)); diff --git a/docs/wiki/framework/arsenal-framework.md b/docs/wiki/framework/arsenal-framework.md index 5bf9ebd5e8a..75a59bc9b80 100644 --- a/docs/wiki/framework/arsenal-framework.md +++ b/docs/wiki/framework/arsenal-framework.md @@ -256,6 +256,8 @@ Example: }, {true}]] call ace_arsenal_fnc_addStat; ``` +If a stat already exists (so same class ID and tab), it will ignore the new addition. + ### 5.3 Removing stats via a function `ace_arsenal_fnc_removeStat` @@ -350,7 +352,7 @@ The argument passed to the condition is: 1 | Stat class ID | String | Required 2 | Title | String | Required 3 | Algorithm | Code | Required -4 | Condition | Code | Optional (default: `true`) +4 | Condition | Code | Optional (default: `{true}`) Return Value: - Array of sort IDs @@ -373,6 +375,8 @@ Example: Sorting method IDs are unique and are generated in the same fashion as the stat IDs (see `5.3 Removing stats via a function`). +If a sorting method already exists (so same class ID and tab), it will ignore the new addition. + ### 6.3 Removing sorting methods via a function `ace_arsenal_fnc_removeSort` @@ -395,8 +399,6 @@ ACE Arsenal actions are customizable, this will show you how. ### 7.1 Adding actions via config -Actions use the same tab definitions as stats, found above. - ```cpp class ace_arsenal_actions { class TAG_myActions { @@ -422,6 +424,57 @@ class ace_arsenal_actions { ``` The focused unit object is passed to the condition and statement functions. +### 7.2 Adding sorting methods via a function + +`ace_arsenal_fnc_addAction` + +| | Argument | Type | Optional (default value) +--- | -------- | ---- | ------------------------ +0 | Tabs to add the sort to | Array of numbers | Required +1 | Action class ID | String | Required +2 | Title | String | Required +3 | Actions | Array of arrays | Required +4 | Condition | Code | Optional (default: `{true}`) +5 | Scope editor | Number | Optional (default: `2`) + +Return Value: +- Array of action IDs + +Example: +```sqf +[[0, 5], "TAG_myActions", "My Actions", [ + ["text", "Text", {true}, "Text"], + ["statement", "Statement", {true}, "", {[_this select 0] call tag_fnc_myTextStatement}], + ["button", "Button", {true}, "", {}, {_this call tag_fnc_myAction}] +]] call ace_arsenal_fnc_addAction; +``` + +The example above results in the same actions as in `7.1 Adding actions via config`. + +Action IDs are unique and are generated as a string in the following fashion: +`_rootClassName + "~" + _class + "~" + _tab` + +The example above returns: +`["TAG_myActions~text~0","TAG_myActions~statement~0","TAG_myActions~button~0","TAG_myActions~text~5","TAG_myActions~statement~5","TAG_myActions~button~5"]` + +If an action already exists (so same class ID and tab within an action), it will ignore the new addition. + +### 7.3 Removing actions via a function + +`ace_arsenal_fnc_removeAction` + +| | Argument | Type | Optional (default value) +---| -------- | ---- | ------------------------ +0 | Array of IDs | Array | Required + +Action IDs are unique and their generation is explained in `7.2 Adding sorting methods via a function`. + +For config added actions the classname is used, for function added ones the string provided is used. + +### 7.4 Action tab numbers + +The same numbers are used for actions as for stats (see `5.4 Stat tab numbers`). + ## 8. Eventhandlers All are local.