• 🏆 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!

Bag System and Advanced Item Handling System

These are two very useful systems for RGP makers, they are MUI, leakfree and use vJass so you need JNGP to make them work well!!

Backpack System
the system can add as much 4-slot-bags as you want to as many units as you want.

the unit will get two special items in the last 2 slots:
next bag and previous bag

by using these items you change the active bag for the specific unit.

You can switch between 2 indication modes:
current bag number or next/previous bag number shown on the next and previous bag items...

items with bonuses won't work if they aren't in the active bag!!
it's more realistic:
if you have a sword in your backpack and not in your hand it won't help you!
Advanced Item System
The Advanced Item system improves the warcraft III item handling

if a unit gets an item with charges which it already has it stacks

in addition and thats what makes this system special, this works even if the inventory is full!!!

if you double-right-click an item in your inventory with charges it splits up
and if you move a item onto an equal item and they have charges they stack
the systems work without each other but i suggest using the Advanced Item system if you are using the Bag System for better inventory usage

give credits when used and +rep if you think it's worth it ;)


UPDATE 2.0: Fixed a bug

UPDATE 3.0: The system was never really MUI!! now it is

UPDATE 4.0: The System is now 1.24 compatible

UPDATE 5.0: A bug is fixed, removing a stacked item when double- rightclicking an item in full inventory

UPDATE 6.0: complete code reworked and hashtables integrated.
split into two seperate librarys
coding improved
bugs fixed

UPDATE 7.0: again code improvements and small fixes

UPDATE 8.0: fixed TriggerHappys suggestions, added a feature to switch between current bag number or next/previous bag number shown on the items, improved code

UPDATE 9.0: after a few years and longer inactivity someone complained over outdated/bugged code, so (to prove everyone and myself that i am still there) i rewrote the whole code to newest standard


JASS:
library BagSystem requires Alloc, Table, RegisterPlayerUnitEvent
//
// The_Witcher 's
//                 Bag System
//
// With this System you're able to give as many bags as you want to EVERY unit you want!
// The unit only needs to have the Inventory(hero) ability for an inventory with 6 slots
// you just need to create two useable items: one for next bag and one for previous bag
//
// you can add and remove bags whenever you want
//
// Requirements:
//  Alloc
//  RegisterPlayerUnitEvent
//  Table
//
// struct Bag:
// create takes unit u, integer maxbags
//       if you want a unit to use bags, you need to init that with this function
//
// addBags takes integer bags
//      with this function you can increase or decrease the amount of bags (add a negative number for the 'bags' value to lower it)
//      if there are items in the removed bag they will be dropped
//
// destroy
//     this function simply removes every bag from the unit
//     if you want the unit to use bags again, you have to use create once again
//
// Bag[myUnit]
//      the [] operator returns the Bag instance bound to the given unit


//---------------------------------------------------------
//----------------          SETUP            --------------
//---------------------------------------------------------
    globals
        // this is the rawcode of the item for next bag
        private constant integer NEXT_BAG_ID = 'I001'
     
        // this is the rawcode of the item for previous bag
        private constant integer PREV_BAG_ID = 'I002'
    
        // false= on previous bag is the number of the previous and on next bag the number of the next bag
        // true = on previous and next bag is the number of the current bag
        private constant boolean DISPLAY_CURRENT_BAG = true
        
        // whether to use a hook to destroy Bags automatically if the owner is removed
        private constant boolean REMOVE_HOOK = true
    endglobals

//---------------------------------------------------------
//-----------Don't modify anything below this line---------
//---------------------------------------------------------
    
    
    struct Bag extends array
        private static Table table
        readonly unit unit
        private Table items
        readonly integer max
        readonly integer bag
        
        implement Alloc
        
        static method operator [] takes unit u returns Bag
            return table[GetHandleId(u)]
        endmethod

        private method saveCurrentBag takes nothing returns nothing
            local integer i = 0
            loop
                exitwhen i >= 4
                set items[bag * 8 + i * 2] = GetItemTypeId(UnitItemInSlot(unit, i))
                set items[bag * 8 + i * 2 + 1] = GetItemCharges(UnitItemInSlot(unit, i))
                call RemoveItem(UnitItemInSlot(unit, i))
                set i = i + 1
            endloop
        endmethod
        
        private method refreshBagNumbers takes nothing returns nothing
            local integer next = bag + 1
            local integer prev = bag + 1
            call RemoveItem(UnitItemInSlot(unit, 4))
            call RemoveItem(UnitItemInSlot(unit, 5))
            call UnitAddItemToSlotById(unit, PREV_BAG_ID, 4)
            call UnitAddItemToSlotById(unit, NEXT_BAG_ID, 5)
            if not DISPLAY_CURRENT_BAG then
                if bag >= max - 1 then
                    set prev = IMaxBJ(1, bag)
                    set next = 1
                elseif bag <= 0 then
                    set prev = max
                    set next = 2
                else
                    set prev = bag
                    set next = bag + 2
                endif
            endif
            call SetItemCharges(UnitItemInSlot(unit, 4), prev)
            call SetItemCharges(UnitItemInSlot(unit, 5), next)
        endmethod
        
        private method dropLastBag takes nothing returns nothing
            local integer i = 0
            local item it
            local real x = GetUnitX(unit)
            local real y = GetUnitY(unit)
            loop
                exitwhen i >= 8
                set it = CreateItem(items[(max - 1) * 8 + i], x, y)
                call SetItemCharges(it, items[(max - 1) * 8 + i + 1])
                set items[(max - 1) * 8 + i] = 0
                set items[(max - 1) * 8 + i + 1] = 0
                set i = i + 2
            endloop
            set it = null
        endmethod

        method addBags takes integer bags returns nothing
            local integer s = max + bags
            if s < 1 then
                set s = 1
            endif
            if (bags < 0) then
                call saveCurrentBag()
                if (bag >= s) then
                    set bag = s - 1
                endif
                loop
                    exitwhen max == s
                    call dropLastBag()
                    set max = max - 1
                endloop
            else
                set max = max + bags
            endif
            call refreshBagNumbers()
        endmethod
        
        static method create takes unit u, integer maxbags returns Bag
            local Bag this = allocate()
            set unit = u
            set max = maxbags
            set bag = 0
            set items = Table.create()
            call refreshBagNumbers()
            set table[GetHandleId(u)] = this
            return this
        endmethod
        
        method destroy takes nothing returns nothing
            local integer i = 0
            call saveCurrentBag()
            loop
                exitwhen i > 5
                call RemoveItem(UnitItemInSlot(unit, i))
                set i = i + 1
            endloop
            loop
                exitwhen max <= 0
                call dropLastBag()
                set max = max - 1
            endloop
            set table[GetHandleId(unit)] = 0
            call items.destroy()
            call deallocate()
        endmethod
        
        private static method onBagChange takes nothing returns nothing
            local unit u = GetTriggerUnit()
            local Bag b = table[GetHandleId(u)]
            local integer i = 0
            local integer itemID = GetItemTypeId(GetManipulatedItem())
            if (itemID == PREV_BAG_ID) or (itemID == NEXT_BAG_ID) then
                call b.saveCurrentBag()
                if itemID == PREV_BAG_ID then
                    set b.bag = b.bag - 1
                    if b.bag < 0 then
                        set b.bag = b.max - 1
                    endif
                else
                    set b.bag = b.bag + 1
                    if b.bag >= b.max then
                        set b.bag = 0
                    endif
                endif
                loop
                    exitwhen i >= 4
                    call UnitAddItemToSlotById(u, b.items[b.bag * 8 + i * 2], i)
                    call SetItemCharges(UnitItemInSlot(u, i), b.items[b.bag * 8 + i * 2 + 1])
                    set i = i + 1
                endloop
                call b.refreshBagNumbers()
            endif
            set u = null
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_USE_ITEM, function Bag.onBagChange)
            set table = Table.create()
        endmethod
         
    endstruct
    
    static if (REMOVE_HOOK) then
    
        private function removeUnitHook takes unit u returns nothing
            if (Bag[u] != 0) then
                call Bag[u].destroy()
            endif
        endfunction
    
        hook RemoveUnit removeUnitHook
        
    endif
    
endlibrary


//Code indented using The_Witcher's Script Language Aligner
//Download the newest version and report bugs at www.hiveworkshop.com

JASS:
library AdvancedItemHandlingSystem requires Alloc, RegisterPlayerUnitEvent, Table
// The_Witcher's
//
// <<advanced item system>>
//
// Requirements:
//  Alloc
//  RegisterPlayerUnitEvent
//  Table
//
// This system improves the Warcraft III item engine
//
// If a unit gets an item which it already has it stacks (only if item has charges!)
// In addition this works even if the inventory is full!!!
//
// If you double-right-click an item with charges in your inventory it splits up
// and if you move an item onto an equal one and they are stackable they stack
//
// No Setup Part! Don't touch anything -> fully automatic :)


    private struct DataStruct extends array
        private static DataStruct array all
        private static integer total = 0
        private static Table table
        private static boolean systemOrder = false
        private static timer timer
        
        unit u
        item ite
        integer index
        real x
        real y
        implement Alloc
        
        private method destroy takes nothing returns nothing
            set table[GetHandleId(u)] = 0
            set total = total - 1
            set all[index] = all[total]
            set all[index].index = index
            call deallocate()
            if (total == 0) then
                call PauseTimer(timer)
            endif
        endmethod

        private static method isInventoryFull takes unit u returns boolean
            local integer i = 0
            loop
                exitwhen i == 6
                if UnitItemInSlot(u, i) == null then
                    return false
                endif
                set i = i + 1
            endloop
            return true
        endmethod

        private static method itemWalk takes nothing returns nothing
            local integer i = 0
            local unit u = GetTriggerUnit()
            local item ite = GetOrderTargetItem()
            local DataStruct this
            if isInventoryFull(u) and GetItemCharges(ite) != 0 then
                loop
                    exitwhen i > 5
                    if GetItemTypeId(ite) == GetItemTypeId(UnitItemInSlot(u, i)) and UnitHasItem(u, ite) == false then
                        call IssuePointOrder( u, "move", GetItemX(ite), GetItemY(ite) )
                        if table[GetHandleId(u)] == 0 then
                            set this = allocate()
                            set all[total] = this
                            set index = total
                            set total = total + 1
                            set .u = u
                            set .ite = ite
                            set x = GetItemX(ite)
                            set y = GetItemY(ite)
                            set table[GetHandleId(u)] = this
                            if (total == 1) then
                                call TimerStart( timer, 0.05, true, function DataStruct.itemTake )
                            endif
                        else
                            set this = table[GetHandleId(u)]
                            set .u = u
                            set .ite = ite
                            set x = GetItemX(ite)
                            set y = GetItemY(ite)
                        endif
                        set i = 100
                    endif
                    set i = i + 1
                endloop
                set this = table[GetHandleId(u)]
                if i < 10 and this != 0 then
                    call destroy()
                endif
            endif
            set u = null
            set ite = null
        endmethod

        private static method itemStack takes nothing returns nothing
            local integer i = 0
            local integer id
            local item k
            local item m = GetManipulatedItem()
            local unit u = GetTriggerUnit()
            local integer ite = GetItemTypeId(m)
            local integer c = GetItemCharges(m)
            local integer it
            if not systemOrder and GetItemCharges(GetManipulatedItem()) > 0 then
                loop
                    exitwhen i > 6
                    set it = GetItemTypeId(UnitItemInSlot(u, i - 1))
                    set k = UnitItemInSlot(u, i - 1)
                    if ( ( it == ite ) and( m != k ) ) then
                        call SetItemCharges( k, GetItemCharges(k) + c )
                        call RemoveItem( m )
                        set i = 10
                    endif
                    set i = i + 1
                endloop
            endif
            set k = null
            set m = null
            set u = null
        endmethod

        private static method itemTake takes nothing returns nothing
            local real dx
            local real dy
            local item it
            local integer i = 0
            local integer j
            local DataStruct this
            loop
                exitwhen i >= total
                set this = all[i]
                set dx = GetItemX(ite) - GetUnitX(u)
                set dy = GetItemY(ite) - GetUnitY(u)
                if ( dx * dx + dy * dy < 10000 ) and IsItemOwned(ite) == false then
                    set systemOrder = true
                    call IssueImmediateOrder(u, "stop")
                    set systemOrder = false
                    set j = 0
                    loop
                        exitwhen j > 5
                        set it = UnitItemInSlot(u, j)
                        if GetItemTypeId(ite) == GetItemTypeId(it) then
                            set j = 5
                        endif
                        set j = j + 1
                    endloop
                    call SetItemCharges( it, GetItemCharges(it) + GetItemCharges(ite) )
                    call RemoveItem( ite )
                    call destroy()
                    set i = i - 1
                endif
                set i = i + 1
            endloop
            set it = null
        endmethod

        private static method itemWalkAbort1 takes nothing returns nothing
            local DataStruct this = table[GetHandleId(GetTriggerUnit())]
            if this != 0 and( GetOrderPointX() != x or GetOrderPointY() != y ) then
                call destroy()
            endif
        endmethod

        private static method itemWalkAbort2 takes nothing returns nothing
            local DataStruct this = table[GetHandleId(GetTriggerUnit())]
            if not systemOrder and this != 0 then
                call destroy()
            endif
        endmethod

        private static method itemSplit takes nothing returns nothing
            local item it = GetOrderTargetItem()
            local integer c = GetItemCharges(it)
            local integer oId = GetIssuedOrderId()
            local unit u = GetOrderedUnit()
            if (oId > 852001 and oId < 852008) then
                if GetOrderTargetItem() == UnitItemInSlot(u, oId - 852002) then
                    if c > 1 then
                        set c = c / 2
                        call SetItemCharges(it, GetItemCharges(it) - c)
                        set systemOrder = true
                        set it = CreateItem(GetItemTypeId(it), GetUnitX(u), GetUnitY(u))
                        call UnitAddItem(u, it)
                        set systemOrder = false
                        call SetItemCharges(it, c)
                    endif
                else
                    if c > 0 and GetItemTypeId(it) == GetItemTypeId(UnitItemInSlot(u, oId - 852002)) then
                        call SetItemCharges(it, GetItemCharges(it) + GetItemCharges(UnitItemInSlot(u, oId - 852002)))
                        call RemoveItem(UnitItemInSlot(u, oId - 852002))
                    endif
                endif
            endif
            set it = null
            set u = null
        endmethod

        private static method onInit takes nothing returns nothing
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function DataStruct.itemWalkAbort1)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function DataStruct.itemWalkAbort2)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function DataStruct.itemWalk)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function DataStruct.itemSplit)
            call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_PICKUP_ITEM, function DataStruct.itemStack)
            set table = Table.create()
            set timer = CreateTimer()
        endmethod

    endstruct

endlibrary


//Code indented using The_Witcher's Script Language Aligner
//Download the newest version and report bugs at www.hiveworkshop.com

Keywords:
bag, system, item, stack, drop, vjass, jngp, rpg, campaign, mui, split, backpack, inventory, handle, improve, full
Contents

Bag System + Item Stack (Map)

Reviews
14:11, 17th Jun 2010 TriggerHappy: Very nice, though I think you should make NextBag/Previous bags charges show the page it will take you to, not the current page. In the future, indent your code better.

Moderator

M

Moderator

14:11, 17th Jun 2010
TriggerHappy:

Very nice, though I think you should make NextBag/Previous bags charges show the page it will take you to, not the current page.
In the future, indent your code better.
 
Level 6
Joined
Nov 10, 2006
Messages
181
Triggerhappy made something similar to this and my CIS library has something similar to this coded efficiently too.

Are bags limited to only 2? Because in the test map I could only switch to the second one.
 
Isn't this like your third bag system?
EDIT:
I may be wrong but i could swore that you had at least 2 more bag systems that got rejected/deleted before :p

well not really
i had one bag system before wich was based on the same idea, but it was highly inefficient and i decided to redo it completly
 

WBL

WBL

Level 2
Joined
Aug 12, 2009
Messages
4
Quick fix

A quick fix to the stackable items disappearing when sorting a full inventory. what do you think? bit inefficient?

insert code after line 350
Code:
// start insert code: gives item back
if ( IsItemOwned(it) != true ) then
        set bj_lastCreatedItem = CreateItem(itid, GetUnitX(dat.u), GetUnitY(dat.u))
        call UnitAddItem(dat.u, bj_lastCreatedItem)
        call SetItemCharges( bj_lastCreatedItem, charges )
endif
// end insert code

insert code after line 349
Code:
// save item info
set charges = GetItemCharges(dat.ite) / 2
set itid = GetItemTypeId(dat.ite)
 // end save item info

insert code after line 333
Code:
// create two local integers to store the item info
local integer charges = 0
local integer itid


* Oh. On another note, if it was possible to make a unit be able to stack items when purchasing from a shop with a full inventory, that would be great. Nevermind, I was just thinking, that could be done with tomes. Which isn't really a integral part of the system.

** Another idea. If a unit has full inventory but extra slots in another bag, throw the item they pick up into another bag. Just a thought. Sounds easier said than done right? :) Actually, this probably would be hard to do either, just switch bag quickly and pick up the item and then switch back to the current bag.
 
Last edited:
I've told you. Giving units items in the slot -13 will make them not show, not take space bug give bonuses.

BUT if i do so i can't remove the item again...
you can add items to any slot under 0 and they won't show and still give bonuses, but you'll never be able to remove them from there...
no function works for that <-- a new blizzard bug
 
Level 5
Joined
Sep 29, 2008
Messages
171
Making stuff in extra bags not give you bonuses also saves time triggering, yes? :p
Kinda nice idea, even if a hassle getting to potions when you really need em
 
You should really update this. Allow bonuses for bag items and remove items in slot 13 by UnitRemoveItem() native.

hmm don't know but maybe you didn't read the discussion here about that:
NO function can remove an item from slot -13! i can just add them but not remove them!!! try by yourself if you don't believe me!
 
Level 3
Joined
Jan 17, 2010
Messages
53
its rly nice just the thing i waz looking for thx man if i get it right and working in my map u will get the credit :p :D nice work . . .
 
Level 6
Joined
Feb 12, 2008
Messages
207
So... if i'm not wrong... can I use only the AIHSystem (Advanced Item Handling System) without the Bag System? If need only that one I mean.
 
Level 5
Joined
Sep 29, 2008
Messages
171
You know, you can do what Kingdom of Kaliron did and have the ability that switches between bags in some form of spell book, maybe have 4 or 5 different bags that you can access with two clicks. This opens up the bottom two slots and takes up one space on the hero's interface
 
Level 5
Joined
Sep 29, 2008
Messages
171
So, if you wanted to only have 2 bags, could you make one ability that switched between both? I'd rather have 2 bags with 5 slots each than 3 with 4, and it's easier to remember where everything is instead of flipping over and over.

Even better: does this work if you make it a hero ability instead of an item that switches bags? maybe have a spellbook with a list of bags you can open?
 
Top