• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJass] (System) CustomInventory

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
By far the biggest inventory system for Warcraft 3.
It's designed to have optional features, like FullScreenInventory-UI.

With a little bit of modification you can use this Inventory System in the normal Warcraft 3 Inventory too.

It is written in vJASS and has the following features:
- 12 Slot Equipment
- MPI
- Very flexible
- Easy to add bonuses to items
- Item Stacking
- Item Forging
- Item Sets (With bonuses)
- Inventory fixes warcraft 3 bugs (Double pickup bug)
- Fast
- Gives the ability to add your own areas with a little bit of code

A small note on the classes:

##############################################################################
# USER INTERFACE - CUSTOM WINDOW
##############################################################################
---------------
CWDestructable
---------------
It's an object that has an destructable for a local player.
(Such as icons, borders and more)

---------------
CWTrackable
---------------
This is basically an object that triggers player events for hovering the icons (CWDestructable).
Whenever an event occurs, it fires an action to the CustomWindow

---------------
CustomWindow
---------------
A collection of CWTrackable and CWDestructable. Every destructable will have an trackable object.
So whenever you hover over or click a trackable, this will be fired.
You will use this object to create hooks to the inventory system.

##############################################################################
# CUSTOM WINDOW - HOOK AND EXTENDING
##############################################################################

---------------
CIAll
---------------
This is a container object of items. It has conditions for pickup and drop and is the base
class for any container, like Slots, Potions, Equipment or even spell slots that you could create!
It has a variety of methods like adding, removing and checks and throws errors.

---------------
CIActions
---------------
This is a library which only use is to find out what the user wants to do. It supports up to 5
different steps which can be done in a row so if you need to build up something complicated here you go.
This action decides when to move, drop, pick and destroy items. It's bound to CIWindow and uses its
events to trigger / delegate events to the CISlots/CIEquips/CIPotions objects.

---------------
CIContent
---------------
This is a little help class for the CIActions object which puts a wrapper around the Equipment,
Potions and Slots. It will also update the camera.

---------------
CIWindow
---------------
This is the hook between CustomWindow, CIActions and the CustomInventory. It also provides
fake units to provide the Interface Functionality and delegates events to CIActions.

---------------
CISlots / CIEquips / CIPotions
---------------
The containers you see (Top left, top right and bottom left) are basically these objects.
They have special / unique conditions for pickup / drop and handle when to add or remove bonuses from the hero.

##############################################################################
# CUSTOM INVENTORY - CORE
##############################################################################

---------------
CIForging
---------------
This module forges items. You can combine any items and merge them to a bigger item. This will
always happen when your inventory updates.

---------------
CIStacking
---------------
You like items that can stack in your Inventory? Well this module is for you!
Create stackable items and make a maximum conditions. Item stacking is automatic, whenever
inventory updates.

---------------
CISet
---------------
Don't we all love this thing from World of Warcraft? Collecting items of a special set and
then get some bonus stuff once we have all of them. If you are cool, use this feature. It
even supports SET STACKING!

---------------
CIAttachment
---------------
Not done yet.

---------------
CIBonus
---------------
This is the hook between items and the unit state modifier library. You can use this module
to add and remove bonuses to the hero on the fly whenever needed.

---------------
CIError
---------------
Throw errors to the user whenever something bad happens.

---------------
CIEventHandler
---------------
Like every other Inventory system, we need some kind of hook for pickup/drop actions to
connect them with our CustomInventory. This will trigger the CustomInventory events.

---------------
CIEvents
---------------
Name may not be completely self-speaking, but this actually triggers the events on the item.
But only when the unit really has the item. (Safety layer)

---------------
CIItemDrop
---------------
This will allow pickup/drop actions of the items WITHOUT triggering the inventory events.
It will be required for many things so this module is a life safer!

---------------
CIItemInfo
---------------
Wouldn't it be cool if items could display what they are in a multiboard. Yes it would.
This module is exactly for that.

---------------
CIItemBoard
---------------
Yes, this is the multiboard which displays all the item statistics. It uses hooks to get
the attached events from the CUstomWindow.

---------------
CIItemProtection
---------------
Every item has states, like locked, owend, equiped and such. This module gives the ability
to change the states and make them available.

---------------
CIItemDest
---------------
Simple module for setting the destructable (icon) for the CustomWindow.

---------------
CIItemSeek
---------------
Module which is build to search and collect information about the inventory.
Find out how many items of which type you have that are not stacked, find out how many duplicated
items you have and even if you have errors in your inventory indexing.

---------------
CIItemSlot
---------------
Small module that adds classes to the items. You can only equip items with the right class
on the right positions.

---------------
CIItemEffect
---------------
This module allows to attach effects to an item wearer. Do whatever you want, but keep in mind
that one item has a maximum of 3 effects.

---------------
CIPowerup
---------------
Not done yet.

---------------
CISetup
---------------
Changes the area around the inventory to be black and create the UI for all players.


---------------
CustomItem
---------------
The item object itself. Will trigger and get triggered from all kind of sources.
Modules are completing the collection of members and methods.

---------------
CustomInventory
---------------
The main object. It forges, stacks, creates sets, triggers events, gets triggered and uses
optional modules to provide the full repository of features to the user.



-Fixed a bug found by Dr. Boom:
Dead units were able to change the inventory and caused bugs.
-Also added a little bit of documentation.


Keywords:
CustomInventory, Inventory, Custom, Anachron, FullScreen, Module, vJass, Stack, Forge, Combine, Set, Powerup, Drop, Equip, Equipment, Equip, System
Contents

CustomInventory - 0.2.2.6 (Map)

Reviews
18:27, 18th Feb 2010 The_Reborn_Devil: Ohsh- That describes pretty much what I'm thinking. My eyes were sore even before I was done reviewing half of the code. The code looks really good and I couldn't find just one little thing that would be...
Antares
While great at the time, this system uses an outdated implementation and has not survived the test of time. Simple
When the hero dies, you can still open the inventory. But when you pick a potion and try to place it into the hero inventory (at the inventar the 4 []), then the item is dropped on the system and lost.

You can put a filter urself on the part which opens the inventory which checks if the hero is alive or not... ^.^

because as baassee said...
 
Last edited:
Level 16
Joined
May 1, 2008
Messages
1,605
Moin moin =)

oO didn't really noticed that Anachon returned. Well I see his post is nearly a year ago and he didn't understand what bug I had, so I post it and hopefully he or someone else will see it then^^


Edit: omg .. the video is not so bright as the preview of it so don't worry...

Greetings and Peace
Dr. Boom
 
Holy crap, I did not account that.
Thanks for the bug, I check if I can get JNGP to work and will fix the bug.

Edit:
I did not really return. Just checking the forum from time to time.

Edit2:
I just released 0.2.2.6. The bug should be fixed now!
Additionally I attached a little description about classes to the first page, so developers can see what the classes are for!
 
Last edited:
I am not sure you understand my library.
I use trackables only for displaying item icons and actions for FullScreenUI.
There is no way how to use trackable/destructable (icons??) for the Board itself.

Thanks for the feedback though!

I think one of the best thing is that this system does not disselect hero and you can still cast spells and all.
 
Level 17
Joined
Jul 17, 2011
Messages
1,863
no no no thats not what i meant, you create a trackable for each "item" in the "inventory" and when the user moves the cursor over the "item" the trackable fires and a multiboard is created displaying the "item" stats etc. what i meant you could do is only set the "item" (which is a destructible) 's "Text - Name" to the "tooltip" on the multiboard and you get a similar result like a hero inventory eg the tooltip appears above the "item" there are some disadvantages to this 1. the destructible model "iconbase" displays its tooltips at random points 2. the tooltips cannot be too long

something like this
R8d3Z7HG4eRQLbl-Wie8mHjZhpbnpAgYWKec_KpW0QhbcGl_mtZqg5plkOcoCtDEnj2b72XlbQU
 
Last edited:
Last edited:
Level 16
Joined
May 1, 2008
Messages
1,605
1.) Why? It's small enough.
2.) Sounds like my idea gets to work - yes!
3.) No, what for...
4.) Drop item; trading done
5.) Don't we have enough of these already >_>

6.) But what you can make is a mailbox system with item attachments! Like for ORPG maps, one player sends a message and an item to some other player. Then 3 days later this player joins the ORPG and got message and item. Impossible but you should try it =)
 
Level 12
Joined
Sep 11, 2011
Messages
1,176
i have a problem when i save map

210329-albums5809-picture61469.jpg


i have 2 library called tables, but they are different. how can i solve this problem ?

this is the other tables i'm talking about :

JASS:
library Table // made by Bribe, special thanks to Nestharus, version 3.0.0.0
/*
    API
    
    ------------
    struct Table
    | static method create takes nothing returns Table
    |     create a new Table
    |    
    | method destroy takes nothing returns nothing
    |     destroy it
    |    
    | method flush takes nothing returns nothing
    |     flush all stored values inside of it
    |    
    | method remove takes integer key returns nothing
    |     remove the value at index "key"
    |    
    | method operator []= takes integer key, $TYPE$ value returns nothing
    |     assign "value" to index "key"
    |    
    | method operator [] takes integer key returns $TYPE$
    |     load the value at index "key"
    |    
    | method has takes integer key returns boolean
    |     whether or not the key was assigned
    |
    ----------------
    struct TableArray
    | static method operator [] takes integer array_size returns TableArray
    |     create a new array of Tables of size "array_size"
    |
    | method destroy takes nothing returns nothing
    |     destroy it
    |
    | method flush takes nothing returns nothing
    |     flush and destroy it
    |
    | method operator size takes nothing returns integer
    |     returns the size of the TableArray
    |
    | method operator [] takes integer key returns Table
    |     returns a Table accessible exclusively to index "key"
*/
    
globals
    private hashtable ht = InitHashtable() //The last hashtable you need
    private integer more = 2 //Index generation for Tables (above 2)
    private integer less = 0 //Index generation for TableArrays (below 0)
endglobals
    
private struct dex extends array
    static method operator size takes nothing returns Table
        return 1
    endmethod
    static method operator list takes nothing returns Table
        return 2
    endmethod
endstruct
    
private struct handles extends array
    method has takes integer key returns boolean
        return HaveSavedHandle(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSavedHandle(ht, this, key)
    endmethod
endstruct
    
private struct agents extends array
    method operator []= takes integer key, agent value returns nothing
        call SaveAgentHandle(ht, this, key, value)
    endmethod
endstruct
    
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$(ht, this, key, value)
    endmethod
    method has takes integer key returns boolean
        return HaveSaved$SUPER$(ht, this, key)
    endmethod
    method remove takes integer key returns nothing
        call RemoveSaved$SUPER$(ht, this, key)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro
    
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
    method operator [] takes integer key returns $TYPE$
        return Load$FUNC$Handle(ht, this, key)
    endmethod
    method operator []= takes integer key, $TYPE$ value returns nothing
        call Save$FUNC$Handle(ht, this, key, value)
    endmethod
endstruct
private module $TYPE$m
    method operator $TYPE$ takes nothing returns $TYPE$s
        return this
    endmethod
endmodule
//! endtextmacro
    
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
    
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
    
struct Table extends array
    
    // Implement modules for intuitive type-syntax
    implement realm
    implement booleanm
    implement stringm
    implement playerm
    implement widgetm
    implement destructablem
    implement itemm
    implement unitm
    implement abilitym
    implement timerm
    implement triggerm
    implement triggerconditionm
    implement triggeractionm
    implement eventm
    implement forcem
    implement groupm
    implement locationm
    implement rectm
    implement boolexprm
    implement soundm
    implement effectm
    implement unitpoolm
    implement itempoolm
    implement questm
    implement questitemm
    implement defeatconditionm
    implement timerdialogm
    implement leaderboardm
    implement multiboardm
    implement multiboarditemm
    implement trackablem
    implement dialogm
    implement buttonm
    implement texttagm
    implement lightningm
    implement imagem
    implement ubersplatm
    implement regionm
    implement fogstatem
    implement fogmodifierm
    implement hashtablem
    
    method operator handle takes nothing returns handles
        return this
    endmethod
    
    method operator agent takes nothing returns agents
        return this
    endmethod
    
    // set this = a[GetSpellAbilityId()]
    method operator [] takes integer key returns Table
        return LoadInteger(ht, this, key)
    endmethod
    
    // set a[389034] = 8192
    method operator []= takes integer key, Table a returns nothing
        call SaveInteger(ht, this, key, a)
    endmethod
    
    // set b = a.has(2493223)
    method has takes integer key returns boolean
        return HaveSavedInteger(ht, this, key)
    endmethod
    
    // call a.remove(294080)
    method remove takes integer key returns nothing
        call RemoveSavedInteger(ht, this, key)
    endmethod
    
    // Remove all data from a Table instance
    method flush takes nothing returns nothing
        call FlushChildHashtable(ht, this)
    endmethod
    
    // local Table a = Table.create()
    static method create takes nothing returns Table
        local Table this = dex.list[0]
        
        if this == 0 then
            set more = more + 1
            set this = more
        else
            set dex.list[0] = dex.list[this]
            call dex.list.remove(this)
        endif
        
        debug set dex.list[this] = -1
        return this
    endmethod
    
    // Removes all data from a Table instance and recycles its index.
    //
    //     call a.destroy()
    //
    method destroy takes nothing returns nothing
        debug if dex.list[this] != -1 then
            debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
            debug return
        debug endif
        
        call this.flush()
        
        set dex.list[this] = dex.list[0]
        set dex.list[0] = this
    endmethod
    
endstruct
    
struct TableArray extends array
    
    //Returns a new TableArray to do your bidding. Simply use:
    //
    //    local TableArray ta = TableArray[array_size]
    //
    static method operator [] takes integer array_size returns TableArray
        local Table a = dex.size[array_size] //Get the unique recycle list for this array size
        local TableArray this = a[0]         //The last-destroyed TableArray that had this array size
        
        debug if array_size <= 0 then
            debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
            debug return 0
        debug endif
        
        if this == 0 then
            set less = less - array_size
            set this = less
        else
            set a[0] = a[this]  //Set the last destroyed to the last-last destroyed
            call a.remove(this) //Clear hash memory
        endif
        
        set dex.size[this] = array_size //This remembers the array size
        return this
    endmethod
    
    //Returns the size of the TableArray
    method operator size takes nothing returns integer
        return dex.size[this]
    endmethod
    
    //da[integer a].unit[integer b] = unit u
    //da[integer a][integer c] = integer d
    //
    //Inline-friendly when not running in debug mode
    //
    method operator [] takes integer key returns Table
        static if DEBUG_MODE then
            local integer i = this.size
            if i == 0 then
                call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
                return 0
            elseif key < 0 or key >= i then
                call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
                return 0
            endif
        endif
        return this + key
    endmethod
    
    //Destroys a TableArray without flushing it; assumed you'd call .flush()
    //if you want it flushed too. This is public so that if you are flushing
    //instances the whole time you don't waste efficiency when disposing the
    //TableArray.
    //
    method destroy takes nothing returns nothing
        local Table a = dex.size[this.size]
        
        debug if this.size <= 0 then
            debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
            debug return
        debug endif
        
        if a == 0 then
            //Create an array to index recycled instances with their array size
            set a = Table.create()
            set dex.size[this.size] = a
        endif
        
        call dex.size.remove(this) //Clear the array size from hash memory
        
        set a[this] = a[0]
        set a[0] = this
    endmethod
    
    //All you need to know about this one is that it won't hit the op limit.
    private static method clean takes Table a, integer end returns nothing
        local integer i = a + 5000
        if i < end then
            call clean.evaluate(i, end)
            set end = i
        endif
        loop
            call a.flush()
            set a = a + 1
            exitwhen a == end
        endloop
    endmethod
    
    //Flushes the TableArray and also destroys it. Doesn't get any more
    //similar to the FlushParentHashtable native than this.
    //
    method flush takes nothing returns nothing
        local integer end = this.size + this
        debug if this == end then
            debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
            debug return
        debug endif
        call clean.evaluate(this, end)
        call this.destroy()
    endmethod
    
endstruct
    
endlibrary

i used them for item drop systems.. hope anyone would help me :)
 
Level 12
Joined
Sep 11, 2011
Messages
1,176
Maybe rename it?
I suggest rename the version from Bribe since I used Table by Vexorian in 1000 places.

where and how should i rename it?

tried to rename the trigger
  • Table Bribe
it's still showing the same error

tried renamed the library name
JASS:
library Table Bribe
still not working

i got this compile error error
line xxxx: not a valid library declaration: [Bribe]?


JASS:
library BribeTable
JASS:
library TableBribe
JASS:
library Tables

i got this
line xxxx: missing requirement : Table (libraries cannot contain scopes)
 
Sorry but seriously, think about it before doing.
You have a struct Table. The trigger name does not matter.
When you change the struct name (which you have to) you have to check the other libraries and wherever you read Table you have to change it to the new table name.

So instead of
JASS:
 library xyz requires Table
you have
JASS:
 library xyz requires BribeTable

But don't do this with my libraries.
 
Level 12
Joined
Sep 11, 2011
Messages
1,176
Sorry but seriously, think about it before doing.
You have a struct Table. The trigger name does not matter.
When you change the struct name (which you have to) you have to check the other libraries and wherever you read Table you have to change it to the new table name.

So instead of
JASS:
 library xyz requires Table
you have
JASS:
 library xyz requires BribeTable

But don't do this with my libraries.

thanks, it worked and i learned something new :)
+rep
 
Level 4
Joined
Apr 19, 2013
Messages
86
Hey man, first off props for this system, it's so versatile. Very well scripted too, I'm so bad at vjass, but your system inspired me to get back into it. I'm literally doing backflips for being able to use it.

I got everything imported successfully, no problems there. However, I was hoping you could point me the right direction quick. I want the inventory to be constructed at the start location for each player. I have it so each players start location is at an empty part of the map where there is room for the construction of the inventory destructibles. Technically all I have to do is define the 'point' right? After that each player inventory will correspond to that players hero, right? I don't really have to edit anything futher, its already mui from what I can tell. I was looking in the CISetup trigger and tried the following:

rect CI_UI_RECT = GetPlayerStartLocation(Player(i))

It says error, undeclared variable. I guess I don't remember much about vjass.

Am I in the right place? And sorry if you already answered a question like this, but I double checked all your notes and almost every page on this forum.

Also I wanted to ask if it was possible to directly add stats to items that are mathematical expressions, not just values, i.e.
(This is from the "Test" trigger)


set ci.dmg = 2*GetHeroLevel(hero)

or maybe something like

set ci.dmg = native GetRandomReal takes real 20., real 50. returns real

Would the ability libraries ect. handle these expressions properly and add the right amount of damage as is? If not would there be an easy tweak somewhere to adjust things properly, or are these things too unpractical? So far I haven't figured this out yet either.

Hopefully after a little hint my intuition will kick in haha. Thanks a bunch.
 
Last edited:
Level 2
Joined
Aug 17, 2012
Messages
10
Sorry but seriously, think about it before doing.
You have a struct Table. The trigger name does not matter.
When you change the struct name (which you have to) you have to check the other libraries and wherever you read Table you have to change it to the new table name.

So instead of
JASS:
 library xyz requires Table
you have
JASS:
 library xyz requires BribeTable

But don't do this with my libraries.

Can you explain more clearly, i have change name of Bribe Table struct and got an error

Line xxxx: method create must return BribeTable

JASS:
    static method create takes nothing returns Table
 
Top