celludriel 79 Posted March 11, 2017 Goal Create a script library able to give objects a new Inventory UI that can store an unlimited weight of items but still quantify them. The current bis arsenal gives you an option to limit the choice of weapons to pick from but they are in fact unlimited to pick. I needed something that could hold unlimited items not constrained by weight limits but still the items would be quantified. Development cycle Agile Kanban This allows me to make increments to the library creating quickly a first implementation and adding to it with each iteration. I monitor progress on a trello scrum board and it will give others the option to join in if they would love to do so. https://trello.com/b/TARfxEdx/infinite-inventory Current backlog Make it possible to register objects to have infinite inventory and show a basic new GUI. It must be possible to add an item to the inventory, take one or add all. Filter placeholders should be in place but do not have to be functional yet. Code repository develop: https://github.com/Celludriel/InfiniteInventory/tree/develop Anyone is welcome to help in the development I will consolidate my own questions and others in this thread in the hope to keep things together and make this a successful library Release version: 1.0.2 github: https://github.com/Celludriel/InfiniteInventory/archive/1.0.2.zip pbo: http://users.telenet.be/sunspot/arma3/InfiniteInventory_102/InfiniteInventory.VR.pbo 2 Share this post Link to post Share on other sites
celludriel 79 Posted March 11, 2017 I'm currently a bit stuck on opening the dialog. It looks like I followed the right steps but I must have missed something. I have the following dialog hpp file and description.ext. ParentClasses166.hpp contains an export of the current parent classes of arma. class INFINV_Dialog { idd = -1; onLoad = ""; onUnload = ""; class Controls { class InfiniteInventoryFrame: RscFrame { idc = 1800; x = 0 * GUI_GRID_W + GUI_GRID_X; y = 0 * GUI_GRID_H + GUI_GRID_Y; w = 40 * GUI_GRID_W; h = 25 * GUI_GRID_H; }; }; }; enableDebugConsole = 1; // gui #include "gui\ParentClasses166.hpp" #include "gui\InfiniteInventoryDialog.hpp" I'm calling it like this params ["_container"]; private ["_ok"]; diag_log format ["Opening container: %1", _container]; _ok = createDialog "INFINV_Dialog"; if (!_ok) then {hint "Dialog couldn't be opened!"}; With following output in the client side error file 9:43:10 Starting mission: 9:43:10 Mission file: __cur_mp (__CUR_MP) 9:43:10 Mission world: VR 9:43:10 Mission directory: mpmissions\__CUR_MP.VR\ 9:43:12 Attempt to override final function - bis_functions_list 9:43:12 Attempt to override final function - bis_functions_listpreinit 9:43:12 Attempt to override final function - bis_functions_listpostinit 9:43:12 Attempt to override final function - bis_functions_listrecompile 9:43:12 Attempt to override final function - bis_fnc_missiontaskslocal 9:43:12 Attempt to override final function - bis_fnc_missionconversationslocal 9:43:12 Attempt to override final function - bis_fnc_missionflow 9:43:13 Mission id: 4c6ba9194dfc7eddd1930a3eb8647b770738d59e 9:43:55 "Opening container: testInventory" 9:43:55 Error loading control mpmissions\__CUR_MP.VR\description.ext/INFINV_Dialog/Controls/InfiniteInventoryFrame/ Is there a way to get a clearer error output then this abstract one ? Can anyone see what is going wrong exactly ? Share this post Link to post Share on other sites
celludriel 79 Posted March 11, 2017 Well I found the solution not sure it's the preferred one but it worked class INFINV_Dialog { idd = 1900; movingEnable = false; enableSimulation = 1; onLoad = ""; onUnload = ""; controlsBackground[] = { "InfiniteInventoryFrame" }; class InfiniteInventoryFrame: RscFrame { idc = 1800; x = 0 * GUI_GRID_W + GUI_GRID_X; y = 0 * GUI_GRID_H + GUI_GRID_Y; w = 40 * GUI_GRID_W; h = 25 * GUI_GRID_H; }; }; The dialog pops up and I can get going with my next tasks Share this post Link to post Share on other sites
celludriel 79 Posted March 12, 2017 I'm stuck on opening the inventory , I know the documentation says you can't override it but I wanted to at least give it a shot if(!isServer || hasInterface) exitWith {}; params ["_container"]; diag_log format ["Registering container: %1", _container]; _container setVariable ["INFINV_CONTENTS", []]; _inventoryOverrideFunction = { _container = _this select 0; diag_log format ["Overriding container opening for: %1", _container]; player setVariable ["InfInvActiveContainer", _container]; _openHandlerFunction = { _container = player getVariable "InfInvActiveContainer"; diag_log format ["Overriding open inventory for: %1", _container]; _container call InfInv_fnc_openInventory; true }; _closeHandlerFunction = { diag_log format ["Overriding closing inventory"]; _openHandler = player getVariable "InfInvOpenHandler"; _closeHandler = player getVariable "InfInvCloseHandler"; player setVariable ["InfInvOpenHandler", nil]; player setVariable ["InfInvCloseHandler", nil]; player setVariable ["InfInvActiveContainer", nil]; diag_log format ["Removing eventhandlers %1, %2", _openHandler, _closeHandler]; player removeEventHandler("InventoryOpened", _openHandler); player removeEventHandler("InventoryClosed", _closeHandler); }; _openHandler = player addEventHandler ["InventoryOpened", _openHandlerFunction]; _closeHandler = player addEventHandler ["InventoryClosed", _closeHandlerFunction]; diag_log format ["Adding eventhandlers %1, %2", _openHandler, _closeHandler]; player setVariable ["InfInvOpenHandler", _openHandler]; player setVariable ["InfInvCloseHandler", _closeHandler]; }; [_container, ["ContainerOpened", _inventoryOverrideFunction]] remoteExec ["addEventHandler", 0, true]; I tried by adding inventory handlers the second the container opens and removing them when it closes. Doesn't seem to work though the default inventory still opens when the container inventory is selected. Is there any way to accomplish it ? Otherwise I need a way to disable the regular inventory option on a container with inventory and do it with an AddAction command. I'm open to any suggestion Share this post Link to post Share on other sites
serena 151 Posted March 12, 2017 Variables with names beginning with an underscore are local. Local variables exist only in the scope where they are defined. More information: #Local_Variables 1 Share this post Link to post Share on other sites
celludriel 79 Posted March 12, 2017 32 minutes ago, serena said: Variables with names beginning with an underscore are local. Local variables exist only in the scope where they are defined. More information: #Local_Variables I understand, but when I set them with player setVariable ["InfInvOpenHandler", _openHandler]; that shouldn't matter, then for any of the handlers. It seems that when you open up a container "InventoryOpened" doesn't get triggered. Maybe I need to do a waitUntil the regular dialog opens and block it. I'm not sure, maybe it's just not possible with the current methods. Maybe you are right and I need to make them global and eventhandlers can't be assigned local , but still seems weird. I've been experimenting a bit in the meanwhile but without luck yet Share this post Link to post Share on other sites
serena 151 Posted March 12, 2017 Problems can appear when trying to access such function remotely or asynchronously Share this post Link to post Share on other sites
celludriel 79 Posted March 12, 2017 Well I gave it a shot without local variables if(!isServer || hasInterface) exitWith {}; params ["_container"]; diag_log format ["Registering container: %1", _container]; _container setVariable ["INFINV_CONTENTS", []]; [_container, ["ContainerOpened", { _container = _this select 0; diag_log format ["Overriding container opening for: %1", _container]; player setVariable ["InfInvActiveContainer", _container]; _openHandler = player addEventHandler ["InventoryOpened", { _container = player getVariable "InfInvActiveContainer"; diag_log format ["Overriding open inventory for: %1", _container]; _container call InfInv_fnc_openInventory; true }]; _closeHandler = player addEventHandler ["InventoryClosed", { diag_log format ["Overriding closing inventory"]; _openHandler = player getVariable "InfInvOpenHandler"; _closeHandler = player getVariable "InfInvCloseHandler"; player setVariable ["InfInvOpenHandler", nil]; player setVariable ["InfInvCloseHandler", nil]; player setVariable ["InfInvActiveContainer", nil]; diag_log format ["Removing eventhandlers %1, %2", _openHandler, _closeHandler]; player removeEventHandler("InventoryOpened", _openHandler); player removeEventHandler("InventoryClosed", _closeHandler); }]; diag_log format ["Adding eventhandlers %1, %2", _openHandler, _closeHandler]; player setVariable ["InfInvOpenHandler", _openHandler]; player setVariable ["InfInvCloseHandler", _closeHandler]; }]] remoteExec ["addEventHandler", 0, true]; Following output in the logs Client side on open inventory 21:09:11 "Overriding container opening for: testInventory" 21:09:11 "Adding eventhandlers 0, 0" Server side on registering the container 21:09:06 "Registering container: testInventory" 21:09:09 Mission id: b189d9c208fb04be7ded4e4b6c1512c3923ecf85 As you can see ContainerOpened does not trigger InventoryOpened as I hoped it would. I feel like strangling BIS on making ContainerOpened not overridable WHY tell me WHY ...grrrr I also learned I cannot hide the inventory option in the menu so if I go the addAction way I will always have Inventory as well in the action menu ... NOT a happy camper at the moment I also tried the following after a post I reread of Larrow if(!isServer || hasInterface) exitWith {}; params ["_container"]; diag_log format ["Registering container: %1", _container]; _container setVariable ["INFINV_CONTENTS", []]; [_container, ["ContainerOpened", { diag_log format ["Finding display 602"]; _display = objNull; while { isnull _display } do { _display = finddisplay 602; }; diag_log format ["Display 602 found"]; closeDialog 2; diag_log format ["Closing 602"]; _container call InfInv_fnc_openInventory; }]] remoteExec ["addEventHandler", 0, true]; Output 21:52:07 Mission id: ffc5932b2a5c8580b26c391788e40475ceac4124 21:52:09 "Finding display 602" 21:52:09 "Display 602 found" 21:52:09 "Closing 602" I was hoping to ok open the display but then immediatly close it and popup my dialog. Unfourtunatly this was a nogo either. I'm not sure it's display 602 that opens on opening a container inventory ? Share this post Link to post Share on other sites
celludriel 79 Posted March 12, 2017 BINGO got it Thanks @Larrow your previous post gave me some insight if(!isServer || hasInterface) exitWith {}; params ["_container"]; diag_log format ["Registering container: %1", _container]; _container setVariable ["INFINV_CONTENTS", []]; [_container, ["ContainerOpened", { _container = _this select 0; [_container] spawn { disableSerialization; diag_log format ["Finding display 602"]; waitUntil { !isNull ( findDisplay 602 ) }; _display = findDisplay 602; diag_log format ["Display 602 found"]; closeDialog 2; diag_log format ["Closing 602"]; _container = _this select 0; _container call InfInv_fnc_openInventory; }; }]] remoteExec ["addEventHandler", 0, true]; This made it happen Share this post Link to post Share on other sites
celludriel 79 Posted March 25, 2017 Well I'm stuck again on something I never should be stuck at ! SQF can be so frustrating I'm trying to call the server to get some data back. Here is my calling script and executing script params ["_display"]; private ["_container", "_contents"]; _container = INFINV_CURRENT_CONTAINER; diag_log format ["Calling InfInv_fnc_getContainerContents with %1", _container]; _contents = objNull; _contents = [_container] remoteExecCall ["InfInv_fnc_getContainerContents", 2]; waitUntil { !(isNull _contents) }; diag_log format ["InfInv_fnc_getContainerContents contents %1", _contents]; if(count _contents > 0) then { _lb = _display displayCtrl 1500; [_lb, _contents] call InfInv_fnc_loadListBoxFromData; }; if(!isServer || hasInterface) exitWith {}; params ["_container"]; diag_log format ["Getting data from %1", _container]; _retValue = _container getVariable ["INFINV_CONTENTS", []]; diag_log format ["Data %1", _retValue]; _retValue Now here is my server output and client output Server 18:42:04 "Getting data from testInventory" 18:42:04 "Data [[""FirstAidKit"",10]]" Client 18:42:04 "Calling InfInv_fnc_getContainerContents with testInventory" 18:42:04 "InfInv_fnc_getContainerContents contents " As you can see on server side I get the right data but when it is send back it arrives as empty string ... how come ? I know it can take a while for the command to execute on server, but that is why I put in a waitUntil ? Is there another way I should approach this ? remoteExecCall should be the right method isn't it ???? I don't get it :( Share this post Link to post Share on other sites
celludriel 79 Posted March 25, 2017 I decided to solve it another way, not my preferred but it works. I call the server with remoteexec, and the server calls the client with remoteexec. it works ... //Calling client script params ["_display"]; private ["_container", "_contents"]; _container = INFINV_CURRENT_CONTAINER; diag_log format ["Calling InfInv_fnc_getContainerContents with %1", _container]; [_container, clientOwner, _display] remoteExecCall ["InfInv_fnc_getContainerContents", 2]; //Responding server script if(!isServer || hasInterface) exitWith {}; params ["_container", "_clientID"]; diag_log format ["Getting data from %1", _container]; _retValue = _container getVariable ["INFINV_CONTENTS", []]; diag_log format ["Data %1", _retValue]; [_retValue] remoteExecCall ["InfInv_fnc_loadInventoryContainer", _clientID]; //Executing client script params ["_contents"]; private ["_display"]; diag_log format ["InfInv_fnc_getContainerContents contents %1", _contents]; _display = findDisplay 1900; if(count _contents > 0) then { _lb = _display displayCtrl 1500; [_lb, _contents] call InfInv_fnc_loadListBoxFromData; }; Share this post Link to post Share on other sites
mrcurry 509 Posted March 25, 2017 1 hour ago, celludriel said: I decided to solve it another way, not my preferred but it works. I call the server with remoteexec, and the server calls the client with remoteexec. it works ... It's the way I would do it. Another means would be publicVariable I suppose. Arma does not come with a decent "hey do this and get back to me" command out of the box (nor those it really need to). The reason you were running into the issue can be found on the BIKI for remoteExecCall: Quote Return Value: Anything - Nil in case of error. String otherwise. If JIP is not requested this is an empty string, if JIP is requested, it is the JIP ID. See the topic Function for more information. The return value is only relevant for JIP execution / handling. It's not the return value of the code you execute. That return value is simply discarded. 2 Share this post Link to post Share on other sites
celludriel 79 Posted March 26, 2017 << nm small brainfart >> I was using the same log message on two places seemed like something crazy was going on Share this post Link to post Share on other sites
celludriel 79 Posted March 27, 2017 Well I'm almost done with version 1.0.0. I got a test version here http://users.telenet.be/sunspot/arma3/InfiniteInventory/InfiniteInventory.VR.pbo 1 Share this post Link to post Share on other sites
celludriel 79 Posted March 27, 2017 There is one more script I need to write and to be honest I'm dreading it at the moment. I'll get it done but just thinking on what all can go wrong ... makes me squirm params ["_item"]; //check if is primary weapon //if primary weapon and has attachments put attachments in inventory if they can't fit on the ground //remove primary weapon //check if is secondary weapon //if secondary weapon and has attachements put attachments in inventory if they can't fit on the ground //remove secondary weapon //check if is handgun //if handgun and has attachements put attachements in inventory if they can't fit on the ground //remove handgun //check if is weapon in inventory //if weapon and has attachments put attachments in inventory if they can't fit on the ground //remove weapon from inventory //check if is attachment //check if attachment on primary weapon //remove attachment from primary weapon //check if attachment on secondary weapon //remove attachment from secondary weapon //check if attachment on handgun //remove attachment from handgun //check if attachment on any of the carried weapons in inventory //remove from carried weapon in inventory //attachment is only in inventory, remove it //regular item delete from inventory I want to create a function that you send an item string to any item string, and then deletes that item from the players inventory. The item can be an assigned weapon, an attachment on an assigned weapon, maybe a weapon in the inventory or an attachment in the inventory. It can be anywhere in the inventory, there could be multiple but I don't care which one get deleted as long as one gets deleted. This one is going to give me headaches ... I just know it Share this post Link to post Share on other sites
celludriel 79 Posted March 27, 2017 Well I got through it , but look at all this code I had to write. JUST to remove an item somewhere in the players inventory ... So unreal that a method like this isn't made by BI themselfes. I'm sure I missed some things and there could still be a bug I haven't encountered. How hard is it to look at the inventory of a player as a mini database. What do you need then, items with ID's ! and methods like updateItemWithID and deleteItemWithID ... but nooooooo a half a dozen methods that all do different things then you expect ... SOOOOOO FRUSTRATING params ["_item"]; diag_log format ["Removing _item: %1", _item]; private ["_deleteDone"]; _deleteDone = false; _detachWeaponItem = { params ["_weaponItem"]; if(typeName _weaponItem == "ARRAY") then { _weaponItem = _weaponItem select 0; }; if(_weaponItem != "") then { if(player canAdd _weaponItem) then { player addItem _weaponItem; } else { _ground = "GroundWeaponHolder" createVehicle (position player); _ground addItemCargo [_weaponItem, 1]; }; }; }; _hasAttachment = { params ["_haystack", "_needle"]; _result = false; { if(typeName _x == "ARRAY") then { if(count _x > 0) then { _x = _x select 0; } else { _x = ""; }; }; if(_needle == _x) exitWith { _result = true; }; } forEach _haystack; _result }; //check if is primary weapon if( _item == (primaryWeapon player) call Bis_fnc_BaseWeapon) exitWith { //if primary weapon and has attachments put attachments in inventory if they can't fit on the ground _weaponItems = primaryWeaponItems player; removeAllPrimaryWeaponItems player; { [_x] call _detachWeaponItem; } forEach _weaponItems; //remove primary weapon player removeWeapon (primaryWeapon player); }; //check if is secondary weapon if( _item == (secondaryWeapon player) call Bis_fnc_BaseWeapon) exitWith { //if secondary weapon and has attachements put attachments in inventory if they can't fit on the ground _weaponItems = secondaryWeaponItems player; { player removeSecondaryWeaponItem _x; } forEach _weaponItems; { [_x] call _detachWeaponItem; } forEach _weaponItems; //remove secondary weapon player removeWeapon (secondaryWeapon player); }; //check if is handgun if( _item == (handgunWeapon player) call Bis_fnc_BaseWeapon) exitWith { //if handgun and has attachements put attachements in inventory if they can't fit on the ground _weaponItems = handgunItems player; removeAllHandgunItems player; { [_x] call _detachWeaponItem; } forEach _weaponItems; //remove handgun player removeWeapon (handgunWeapon player); }; //remove weapon from uniform if present, put attachments in inventory if they can't put on the ground _uniform = uniformContainer player; if(!(isNull _uniform)) then { _weaponItems = weaponsItems _uniform; { if(_item == (_x select 0)) exitWith { { if(_forEachIndex == 0) then { player removeItemFromUniform _x; } else { [_x] call _detachWeaponItem; }; } forEach _x; _deleteDone = true; }; } forEach _weaponItems; }; if(_deleteDone) exitWith { true }; //remove weapon from vest if present, put attachments in inventory if they can't put on the ground _vest = vestContainer player; if(!(isNull _vest)) then { _weaponItems = weaponsItems _vest; { if(_item == (_x select 0)) exitWith { { if(_forEachIndex == 0) then { player removeItemFromVest _x; } else { [_x] call _detachWeaponItem; }; } forEach _x; _deleteDone = true; }; } forEach _weaponItems; }; if(_deleteDone) exitWith { true }; //remove weapon from backpack if present, put attachments in inventory if they can't put on the ground _backpack = backpackContainer player; if(!(isNull _backpack)) then { _weaponItems = weaponsItems _backpack; { if(_item == (_x select 0)) exitWith { { if(_forEachIndex == 0) then { player removeItemFromBackpack _x; } else { [_x] call _detachWeaponItem; }; } forEach _x; _deleteDone = true; }; } forEach _weaponItems; }; if(_deleteDone) exitWith { true }; //check for attachment //check if attachment on primary weapon _weaponItems = primaryWeaponItems player + primaryWeaponMagazine player; if(_weaponItems find _item >= 0) exitWith { //remove attachment from primary weapon player removePrimaryWeaponItem _item; }; //check if attachment on secondary weapon _weaponItems = secondaryWeaponItems player + secondaryWeaponMagazine player; if(_weaponItems find _item >= 0) exitWith { //remove attachment from secondary weapon player removeSecondaryWeaponItem _item; }; //check if attachment on handgun _weaponItems = handgunItems player + handgunMagazine player; if(_weaponItems find _item >= 0) exitWith { //remove attachment from handgun player removeHandgunItem _item; }; //check if attachment on any of the carried weapons in inventory _weaponItems = weaponsItems _vest; _weaponItems = _weaponItems + (weaponsItems _backpack); _weaponItems = _weaponItems + (weaponsItems _uniform); { diag_log format ["weaponitems %1", _x]; if([_x, _item] call _hasAttachment) then { diag_log format ["Attachment present %1", _x]; //delete the weapon player removeItem (_x select 0); { //only way is to fake disassembling the weapon and adding each part again except the one we don't want if(typeName _x == "ARRAY") then { _x = _x select 0; }; if(_x != _item && _x != "") then { player addItem _x; }; } forEach _x; _deleteDone = true; }; } forEach _weaponItems; if(_deleteDone) exitWith { true }; //item is only in inventory, remove it //regular item delete from inventory diag_log format ["Just removing %1", _item]; player removeItem _item; true Share this post Link to post Share on other sites
celludriel 79 Posted March 28, 2017 Version 1.0.0 is released. I just tested it by myself so I'm not sure if there are multiplayer bugs with regards to concurrency, I sure hope not. Tell me what you think and where I could improve a little. A little code review would be appreciated as well. I hope you can use this in any missions you will make. Share this post Link to post Share on other sites
Guest Posted March 29, 2017 Release frontpaged on the Armaholic homepage. Infinite Inventory v1.0.0 Share this post Link to post Share on other sites
celludriel 79 Posted March 29, 2017 There is always one that skips through if you do your own Q&A :( There was a bug when you took an item from the inventory that controls stayed locked. This only occured when ran on a dedicated server or you where not the hosting server. It was an easy fix and version 1.0.1 is released. Sorry for the inconvenience. Share this post Link to post Share on other sites
Guest Posted March 29, 2017 The updated version has been frontpaged on the Armaholic homepage. Infinite Inventory v1.0.1 Share this post Link to post Share on other sites
celludriel 79 Posted March 30, 2017 After a play session in my mission where I incorporated Infinite Inventory I stumbled on another bug. If you had weapons of a complex variety in your backpack and you added them to the inventory they didn't get properly removed from the player inventory. This caused item duplication, I made release 1.0.2 fixing this bug. You find the new links in my updated post above Share this post Link to post Share on other sites
barto300 13 Posted July 5, 2018 On 11-3-2017 at 9:56 AM, celludriel said: Snip As someone who has 0 knowledge of scripts.. how do I install this? Share this post Link to post Share on other sites
DIMENZz 0 Posted November 28, 2023 is it still working and is there a fresh download link Share this post Link to post Share on other sites
_foley 192 Posted November 28, 2023 9 hours ago, DIMENZz said: is it still working and is there a fresh download link These days you can set max load on a container, that will give you the same effect if you set it very high. Check out https://community.bistudio.com/wiki/setMaxLoad 1 Share this post Link to post Share on other sites