diff --git a/addons/arsenal/XEH_postInit.sqf b/addons/arsenal/XEH_postInit.sqf index 85ab3f926ab..7c7c677819b 100644 --- a/addons/arsenal/XEH_postInit.sqf +++ b/addons/arsenal/XEH_postInit.sqf @@ -108,24 +108,16 @@ GVAR(lastSortDirectionRight) = DESCENDING; private _face = _extendedInfo getOrDefault [QGVAR(face), ""]; if (_face != "") then { - if (isMultiplayer) then { - private _id = [QGVAR(broadcastFace), [_unit, _face], QGVAR(centerFace_) + netId _unit] call CBA_fnc_globalEventJIP; - [_id, _unit] call CBA_fnc_removeGlobalEventJIP; - } else { - _unit setFace _face; - }; + private _id = [QGVAR(broadcastFace), [_unit, _face], QGVAR(centerFace_) + hashValue _unit] call CBA_fnc_globalEventJIP; + [_id, _unit] call CBA_fnc_removeGlobalEventJIP; }; // Set voice private _voice = _extendedInfo getOrDefault [QGVAR(voice), ""]; if (_voice != "") then { - if (isMultiplayer) then { - private _id = [QGVAR(broadcastVoice), [_unit, _voice], QGVAR(centerVoice_) + netId _unit] call CBA_fnc_globalEventJIP; - [_id, _unit] call CBA_fnc_removeGlobalEventJIP; - } else { - _unit setSpeaker _voice; - }; + private _id = [QGVAR(broadcastVoice), [_unit, _voice], QGVAR(centerVoice_) + hashValue _unit] call CBA_fnc_globalEventJIP; + [_id, _unit] call CBA_fnc_removeGlobalEventJIP; }; // Set insignia @@ -147,7 +139,7 @@ GVAR(lastSortDirectionRight) = DESCENDING; // Set voice if enabled if (GVAR(loadoutsSaveVoice)) then { - _extendedInfo set [QGVAR(voice), speaker _unit]; + _extendedInfo set [QGVAR(voice), (speaker _unit) call EFUNC(common,getConfigName)]; }; // Set insignia if enabled diff --git a/addons/arsenal/XEH_preInit.sqf b/addons/arsenal/XEH_preInit.sqf index b6ebd4d1604..10cc3989e94 100644 --- a/addons/arsenal/XEH_preInit.sqf +++ b/addons/arsenal/XEH_preInit.sqf @@ -91,5 +91,4 @@ private _insigniaCondition = toString { GVAR(insigniaCache) set [_x, 2]; } forEach (_insigniaCondition configClasses (missionConfigFile >> "CfgUnitInsignia")); - ADDON = true; diff --git a/addons/arsenal/functions/fnc_addListBoxItem.sqf b/addons/arsenal/functions/fnc_addListBoxItem.sqf index 655977ea560..4368eb6ea1a 100644 --- a/addons/arsenal/functions/fnc_addListBoxItem.sqf +++ b/addons/arsenal/functions/fnc_addListBoxItem.sqf @@ -6,7 +6,7 @@ * * Arguments: * 0: Config category, must be "CfgWeapons", "CfgVehicles", "CfgMagazines", "CfgVoice" or "CfgUnitInsignia" - * 1: Classname + * 1: Classname (must be in config case) * 2: Panel control * 3: Name of the picture entry in that Cfg class (default: "picture") * 4: Config root (default: 0 -> configFile) @@ -42,7 +42,7 @@ if (_skip) then { if (_skip) exitWith {}; -// Sanitise key, as it's public; If not in cache, find info and cache it for later use +// If not in cache, find info and cache it for later use ((uiNamespace getVariable QGVAR(addListBoxItemCache)) getOrDefaultCall [_configCategory + _className + str _configRoot, { // Get classname (config case), display name, picture and DLC private _configPath = ([configFile, campaignConfigFile, missionConfigFile] select _configRoot) >> _configCategory >> _className; diff --git a/addons/arsenal/functions/fnc_fillLoadoutsList.sqf b/addons/arsenal/functions/fnc_fillLoadoutsList.sqf index 0c0d37ad685..6174193b73d 100644 --- a/addons/arsenal/functions/fnc_fillLoadoutsList.sqf +++ b/addons/arsenal/functions/fnc_fillLoadoutsList.sqf @@ -71,15 +71,16 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then { _loadoutCachedInfo = [_loadoutData] call FUNC(verifyLoadout); _contentPanelCtrl setVariable [_loadoutNameAndTab, _loadoutCachedInfo]; - _loadoutCachedInfo params ["", "_nullItemsList", "_unavailableItemsList"]; + _loadoutCachedInfo params ["", "_nullItemsList", "_unavailableItemsList", "_missingExtendedInfo"]; // Log missing / nil items to RPT (only once per arsenal session) - if (GVAR(EnableRPTLog) && {(_nullItemsList isNotEqualTo []) || {_unavailableItemsList isNotEqualTo []}}) then { + if (GVAR(EnableRPTLog) && {(_nullItemsList isNotEqualTo []) || {_unavailableItemsList isNotEqualTo [] || {_missingExtendedInfo isNotEqualTo []}}}) then { private _printComponent = "ACE_Arsenal - Loadout:"; private _printNullItemsList = ["Missing items:", str _nullItemsList] joinString " "; private _printUnavailableItemsList = ["Unavailable items:", str _unavailableItemsList] joinString " "; + private _printMissingExtendedInfo = ["Missing extended loadout:", str _missingExtendedInfo] joinString " "; - diag_log text (format ["%1%5 %2%5 %3%5 %4", _printComponent, "Name: " + _loadoutName, _printNullItemsList, _printUnavailableItemsList, endl]); + diag_log text (format ["%1%6 %2%6 %3%6 %4%6 %5", _printComponent, "Name: " + _loadoutName, _printNullItemsList, _printUnavailableItemsList, _printMissingExtendedInfo, endl]); }; }; diff --git a/addons/arsenal/functions/fnc_onArsenalOpen.sqf b/addons/arsenal/functions/fnc_onArsenalOpen.sqf index 9077a13a7a8..f307c932b13 100644 --- a/addons/arsenal/functions/fnc_onArsenalOpen.sqf +++ b/addons/arsenal/functions/fnc_onArsenalOpen.sqf @@ -68,7 +68,7 @@ if (isNil QGVAR(virtualItems)) then { GVAR(virtualItemsFlatAll) = +GVAR(virtualItemsFlat); GVAR(currentFace) = face GVAR(center); -GVAR(currentVoice) = speaker GVAR(center); +GVAR(currentVoice) = (speaker GVAR(center)) call EFUNC(common,getConfigName); GVAR(currentInsignia) = GVAR(center) call BIS_fnc_getUnitInsignia; GVAR(currentAction) = "Stand"; diff --git a/addons/arsenal/functions/fnc_onSelChangedLeft.sqf b/addons/arsenal/functions/fnc_onSelChangedLeft.sqf index 3d356fc207e..5bf08245c88 100644 --- a/addons/arsenal/functions/fnc_onSelChangedLeft.sqf +++ b/addons/arsenal/functions/fnc_onSelChangedLeft.sqf @@ -683,14 +683,19 @@ switch (GVAR(currentLeftPanel)) do { TOGGLE_RIGHT_PANEL_HIDE - private _unitInsigniaConfig = configFile >> "CfgUnitInsignia" >> _item; + // Check for correct config: First mission, then campaign and finally regular config + private _itemCfg = missionConfigFile >> "CfgUnitInsignia" >> _item; - // Display new items's info on the bottom right - if (isNull _unitInsigniaConfig) then { - [_display, _control, _curSel, missionConfigFile >> "CfgUnitInsignia" >> _item] call FUNC(itemInfo); - } else { - [_display, _control, _curSel, _unitInsigniaConfig] call FUNC(itemInfo); + if (isNull _itemCfg) then { + _itemCfg = campaignConfigFile >> "CfgUnitInsignia" >> _item; + }; + + if (isNull _itemCfg) then { + _itemCfg = configFile >> "CfgUnitInsignia" >> _item; }; + + // Display new items's info on the bottom right + [_display, _control, _curSel, _itemCfg] call FUNC(itemInfo); }; }; diff --git a/addons/arsenal/functions/fnc_sortPanel.sqf b/addons/arsenal/functions/fnc_sortPanel.sqf index bbe48d6e351..7a8a8978eba 100644 --- a/addons/arsenal/functions/fnc_sortPanel.sqf +++ b/addons/arsenal/functions/fnc_sortPanel.sqf @@ -15,6 +15,10 @@ params ["_control"]; +// https://community.bistudio.com/wiki/toString, see comment +// However, using 55295 did not work as expected, 55291 was found by trial and error +#define HIGHEST_VALUE_CHAR 55291 + // When filling the sorting panel, FUNC(sortPanel) is called twice, so ignore first call if (GVAR(ignoreFirstSortPanelCall)) exitWith { GVAR(ignoreFirstSortPanelCall) = false; @@ -29,6 +33,7 @@ private _sortDirectionCtrl = _display displayCtrl ([IDC_sortLeftTabDirection, ID private _cfgMagazines = configFile >> "CfgMagazines"; private _cfgFaces = configFile >> "CfgFaces"; private _cfgUnitInsignia = configFile >> "CfgUnitInsignia"; +private _cfgUnitInsigniaCampaign = campaignConfigFile >> "CfgUnitInsignia"; private _cfgUnitInsigniaMission = missionConfigFile >> "CfgUnitInsignia"; if (_rightSort) then { @@ -126,7 +131,6 @@ private _selected = if (_right) then { _panel lbData _curSel }; -private _originalNames = createHashMap; private _item = ""; private _quantity = ""; private _itemCfg = configNull; @@ -136,12 +140,8 @@ private _fillerChar = toString [1]; private _magazineMiscItems = uiNamespace getVariable QGVAR(magazineMiscItems); private _sortCache = uiNamespace getVariable QGVAR(sortCache); - -private _faceCache = if (_cfgClass == _cfgFaces) then { - uiNamespace getVariable [QGVAR(faceCache), createHashMap] -} else { - createHashMap -}; +private _faceCache = uiNamespace getVariable QGVAR(faceCache); +private _insigniaCache = uiNamespace getVariable QGVAR(insigniaCache); private _countColumns = if (_right) then { count lnbGetColumnsPosition _panel @@ -150,9 +150,9 @@ private _countColumns = if (_right) then { }; private _for = if (_right) then { - for '_i' from 0 to (lnbSize _panel select 0) - 1 + for "_i" from 0 to (lnbSize _panel select 0) - 1 } else { - for '_i' from 0 to (lbSize _panel) - 1 + for "_i" from 0 to (lbSize _panel) - 1 }; _for do { @@ -163,6 +163,14 @@ _for do { _panel lbData _i }; + // Check if entry is "Empty" + if (!_right && {(_panel lbValue _i) == -1}) then { + // Set to lowest/highest lexicographical value, so that "Empty" is always at the top + _panel lbSetTextRight [_i, ["", toString [HIGHEST_VALUE_CHAR, HIGHEST_VALUE_CHAR, HIGHEST_VALUE_CHAR, HIGHEST_VALUE_CHAR, HIGHEST_VALUE_CHAR]] select (_sortDirection == ASCENDING)]; + + continue; + }; + // Get item's count _quantity = if (_right) then { parseNumber (_panel lnbText [_i, 2]) @@ -179,18 +187,22 @@ _for do { _itemCfg = if !(_cfgClass in [_cfgFaces, _cfgUnitInsignia]) then { _cfgClass >> _item } else { - // If insignia, check both config and mission file + // If insignia, check for correct config: First mission, then campaign and finally regular config if (_cfgClass == _cfgUnitInsignia) then { - _itemCfg = _cfgClass >> _item; + _itemCfg = _cfgUnitInsigniaMission >> _item; if (isNull _itemCfg) then { - _itemCfg = _cfgUnitInsigniaMission >> _item; + _itemCfg = _cfgUnitInsigniaCampaign >> _item; + }; + + if (isNull _itemCfg) then { + _itemCfg = _cfgUnitInsignia >> _item; }; _itemCfg } else { // If face, check correct category - _cfgClass >> (_faceCache get _item) param [2, "Man_A3"] >> _item + _cfgClass >> (_faceCache getOrDefault [_item, []]) param [2, "Man_A3"] >> _item }; }; @@ -216,37 +228,29 @@ _for do { _value }, true]; - // Save the current row's item's name in a cache and set text to it's sorting value + // Set the right text temporarily, so it can be used for sorting if (_right) then { - _name = _panel lnbText [_i, 1]; - _originalNames set [_item, _name]; - // Use value, display name and classname to sort, which means a fixed alphabetical order is guaranteed - // Filler char has lowest lexicographical order possible - _panel lnbSetText [[_i, 1], format ["%1%2%4%3", _value, _name, _item, _fillerChar]]; + // Filler char has lowest lexicographical value possible + _panel lnbSetTextRight [[_i, 1], format ["%1%2%4%3", _value, _panel lnbText [_i, 1], _item, _fillerChar]]; } else { if (_item != "") then { - _name = _panel lbText _i; - _originalNames set [_item, _name]; - // Use value, display name and classname to sort, which means a fixed alphabetical order is guaranteed - // Filler char has lowest lexicographical order possible - _panel lbSetText [_i, format ["%1%2%4%3", _value, _name, _item, _fillerChar]]; + // Filler char has lowest lexicographical value possible + _panel lbSetTextRight [_i, format ["%1%2%4%3", _value, _panel lbText _i, _item, _fillerChar]]; }; }; }; -// Sort alphabetically, find the previously selected item, select it again and reset text to original text +// Sort alphabetically, find the previously selected item and select it again if (_right) then { - _panel lnbSort [1, _sortDirection == ASCENDING]; + [_panel, 1] lnbSortBy ["TEXT", _sortDirection == ASCENDING, false, true, false]; _for do { - _item = _panel lnbData [_i, 0]; - - _panel lnbSetText [[_i, 1], _originalNames get _item]; + // Remove sorting text, as it blocks the item name otherwise + _panel lnbSetTextRight [[_i, 1], ""]; - // Set selection after text, otherwise item info box on the right side shows invalid name - if (_curSel != -1 && {_item == _selected}) then { + if (_curSel != -1 && {(_panel lnbData [_i, 0]) == _selected}) then { _panel lnbSetCurSelRow _i; // To avoid unnecessary checks after previsouly selected item was found @@ -254,17 +258,17 @@ if (_right) then { }; }; } else { - lbSort [_panel, ["DESC", "ASC"] select _sortDirection]; + _panel lbSortBy ["TEXT", _sortDirection == ASCENDING, false, true, false]; _for do { _item = _panel lbData _i; // Check if valid item (problems can be caused when searching) if (_item != "") then { - _panel lbSetText [_i, _originalNames get _item]; + // Remove sorting text, as it blocks the item name otherwise + _panel lbSetTextRight [_i, ""]; }; - // Set selection after text, otherwise item info box on the right side shows invalid name if (_curSel != -1 && {_item == _selected}) then { _panel lbSetCurSel _i; diff --git a/addons/arsenal/functions/fnc_verifyLoadout.sqf b/addons/arsenal/functions/fnc_verifyLoadout.sqf index 03c58985793..f321293fe9a 100644 --- a/addons/arsenal/functions/fnc_verifyLoadout.sqf +++ b/addons/arsenal/functions/fnc_verifyLoadout.sqf @@ -27,6 +27,7 @@ private _name = ""; private _itemArray = []; private _nullItemsList = []; private _unavailableItemsList = []; +private _missingExtendedInfo = []; // Search for all items and check their availability private _fnc_filterLoadout = { @@ -70,7 +71,22 @@ private _fnc_filterLoadout = { // Loadout might come from a different modpack, which might have different config naming _loadout = _loadout call _fnc_filterLoadout; +{ + private _class = _extendedInfo getOrDefault [_x, ""]; + private _cache = missionNamespace getVariable (_x + "Cache"); + + // Previously voices were stored in lower case (speaker command returns lower case), so this is to make old loadouts compatible + if (_class != "" && {_x == QGVAR(voice)}) then { + _class = _class call EFUNC(common,getConfigName); + }; + if (_class != "" && {!(_class in _cache)}) then { + _missingExtendedInfo pushBack [_x, _class]; + _extendedInfo deleteAt _x; + }; +} forEach [QGVAR(insignia), QGVAR(face), QGVAR(voice)]; + // Raise event for 3rd party: mostly for handling extended info -[QGVAR(loadoutVerified), [_loadout, _extendedInfo]] call CBA_fnc_localEvent; +// Pass all items, including duplicates +[QGVAR(loadoutVerified), [_loadout, _extendedInfo, _nullItemsList, _unavailableItemsList, _missingExtendedInfo]] call CBA_fnc_localEvent; -[[_loadout, _extendedInfo], _nullItemsList arrayIntersect _nullItemsList, _unavailableItemsList arrayIntersect _unavailableItemsList] +[[_loadout, _extendedInfo], _nullItemsList arrayIntersect _nullItemsList, _unavailableItemsList arrayIntersect _unavailableItemsList, _missingExtendedInfo] diff --git a/addons/arsenal/ui/RscAttributes.hpp b/addons/arsenal/ui/RscAttributes.hpp index f1815d9ed3e..dc70caa0c27 100644 --- a/addons/arsenal/ui/RscAttributes.hpp +++ b/addons/arsenal/ui/RscAttributes.hpp @@ -564,6 +564,7 @@ class GVAR(display) { colorSelect[] = {1,1,1,1}; colorSelect2[] = {1,1,1,1}; colorPictureRightSelected[] = {1,1,1,1}; + colorTextRight[] = {0.5, 0.5, 0.5, 0}; onLBSelChanged = QUOTE(_this call FUNC(onSelChangedLeft)); onLBDblClick = QUOTE(_this call FUNC(onPanelDblClick)); onSetFocus = QUOTE(GVAR(leftTabFocus) = true); @@ -594,6 +595,7 @@ class GVAR(display) { colorSelect[] = {1,1,1,1}; colorSelect2[] = {1,1,1,1}; colorPictureRightSelected[] = {1,1,1,1}; + colorTextRight[] = {0.5, 0.5, 0.5, 0}; columns[] = {0.07, 0.15, 0.75}; idcLeft = IDC_arrowMinus; idcRIght = IDC_arrowPlus; diff --git a/addons/arsenal/ui/script_component.hpp b/addons/arsenal/ui/script_component.hpp deleted file mode 100644 index fcf9da9867e..00000000000 --- a/addons/arsenal/ui/script_component.hpp +++ /dev/null @@ -1 +0,0 @@ -#include "..\script_component.hpp" diff --git a/addons/common/functions/fnc_getConfigName.sqf b/addons/common/functions/fnc_getConfigName.sqf index 54ebf84343a..3495014d555 100644 --- a/addons/common/functions/fnc_getConfigName.sqf +++ b/addons/common/functions/fnc_getConfigName.sqf @@ -17,7 +17,7 @@ params ["_className"]; -(uiNamespace getVariable QGVAR(configNames)) getOrDefaultCall [toLower _className, { +(uiNamespace getVariable QGVAR(configNames)) getOrDefaultCall [toLowerANSI _className, { private _config = configNull; { diff --git a/addons/gunbag/XEH_preInit.sqf b/addons/gunbag/XEH_preInit.sqf index f7a3cb23a6a..c37533493d1 100644 --- a/addons/gunbag/XEH_preInit.sqf +++ b/addons/gunbag/XEH_preInit.sqf @@ -27,23 +27,34 @@ PREP_RECOMPILE_END; }] call CBA_fnc_addClassEventHandler; [QEGVAR(arsenal,loadoutVerified), { - params ["_loadout", "_extendedInfo"]; + params ["_loadout", "_extendedInfo", "", "", "_missingExtendedInfo"]; private _gunbagInfo = _extendedInfo getOrDefault [QGVAR(gunbagWeapon), []]; if (_gunbagInfo isEqualTo []) exitWith {}; private _weapon = (_gunbagInfo select 0) call EFUNC(arsenal,baseWeapon); if !(_weapon in EGVAR(arsenal,virtualItemsFlat)) exitWith { - INFO_1("removing [%1] from loadout",_gunbagInfo); + _missingExtendedInfo pushBack [QGVAR(gunbagWeapon), _weapon]; _extendedInfo deleteAt QGVAR(gunbagWeapon); }; + private _missingItems = []; + private _attachments = _gunbagInfo select 1; + { + if (_x != "" && {!(_x call EFUNC(arsenal,baseWeapon) in EGVAR(arsenal,virtualItemsFlat))}) then { + _missingItems pushBack _x; + _attachments set [_forEachIndex, ""]; + }; + } forEach _attachments; + private _magazines = _gunbagInfo select 2; { private _class = _x param [0, ""]; - private _defaultValue = ["", []] select {_x isEqualType []}; - if (_class != "" && {!(_class in EGVAR(arsenal,virtualItemsFlat))}) then { - INFO_1("removing [%1] from loadout",_x); - _gunbagInfo set [_forEachIndex + 1, _defaultValue]; + if !(_class != "" && {_class in EGVAR(arsenal,virtualItemsFlat)}) then { + _missingItems pushBack _class; + _magazines set [_forEachIndex, ["", 0]]; }; - } forEach (_gunbagInfo select [1]); // weapon was verified above + } forEach _magazines; + if (_missingItems isNotEqualTo []) then { + _missingExtendedInfo pushBack [QGVAR(gunbagWeapon), _missingItems]; + }; }] call CBA_fnc_addEventHandler; ["CBA_loadoutSet", { diff --git a/docs/wiki/framework/arsenal-framework.md b/docs/wiki/framework/arsenal-framework.md index a586d779e2c..35bcb3cefe4 100644 --- a/docs/wiki/framework/arsenal-framework.md +++ b/docs/wiki/framework/arsenal-framework.md @@ -511,7 +511,7 @@ All are local. | ace_arsenal_loadoutsDisplayClosed | None | 3.12.3 | | ace_arsenal_loadoutsTabChanged | loadouts screen display (DISPLAY), tab control (CONTROL) | 3.12.3 | | ace_arsenal_loadoutsListFilled | loadouts screen display (DISPLAY), tab control (CONTROL) | 3.12.3 | -| ace_arsenal_loadoutVerified | loadout data (ARRAY), loadout CBA extended data (HASHMAP) | 3.17.0 | +| ace_arsenal_loadoutVerified | loadout data (ARRAY), loadout CBA extended data (HASHMAP), null items (ARRAY), unavailable items (ARRAY), unavailable extended data (ARRAY) | 3.17.0 | | ace_arsenal_weaponItemChanged | weapon classname (STRING), item classname (STRING), item index (NUMBER, 0-5: muzzle, side, optic, bipod, magazine, underbarrel) | 3.16.0 | ## 9. Custom sub item categories