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

Inventory v.1.7

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.

Inventory


Inventory is a full screen inventory interface for Warcraft III.


First and foremost I have to mention that Inventory is a not even halfway finished system,
however what I coded so far is working and therefore I submit it here for you.
I will update the presentation in this thread together with the Inventory system.


The list of requirements is huge and so is the installation.
I'll try to make it slimer in the next updates.
Obligatory RequirementsOptional Requirements

  • -

What can Inventory doWhat Inventory can't do yet

  • A backpack interface with pages.
  • An equipment interface.
  • An optional dummy portrait.
  • Displaying unit stats.
  • Selling items to merchants.
  • Creating static and dynamic items.
  • Using Abilities/Bonuses/ItemPowers as item affixes.
  • Properly firing events for all interfaces.
  • Buying items from merchants.
  • Trading with other players.
  • Interact with the native inventory UI.
  • Sockets / socket combinations.
  • Level limitation for items.
  • .... Stuff I didn't yet think about.

As user you will not have to care about the code, as all you can configurate will
be listed in the user settings:
JASS:
library InventoryUserSetup/* By BPower
*************************************************************************************
*  
*   Covers all fields which you can customize in Inventory.
*
*************************************************************************************
*
*   */ uses /*
*           
*       */ UIPackage  /* hiveworkshop.com/forums/lab-715/uipackage-full-screen-system-267876/
*       */ CustomItem /* Included in this map. 
*
*************************************************************************************/

//=========================================================================
// External object merger.
// This tool can generate a bunch of destructable objects
// for you, so you don't have to copy 'n' paste everything from this map.
//=========================================================================

    globals
        constant boolean INVENTORY_GENERATE_OBJECTS = false
        //
        // 1.) Set INVENTORY_GENERATE_OBJECTS = true.
        // 2.) Save your map, close the editor, start the editor and open your map.
        // 3.) set INVENTORY_GENERATE_OBJECTS = false.
    endglobals

//=========================================
// Inventory constants.
// Like the game play constants, 
// but for Inventory.
//=========================================
    
    globals
        
        // Shop related.
        constant real    INVENTORY_PAWN_ITEM_REFUND_FACTOR = .75 
        constant integer INVENTORY_MERCHANT_EXTRA_PAGES    = 0
        constant real    INVENTORY_MAXIMUM_SHOP_RANGE      = 450.
        
        // Tooltip related.
        constant string  INVENTORY_RESOURCE_GOLD_ICON      = "UI\\Feedback\\Resources\\ResourceGold.blp"
        constant string  INVENTORY_RESOURCE_WOOD_ICON      = "UI\\Feedback\\Resources\\ResourceLumber.blp"
        
        // Font related.
        constant real    INVENTORY_FONT_SIZE               = 8.// Recommended setting is 8.
        
        // Cell selector.
        constant integer INVENTORY_CELL_SELECTOR_ID        = 'Bisa'
        constant real    INVENTORY_CELL_SELECTOR_SCALE     = .75
        
        // Default disabled path / object.
        // This file / id is used, when no disabled path / id is set for an object.
        // The string refers to an image file path, the id to a destructable object.
        // If declared the string file path is used over the the destructable id.
        constant string  CUSTOM_ITEM_DIS_ICON_PATH = null
        constant integer CUSTOM_ITEM_DIS_ICON_ID   = 'Biba'
    endglobals

//=============================================
// Global default values.
// These informactions are used,
// if not specified different per instance.
//=============================================

    // Default font for textsplats in every Inventory instance. 
    // Use instance.setFont(desiredFont) to change the font of an inventory instances.
    constant function Inventory_GetDefaultFont takes nothing returns font
        return TREBUCHET_MS
    endfunction
    
    // Set the font color for interface errors in ARGB.
    function Inventory_GetMsgFontColor takes nothing returns ARGB
        return ARGB.create(255, 218, 165, 32)
    endfunction

//==================================================
// Struct and race based fields.
// The setup below is specific for each
// individual interface window of Inventory.
//==================================================
    
    //=========================================
    // Equipment related setup.
    //=========================================
    //  To change the visual layout and / or the item classes of buttons, go to library "Inventory Equipment",
    // --> "static method onCreate takes Inventory inventory returns thistype" 
    // and configurate the buttons coordinates and data to your needs.
     
    // Set user interface border fields. 
    function Inventory_SetEquipmentBorderFields takes unit source returns nothing
        local race r = GetUnitRace(source)
        // Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
        // you don't specify the path. You can also change the default setting inside the UIBasic library.
        if (r == RACE_ORC) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftOC.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightOC.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpOC.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownOC.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftOC.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftOC.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightOC.TGA"
        elseif (r == RACE_NIGHTELF) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftNE.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightNE.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpNE.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownNE.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftNE.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftNE.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightNE.TGA"        
        endif
        set UI_BORDER_BACKGROUND_COLOR = ARGB.create(255, 0, 0, 0)
        set UI_BORDER_BACKGROUND       = "war3mapImported\\background.TGA"
        set r = null
    endfunction 
    
    //=========================================
    // Backpack related setup.
    //=========================================
    // To change the visual layout and / or the item classes of buttons, go to library "Inventory Backpack",
    // --> "static method onCreate takes Inventory inventory returns thistype" 
    // and configurate the buttons coordinates and data to your needs.
     
    // Set the backpack row count.
    constant function Inventory_GetBackpackRowCount takes unit source returns integer
        return 4
    endfunction
    
    // Set the backpack column count.
    constant function Inventory_GetBackpackColumnCount takes unit source returns integer
        return 5
    endfunction
    
    // Add extra pages to the backpack. If you don't want to add pages pass in 0 or 1.
    constant function Inventory_GetBackpackPageCount takes unit source returns integer
        return 3
    endfunction
    
    // Set the path for the image used as empty slot.
    constant function Inventory_GetBackpackSlotFilePath takes unit source returns string
        return "UI\\Console\\Human\\human-transport-slot.blp"
    endfunction
    
    // Set user interface border fields. For full API check the available fields for UIBorder.
    function Inventory_SetBackpackBorderFields takes unit source returns nothing
        local race r = GetUnitRace(source)
        // Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
        // you don't specify the path. You can change the default setting inside the UIBasic library.
        if (r == RACE_ORC) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftOC.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightOC.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpOC.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownOC.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftOC.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftOC.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightOC.TGA"
        elseif (r == RACE_NIGHTELF) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftNE.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightNE.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpNE.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownNE.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftNE.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftNE.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightNE.TGA"        
        endif
        set UI_BORDER_BACKGROUND       = null
    endfunction 
    
    //=========================================
    // Controll panel related setup.
    //=========================================
    // To change the visual layout, go to library "Inventory Controll",
    // --> "static method onCreate takes Inventory inventory returns thistype" 
    // and configurate the buttons coordinates to your needs.
     
    // Set the path for the scroll up arrow.
    constant function Inventory_GetScrollUpPath takes unit source returns string
        return "war3mapImported\\ScrollUp.tga"
    endfunction
     
     // Set the path for the scroll down arrow.    
    constant function Inventory_GetScrollDownPath takes unit source returns string
        return "war3mapImported\\ScrollDown.tga"
    endfunction
     
     // Set the destructable id for the drop item button. You can also use an image file, 
     // but then you have to change addDestButton to addImageButton inside the library.
    constant function Inventory_GetDropButtonId takes unit source returns integer
        return 'Bibb'
    endfunction
    
     // Set the destructable id for the close inventory button. You can also use an image file, 
     // but then you have to change addDestButton to addImageButton inside the library.
    constant function Inventory_GetCloseButtonId takes unit source returns integer
        return 'Biba'
    endfunction
    
    // Set the destructable id for the pawn item button.
    constant function Inventory_GetPawnButtonId takes unit source returns integer
        return 'Bibc'
    endfunction
    
    // Set the destructable id for the buy item button.
    constant function Inventory_GetBuyButtonId takes unit source returns integer
        return 'Bibd'
    endfunction
    
    // Set the background image path.
    constant function Inventory_GetBackgroundFilePath takes unit source returns string
        return "UI\\Widgets\\EscMenu\\Human\\human-options-button-background-disabled.blp"
    endfunction
    
    // Colorize the background image via an ARGB value.
    function Inventory_GetBackgroundColor takes unit source returns ARGB
        if (GetUnitRace(source) == RACE_NIGHTELF) then
            return ARGB.create(255, 150, 255, 150)
        endif
        return ARGB.create(255, 139, 69, 19)
    endfunction
    
    //===========================================
    // Dummy window and affix box related setup.
    //===========================================
    // This setup relates to the unit affix box, 
    // which is displayed when clicking the user interface dummy unit.
    //
    // The affix box is one the more heavy operations within the inventory source code.
    // Please do not overdo it in terms of text rows. Another factor are color codes.
    // Color codes have to be translated to hexadecimal, then to ARGB and through this add overhead.
    //
    // The box automatically updates on equip and unequip. 
    // You can update it manually by calling:
    //
    // function Inventory_UpdateAffixBox takes unit source returns nothing
    
    // Set which units should allocate an affix box at all.
    function Inventory_AllocateAffixBoxForUnit takes unit source returns boolean
        return IsUnitType(source, UNIT_TYPE_HERO)
    endfunction
    
    // Set the headline text.
    function Inventory_GetAffixBoxHeadline takes unit source returns string
        return "|cffdaa520Attributes:|r" 
    endfunction
    
    // Set the amount of rows inside the box.
    function Inventory_GetAffixBoxRowCount takes unit source returns integer
        return 4
    endfunction
    
    // Set the string per row within the affix box. First row index is 0.
    function Inventory_GetAffixBoxRowString takes unit source, integer row returns string
        if     (row == 0) then
            return"|cffdaa520Strength:|r " + I2S(GetHeroStr(source, true))       
        elseif (row == 1) then
            return "|cffdaa520Agility:|r " + I2S(GetHeroAgi(source, true))
        elseif (row == 2) then
            return "|cffdaa520Intelligence:|r " + I2S(GetHeroInt(source, true))
        endif
        return "|cffdaa520Movement Speed:|r " + I2S(R2I(GetUnitMoveSpeed(source)))
    endfunction
    
    // Set border fields for the dummy window.
    function Inventory_SetDummyBorderFields takes unit source returns nothing
        local race r = GetUnitRace(source)
        if r == RACE_NIGHTELF then
            set UI_BORDER_BACKGROUND   = "UI\\Glues\\Loading\\Backgrounds\\Campaigns\\NightElfSymbol.blp"
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftNE.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightNE.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpNE.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownNE.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftNE.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftNE.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightNE.TGA"
        elseif r == RACE_ORC then
            set UI_BORDER_BACKGROUND   = "UI\\Glues\\Loading\\Backgrounds\\Campaigns\\OrcSymbol.blp"
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftOC.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightOC.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpOC.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownOC.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftOC.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftOC.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightOC.TGA"
        endif
        // Higher layer than the equipment.
        set UI_BORDER_BACKGROUND_IMAGE_TYPE = IMAGE_TYPE_OCCLUSION_MASK
        set r = null
    endfunction 
    
    // Set border fields for the affix box window.
    function Inventory_SetAffixBoxBorderFields takes unit source returns nothing
        local race r = GetUnitRace(source)
        // Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
        // you don't specify the path. You can change the default setting inside the UIBasic library.
        if (r == RACE_ORC) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftOC.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightOC.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpOC.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownOC.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftOC.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftOC.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightOC.TGA"
        elseif (r == RACE_NIGHTELF) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftNE.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightNE.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpNE.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownNE.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftNE.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftNE.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightNE.TGA"        
        endif
        set UI_BORDER_BACKGROUND_COLOR = ARGB.create(255, 0, 0, 0)
        set UI_BORDER_BACKGROUND       = "war3mapImported\\background.TGA"
    endfunction                     
    
    //===========================================
    // Native Inventory related setup.
    //===========================================
    constant function Inventory_GetSlotFiller takes unit source returns string
        return "war3mapImported\\background.TGA"
    endfunction

    //===========================================
    // Tooltip box related setup.
    //===========================================
    
    function Inventory_SetTooltipBoxBorderFields takes unit source returns nothing
        local race r = GetUnitRace(source)
        // Hint: UIBorder uses a default setting ( null ) for each border brick, which is used if
        // you don't specify the path. You can change the default setting inside the UIBasic library.
        if (r == RACE_ORC) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftOC.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightOC.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpOC.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownOC.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftOC.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightOC.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftOC.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightOC.TGA"
        elseif (r == RACE_NIGHTELF) then
            set UI_BORDER_LEFT         = "war3mapImported\\BorderLeftNE.TGA"
            set UI_BORDER_RIGHT        = "war3mapImported\\BorderRightNE.TGA"
            set UI_BORDER_TOP          = "war3mapImported\\BorderUpNE.TGA"
            set UI_BORDER_BOTTOM       = "war3mapImported\\BorderDownNE.TGA"
            set UI_BORDER_BOTTOM_LEFT  = "war3mapImported\\BorderDownLeftNE.TGA"
            set UI_BORDER_BOTTOM_RIGHT = "war3mapImported\\BorderDownRightNE.TGA"
            set UI_BORDER_TOP_LEFT     = "war3mapImported\\BorderUpLeftNE.TGA"
            set UI_BORDER_TOP_RIGHT    = "war3mapImported\\BorderUpRightNE.TGA"        
        endif
        set UI_BORDER_BACKGROUND = "war3mapImported\\background.TGA"
    endfunction 

endlibrary


Keywords:
Custom UI, Inventory
Contents

Inventory (Map)

Reviews
19:19, 3rd Apr 2016 Flux: Set to Needs Fix on author's request.
Level 19
Joined
Mar 18, 2012
Messages
1,716
Changelog
Version 1.0

Version 1.1
  • Added a global hashtable over which the entire system should finally run. ( doing this step by step )
  • Added a recycler to the affix handler, so keys never overflow.
  • Made space for ~75000 items ( during the same time ). If an item leaves the map the recycler ( point above ) accesses and frees the occupied child keys.
  • Shops are now detected by a get closests shop search when opening the interface. Conditions:
    1. A shop must have the Shop Purchase Item ('Apit') ability.
    2. The shop must be an ally or neutral affected to the inventory owning unit.
    3. The shop must be in INVENTORY_MAXIMUM_SHOP_RANGE ( user configuration )
Version 1.2
  • Re-designed the CustomItem library. It works now like PUI + All player unit item events to detect items. Cleanup is done every new 16 items detected.
  • CustomItem now accesses the shared global hashtable instead of initializing its own.
  • I wrote an documentation of the API ( available in the main thread )
Version 1.4
  • Re-wrote the code for the item parser, which generates random items. It's now really nice.
Version 1.3
  • Re-wrote the entire way item affixes saved, item names are generated, tooltips are created.... That part is now really epic.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Played around with it and I noticed that you can't sell items in your inventory
Yes, a good merchant implementation is very difficult to realise.
Actually you can sell an item, if you go close to the goblin shop and use the select hero ability of the shop.
Then the shop interface opens when you open the inventory.

It gets tricky, when you have to decide if a shop has a local or global item stock.
By global I mean if unit A buys an item and unit B watches the shop. The item vanishes for units B.

More tricky is the stock update and how to choose random items.

Even worse, should sold items be gone or added to the stock.

I'm working on it.

Also, if you right click while inside the inventory, your hero will move.
I know. You can't disable the user controll, while keeping the UI controll up.
A user can pause his main char, when opening the Inventory.
Still all other units could be moved. I can't change that.

The only possible thing would be to detect point orders inside the UI area and cancel them.

Also, this:
This happens if an item has no string path or destructable object registered.
The system has to draw an icon for an item, therefore a use must set a path / object.
Probably happened when you bought an item from the shop.

I will improve the demo and register more items asap.

Edit: I found a mistake in the affix handle. Gonna update soon.
 
Last edited:
Yes, a good merchant implementation is very difficult to realise.
Actually you can sell an item, if you go close to the goblin shop and use the select hero ability of the shop.
Then the shop interface opens when you open the inventory.

It gets tricky, when you have to decide if a shop has a local or global item stock.
By global I mean if unit A buys an item and unit B watches the shop. The item vanishes for units B.

More tricky is the stock update and how to choose random items.

Even worse, should sold items be gone or added to the stock.

I'm working on it.
Maybe you could add an icon like the drop item one that is normally greyed out and become clickable once ur in range of a unit with the sell item ability? If there are multiple shop then just pick the closest one for the stock.

I know. You can't disable the user controll, while keeping the UI controll up.
A user can pause his main char, when opening the Inventory.
Still all other units could be moved. I can't change that.

The only possible thing would be to detect point orders inside the UI area and cancel them.
Maybe order the Hero to stop whenever he's given an order while the inventory is opened?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Maybe you could add an icon like the drop item one that is normally greyed out and become clickable once ur in range of a unit with the sell item ability? If there are multiple shop then just pick the closest one for the stock.
Now the shop interface opens with the inventory, when you are close to the shop.
The closest shop will interact with you.

Thanks for that input.

Maybe order the Hero to stop whenever he's given an order while the inventory is opened?
I'll think about it in the next version. Probably I will only cancel point orders within the UI rect.
All other orders are allowed to take place.


Anyway I want to change the way items are detected. ( As we all know there is no good ItemIndexer and please don't try to make one )
Currently the most common unit item events detect items and convert them on the first interaction to CustomItems.
In future I want to go with a PUI like method.
( PUI is a unit indexer design which indexes units the first time GetUnitIndex(unit) is used. )

Edit: Update to version 1.2. Changelog is in post #1
I'm working now on a better algorithm of how dynamic items are generated.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Aside from the original inventory not displaying certain items in the coded UI version
I'm already thinking to totally remove a sync with the native inventory.
There is actually not so much use in having it, but it automatically comes along with a lot of flaws.

About displaying items
That's the biggest downside of any coded custom inventory.
We can't access the texture path of an object set in the Object Editor.
That's why we need object data ( destructables ) using the desired texture to display an icon ingame.
I went a bit further and enabled the option to pass in the texture path directly into function:

It's enough to pass either a destructable id or a file path. A DIS path is in 99% of all cases also not needed.
JASS:
function SetCustomItemIcon takes integer itemId, string filePath, string DISfilePath, integer destId, integer DISdestId returns nothing

The system knows if you passed in a destructable object or a file path,
then either creates a destructable or an image handle to display the item icon.

Here comes a downside of Warcraft III ... texture paths of standard icons
don't have a transparent pixel border and therefore are not displayed correctly as image.

Do you plan to add shop items in the 'sell item UI' box at the top left?
At the moment I'm working on how random item affixes are rolled.
Currently it goes in one big operation, but in the future is goes step by step.
Especially the translation of rolled affixes to item tooltip must be improved.

After that I want to work on the shop interface and tackle a Save & Load implementation.
The latter will probably fail :D
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
There is actually not so much use in having it
I'm absolutely disagree with this. Syncing the native inventory will absolutely helpful or let's say it's must have. Without this, player can't easily access the wanted item without opening the full screen inventory, which would be very disturbing since he/she will lose sight of his unit(s).

I have worked on FSI several times actually. And all of those are synced (have access) to native inventory. And I didn't find any flaw, tho they require much works around. It's quite troublesome when we need to sync the custom and the native one when one or some items are being used (under cooldown).

I synced my custom and the native one pretty much flawlessly here (I think only mod can view it since it's deleted). But yeah, it's not even MPI. Those features are what I was trying to achieve the last time I worked on another FSI. But using sharpcraft was a big mistake (for the fancy icon dragging effect). And I'm too lazy to start over. Plus, your FSI is one step ahead for having separate UI system, which is much neater ;)

EDIT:
Have just re-checked my system again, and it's not that flawless actually lol
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Have just re-checked the system again, and it's not that flawless actually lol
Of course not. There are a lot of areas which need improvement.
I working heavily on it :)
I'm absolutely disagree with this. Syncing the native inventory will absolutely helpful or let's say it's must have. Without this, player can't easily access the wanted item without opening the full screen inventory, which would be very disturbing since he/she will lose sight of his unit(s).
The current design makes two special rules for items:

1.) Power up item types are invalid for custom UI interaction. Obviously because these items die, once they are picked up.

2.) ITEM_TYPE_PURCHASABLE don't get moved into the custom UI.
Instead they go directly to the native interface.
Also item type ids which are not registered to Inventory remain in the native interface.

For me the native/custom sync has two meanings:
1.) You can interact with custom merchant interfaces ( once I coded them )
2.) For best overview of all items a unit has.

The main flaw is that a sync need 0 second timers to detect move/cancel inventory events.
Also you can't add an "item" to a specific slot. You can only add an "item id" to a specified slot.

I synced my custom and the native one pretty much flawlessly here
I will take a look.

Plus, your FSI is one step ahead for having separate UI system, which is much neater ;)

Thanks. The UIPackage is one of the best things I've ever wrote. Just nobody knows it ... :).
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Have just re-checked the system again, and it's not that flawless actually lol
Of course not. There are a lot of areas which need improvement.
I working heavily on it :)
Don't get it wrong. I edited the post, what I meant with "the system" is "my system" ;)

1.) Power up item types are invalid for custom UI interaction. Obviously because these items die, once they are picked up.

2.) ITEM_TYPE_PURCHASABLE don't get moved into the custom UI, t
they go instead to the native interface. Also item type ids which are not registered
remain in the native interface.
That's neat solution. Since actually players mostly only need quick access on consumable items.

Also you can't add an "item" to a specific slot. You can only add an "item id" to a specified slot.

Why can't? It's just like:
JASS:
set item = CreateItem(...)
call UnitAddItem(unit, item)
call UnitDropItemSlot(unit, item, targetSlot)
Unless I understand it wrong.

Thanks. The UIPackage is one of the best things I've ever wrote. Just nobody knows it ... :).
Don't be so sure ;) You will always create better things as you make more things ;P
I wonder tho what makes it different than Custom Window by Maker?
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Why can't? It's just like:
Jass:
set item = CreateItem(...)
call UnitAddItem(unit, item)
call UnitDropItemSlot(unit, item, targetSlot)
Unless I understand it wrong.
Could be I never tried that native. From the name it could also remove an item from that slot.

Don't be so sure ;) You will always create better things as you make more things ;P
I wonder tho what makes it different than Custom Window by Maker?
.... Uuhhh Custom window and UIPackage are two worlds.
Custom window can create a border and a texttag text inside.

The UIPackage allows you to quickly create an entire custom interface.
Buttons ( trackables ), borders, camera, sound, multiboard, ....
I should create some example systems using the package :)
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
When you convert
Unit - Order (Last created unit) to move (Last created item) to inventory slot 1
to jass you will find that native eventually ;)
Thanks. That solves a lot of problems I was facing.
Well then I will write a native / custom sync :D

Edit:
I've updated the map, because I improved the way dynamic items are generated.
The current version doesn't fully support "ItemPower" .. not a big problem I guess.

I've made a giant update ( at least behind the scenes ).
1.) ItemPower is no longer supported. It was a nice system when I made it two years ago but in Inventory is no place for it.

2.) I build a giant doubly linked list structure, by giant I mean that it allocates it's nodes and heads from an hashtable child key.
JASS:
    //===================================================================================
    // Global linked list.
    //===================================================================================
    globals
        private integer lists = 0
        private integer nodes = 0
    endglobals

    // It would be naive to believe everything works perfect right away ...
    // Better let the system shout at us, than ignoring fatal mistakes in code.
static if DEBUG_MODE then
    private function IsNode takes integer node returns boolean
        return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node)
    endfunction
    private function IsList takes integer list returns boolean
        return HaveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list)
    endfunction
endif

    function Inventory_GetListByNode takes integer node returns integer
        debug call ThrowError((node == 0),      "InventoryCore", "Inventory_ListGetListByNode", "node", node, "Attempt to read null node!")
        debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_ListGetListByNode", "node", node, "Attempt to read invalid node!")
        
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node)
    endfunction
    private function GetList takes integer node returns integer
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node)
    endfunction
    private function SetList takes integer node, integer list returns nothing
        call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_LIST, node, list)
    endfunction
    //
    function Inventory_GetListFirstNode takes integer list returns integer
        debug call ThrowError((list == 0),      "InventoryCore", "Inventory_GetListFirstNode", "list", list, "Attempt to read null list!")
        debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_GetListFirstNode", "list", list, "Attempt to read invalid list!")
        //
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list)
    endfunction
    private function GetFirst takes integer list returns integer
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list)
    endfunction
    private function SetFirst takes integer list, integer node returns nothing
        call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_FIRST, list, node)
    endfunction
    //
    function Inventory_GetListLastNode takes integer list returns integer
        debug call ThrowError((list == 0),      "InventoryCore", "Inventory_GetListLastNode", "list", list, "Attempt to read null list!")
        debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_GetListLastNode", "list", list, "Attempt to read invalid list!")
        //
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list)
    endfunction
    private function GetLast takes integer list returns integer
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list)
    endfunction
    private function SetLast takes integer list, integer node returns nothing
        call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_LAST, list, node)
    endfunction
    //
    function Inventory_GetListNextNode takes integer node returns integer
        debug call ThrowError((node == 0),      "InventoryCore", "Inventory_GetListNextNode", "node", node, "Attempt to read null node!")
        debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_GetListNextNode", "node", node, "Attempt to read invalid node!")
        //
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node)
    endfunction
    private function GetNext takes integer node returns integer   
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node)
    endfunction
    private function SetNext takes integer node, integer nextNode returns nothing
        call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_NEXT, node, nextNode)
    endfunction
    //
    function Inventory_GetListPrevNode takes integer node returns integer
        debug call ThrowError((node == 0),      "InventoryCore", "Inventory_GetListPrevNode", "node", node, "Attempt to read null node!")
        debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_GetListPrevNode", "node", node, "Attempt to read invalid node!")
        //
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node)
    endfunction
    private function GetPrev takes integer node returns integer
        return LoadInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node)
    endfunction
    private function SetPrev takes integer node, integer prevNode returns nothing
        call SaveInteger(Inventory_GetTable(), INVENTORY_LIST_PREV, node, prevNode)
    endfunction
    
    private function AllocateList takes nothing returns integer
        local integer list = GetFirst(0)
        if (0 == list) then
            // Pffffff.
            debug call ThrowError(lists == 2147483648, "InventoryCore", "AllocateList", "lists", 0, "Overflow!")
            //
            set lists = lists + 1
            set list = lists
        else
            call SetFirst(0, GetFirst(list))
        endif
        debug call SaveBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list, true)
        return list
    endfunction
    private function AllocateNode takes nothing returns integer
        local integer node = GetNext(0)
        if (0 == node) then
            // Pffffff.
            debug call ThrowError(nodes == 2147483648, "InventoryCore", "AllocateNode", "nodes", 0, "Overflow!")
            //
            set nodes = nodes + 1
            set node = nodes
        else
            call SetNext(0, GetNext(node))
        endif
        
        debug call SaveBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node, true)
        return node
    endfunction
        
    function Inventory_EnqueueToList takes integer list returns integer
        local integer node = AllocateNode()
        
        debug call ThrowError((list == 0),      "InventoryCore", "Inventory_ListEnqueue", "list", list, "Attempted to enqueue on to null list.")
        debug call ThrowError(not IsList(list) ,"InventoryCore", "Inventory_ListEnqueue", "list", list, "Attempted to enqueue on to invalid list.")
        
        call SetList(node, list)
        if (0 == GetFirst(list)) then
            call SetFirst(list, node)
            call SetLast(list, node)
            call SetPrev(node, 0)
        else
            call SetNext(GetLast(list), node)
            call SetPrev(node, GetLast(list))
            call SetLast(list, node)
        endif
        
        call SetNext(node, 0)
        return node
    endfunction
    
    function Inventory_RemoveNodeFromList takes integer node returns nothing
        local integer list = GetList(node)
        
        debug call ThrowError((node == 0),      "InventoryCore", "Inventory_ListRemoveNode", "list", list, "Attempted to remove null node!")
        debug call ThrowError(not IsNode(node), "InventoryCore", "Inventory_ListRemoveNode", "list", list, "Attempted to remove invalid node [" + I2S(node) + "]!")
        debug call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_NODES, node)
        
        call SetList(node, 0)
        if (0 == GetPrev(node)) then
            call SetFirst(list, GetNext(node))
        else
            call SetNext(GetPrev(node), GetNext(node)) 
        endif
        if (0 == GetNext(node)) then
            call SetLast(list, GetPrev(node))
        else
            call SetPrev(GetNext(node), GetPrev(node))
        endif
        call SetNext(node, GetNext(0))
        call SetNext(0, node)
    endfunction
    
    private function ReleaseNodes takes integer list returns nothing
        local integer node = GetFirst(list)
        local integer temp 
        loop
            exitwhen (0 == node)
            set temp = GetNext(node)
            call Inventory_RemoveNodeFromList(node)
            set node = temp
        endloop
    endfunction
    
    function Inventory_DestroyList takes integer list returns nothing
        debug call ThrowError(list == 0,        "InventoryCore", "Inventory_DestroyList", "list", list, "Attempted to destroy a null list!")
        debug call ThrowError(not IsList(list), "InventoryCore", "Inventory_DestroyList", "list", list, "Attempted to destroy an invalid list!")
        
        call ReleaseNodes(list)
        if (0 != GetFirst(list)) then
            call SetNext(GetLast(list), GetNext(0))
            call SetNext(0, GetFirst(list))
            call SetLast(list, 0)
        endif
        
        call SetFirst(list, GetFirst(0))
        call SetFirst(0, list)
        
        debug call RemoveSavedBoolean(Inventory_GetTable(), INVENTORY_LIST_LISTS, list)
    endfunction
    
    function Inventory_KeyForListExists takes integer parent, integer whichKey returns boolean
        return HaveSavedInteger(Inventory_GetTable(), parent, whichKey)
    endfunction
    
    // Returns the list or creates one.
    function Inventory_GetListByKey takes integer parent, integer whichKey returns integer
        if not HaveSavedInteger(Inventory_GetTable(), parent, whichKey) then
            call SaveInteger(Inventory_GetTable(), parent, whichKey, AllocateList())
        endif
        return LoadInteger(Inventory_GetTable(), parent, whichKey)
    endfunction
    
    function Inventory_DestroyListByKey takes integer parent, integer whichKey returns nothing
        call Inventory_DestroyList(LoadInteger(Inventory_GetTable(), parent, whichKey))
        call RemoveSavedInteger(Inventory_GetTable(), parent, whichKey)
    endfunction
Basically it's an endless linked list. Or countless small linked lists from one table ( that's what I needed ).
In the process of doing so, I could remove two hashtables from Inventory. Now it only uses two instead of four.
I might remove one of the two remaining later....

3.) Now bonus and ability lists for items exists and can be easily read from everywhere.
They are no longer singly linked list, instead they are doubly linked lists.
Basically the first steps towards gems / sockets. The system can simply transfer
bonuses from one item to another and back ...
JASS:
library CustomItemAffixList uses InventoryCore
//=========================================================================
// Item affixes are effects an item grants to its carrier unit.
// These affixes can be of two types:
//  1.) ability id     ( inlcuding ability levels )
//  2.) BonusMod Bonus ( refer to: wc3c.net/showthread.php?t=107940 )
//=========================================================================
// CustomItemAffixList saves all affixes for item ids 
// and item type ids in seperate lists. 
// Only affixes of item id lists are added to units,
// while the item type id lists transfer their nodes
// to item ids lists when a new CustomItem is created.
//=========================================================================
// In order to avoid code collision inside the hashtable,
// there are all together four parent keys, 
// which determine the type of affix list.
//  1.) INVENTORY_ITEM_TYPE_ID_ABILITY_LIST
//  2.) INVENTORY_ITEM_TYPE_ID_BONUS_LIST
//  3.) INVENTORY_ITEM_ID_ABILITY_LIST
//  4.) INVENTORY_ITEM_ID_BONUS_LIST
// 
// Also two parent keys to distinguish affix id and
// affix quantity between item type ids and item ids.
//  A.) INVENTORY_ITEM_TYPE_ID_AFFIXES
//  B.) INVENTORY_ITEM_ID_AFFIXES 
//
// Affix ids are saved on the + value of a node.
// Affix quantities are saved on the - value of a node.
//========================================================================

//========================================================================
// Saving affixes to lists.
//======================================================================== 
                                                   // Bonus or ability.
    private function SearchNodeMatch takes integer affixType, integer list, integer affix returns integer
        local integer node = Inventory_GetListFirstNode(list)
        
        loop
            exitwhen 0 == node
            if LoadInteger(Inventory_GetTable(), affixType, node) == affix then
                return node
            endif
            set node = Inventory_GetListNextNode(node)
        endloop
        
        return 0
    endfunction
                                             // Bonus or ability.
    private function SaveAffix takes integer affixType, integer parent, integer itemKey, integer affix, integer amount returns nothing
        local integer list = Inventory_GetListByKey(parent, itemKey)
        local integer node = SearchNodeMatch(affixType, list, affix)
        local integer sum  = LoadInteger(Inventory_GetTable(), affixType, -node) + amount
        
        if node == 0 then
            if amount != 0 then
                set node = Inventory_EnqueueToList(list)
                call SaveInteger(Inventory_GetTable(), affixType,  node, affix)
                call SaveInteger(Inventory_GetTable(), affixType, -node, amount)
            endif
        elseif sum == 0 then
            call RemoveSavedInteger(Inventory_GetTable(), affixType, node)
            call RemoveSavedInteger(Inventory_GetTable(), affixType, -node)
            call Inventory_RemoveNodeFromList(node)
        else
            call SaveInteger(Inventory_GetTable(), affixType, -node, sum)
        endif
    endfunction
        
    // For read-ability only.
    private function SaveItemTypeIdAffix takes integer parent, integer itemKey, integer affix, integer amount returns nothing
        call SaveAffix(INVENTORY_ITEM_TYPE_ID_AFFIXES, parent, itemKey, affix, amount)
    endfunction
    private function SaveItemIdAffix takes integer parent, integer itemKey, integer affix, integer amount returns nothing
        call SaveAffix(INVENTORY_ITEM_ID_AFFIXES, parent, itemKey, affix, amount)
    endfunction

//========================================================================
// Adding & removing affixes to units.
//======================================================================== 
    
static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
// Required system is not present.
else                                                                                      // + or -
    private function AddBonuses takes unit source, integer affixType, integer id, integer signum returns nothing
        local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_BONUS_LIST, id)
        local integer node = Inventory_GetListFirstNode(list)// List of an item handle id.
        local integer bonus 
        local integer amount
        
        loop
            exitwhen 0 == node
            set bonus = LoadInteger(Inventory_GetTable(), affixType, node)
            set amount = LoadInteger(Inventory_GetTable(), affixType, -node)*signum
            call AddUnitBonus(source, bonus, amount)
            
            set node = Inventory_GetListNextNode(node)
        endloop
    endfunction
endif
                                                                                // GetHandleId(item)
    private function AddAbilities takes unit source, integer affixType, integer id returns nothing
        local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
        local integer node = Inventory_GetListFirstNode(list)
        local integer abilityId 
        local integer level 
        
        loop
            exitwhen 0 == node
            set abilityId = LoadInteger(Inventory_GetTable(), affixType, node)
            set level = LoadInteger(Inventory_GetTable(), affixType, -node)
            
            if UnitAddAbility(source, abilityId) then
                call SetUnitAbilityLevel(source, abilityId, level) 
                call UnitMakeAbilityPermanent(source, true, abilityId)
            // The ability was already on the unit.
            else
                set level = level + GetUnitAbilityLevel(source, abilityId)
                if level != SetUnitAbilityLevel(source, abilityId, level) then
                    debug call ThrowWarning(true, "CustomItemAffixList", "AddAbilities", "level", 0, "The map doesn't support for " + GetObjectName(abilityId) + " a level of " + I2S(level))
                endif
            endif
            
            set node = Inventory_GetListNextNode(node)
        endloop        
    endfunction
                                                                                   // GetHandleId(item)
    private function RemoveAbilities takes unit source, integer affixType, integer id returns nothing
        local integer list = Inventory_GetListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
        local integer node = Inventory_GetListFirstNode(list)
        local integer abilityId  
        local integer level
        
        loop
            exitwhen 0 == node
            set abilityId = LoadInteger(Inventory_GetTable(), affixType, node)
            set level = LoadInteger(Inventory_GetTable(), affixType, -node)
            
            if GetUnitAbilityLevel(source, abilityId) <= level then
                call UnitRemoveAbility(source, abilityId)
            else
                call SetUnitAbilityLevel(source, abilityId, GetUnitAbilityLevel(source, abilityId) - level)
            endif
            
            set node = Inventory_GetListNextNode(node)
        endloop        
    endfunction

//========================================================================
// Public API called from Inventory on equip and unequip event.
//======================================================================== 

    function RemoveUnitItemAffixes takes unit source, item whichItem returns nothing
        local integer id = GetHandleId(whichItem)
        if (id != 0) and (GetUnitTypeId(source) != 0) then
            call RemoveAbilities(source, INVENTORY_ITEM_ID_AFFIXES, id)
            
            static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
            // Required system is not present.
            else
                call AddBonuses(source, INVENTORY_ITEM_ID_AFFIXES, id, -1)
            endif
        endif
    endfunction
    
    function AddUnitItemAffixes takes unit source, item whichItem returns nothing
        local integer id = GetHandleId(whichItem)
        if (id != 0) and (GetUnitTypeId(source) != 0) then
            call AddAbilities(source, INVENTORY_ITEM_ID_AFFIXES, id)
            
            static if not LIBRARY_BonusMod and not LIBRARY_Bonus then
            // Required system is not present.
            else
                call AddBonuses(source, INVENTORY_ITEM_ID_AFFIXES, id, 1)
            endif
        endif
    endfunction

//========================================================================
// User API; Documentated in Inventory API.
//======================================================================== 

    // These abilities are always present on the ability list for items of type "itemId".
    function AddCustomItemIdAbility takes integer itemId, integer abilityId, integer level returns nothing
        debug call ThrowError(itemId == 0, "CustomItemAffixList", "AddCustomItemIdAbility", "itemId", 0, "Invalid item id ( 0 )!")
        
        call SaveItemTypeIdAffix(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, itemId, abilityId, level)
    endfunction
    
    // These bonuses are always present on the bonus list for items of type "itemId".
    function AddCustomItemIdBonus takes integer itemId, integer bonus, integer amount returns nothing
        debug call ThrowError(itemId == 0, "CustomItemAffixList", "AddCustomItemIdAbility", "itemId", 0, "Invalid item id ( 0 )!")
        
        call SaveItemTypeIdAffix(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, itemId, bonus, amount)
    endfunction
    
    // These abilities are only present on the ability list of a specific item.
    function AddCustomItemAbility takes item i, integer abilityId, integer level returns nothing 
        if (GetItemTypeId(i) != 0) then
            call SaveItemIdAffix(INVENTORY_ITEM_ID_ABILITY_LIST, GetHandleId(i), abilityId, level)
        endif
    endfunction
    
    // These bonuses are only present on the bonus list of a specific item.
    function AddCustomItemBonus takes item i, integer bonus, integer amount returns nothing
        if (GetItemTypeId(i) != 0) then
            call SaveItemIdAffix(INVENTORY_ITEM_ID_BONUS_LIST, GetHandleId(i), bonus, amount)
        endif
    endfunction
    
    // Completely empties all lists for an item handle id.
    function FlushCustomItemAffixes takes integer id returns nothing
        debug call ThrowWarning(id == 0, "CustomItemAffixList", "FlushCustomItemAffixes", "id", 0, "Invalid handle id ( 0 )!")
        
        if (id != 0) then
            call Inventory_DestroyListByKey(INVENTORY_ITEM_ID_BONUS_LIST, id)
            call Inventory_DestroyListByKey(INVENTORY_ITEM_ID_ABILITY_LIST, id)
        endif
    endfunction

//==========================================================================
// Import from item id list to handle id list. Called from the item parser.
//========================================================================== 
    
    private function ImportBonuses takes item i returns nothing
        local integer id = GetHandleId(i)
        local integer list = Inventory_GetListByKey(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, GetItemTypeId(i))
        local integer node = Inventory_GetListFirstNode(list)
        local integer bonus 
        local integer amount
        
        loop
            exitwhen 0 == node
            set bonus = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, node)
            set amount = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, -node)
            call SaveItemIdAffix(INVENTORY_ITEM_ID_BONUS_LIST, id, bonus, amount)
            
            set node = Inventory_GetListNextNode(node)
        endloop
    endfunction
    
    private function ImportAbilities takes item i returns nothing
        local integer id = GetHandleId(i)
        local integer list = Inventory_GetListByKey(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, GetItemTypeId(i))
        local integer node = Inventory_GetListFirstNode(list)
        local integer abilityId 
        local integer level
        
        loop
            exitwhen 0 == node
            set abilityId = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, node)
            set level = LoadInteger(Inventory_GetTable(), INVENTORY_ITEM_TYPE_ID_AFFIXES, -node)
            call SaveItemIdAffix(INVENTORY_ITEM_ID_ABILITY_LIST, id, abilityId, level)
            
            set node = Inventory_GetListNextNode(node)
        endloop       
    endfunction
    
    function ImportCustomItemAffixes takes item i returns nothing
        local integer itemId = GetItemTypeId(i)
        if (itemId != 0) then

            if Inventory_KeyForListExists(INVENTORY_ITEM_TYPE_ID_BONUS_LIST, itemId) then
                call ImportBonuses(i)
            endif
    
            if Inventory_KeyForListExists(INVENTORY_ITEM_TYPE_ID_ABILITY_LIST, itemId) then
                call ImportAbilities(i)
            endif
        endif
    endfunction
    
endlibrary

4.) The random affix generator is a bit slow ...

Now that the background basics are working nicely I will focus on the actual user interface :)
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Not yet. I will see what I can do.

Edit:
The native custom sync is written. It code looks a bit messy now.
I will clean everything up for the next update, version 1.6.

Edit2:
I've updated to version 1.6 in which I purged a couple of bugs I encountered.
I have to test the system a bit to see where it need further improvement

Edit3:
I had to update to version 1.7, because of a flaw in the linked list structure used in Inventory.
Version 1.6 is not working and will bug!!!!
Also new in 1.7
  • Item costs detection is now working properly.
  • Merging charged items is now working properly.
  • The native / custom sync is now working.

I guess there will be further changes in the next updates.
I hope that everything is finished until version 2.0

Is there a way to import your system easier? Too many import files @@
It's because the native image API is not so good supported by Blizzard.
Otherwise there would be only a few things, which you would have to import. :)

Edit4:
v1.7 seems to be very stable. I'm testing it now and I guess it will take some time
before I update to v1.8 ( if required ).
In case you have any wishes or ideas concerning the system ... fell free to post them here.

--------------------------------

To mods and reviewers:
I'm still structuring the code and trying to short what can be shortend,
better start off with the other resources of our spell section.
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
Yes I'm still make developments in the code.
I'll let you know when it's finished.

I'll put this on ice, until it comes clear what the new patch will bring.
Eventually everything must be re-written if they add anything related to
trackable, image API or object editor field access.
Therefore there is no rush in updating further.
Maybe I keep on working on the data structure,
but for now not on the visuals.

Edit: I'm actually working on it, but as I said there is no reason to
update the thread content as long as it's not clear what Blizzard's next
move is.

You can send this to need fix status or leave it longtime pending. :)

Edit2: Need Fix until soon™
 
Last edited:
Top