GMS 2.3+ RPG inventories and the best data structure (list, grid, map, etc)

I've been trying to wrap my head around this reading online, but now I'm a bit overwhelmed. I'm trying to make a very simple 16 slot inventory you'd see in an RPG like Final Fantasy or Earthbound, but I have no idea where to start. I have a simple 2 by 8 DSgrid labeled as inventory to start. So now I need to add items with properties and stuff like class compatibility, stats, etc. Some items are a bit more complex where they have an action like controlling another NPC on usage. So because of the detail that goes into these, I think I've figured DSmaps are a good way to go? So the idea, is I make a DSlist with all the items, and underneath those items are seperate DSmaps? Or do they all share the same DSmaps? If so, wouldn't they all have the same stats? It'd just be really nice if someone can explain this a little more clear to me, or if there's a much better solution, inform me about that.

Bonus points if you can also help me understand how adding and removing items from the inventory in-game would work. How would gamemaker understand to add an item to the "earliest" empty inventory spot?
 

chamaeleon

Member
Instead of using a ds_map, it seems to me that structs is much more convenient to work with to represent the information of items. Given the ability to simply declare a struct and its content using my_item = {_type: ItemEnum.Sword, _name: "Short Sword", _damage: 5, _cost: 50 } (I used underscore in the beginning as a convention, not necessary unless the name collides with a GMS reserved name) makes it looks so much more appealing than dealing with maps. Whether you store these structs in a variable like the example or directly in your grids or lists depends on how you structure your program. The garbage collector will manage the memory for you, you should not need to do any explicit cleanup of these structures.
 

samspade

Member
Inventories are often a more complex problem to solve than they appear. The 'problem' with inventories is that how you make them really depends on what you want them to do. For example, an inventory that only ever picks up and holds a single item in a slot. That is probably easiest to do as single array with some math to make the array appear as a grid. An inventory that can stack - maybe that should be an array of arrays or a ds_grid. An inventory where the item itself can have many properties and maybe should be object like itself in some way could be solved with structs or instances.

I would say start with a very, very basic inventory - one done as a single value in an array or grid and build from there.

Finding the first empty spot depends on the structure of the inventory, but in an array it would be:

GML:
for (var i = 0; i < array_length(inventory); i += 1) {
    if (inventory[i] == empty) {
        //do stuff, this is the first empty spot
    }
}
Where empty would be something you define as empty - either an enum, or a macro, or just something like -1.
 
Instead of using a ds_map, it seems to me that structs is much more convenient to work with to represent the information of items. Given the ability to simply declare a struct and its content using my_item = {_type: ItemEnum.Sword, _name: "Short Sword", _damage: 5, _cost: 50 } (I used underscore in the beginning as a convention, not necessary unless the name collides with a GMS reserved name) makes it looks so much more appealing than dealing with maps. Whether you store these structs in a variable like the example or directly in your grids or lists depends on how you structure your program. The garbage collector will manage the memory for you, you should not need to do any explicit cleanup of these structures.
Huh, very interesting. I had no idea structs were even a thing till now. That sounds very useful for covering items, thanks!

Inventories are often a more complex problem to solve than they appear. The 'problem' with inventories is that how you make them really depends on what you want them to do. For example, an inventory that only ever picks up and holds a single item in a slot. That is probably easiest to do as single array with some math to make the array appear as a grid. An inventory that can stack - maybe that should be an array of arrays or a ds_grid. An inventory where the item itself can have many properties and maybe should be object like itself in some way could be solved with structs or instances.

I would say start with a very, very basic inventory - one done as a single value in an array or grid and build from there.

Finding the first empty spot depends on the structure of the inventory, but in an array it would be:

GML:
for (var i = 0; i < array_length(inventory); i += 1) {
    if (inventory[i] == empty) {
        //do stuff, this is the first empty spot
    }
}
Where empty would be something you define as empty - either an enum, or a macro, or just something like -1.
I would think my ideal inventory sounds basic. No stacking items, no hotbar. Just an inventory screen, then 2 columns, and 8 rows, like an oldschool RPG. Is there a substantial difference/reason between using the 2x8 DSgrid I have now, and an array like you mentioned? Also thank you so much for the empty spot explanation, iterating through it makes complete sense. With that in mind, I should be iterating/filling up my grid or array with -1 on creation, right?
 

samspade

Member
Not really. I personally prefer arrays but a grid would also work fine. There are minor difference, mostly with how you save it, but I don't think there's a reason to switch unless you want to. As far as staring with -1 I think that is personal preference as well. An enum or macro would be fine too, you just want something straight forward that you can remember.
 

Yal

šŸ§ *penguin noises*
GMC Elder
I personally would use a one-dimensional array under the hood and just present it in whatever shape you want. You get much easier code if you just need to go through the array from start to end for batch operations (add item, check if party has an item) than if you need to keep track of the shape everywhere you use it.

I always make a special NONE constant with a very distinct value in my projects, that way it's possible to instantly tell what's been intialized to a proper invalid value when debugging the game (0 is the default value when extending a data structure so you can get weird issues with uninitialized data if it's interpreting that as a valid value, -1 is used for self and some GM default returns)... there's also undefined now in GM, but it needs some special handling.
 
I personally use ds grids for my rpg-style inventory and I think it works very well. You can have the row be like an accessor, where the row number can be accessed by any other object/script to give the specific item, then all kinds of info about the item in question can be stored in the columns. I use the columns to store anything from numbers to sprites to scripts to objects, so it's good for complex items like you mentioned.
 

Rob

Member
If you can get your head around "every different item is a number", and design around that, it will help a lot.

You can only store 1 piece of data per cell in your grid, but you want each of those items to have stats.

You could add an array/list to each grid cell and store data that way, or you can have a ds_grid made from an excel sheet (using load_csv) and just store whatever number corresponds with that item in your inventory grid.

Then, when you need to reference an item's stats/etc, you just take whatever number the item is and reference the appropriate row/column in your stats grid.

One thing I like to do is have one Sprite for all items. Every index in that Sprite is a different item and corresponds with the appropriate number for that item.

Eg if sword is your first item, it would take the first index of the item Sprite.
 
Top