• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] [GUI] Easy Item Stack 'n Split v3.0.0

This bundle is marked as awaiting update. A staff member has requested changes to it before it can be approved.

Easy Item Stack 'n Split v3.0.0​


This system was originaly created by Dangerb0y, who left the Hive community a few years ago. The original post is here. And with his agreement (via PM), I'm maintaining it for the latest Warcraft 3 Reforged versions. I also did a few improvements.

FEATURES:
- Automatically stacks obtained items to an optional stack limit.
- Allows stacking items within a unit's inventory.
- Allows splitting item stacks within a unit's inventory.
- Allows units to seamlessly pick up items regardless of a full inventory.
- Copy-paste implementation.
- (Optional) Possibility to buy item charges even with inventory full. If charges go higher than the max, the remaining charges are dropped on the floor.

STACKING ITEMS:
- Obtained items are automatically stacked with same-type items in a unit's inventory.
- Placing same-type items on top of each other will stack them together.

SPLITTING ITEMS:
- Double right-clicking or dropping an item stack on itself will split 1 off the pile. (optional)
- Consecutively split items will stack together. (optional)
- If a unit's inventory slots are full when splitting an item stack, the split item will be dropped on the ground in front of the unit. (optional)

ITEM STACKS:
- The maximum stack size is set by the item's level in the Object Editor (Shift+Double-click allows entering levels greater than 8).
- Setting an item's level to 0 will give it an unlimited stack size.
- Items must have charges in order to be stacked; non-charged items will function as Blizzard intended.

RELEASE NOTES:
- This system can be implemented into just about any map.
- This system is completely leak-free.
- This system is completely lag-free.
- This system is fully MUI and MPI.
- This system works with all inventory sizes.
- This system does NOT use hashtables or gamecache, i.e. it is much faster.
- This system is available in vJASS and GUI.

How to install (vJASS):
1) Copy the trigger "EasyItemStacknSplit" to your Trigger Editor.

How to install (GUI):
1) In map view, go to File > Preferences... and tick "Automatically create unknown variables while pasting trigger data" under the General tab.
2) Copy the trigger "EasyItemStacknSplitGUI config" & "EasyItemStacknSplitGUI" to your Trigger Editor.

How to install (extension for buying charges even with inventory full)
- Unfortunately this requires to create a dummy item for each item that can be bought with inventory full.
1) In the Object editor, for each item create a copy with changing the following attributes:
> Remove all abilities
> Set "Status - Use Automatically When Acquired" = true
2) In your shop units "Sold Item List", replace the real items by their dummy copy.
3) In the system trigger, complete the variables DUMMY_ITEM_TYPES, REAL_ITEM_TYPES (for vJass version), or ShopDummyCharges, ShopRealCharges, ShopChargedItemCount (for GUI version).
4) In menu "Advanced" > "Game Interface": set "Text - Message - 'Inventory is full'" (Inventoryfull) to 1 space character " ".

v3.0.0:
1 - Added a message "inventory is full" when playing the inventory full error.
2 - Filtered out items with "Use on acquire" and "charges = 1".
3 - Added an extension to make possible buying item charges even when inventory is full.
4 - Added a 2nd extension to trigger a move item in-place event when stacking charges.
5 - Added a condition that limits the system to units with inventory of non-null size.
6 - Separated GUI config at init from GUI execution. Integer 8001 became deprecated.
7 - Fixed unhandled case when issuing an attack item order. Not-sure that order "851981 - getitem" is handled properly tho.
8 - Fixed an error in JASS&GUI when stacking an item on another of same type and not all charges cannot be transfered
9 - Fixed an error when PLAYITEMSOUND macro was called with fullpath instead of soundfile name without extension
10 - To try reducing desync, moved GetLocalPlayer so that only "PlaySound()" is executed inside.
Contents

Easy Item Stack 'n Split v2.7.4 (Map)

Reviews
Wrda
Moved to Awaiting update, as it should be.

Wrda

Spell Reviewer
Level 26
Joined
Nov 18, 2012
Messages
1,888
In UnitStackItem you have:
JASS:
    function UnitStackItem takes unit u, item item1 returns boolean
        local integer item1Charges = GetItemCharges( item1 )
        local integer inventorySize
        local integer item1Level
        local integer item1TypeId
        local item item2
        local integer item2Charges
        local integer item2Level
        local integer slot
        local real posX
        local real posY
        local real unitAngle


        // Make sure the item has charges
        if item1Charges <= 0 then
            // If not we just give it to the unit
            call DisableTrigger( gg_trg_EasyItemStacknSplit )
            call UnitAddItem( u, item1 )
            call EnableTrigger( gg_trg_EasyItemStacknSplit )
        else
            // Item has charges
            set inventorySize = UnitInventorySize( u )
            set item1Level = GetItemLevel( item1 )
            set item1TypeId = GetItemTypeId( item1 )
(...)
Just return false earlier and remove the else part.
JASS:
        // Make sure the item has charges
        if item1Charges <= 0 then
            // If not we just give it to the unit
            call DisableTrigger( gg_trg_EasyItemStacknSplit )
            call UnitAddItem( u, item1 )
            call EnableTrigger( gg_trg_EasyItemStacknSplit )
            return false
        endif
        // Item has charges
        set inventorySize = UnitInventorySize( u )
        set item1Level = GetItemLevel( item1 )
        set item1TypeId = GetItemTypeId( item1 )
(...)
Also it's needed to null item2 before the last two returns.
Sound variable also must be nulled, in the text macro.
JASS:
if deltax * deltax + deltay * deltay <= 22500 or orderId != 851986 then
Where does the number 22500 comes from? Curious.
I don't understand why you're using a text macro while the exact same thing can be done with a (private) function.
You should cache GetTriggerUnit and the other function calls since they'll be called several times within the loop in CancelController function

ActionController: There's not a single time that GetManipulatedItem returns null on such an event, unless it is manually removed (I haven't tested if one makes a trigger with the same event and remove the item makes a function ActionController GetManipulatedItem return null).
JASS:
                if ( BlzGetItemBooleanField(item1, ITEM_BF_USE_AUTOMATICALLY_WHEN_ACQUIRED) == true and GetItemCharges(item1) == 1 ) then
                    // Nothing to add in inventory, all charges will be used on acquisition
                else
                    // The item is being acquired so we stack it
                    if UnitStackItem(u, item1) then
                        // Couldnot stack all charges in inventory, some remain on the floor. Play the "Item Drop" sound
                        set p = GetOwningPlayer( u )
                        //! runtextmacro EasyItemStacknSplit_PLAYITEMSOUND( "HeroDropItem1.wav" )
                    endif
                endif
Invert it with "not", removing the else part.
JASS:
 if UnitInventoryFull(u) then
                            // The item is being targeted with a full inventory so we add it to the timer queue
                            set item1Charges = GetItemCharges( item1 )
                            set index = 0
                            if ( item1Charges > 0 ) then
                                // Item with charges, look up if there is an item available to receive those charges in unit inventory
                                set item1Level = GetItemLevel( item1 )
                                set item1TypeId = GetItemTypeId( item1 )
                                set index = 0
                                loop
                                    set item2 = UnitItemInSlot( u, index )
                                    set item2Level = GetItemLevel( item2 )
                                    if item2 != item1 and GetItemTypeId(item2) == item1TypeId and (not USE_ITEM_LEVEL or item2Level == 0 or GetItemCharges(item2) < item2Level) then
                                        // Found item with same type
                                        set index = inventorySize + 1
                                    else
                                        set index = index + 1
                                    endif
                                    exitwhen index >= inventorySize
                                endloop
"set index = 0" is being unnecessarily duplicated.
JASS:
                            else
                                // Full inventory error
                                call IssueImmediateOrder( u, "stop" )
                                set p = GetOwningPlayer( u )
                                // Play error sound
                                if ERROR_SOUND != null and ERROR_SOUND != "" then
                                    set str = ERROR_SOUND
                                    set snd = CreateSound( str, false, false, false, 12700, 12700, "" )
                                    call SetSoundVolume( snd, 127 )
                                    if GetLocalPlayer() != p then
                                        call StartSound( snd )
                                    endif
                                    call KillSoundWhenDone( snd )
                                    call ClearTextMessages()
                                    call DisplayTimedTextToPlayer(GetOwningPlayer(udg_EasyItem_unit),0.50,-1.00,2.00,"|cffffcc00"+ERROR_MESSAGE+"|r")
                                endif
                            endif
I wouldn't clear text messages as that can be quite bad. If you're concerned with the messages stacking, there's a library somewhere around for specifically error messages. Currently the error message didn't work, nor the sound.
Looping to find the index of the unit with the items and other loop to match the index with item type start to become costly if accumulated, a hashtable would be a safer option.
ActionController function is very long, too long that it seems like some parts of the logic could be split into smaller pieces.
The GUI version seems very messy, everything is in one place, with lots of "magic numbers", even with the developer info trigger comment it's pretty hard to figure out what's happening. So the logic should be split into sections.

All in all, it's another iteration of item stacking-splitting mechanic, with a few unique quirks, such as unstacking the item can also stack the item on the next inventory slot if done within a time limit. However, since some patches there's a native way of stacking by enabling it in the gameplay constants (no unstacking mechanism however), so it renders the stacking part of this system redundant. Baradé's system handles the unstacking quite well, but only vJASS and doesn't include GUI.
I still wonder if even after the changes there is equal footing between this system and Baradé's, as in, has other pros than the latter. One surely can be that it supports GUI.

Needs fix.
 
Top