• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Solved] Alternative to multidimensional arrays

Level 9
Joined
Jul 30, 2018
Messages
445
Hey!

It's been a while since I coded for Warcraft III (or in general, for that matter), thus my brains are a bit scrambled right now. I am trying to make a simple inventory system that changes all the items for multiple pages of the inventory.

I got it working, actually, as I just brute forced it for the proof of concept, but now I am wondering how I could make the code more efficient. There are two obvious problems: it's not very efficient, and you can't add more pages very easily. My solution would be multidimensional arrays, but as far as I know, JASS doesn't really support them (let alone three-dimensional ones, since I also need a Unit Indexer and the Custom Value of the unit must also be considered). Or if they are supported, could someone maybe give a little explanation on how to implement them? I couldn't find any info on how to use those.

Anyway, any good tips to make it more efficient come to your mind?

Here's a snipped from the most essential part of the code:
JASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer i = GetConvertedPlayerId(p)
    local integer key = GetUnitUserData(udg_InventoryUnit[i])
    if (udg_InvUnitPage[key] == 0) then
        set udg_InvItemSlot1Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 1))
        set udg_InvItemSlot2Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 2))
        set udg_InvItemSlot3Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 3))
        set udg_InvItemSlot4Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 4))
        set udg_InvItemSlot5Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 5))
        set udg_InvItemSlot6Page1[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 6))
    else
    endif
    if (udg_InvUnitPage[key] == 1) then
        set udg_InvItemSlot1Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 1))
        set udg_InvItemSlot2Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 2))
        set udg_InvItemSlot3Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 3))
        set udg_InvItemSlot4Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 4))
        set udg_InvItemSlot5Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 5))
        set udg_InvItemSlot6Page2[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 6))
    else
    endif
    if (udg_InvUnitPage[key] == 2) then
        set udg_InvItemSlot1Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 1))
        set udg_InvItemSlot2Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 2))
        set udg_InvItemSlot3Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 3))
        set udg_InvItemSlot4Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 4))
        set udg_InvItemSlot5Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 5))
        set udg_InvItemSlot6Page3[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 6))
    else
    endif
    if (udg_InvUnitPage[key] == 3) then
        set udg_InvItemSlot1Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 1))
        set udg_InvItemSlot2Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 2))
        set udg_InvItemSlot3Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 3))
        set udg_InvItemSlot4Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 4))
        set udg_InvItemSlot5Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 5))
        set udg_InvItemSlot6Page4[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 6))
    else
    endif
    if (udg_InvUnitPage[key] == 4) then
        set udg_InvItemSlot1Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 1))
        set udg_InvItemSlot2Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 2))
        set udg_InvItemSlot3Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 3))
        set udg_InvItemSlot4Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 4))
        set udg_InvItemSlot5Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 5))
        set udg_InvItemSlot6Page5[key] = GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], 6))
    else
    endif
endfunction
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
Making the swap to a Hashtable is pretty simple:
vJASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local integer parentKey = GetHandleId(udg_InventoryUnit[id])
    local integer i = 1

    loop
        call SaveInteger( udg_InvHash, parentKey, i, GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[id], i)) )
        set i = i + 1
        exitwhen i > 6
    endloop

    set p = null
endfunction
Then you can get this data whenever you want:
vJASS:
local integer parentKey = GetHandleId(udg_InventoryUnit[id])
local integer firstItemType = LoadInteger( udg_InvHash, parentKey, 1 )
local integer secondItemType = LoadInteger( udg_InvHash, parentKey, 2 )
It's practically the same exact logic, you just need to get used to the syntax of the Hashtable functions.

Also, don't forget to actually create and store the Hashtable:
  • Events
    • Map initialization
  • Conditions
  • Actions
    • Hashtable - Create a hashtable
    • Set Variable InvHash = (Last created hashtable)
 
Last edited:
Level 9
Joined
Jul 30, 2018
Messages
445
Making the swap to a Hashtable is pretty simple:
vJASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local integer parentKey = GetHandleId(udg_InventoryUnit[id])
    local integer i = 1

    loop
        call SaveInteger( udg_InvHash, parentKey, i, GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[i], i)) )
        set i = i + 1
        exitwhen i > 6
    endloop

    set p = null
endfunction
Then you can get this data whenever you want:
vJASS:
local integer parentKey = GetHandleId(udg_InventoryUnit[id])
local integer firstItemType = LoadInteger( udg_InvHash, parentKey, 1 )
local integer secondItemType = LoadInteger( udg_InvHash, parentKey, 2 )
It's practically the same exact logic, you just need to get used to the syntax of the Hashtable functions.
Ooh! Haven't really used Hashtables much, but that seems simple enough! Maybe I'll give this a try first. Thanks!

EDIT:

Hmm...I played around a bit, and I maybe got the hang of it, a bit. But I ran into the problem I originally thought was always going to be the problem with hashtables too; they are not 3-dimensional. Is there way around this maybe?

This is as close as I got to what I need:
JASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local integer parentKey = GetHandleId(udg_InventoryUnit[id])
    local integer customValue = GetUnitUserData(udg_InventoryUnit[id])
    local integer currentPage = udg_InvUnitPage[customValue]
    local integer maxPages = udg_InvUnitPagesMax[customValue]
    local integer n = 1
    local integer i = 1
   
    loop
        if (n == currentPage) then
            loop
                call SaveInteger(udg_InvHash, parentKey, i, GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[id], i))) //I need to save the page number here too
                set i = i + 1
                exitwhen i > 6
            endloop
        endif
        set n = n + 1
        exitwhen n > maxPages
    endloop

    set p = null
endfunction

function LoadInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local integer parentKey = GetHandleId(udg_InventoryUnit[id])
    local integer customValue = GetUnitUserData(udg_InventoryUnit[id])
    local integer currentPage = udg_InvUnitPage[customValue]
    local integer maxPages = udg_InvUnitPagesMax[customValue]
    local integer n = 1
    local integer i = 1
    loop
        if (n == currentPage) then
            loop
                call RemoveItem(UnitItemInSlotBJ(udg_InventoryUnit[id], i))
                call UnitAddItemByIdSwapped(LoadInteger(udg_InvHash, parentKey, i), udg_InventoryUnit[id]) //Now here I should load the page number too
                set i = i + 1
                exitwhen i > 6
            endloop
        endif
        set n = n + 1
        exitwhen n > maxPages
    endloop
endfunction
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
You can adjust i based on the page number:
1 -> 6 for page 1
7 -> 12 for page 2
13 -> 19 for page 3
etc...
vJASS:
function LoadInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local unit u = udg_InventoryUnit[id]
    local integer parentKey = GetHandleId(u)
    local integer customValue = GetUnitUserData(u)
    local integer maxPages = udg_InvUnitPagesMax[customValue]
    local integer n = 0 // I'm also using n to calculate the correct value for i in the hashtable
    local integer i = 1

    loop
        loop
            call RemoveItem(UnitItemInSlotBJ(u, i))
            call UnitAddItemByIdSwapped(LoadInteger(udg_InvHash, parentKey, i + 6*n), u)
            set i = i + 1
            exitwhen i > 6
        endloop

        set n = n + 1
        set i = 1
        exitwhen n >= maxPages
    endloop

    // Don't forget to clean up memory leaks:
    set p = null
    set u = null
endfunction

But I don't fully understand what you're trying to achieve with the Save function since a Unit can only have six Items at a time.

Is the idea to Save everything in the Inventories "current page"?
vJASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local unit u = udg_InventoryUnit[id]
    local integer parentKey = GetHandleId(u)
    local integer currentPage = udg_InvUnitPage[GetUnitUserData(u)]
    local integer i = currentPage * 6 - 5
    local integer i2 = i + 5

    loop
        call SaveInteger( udg_InvHash, parentKey, i, GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[id], i)) )
        set i = i + 1
        exitwhen i > i2
    endloop

    // Don't forget to clean up memory leaks:
    set p = null
    set u = null
endfunction
This simple arithmetic should allow you to always reference the correct range based on the page number. 1-6, 7-12, 13-19, etc...

It basically translates to this:
  • Set MySaveData[unit][1] = item 1 of page 1
  • Set MySaveData[unit][2] = item 2 of page 1
  • Set MySaveData[unit][3] = item 3 of page 1
  • Set MySaveData[unit][4] = item 4 of page 1
  • Set MySaveData[unit][5] = item 5 of page 1
  • Set MySaveData[unit][6] = item 6 of page 1
  • Set MySaveData[unit][7] = item 1 of page 2
  • Set MySaveData[unit][8] = item 2 of page 2
  • Set MySaveData[unit][9] = item 3 of page 2
  • Set MySaveData[unit][10] = item 4 of page 2
  • Set MySaveData[unit][11] = item 5 of page 2
  • Set MySaveData[unit][12] = item 6 of page 2
 
Last edited:
Level 9
Joined
Jul 30, 2018
Messages
445
You can adjust i based on the page number:
1 -> 6 for page 1
7 -> 12 for page 2
13 -> 19 for page 3
etc...
vJASS:
function LoadInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local unit u = udg_InventoryUnit[id]
    local integer parentKey = GetHandleId(u)
    local integer customValue = GetUnitUserData(u)
    local integer maxPages = udg_InvUnitPagesMax[customValue]
    local integer n = 0 // I'm also using n to calculate the correct value for i in the hashtable
    local integer i = 1

    loop
        loop
            call RemoveItem(UnitItemInSlotBJ(u, i))
            call UnitAddItemByIdSwapped(LoadInteger(udg_InvHash, parentKey, i + 6*n), u)
            set i = i + 1
            exitwhen i > 6
        endloop

        set n = n + 1
        set i = 1
        exitwhen n >= maxPages
    endloop

    // Don't forget to clean up memory leaks:
    set p = null
    set u = null
endfunction

But I don't fully understand what you're trying to achieve with the Save function since a Unit can only have six Items at a time.

Is the idea to Save everything in the Inventories "current page"?
vJASS:
function SaveInventory takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local integer id = GetConvertedPlayerId(p)
    local unit u = udg_InventoryUnit[id]
    local integer parentKey = GetHandleId(u)
    local integer currentPage = udg_InvUnitPage[GetUnitUserData(u)]
    local integer i = currentPage * 6 - 5
    local integer i2 = i + 5

    loop
        call SaveInteger( udg_InvHash, parentKey, i, GetItemTypeId(UnitItemInSlotBJ(udg_InventoryUnit[id], i)) )
        set i = i + 1
        exitwhen i > i2
    endloop

    // Don't forget to clean up memory leaks:
    set p = null
    set u = null
endfunction
This simple arithmetic should allow you to always reference the correct range based on the page number. 1-6, 7-12, 13-19, etc...

It basically translates to this:
  • Set MySaveData[unit][1] = item 1 of page 1
  • Set MySaveData[unit][2] = item 2 of page 1
  • Set MySaveData[unit][3] = item 3 of page 1
  • Set MySaveData[unit][4] = item 4 of page 1
  • Set MySaveData[unit][5] = item 5 of page 1
  • Set MySaveData[unit][6] = item 6 of page 1
  • Set MySaveData[unit][7] = item 1 of page 2
  • Set MySaveData[unit][8] = item 2 of page 2
  • Set MySaveData[unit][9] = item 3 of page 2
  • Set MySaveData[unit][10] = item 4 of page 2
  • Set MySaveData[unit][11] = item 5 of page 2
  • Set MySaveData[unit][12] = item 6 of page 2
Yes, the idea is that I can save all six items the unit currently has into basically one variable (InvUnitPage[CustomValueOfUnit]). This way I can add more pages just by changing one number, the maximum number of pages. And this way the system stays dynamic so that you can add virtually any number of pages and they will work with the same system. That's why I need to store the page number in a second array. But yeah, later thinking about this, I realized that here simulating a third array (i + n * 6) is perfectly viable option.

Anyway, I got it working! Thanks a lot for the help. :thumbs_up:
 
Top