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

Just another Full Screen Inventory

Status
Not open for further replies.

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I'm currently writing a full screen inventory system. As always I will try to make it the best around. Not hoping to get DC actually (bcs I guess it's just impossible these days), I just have a plan to work on ORPG game, my dream from the first place.

okay..
it will be:
- MUI
- Supports multiple pages
- Near unlimited inventory slots
- Not supports bonusmod, but easy to implement any bonusmod

additional feature:
- Item macro
- Equipment system
- Drag icon feature

I will think the other things later, so here is the current code..
I guess it's still 5% done, some parts are still scratch (debuging purpose)
JASS:
library UltimateInventory initializer ini requires Table, Track

    globals
        // Miscs
        private constant    real            CAMERA_DISTANCE             = 2500.0
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 100.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    string          TRACKABLE_PATH              = "war3mapImported\\32x32-Track.mdx"
        private constant    real            TRACKABLE_SIZE              = 32.0
        private constant    real            TRACKABLE_AREA_MAX_X        = 512.0
        private constant    real            TRACKABLE_AREA_MIN_X        = -5600.0
        private constant    real            TRACKABLE_AREA_MAX_Y        = 512.0
        private constant    real            TRACKABLE_AREA_MIN_Y        = -6200.0
        private constant    real            WINDOW_DISTANCE             = 800.0
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SPACE                    = 64.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         SPECIAL_SLOT_TOTAL          = 4
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
    endglobals
    
    globals
        public unit array CameraUnit
        public real array CameraX
        public real array CameraY
        private integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
        private integer ItemTotal = 0
        private force PlayerForce = CreateForce()
        private trigger OnClick = CreateTrigger()
        private trigger OnHover = CreateTrigger()
        private trigger ItemPickup = CreateTrigger()
        private rect TrackArea
        public Table array SlotX[11]
        public Table array SlotY[11]
        private Table array SlotIndex
        private Table array SlotCategory
        private Table array SlotCharge
        private Table array SlotCooldown
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
        private Table TrackableX
        private Table TrackableY
    endglobals
    
    private function GetEmptySlot takes integer hand returns integer
        local integer i = 0
        
        loop
            exitwhen i == SlotTotal
            if ItemIndex[hand].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        return i
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
        set EmptySlot.integer[GetHandleId(whichUnit)] = 1
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
        local integer hand = GetHandleId(whichUnit)
        
        set EmptySlot.integer[hand] = 0
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
        endif
        return false
    endfunction
    
    function RegisterInventoryItem takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusDex, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddItemToInventory takes unit whichUnit, integer itemID returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        local integer slot = EmptySlot[hand] - 1
        
        if EmptySlot.integer[hand] > 0 and EmptySlot.integer[hand] <= SlotTotal then
            set SlotIndex[hand].integer[slot] = dex
            set EmptySlot.integer[hand] = GetEmptySlot(hand) + 1
            return true
        endif
        return false
    endfunction
    
    function AddItemToInventoryAtSlot takes unit whichUnit, integer itemID, integer slotIndex returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        set EmptySlot.integer[hand] = slotIndex
        if AddItemToInventory(whichUnit, itemID) then
            return true
        endif
        return false
    endfunction
    
    private function onPickup takes nothing returns boolean
        local integer id = GetItemTypeId(GetManipulatedItem())
        
        call AddItemToInventory(GetTriggerUnit(), id)
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        return false
    endfunction
    
    private function initInterface takes integer pn returns nothing
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local real x = LOCATION_X + ((ext_j + 4) * UI_SPACE * pn) + WINDOW_DISTANCE * pn
        
        loop
            exitwhen i > ext_i
            set j = 0
            loop
                exitwhen j > ext_j
                if i == 0 then
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_CORNER_BOTTOM_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_CORNER_BOTTOM_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        call CreateDeadDestructableZ(UI_BORDER_DOWN, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                elseif i == ext_i then
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_CORNER_UPPER_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_CORNER_UPPER_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        call CreateDeadDestructableZ(UI_BORDER_UP, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                else
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_BORDER_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_BORDER_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                    if i < 1 + ITEM_SLOT_HEIGHT then
                        if j > 0 and j <= ITEM_SLOT_WIDTH then
                            set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j - 1
                            set SlotX[pn].real[dex] = x + UI_SPACE * j
                            set SlotY[pn].real[dex] = LOCATION_Y + UI_SPACE * i
                            call CreateDeadDestructableZ(UI_SLOT_EMPTY, SlotX[pn].real[dex], SlotY[pn].real[dex], LOCATION_Z + 32., 270., 1., 0)
                        endif
                    endif
                endif
                set j = j + 1
            endloop
            set i = i + 1
        endloop
    endfunction
    
    private function initTrackable takes nothing returns nothing
        local integer i = 0
        local integer j
        local integer hand
        local real x// = LOCATION_X - TRACKABLE_EXPAND_AREA
        local real y// = LOCATION_Y - TRACKABLE_EXPAND_AREA
        local trackable track
        
        loop
            set j = 0
            loop
                set track = CreateTrackable(TRACKABLE_PATH, x + TRACKABLE_SIZE * j, y + TRACKABLE_SIZE * i, 270.)
                call TriggerRegisterTrackableHitEvent(OnClick, track)
                call TriggerRegisterTrackableTrackEvent(OnHover, track)
                set track = null
                set j = j + 1
            endloop
            set i = i + 1
        endloop
    endfunction
    
    private function ini takes nothing returns nothing
        local integer i = 0
        
        loop
            exitwhen i > 11
            //if GetPlayerController(Player(i)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
                call ForceAddPlayer(PlayerForce, Player(i))
                set SlotX[i] = Table.create()
                set SlotY[i] = Table.create()
                call initInterface(i)
            //endif
            set i = i + 1
        endloop
        call TriggerRegisterUnitEvent(ItemPickup, null, EVENT_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(OnClick, Condition(function onClick))
        call TriggerAddCondition(OnHover, Condition(function onHover))
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        set TrackableX[i] = Table.create()
        set TrackableY[i] = Table.create()
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        call initTrackable()
    endfunction
    
endlibrary

I'm currently wondering how to spread out small trackables around inventory window. If you got any idea please share.. thanks..
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
just want to HIGHLIGHT my current problem

I'm currently wondering how to spread out small trackables around inventory window. If you got any idea please share.. thanks..

and I'm not sure want to implement Track by PurgeandFire or not, I guess it will be better, just need several times to consider..

Think this thread is pointless, ey? I don't know but it's just fun to share development progress of a big system step by step.. just like my UCS.. and seems like this one will be twice/thrice bigger.. not sure.. just enjoy the EPIC CINEMATIC
 
Last edited:
All I can say is: check out the current FSI by Anachron on the spells section and see what you can improve for your system. Otherwise it's pretty much pointless to write your own.

That being said, I feel that Anachrons System is pretty much perfect in design and comes with a lot of optional plugins. I highly doubt there can be done anything to improve it, really (aside from some speed improvements, which usually don't matter with inventories anyway because the bottleneck are the events, not the code runtime).
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I have checked it, I will add some new features such like:
- multiple pages
- icon drag features
- cooldown animation
- item sort

mostly I'm inspired from ISWP by SecondHand (sadly it's not MUI and MPI neither)

EDIT:
I have reviewed anachron's further more:
- it's quite buggy, sometimes the item inside disappeared strangely
- inventory bugged when slot is full
- item forging is cool additional feature but it's lacking basic feature like consumable stacking, multiple pages (which increase the useability)
- set ci.dmg = 35 my inventory will supports unlimited kind of bonus, not limited by default wc3 attributes, so you can add new bonus like dexterity, magic, endurance, etc.
- I will try to include every feature in anachron's
- there will be several following systems after this inventory like, save/load system that supports saving this inventory, and also several add-ons like item forging, equip set, etc.
 
Last edited:
It sounds neat. It is always interesting to see how different people implement these systems. I had worked on one a few years ago, but I never finished it. It is working, but it still needs a lot of features. But hopefully it is enough for me to give you some neat suggestions.

  1. Make sure the coding interface is simple, and clean. It should be easy to use and easy to follow from example. FSI systems are generally very large, so users will usually refer to your demo map to figure out how to use it.
  2. Don't overload on features! I've rewritten FSI systems 3 or so times. I always end up dissatisfied by how many superfluous functions there are. Make it clear which ones are for use by the user and which ones are for use by the system.

    For example, you may have four different functions to create a long border on each side. However, you may want to consolidate it into one, and then give the user an option to determine which function is called (based on the side input). That way, they only have to deal with 1 function opposed to 4.
  3. Once you start to use GetLocalPlayer(), test it in multiplayer often! When I was making the FSI for Ardent Heroes, it all looked awesome until I tested it out with Craka. Everything got messed up (his objects appeared differently than mine), and I think it desynced. I fixed it eventually, but it is generally difficult to debug as the system gets larger.
  4. Design the system with a generalized perspective. For example, I first had a function to create a square border. But what if a user wants to have a rectangular border?

    Another example: one of the drawbacks of Anachron's system was that all icons ended up the same size. This limited my design a little. As such, I made 10 planes to accommodate for different sizes. It would automatically choose the path based on the scale given.

Here is the code I used to test my system. It was just for testing, so it doesn't show off the features of the system or anything, but it might give you some ideas on how to structure the API:
JASS:
scope Test initializer Init
 
    globals
        private constant real CENTER_X          = 2500.
        private constant real CENTER_Y          = 2500.
        private constant real BORDER_SCALE = 0.3
        private constant real ICON_SCALE   = 0.33
   
        private boolean z = true
        private Spec UI   = 0
       
        private constant integer ICON_CODE = 'Tx09'
       
        private SpecIcon Temp = 0
    endglobals
   
    private function onEsc takes nothing returns boolean
        call UI.apply(Player(0), z)
        set z = not z
        return false
    endfunction
   
    private function onHover takes nothing returns nothing
        call BJDebugMsg("Hover!")
    endfunction
   
    private function onClick takes nothing returns nothing
        call BJDebugMsg("Click!")
    endfunction
   
    private function onRightClick takes nothing returns nothing
        call BJDebugMsg("Right Click!")
    endfunction
   
    private function onStart takes nothing returns nothing
        local unit footman
        local unit knight
       
        set UI = Spec.create(CENTER_X, CENTER_Y, true)
        /* This creates one UI instance */
       
        call UI.createBorderOffset( -100, 0, 0, 100, 150, BORDER_SCALE )
        /* This creates a square border */
       
        set Temp = UI.createIcon(ICON_CODE, CENTER_X - 100, CENTER_Y, 0, ICON_SCALE )
        /* This creates an icon */
       
        call UI.registerIconHover( Temp, function onHover )
        call UI.registerIconClick( Temp, function onClick )
        /* These register the "hover" and "click" events.
           When the icon is hovered or clicked, the functions
           "onHover" and "onClick" will be executed respectively. */
       
        set footman = UI.createUnitModel('h007', CENTER_X + 50, CENTER_Y - 80, 0.6)
        set knight  = UI.createUnitModel('h008', CENTER_X + 110, CENTER_Y - 80, 0.6)
        /* Creates a "footman" and a "knight" at the coordinates. The
           units will be facing the camera, and they will be unselectable.
           For information on how to create these objects, look at the
           "SpecUnitModel" trigger. */
    endfunction
   
    private function Init takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerEventEndCinematic(t, Player(0))
        call TriggerAddCondition(t, Condition(function onEsc))
        call TimerStart( CreateTimer(), 0, false, function onStart)
        call FogMaskEnable(false)
        call FogEnable(false)
    endfunction
endscope

Now, mine wasn't actually a full-screen inventory system, but rather a full-screen interface system. That distinction is important, because I had to keep in mind how people would use my API to design their UI's, rather than thinking just about how they would interact with an inventory system. My general idea was to allow the user to do as much as possible with very simple code. I was hoping that users would be able to focus more code bloat on the actual system (e.g. inventory, talent tree, etc.) rather than the UI. It went pretty well too. The script above generated this screen, which is toggleable through pressing ESC:
FSI.jpg


4 lines of code for 4 elements. That is pretty good. As a final suggestion:
Zwiebelchen said:
All I can say is: check out the current FSI by Anachron on the spells section and see what you can improve for your system. Otherwise it's pretty much pointless to write your own.

I cannot emphasize enough the importance of looking at other systems (Anachron's especially). I wouldn't say it is perfect (it was rather strenuous to add features to it. Using the existing API was good though, except for a few qualms here and there), but it is great to understand (1) what features are expected (2) how to keep an elegant design. I didn't like that it had 100 dependencies and everything was spread out so thin, but it is better than having it all clumped up in one trigger (*cough* Toadcop's inventory system, one of the first ones released). So spread out within reason. If you ever want to look at mine, I will gladly send you it (or maybe I'll attach it to this post later, it is on my other computer).

Dalvengyr said:
I'm currently wondering how to spread out small trackables around inventory window. If you got any idea please share.. thanks..

You can always use invisible platforms. I also have 10 planes of different sizes in my FSI interface that you can take from.

If there is an issue with Track, just let me know. I'm always looking to improve it. :)

And good luck with the system!
 
I have checked it, I will add some new features such like:
- multiple pages
- icon drag features
- cooldown animation
- item sort

mostly I'm inspired from ISWP by SecondHand (sadly it's not MUI and MPI neither)

EDIT:
I have reviewed anachron's further more:
- it's quite buggy, sometimes the item inside disappeared strangely
- inventory bugged when slot is full
- item forging is cool additional feature but it's lacking basic feature like consumable stacking, multiple pages (which increase the useability)
- set ci.dmg = 35 my inventory will supports unlimited kind of bonus, not limited by default wc3 attributes, so you can add new bonus like dexterity, magic, endurance, etc.
- I will try to include every feature in anachron's
Sounds good!
But I also have to agree with Purge here:
Simplicity and API are the two major selling points of a full screen inventory. If the regular joe can't use it because it's too complicated, then you failed, because more experienced users will hardcode their own full screen inventory instead of relying on a system that comes with features they probably won't need for their specific application.

Make it easy to use, make it clean, make it bugfree and it will most likely get a 6/5 rating.

- there will be several following systems after this inventory like, save/load system that supports saving this inventory, and also several add-ons like item forging, equip set, etc.
Save/Load should never be part of an inventory system. All popular save/load systems are integer based and it's no big deal even for inexperienced users to just push all the saved items into the save/load on their own, using customized dictionaries.
Besides, a save/load system will almost always be rejected as a resource, as nestharus' save/load /w snippets (for JASSers) and TriggerHappy's codegen (for GUIers) leave nothing to be desired for.

I only partly agree on forging and equipment sets, really. All of this can be done manually by the user (by providing SetItemStats or SetItemDescription functions).
A full screen inventory should only come with basic functionality and leave all the content specific things to the user, so they can customize it to their liking.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
(aside from some speed improvements, which usually don't matter with inventories anyway because the bottleneck are the events, not the code runtime)
I 'm not sure I can make this faster, I'm bad at code optimizations :p

I will not make this so perfect with many glorious features, I will just add as useful as possible basic features that many RPGs are still lacking of it..

Make sure the coding interface is simple, and clean. It should be easy to use and easy to follow from example. FSI systems are generally very large, so users will usually refer to your demo map to figure out how to use it.
of course, that's the main goal, make this useable and super friendly for GUI ers also Jasser as well

Don't overload on features! I've rewritten FSI systems 3 or so times. I always end up dissatisfied by how many superfluous functions there are. Make it clear which ones are for use by the user and which ones are for use by the system.

For example, you may have four different functions to create a long border on each side. However, you may want to consolidate it into one, and then give the user an option to determine which function is called (based on the side input). That way, they only have to deal with 1 function opposed to 4.
roger..

Once you start to use GetLocalPlayer(), test it in multiplayer often! When I was making the FSI for Ardent Heroes, it all looked awesome until I tested it out with Craka. Everything got messed up (his objects appeared differently than mine), and I think it desynced. I fixed it eventually, but it is generally difficult to debug as the system gets larger.
at the first thought, I just want use local player to create floating text (guess it will also support floating tooltip above the icon when the mouse hover above the icon)

Design the system with a generalized perspective. For example, I first had a function to create a square border. But what if a user wants to have a rectangular border?

Another example: one of the drawbacks of Anachron's system was that all icons ended up the same size. This limited my design a little. As such, I made 10 planes to accommodate for different sizes. It would automatically choose the path based on the scale given.
as you see at the current configuration part inside the code, user may configure the inventory size etc. Furthermore, I want to make position of item macro, action windows, etc is configurable too

Here is the code I used to test my system. It was just for testing, so it doesn't show off the features of the system or anything, but it might give you some ideas on how to structure the API:
you know, drag icon feature is fully inspired from your Track demo map? :p
and how can I check whether it's a right or left click?

I didn't like that it had 100 dependencies and everything was spread out so thin, but it is better than having it all clumped up in one trigger (*cough* Toadcop's inventory system, one of the first ones released). So spread out within reason. If you ever want to look at mine, I will gladly send you it (or maybe I'll attach it to this post later, it is on my other computer).
at first I want to make it packed into one trigger, will consider it again :p

You can always use invisible platforms. I also have 10 planes of different sizes in my FSI interface that you can take from.

If there is an issue with Track, just let me know. I'm always looking to improve it. :)

And good luck with the system!
I just need a small platform, currently I use 32x32

and thanks for useful suggestions :grin:

Sounds good!
But I also have to agree with Purge here:
Simplicity and API are the two major selling points of a full screen inventory. If the regular joe can't use it because it's too complicated, then you failed, because more experienced users will hardcode their own full screen inventory instead of relying on a system that comes with features they probably won't need for their specific application.
it will be very understandable by any user I promise :grin:

I will make it very easy to implement and import (will use something like //!, don't know what's the name) so user just need to copy the codes, then register their items and, bingo.. they are done :grin: nothing is pre-placed by the way.. maybe the hardest thing about implementing this system is configuring the trackable spread area, which user need to define maxX, minX, maxY, minY by themselves. but it just requires several test and they are ready to go :grin:

Save/Load should never be part of an inventory system. All popular save/load systems are integer based and it's no big deal even for inexperienced users to just push all the saved items into the save/load on their own, using customized dictionaries.
Besides, a save/load system will almost always be rejected as a resource, as nestharus' save/load /w snippets (for JASSers) and TriggerHappy's codegen (for GUIers) leave nothing to be desired for.
I never tried to implement any save/load system, I guess they are only supporting saving level, hero attributes, and items. So I think I will create a demo save/load system to save this inventory. yes, maybe it will just be a demo, not stand-alone

I only partly agree on forging and equipment sets, really. All of this can be done manually by the user (by providing SetItemStats or SetItemDescription functions).
A full screen inventory should only come with basic functionality and leave all the content specific things to the user, so they can customize it to their liking.
equipment sets, item forging, are just add-ons. I will just give several abosolute useful basic features. in my mind they are something like:
- multiple pages
- cooldown anim
- consumable stacking
- equipment system

just that, so simple, but I promise it will be easy to implement add-ons like bonusMod, item forging, item set, etc. Everything comes clear in my mind, still don't know that I will successfully create them :p

thks for feedbacks guys :grin:

EDIT:
I also have a plan about adding several trigger events which fires when:
- Item is clicked/dragged/hovered/acquired/used/droped etc
- item is equiped/unequiped
- item is added/removed from macro
- etc
so that user could add what extra actions they want to do on that event
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
just have working on this system for 1,5 hours since tomorrow will be a crazy day, so here is small progressive, it's now ~6%
- added new API
- fixed small bugs
- added very detailed documentations so later on I can fix everything easier, thanks to BPower for his signature :thumbs_up: although he is not around

JASS:
library UltimateInventoryCore initializer ini requires Table, Track optional ClearItems

    globals
        // Miscs
        private constant    real            CAMERA_DISTANCE             = 2500.0
        private constant    real            WINDOW_DISTANCE             = 800.0
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 100.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            TRACKABLE_SIZE              = 32.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 512.0
        private constant    string          TRACKABLE_PATH              = "war3mapImported\\32x32-Track.mdx"
        private constant    boolean         AUTO_ADD                    = false
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SPACE                    = 64.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         MACRO_SLOT_TOTAL            = 4
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
        public real array CameraX
        public real array CameraY
        private rect TrackArea
        private trigger ItemPickup = CreateTrigger()
        private trigger OnClick = CreateTrigger()
        private trigger OnHover = CreateTrigger()
        public unit array CameraUnit
        public Table array SlotX[11]
        public Table array SlotY[11]
        private Table array SlotIndex
        private Table array SlotCategory
        private Table array SlotCharge
        private Table array SlotCooldown
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
        private Table TrackableX
        private Table TrackableY
    endglobals
    
    private function GetEmptySlot takes integer hand returns integer
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[hand].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
        set EmptySlot.integer[GetHandleId(whichUnit)] = 1
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
        local integer hand = GetHandleId(whichUnit)
        local integer i = 1
        
        set EmptySlot.integer[hand] = 0
        loop
            exitwhen i > SlotTotal
            // Reset every inventory slot of removed unit
            set SlotIndex[hand].integer[i] = 0
            set SlotCategory[hand].integer[i] = 0
            set SlotCharge[hand].integer[i] = 0
            set SlotCooldown[hand].real[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
            return true
        endif
        return false
    endfunction
    
    function RegisterInventoryItem takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
    endfunction
    
    function RemoveInventoryItem takes integer itemID returns nothing
        local integer dex = ItemIndex.integer[itemID]
        
        // Replace removed item's index with last item's index
        set ItemIndex.integer[ItemTotal] = dex
        // And replace other item's data
        set ItemIcon.integer[dex] = ItemIcon.integer[ItemTotal]
        set ItemCategory.integer[dex] = ItemCategory.integer[ItemTotal]
        set ItemCharge.integer[dex] = ItemCharge.integer[ItemTotal]
        set ItemCooldown.real[dex] = ItemCooldown.real[ItemTotal]
        set ItemTotal = ItemTotal - 1
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusDex, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddItemToInventory takes unit whichUnit, integer itemID, integer itemAmount returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // If unit has inventory
        if EmptySlot.integer[hand] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot.integer[hand] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[hand].integer[EmptySlot[hand]] = dex
                    // Set slot category to item's category at library
                    set SlotCategory[hand].integer[EmptySlot[hand]] = ItemCategory.integer[dex]
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[dex] > 0 then
                        // If charge amount is higher than max charge then
                        if SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount > ItemCharge.integer[dex] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[dex] - SlotCharge[hand].integer[EmptySlot[hand]])
                            // Set slot charge to max charge
                            set SlotCharge[hand].integer[EmptySlot[hand]] = ItemCharge.integer[dex]
                        // If not just sum it
                        else
                            set SlotCharge[hand].integer[EmptySlot[hand]] = SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount
                            set itemAmount = 0
                        endif
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot.integer[hand] = GetEmptySlot(hand)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        return false
    endfunction
    
    function AddItemToInventoryAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot.integer[hand] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddItemToInventory(whichUnit, itemAmount, itemID) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot.integer[hand] = GetEmptySlot(hand)
        endif
        return false
    endfunction
    
    private function onPickup takes nothing returns boolean
        local integer id = GetItemTypeId(GetManipulatedItem())
        
        static if AUTO_ADD then
            call AddItemToInventory(GetTriggerUnit(), id)
        endif
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        return false
    endfunction
    
    private function initInterface takes integer pn returns nothing
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local real x = LOCATION_X + (ext_j + 3) * UI_SPACE * pn + WINDOW_DISTANCE * pn
        
        loop
            exitwhen i > ext_i
            set j = 0
            loop
                exitwhen j > ext_j
                if i == 0 then
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_CORNER_BOTTOM_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_CORNER_BOTTOM_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        call CreateDeadDestructableZ(UI_BORDER_DOWN, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                elseif i == ext_i then
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_CORNER_UPPER_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_CORNER_UPPER_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        call CreateDeadDestructableZ(UI_BORDER_UP, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                else
                    if j == 0 then
                        call CreateDeadDestructableZ(UI_BORDER_LEFT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        call CreateDeadDestructableZ(UI_BORDER_RIGHT, x + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                    if i < 1 + ITEM_SLOT_HEIGHT then
                        if j > 0 and j <= ITEM_SLOT_WIDTH then
                            set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                            set SlotX[pn].real[dex] = x + UI_SPACE * j
                            set SlotY[pn].real[dex] = LOCATION_Y + UI_SPACE * i
                            //call CreateTextTagLocBJ(I2S(dex), Location(SlotX[pn].real[dex],SlotY[pn].real[dex]), 0, 10, 100, 100, 100, 0)
                            call CreateDeadDestructableZ(UI_SLOT_EMPTY, SlotX[pn].real[dex], SlotY[pn].real[dex], LOCATION_Z + 32., 270., 1., 0)
                        endif
                    endif
                endif
                set j = j + 1
            endloop
            set i = i + 1
        endloop
    endfunction
    
    private function initTrackable takes nothing returns nothing
        local integer i = 0
        local integer j
        local integer hand
        local real x = LOCATION_X - TRACKABLE_EXPAND_AREA
        local real y = LOCATION_Y - TRACKABLE_EXPAND_AREA
        local trackable track
        
        loop
            set j = 0
            loop
                set track = CreateTrackable(TRACKABLE_PATH, x + TRACKABLE_SIZE * j, y + TRACKABLE_SIZE * i, 270.)
                call TriggerRegisterTrackableHitEvent(OnClick, track)
                call TriggerRegisterTrackableTrackEvent(OnHover, track)
                set track = null
                set j = j + 1
            endloop
            set i = i + 1
        endloop
    endfunction
    
    private function ini takes nothing returns nothing
        local integer i = 0
        
        loop
            exitwhen i > 11
            if GetPlayerController(Player(i)) == MAP_CONTROL_USER and GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
                set SlotX[i] = Table.create()
                set SlotY[i] = Table.create()
                call initInterface(i)
            endif
            set i = i + 1
        endloop
        call TriggerRegisterUnitEvent(ItemPickup, null, EVENT_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(OnClick, Condition(function onClick))
        call TriggerAddCondition(OnHover, Condition(function onHover))
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        set TrackableX[i] = Table.create()
        set TrackableY[i] = Table.create()
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        call initTrackable()
    endfunction
    
endlibrary

I will just go very carefuly and slowly on this system, go fast will just increase chance to make a buggy inventory system..
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Right-click requires a unit in the trackable area (then you can detect the smart order). However, I think I was just testing the possibility for it. I think units will actually cover the trackables, so you would probably have to choose between having a unit or a trackable, not both. Sorry for the confusion. :p
I think I have an idea like this, like this:
-left-click to drag icon
- right-click to instantly use the clicked icon

well youre very ambitious gj
good job for what? :p I can not guarantee that all features I have mentioned above will be sucessfully added. lol
but at least I have imagine how the code will be looks like, haven't thinking about the trouble will come later
 
Right-click requires a unit in the trackable area (then you can detect the smart order). However, I think I was just testing the possibility for it. I think units will actually cover the trackables, so you would probably have to choose between having a unit or a trackable, not both. Sorry for the confusion. :p
Which one is actually faster, btw?
Unit selection event (for left click) or trackable clicks?

If the former is faster, then there's no real reason to use trackables in the first place.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
The system should not save the item bonus. It should just fire the events.
you are right there.. thnks!!

here is newest update, nothing's new.. just many improvements and the code is so much neater and shorter, but I still have problem with spreading small trackable around the inventory D:
JASS:
library UltimateInventoryCore initializer ini requires Table, Track optional ClearItems

    globals
        // Miscs
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 100.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            TRACKABLE_SIZE              = 32.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 256.0
        private constant    string          TRACKABLE_PATH              = "Doodads\\LordaeronSummer\\Plants\\GrassTuft\\GrassTuft2.mdl"//"war3mapImported\\32x32-Track.mdx""Doodads\\LordaeronSummer\\Plants\\GrassTuft\\GrassTuft2.mdl"
        private constant    boolean         AUTO_ADD                    = false
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SPACE                    = 64.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         MACRO_SLOT_TOTAL            = 4
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
        public real array CameraX
        public real array CameraY
        private real InventoryAreaMaxX
        private real InventoryAreaMaxY
        private rect TrackArea
        private trigger ItemPickup = CreateTrigger()
        public unit array CameraUnit
        public Table array SlotX[11]
        public Table array SlotY[11]
        private Table array SlotIndex
        private Table array SlotCategory
        private Table array SlotCharge
        private Table array SlotCooldown
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemType
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
    endglobals
    
    private function GetEmptySlot takes integer hand returns integer
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[hand].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
            return true
        endif
        return false
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
        set EmptySlot.integer[GetHandleId(whichUnit)] = 1
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
        local integer hand = GetHandleId(whichUnit)
        local integer i = 1
        
        set EmptySlot.integer[hand] = 0
        loop
            exitwhen i > SlotTotal
            // Reset every inventory slot of removed unit
            set SlotIndex[hand].integer[i] = 0
            set SlotCategory[hand].integer[i] = 0
            set SlotCharge[hand].integer[i] = 0
            set SlotCooldown[hand].real[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function RegisterInventoryItem takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemType.integer[ItemTotal] = itemID
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
    endfunction
    
    function RemoveInventoryItem takes integer itemID returns nothing
        local integer dex = ItemIndex.integer[itemID]
        
        // Replace removed item's index with last item's index
        set ItemIndex.integer[ItemType.integer[ItemTotal]] = dex
        // And replace other item's data
        set ItemType.integer[dex] = ItemType.integer[ItemTotal]
        set ItemIcon.integer[dex] = ItemIcon.integer[ItemTotal]
        set ItemCategory.integer[dex] = ItemCategory.integer[ItemTotal]
        set ItemCharge.integer[dex] = ItemCharge.integer[ItemTotal]
        set ItemCooldown.real[dex] = ItemCooldown.real[ItemTotal]
        set ItemTotal = ItemTotal - 1
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusType, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddItemToInventory takes unit whichUnit, integer itemID, integer itemAmount returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // If unit has inventory
        if EmptySlot.integer[hand] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot.integer[hand] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[hand].integer[EmptySlot[hand]] = dex
                    // Set slot category to item's category at library
                    set SlotCategory[hand].integer[EmptySlot[hand]] = ItemCategory.integer[dex]
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[dex] > 0 then
                        // If charge amount is higher than max charge then
                        if SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount > ItemCharge.integer[dex] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[dex] - SlotCharge[hand].integer[EmptySlot[hand]])
                            // Set slot charge to max charge
                            set SlotCharge[hand].integer[EmptySlot[hand]] = ItemCharge.integer[dex]
                        // If not just sum it
                        else
                            set SlotCharge[hand].integer[EmptySlot[hand]] = SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount
                            set itemAmount = 0
                        endif
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot.integer[hand] = GetEmptySlot(hand)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        return false
    endfunction
    
    function AddItemToInventoryAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot.integer[hand] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddItemToInventory(whichUnit, itemAmount, itemID) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot.integer[hand] = GetEmptySlot(hand)
        endif
        return false
    endfunction
    
    function RemoveItemFromInventory takes integer slotIndex returns boolean
        return false
    endfunction
    
    private function onPickup takes nothing returns boolean
        local integer id = GetItemTypeId(GetManipulatedItem())
        
        static if AUTO_ADD then
            call AddItemToInventory(GetTriggerUnit(), id)
        endif
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        return false
    endfunction
    
    private function initInterface takes nothing returns nothing
        local integer pn = GetPlayerId(GetEnumPlayer())
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        
        set SlotX[pn] = Table.create()
        set SlotY[pn] = Table.create()
        if GetLocalPlayer() == GetEnumPlayer() then
            loop
                exitwhen i > ext_i
                set j = 0
                loop
                    exitwhen j > ext_j
                    if i == 0 then
                        if j == 0 then
                            call CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            call CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        else
                            call CreateDestructableZ(UI_BORDER_DOWN, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                    elseif i == ext_i then
                        if j == 0 then
                            call CreateDestructableZ(UI_CORNER_UPPER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            set InventoryAreaMaxX = LOCATION_X + UI_SPACE * j
                            set InventoryAreaMaxY = LOCATION_Y + UI_SPACE * i
                            call CreateDestructableZ(UI_CORNER_UPPER_RIGHT, InventoryAreaMaxX, InventoryAreaMaxY, LOCATION_Z, 270., 1., 0)
                        else
                            call CreateDestructableZ(UI_BORDER_UP, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                    else
                        if j == 0 then
                            call CreateDestructableZ(UI_BORDER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            call CreateDestructableZ(UI_BORDER_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                        if i < 1 + ITEM_SLOT_HEIGHT then
                            if j > 0 and j <= ITEM_SLOT_WIDTH then
                                set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                                set SlotX[pn].real[dex] = LOCATION_X + UI_SPACE * j
                                set SlotY[pn].real[dex] = LOCATION_Y + UI_SPACE * i
                                //call CreateTextTagLocBJ(I2S(pn), Location(SlotX[pn].real[dex], SlotY[pn].real[dex]), 0, 10, 100, 100, 100, 0)
                                //call CreateTextTagLocBJ(I2S(pn), Location(SlotX[pn].real[dex], SlotY[pn].real[dex]), 0, 10, 100, 100, 100, 0)
                                call CreateDestructableZ(UI_SLOT_EMPTY, SlotX[pn].real[dex], SlotY[pn].real[dex], LOCATION_Z + 32., 270., 1., 0)
                            endif
                        endif
                    endif
                    set j = j + 1
                endloop
                set i = i + 1
            endloop
        endif
    endfunction
    
    private function initTrackable takes nothing returns nothing
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local real startX = LOCATION_X - TRACKABLE_EXPAND_AREA
        local real startY
        local Track track
        
        loop
            exitwhen startX > InventoryAreaMaxX + TRACKABLE_EXPAND_AREA
            set startY = LOCATION_Y - TRACKABLE_EXPAND_AREA
            loop
                exitwhen startY > InventoryAreaMaxY + TRACKABLE_EXPAND_AREA
                set track = CreateTrackForPlayer(TRACKABLE_PATH, startX, startY, 0., 270., p)
                //set track = CreateTrackable(TRACKABLE_PATH, startX, startY, 270.)
                call RegisterClickEvent(track, function onClick)
                call RegisterHoverEvent(track, function onHover)
                set startY = startY + 32
            endloop
            set startX = startX + 32
        endloop
        if GetLocalPlayer() == p then
            call BJDebugMsg("end")
        endif
    endfunction
    
    private function ini takes nothing returns nothing
        local integer i = 0
        
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call ForForce(bj_FORCE_ALL_PLAYERS, function initTrackable)
        call TriggerRegisterUnitEvent(ItemPickup, null, EVENT_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
    endfunction
    
endlibrary

now destructables and trackables are created locally, so, the inventory is no longer takes so much space in the map anymore, even it's safe to place it above playable area (it's currently not really practiceable since there is op-limit, just let's see if I can modify trackables height z without hitting the op limit or not).. so far it's not desynced, have tested it in mp.. :)

also I have a plan to implement texture switcher so we can drag icon without uselessly re-creating new destructable since we can't modify a destructable's X/Y/Z after they are created, so we will use unit.. Which is not so much harder to configure..
 
Last edited:
Simply run the multiplayer emulator through JNGP, and input "2" into the number of clients to open.

Put your map on LAN, connect to it from the other client, and then load. And then you just do whatever tests you need. If there is something that will desync, then the other player should receive a message saying "You have been disconnected".

For example, I ran this code:
JASS:
library Test initializer Init
    function Esc takes nothing returns boolean
        if GetLocalPlayer() == Player(0) then
            call CreateDestructableZ('LTbx', 2000, 2000, 0, 0, 1, 0)
        endif
        return false
    endfunction
    
    function Init takes nothing returns nothing 
        local trigger z = CreateTrigger()
        call TriggerRegisterPlayerEventEndCinematic(z, Player(0))
        call TriggerAddCondition(z, Condition(function Esc))
    endfunction
endlibrary

After pressing ESC as player 1, player 2 received this message:
attachment.php


If you need to know the rules for GetLocalPlayer(), you can check out:
http://www.thehelper.net/threads/jass-getlocalplayer.77369/
 

Attachments

  • Desync.jpg
    Desync.jpg
    14.1 KB · Views: 200

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
here is new small update:
- Trackable is now sucessfully create :thumbs_up: special thanks for PurgeandFire
- Trackable is not created locally anymore since all player will use the exact same location

JASS:
library UltimateInventoryCore initializer ini requires Table, Track optional ClearItems

    globals
        // Miscs
        private constant    integer         DUMMY_ID                    = 'SIU0'
        private constant    integer         BUTTON_ID                   = 'SIU1'
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 10.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            TRACKABLE_SIZE              = 32.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 256.0
        private constant    string          TRACKABLE_PATH              = "war3mapImported\\32x32-Track.mdx"
        private constant    boolean         AUTO_ADD                    = false
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SPACE                    = 64.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         MACRO_SLOT_TOTAL            = 4
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
        public real array CameraX
        public real array CameraY
        private real InventoryAreaMaxX
        private real InventoryAreaMaxY
        private real TrackX
        private real TrackY
        private trigger ItemPickup = CreateTrigger()
        private rect TrackArea
        public unit array CameraUnit
        public Table array SlotX[11]
        public Table array SlotY[11]
        private Table array SlotIndex
        private Table array SlotCategory
        private Table array SlotCharge
        private Table array SlotCooldown
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemType
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
    endglobals
    
    private function GetEmptySlot takes integer hand returns integer
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[hand].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
            return true
        endif
        return false
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
        set EmptySlot.integer[GetHandleId(whichUnit)] = 1
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
        local integer hand = GetHandleId(whichUnit)
        local integer i = 1
        
        set EmptySlot.integer[hand] = 0
        loop
            exitwhen i > SlotTotal
            // Reset every inventory slot of removed unit
            set SlotIndex[hand].integer[i] = 0
            set SlotCategory[hand].integer[i] = 0
            set SlotCharge[hand].integer[i] = 0
            set SlotCooldown[hand].real[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function RegisterInventoryItem takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemType.integer[ItemTotal] = itemID
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
    endfunction
    
    function RemoveInventoryItem takes integer itemID returns nothing
        local integer dex = ItemIndex.integer[itemID]
        
        // Replace removed item's index with last item's index
        set ItemIndex.integer[ItemType.integer[ItemTotal]] = dex
        // And replace other item's data
        set ItemType.integer[dex] = ItemType.integer[ItemTotal]
        set ItemIcon.integer[dex] = ItemIcon.integer[ItemTotal]
        set ItemCategory.integer[dex] = ItemCategory.integer[ItemTotal]
        set ItemCharge.integer[dex] = ItemCharge.integer[ItemTotal]
        set ItemCooldown.real[dex] = ItemCooldown.real[ItemTotal]
        set ItemTotal = ItemTotal - 1
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusType, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddItemToInventory takes unit whichUnit, integer itemID, integer itemAmount returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // If unit has inventory
        if EmptySlot.integer[hand] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot.integer[hand] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[hand].integer[EmptySlot[hand]] = dex
                    // Set slot category to item's category at library
                    set SlotCategory[hand].integer[EmptySlot[hand]] = ItemCategory.integer[dex]
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[dex] > 0 then
                        // If charge amount is higher than max charge then
                        if SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount > ItemCharge.integer[dex] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[dex] - SlotCharge[hand].integer[EmptySlot[hand]])
                            // Set slot charge to max charge
                            set SlotCharge[hand].integer[EmptySlot[hand]] = ItemCharge.integer[dex]
                        // If not just sum it
                        else
                            set SlotCharge[hand].integer[EmptySlot[hand]] = SlotCharge[hand].integer[EmptySlot[hand]] + itemAmount
                            set itemAmount = 0
                        endif
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot.integer[hand] = GetEmptySlot(hand)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        return false
    endfunction
    
    function AddItemToInventoryAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer hand = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot.integer[hand] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddItemToInventory(whichUnit, itemAmount, itemID) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot.integer[hand] = GetEmptySlot(hand)
        endif
        return false
    endfunction
    
    function RemoveItemFromInventory takes integer slotIndex returns boolean
        return false
    endfunction
    
    private function onPickup takes nothing returns boolean
        local integer id = GetItemTypeId(GetManipulatedItem())
        
        static if AUTO_ADD then
            call AddItemToInventory(GetTriggerUnit(), id)
        endif
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        return false
    endfunction
    
    private function initInterface takes nothing returns nothing
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local destructable des
        
        set SlotX[pn] = Table.create()
        set SlotY[pn] = Table.create()
        loop
            exitwhen i > ext_i
            set j = 0
            loop
                exitwhen j > ext_j
                if i == 0 then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        set des = CreateDestructableZ(UI_BORDER_DOWN, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                    if GetLocalPlayer() != p then
                        call ShowDestructable(des, false)
                    endif
                elseif i == ext_i then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        // Get inventory area max x and y when loop will be ended
                        set InventoryAreaMaxX = LOCATION_X + UI_SPACE * j
                        set InventoryAreaMaxY = LOCATION_Y + UI_SPACE * i
                        set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, InventoryAreaMaxX, InventoryAreaMaxY, LOCATION_Z, 270., 1., 0)
                    else
                        set des = CreateDestructableZ(UI_BORDER_UP, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                    if GetLocalPlayer() != p then
                        call ShowDestructable(des, false)
                    endif
                else
                    if j == 0 then
                        set des = CreateDestructableZ(UI_BORDER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    elseif j == ext_j then
                        set des = CreateDestructableZ(UI_BORDER_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                    if i < 1 + ITEM_SLOT_HEIGHT then
                        if j > 0 and j <= ITEM_SLOT_WIDTH then
                            set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                            set SlotX[pn].real[dex] = LOCATION_X + UI_SPACE * j
                            set SlotY[pn].real[dex] = LOCATION_Y + UI_SPACE * i
                            set des = CreateDestructableZ(UI_SLOT_EMPTY, SlotX[pn].real[dex], SlotY[pn].real[dex], LOCATION_Z, 270., 1., 0)
                        endif
                    endif
                    if GetLocalPlayer() != p then
                        call ShowDestructable(des, false)
                    endif
                endif
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set des = null
    endfunction
    
    private function addTracks takes nothing returns nothing
        local Track track
        
        set track = CreateTrack(TRACKABLE_PATH, TrackX, TrackY, LOCATION_Z, 270.)
        call RegisterClickEvent(track, function onClick)
        call RegisterHoverEvent(track, function onHover)
    endfunction
    
    private function initTrackable takes nothing returns nothing
        set TrackX = LOCATION_X - TRACKABLE_EXPAND_AREA
        loop
            exitwhen TrackX > InventoryAreaMaxX + TRACKABLE_EXPAND_AREA
            set TrackY = LOCATION_Y - TRACKABLE_EXPAND_AREA
            loop
                exitwhen TrackY > InventoryAreaMaxY + TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set TrackY = TrackY + 32
            endloop
            set TrackX = TrackX + 32
        endloop
    endfunction
    
    private function ini takes nothing returns nothing
        local integer i = 0
        
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call TriggerRegisterUnitEvent(ItemPickup, null, EVENT_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        call initTrackable()
    endfunction
    
endlibrary
next step is maybe the hardest part on this developement: item slot and page handling
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
sorry it was a bad and buggy update, I upload since Bribe need the full code to help me.. I will update the code tonight, I have made a quite good progress..

here is the quite big update. so far the slot handling seems works correctly, now I'm about to work on macro handling..
JASS:
library UltimateInventoryCore initializer ini uses Table, Track optional ClearItems, WorldBounds

    globals
        // Miscs
        private constant    integer         BUTTON_ID                   = 'u000'
        private constant    integer         DUMMY_ID                    = 'u001'
        private constant    integer         SPELL_ID                    = 'SIA0'
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 100.0
        private constant    real            BUTTON_NORMALZ              = 0.0
        private constant    real            BUTTON_DRAGZ                = 175.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            TRACKABLE_SIZE              = 32.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 256.0
        private constant    string          TRACKABLE_PATH              = "war3mapImported\\32x32-Track.mdx"
        private constant    boolean         AUTO_STACK                  = true
        private constant    boolean         DO_PRELOAD                  = true
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SPACE                    = 64.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
        // Item categories
        private constant    integer         ITEM_CATEGORY_LEFT_WEAPON   = 1
        private constant    integer         ITEM_CATEGORY_RIGHT_WEAPON  = 2
        private constant    integer         ITEM_CATEGORY_TWO_HANDED    = 3
        private constant    integer         ITEM_CATEGORY_ACCESSORY     = 4
        private constant    integer         ITEM_CATEGORY_HELMET        = 5
        private constant    integer         ITEM_CATEGORY_ARMOR         = 6
        private constant    integer         ITEM_CATEGORY_GLOVES        = 7
        private constant    integer         ITEM_CATEGORY_LEGS          = 8
        private constant    integer         ITEM_CATEGORY_BOOTS         = 9
        private constant    integer         ITEM_CATEGORY_BACK          = 10
        private constant    integer         ITEM_CATEGORY_MISCS         = 11
        private constant    integer         ITEM_CATEGORY_CONSUMABLES   = 12
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer PreloadTotal = -1
        private integer array PlayerPage
        private integer SlotPage = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT
        private integer SlotTotal = SlotPage * ITEM_SLOT_PAGES
        public real array CameraX
        public real array CameraY
        private real InventoryAreaMaxX
        private real InventoryAreaMaxY
        private real TrackX
        private real TrackY
        private real WorldMaxX
        private real WorldMaxY
        private rect TrackArea
        private trigger ItemPickup = CreateTrigger()
        private unit ReturnedUnit
        public unit array CameraUnit
        private TableArray SlotIndex
        private TableArray SlotIcon
        private TableArray SlotCategory
        private TableArray SlotCharge
        private TableArray SlotCooldown
        public Table SlotX
        public Table SlotY
        private Table PreloadUnit
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemType
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
    endglobals
    
    private function getPreloadUnit takes player p returns unit
        set ReturnedUnit = PreloadUnit.unit[PreloadTotal]
        set PreloadTotal = PreloadTotal - 1
        if GetLocalPlayer() == p then
            call SetUnitVertexColor(ReturnedUnit, 255, 255, 255, 255)
        endif
        return ReturnedUnit
    endfunction
    
    private function releasePreloadUnit takes unit u returns nothing
        set PreloadTotal = PreloadTotal + 1
        set PreloadUnit.unit[PreloadTotal] = u
        static if LIBRARY_WorldBounds then
            call SetUnitX(u, WorldBounds.maxX)
            call SetUnitY(u, WorldBounds.maxY)
        else
            call SetUnitX(u, WorldMaxX)
            call SetUnitY(u, WorldMaxY)
        endif
        call SetUnitVertexColor(PreloadUnit.unit[PreloadTotal], 255, 255, 255, 0)
    endfunction
    
    private function getEmptySlot takes integer id returns integer
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[id].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    private function getStackingSlot takes integer slotDex returns integer
        return 0
    endfunction
    
    private function changeTexture takes unit whichUnit, integer whichTexture returns nothing
    local destructable d  = CreateDestructable(whichTexture, GetUnitX(whichUnit) + 32. * Cos(GetUnitFacing(whichUnit) * bj_DEGTORAD), GetUnitY(whichUnit) + 32. * Sin(GetUnitFacing(whichUnit) * bj_DEGTORAD), 0, .1, 1)

    call UnitAddAbility(whichUnit, SPELL_ID)
    call IssueTargetOrder(whichUnit, "grabtree", d)
    call UnitRemoveAbility(whichUnit, SPELL_ID)
    call RemoveDestructable(d)
    set d = null
endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
            return true
        endif
        return false
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
        local integer id = GetHandleId(whichUnit)
        
        set EmptySlot[id] = 1
        set SlotIndex = TableArray[SlotTotal]
        set SlotIcon = TableArray[SlotTotal]
        set SlotCategory = TableArray[SlotTotal]
        set SlotCharge = TableArray[SlotTotal]
        set SlotCooldown = TableArray[SlotTotal]
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
        local integer id = GetHandleId(whichUnit)
        
        set EmptySlot[id] = 0
        call SlotIndex.destroy()
        call SlotIcon.destroy()
        call SlotCategory.destroy()
        call SlotCharge.destroy()
        call SlotCooldown.destroy()
    endfunction
    
    function RegisterInventoryItem takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemType.integer[ItemTotal] = itemID
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
    endfunction
    
    function RemoveInventoryItem takes integer itemID returns nothing
        local integer dex = ItemIndex.integer[itemID]
        
        // Replace removed item's index with last item's index
        set ItemIndex.integer[ItemType.integer[ItemTotal]] = dex
        // And replace other item's data
        set ItemType.integer[dex] = ItemType.integer[ItemTotal]
        set ItemIcon.integer[dex] = ItemIcon.integer[ItemTotal]
        set ItemCategory.integer[dex] = ItemCategory.integer[ItemTotal]
        set ItemCharge.integer[dex] = ItemCharge.integer[ItemTotal]
        set ItemCooldown.real[dex] = ItemCooldown.real[ItemTotal]
        set ItemTotal = ItemTotal - 1
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusType, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddItemToInventory takes unit whichUnit, integer itemID, integer itemAmount returns boolean
        local player p = GetOwningPlayer(whichUnit)
        local integer dex = ItemIndex.integer[itemID]
        local integer id = GetHandleId(whichUnit)
        local integer slot
        local integer pn = GetPlayerId(p)
        
        // If unit has inventory
        if EmptySlot[id] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot[id] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[id].integer[EmptySlot[id]] = dex
                    // Set slot category to item's category at library
                    set SlotCategory[id].integer[EmptySlot[id]] = ItemCategory.integer[dex]
                    // If player is currently opening the page then create icon for slot
                    if PlayerPage[pn] == EmptySlot[id] / SlotPage + 1 or EmptySlot[id] == SlotPage * PlayerPage[pn] then
                        set slot = EmptySlot[id] - (EmptySlot[id] / SlotPage) * SlotPage
                        if slot == 0 then
                            set slot = SlotPage
                        endif
                        // If everything have been preloaded
                        static if DO_PRELOAD then
                            // Get preloaded unit
                            set SlotIcon[id].unit[EmptySlot[id]] = getPreloadUnit(p)
                            call SetUnitX(SlotIcon[id].unit[EmptySlot[id]], SlotX.real[slot])
                            call SetUnitY(SlotIcon[id].unit[EmptySlot[id]], SlotY.real[slot])
                        else
                            set SlotIcon[id].unit[EmptySlot[id]] = CreateUnit(p, DUMMY_ID, SlotX.real[slot], SlotY.real[slot], 270.)
                            static if not LIBRARY_AutoFly then
                                if UnitAddAbility(SlotIcon[id].unit[EmptySlot[id]], 'Amrf') and UnitRemoveAbility(SlotIcon[id].unit[EmptySlot[id]], 'Amrf') then
                                endif
                                call SetUnitFlyHeight(SlotIcon[id].unit[EmptySlot[id]], LOCATION_Z + 5., 0.)
                            endif
                        endif
                        call changeTexture(SlotIcon[id].unit[EmptySlot[id]], ItemIcon.integer[dex])
                    endif
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[dex] > 0 then
                        static if AUTO_STACK then
                            // Get the slot that can be stacked
                            set EmptySlot[id] = getStackingSlot(EmptySlot[id])
                        endif
                        // If charge amount is higher than max charge then
                        if SlotCharge[id].integer[EmptySlot[id]] + itemAmount > ItemCharge.integer[dex] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[dex] - SlotCharge[id].integer[EmptySlot[id]])
                            // Set slot charge to max charge
                            set SlotCharge[id].integer[EmptySlot[id]] = ItemCharge.integer[dex]
                        // If not just sum it
                        else
                            set SlotCharge[id].integer[EmptySlot[id]] = SlotCharge[id].integer[EmptySlot[id]] + itemAmount
                            set itemAmount = 0
                        endif
                    else
                        set SlotCharge[id].integer[EmptySlot[id]] = 0
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot[id] = getEmptySlot(id)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        return false
    endfunction
    
    function AddItemToInventoryAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
        local integer dex = ItemIndex.integer[itemID]
        local integer id = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot[id] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddItemToInventory(whichUnit, itemID, itemAmount) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot[id] = getEmptySlot(id)
        endif
        return false
    endfunction
    
    function DropItemFromInventory takes integer slotIndex returns boolean
        return false
    endfunction
    
    function RemoveItemFromInventory takes integer slotIndex returns boolean
        return false
    endfunction
    
    private function preloadUnits takes nothing returns nothing
        local integer i = 0
        
        loop
            exitwhen i > SlotTotal
            set PreloadTotal = PreloadTotal + 1
            static if LIBRARY_WorldBounds then
                set PreloadUnit.unit[PreloadTotal] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270.)
            else
                set PreloadUnit.unit[PreloadTotal] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldMaxX, WorldMaxY, 270.)
            endif
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(PreloadUnit.unit[PreloadTotal], 'Amrf') and UnitRemoveAbility(PreloadUnit.unit[PreloadTotal], 'Amrf') then
                endif
            endif
            call SetUnitFlyHeight(PreloadUnit.unit[PreloadTotal], LOCATION_Z + BUTTON_NORMALZ, 0)
            call SetUnitVertexColor(PreloadUnit.unit[PreloadTotal], 255, 255, 255, 0)
            set i = i + 1
        endloop
    endfunction
    
    private function onPickup takes nothing returns boolean
        call AddItemToInventory(GetTriggerUnit(), GetItemTypeId(GetManipulatedItem()), GetItemCharges(GetManipulatedItem()))
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        return false
    endfunction
    
    private function initMacro takes nothing returns nothing
        local player p = GetEnumPlayer()
        local integer i = 0
        local integer j
        local real x = InventoryAreaMaxX + UI_SPACE * 3
        local real y = InventoryAreaMaxY - UI_SPACE
        local destructable des
        
        loop
            exitwhen i > 2
            set j = 0
            loop
                exitwhen j > 1
                if i == 0 then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                elseif i == 2 then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                else
                    if j == 0 then
                        set des = CreateDestructableZ(UI_BORDER_LEFT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    else
                        set des = CreateDestructableZ(UI_BORDER_RIGHT, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                    endif
                endif
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set des = CreateDestructableZ(UI_SLOT_EMPTY, x + UI_SPACE * j, y - UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set des = null
    endfunction
    
    private function initInterface takes nothing returns nothing
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local destructable des
        
        if GetPlayerController(p) == MAP_CONTROL_USER then
            loop
                exitwhen i > ext_i
                set j = 0
                loop
                    exitwhen j > ext_j
                    if i == 0 then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_DOWN, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    elseif i == ext_i then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            // Get inventory area max x and y when loop will be ended
                            set InventoryAreaMaxX = LOCATION_X + UI_SPACE * j
                            set InventoryAreaMaxY = LOCATION_Y + UI_SPACE * i
                            set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, InventoryAreaMaxX, InventoryAreaMaxY, LOCATION_Z, 270., 1., 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_UP, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    else
                        if j == 0 then
                            set des = CreateDestructableZ(UI_BORDER_LEFT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_BORDER_RIGHT, LOCATION_X + UI_SPACE * j, LOCATION_Y + UI_SPACE * i, LOCATION_Z, 270., 1., 0)
                        endif
                        if i < 1 + ITEM_SLOT_HEIGHT then
                            if j > 0 and j <= ITEM_SLOT_WIDTH then
                                set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                                set SlotX.real[dex] = LOCATION_X + UI_SPACE * j
                                set SlotY.real[dex] = LOCATION_Y + UI_SPACE * i
                                //call CreateTextTagLocBJ(I2S(dex), Location(SlotX.real[dex],SlotY.real[dex]), LOCATION_Z, 10, 100, 100, 100, 0)
                                set des = CreateDestructableZ(UI_SLOT_EMPTY, SlotX.real[dex], SlotY.real[dex], LOCATION_Z, 270., 1., 0)
                            endif
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    endif
                    set j = j + 1
                endloop
                set i = i + 1
            endloop
            static if DO_PRELOAD then
                call preloadUnits()
            endif
            call ForForce(bj_FORCE_PLAYER[pn], function initMacro)
            set PlayerPage[pn] = 1
            set des = null
        endif
    endfunction
    
    private function addTracks takes nothing returns nothing
        local Track track
        
        set track = CreateTrack(TRACKABLE_PATH, TrackX, TrackY, LOCATION_Z, 270.)
        call RegisterClickEvent(track, function onClick)
        call RegisterHoverEvent(track, function onHover)
    endfunction
    
    private function initTrackable takes nothing returns nothing
        set TrackX = LOCATION_X - TRACKABLE_EXPAND_AREA
        loop
            exitwhen TrackX > InventoryAreaMaxX + TRACKABLE_EXPAND_AREA
            set TrackY = LOCATION_Y - TRACKABLE_EXPAND_AREA
            loop
                exitwhen TrackY > InventoryAreaMaxY + TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set TrackY = TrackY + TRACKABLE_SIZE
            endloop
            set TrackX = TrackX + TRACKABLE_SIZE
        endloop
    endfunction
    
    private function ini takes nothing returns nothing
        local integer i = 0
        
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemType = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        set SlotX = Table.create()
        set SlotY = Table.create()
        static if DO_PRELOAD then
            set PreloadUnit = Table.create()
        endif
        static if not LIBRARY_WorldBounds then
            set WorldMaxX = GetRectMaxX(GetWorldBounds())
            set WorldMaxY = GetRectMaxY(GetWorldBounds())
        endif
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call TriggerRegisterAnyUnitEventBJ(ItemPickup, EVENT_PLAYER_UNIT_PICKUP_ITEM )
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        call initTrackable()
    endfunction
    
endlibrary

I will write current credit list here so later I don't forget them :grin:
- PurgeandFire, help me a lot with LocalPlayer thingy
- Bribe, his table and for his patience answering my crappy questions
- Zwei & muzzel for suggestion
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
here is an update, quite good. I have made a library for this FSI, and I have made several parts about icon draggin, and it works! but sadly it can't be really smooth because of so many stupid wc3 shortcomings :( also there are numerous renames and some functions are deleted. Also now you are able to scale up/down your inventory :thumbs_up:
here is the code
JASS:
library UltimateInventoryCore initializer ini uses Table, Track, UnitInventoryEvent optional ClearItems, WorldBounds

    globals
        // Miscs
        private constant    integer         BUTTON_ID                   = 'u000'
        private constant    integer         DUMMY_ID                    = 'u001'
        private constant    integer         SPELL_ID                    = 'SIA0'
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 0.0
        private constant    real            BUTTON_NORMALZ              = 0.0
        private constant    real            BUTTON_DRAGZ                = 175.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            TRACKABLE_SPACE             = 16.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 256.0
        private constant    string          TRACKABLE_PATH              = "war3mapImported\\16x16-Track.mdx"
        private constant    boolean         AUTO_STACK                  = true
        private constant    boolean         DO_PRELOAD                  = true
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    real            UI_SIZE                     = 1.0
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
        // Item categories
        private constant    integer         ITEM_CATEGORY_LEFT_WEAPON   = 1
        private constant    integer         ITEM_CATEGORY_RIGHT_WEAPON  = 2
        private constant    integer         ITEM_CATEGORY_TWO_HANDED    = 3
        private constant    integer         ITEM_CATEGORY_ACCESSORY     = 4
        private constant    integer         ITEM_CATEGORY_HELMET        = 5
        private constant    integer         ITEM_CATEGORY_ARMOR         = 6
        private constant    integer         ITEM_CATEGORY_GLOVES        = 7
        private constant    integer         ITEM_CATEGORY_LEGS          = 8
        private constant    integer         ITEM_CATEGORY_BOOTS         = 9
        private constant    integer         ITEM_CATEGORY_BACK          = 10
        private constant    integer         ITEM_CATEGORY_MISCS         = 11
        private constant    integer         ITEM_CATEGORY_CONSUMABLES   = 12
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer PreloadTotal = -1
        private integer array PlayerPage
        private integer array ClickIndex
        private integer SlotPage = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT
        private integer SlotTotal = SlotPage * ITEM_SLOT_PAGES
        private real InventoryAreaMaxX
        private real InventoryAreaMaxY
        private real TrackX
        private real TrackY
        private real WorldMaxX
        private real WorldMaxY
        private real array SlotX
        private real array SlotY
        private real array MacroX
        private real array MacroY
        public real array CameraX
        public real array CameraY
        private rect TrackArea
        private trigger ItemPickup = CreateTrigger()
        private trigger ItemSwap = CreateTrigger()
        private boolean array IsClick
        private unit ReturnedUnit
        public unit array CameraUnit
        private TableArray SlotIndex
        private TableArray SlotIcon
        private TableArray SlotCategory
        private TableArray SlotCharge
        private TableArray SlotCooldown
        private Table PreloadUnit
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemType
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
    endglobals
    
    private function getPreloadUnit takes player p returns unit
    
        set ReturnedUnit = PreloadUnit.unit[PreloadTotal]
        set PreloadTotal = PreloadTotal - 1
        if GetLocalPlayer() == p then
            call SetUnitVertexColor(ReturnedUnit, 255, 255, 255, 255)
        endif
        
        return ReturnedUnit
    endfunction
    
    private function releasePreloadUnit takes unit u returns nothing
    
        set PreloadTotal = PreloadTotal + 1
        set PreloadUnit.unit[PreloadTotal] = u
        static if LIBRARY_WorldBounds then
            call SetUnitX(u, WorldBounds.maxX)
            call SetUnitY(u, WorldBounds.maxY)
        else
            call SetUnitX(u, WorldMaxX)
            call SetUnitY(u, WorldMaxY)
        endif
        call SetUnitVertexColor(PreloadUnit.unit[PreloadTotal], 255, 255, 255, 0)
        
    endfunction
    
    private function getEmptySlot takes integer id returns integer
    
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[id].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    private function getStackingSlot takes integer slotDex returns integer
        return 0
    endfunction
    
    private function changeTexture takes unit whichUnit, integer whichTexture returns nothing
    
        local destructable d  = CreateDestructable(whichTexture, GetUnitX(whichUnit) + 32. * Cos(GetUnitFacing(whichUnit) * bj_DEGTORAD), GetUnitY(whichUnit) + 32. * Sin(GetUnitFacing(whichUnit) * bj_DEGTORAD), 0, .1, 1)

        call UnitAddAbility(whichUnit, SPELL_ID)
        call IssueTargetOrder(whichUnit, "grabtree", d)
        call UnitRemoveAbility(whichUnit, SPELL_ID)
        call RemoveDestructable(d)
        set d = null
        
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
    
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            if isOpen then
            else
            endif
            return true
        endif
        
        return false
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
    
        local integer id = GetHandleId(whichUnit)
        
        set EmptySlot[id] = 1
        set SlotIndex = TableArray[SlotTotal]
        set SlotIcon = TableArray[SlotTotal]
        set SlotCategory = TableArray[SlotTotal]
        set SlotCharge = TableArray[SlotTotal]
        set SlotCooldown = TableArray[SlotTotal]
        
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
    
        local integer id = GetHandleId(whichUnit)
        
        set EmptySlot[id] = 0
        call SlotIndex.destroy()
        call SlotIcon.destroy()
        call SlotCategory.destroy()
        call SlotCharge.destroy()
        call SlotCooldown.destroy()
        
    endfunction
    
    function RegisterItemToLibrary takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
    
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemType.integer[ItemTotal] = itemID
        set ItemIcon.integer[ItemTotal] = itemIcon
        set ItemCategory.integer[ItemTotal] = itemCategory
        set ItemCharge.integer[ItemTotal] = itemMaxCharge
        set ItemCooldown.real[ItemTotal] = itemCooldown
        
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusType, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddInventoryItem takes unit whichUnit, integer itemID, integer itemAmount returns boolean
    
        local player p = GetOwningPlayer(whichUnit)
        local integer dex = ItemIndex.integer[itemID]
        local integer id = GetHandleId(whichUnit)
        local integer slot
        local integer pn = GetPlayerId(p)
        
        // If unit has inventory
        if EmptySlot[id] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot[id] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[id].integer[EmptySlot[id]] = dex
                    // Set slot category to item's category at library
                    set SlotCategory[id].integer[EmptySlot[id]] = ItemCategory.integer[dex]
                    // If player is currently opening the page then create icon for slot
                    if PlayerPage[pn] == EmptySlot[id] / SlotPage + 1 or EmptySlot[id] == SlotPage * PlayerPage[pn] then
                        set slot = EmptySlot[id] - (EmptySlot[id] / SlotPage) * SlotPage
                        if slot == 0 then
                            set slot = SlotPage
                        endif
                        // If everything have been preloaded
                        static if DO_PRELOAD then
                            // Get preloaded unit
                            set SlotIcon[id].unit[EmptySlot[id]] = getPreloadUnit(p)
                            call SetUnitX(SlotIcon[id].unit[EmptySlot[id]], SlotX[slot])
                            call SetUnitY(SlotIcon[id].unit[EmptySlot[id]], SlotY[slot])
                        else
                            set SlotIcon[id].unit[EmptySlot[id]] = CreateUnit(p, DUMMY_ID, SlotX[slot], SlotY[slot], 270.)
                            static if not LIBRARY_AutoFly then
                                if UnitAddAbility(SlotIcon[id].unit[EmptySlot[id]], 'Amrf') and UnitRemoveAbility(SlotIcon[id].unit[EmptySlot[id]], 'Amrf') then
                                endif
                                call SetUnitFlyHeight(SlotIcon[id].unit[EmptySlot[id]], LOCATION_Z + 5., 0.)
                            endif
                        endif
                        call changeTexture(SlotIcon[id].unit[EmptySlot[id]], ItemIcon.integer[dex])
                    endif
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[dex] > 0 then
                        static if AUTO_STACK then
                            // Get the slot that can be stacked
                            set EmptySlot[id] = getStackingSlot(EmptySlot[id])
                        endif
                        // If charge amount is higher than max charge then
                        if SlotCharge[id].integer[EmptySlot[id]] + itemAmount > ItemCharge.integer[dex] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[dex] - SlotCharge[id].integer[EmptySlot[id]])
                            // Set slot charge to max charge
                            set SlotCharge[id].integer[EmptySlot[id]] = ItemCharge.integer[dex]
                        // If not just sum it
                        else
                            set SlotCharge[id].integer[EmptySlot[id]] = SlotCharge[id].integer[EmptySlot[id]] + itemAmount
                            set itemAmount = 0
                        endif
                    else
                        set SlotCharge[id].integer[EmptySlot[id]] = 0
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot[id] = getEmptySlot(id)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        
        return false
    endfunction
    
    function AddInventoryItemAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
    
        local integer dex = ItemIndex.integer[itemID]
        local integer id = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot[id] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddInventoryItem(whichUnit, itemID, itemAmount) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot[id] = getEmptySlot(id)
        endif
        
        return false
    endfunction
    
    function DropInventoryItem takes integer slotIndex returns nothing
    endfunction
    
    function MoveInventoryItemToSlot takes integer slotSource, integer slotTarget returns nothing
    endfunction
    
    private function preloadUnits takes nothing returns nothing
    
        local integer i = 0
        
        loop
            exitwhen i > SlotPage
            set PreloadTotal = PreloadTotal + 1
            static if LIBRARY_WorldBounds then
                set PreloadUnit.unit[PreloadTotal] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270.)
            else
                set PreloadUnit.unit[PreloadTotal] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldMaxX, WorldMaxY, 270.)
            endif
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(PreloadUnit.unit[PreloadTotal], 'Amrf') and UnitRemoveAbility(PreloadUnit.unit[PreloadTotal], 'Amrf') then
                endif
            endif
            call SetUnitFlyHeight(PreloadUnit.unit[PreloadTotal], LOCATION_Z + BUTTON_NORMALZ, 0)
            call SetUnitVertexColor(PreloadUnit.unit[PreloadTotal], 255, 255, 255, 0)
            call SetUnitScale(PreloadUnit.unit[PreloadTotal], UI_SIZE, 0., 0.)
            set i = i + 1
        endloop
        
    endfunction
    
    private function onPickup takes nothing returns boolean
        call AddInventoryItem(GetTriggerUnit(), GetItemTypeId(GetManipulatedItem()), GetItemCharges(GetManipulatedItem()))
        return false
    endfunction
    
    private function onSwap takes nothing returns boolean
        return false
    endfunction
    
    private function onMove takes nothing returns boolean
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local real s = 32. * UI_SIZE
        local integer i = 1
        local integer id
        
        loop
            exitwhen i > SlotPage
            if x < SlotX[i] + s and x > SlotX[i] - s and y < SlotY[i] + s and y > SlotY[i] - s then
                set id = GetPlayerId(GetTriggerTrackablePlayer())
                if IsClick[id] then
                    //call MoveInventoryItemToSlot(ClickIndex[id], i)
                else
                    set ClickIndex[id] = i
                endif
                set IsClick[id] = not IsClick[id]
            endif
            set i = i + 1
        endloop
        
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local integer id = GetPlayerId(GetTriggerTrackablePlayer())
        
        if IsClick[id] then
            call SetUnitX(SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[ClickIndex[id]], x)
            call SetUnitY(SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[ClickIndex[id]], y)
        endif
        
        return false
    endfunction
    
    private function initMacro takes nothing returns nothing
    
        local integer dex = 0
        local player p = GetEnumPlayer()
        local integer i = 0
        local integer j
        local real x = InventoryAreaMaxX + 64. * UI_SIZE * 3
        local real y = InventoryAreaMaxY - 64. * UI_SIZE
        local destructable des
        
        loop
            exitwhen i > 2
            set j = 0
            loop
                exitwhen j > 1
                if i == 0 then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                elseif i == 2 then
                    if j == 0 then
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                else
                    if j == 0 then
                        set des = CreateDestructableZ(UI_BORDER_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        set des = CreateDestructableZ(UI_BORDER_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                endif
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set MacroX[dex] = x + 64. * UI_SIZE * j
                set MacroY[dex] = y - 64. * UI_SIZE * i
                set des = CreateDestructableZ(UI_SLOT_EMPTY, MacroX[dex], MacroY[dex], LOCATION_Z, 270., UI_SIZE, 0)
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set dex = dex + 1
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set des = null
        
    endfunction
    
    private function initInterface takes nothing returns nothing
    
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local destructable des
        
        if GetPlayerController(p) == MAP_CONTROL_USER then
            loop
                exitwhen i > ext_i
                set j = 0
                loop
                    exitwhen j > ext_j
                    if i == 0 then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_DOWN, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    elseif i == ext_i then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            // Get inventory area max x and y when loop will be ended
                            set InventoryAreaMaxX = LOCATION_X + 64. * UI_SIZE * j
                            set InventoryAreaMaxY = LOCATION_Y + 64. * UI_SIZE * i
                            set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, InventoryAreaMaxX, InventoryAreaMaxY, LOCATION_Z, 270., UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_UP, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    else
                        if j == 0 then
                            set des = CreateDestructableZ(UI_BORDER_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_BORDER_RIGHT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if i < 1 + ITEM_SLOT_HEIGHT then
                            if j > 0 and j <= ITEM_SLOT_WIDTH then
                                set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                                set SlotX[dex] = LOCATION_X + 64. * UI_SIZE * j
                                set SlotY[dex] = LOCATION_Y + 64. * UI_SIZE * i
                                set des = CreateDestructableZ(UI_SLOT_EMPTY, SlotX[dex], SlotY[dex], LOCATION_Z, 270., UI_SIZE, 0)
                            endif
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    endif
                    set j = j + 1
                endloop
                set i = i + 1
            endloop
            static if DO_PRELOAD then
                call preloadUnits()
            endif
            call ForForce(bj_FORCE_PLAYER[pn], function initMacro)
            set PlayerPage[pn] = 1
            set IsClick[pn] = false
            set des = null
        endif
        
    endfunction
    
    private function addTracks takes nothing returns nothing
    
        local Track track
        
        set track = CreateTrack(TRACKABLE_PATH, TrackX, TrackY, LOCATION_Z, 270.)
        call RegisterClickEvent(track, function onClick)
        call RegisterHoverEvent(track, function onHover)
        
    endfunction
    
    private function initTrackable takes nothing returns nothing
    
        set TrackX = LOCATION_X - TRACKABLE_EXPAND_AREA
        loop
            exitwhen TrackX > InventoryAreaMaxX + TRACKABLE_EXPAND_AREA
            set TrackY = LOCATION_Y - TRACKABLE_EXPAND_AREA
            loop
                exitwhen TrackY > InventoryAreaMaxY + TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set TrackY = TrackY + TRACKABLE_SPACE
            endloop
            set TrackX = TrackX + TRACKABLE_SPACE
        endloop
        
    endfunction
    
    private function ini takes nothing returns nothing
    
        local integer i = 0
        
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemType = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        static if DO_PRELOAD then
            set PreloadUnit = Table.create()
        endif
        static if not LIBRARY_WorldBounds then
            set WorldMaxX = GetRectMaxX(GetWorldBounds())
            set WorldMaxY = GetRectMaxY(GetWorldBounds())
        endif
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call TriggerRegisterAnyUnitEventBJ(ItemPickup, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call RegisterAnyUnitInventoryEvent(Condition(function onSwap), EVENT_UNIT_ITEM_SWAP)
        call RegisterAnyUnitInventoryEvent(Condition(function onMove), EVENT_UNIT_ITEM_MOVE)
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        call initTrackable()
        
    endfunction
    
endlibrary
 
Why not use interpolation between the last X/Y positions of the mouse to make the dragging more smooth?

So instead of setting the X and Y of the dragged icon to the trackable the mouse is on, set it to interpolated values depending on the last trackable and the current trackable. This will allow you 32 fps updates of the dragged icon.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
if I get your comment correctly...
so it's like giving a simple 2D movement to the icon (like 2D missile, idk the name) ?
it will be very smooth but I'm affraid the icon is moving too slow and left behind from the cursor too far :/
is that a problem?

and another problem is that those lazy wc3 coders didn't make the trigger fires right after we hover another track, I mean it has a very bad delay.. makes the drag looks ugly when we move the cursor too fast...

and another strange thingy is that: idk why it's lagged when we create so many small trackable that is visually invisible, I think wc3 does a wasted process, I mean maybe you don't need to process thing in-game that actually doesn't have visible texture (invisible) and doesn't have animation too.. it's just stupid shortcomings.. and I think playing trackable animation is just a very bad idea, meanless and useless..
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
well, after so many reconsiderings, I cancels my plan to release this to public, I have no time to learn struct (Purge said it's required for this system) and will use table instead. maybe I will implement struct then release it after my game is finished.. still I will post the progress here. :) btw, drag feature is removed, changed to something nicer..
 
Level 6
Joined
Jul 30, 2013
Messages
282
excuse me but...

ARE YOU FUCKING SERIOUS?!?!?!?!

struct/class is a very common base level programming construct..
(vjass struct would be considered a class in most other languages)

class is* (a description for a certain type of object, namespace for functions/methods) and that's bout it..

you need to learn it regardless of if you release this system or not.



leveraging some object oriented vJASS goodness would definitely help make the system more intuitive to work with for the majority of people who would ever actually go thru the trouble of using it (not likely total noobs would use such resources )

now.. presuming your globals groupings mean what i think they do..

this..[jass=vjass]
globals
public unit array CameraUnit
public real array CameraX
public real array CameraY
private integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
private integer ItemTotal = 0
private force PlayerForce = CreateForce()
private trigger OnClick = CreateTrigger()
private trigger OnHover = CreateTrigger()
private trigger ItemPickup = CreateTrigger()
private rect TrackArea
public Table array SlotX[11]
public Table array SlotY[11]
private Table array SlotIndex
private Table array SlotCategory
private Table array SlotCharge
private Table array SlotCooldown
private Table EmptySlot
private Table ItemIndex
private Table ItemIcon
private Table ItemCategory
private Table ItemCooldown
private Table ItemCharge
private Table TrackableX
private Table TrackableY
endglobals
private function GetEmptySlot takes integer hand returns integer
local integer i = 0

loop
exitwhen i == SlotTotal
if ItemIndex[hand].integer == 0 then
return i
endif
set i = i + 1
endloop
return i
endfunction[/code]
should translate elegantly to this...
[jass=vjass]
struct FSI
public unit CameraUnit
public real CameraX
public real CameraY
private static integer SlotTotal = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT * ITEM_SLOT_PAGES
private static integer ItemTotal = 0
private static force PlayerForce = CreateForce()
private static trigger OnClick = CreateTrigger()
private static trigger OnHover = CreateTrigger()
private static trigger ItemPickup = CreateTrigger()
private static rect TrackArea
public Table SlotX
public Table SlotY
private Table SlotIndex
private Table SlotCategory
private Table SlotCharge
private Table SlotCooldown

//it seems these are shared/temp/truly global..
private static Table EmptySlot
private static Table ItemIndex
private static Table ItemIcon
private static Table ItemCategory
private static Table ItemCooldown
private static Table ItemCharge
private static Table TrackableX
private static Table TrackableY
private method getEmptySlot takes nothing returns integer
local integer i = 0
loop
exitwhen i == SlotTotal
if this.ItemIndex.integer == 0 then
return i
endif
set i = i + 1
endloop
return i
endmethod
endstruct

[/code]
use.. would be sth like..
[jass=vjass]
FSI myfsi= FSI.create();
static stuff you would access FSI.EmptySlot
non-static as mfsi.SlotX
[/code]



also you might want to check out a few java tutorials.. since it is impossible to write java without understanding classes a LOT of effort has gone to finding the most elegant explanations/tutorials possible (tho verbose, the code is -- usually-- very readable so its good as a learning base)

*class is also used for things like encapsulaton, inheritance and a few other useful things but they are not the essence of what a class is, you can ignore them for now.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
struct/class is a very common base level programming construct..
(vjass struct would be considered a class in most other languages)
I can use it in c++ but somehow I'm not sure I understand it on vJass (bcs I haven't tried it yet) I'm still a noob programer btw and still don't understand how exactly a struct works.. but I think it's different between c++ and vJass (at vJass, struct is just integer and just another representation of normal indexing like somestruct.caster == caster[somestruct]) so I guess it does need different understanding..

EDIT:
wait, I haven't learned about class, what I know is just struct, I'm still in very early year of my college (~0,75 y) yup, second semester I mean..
 
Level 6
Joined
Jul 30, 2013
Messages
282
in c++ struct and class are nearly the same..

in essence a class is like struct++ :)

and.. no, unless you want to do really hacky stuff you can use them just as you would in c++. (afterall in C++ also they are just ints/pointers internally, a lot of things are just ints in the end, that does not mean we have to think about it constantly)

just skip the struct extends array thing that nes' is pushing and you will have something really elegant (bit bad for performance i guess.. but you can always add it later if its actually in a state somebody might want to use it)
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
system is back to developement (I'm at my PC now and for the next 4 months, hope this will be finished) and so do my charming Magic Soul :>

Still using normal vJass, not very optimized but here is the newest update:
JASS:
library UltimateInventoryCore initializer ini uses Table, Track optional ClearItems, optional WorldBounds, optional AutoFly

    globals
        // Miscs
        private constant    integer         BUTTON_ID                   = 'u000'
        private constant    integer         DUMMY_ID                    = 'u001'
        private constant    integer         SPELL_ID                    = 'SIA0'
        private constant    real            LOCATION_X                  = -5600.0
        private constant    real            LOCATION_Y                  = -6200.0
        private constant    real            LOCATION_Z                  = 10.0
        private constant    real            BUTTON_NORMALZ              = 0.0
        private constant    real            BUTTON_DRAGZ                = 175.0
        private constant    real            TOOLTIP_SIZE                = 0.5
        private constant    real            ACCURACY                    = 0.0312500
        private constant    real            CAMERA_DISTANCE             = 1800.0
        private constant    real            TRACKABLE_SPACE             = 32.0
        private constant    real            TRACKABLE_EXPAND_AREA       = 768.0
        private constant    string          TRACKABLE_PATH_1            = "war3mapImported\\text-track1.mdx"
        private constant    string          TRACKABLE_PATH_2            = "war3mapImported\\text-track2.mdx"
        private constant    boolean         AUTO_STACK                  = true
        private constant    boolean         DO_PRELOAD                  = true//false
        // Destructables
        private constant    integer         UI_BORDER_LEFT              = 'SIS5'
        private constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        private constant    integer         UI_BORDER_UP                = 'SIS7'
        private constant    integer         UI_BORDER_DOWN              = 'SIS8'
        private constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        private constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        private constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        private constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        private constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        private constant    integer         UI_NEXT_PAGE                = 'SISB'
        private constant    integer         UI_PREV_PAGE                = 'SISA'
        private constant    integer         UI_PAGE_BUTTON_POSITION     = 0
        private constant    real            UI_PAGE_BUTTON_SPACE        = 64.
        private constant    real            UI_SIZE                     = 1.
        // Interfaces
        private constant    integer         ITEM_SLOT_WIDTH             = 5
        private constant    integer         ITEM_SLOT_HEIGHT            = 4
        private constant    integer         ITEM_SLOT_PAGES             = 5
        private constant    integer         TOOLTIP_MAX_LENGTH          = 32
        // Item categories
        private constant    integer         ITEM_CATEGORY_LEFT_WEAPON   = 1
        private constant    integer         ITEM_CATEGORY_RIGHT_WEAPON  = 2
        private constant    integer         ITEM_CATEGORY_TWO_HANDED    = 3
        private constant    integer         ITEM_CATEGORY_ACCESSORY     = 4
        private constant    integer         ITEM_CATEGORY_HELMET        = 5
        private constant    integer         ITEM_CATEGORY_ARMOR         = 6
        private constant    integer         ITEM_CATEGORY_GLOVES        = 7
        private constant    integer         ITEM_CATEGORY_LEGS          = 8
        private constant    integer         ITEM_CATEGORY_BOOTS         = 9
        private constant    integer         ITEM_CATEGORY_BACK          = 10
        private constant    integer         ITEM_CATEGORY_MISCS         = 11
        private constant    integer         ITEM_CATEGORY_CONSUMABLES   = 12
    endglobals
    
    globals
        private integer ItemTotal = 0
        private integer PreloadTotal = -1
        private integer SlotPage = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT
        private integer SlotTotal = SlotPage * ITEM_SLOT_PAGES
        private boolean array IsOpening
        private boolean array IsClick
        private integer array ClickIndex
        private real InventoryAreaMaxX
        private real InventoryAreaMaxY
        private real TrackX
        private real TrackY
        private real WorldMaxX
        private real WorldMaxY
        private real array SlotX
        private real array SlotY
        private real array MacroArea
        private real array MacroX
        private real array MacroY
        public real CameraX
        public real CameraY
        public real PrevButtonX
        public real PrevButtonY
        public real NextButtonX
        public real NextButtonY
        private rect TrackArea
        private trigger ItemPickup = CreateTrigger()
        private trigger ItemSwap = CreateTrigger()
        public unit CameraUnit
        private integer CameraCounter = -1
        private timer CameraTimer = CreateTimer()
        private TableArray SlotIndex
        private TableArray SlotIcon
        private TableArray SlotCategory
        private TableArray SlotCharge
        private TableArray SlotCooldown
        private TableArray MacroIndex
        private TableArray MacroIcon
        private TableArray MacroCategory
        private TableArray MacroCharge
        private TableArray MacroCooldown
        private Table PlayerPage
        private Table PlayerPageMax
        private Table EquipEnabled
        private Table MacroEnabled
        private Table PreloadUnit
        private Table EmptySlot
        private Table ItemIndex
        private Table ItemType
        private Table ItemIcon
        private Table ItemCategory
        private Table ItemCooldown
        private Table ItemCharge
    endglobals
    
    private function getPreloadUnit takes player p returns unit
    
        local unit r = PreloadUnit.unit[PreloadTotal]
    
        set PreloadTotal = PreloadTotal - 1
        if GetLocalPlayer() == p then
            call SetUnitScale(r, UI_SIZE, 0., 0.)
        endif
        
        return r
    endfunction
    
    private function releasePreloadUnit takes unit u returns nothing
    
        set PreloadTotal = PreloadTotal + 1
        set PreloadUnit.unit[PreloadTotal] = PreloadUnit.unit[0]
        set PreloadUnit.unit[0] = u
        static if LIBRARY_WorldBounds then
            call SetUnitX(u, WorldBounds.maxX)
            call SetUnitY(u, WorldBounds.maxY)
        else
            call SetUnitX(u, WorldMaxX)
            call SetUnitY(u, WorldMaxY)
        endif
        call SetUnitScale(PreloadUnit.unit[0], 0., 0., 0.)
        
    endfunction
    
    private function getEmptySlot takes integer id returns integer
    
        local integer i = 1
        
        loop
            exitwhen i > SlotTotal
            // If slot is empty then return the slot index
            if SlotIndex[id].integer[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        
        // Because the loop ended when i > SlotTotal so at this state i == SlotTotal + 1
        // which means inventory is full
        return i
    endfunction
    
    private function getStackingSlot takes integer slotDex returns integer
        return 0
    endfunction
    
    private function changeTexture takes unit whichUnit, integer whichTexture returns nothing
    
        local destructable d  = CreateDestructable(whichTexture, GetUnitX(whichUnit) + 32. * Cos(270. * bj_DEGTORAD), GetUnitY(whichUnit) + 32. * Sin(270. * bj_DEGTORAD), 0, 0, 1)

        call IssueTargetOrderById(whichUnit, 852511, d)
        call RemoveDestructable(d)
        set d = null
        
    endfunction
    
    private function updateSlotIcon takes integer id, integer slot, integer dex, player p returns nothing
    
        // If everything have been preloaded
        static if DO_PRELOAD then
            // Get preloaded unit
            set SlotIcon[id].unit[slot] = getPreloadUnit(p)
            call SetUnitX(SlotIcon[id].unit[slot], SlotX[slot])
            call SetUnitY(SlotIcon[id].unit[slot], SlotY[slot])
        else
            set SlotIcon[id].unit[slot] = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, SlotX[slot], SlotY[slot], 270.)
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(SlotIcon[id].unit[slot], 'Amrf') and UnitRemoveAbility(SlotIcon[id].unit[slot], 'Amrf') then
                endif
                call SetUnitFlyHeight(SlotIcon[id].unit[slot], LOCATION_Z + 5., 0.)
            endif
        endif
        call changeTexture(SlotIcon[id].unit[slot], ItemIcon.integer[dex])
        
    endfunction
    
    private function cameraLoop takes nothing returns nothing
        
        local integer i = 0
        
        loop
            exitwhen i > bj_MAX_PLAYER_SLOTS
            if IsOpening[i] then
                if GetLocalPlayer() == Player(i) then
                    call SetCameraTargetController(CameraUnit, 0, 0, false)
                    call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, CAMERA_DISTANCE, 0)
                    call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270., 0)
                endif
            endif
            set i = i + 1
        endloop
        
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean isOpen returns boolean
    
        local integer pn = GetPlayerId(GetOwningPlayer(whichUnit))
    
        if EmptySlot.integer[GetHandleId(whichUnit)] > 0 then
            set IsOpening[pn] = isOpen
            if isOpen then
                set CameraCounter = CameraCounter + 1
                if CameraCounter == 0 then
                    call TimerStart(CameraTimer, ACCURACY, true, function cameraLoop)
                endif
            else
                set CameraCounter = CameraCounter - 1
                if CameraCounter < 0 then
                    call PauseTimer(CameraTimer)
                endif
            endif
            return true
        endif
        
        return false
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
    
        local integer id = GetHandleId(whichUnit)
    
        set EmptySlot[id] = 1
        set PlayerPage.integer[id] = 1
        set PlayerPageMax.integer[id] = ITEM_SLOT_PAGES
        set SlotIndex = TableArray[SlotTotal]
        set SlotIcon = TableArray[SlotTotal]
        set SlotCategory = TableArray[SlotTotal]
        set SlotCharge = TableArray[SlotTotal]
        set SlotCooldown = TableArray[SlotTotal]
        set MacroEnabled.boolean[id] = enableMacro
        set EquipEnabled.boolean[id] = enableEquip
        if enableMacro then
            set MacroIndex = TableArray[6]
            set MacroIcon = TableArray[6]
            set MacroCategory = TableArray[6]
            set MacroCharge = TableArray[6]
            set MacroCooldown = TableArray[6]
        endif
        
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
    
        local integer id = GetHandleId(whichUnit)
        
        set EmptySlot[id] = 0
        call SlotIndex[id].destroy()
        call SlotIcon[id].destroy()
        call SlotCategory[id].destroy()
        call SlotCharge[id].destroy()
        call SlotCooldown[id].destroy()
        call MacroIndex[id].destroy()
        call MacroIcon[id].destroy()
        call MacroCategory[id].destroy()
        call MacroCharge[id].destroy()
        call MacroCooldown[id].destroy()
        
    endfunction
    
    function RegisterItemToLibrary takes integer itemID, integer itemIcon, integer itemCategory, integer itemMaxCharge, real itemCooldown, boolean itemUseable returns nothing
    
        set ItemTotal = ItemTotal + 1
        set ItemIndex.integer[itemID] = ItemTotal
        set ItemType.integer[itemID] = itemID
        set ItemIcon.integer[itemID] = itemIcon
        set ItemCategory.integer[itemID] = itemCategory
        set ItemCharge.integer[itemID] = itemMaxCharge
        set ItemCooldown.real[itemID] = itemCooldown
        
    endfunction
    
    function SetItemBonus takes integer itemID, integer bonusType, real bonusAmount returns nothing
    endfunction
    
    function SetItemDescription takes integer itemID, string itemDescription returns nothing
    endfunction
    
    function AddInventoryItem takes unit whichUnit, integer itemID, integer itemAmount returns boolean
    
        local player p = GetOwningPlayer(whichUnit)
        local integer id = GetHandleId(whichUnit)
        local integer slot
        local integer pn = GetPlayerId(p)
        
        // If unit has inventory
        if EmptySlot[id] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if EmptySlot[id] <= SlotTotal then
                    // Set slot index to item's index at library
                    set SlotIndex[id].integer[EmptySlot[id]] = ItemIndex.integer[itemID]
                    // Set slot category to item's category at library
                    set SlotCategory[id].integer[EmptySlot[id]] = ItemCategory.integer[itemID]
                    // If player is currently opening the page then create icon for slot
                    if PlayerPage.integer[id] == EmptySlot[id] / SlotPage + 1 or EmptySlot[id] == SlotPage * PlayerPage.integer[id] then
                        set slot = EmptySlot[id] - (EmptySlot[id] / SlotPage) * SlotPage
                        if slot == 0 then
                            set slot = SlotPage
                        endif
                        if slot <= SlotPage * PlayerPage.integer[id] then
                            call updateSlotIcon(id, slot, itemID, p)
                        endif
                    endif
                    // If items is allowed more than 1 charge
                    if ItemCharge.integer[itemID] > 0 then
                        static if AUTO_STACK then
                            // Get the slot that can be stacked
                            set EmptySlot[id] = getStackingSlot(EmptySlot[id])
                        endif
                        // If charge amount is higher than max charge then
                        if SlotCharge[id].integer[EmptySlot[id]] + itemAmount > ItemCharge.integer[itemID] then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (ItemCharge.integer[itemID] - SlotCharge[id].integer[EmptySlot[id]])
                            // Set slot charge to max charge
                            set SlotCharge[id].integer[EmptySlot[id]] = ItemCharge.integer[itemID]
                        // If not just sum it
                        else
                            set SlotCharge[id].integer[EmptySlot[id]] = SlotCharge[id].integer[EmptySlot[id]] + itemAmount
                            set itemAmount = 0
                        endif
                    else
                        set SlotCharge[id].integer[EmptySlot[id]] = 0
                    endif
                    // Check for other empty slot to fill at next add item event
                    set EmptySlot[id] = getEmptySlot(id)
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucesfully added
            return true
        endif
        
        return false
    endfunction
    
    function AddInventoryItemAtSlot takes unit whichUnit, integer itemID, integer itemAmount, integer slotIndex returns boolean
    
        local integer id = GetHandleId(whichUnit)
        
        // First set current empty slot at desired item index
        set EmptySlot[id] = slotIndex
        // If item is sucessfully added to inventory then return true
        if AddInventoryItem(whichUnit, itemID, itemAmount) then
            return true
        // If not just check for another empty slot so it will not bugged
        else
            set EmptySlot[id] = getEmptySlot(id)
        endif
        
        return false
    endfunction
    
    function DropInventoryItem takes integer slotIndex returns nothing
    endfunction
    
    function MoveInventoryItemToSlot takes integer slotSource, integer slotTarget returns nothing
    endfunction
    
    private function preloadUnits takes nothing returns nothing
    
        local integer i = 0
        local unit u
        
        loop
            exitwhen i > SlotPage + 6
            set PreloadTotal = PreloadTotal + 1
            static if LIBRARY_WorldBounds then
                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270.)
            else
                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), BUTTON_ID, WorldMaxX, WorldMaxY, 270.)
            endif
            set PreloadUnit.unit[PreloadTotal] = u
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
                endif
            endif
            call UnitAddAbility(u, SPELL_ID)
            call SetUnitFlyHeight(u, LOCATION_Z + BUTTON_NORMALZ, 0)
            call SetUnitScale(u, 0., 0., 0.)
            set i = i + 1
        endloop
        set u = null
        
    endfunction
    
    private function onPickup takes nothing returns boolean
        call AddInventoryItem(GetTriggerUnit(), GetItemTypeId(GetManipulatedItem()), GetItemCharges(GetManipulatedItem()))
        return false
    endfunction
    
    private function onClick takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local real s = 32. * UI_SIZE
        local integer i = 1
        local integer pn = GetPlayerId(GetTriggerTrackablePlayer())
        
        loop
            exitwhen i > SlotPage
            if x < SlotX[i] + s and x > SlotX[i] - s and y < SlotY[i] + s and y > SlotY[i] - s then
                if IsClick[pn] then
                    //call MoveInventoryItemToSlot(ClickIndex[pn], i)
                else
                    set ClickIndex[pn] = i
                endif
                set IsClick[pn] = not IsClick[pn]
            endif
            set i = i + 1
        endloop
        
        return false
    endfunction
    
    private function onHover takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local integer pn = GetPlayerId(GetTriggerTrackablePlayer())
        
        if IsClick[pn] then
            call SetUnitX(SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[ClickIndex[pn]], x)
            call SetUnitY(SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[ClickIndex[pn]], y)
        endif
        
        return false
    endfunction
    
    private function initMacro takes nothing returns nothing
    
        local integer dex = 0
        local player p = GetEnumPlayer()
        local integer i = 0
        local integer j
        local real x = InventoryAreaMaxX + 64. * UI_SIZE * 3
        local real y = InventoryAreaMaxY - 64. * UI_SIZE
        local destructable des
        
        loop
            exitwhen i > 2
            set j = 0
            loop
                exitwhen j > 1
                if i == 0 then
                    if j == 0 then
                        // Min x
                        set MacroArea[2] = x + 64. * UI_SIZE * j
                        set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        // Max y
                        set MacroArea[1] = y - 64. * UI_SIZE * i
                        set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                elseif i == 2 then
                    if j == 0 then
                        // Min y
                        set MacroArea[3] = y - 64. * UI_SIZE * i
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        // Max x
                        set MacroArea[0] = x + 64. * UI_SIZE * j
                        set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                else
                    if j == 0 then
                        set des = CreateDestructableZ(UI_BORDER_LEFT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    else
                        set des = CreateDestructableZ(UI_BORDER_RIGHT, x + 64. * UI_SIZE * j, y - 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                    endif
                endif
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set MacroX[dex] = x + 64. * UI_SIZE * j
                set MacroY[dex] = y - 64. * UI_SIZE * i
                set des = CreateDestructableZ(UI_SLOT_EMPTY, MacroX[dex], MacroY[dex], LOCATION_Z, 270., UI_SIZE, 0)
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set dex = dex + 1
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set CameraX = LOCATION_X + (InventoryAreaMaxX - LOCATION_X + (MacroArea[0] - MacroArea[2]))/2
        set CameraY = LOCATION_Y + (InventoryAreaMaxY - LOCATION_Y)/2
        set CameraUnit = CreateUnit(p, DUMMY_ID, CameraX, CameraY, 270.)
        set des = null
        
    endfunction
    
    private function initInterface takes nothing returns nothing
    
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local integer i = 0
        local integer j
        local integer ext_i = 8 + ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + ITEM_SLOT_WIDTH
        local integer dex
        local destructable des
        
        if GetPlayerController(p) == MAP_CONTROL_USER then
            loop
                exitwhen i > ext_i
                set j = 0
                loop
                    exitwhen j > ext_j
                    if i == 0 then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_CORNER_BOTTOM_RIGHT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_DOWN, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    elseif i == ext_i then
                        if j == 0 then
                            set des = CreateDestructableZ(UI_CORNER_UPPER_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            // Get inventory area max x and y when loop will be ended
                            set InventoryAreaMaxX = LOCATION_X + 64. * UI_SIZE * j
                            set InventoryAreaMaxY = LOCATION_Y + 64. * UI_SIZE * i
                            set des = CreateDestructableZ(UI_CORNER_UPPER_RIGHT, InventoryAreaMaxX, InventoryAreaMaxY, LOCATION_Z, 270., UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(UI_BORDER_UP, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    else
                        if j == 0 then
                            set des = CreateDestructableZ(UI_BORDER_LEFT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(UI_BORDER_RIGHT, LOCATION_X + 64. * UI_SIZE * j, LOCATION_Y + 64. * UI_SIZE * i, LOCATION_Z, 270., UI_SIZE, 0)
                        endif
                        if i < 1 + ITEM_SLOT_HEIGHT then
                            if j > 0 and j <= ITEM_SLOT_WIDTH then
                                set dex = ITEM_SLOT_HEIGHT * ITEM_SLOT_WIDTH - ITEM_SLOT_WIDTH * i + j
                                set SlotX[dex] = LOCATION_X + 64. * UI_SIZE * j
                                set SlotY[dex] = LOCATION_Y + 64. * UI_SIZE * i
                                set des = CreateDestructableZ(UI_SLOT_EMPTY, SlotX[dex], SlotY[dex], LOCATION_Z, 270., UI_SIZE, 0)
                            endif
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    endif
                    set j = j + 1
                endloop
                set i = i + 1
            endloop
            static if DO_PRELOAD then
                call preloadUnits()
            endif
            if UI_PAGE_BUTTON_POSITION == 0 then
                set PrevButtonX = (SlotX[1] + ((64. * UI_SIZE) * (ITEM_SLOT_WIDTH - 1))/2) - UI_PAGE_BUTTON_SPACE
                set PrevButtonY = SlotY[1] + (64. * UI_SIZE)
                set NextButtonX = (SlotX[1] + ((64. * UI_SIZE) * (ITEM_SLOT_WIDTH - 1))/2) + UI_PAGE_BUTTON_SPACE
                set NextButtonY = SlotY[1] + (64. * UI_SIZE)
            endif
            set des = CreateDestructableZ(UI_NEXT_PAGE, NextButtonX, NextButtonY, LOCATION_Z, 270., UI_SIZE, 0)
            if GetLocalPlayer() != p then
                call ShowDestructable(des, false)
            endif
            set des = CreateDestructableZ(UI_PREV_PAGE, PrevButtonX, PrevButtonY, LOCATION_Z, 270., UI_SIZE, 0)
            if GetLocalPlayer() != p then
                call ShowDestructable(des, false)
            endif
            call ForForce(bj_FORCE_PLAYER[pn], function initMacro)
            set IsClick[pn] = false
            set IsOpening[pn] = false
            set des = null
        endif
        
    endfunction
    
    private function addTracks takes nothing returns nothing
    
        local Track track
        
        if (TrackX <= InventoryAreaMaxX + UI_SIZE * 64. and TrackX >= LOCATION_X - UI_SIZE * 64. and TrackY <= InventoryAreaMaxY + UI_SIZE * 64. and TrackY >= LOCATION_Y - UI_SIZE * 64.) /*
        */ or (TrackX <= MacroArea[0] + UI_SIZE * 64. and TrackX >= MacroArea[2] - UI_SIZE * 64. and TrackY <= MacroArea[1] + UI_SIZE * 64. and TrackY >= MacroArea[3] - UI_SIZE * 64.) then
            set track = CreateTrack(TRACKABLE_PATH_1, TrackX, TrackY, LOCATION_Z-4., 3.14)
        else
            set track = CreateTrack(TRACKABLE_PATH_2, TrackX, TrackY, LOCATION_Z-4., 3.14)
        endif
        call RegisterClickEvent(track, function onClick)
        call RegisterHoverEvent(track, function onHover)
        
    endfunction
    
    private function initTrackable takes nothing returns nothing
    
        set TrackX = LOCATION_X - TRACKABLE_EXPAND_AREA
        loop
            exitwhen TrackX > InventoryAreaMaxX + TRACKABLE_EXPAND_AREA + TRACKABLE_SPACE
            set TrackY = LOCATION_Y - TRACKABLE_EXPAND_AREA
            loop
                exitwhen TrackY > InventoryAreaMaxY + TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set TrackY = TrackY + TRACKABLE_SPACE
            endloop
            set TrackX = TrackX + TRACKABLE_SPACE
        endloop
        
    endfunction
    
    private function ini takes nothing returns nothing
    
        local integer i = 0
        
        set EmptySlot = Table.create()
        set ItemIndex = Table.create()
        set ItemType = Table.create()
        set ItemIcon = Table.create()
        set ItemCategory = Table.create()
        set ItemCooldown = Table.create()
        set ItemCharge = Table.create()
        set PlayerPage = Table.create()
        set PlayerPageMax = Table.create()
        set MacroEnabled = Table.create()
        set EquipEnabled = Table.create()
        static if DO_PRELOAD then
            set PreloadUnit = Table.create()
        endif
        static if not LIBRARY_WorldBounds then
            set WorldMaxX = GetRectMaxX(GetWorldBounds())
            set WorldMaxY = GetRectMaxY(GetWorldBounds())
        endif
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call TriggerRegisterAnyUnitEventBJ(ItemPickup, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(ItemPickup, Condition(function onPickup))
        call initTrackable()
        
    endfunction
    
endlibrary
- Drag feature is kept
- Still done in vJass
- Very not optimized. Optimization will be on but later. Don't give me a sit about this :)
- Click the thumbnail below to see screenie
 

Attachments

  • inv.jpg
    inv.jpg
    143.8 KB · Views: 151
Level 6
Joined
Jul 30, 2013
Messages
282
[jass=vjass]
function DropInventoryItem takes integer slotIndex returns nothing
endfunction

function MoveInventoryItemToSlot takes integer slotSource, integer slotTarget returns nothing
endfunction
[/code]

such elegant functions u got, must be magic :p
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Now the body is separated into several triggers:
JASS:
library Globals initializer ini uses Containers, Table
    
    globals
        // Miscs
        public constant    integer         BUTTON_ID                   = 'u000'
        public constant    integer         DUMMY_ID                    = 'u001'
        public constant    integer         SPELL_ID                    = 'SIA0'
        public constant    real            LOCATION_X                  = -5600.0
        public constant    real            LOCATION_Y                  = -6200.0
        public constant    real            LOCATION_Z                  = 10.0
        public constant    real            BUTTON_NORMALZ              = 0.0
        public constant    real            BUTTON_DRAGZ                = 175.0
        public constant    real            TOOLTIP_SIZE                = 0.5
        public constant    real            ACCURACY                    = 0.0312500
        public constant    real            CAMERA_DISTANCE             = 1800.0
        public constant    real            TRACKABLE_SPACE             = 32.0
        public constant    real            TRACKABLE_EXPAND_AREA       = 768.0
        public constant    string          TRACKABLE_PATH_1            = "war3mapImported\\text-track1.mdx"
        public constant    string          TRACKABLE_PATH_2            = "war3mapImported\\text-track2.mdx"
        public constant    boolean         AUTO_STACK                  = true
        public constant    boolean         DO_PRELOAD                  = true//false
        
        // Destructables
        public constant    integer         UI_BORDER_LEFT              = 'SIS5'
        public constant    integer         UI_BORDER_RIGHT             = 'SIS6'
        public constant    integer         UI_BORDER_UP                = 'SIS7'
        public constant    integer         UI_BORDER_DOWN              = 'SIS8'
        public constant    integer         UI_CORNER_UPPER_RIGHT       = 'SIS1'
        public constant    integer         UI_CORNER_UPPER_LEFT        = 'SIS2'
        public constant    integer         UI_CORNER_BOTTOM_RIGHT      = 'SIS3'
        public constant    integer         UI_CORNER_BOTTOM_LEFT       = 'SIS4'
        public constant    integer         UI_SLOT_EMPTY               = 'SIS9'
        public constant    integer         UI_NEXT_PAGE                = 'SISB'
        public constant    integer         UI_PREV_PAGE                = 'SISA'
        public constant    integer         UI_PAGE_BUTTON_POSITION     = 2
        public constant    real            UI_PAGE_BUTTON_SPACE        = 128.
        public constant    real            UI_SIZE                     = 1.
        
        // Interfaces
        public constant    integer         ITEM_SLOT_WIDTH             = 5
        public constant    integer         ITEM_SLOT_HEIGHT            = 4
        public constant    integer         ITEM_SLOT_PAGES             = 5
        public constant    integer         TOOLTIP_MAX_LENGTH          = 32
        
    endglobals
    
    private keyword SlotData
    
    globals
        public constant integer PAGE_SLOT = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT
        public constant integer TOTAL_SLOT = PAGE_SLOT * ITEM_SLOT_PAGES
        public integer ItemTotal = 0
        public integer PreloadTotal = -1
        public integer CameraCounter = -1
        public integer array EquipSlotIcon
        public integer array ClickIndex
        public boolean array IsOpening
        public boolean array IsClick
        public real InventoryAreaMaxX
        public real InventoryAreaMaxY
        public real TrackX
        public real TrackY
        public real WorldMaxX
        public real WorldMaxY
        public real array SlotX
        public real array SlotY
        public real array MacroArea
        public real array MacroX
        public real array MacroY
        public real CameraX
        public real CameraY
        public real PrevButtonX
        public real PrevButtonY
        public real NextButtonX
        public real NextButtonY
        public rect TrackArea
        public timer CameraTimer = CreateTimer()
        public trigger ItemPickup = CreateTrigger()
        public trigger ItemSwap = CreateTrigger()
        public unit CameraUnit
        public unit array InventoryUnit
        public integer array PlayerPage
        public integer array PlayerPageMax
        public integer array EmptySlot
        public boolean array EquipEnabled
        public boolean array MacroEnabled
        public unit array PreloadUnit
        public Containers_SlotData Slot
        public Containers_MacroData Macro
        public Containers_EquipData Equip
        public Table ItemIndex
    endglobals
    
    private function ini takes nothing returns nothing
    endfunction
    
endlibrary
JASS:
library Containers

    public struct ItemData
        integer id
        integer icon
        integer charge
        integer type
        real cooldown
    endstruct

    public struct SlotData
        integer array index[Globals_TOTAL_SLOT]
        integer array icon[Globals_TOTAL_SLOT]
        integer array charge[Globals_TOTAL_SLOT]
        integer array type[Globals_TOTAL_SLOT]
        real array cooldown[Globals_TOTAL_SLOT]
    endstruct

    public struct MacroData
        integer array index[Globals_TOTAL_SLOT]
        integer array icon[Globals_TOTAL_SLOT]
        integer array charge[Globals_TOTAL_SLOT]
        integer array type[Globals_TOTAL_SLOT]
        real array cooldown[Globals_TOTAL_SLOT]
    endstruct

    public struct EquipData
        integer array index[Globals_TOTAL_SLOT]
        integer array icon[Globals_TOTAL_SLOT]
        integer array charge[Globals_TOTAL_SLOT]
        integer array type[Globals_TOTAL_SLOT]
        real array cooldown[Globals_TOTAL_SLOT]
    endstruct

endlibrary
JASS:
library Preloader
    
    globals
        private integer Counter = -1
        private unit array Unit
    endglobals
    
    public function getPreloadUnit takes player p returns unit
    
        local unit r = Unit[Counter]
    
        set Counter = Counter - 1
        if GetLocalPlayer() == p then
            call SetUnitScale(r, Globals_UI_SIZE, 0., 0.)
        endif
        
        return r
    endfunction
    
    public function releasePreloadUnit takes unit u returns nothing
    
        set Counter = Counter + 1
        set Unit[Counter] = Unit[0]
        set Unit[0] = u
        static if LIBRARY_WorldBounds then
            call SetUnitX(u, WorldBounds.maxX)
            call SetUnitY(u, WorldBounds.maxY)
        else
            call SetUnitX(u, WorldMaxX)
            call SetUnitY(u, WorldMaxY)
        endif
        call SetUnitScale(Unit[0], 0, 0, 0)
        
    endfunction
    
    public function preload takes nothing returns nothing
    
        local integer i = 0
        local unit u
        
        loop
            exitwhen i > Globals_PAGE_SLOT + 6
            set Counter = Counter + 1
            static if LIBRARY_WorldBounds then
                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), Globals_BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270.)
            else
                set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), Globals_BUTTON_ID, WorldMaxX, WorldMaxY, 270.)
            endif
            set Unit[Counter] = u
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
                endif
            endif
            call UnitAddAbility(u, Globals_SPELL_ID)
            call SetUnitFlyHeight(u, Globals_LOCATION_Z + Globals_BUTTON_NORMALZ, 0)
            call SetUnitScale(u, 0, 0, 0)
            set i = i + 1
        endloop
        set u = null
        
    endfunction
    
endlibrary
JASS:
library InventoryUI initializer ini uses Track, Preloader
    
    public function onClick takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local real s = 32 * Globals_UI_SIZE
        local integer i
        local integer pn = GetPlayerId(GetTriggerTrackablePlayer())
        local integer id = GetHandleId(Globals_InventoryUnit[pn])
        
        if x < Globals_PrevButtonX + s and x > Globals_PrevButtonX - s and y < Globals_PrevButtonY + s and y > Globals_PrevButtonY - s then
            set Globals_PlayerPage[id] = Globals_PlayerPage[id] - 1
            if Globals_PlayerPage[id] < 1 then
                set Globals_PlayerPage[id] = Globals_PlayerPageMax[id]
            endif
        elseif x < Globals_NextButtonX + s and x > Globals_NextButtonX - s and y < Globals_NextButtonY + s and y > Globals_NextButtonY - s then
            set Globals_PlayerPage[id] = Globals_PlayerPage[id] + 1
            if Globals_PlayerPage[id] > Globals_PlayerPageMax[id] then
                set Globals_PlayerPage[id] = 1
            endif
        else
            set i = 1
            loop
                exitwhen i > Globals_PAGE_SLOT
                if x < Globals_SlotX[i] + s and x > Globals_SlotX[i] - s and y < Globals_SlotY[i] + s and y > Globals_SlotY[i] - s then
                    if Globals_IsClick[pn] then
                        //call MoveInventoryItemToSlot(Globals_ClickIndex[pn], i)
                    else
                        set Globals_ClickIndex[pn] = i
                    endif
                    set Globals_IsClick[pn] = not Globals_IsClick[pn]
                endif
                set i = i + 1
            endloop
        endif
        
        return false
    endfunction
    
    public function onHover takes nothing returns boolean
        
        local real x = GetTriggerTrackInstance().x
        local real y = GetTriggerTrackInstance().y
        local integer pn = GetPlayerId(GetTriggerTrackablePlayer())
        
        if Globals_IsClick[pn] then
            /*call SetUnitX(Globals_SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[Globals_ClickIndex[pn]], x)
            call SetUnitY(Globals_SlotIcon[GetHandleId(gg_unit_Hamg_0013)].unit[Globals_ClickIndex[pn]], y)*/
        endif
        
        return false
    endfunction
    
    public function initMacro takes nothing returns nothing
    
        local integer dex = 0
        local player p = GetEnumPlayer()
        local integer i = 0
        local integer j
        local real x = Globals_InventoryAreaMaxX + 64. * Globals_UI_SIZE * 3
        local real y = Globals_InventoryAreaMaxY - 64. * Globals_UI_SIZE
        local destructable des
        
        loop
            exitwhen i > 2
            set j = 0
            loop
                exitwhen j > 1
                if i == 0 then
                    if j == 0 then
                        // Min x
                        set Globals_MacroArea[2] = x + 64. * Globals_UI_SIZE * j
                        set des = CreateDestructableZ(Globals_UI_CORNER_UPPER_LEFT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    else
                        // Max y
                        set Globals_MacroArea[1] = y - 64. * Globals_UI_SIZE * i
                        set des = CreateDestructableZ(Globals_UI_CORNER_UPPER_RIGHT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    endif
                elseif i == 2 then
                    if j == 0 then
                        // Min y
                        set Globals_MacroArea[3] = y - 64. * Globals_UI_SIZE * i
                        set des = CreateDestructableZ(Globals_UI_CORNER_BOTTOM_LEFT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    else
                        // Max x
                        set Globals_MacroArea[0] = x + 64. * Globals_UI_SIZE * j
                        set des = CreateDestructableZ(Globals_UI_CORNER_BOTTOM_RIGHT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    endif
                else
                    if j == 0 then
                        set des = CreateDestructableZ(Globals_UI_BORDER_LEFT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    else
                        set des = CreateDestructableZ(Globals_UI_BORDER_RIGHT, x + 64. * Globals_UI_SIZE * j, y - 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                    endif
                endif
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set Globals_MacroX[dex] = x + 64. * Globals_UI_SIZE * j
                set Globals_MacroY[dex] = y - 64. * Globals_UI_SIZE * i
                set des = CreateDestructableZ(Globals_UI_SLOT_EMPTY, Globals_MacroX[dex], Globals_MacroY[dex], Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                if GetLocalPlayer() != p then
                    call ShowDestructable(des, false)
                endif
                set dex = dex + 1
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set Globals_CameraX = Globals_LOCATION_X + (Globals_InventoryAreaMaxX - Globals_LOCATION_X + (Globals_MacroArea[0] - Globals_MacroArea[2]))/2
        set Globals_CameraY = Globals_LOCATION_Y + (Globals_InventoryAreaMaxY - Globals_LOCATION_Y)/2
        set Globals_CameraUnit = CreateUnit(p, Globals_DUMMY_ID, Globals_CameraX, Globals_CameraY, 270.)
        set des = null
        
    endfunction
    
    public function initInterface takes nothing returns nothing
    
        local player p = GetEnumPlayer()
        local integer pn = GetPlayerId(p)
        local integer i = 0
        local integer j
        local integer ext_i = 8 + Globals_ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + Globals_ITEM_SLOT_WIDTH
        local integer dex
        local destructable des
        
        if GetPlayerController(p) == MAP_CONTROL_USER then
            loop
                exitwhen i > ext_i
                set j = 0
                loop
                    exitwhen j > ext_j
                    if i == 0 then
                        if j == 0 then
                            set des = CreateDestructableZ(Globals_UI_CORNER_BOTTOM_LEFT, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(Globals_UI_CORNER_BOTTOM_RIGHT, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(Globals_UI_BORDER_DOWN, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    elseif i == ext_i then
                        if j == 0 then
                            set des = CreateDestructableZ(Globals_UI_CORNER_UPPER_LEFT, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        elseif j == ext_j then
                            // Get inventory area max x and y when loop will be ended
                            set Globals_InventoryAreaMaxX = Globals_LOCATION_X + 64. * Globals_UI_SIZE * j
                            set Globals_InventoryAreaMaxY = Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i
                            set des = CreateDestructableZ(Globals_UI_CORNER_UPPER_RIGHT, Globals_InventoryAreaMaxX, Globals_InventoryAreaMaxY, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        else
                            set des = CreateDestructableZ(Globals_UI_BORDER_UP, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    else
                        if j == 0 then
                            set des = CreateDestructableZ(Globals_UI_BORDER_LEFT, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        elseif j == ext_j then
                            set des = CreateDestructableZ(Globals_UI_BORDER_RIGHT, Globals_LOCATION_X + 64. * Globals_UI_SIZE * j, Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                        endif
                        if i < 1 + Globals_ITEM_SLOT_HEIGHT then
                            if j > 0 and j <= Globals_ITEM_SLOT_WIDTH then
                                set dex = Globals_ITEM_SLOT_HEIGHT * Globals_ITEM_SLOT_WIDTH - Globals_ITEM_SLOT_WIDTH * i + j
                                set Globals_SlotX[dex] = Globals_LOCATION_X + 64. * Globals_UI_SIZE * j
                                set Globals_SlotY[dex] = Globals_LOCATION_Y + 64. * Globals_UI_SIZE * i
                                set des = CreateDestructableZ(Globals_UI_SLOT_EMPTY, Globals_SlotX[dex], Globals_SlotY[dex], Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
                            endif
                        endif
                        if GetLocalPlayer() != p then
                            call ShowDestructable(des, false)
                        endif
                    endif
                    set j = j + 1
                endloop
                set i = i + 1
            endloop
            static if DO_PRELOAD then
                call Preloader_preload()
            endif
            if Globals_UI_PAGE_BUTTON_POSITION == 0 then
                set Globals_PrevButtonX = (Globals_SlotX[1] + ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_WIDTH - 1))/2) - Globals_UI_PAGE_BUTTON_SPACE
                set Globals_PrevButtonY = Globals_SlotY[1] + (64. * Globals_UI_SIZE)
                set Globals_NextButtonX = (Globals_SlotX[1] + ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_WIDTH - 1))/2) + Globals_UI_PAGE_BUTTON_SPACE
                set Globals_NextButtonY = Globals_SlotY[1] + (64. * Globals_UI_SIZE)
            elseif Globals_UI_PAGE_BUTTON_POSITION == 1 then
                set Globals_PrevButtonX = Globals_SlotX[1] - (64. * Globals_UI_SIZE)
                set Globals_PrevButtonY = Globals_SlotY[1] - ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_HEIGHT - 1))/2
                set Globals_NextButtonX = Globals_SlotX[Globals_ITEM_SLOT_WIDTH] + (64. * Globals_UI_SIZE)
                set Globals_NextButtonY = Globals_SlotY[1] - ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_HEIGHT - 1))/2
            else
                set Globals_PrevButtonX = (Globals_SlotX[1] + ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_WIDTH - 1))/2) - Globals_UI_PAGE_BUTTON_SPACE
                set Globals_PrevButtonY = Globals_SlotY[Globals_PAGE_SLOT] - (64. * Globals_UI_SIZE)
                set Globals_NextButtonX = (Globals_SlotX[1] + ((64. * Globals_UI_SIZE) * (Globals_ITEM_SLOT_WIDTH - 1))/2) + Globals_UI_PAGE_BUTTON_SPACE
                set Globals_NextButtonY = Globals_SlotY[Globals_PAGE_SLOT] - (64. * Globals_UI_SIZE)
            endif
            set des = CreateDestructableZ(Globals_UI_NEXT_PAGE, Globals_NextButtonX, Globals_NextButtonY, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
            if GetLocalPlayer() != p then
                call ShowDestructable(des, false)
            endif
            set des = CreateDestructableZ(Globals_UI_PREV_PAGE, Globals_PrevButtonX, Globals_PrevButtonY, Globals_LOCATION_Z, 270., Globals_UI_SIZE, 0)
            if GetLocalPlayer() != p then
                call ShowDestructable(des, false)
            endif
            call ForForce(bj_FORCE_PLAYER[pn], function initMacro)
            set des = null
        endif
        
    endfunction
    
    public function addTracks takes nothing returns nothing
    
        local Track track
        
        if (Globals_TrackX <= Globals_InventoryAreaMaxX + Globals_UI_SIZE * 64. and Globals_TrackX >= Globals_LOCATION_X - Globals_UI_SIZE * 64. and Globals_TrackY <= Globals_InventoryAreaMaxY + Globals_UI_SIZE * 64. and Globals_TrackY >= Globals_LOCATION_Y - Globals_UI_SIZE * 64.) /*
        */ or (Globals_TrackX <= Globals_MacroArea[0] + Globals_UI_SIZE * 64. and Globals_TrackX >= Globals_MacroArea[2] - Globals_UI_SIZE * 64. and Globals_TrackY <= Globals_MacroArea[1] + Globals_UI_SIZE * 64. and Globals_TrackY >= Globals_MacroArea[3] - Globals_UI_SIZE * 64.) then
            set track = CreateTrack(Globals_TRACKABLE_PATH_1, Globals_TrackX, Globals_TrackY, Globals_LOCATION_Z-4., 3.14)
        else
            set track = CreateTrack(Globals_TRACKABLE_PATH_2, Globals_TrackX, Globals_TrackY, Globals_LOCATION_Z-4., 3.14)
        endif
        call RegisterClickEvent(track, function onClick)
        call RegisterHoverEvent(track, function onHover)
        
    endfunction
    
    public function initTrackable takes nothing returns nothing
    
        set Globals_TrackX = Globals_LOCATION_X - Globals_TRACKABLE_EXPAND_AREA
        loop
            exitwhen Globals_TrackX > Globals_InventoryAreaMaxX + Globals_TRACKABLE_EXPAND_AREA + Globals_TRACKABLE_SPACE
            set Globals_TrackY = Globals_LOCATION_Y - Globals_TRACKABLE_EXPAND_AREA
            loop
                exitwhen Globals_TrackY > Globals_InventoryAreaMaxY + Globals_TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set Globals_TrackY = Globals_TrackY + Globals_TRACKABLE_SPACE
            endloop
            set Globals_TrackX = Globals_TrackX + Globals_TRACKABLE_SPACE
        endloop
        
    endfunction

    private function ini takes nothing returns nothing
        call ForForce(bj_FORCE_ALL_PLAYERS, function initInterface)
        call initTrackable()
    endfunction
    
endlibrary

And the best of all: less use of hashtable!
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Hello guys, here is the newest update.

In the test map you are
Able to:
- Move & swap items by dragging their icons (left click)
- Change between pages
- To move item to another page, simply drag the icon to the page button and wait for a sec before it opens the other page

Unable to
- Drop items
- Close inventory, I'm too lazy to draw X button :V But sure I will add this soon

I'm not sure the current page handling is MUI MPI already since somehow I can't test this in multiplayer because I'm applying sharpcraft features in this system. And yes you should use sharpcraft to test and these plugin packs: link - linked to TH's plugin packs.

I don't give a shit about the inventory name, initial RI means RetardedInventory, but again, I don't give shit.

Please, just report any bugs you found. Thanks for your attention.

This demonstrates the most things it can actually do in the current state
235220-albums7646-picture89944.gif
 

Attachments

  • MagicSoul-FSI2.w3x
    277.6 KB · Views: 63
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
That drag&drop functionality is pretty cool. Did you make use of my interpolation suggestion to make it more smooth? Did you check it out in multiplayer if its as smooth as it is in singleplayer there?

It's sharpcraft actually. And I can't test it in multiplayer because of that sharpcraft. I hope MindWorX will update it further so it works for multiplayer. And currently, I think the drag feature is not MPI already since TH's new native does not support GetTriggerPlayer(), at least not yet.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Update:
- Macro feature is done. Although the items haven't been displayed at hero's inventory yet.

Code:
Don't ever doubt about it's efficiency! I promise you the fastest inventory system ever.
JASS:
/*
    Contains all globals used by core triggers
*/

library RIGlobals uses RIContainer, Table
    
    globals
        // Miscs
        public constant integer BUTTON_ID               = 'u000'
        public constant integer DUMMY_ID                = 'u001'
        public constant integer SPELL_ID                = 'SIA0'
        public constant real    LOCATION_X              = -5600.0
        public constant real    LOCATION_Y              = -6200.0
        public constant real    LOCATION_Z              = 10.0
        public constant real    BUTTON_NORMALZ          = 0.0
        public constant real    BUTTON_DRAG_Z           = 1.0
        public constant real    BUTTON_DRAG_SIZE        = 1.15
        public constant real    TOOLTIP_SIZE            = 0.5
        public constant real    INTERVAL                = 0.03125
        public constant real    CAMERA_DISTANCE         = 1800.0
        public constant real    TRACKABLE_EXPAND_AREA   = 768.0
        public constant real    DROP_EXPAND_AREA        = 96.0
        public constant real    DRAG_CHANGE_PAGE_DELAY  = 1.5
        public constant boolean AUTO_STACK              = true
        public constant boolean DO_PRELOAD              = true//false
        
        // Destructables
        public constant integer UI_BORDER_LEFT          = 'SIS5'
        public constant integer UI_BORDER_RIGHT         = 'SIS6'
        public constant integer UI_BORDER_UP            = 'SIS7'
        public constant integer UI_BORDER_DOWN          = 'SIS8'
        public constant integer UI_CORNER_UPPER_RIGHT   = 'SIS1'
        public constant integer UI_CORNER_UPPER_LEFT    = 'SIS2'
        public constant integer UI_CORNER_BOTTOM_RIGHT  = 'SIS3'
        public constant integer UI_CORNER_BOTTOM_LEFT   = 'SIS4'
        public constant integer UI_SLOT_EMPTY           = 'SIS9'
        public constant integer UI_NEXT_PAGE            = 'SISB'
        public constant integer UI_PREV_PAGE            = 'SISA'
        public constant integer UI_THEME_IN             = 'B007'
        public constant integer UI_THEME_OUT            = 'B008'
        public constant integer UI_ALPHA                = 'B00A'
        public constant integer UI_PAGE_BUTTON_POSITION = 2
        public constant real    UI_PAGE_BUTTON_SPACE    = 128.0
        public constant real    UI_SIZE                 = 1.0
        
        // Interfaces
        public constant integer ITEM_SLOT_WIDTH         = 5
        public constant integer ITEM_SLOT_HEIGHT        = 4
        public constant integer ITEM_SLOT_PAGES         = 3
        public constant integer TOOLTIP_MAX_LENGTH      = 32
        
        // Item types
        
    endglobals
    
    private module EquipSlots
        // Left hand
        set RIGlobals_EquipSlotIcon[0] = 'SIS9'
        // Right hand
        set RIGlobals_EquipSlotIcon[1] = 'SIS9'
        // Helmet
        set RIGlobals_EquipSlotIcon[2] = 'SIS9'
        // Armor
        set RIGlobals_EquipSlotIcon[3] = 'SIS9'
        // Gloves
        set RIGlobals_EquipSlotIcon[4] = 'SIS9'
        // Legs
        set RIGlobals_EquipSlotIcon[5] = 'SIS9'
        // Boots
        set RIGlobals_EquipSlotIcon[6] = 'SIS9'
        // Back
        set RIGlobals_EquipSlotIcon[7] = 'SIS9'
        // Accessory1
        set RIGlobals_EquipSlotIcon[8] = 'SIS9'
        // Accessory2
        set RIGlobals_EquipSlotIcon[9] = 'SIS9'
    endmodule
    
    private keyword SlotData
    
    globals
        public integer ItemTotal = 0
        public boolean array IsOpening
        public integer array EquipSlotIcon
        public integer array ClickIndex
        public integer array ClickType
        public unit array ClickIcon
        public real array ClickDelay
        public real array ClickX
        public real array ClickY
        public real InventoryAreaMaxX
        public real InventoryAreaMaxY
        public real TrackX
        public real TrackY
        public real WorldMaxX
        public real WorldMaxY
        public real array SlotX
        public real array SlotY
        public real array MacroArea
        public real array MacroX
        public real array MacroY
        public real CameraX
        public real CameraY
        public real PrevButtonX
        public real PrevButtonY
        public real NextButtonX
        public real NextButtonY
        public rect TrackArea
        public timer array CameraTimer
        public trigger ItemSwap   = CreateTrigger()
        public unit CameraUnit
        public unit array InventoryUnit
        public integer array UnitPage
        public integer array UnitPageMax
        public integer array EmptySlot
        public boolean array EquipEnabled
        public boolean array MacroEnabled
        public RIContainer_SlotData  array Slot
        public RIContainer_MacroData array Macro
        public RIContainer_EquipData array Equip
        public RIContainer_ItemData  array Item
        public Table ItemIndex
        public constant integer PAGE_SLOT  = ITEM_SLOT_WIDTH * ITEM_SLOT_HEIGHT
        public constant integer TOTAL_SLOT = PAGE_SLOT * ITEM_SLOT_PAGES
        public constant player  PASSIVE    = Player(PLAYER_NEUTRAL_PASSIVE)
    endglobals
    
    private struct Conf
        static method onInit takes nothing returns nothing
            implement EquipSlots
            set ItemIndex = Table.create()
        endmethod
    endstruct
    
endlibrary

JASS:
library RIContainer

    public struct ItemData
        integer id
        integer icon
        integer charge
        integer type
        real    cooldown
    endstruct

    public struct SlotData
        integer array index[RIGlobals_TOTAL_SLOT]
        integer array charge[RIGlobals_TOTAL_SLOT]
        integer array type[RIGlobals_TOTAL_SLOT]
        boolean array state[RIGlobals_TOTAL_SLOT]
        unit    array icon[RIGlobals_TOTAL_SLOT]
        real    array cooldown[RIGlobals_TOTAL_SLOT]
    endstruct

    public struct MacroData
        integer array index[RIGlobals_TOTAL_SLOT]
        integer array charge[RIGlobals_TOTAL_SLOT]
        integer array type[RIGlobals_TOTAL_SLOT]
        boolean array state[RIGlobals_TOTAL_SLOT]
        unit    array icon[RIGlobals_TOTAL_SLOT]
        real    array cooldown[RIGlobals_TOTAL_SLOT]
    endstruct

    public struct EquipData
        integer array index[RIGlobals_TOTAL_SLOT]
        integer array charge[RIGlobals_TOTAL_SLOT]
        integer array type[RIGlobals_TOTAL_SLOT]
        boolean array state[RIGlobals_TOTAL_SLOT]
        unit    array icon[RIGlobals_TOTAL_SLOT]
        real    array cooldown[RIGlobals_TOTAL_SLOT]
    endstruct

endlibrary

JASS:
library RIRecycler initializer preload
    
    globals
        private integer Counter = -1
        private unit array Unit
    endglobals
    
    public function GetPreloadUnit takes player p returns unit
    
        local unit r
    
        // If preload unit is still available
        if Counter > 0 then
            set r = Unit[Counter]
            set Counter = Counter - 1
            if GetLocalPlayer() == p then
                call SetUnitScale(r, RIGlobals_UI_SIZE, 0, 0)
            endif
        else
            set r = CreateUnit(RIGlobals_PASSIVE, RIGlobals_BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270)
            static if not LIBRARY_AutoFly then
                if UnitAddAbility(r, 'Amrf') and UnitRemoveAbility(r, 'Amrf') then
                endif
            endif
            call UnitRemoveAbility(r, 'Amov')
            call UnitAddAbility(r, RIGlobals_SPELL_ID)
            if GetLocalPlayer() == p then
                call SetUnitScale(r, RIGlobals_UI_SIZE, 0, 0)
            else
                call SetUnitScale(r, 0, 0, 0)
            endif
        endif
        
        return r
    endfunction
    
    public function ReleasePreloadUnit takes unit u returns nothing
    
        set Counter = Counter + 1
        set Unit[Counter] = Unit[0]
        set Unit[0] = u
        call SetUnitX(u, WorldBounds.maxX)
        call SetUnitY(u, WorldBounds.maxY)
        call SetUnitScale(u, 0, 0, 0)
        call SetUnitFlyHeight(u, RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ, 0)
        
    endfunction
    
    private function preload takes nothing returns nothing
    
        local integer i = 0
        
        static if RIGlobals_DO_PRELOAD then
            loop
                exitwhen i > RIGlobals_TOTAL_SLOT
                set Counter = Counter + 1
                set Unit[Counter] = CreateUnit(RIGlobals_PASSIVE, RIGlobals_BUTTON_ID, WorldBounds.maxX, WorldBounds.maxY, 270)
                static if not LIBRARY_AutoFly then
                    if UnitAddAbility(Unit[Counter], 'Amrf') and UnitRemoveAbility(Unit[Counter], 'Amrf') then
                    endif
                endif
                call UnitRemoveAbility(Unit[Counter], 'Amov')
                // Add texture swithcer
                call UnitAddAbility(Unit[Counter], RIGlobals_SPELL_ID)
                call SetUnitFlyHeight(Unit[Counter], RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ, 0)
                // Hide it
                call SetUnitScale(Unit[Counter], 0, 0, 0)
                set i = i + 1
            endloop
        endif
        
    endfunction
    
endlibrary

JASS:
library RIInterface initializer Init uses RIRecycler
    
    private function initMacro takes nothing returns nothing
    
        local integer dex = 0
        local integer i = 0
        local integer j
        local real x = RIGlobals_InventoryAreaMaxX + 64 * RIGlobals_UI_SIZE * 3
        local real y = RIGlobals_InventoryAreaMaxY - 64 * RIGlobals_UI_SIZE
        
        loop
            exitwhen i > 2
            set j = 0
            loop
                exitwhen j > 1
                if i == 0 then
                    if j == 0 then
                        // Min x
                        set RIGlobals_MacroArea[2] = x + 64 * RIGlobals_UI_SIZE * j
                        call CreateDestructableZ(RIGlobals_UI_CORNER_UPPER_LEFT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    else
                        // Max y
                        set RIGlobals_MacroArea[1] = y - 64 * RIGlobals_UI_SIZE * i
                        call CreateDestructableZ(RIGlobals_UI_CORNER_UPPER_RIGHT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                elseif i == 2 then
                    if j == 0 then
                        // Min y
                        set RIGlobals_MacroArea[3] = y - 64 * RIGlobals_UI_SIZE * i
                        call CreateDestructableZ(RIGlobals_UI_CORNER_BOTTOM_LEFT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    else
                        // Max x
                        set RIGlobals_MacroArea[0] = x + 64 * RIGlobals_UI_SIZE * j
                        call CreateDestructableZ(RIGlobals_UI_CORNER_BOTTOM_RIGHT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                else
                    if j == 0 then
                        call CreateDestructableZ(RIGlobals_UI_BORDER_LEFT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    else
                        call CreateDestructableZ(RIGlobals_UI_BORDER_RIGHT, x + 64 * RIGlobals_UI_SIZE * j, y - 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                endif
                set RIGlobals_MacroX[dex] = x + 64 * RIGlobals_UI_SIZE * j
                set RIGlobals_MacroY[dex] = y - 64 * RIGlobals_UI_SIZE * i
                call CreateDestructableZ(RIGlobals_UI_SLOT_EMPTY, RIGlobals_MacroX[dex], RIGlobals_MacroY[dex], RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                set dex = dex + 1
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        set RIGlobals_CameraX = RIGlobals_LOCATION_X + (RIGlobals_InventoryAreaMaxX - RIGlobals_LOCATION_X + (RIGlobals_MacroArea[0] - RIGlobals_MacroArea[2]))/2
        set RIGlobals_CameraY = RIGlobals_LOCATION_Y + (RIGlobals_InventoryAreaMaxY - RIGlobals_LOCATION_Y)/2
        
    endfunction
    
    private function initInterface takes nothing returns nothing
    
        local integer i = 0
        local integer j
        local integer ext_i = 8 + RIGlobals_ITEM_SLOT_HEIGHT
        local integer ext_j = 1 + RIGlobals_ITEM_SLOT_WIDTH
        local integer dex
        
        loop
            exitwhen i > ext_i
            set j = 0
            loop
                exitwhen j > ext_j
                if i == 0 then
                    if j == 0 then
                        call CreateDestructableZ(RIGlobals_UI_CORNER_BOTTOM_LEFT, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    elseif j == ext_j then
                        call CreateDestructableZ(RIGlobals_UI_CORNER_BOTTOM_RIGHT, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    else
                        call CreateDestructableZ(RIGlobals_UI_BORDER_DOWN, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                elseif i == ext_i then
                    if j == 0 then
                        call CreateDestructableZ(RIGlobals_UI_CORNER_UPPER_LEFT, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    elseif j == ext_j then
                        // Get inventory area max x and y when loop will be ended
                        set RIGlobals_InventoryAreaMaxX = RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j
                        set RIGlobals_InventoryAreaMaxY = RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i
                        call CreateDestructableZ(RIGlobals_UI_CORNER_UPPER_RIGHT, RIGlobals_InventoryAreaMaxX, RIGlobals_InventoryAreaMaxY, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    else
                        call CreateDestructableZ(RIGlobals_UI_BORDER_UP, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                else
                    if j == 0 then
                        call CreateDestructableZ(RIGlobals_UI_BORDER_LEFT, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    elseif j == ext_j then
                        call CreateDestructableZ(RIGlobals_UI_BORDER_RIGHT, RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j, RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                    endif
                    if i < 1 + RIGlobals_ITEM_SLOT_HEIGHT then
                        if j > 0 and j <= RIGlobals_ITEM_SLOT_WIDTH then
                            set dex = RIGlobals_ITEM_SLOT_HEIGHT * RIGlobals_ITEM_SLOT_WIDTH - RIGlobals_ITEM_SLOT_WIDTH * i + j
                            set RIGlobals_SlotX[dex] = RIGlobals_LOCATION_X + 64 * RIGlobals_UI_SIZE * j
                            set RIGlobals_SlotY[dex] = RIGlobals_LOCATION_Y + 64 * RIGlobals_UI_SIZE * i
                            call CreateDestructableZ(RIGlobals_UI_SLOT_EMPTY, RIGlobals_SlotX[dex], RIGlobals_SlotY[dex], RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
                        endif
                    endif
                endif
                set j = j + 1
            endloop
            set i = i + 1
        endloop
        
        if RIGlobals_UI_PAGE_BUTTON_POSITION == 0 then
            set RIGlobals_PrevButtonX = (RIGlobals_SlotX[1] + ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_WIDTH - 1))/2) - RIGlobals_UI_PAGE_BUTTON_SPACE/2
            set RIGlobals_PrevButtonY = RIGlobals_SlotY[1] + (64 * RIGlobals_UI_SIZE)
            set RIGlobals_NextButtonX = (RIGlobals_SlotX[1] + ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_WIDTH - 1))/2) + RIGlobals_UI_PAGE_BUTTON_SPACE/2
            set RIGlobals_NextButtonY = RIGlobals_SlotY[1] + (64 * RIGlobals_UI_SIZE)
        elseif RIGlobals_UI_PAGE_BUTTON_POSITION == 1 then
            set RIGlobals_PrevButtonX = RIGlobals_SlotX[1] - (64 * RIGlobals_UI_SIZE)
            set RIGlobals_PrevButtonY = RIGlobals_SlotY[1] - ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_HEIGHT - 1))/2
            set RIGlobals_NextButtonX = RIGlobals_SlotX[RIGlobals_ITEM_SLOT_WIDTH] + (64 * RIGlobals_UI_SIZE)
            set RIGlobals_NextButtonY = RIGlobals_SlotY[1] - ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_HEIGHT - 1))/2
        else
            set RIGlobals_PrevButtonX = (RIGlobals_SlotX[1] + ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_WIDTH - 1))/2) - RIGlobals_UI_PAGE_BUTTON_SPACE/2
            set RIGlobals_PrevButtonY = RIGlobals_SlotY[RIGlobals_PAGE_SLOT] - (64 * RIGlobals_UI_SIZE)
            set RIGlobals_NextButtonX = (RIGlobals_SlotX[1] + ((64 * RIGlobals_UI_SIZE) * (RIGlobals_ITEM_SLOT_WIDTH - 1))/2) + RIGlobals_UI_PAGE_BUTTON_SPACE/2
            set RIGlobals_NextButtonY = RIGlobals_SlotY[RIGlobals_PAGE_SLOT] - (64 * RIGlobals_UI_SIZE)
        endif
        call CreateDestructableZ(RIGlobals_UI_NEXT_PAGE, RIGlobals_NextButtonX, RIGlobals_NextButtonY, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
        call CreateDestructableZ(RIGlobals_UI_PREV_PAGE, RIGlobals_PrevButtonX, RIGlobals_PrevButtonY, RIGlobals_LOCATION_Z, 270, RIGlobals_UI_SIZE, 0)
        call ForForce(bj_FORCE_PLAYER[0], function initMacro)
    
    endfunction
    
    private function addTracks takes nothing returns nothing
        
        if (RIGlobals_TrackX <= RIGlobals_InventoryAreaMaxX + RIGlobals_UI_SIZE * 64 and RIGlobals_TrackX >= RIGlobals_LOCATION_X - RIGlobals_UI_SIZE * 64 and RIGlobals_TrackY <= RIGlobals_InventoryAreaMaxY + RIGlobals_UI_SIZE * 64 and RIGlobals_TrackY >= RIGlobals_LOCATION_Y - RIGlobals_UI_SIZE * 64) /*
        */ or (RIGlobals_TrackX <= RIGlobals_MacroArea[0] + RIGlobals_UI_SIZE * 64 and RIGlobals_TrackX >= RIGlobals_MacroArea[2] - RIGlobals_UI_SIZE * 64 and RIGlobals_TrackY <= RIGlobals_MacroArea[1] + RIGlobals_UI_SIZE * 64 and RIGlobals_TrackY >= RIGlobals_MacroArea[3] - RIGlobals_UI_SIZE * 64) then
            call CreateDestructableZ(RIGlobals_UI_THEME_IN, RIGlobals_TrackX, RIGlobals_TrackY, RIGlobals_LOCATION_Z-4, 270, RIGlobals_UI_SIZE, 0)
        else
            call CreateDestructableZ(RIGlobals_UI_THEME_OUT, RIGlobals_TrackX, RIGlobals_TrackY, RIGlobals_LOCATION_Z-4, 270, RIGlobals_UI_SIZE, 0)
        endif
        
    endfunction
    
    private function initTrackable takes nothing returns nothing
    
        set RIGlobals_TrackX = RIGlobals_LOCATION_X - RIGlobals_TRACKABLE_EXPAND_AREA
        loop
            exitwhen RIGlobals_TrackX > RIGlobals_InventoryAreaMaxX + RIGlobals_TRACKABLE_EXPAND_AREA
            set RIGlobals_TrackY = RIGlobals_LOCATION_Y - RIGlobals_TRACKABLE_EXPAND_AREA
            loop
                exitwhen RIGlobals_TrackY > RIGlobals_InventoryAreaMaxY + RIGlobals_TRACKABLE_EXPAND_AREA
                call ForForce(bj_FORCE_PLAYER[0], function addTracks)
                set RIGlobals_TrackY = RIGlobals_TrackY + 64*RIGlobals_UI_SIZE
            endloop
            set RIGlobals_TrackX = RIGlobals_TrackX + 64*RIGlobals_UI_SIZE
        endloop
        
    endfunction

    private function Init takes nothing returns nothing
        
        local integer i = 0
        
        call initInterface()
        call initTrackable()
        
        set RIGlobals_CameraUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), RIGlobals_DUMMY_ID, RIGlobals_CameraX, RIGlobals_CameraY, 270)
    endfunction
    
endlibrary

JASS:
library RILibrary
    
    function RegisterItemToLibrary takes integer itemID, integer ItemIcon, integer ItemCategory, integer itemMaxCharge, real ItemCooldown, boolean itemUseable returns nothing
    
        set RIGlobals_ItemTotal = RIGlobals_ItemTotal + 1
        set RIGlobals_ItemIndex.integer[itemID]          = RIGlobals_ItemTotal
        set RIGlobals_Item[RIGlobals_ItemTotal]          = RIContainer_ItemData.create()
        set RIGlobals_Item[RIGlobals_ItemTotal].id       = itemID
        set RIGlobals_Item[RIGlobals_ItemTotal].icon     = ItemIcon
        set RIGlobals_Item[RIGlobals_ItemTotal].type     = ItemCategory
        set RIGlobals_Item[RIGlobals_ItemTotal].charge   = itemMaxCharge
        set RIGlobals_Item[RIGlobals_ItemTotal].cooldown = ItemCooldown
        
    endfunction
    
    function RegisterInventoryUnit takes unit whichUnit, boolean enableEquip, boolean enableMacro returns nothing
    
        local integer dex = GetUnitUserData(whichUnit)
    
        set RIGlobals_EmptySlot[dex]    = 1
        set RIGlobals_UnitPage[dex]     = 1
        set RIGlobals_UnitPageMax[dex]  = RIGlobals_ITEM_SLOT_PAGES
        set RIGlobals_Slot[dex]         = RIContainer_SlotData.create()
        set RIGlobals_MacroEnabled[dex] = enableMacro
        set RIGlobals_EquipEnabled[dex] = enableEquip
        if enableMacro then
            set RIGlobals_Macro[dex]    = RIContainer_MacroData.create()
        endif
        if enableEquip then
            set RIGlobals_Equip[dex]    = RIContainer_EquipData.create()
        endif
        
    endfunction
    
    function RemoveInventoryUnit takes unit whichUnit returns nothing
    
        local integer dex = GetUnitUserData(whichUnit)
        
        set RIGlobals_EmptySlot[dex] = 0
        call RIGlobals_Slot[dex].destroy()
        if RIGlobals_MacroEnabled[dex] then
            call RIGlobals_Macro[dex].destroy()
        endif
        if RIGlobals_EquipEnabled[dex] then
            call RIGlobals_Equip[dex].destroy()
        endif
        
    endfunction
    
endlibrary

JASS:
library RIControlPoint uses TimerUtils
    
    private function modInt takes integer a, integer b returns integer
        return a - (a/b) * b
    endfunction

    public function ChangeTexture takes unit whichUnit, integer whichTexture returns nothing
        call IssueTargetOrderById(whichUnit, 852511, CreateDestructable(whichTexture, 0, 0, 0, 0, 1))
    endfunction
    
    public function IsInInventoryArea takes real x, real y returns boolean
        return x < RIGlobals_InventoryAreaMaxX+RIGlobals_DROP_EXPAND_AREA and y < RIGlobals_InventoryAreaMaxY+RIGlobals_DROP_EXPAND_AREA/*
        */ and x > RIGlobals_LOCATION_X-RIGlobals_DROP_EXPAND_AREA and y > RIGlobals_LOCATION_Y-RIGlobals_DROP_EXPAND_AREA
    endfunction
    
    public function IsInMacroArea takes real x, real y returns boolean
        return x < RIGlobals_MacroArea[0]+RIGlobals_DROP_EXPAND_AREA and y < RIGlobals_MacroArea[1]+RIGlobals_DROP_EXPAND_AREA/*
        */ and x > RIGlobals_MacroArea[2]-RIGlobals_DROP_EXPAND_AREA and y > RIGlobals_MacroArea[3]-RIGlobals_DROP_EXPAND_AREA
    endfunction
    
    private function getStackingSlot takes integer dex, integer idex, integer id, integer slotDex returns integer
    
        local integer i = 1
        
        loop
            exitwhen i > RIGlobals_TOTAL_SLOT
            // If slot's id is the same as desired id and charge count is not full yet
            if RIGlobals_Item[idex].id == id and RIGlobals_Slot[dex].charge[i] < RIGlobals_Item[idex].charge then
                return i
            endif
            set i = i + 1
        endloop
        
        return slotDex
    endfunction
    
    private function getEmptySlot takes integer dex returns integer
    
        local integer i = 1
        
        loop
            exitwhen i > RIGlobals_TOTAL_SLOT
            // If slot is empty then return the slot index
            if RIGlobals_Slot[dex].index[i] == 0 then
                return i
            endif
            set i = i + 1
        endloop
        
        return i
    endfunction
    
    function RefreshInventory takes unit whichUnit returns nothing
    
        local player  p   = GetOwningPlayer(whichUnit)
        local integer pn  = GetPlayerId(p)
        local integer i   = 1
        local integer dex = GetUnitUserData(whichUnit)
        local integer sdex
        debug local destructable d
        
        loop
            exitwhen i > RIGlobals_PAGE_SLOT
            set sdex = RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i
            if RIGlobals_Slot[dex].index[sdex] == 0 then
                if RIGlobals_Slot[dex].icon[i] != null then
                    debug call BJDebugMsg("Icon at slot-" + I2S(i) + " has been released")
                    call RIRecycler_ReleasePreloadUnit(RIGlobals_Slot[dex].icon[i])
                    set RIGlobals_Slot[dex].icon[i] = null
                endif
            else
                if RIGlobals_Slot[dex].icon[i] == null then
                    set RIGlobals_Slot[dex].icon[i] = RIRecycler_GetPreloadUnit(p)
                    debug call BJDebugMsg("Slot-" + I2S(i) + " is getting preload unit")
                endif
                if RIGlobals_Slot[dex].state[sdex] then
                    call ChangeTexture(RIGlobals_Slot[dex].icon[i], RIGlobals_UI_ALPHA)
                else
                    call ChangeTexture(RIGlobals_Slot[dex].icon[i], RIGlobals_Item[RIGlobals_Slot[dex].index[sdex]].icon)
                endif
                debug set d = CreateDestructable(RIGlobals_Item[RIGlobals_Slot[dex].index[sdex]].icon, 0, 0, 0, 1, 1)
                debug call BJDebugMsg(I2S(sdex) + " switch texture to " + GetDestructableName(d))
                debug call RemoveDestructable(d)
                debug set d = null
                call SetUnitX(RIGlobals_Slot[dex].icon[i], RIGlobals_SlotX[i])
                call SetUnitY(RIGlobals_Slot[dex].icon[i], RIGlobals_SlotY[i])
                call SetUnitFlyHeight(RIGlobals_Slot[dex].icon[i], RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ, 0)
                call SetUnitScale(RIGlobals_Slot[dex].icon[i], RIGlobals_UI_SIZE, 1, 1)
            endif
            set i = i + 1
        endloop
        set i = 0
        loop
            exitwhen i > 5
            if RIGlobals_Macro[dex].index[i] == 0 then
                if RIGlobals_Macro[dex].icon[i] != null then
                    debug call BJDebugMsg("Icon at macro slot-" + I2S(i) + " has been released")
                    call RIRecycler_ReleasePreloadUnit(RIGlobals_Macro[dex].icon[i])
                    set RIGlobals_Macro[dex].icon[i] = null
                endif
            else
                if RIGlobals_Macro[dex].icon[i] == null then
                    set RIGlobals_Macro[dex].icon[i] = RIRecycler_GetPreloadUnit(p)
                    debug call BJDebugMsg("Macro Slot-" + I2S(i) + " is getting preload unit")
                endif
                if RIGlobals_Macro[dex].state[i] then
                    call ChangeTexture(RIGlobals_Macro[dex].icon[i], RIGlobals_UI_ALPHA)
                else
                    call ChangeTexture(RIGlobals_Macro[dex].icon[i], RIGlobals_Item[RIGlobals_Macro[dex].index[i]].icon)
                endif
                debug set d = CreateDestructable(RIGlobals_Item[RIGlobals_Macro[dex].index[i]].icon, 0, 0, 0, 1, 1)
                debug call BJDebugMsg(I2S(i) + " switch texture to " + GetDestructableName(d))
                debug call RemoveDestructable(d)
                debug set d = null
                call SetUnitX(RIGlobals_Macro[dex].icon[i], RIGlobals_MacroX[i])
                call SetUnitY(RIGlobals_Macro[dex].icon[i], RIGlobals_MacroY[i])
                call SetUnitFlyHeight(RIGlobals_Macro[dex].icon[i], RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ, 0)
                call SetUnitScale(RIGlobals_Macro[dex].icon[i], RIGlobals_UI_SIZE, 1, 1)
            endif
            set i = i + 1
        endloop
        
    endfunction
    
    function AddInventoryItem takes unit whichUnit, integer itemID, integer itemAmount returns boolean
    
        local player p     = GetOwningPlayer(whichUnit)
        local integer dex  = GetUnitUserData(whichUnit)
        local integer idex = RIGlobals_ItemIndex.integer[itemID]
        local integer pn   = GetPlayerId(p)
        local integer am
        local integer slot
        
        // If unit has inventory
        if RIGlobals_EmptySlot[dex] > 0 then
            // Repeat add item action until reach the desired item amount
            loop
                // If inventory is not full
                if RIGlobals_EmptySlot[dex] <= RIGlobals_TOTAL_SLOT then
                    // Check for other empty slot to fill
                    set RIGlobals_EmptySlot[dex] = getEmptySlot(dex)
                    // Set slot index to item's index at library
                    set RIGlobals_Slot[dex].index[RIGlobals_EmptySlot[dex]] = idex
                    // Set slot category to item's category at library
                    set RIGlobals_Slot[dex].type[RIGlobals_EmptySlot[dex]] = RIGlobals_Item[idex].type
                    // If player is currently opening the page then create icon for slot
                    if RIGlobals_UnitPage[dex] == RIGlobals_EmptySlot[dex]/RIGlobals_PAGE_SLOT + 1 or RIGlobals_EmptySlot[dex] == RIGlobals_PAGE_SLOT * RIGlobals_UnitPage[dex] then
                        set slot = RIGlobals_EmptySlot[dex] - (RIGlobals_EmptySlot[dex]/RIGlobals_PAGE_SLOT) * RIGlobals_PAGE_SLOT
                        if slot == 0 then
                            set slot = RIGlobals_PAGE_SLOT
                        endif
                    endif
                    
                    // If item is consumeable or misc items
                    if RIGlobals_Item[idex].type < 2 then
                        static if RIGlobals_AUTO_STACK then
                            // Get the slot that can be stacked
                            set RIGlobals_EmptySlot[dex] = getStackingSlot(dex, idex, itemID, RIGlobals_EmptySlot[dex])
                        endif
                        // If charge amount is higher than max charge then
                        if RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]] + itemAmount > RIGlobals_Item[idex].charge then
                            // Calculate remaining amount = amount - (max charge - slot charge)
                            set itemAmount = itemAmount - (RIGlobals_Item[idex].charge - RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]])
                            // Set slot charge to max charge
                            set RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]] = RIGlobals_Item[idex].charge
                        // If not just sum it
                        else
                            set RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]] = RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]] + itemAmount
                            set itemAmount = 0
                        endif
                    else
                        set RIGlobals_Slot[dex].charge[RIGlobals_EmptySlot[dex]] = 0
                    endif
                // If inventory is full
                else
                    // Create the item at unit's position with remaining itemAmount as it's charge
                    call SetItemCharges(CreateItem(itemID, GetUnitX(whichUnit), GetUnitY(whichUnit)), itemAmount)
                    set itemAmount = 0
                endif
                // Exit when desired item amount is reached
                exitwhen itemAmount == 0
            endloop
            // Return true if item is sucessfully added
            return true
        endif
        
        return false
    endfunction
    
    function RemoveInventoryItem takes unit whichUnit, integer slotIndex returns nothing
        
        local integer dex = GetUnitUserData(whichUnit)
        
        set RIGlobals_Slot[dex].index[slotIndex]    = 0
        set RIGlobals_Slot[dex].charge[slotIndex]   = 0
        set RIGlobals_Slot[dex].type[slotIndex]     = 0
        set RIGlobals_Slot[dex].cooldown[slotIndex] = 0
        
    endfunction
    
    function MoveItemToSlot takes unit whichUnit, integer source, integer target returns nothing
        
        local integer dex  = GetUnitUserData(whichUnit)
        local integer tdex = RIGlobals_Slot[dex].index[target]
        local integer tcrg = RIGlobals_Slot[dex].charge[target]
        local integer ttyp = RIGlobals_Slot[dex].type[target]
        local real    tcdn = RIGlobals_Slot[dex].cooldown[target]
        local boolean tsta = RIGlobals_Slot[dex].state[target]
    
        debug call BJDebugMsg("Move from " + I2S(source) + " to " + I2S(target))
        debug call BJDebugMsg("Index at " + I2S(source) + " was " + I2S(RIGlobals_Slot[dex].index[source]) + ", at " + I2S(target) + " was " + I2S(RIGlobals_Slot[dex].index[target]))
        set RIGlobals_Slot[dex].index[target]    = RIGlobals_Slot[dex].index[source]
        set RIGlobals_Slot[dex].charge[target]   = RIGlobals_Slot[dex].charge[source]
        set RIGlobals_Slot[dex].type[target]     = RIGlobals_Slot[dex].type[source]
        set RIGlobals_Slot[dex].cooldown[target] = RIGlobals_Slot[dex].cooldown[source]
        set RIGlobals_Slot[dex].state[target]    = RIGlobals_Slot[dex].state[source]
        
        set RIGlobals_Slot[dex].index[source]    = tdex
        set RIGlobals_Slot[dex].charge[source]   = tcrg
        set RIGlobals_Slot[dex].type[source]     = ttyp
        set RIGlobals_Slot[dex].cooldown[source] = tcdn
        set RIGlobals_Slot[dex].state[source]    = tsta
        debug call BJDebugMsg("Index at " + I2S(source) + " now " + I2S(RIGlobals_Slot[dex].index[source]) + ", at " + I2S(target) + " now " + I2S(RIGlobals_Slot[dex].index[target]))
        
    endfunction
    
    function MoveMacroToMacro takes unit whichUnit, integer source, integer target returns nothing
        
        local integer dex  = GetUnitUserData(whichUnit)
        local integer tdex = RIGlobals_Macro[dex].index[target]
        local integer tcrg = RIGlobals_Macro[dex].charge[target]
        local integer ttyp = RIGlobals_Macro[dex].type[target]
        local real    tcdn = RIGlobals_Macro[dex].cooldown[target]
        local boolean tsta = RIGlobals_Macro[dex].state[target]
    
        debug call BJDebugMsg("Macro move from " + I2S(source) + " to " + I2S(target))
        debug call BJDebugMsg("Index at " + I2S(source) + " was " + I2S(RIGlobals_Macro[dex].index[source]) + ", at " + I2S(target) + " was " + I2S(RIGlobals_Macro[dex].index[target]))
        set RIGlobals_Macro[dex].index[target]    = RIGlobals_Macro[dex].index[source]
        set RIGlobals_Macro[dex].charge[target]   = RIGlobals_Macro[dex].charge[source]
        set RIGlobals_Macro[dex].type[target]     = RIGlobals_Macro[dex].type[source]
        set RIGlobals_Macro[dex].cooldown[target] = RIGlobals_Macro[dex].cooldown[source]
        set RIGlobals_Macro[dex].state[target]    = RIGlobals_Macro[dex].state[source]
        
        set RIGlobals_Macro[dex].index[source]    = tdex
        set RIGlobals_Macro[dex].charge[source]   = tcrg
        set RIGlobals_Macro[dex].type[source]     = ttyp
        set RIGlobals_Macro[dex].cooldown[source] = tcdn
        set RIGlobals_Macro[dex].state[source]    = tsta
        debug call BJDebugMsg("Index at " + I2S(source) + " now " + I2S(RIGlobals_Macro[dex].index[source]) + ", at " + I2S(target) + " now " + I2S(RIGlobals_Macro[dex].index[target]))
        
    endfunction
    
    function MoveItemToMacro takes unit whichUnit, integer source, integer target returns nothing
    
        local integer i
        local integer dex = GetUnitUserData(whichUnit)
        local integer tdex
        local integer tcrg
        local integer ttyp
        local real    tcdn
        local boolean tsta
        
        if RIGlobals_Slot[dex].type[source] == 1 then
            // If target slot is automatic
            if target < 0 then
                set i = 0
                loop
                    exitwhen i > 5 or target > -1
                    if RIGlobals_Macro[dex].index[target] == 0 then
                        set target = i
                    endif
                    set i = i + 1
                endloop
            endif
            
            if target > -1 then
                set tdex = RIGlobals_Macro[dex].index[target]
                set tcrg = RIGlobals_Macro[dex].charge[target]
                set ttyp = RIGlobals_Macro[dex].type[target]
                set tcdn = RIGlobals_Macro[dex].cooldown[target]
                set tsta = RIGlobals_Macro[dex].state[target]
                
                set RIGlobals_Macro[dex].index[target]    = RIGlobals_Slot[dex].index[source]
                set RIGlobals_Macro[dex].charge[target]   = RIGlobals_Slot[dex].charge[source]
                set RIGlobals_Macro[dex].type[target]     = RIGlobals_Slot[dex].type[source]
                set RIGlobals_Macro[dex].state[target]    = RIGlobals_Slot[dex].state[source]
                set RIGlobals_Macro[dex].cooldown[target] = RIGlobals_Slot[dex].cooldown[source]
                
                set RIGlobals_Slot[dex].index[source]    = tdex
                set RIGlobals_Slot[dex].charge[source]   = tcrg
                set RIGlobals_Slot[dex].type[source]     = ttyp
                set RIGlobals_Slot[dex].cooldown[source] = tcdn
                set RIGlobals_Slot[dex].state[source]    = tsta
            debug else
                debug call BJDebugMsg("Failed. Macro is full")
            endif
        debug else
            debug call BJDebugMsg("Failed. Item is not a consumable")
        endif
    endfunction
    
    function MoveMacroToSlot takes unit whichUnit, integer source, integer target returns nothing
    
        local integer i
        local integer dex = GetUnitUserData(whichUnit)
        local integer tdex
        local integer tcrg
        local integer ttyp
        local real    tcdn
        local boolean tsta
        
        // If target slot is automatic
        if target < 0 then
            set target = getEmptySlot(dex)
        endif
        
        if target > -1 and target <= RIGlobals_TOTAL_SLOT then
            set tdex = RIGlobals_Slot[dex].index[target]
            set tcrg = RIGlobals_Slot[dex].charge[target]
            set ttyp = RIGlobals_Slot[dex].type[target]
            set tcdn = RIGlobals_Slot[dex].cooldown[target]
            set tsta = RIGlobals_Slot[dex].state[target]
            
            set RIGlobals_Slot[dex].index[target]    = RIGlobals_Macro[dex].index[source]
            set RIGlobals_Slot[dex].charge[target]   = RIGlobals_Macro[dex].charge[source]
            set RIGlobals_Slot[dex].type[target]     = RIGlobals_Macro[dex].type[source]
            set RIGlobals_Slot[dex].state[target]    = RIGlobals_Macro[dex].state[source]
            set RIGlobals_Slot[dex].cooldown[target] = RIGlobals_Macro[dex].cooldown[source]
            
            set RIGlobals_Macro[dex].index[source]    = tdex
            set RIGlobals_Macro[dex].charge[source]   = tcrg
            set RIGlobals_Macro[dex].type[source]     = ttyp
            set RIGlobals_Macro[dex].cooldown[source] = tcdn
            set RIGlobals_Macro[dex].state[source]    = tsta
        debug else
            debug call BJDebugMsg("Failed. Macro is full")
        endif
         
    endfunction
    
    private function cameraLoop takes nothing returns nothing
        
        if GetLocalPlayer() == Player(GetTimerData(GetExpiredTimer())) then
            call ClearSelection()
            call SetCameraTargetController(RIGlobals_CameraUnit, 0, 0, false)
            call SetCameraField(CAMERA_FIELD_ROLL, 0, 0)
            call SetCameraField(CAMERA_FIELD_ZOFFSET, 0, 0)
            call SetCameraField(CAMERA_FIELD_ROTATION, 90, 0)
            call SetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK, 270, 0)
            call SetCameraField(CAMERA_FIELD_TARGET_DISTANCE, RIGlobals_CAMERA_DISTANCE, 0)
        endif
        
    endfunction
    
    function OpenInventoryWindow takes unit whichUnit, boolean flag returns boolean
    
        local unit    u
        local player  p  = GetOwningPlayer(whichUnit)
        local integer pn = GetPlayerId(p)
    
        if RIGlobals_EmptySlot[GetUnitUserData(whichUnit)] > 0 then
            if RIGlobals_IsOpening[pn] != flag then
                set RIGlobals_IsOpening[pn] = flag
                if flag then
                    set RIGlobals_InventoryUnit[pn] = whichUnit
                    set RIGlobals_CameraTimer[pn] = NewTimerEx(pn)
                    call TimerStart(RIGlobals_CameraTimer[pn], RIGlobals_INTERVAL, true, function cameraLoop)
                else
                    set RIGlobals_InventoryUnit[pn] = null
                    call ReleaseTimer(RIGlobals_CameraTimer[pn])
                    set u = CreateUnit(RIGlobals_PASSIVE, 'ewsp', 0, 0, 0)
                    call SetCameraTargetController(u, 0, 0, false)
                    call RemoveUnit(u)
                    set u = null
                endif
                return true
            endif
        endif
        
        return false
    endfunction
    
endlibrary

JASS:
scope RIInteraction initializer Init

    globals
        private timer array DragTimer
    endglobals
    
    private function modInt takes integer a, integer b returns integer
        return a - (a/b) * b
    endfunction
    
    private function dragIcon takes nothing returns nothing
        
        local integer pn  = GetTimerData(GetExpiredTimer())
        local integer dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
        local real    s   = 32 * RIGlobals_UI_SIZE
        local real    x   = GetMouseTerrainX()
        local real    y   = GetMouseTerrainY()
        
        if x < RIGlobals_PrevButtonX + s and x > RIGlobals_PrevButtonX - s and y < RIGlobals_PrevButtonY + s and y > RIGlobals_PrevButtonY - s then
            if RIGlobals_ClickDelay[pn] <= RIGlobals_DRAG_CHANGE_PAGE_DELAY then
                set RIGlobals_ClickDelay[pn] = RIGlobals_ClickDelay[pn] + 0.0003
                if RIGlobals_ClickDelay[pn] > RIGlobals_DRAG_CHANGE_PAGE_DELAY then
                    set RIGlobals_UnitPage[dex] = RIGlobals_UnitPage[dex] - 1
                    if RIGlobals_UnitPage[dex] < 1 then
                        set RIGlobals_UnitPage[dex] = RIGlobals_UnitPageMax[dex]
                    endif
                    call RefreshInventory(RIGlobals_InventoryUnit[pn])
                endif
            endif
        elseif x < RIGlobals_NextButtonX + s and x > RIGlobals_NextButtonX - s and y < RIGlobals_NextButtonY + s and y > RIGlobals_NextButtonY - s then
            if RIGlobals_ClickDelay[pn] <= RIGlobals_DRAG_CHANGE_PAGE_DELAY then
                set RIGlobals_ClickDelay[pn] = RIGlobals_ClickDelay[pn] + 0.0003
                if RIGlobals_ClickDelay[pn] > RIGlobals_DRAG_CHANGE_PAGE_DELAY then
                    set RIGlobals_UnitPage[dex] = RIGlobals_UnitPage[dex] + 1
                    if RIGlobals_UnitPage[dex] > RIGlobals_UnitPageMax[dex] then
                        set RIGlobals_UnitPage[dex] = 1
                    endif
                    call RefreshInventory(RIGlobals_InventoryUnit[pn])
                endif
            endif
        else
            set RIGlobals_ClickDelay[pn] = 0
        endif
        call SetUnitX(RIGlobals_ClickIcon[pn], x + RIGlobals_ClickX[pn])
        call SetUnitY(RIGlobals_ClickIcon[pn], y + RIGlobals_ClickY[pn])
        
    endfunction
    
    private function mouseLeftClick takes nothing returns boolean
        
        local player p   = GetTriggerPlayer()
        local integer pn = GetPlayerId(p)
        local integer dex
        local integer i
        local real x
        local real y
        local real s = 32 * RIGlobals_UI_SIZE
        
        if RIGlobals_IsOpening[pn] then
            set x = GetMouseTerrainX()
            set y = GetMouseTerrainY()
            set RIGlobals_ClickIndex[pn] = -1
            if x < RIGlobals_PrevButtonX + s and x > RIGlobals_PrevButtonX - s and y < RIGlobals_PrevButtonY + s and y > RIGlobals_PrevButtonY - s then
                set dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
                set RIGlobals_UnitPage[dex] = RIGlobals_UnitPage[dex] - 1
                if RIGlobals_UnitPage[dex] < 1 then
                    set RIGlobals_UnitPage[dex] = RIGlobals_UnitPageMax[dex]
                endif
                call RefreshInventory(RIGlobals_InventoryUnit[pn])
                debug call BJDebugMsg("Current page: " + I2S(RIGlobals_UnitPage[dex]))
            elseif x < RIGlobals_NextButtonX + s and x > RIGlobals_NextButtonX - s and y < RIGlobals_NextButtonY + s and y > RIGlobals_NextButtonY - s then
                set dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
                set RIGlobals_UnitPage[dex] = RIGlobals_UnitPage[dex] + 1
                if RIGlobals_UnitPage[dex] > RIGlobals_UnitPageMax[dex] then
                    set RIGlobals_UnitPage[dex] = 1
                endif
                call RefreshInventory(RIGlobals_InventoryUnit[pn])
                debug call BJDebugMsg("Current page: " + I2S(RIGlobals_UnitPage[dex]))
            elseif RIControlPoint_IsInInventoryArea(x, y) then
                set i = 1
                loop
                    exitwhen i > RIGlobals_PAGE_SLOT
                    if x < RIGlobals_SlotX[i] + s and x > RIGlobals_SlotX[i] - s and y < RIGlobals_SlotY[i] + s and y > RIGlobals_SlotY[i] - s then
                        set dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
                        set RIGlobals_ClickIndex[pn] = RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i
                        set RIGlobals_ClickType[pn]  = 0
                        if RIGlobals_Slot[dex].index[RIGlobals_ClickIndex[pn]] != 0 then
                            set RIGlobals_ClickX[pn] = RIGlobals_SlotX[i] - x
                            set RIGlobals_ClickY[pn] = RIGlobals_SlotY[i] - y
                            set RIGlobals_Slot[dex].state[RIGlobals_ClickIndex[pn]] = true
                            set RIGlobals_ClickIcon[pn] = RIGlobals_Slot[dex].icon[i]
                            set RIGlobals_Slot[dex].icon[i] = null
                            set DragTimer[pn] = NewTimerEx(pn)
                            call SetUnitFlyHeight(RIGlobals_ClickIcon[pn], RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ + RIGlobals_BUTTON_DRAG_Z, 0)
                            call SetUnitScale(RIGlobals_ClickIcon[pn], RIGlobals_BUTTON_DRAG_SIZE, 1, 1)
                            call TimerStart(DragTimer[pn], 0, true, function dragIcon)
                            exitwhen true
                        else
                            debug call BJDebugMsg(I2S(RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i) + " is empty")
                        endif
                    endif
                    set i = i + 1
                endloop
            elseif RIControlPoint_IsInMacroArea(x, y) then
                set i = 0
                loop
                    exitwhen i > 5
                    if x < RIGlobals_MacroX[i] + s and x > RIGlobals_MacroX[i] - s and y < RIGlobals_MacroY[i] + s and y > RIGlobals_MacroY[i] - s then
                        set dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
                        set RIGlobals_ClickIndex[pn] = i
                        set RIGlobals_ClickType[pn]  = 1
                        if RIGlobals_Macro[dex].index[i] != 0 then
                            set RIGlobals_ClickX[pn] = RIGlobals_MacroX[i] - x
                            set RIGlobals_ClickY[pn] = RIGlobals_MacroY[i] - y
                            set RIGlobals_Macro[dex].state[i] = true
                            set RIGlobals_ClickIcon[pn] = RIGlobals_Macro[dex].icon[i]
                            set RIGlobals_Macro[dex].icon[i] = null
                            set DragTimer[pn] = NewTimerEx(pn)
                            call SetUnitFlyHeight(RIGlobals_ClickIcon[pn], RIGlobals_LOCATION_Z + RIGlobals_BUTTON_NORMALZ + RIGlobals_BUTTON_DRAG_Z, 0)
                            call SetUnitScale(RIGlobals_ClickIcon[pn], RIGlobals_BUTTON_DRAG_SIZE, 1, 1)
                            call TimerStart(DragTimer[pn], 0, true, function dragIcon)
                            exitwhen true
                        else
                            debug call BJDebugMsg(I2S(RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i) + " is empty")
                        endif
                    endif
                    set i = i + 1
                endloop
            endif
        endif
        
        return false
    endfunction
    
    private function mouseLeftRelease takes nothing returns boolean
        
        local integer pn = GetPlayerId(GetTriggerPlayer())
        local integer dex
        local integer i
        local real x
        local real y
        local real s = 32 * RIGlobals_UI_SIZE
        
        if RIGlobals_IsOpening[pn] and RIGlobals_ClickIndex[pn] > -1 then
            call ReleaseTimer(DragTimer[pn])
            call RIRecycler_ReleasePreloadUnit(RIGlobals_ClickIcon[pn])
            set dex = GetUnitUserData(RIGlobals_InventoryUnit[pn])
            set x = GetMouseTerrainX()
            set y = GetMouseTerrainY()
            if RIGlobals_ClickType[pn] == 0 then
                set RIGlobals_Slot[dex].state[RIGlobals_ClickIndex[pn]] = false
            elseif RIGlobals_ClickType[pn] == 1 then
                set RIGlobals_Macro[dex].state[RIGlobals_ClickIndex[pn]] = false
            endif
            if RIControlPoint_IsInInventoryArea(x, y) then
                set i = 1
                loop
                    exitwhen i > RIGlobals_PAGE_SLOT
                    if x < RIGlobals_SlotX[i] + s and x > RIGlobals_SlotX[i] - s and y < RIGlobals_SlotY[i] + s and y > RIGlobals_SlotY[i] - s then
                        if RIGlobals_ClickType[pn] == 0 then
                            call BJDebugMsg("Slot to Slot")
                            call MoveItemToSlot(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn], RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i)
                        elseif RIGlobals_ClickType[pn] == 1 then
                            call BJDebugMsg("Macro to Slot")
                            call MoveMacroToSlot(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn], RIGlobals_PAGE_SLOT * (RIGlobals_UnitPage[dex]-1) + i)
                        endif
                        exitwhen true
                    endif
                    set i = i + 1
                endloop
                /*if i > RIGlobals_PAGE_SLOT then
                endif*/
            elseif RIControlPoint_IsInMacroArea(x, y) then
                set i = 0
                loop
                    exitwhen (i > 5)/*
                    */or (x < RIGlobals_MacroX[i] + s and x > RIGlobals_MacroX[i] - s /*
                    */and y < RIGlobals_MacroY[i] + s and y > RIGlobals_MacroY[i] - s)
                    set i = i + 1
                endloop
                if i > 5 then
                    if RIGlobals_ClickType[pn] == 0 then
                        call BJDebugMsg("Slot to Macro (Auto)")
                        if RIGlobals_Slot[dex].type[RIGlobals_ClickIndex[pn]] == 1 then
                            call MoveItemToMacro(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn], -1)
                        debug else
                            debug call BJDebugMsg("Failed. Attempted to move invalid item type.")
                        endif
                    endif
                else
                    if RIGlobals_ClickType[pn] == 0 then
                        call BJDebugMsg("Slot to Macro-" + I2S(i))
                        if RIGlobals_Slot[dex].type[RIGlobals_ClickIndex[pn]] == 1 then
                            call MoveItemToMacro(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn], i)
                        debug else
                            debug call BJDebugMsg("Failed. Attempted to move invalid item type.")
                        endif
                    elseif RIGlobals_ClickType[pn] == 1 then
                        call BJDebugMsg("Macro-" + I2S(RIGlobals_ClickIndex[pn]) + " to Macro-" + I2S(i))
                        call MoveMacroToMacro(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn], i)
                    endif
                endif
            else
                // Drop item event
                call SetItemCharges(CreateItem(RIGlobals_Item[RIGlobals_Slot[dex].index[RIGlobals_ClickIndex[pn]]].id, GetUnitX(RIGlobals_InventoryUnit[pn]), GetUnitY(RIGlobals_InventoryUnit[pn])), RIGlobals_Slot[dex].charge[RIGlobals_ClickIndex[pn]])
                call RemoveInventoryItem(RIGlobals_InventoryUnit[pn], RIGlobals_ClickIndex[pn])
            endif
            call RefreshInventory(RIGlobals_InventoryUnit[pn])
            set RIGlobals_ClickIndex[pn] = 0
        endif
        
        return false
    endfunction
    
    private function exitWindow takes nothing returns boolean
        
        local unit    u
        local player  p  = GetTriggerPlayer()
        local integer pn = GetPlayerId(p)
        
        set u = RIGlobals_InventoryUnit[pn]
        call OpenInventoryWindow(RIGlobals_InventoryUnit[pn], false)
        if GetLocalPlayer() == p then
            call PanCameraTo(GetUnitX(u), GetUnitY(u))
            call ResetToGameCamera(0)
        endif
        set u = null
        
        return false
    endfunction
    
    public function onPickup takes nothing returns boolean
        
        local item t = GetManipulatedItem()
        local unit u = GetTriggerUnit()
        
        if AddInventoryItem(u, GetItemTypeId(t), GetItemCharges(t)) then
            call RefreshInventory(u)
        endif
        call RemoveItem(t)
        set u = null
        set t = null
        
        return false
    endfunction
    
    private function onCast takes nothing returns boolean
    
        if GetSpellAbilityId() == 'SIA1' then
            call OpenInventoryWindow(GetTriggerUnit(), true)
        endif
    
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        
        local trigger t
        
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_PICKUP_ITEM)
        call TriggerAddCondition(t, Condition(function onPickup))
        
        set t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function onCast))
        
        set t = CreateTrigger()
        call TriggerRegisterMouseEvent(t, 0, 0)
        call TriggerAddCondition(t, Condition(function mouseLeftClick))
        
        set t = CreateTrigger()
        call TriggerRegisterMouseEvent(t, 0, 1)
        call TriggerAddCondition(t, Condition(function mouseLeftRelease))
        
        set t = CreateTrigger()
        call TriggerRegisterKeyEvent(t, 27, 0)
        call TriggerAddCondition(t, Condition(function exitWindow))
        set t = null
        
    endfunction
    
endscope

File:
 

Attachments

  • MagicSoul-FSI2 - Copy.w3x
    282.7 KB · Views: 63
Status
Not open for further replies.
Top