Thank you, that really helps Kevin. I will read your tutorial too Phaelax because it looks like that could be useful not only for inventory management.
I attach to this post my full inventory, borrowing Phaelax sorting code (a bit modified to fit my need since an empty inventory-slot in my setup will appear as "-1" to the array, and that causes a crash as you can expect) as well as suggestion from Kevin to use a bitwise operations calculation in order to find sorting priority.
The code is a complete mess, but I have tried to comment everything out. I am sure that it would be possible to optimize this snippet a lot, but for now I am too lazy to begin with that.
Edit: need advanced 2d plugin to compile, and I dunno what else (if anything).
Sync On
Sync Rate 0
Rem LW - FIRST OF ALL, let's decide how big our inventory will be
Rem LW - I use global variable and not constant, because in my game
Rem LW - player will eventually be able to upgrade his inventory size
GLOBAL INVENTORY_SLOTS_X : INVENTORY_SLOTS_X = 6
GLOBAL INVENTORY_SLOTS_Y : INVENTORY_SLOTS_Y = 2
Rem LW - Make our Advanced2D font
Global a2FontINV
a2FontINV = a2CreateFont("consolas", 9, a2Size_Point(), a2Style_Normal())
Rem LW - the following is just here to make it media-free
Rem LW - although it should be noted that I am focusing this on 32x32 size tiles/images
Global inventory_image : inventory_image =1
Global item_img_start : item_img_start=100
Rem LW - make inventory images
make_inventory_image()
For x = 0 To 1:cls rgb(200-(x*100),50+(x*150),100+(x*50))
Get image item_img_start+x, 0,0, 32,32:Next x
Rem LW - The UDT for all item information
Type tItem
Rem LW - itemdata() specific
StackMax As Integer Rem LW - how many stacks is max
Group As Integer Rem LW - Define a group, such as "consumable" or "glove"
Rarity As Integer Rem LW - Define a rarity, such as (3->0) normal, common, rare, legendary
Rem LW - items() and itemdata() shared
Name As String Rem LW - name of item
StackAble As Boolean Rem LW - if item is stackable
Description As String Rem LW - short(or long) description about the item
ImageID As Integer
Rem LW - items() specific
inInventory As Boolean Rem LW - if the item is in inventory or not
SourceID As Integer Rem LW - it tells us what ID from ITEMDATA() this item is
sortKey As Integer Rem LW - generate key for when sorting item in inventory
EndType
Rem LW - This array holds default information about items. It is filled automatically by reading data from
Rem LW - a game database (sqlite). But for this media-free demo I enter here some random crap manually >.>
Dim ITEMDATA() As tItem
Dim ITEMGROUP$(4)
ITEMGROUP$(0) = "Weapon"
ITEMGROUP$(1) = "Chest"
ITEMGROUP$(2) = "Hands"
ITEMGROUP$(3) = "Feet"
ITEMGROUP$(4) = "Consumable"
Array Insert At Bottom ITEMDATA() : pos = Array Count( ITEMDATA() )
ITEMDATA(pos).NAme = "crazy hat"
ITEMDATA(pos).StackAble = 0
ITEMDATA(pos).ImageID = item_img_start Rem LW - "weap" will be this imageid
ITEMDATA(pos).Description = "This crazy hat was once worn by an evil wizard who murdered thousands of ants every year"
ITEMDATA(pos).Group = 0 Rem LW - 0, weapon
ITEMDATA(pos).Rarity = 1 Rem LW - 1, yellow
Array Insert At Bottom ITEMDATA() : pos = Array Count( ITEMDATA() )
ITEMDATA(pos).Name = "potion"
ITEMDATA(pos).StackAble = 1
ITEMDATA(pos).StackMax = 10
ITEMDATA(pos).ImageID = item_img_start+1 Rem LW - "Consumable" will be this imageid
ITEMDATA(pos).Description = "You smell a strong fragrance of disgustingness, why would you inspect this potion? WHY?"
ITEMDATA(pos).Group = 4 Rem LW - 4, Consumable
ITEMDATA(pos).Rarity = 3 Rem LW - 3, white
Array Insert At Bottom ITEMDATA() : pos = Array Count( ITEMDATA() )
ITEMDATA(pos).Name = "lobster"
ITEMDATA(pos).StackAble = 0
ITEMDATA(pos).ImageID = item_img_start+1 Rem LW - "Consumable" will be this imageid
ITEMDATA(pos).Description = "This lobster is still alive and struggling, you better put it down soon."
ITEMDATA(pos).Group = 4 Rem LW - 4, Consumable
ITEMDATA(pos).Rarity = 2 Rem LW - 2, blue
Array Insert At Bottom ITEMDATA() : pos = Array Count( ITEMDATA() )
ITEMDATA(pos).Name = "Scorched pancake"
ITEMDATA(pos).StackAble = 1
ITEMDATA(pos).StackMax = 5
ITEMDATA(pos).ImageID = item_img_start+1 Rem LW - "Consumable" will be this imageid
ITEMDATA(pos).Description = "It looks like this will not only restore your health, but you will also require a toilet for the remainer of the day"
ITEMDATA(pos).Group = 4 Rem LW - 4, Consumable
ITEMDATA(pos).Rarity = 0 Rem LW - 0, orange
Rem LW - The UDT for inventory information
Type tInventory
SourceID As Integer Rem LW - id of the source item in this slot, as grabbed from ITEMS() array
Stacks As Integer Rem LW - how many stacks of an item in this slot
Busy As Boolean Rem LW - if there is an item in this slot
TileX As Integer
TileY As Integer Rem LW - these TileX/Y variable tell us the position in x/y grid in inventory window
EndType
Rem LW - Global inventory variables, set empty slots in inventory as -1 item ID
Dim INVENTORY( (INVENTORY_SLOTS_X*INVENTORY_SLOTS_Y)-1) As tInventory
For x= 0 To Array Count( INVENTORY() ) : INVENTORY(x).SourceID = -1 : Next x
Global Inventory_Dragging As Integer
Global Inventory_Index_underMouse As Integer
Rem LW - This array holds information about items that we have loaded into the game
Rem LW - it is the most important because essentially it decides if an object should be hidden or visible-
Rem LW - if, in the game, there existed the possibility of dropping items on the ground.
Dim ITEMS() as tItem
Rem LW - randomly fill this items() array
Rem LW - let's pretend that a boss was killed and it dropped 5 items on the ground
kill_a_boss( 5 )
Global _mouseclick
Do : cls rgb(128,128,128)
Rem LW - if you press SPACE we kill a boss and more items drops
If SpaceKey() And sl=0 : kill_a_boss( 1+rnd(4) ) : sl =1 : EndIf
If not SpaceKey() Then sl =0
Rem LW - if you press RETURN add one more row to your inventory
If returnkey() And rl=0
Inc INVENTORY_SLOTS_Y, 1
Dim INVENTORY( (INVENTORY_SLOTS_X*INVENTORY_SLOTS_Y)-1) As tInventory
For x= 0 To Array Count( INVENTORY() ) : If INVENTORY(x).Busy =0 : INVENTORY(x).SourceID = -1 : EndIf : Next x
make_inventory_image()
cls rgb(128,128,128) Rem LW - clear the screen in this bgcolor again-
rl =1 Rem LW - to avoid flash when creating new inventory image
EndIf
If not returnkey() Then rl =0
Rem LW - if you press CTRL then sort inventory
If ControlKey() And cl =0
cl =1
INVENTORY_SORT( 0, Array Count(INVENTORY()) )
EndIf
If Not ControlKEy() Then cl =0
Rem LW - latch on mouseclick
_mouseclick =0
If MouseClick()=1 And ml =0 Then ml =1 : _mouseclick =1
If MouseClick()<>1 Then ml =0
Rem LW - I forgot what this function call does
INVENTORY_UPDATE()
Rem LW - this is some useless code here for printing stuff on screen
Set Cursor 0,0 : Print "Items in database: "+Str$( Array Count(ITEMDATA())+1 )
For x = 0 To Array Count( ITEMDATA() ) : Print ITEMDATA(x).Name : NExt x
Print "--press space to drop rnd() amount of items from this database onto the ground below--"
Print : PRint : Print : Print "Items on the ground: "+Str$( _count+1 )
Text 450, 165, "drag outside inventory to drop"+chr$(10)+"Press return to add row"
Text 390, 200, "(CTRL)"+chr$(10)+"to sort"
Rem LW - if you click an item from list, pick it up and place in inventory
_count = -1
For x = 0 To Array Count( ITEMS() )
If NOT ITEMS(x).inInventory
inc _count, 1
If MouseX() < 200 And MouseY() > 153+(_count*15) And MouseY() < 15+153+(_count*15) Then add$ = "<" Else add$ = ""
If add$ = "<" And _mouseclick =1 : add$ = "X" `<- that X is just here for no reason, don't worry
result = INVENTORY_ADD_ITEM( x )
EndIf
Print x,") ",ITEMS(x).Name,add$
EndIf
Next x
Print "--click item to pick it up--"
Rem LW - print out inventory array
text 700, 85, "slot, item, s_key"
for x = 0 To Array Count( inventory() )
add$ = "65535"
If inventory(x).SourceID > -1 Then add$ = str$(ITEMS(INVENTORY(x).SourceID).SortKey)
if x < 10 then add2$ = "0" else add2$=""
if inventory(x).SourceID < 10 And inventory(x).SourceID >-1 then add3$ = "0" else add3$=""
text 700, 100+(x*15), add2$+str$(x)+") "+add3$+Str$(inventory(x).SourceID)+" "+add$
next x
Sync : Loop
Rem LW - this function will add an item to the inventory
Function INVENTORY_ADD_ITEM( id As Integer )
Local _isStack As Boolean
Local _freeSlot As Integer
Rem LW - First of all, we need to see if there is a free slot in the inventory
_isStack = 0
_freeSlot = -1
Rem LW - the first step is to see if the item we want to add is stackable
Rem LW - so that the item we pick up will rest in a stack, and not in a free slot
If ITEMS( id ).StackAble =1
Rem LW - now we need to see if there is a free slot in any of the stacks
For i = 0 To Array Count(INVENTORY())
Rem LW - inside all busy slots,
If Inventory(i).Busy =1
Rem LW - see if the item here is stackable
source = INVENTORY(i).SourceID
If ITEMS(source).StackAble =1
Rem LW - and see if they are from the same item family
If ITEMS(source).SourceID = ITEMS(id).SourceID
Rem LW - now, see if there is a free slot in this stack finally
If INVENTORY(i).Stacks+1 <= ITEMDATA(ITEMS(id).SourceID).StackMax-1
Rem LW - there is a free slot in this stack
Rem LW - we save this position
_freeSlot = i
Rem LW - we also let application know that we want to add to a stack
_isStack =1
Rem LW - now exit
Exit
EndIf
Rem LW - if there was no free slot in this stack, the application
Rem LW - will continue to search through the whole inventory until
Rem LW - it finds a stack that has a free slot, or if it does not
Rem LW - then it will eventually search for a free normal slot below.
EndIf
EndIf
EndIf
Next i
EndIF
Rem LW - now we need to check if we still need to look for a free slot,
Rem LW - because - if the requested item was not stackable then _freeSlot
Rem LW - will still be -1 and as well if there did not exist any free
Rem LW - slot in a stack it will be -1 so even then this requested item
Rem LW - should go to a new slot and open a new stack here
If _freeSlot = -1
For i = 0 To Array Count( INVENTORY() )
If INVENTORY(i).Busy =0
Rem LW - if there exist a free slot, we save this position and exit
_freeSlot = i
Exit
EndIf
Next i
EndIf
Rem LW - Now we need to check one last time what is the value of _freeSlot
Rem LW - if it is still -1 then there is no room for more items
Rem LW - so there is nothing to it.. let's run away!
If _freeSlot = -1 Then ExitFunction 0
Rem LW - if the fuction call made it this far, it means that there is a free slot somewhere in the inventory
Rem LW - and we have the free slot position stored in variable _freeSlot.
Rem LW - but first, let's see if this item should be stored inside a stack
If _isStack =1
Rem LW - and increase the amount of items in this stack
Inc INVENTORY( _freeSlot ).Stacks, 1
Else
Rem LW - Otherwise add this item to the inventory, and set status to busy=1
INVENTORY( _freeSlot ).Busy =1
INVENTORY( _freeSlot ).SourceID = id
EndIf
Rem LW - Finally we need to update items() array on requested id index to
Rem LW - let the game know that this item is now inside the inventory
ITEMS( id ).inInventory = 1
EndFunction 1
Function INVENTORY_UPDATE()
Local _index As Integer
Local _displayInfo As Integer
Rem LW - Reset variables that need to reset
_displayInfo = -1 Rem LW - here is trigger to display item information
Inventory_Index_underMouse = -1 Rem LW - here is which index in inventory is currently under mouse
Rem LW - temporary variable store for inventory window position
xpos = 450
ypos = 200
Paste Image inventory_image, xpos, ypos
Rem LW - look through whole inventory
Rem LW - this variale _index is here for inventory() array slot position
_index = -1
Rem LW - remember we begin with y first, because we want the items to appear horizontally
For y = 0 To INVENTORY_SLOTS_Y-1
For x = 0 To INVENTORY_SLOTS_X-1
Rem LW - since we start at index -1 we can increase this variable already now
Rem LW - so that we begin this son of a gun loop with an index of 0
Rem LW - which will make our inventory() array very happy
Inc _index, 1
Rem LW - Store x/y position for this index
INVENTORY( _index ).TileX = x
INVENTORY( _index ).TileY = y
Rem LW - check mouse hover over this index
x1 = (xpos+5)+(x*33)
x2 = (xpos+5)+((x+1)*33)
y1 = (ypos+5)+(y*33)
y2 = (ypos+5)+((y+1)*33)
If MouseX() >= x1 And MouseX() <= x2 And MouseY() >= y1 And MouseY() <= y2
_hover =1
Rem LW - Update this index_undermouse variable so we can later check if that index is free for manually putting an item
Rem LW - or if an item already exist, we can know which two index's should switch places
Inventory_Index_underMouse = _index
EndIf
Rem LW - Check if there is anything in this slot
If INVENTORY( _index ).Busy =1
Rem LW - yeah, now paste this image from item
a2DrawImage ITEMS( INVENTORY(_index).SourceID ).ImageID, (xpos+5)+(x*33), (ypos+5)+(y*33), 0, 0, 0, 1, 0, 0xFFFFFFFF
Rem LW - if this item is rare, let us know!
rarity = ITEMDATA( ITEMS( INVENTORY(_index).SourceID ).SourceID ).Rarity
If rarity < 3
If rarity =2 Then a2FillBox (xpos+5)+(x*33), (ypos+5)+(y*33), (xpos+5)+(x*33)+10, (ypos+5)+(y*33)+10, 0xFF7777FF
If rarity =1 Then a2FillBox (xpos+5)+(x*33), (ypos+5)+(y*33), (xpos+5)+(x*33)+10, (ypos+5)+(y*33)+10, 0xFFf3fa35
If rarity =0 Then a2FillBox (xpos+5)+(x*33), (ypos+5)+(y*33), (xpos+5)+(x*33)+10, (ypos+5)+(y*33)+10, 0xFFfaae50
EndIf
Rem LW - If there are any stacks, let us know!
If INVENTORY( _index ).Stacks >0
text_x = ((xpos+5)+((x+1)*33))-(a2GetTextWidth(a2FontINV,Str$(INVENTORY( _index ).Stacks+1)))-3
a2Text a2FontINV, text_x, (ypos+5)+(y*33), Str$(INVENTORY( _index ).Stacks+1), 0xFF000000
EndIf
If _hover =1
Rem LW - store item id in variable so we can draw that information on top of everything else
_displayInfo = INVENTORY(_index).SourceID
Rem LW - If Mousebutton is held, and user is not already dragging an item
If MouseClick() =1 And Inventory_dragging = -1
Inventory_dragging = _index
EndIf
EndIf
EndIf
_hover =0
Next x
Next y
Rem LW - if user is dragging an item
If Inventory_Dragging > -1
If INVENTORY( Inventory_Dragging ).SourceID > -1
Rem LW - highlight the old tile
x1 = (xpos+5)+(INVENTORY( Inventory_Dragging ).TileX*33)
x2 = (xpos+4)+((INVENTORY( Inventory_Dragging ).TileX+1)*33)
y1 = (ypos+5)+(INVENTORY( Inventory_Dragging ).TileY*33)
y2 = (ypos+4)+((INVENTORY( Inventory_Dragging ).TileY+1)*33)
a2FillBox x1, y1, x2, y2, 0xAA000000
a2DrawImage ITEMS( INVENTORY(Inventory_Dragging).SourceID ).ImageID, MouseX(), MouseY(), 0, 0, 0, 1, 0, 0xAAFFFFFF
Rem LW - Highlight the new tile
If Inventory_Index_underMouse > -1 And Inventory_Index_UnderMouse <> Inventory_Dragging
x1 = (xpos+5)+(INVENTORY( Inventory_Index_underMouse ).TileX*33)
x2 = (xpos+4)+((INVENTORY( Inventory_Index_underMouse ).TileX+1)*33)
y1 = (ypos+5)+(INVENTORY( Inventory_Index_underMouse ).TileY*33)
y2 = (ypos+4)+((INVENTORY( Inventory_Index_underMouse ).TileY+1)*33)
a2FillBox x1, y1, x2, y2, 0xAA97ce68
Rem LW - If user stop holding mouse button
If MouseClick() =0
INVENTORY_SWAP( Inventory_Dragging, Inventory_Index_underMouse )
EndIf
EndIf
Rem LW - If mouse is outside inventory
If Inventory_Index_underMouse = -1
Rem LW - if player release mouse button
If MouseClick() =0
old_item = INVENTORY(Inventory_Dragging).SourceID
ITEMS( old_item ).inInventory =0
Rem LW - if it has stacks
If INVENTORY( Inventory_Dragging ).Stacks >0
Rem LW - find new itemID to assign for this inventory slot by searching
Rem LW - items() array for same name item and if that item is in inventory
Rem LW - since in my game items will not be unique in a sense that if "wrong"
Rem LW - item was dropped then it would impact gameplay and or visuals..
Rem LW - if they have the same name, then ultimately they are the same item.
For x = 0 To Array Count( ITEMS() )
If ITEMS(x).inInventory =1
If ITEMDATA(ITEMS(x).SourceID).Name = ITEMDATA(ITEMS(old_item).SourceID).Name
Rem LW - make sure that this item_id is not already assigned to another slot
item_busy =0
For y = 0 To Array Count( INVENTORY() )
If INVENTORY(y).SourceID = x
item_busy =1
Exit
EndIf
Next y
If item_busy =0
INVENTORY( Inventory_Dragging ).SourceID = x
exit
EndIf
EndIf
EndIf
Next x
Dec INVENTORY( Inventory_Dragging ).Stacks, 1
Rem LW - if it does not have stacks
Else
INVENTORY( inventory_Dragging ).Busy = 0
INVENTORY( inventory_Dragging ).sourceID = -1
EndIf
EndIf
EndIf
EndIf
EndIf
If MouseClick() =0 Then Inventory_Dragging = -1
Rem LW - if requested, draw some information about this item
If _displayInfo > -1 And Inventory_Dragging = -1
INVENTORY_DISPLAY_ITEMINFO( _displayInfo )
EndIf
EndFunction
Rem LW - this will draw a little window with some text about the item mouse is currently hovering over
Function INVENTORY_DISPLAY_ITEMINFO( item_id As Integer)
If MouseX() >= Screen Width()/2 Then _direction =1
text$ = ITEMS( item_id ).Description
length = 250 : height = 45 + ((a2GetTextWidth(a2FontINV,text$)/length)*a2GetLineHeight(a2FontINV))
rarity = ITEMDATA( ITEMS(item_id).SourceID ).Rarity
If rarity =3 Then color = 0xFFFFFFFF
If rarity =2 Then color = 0xFF7777FF
If rarity =1 Then color = 0xFFf3fa35
If rarity =0 Then color = 0xFFfaae50
Rem LW - draw towards right side of screen
If _direction =0
x1 = MouseX()+15 : x2 = x1+length
y1 = MouseY() : y2 = y1+height
a2FillBox x1, y1, x2+5, y2+5, 0xCC34332b
a2Text a2FontINV, x1+5, y1+2, ITEMS( item_id ).Name, color
a2Text a2FontINV, x1+5, y1+15, ITEMGROUP$(ITEMDATA( ITEMS(item_id).SourceID ).Group), 0xFFAAAAAA
a2BoxText a2FontINV, x1+5, y1+30, x2, y2, text$, 0, 0, 1, 0xFFFFFFFF
Else
Rem LW - draw towards left of screen
x1 = MouseX()-15 : x2 = x1-length
y1 = MouseY() : y2 = y1+height
a2FillBox x1, y1, x2+5, y2+5, 0xCC34332b
a2Text a2FontINV, x2+10, y1+2, ITEMS( item_id ).Name, color
a2Text a2FontINV, x2+10, y1+15, ITEMGROUP$(ITEMDATA( ITEMS(item_id).SourceID ).Group), 0xFFAAAAAA
a2BoxText a2FontINV, x2+10, y1+30, x1, y2, text$, 0, 0, 1, 0xFFFFFFFF
EndIf
EndFunction
Rem LW - This little function will swap two indexes in inventory
Function INVENTORY_SWAP( source, target )
temp As tInventory
temp = INVENTORY(source)
INVENTORY(source) = INVENTORY(target)
INVENTORY(target) = temp
EndFunction
Rem LW - this function drops x amount of items on the ground, as available from our itemdata() array
Function kill_a_boss( amount As Integer )
For x = 1 To amount
Array Insert At Bottom ITEMS()
pos = Array Count( ITEMS() )
source_id = rnd(Array Count(ITEMDATA()))
ITEMS( pos ).SourceID = source_id
ITEMS( pos ).ImageID = ITEMDATA( source_id ).ImageID
ITEMS( pos ).Name = ITEMDATA( source_id ).Name
ITEMS( pos ).StackAble = ITEMDATA( source_id ).StackAble
ITEMS( pos ).Description = ITEMDATA( source_id ).Description
sortkey = ( (ITEMDATA(source_id).Group and 255) << 8 )
sortkey = sortkey+(ITEMDATA(source_id).rarity and 255)
ITEMS( pos ).SortKEy = sortkey
Next x
EndFunction
Rem LW - this makes an inventory image for us
Function make_inventory_image()
If image Exist( inventory_image )=1 Then delete image Inventory_Image
CLS RGB(0,0,0):For x = 0 To INVENTORY_SLOTS_X-1:For y = 0 To INVENTORY_SLOTS_Y-1
Ink RGB(128,127,110), 0:Box 5+(x*33), 5+(y*33), 5+(x*33)+32, 5+(y*33)+32
Next y:Next x:Get Image inventory_image, 0,0, 10+(33*INVENTORY_SLOTS_X), 10+(33*INVENTORY_SLOTS_Y)
Ink 0,0
EndFunction
Rem LW - Function to sort inventory,
Rem LW - originally by Phaelax (www.zimnox.com)
Function INVENTORY_SORT( left as integer, right as integer)
li = left
ri = right
If right - left > 0
pivot = (left + right ) /2
While ( li <= pivot ) And ( ri >= pivot )
safe1 = inventory(li).sourceid : If safe1 = -1 then tmp1 = 65535 Else tmp1 = ITEMS(safe1).SortKey
safe2 = inventory(pivot).sourceid : If safe2 = -1 then tmp2 = 65535 Else tmp2 = ITEMS(safe2).SortKey
While ( tmp1 < tmp2 ) And (li <= pivot )
Inc li
safe1 = inventory(li).sourceid : If safe1 = -1 then tmp1 = 65535 Else tmp1 = ITEMS(safe1).SortKey
EndWhile
safe1 = inventory(ri).sourceid : If safe1 = -1 then tmp1 = 65535 Else tmp1 = ITEMS(safe1).SortKey
safe2 = inventory(pivot).sourceid : If safe2 = -1 then tmp2 = 65535 Else tmp2 = ITEMS(safe2).SortKey
While ( tmp1 > tmp2 ) And (ri >= pivot )
Dec ri
safe1 = inventory(ri).sourceid : If safe1 = -1 then tmp1 = 65535 Else tmp1 = ITEMS(safe1).SortKey
EndWhile
INVENTORY_SWAP( li, ri )
inc li
dec ri
If li -1 = pivot
inc ri
pivot = ri
Else
If ri +1 = pivot
dec li
pivot = li
EndIf
EndIf
EndWhile
INVENTORY_SORT( left, pivot-1 )
INVENTORY_SORT( pivot+1, right )
EndIf
EndFunction
Edit 2: lol, fixed a silly bug.. which is hard to explain. but basically it would remove a busy item from inventory so that when the true item was removed it was not removed but yet it was.. eh, like I said: hard to explain >.< Anyway, now it's a working code.
IS that a game, a program, or 3 year old drawing.