• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Design Best way of storing inventory item values?

Reign

Member
I know of a few ways to do this and how I would do this in C++, but I'm wondering what someone more experienced with game maker thinks of my setup and if there's a better way before I call this complete and make 100's of items.

Basically, I have an inventory that looks something like the newer Fallout games
which requires item name, amount, description, icon, max amount, etc. You can craft and store/loot from containers like many Bethesda games. I'm using a ds_grid as an inventory which only stores the ID and amount of each item. I can then look up the description and other properties using the ID to index an array of the item definitions that are stored like so->

Code:
///scrInitAllItems()
for(var i=0;i<NUM_ITEMS;++i)
    global.Items[i]=instance_create(0,0,objItem);

// Bow (example item)
global.Items[12].Type = TYPE_WEAPON;
global.Items[12].Name = "Wood Bow";
global.Items[12].Description = "Looks hand crafted.";
global.Items[12].Amount = 0;
global.Items[12].MaxStack = 0;
global.Items[12].Value = 100;
global.Items[12].Tile = 12;
global.Items[12].Effect = -1;
global.Items[12].Damage = 5;
global.Items[12].DamageType = DAMAGE_SHOT;
global.Items[12].Range = 356;
global.Items[12].ClipSize = -1;
global.Items[12].ReloadTime = -1;
global.Items[12].Projectile = objArrow;
global.Items[12].ProjectileOffset = 0;
global.Items[12].BulletSpeed = 12.0;
global.Items[12].EquipSlot = SLOT_WEAPON;
global.Items[12].Speed = 8;
global.Items[12].Armor = 0;
I can use items like this
Code:
        global.Player.Damage = global.Items[itemid].Damage;
        global.Player.DamageType = global.Items[itemid].DamageType;
        global.Player.Projectile = global.Items[itemid].Projectile;
        global.Player.ClipSize = global.Items[itemid].ClipSize;
Is that fine or should I make each item its own gamemaker object with its properties defined in the create event? or is there an even better way?
 
Last edited:
Here is a link to a post where someone asked a similar question. https://forum.yoyogames.com/index.p...ory-and-items-ideas-solved.26606/#post-165834 I've copied my answer from there (it's a lot of text, didn't want to re-write). I did add a few things related to what you posted.

Your solution obviously works, but you can actually do the whole thing without any instances at all, and use enums for item id's so you don't have to remember arbitrary numbers. You can also add functions for adding items so you don't rewrite the array stuff every single time.

Honestly, you've got 20 lines defining that item... Can you imagine that for 50 items? *dies inside* Simplify your life and write some functions that just set those values based on function arguments.

I set up all of my items with ID's like this.
Code:
enum item
 {
 bomb,
 gun,
 total
 }
This enum is for each particular stat about each item.
Code:
enum stat
 {
 name,
 description,
 max_uses,
 uses,
 owned,
 total
 }
Then setting up the grid and all the information for the items:
Code:
global.item_index = ds_grid_create(item.total, stat.total);

global.item_index[#item.bomb, stat.name] = "Bomb";
global.item_index[#item.bomb, stat.description] = "It goes BOOM!";
global.item_index[#item.bomb, stat.max_uses] = 0;
global.item_index[#item.bomb, stat.uses] = 0;
global.item_index[#item.bomb, stat.owned] = false;
We can also create a function for adding all these stats.
Code:
///scr_add_item(ID, name, description, max uses, uses, owned);

var item_id = argument0;
global.item_index[#item_id, stat.name] = argument1;
global.item_index[#item_id, stat.description] = argument2;
global.item_index[#item_id, stat.max_uses] = argument3;
global.item_index[#item_id, stat.uses] = argument4;
global.item_index[#item_id, stat.owned] = argument5;
To add the gun, or any other item.
Code:
scr_add_item(item.gun, "Gun", "It shoots bullets.", 0, 0, false);
To deal with the sprites, instead of storing the sprite within the grid, I create a single sprite where each sub-image corresponds with the item ID. In this example, the first sub-image is the bomb and the second sub-image is the gun. Then to determine which sprite to use, you just draw the generic item sprite and set the sub-image to the item ID. If you want animated item images, you'll have to do it with separate sprites though.

The reason I like the enums is because it's really easy to reference any item in the game. If I want to increase the uses on the gun: "global.item_index[#item.gun, stat.uses] ++;"
Or if I want to find out if the player has the bombs yet: "if (global.item_index[#item.bomb, stat.owned] == true)"
I deal with which items are equipped just by setting a variable to the item ID I want: item_equipped = item.bomb;
Code:
///Key press space bar

if (item_equipped == item.bomb)
 {
 if (global.item_index[#item.bomb, stat.uses] < global.item_index[#item.bomb, stat.max_uses])
  {
  instance_create(x, y, obj_bomb);
  global.item_index[#item.bomb, stat.uses] ++;
  }
 }
Each of those lines of code tell you exactly what they do because the enums label which values you're changing.

At any rate, that's just how I do it.
 
Last edited:

Reign

Member
@Cloaked Games Coincidentally, that's almost identical to how I had everything set up prior to storing them in objects. Now you have me thinking I should put it back the way it was lol.

The reason I switched to objects is that it's much cleaner to define the items, imo.

Code:
global.Items[12].Type = TYPE_WEAPON;
global.Items[12].Projectile = objArrow;
vs

Code:
// Bow
global.Items[# item.bow, stat.name] = TYPE_WEAPON;
global.Items[# item.bow, stat.projectile] = objArrow;
Do you think using a grid is worth the extra effort for efficiency?
Unfortunately, I can't use things like scr_add_item() to clean it up, because some items have so many stats. In my game there are weapons, usable items, separate armor pieces(helmet, body, legs) with stats for speed, boosting attack, defense, etc.
 
Efficiency wise there's hardly a difference. Either system, here's what I'd suggest:

1) Use enums to define ids:
Code:
enum item
 {
 sword,
 bow
 }

//bow = item.bow instead of just 12
2) Use functions to add items:

EDIT: Just noticed your comment at the bottom. You can use multiple functions if you have to. I just have an item stat for every type of item. But that is a reason to use instances. Just trust me though, if you have a list of "global.items[12] =" over and over again, there is a way to not write that every time.

If there are items that don't need some of those values defined, create separate functions for each type of item and only use them to define the needed values. (For example, armor might need defense stats, but guns don't probably).
Code:
add_item(item.bow, type.weapon, objArrow);

///add_item(item id, type, projectile);
global.Items[# argument0, stat.type] = argument1;
global.Items[# argument0, stat.projectile] = argument2;

//OR

add_item(item.bow, type.weapon); //Item id, item type
add_item_attack(objArrow, 15, damage_type.RANGED); //Object, damage, damage type
add_item_cost(30, 40); //Sell, buy
Personally I prefer the beauty of sorts of it not using any instances, but I don't think there's a noticeable difference until you get to massive numbers of items, and even then, I have no idea how to calculate which is better. Neither system is going to be an issue probably. The one thing I'd say about instances is that you have to somehow keep track of where they all are and which rooms they're in. Not a hard task per say, but a slight complication, for example, if you need to loop through all instances to do something for some reason and you have to manually exclude those ones. (A disclaimer on this, I've never used instances for storing information like that... just considered how such systems would work).
 

Reign

Member
@Cloaked Games I do like the enum idea for the Id. I'll try to clean up the stats once I take the time to plan out which I need exactly and if I get rid of enough, I may be able to use scripts to set their stats, but otherwise, it seems like a little too much for defining every item.

I'm sure grid is more efficient because an object has several extra data structures in it (like x,y,vspeed,hspeed, etc.) which are not being used, but yeah. Guess I'll switch back to using the ds_grid despite it being a little more tedious.

Good point about instances and changing rooms, though I only ever use one room.
That's another thing I've been thinking about though. Could the inventory be its own room and do you know how to go about doing that? I'm assuming you would just use room_goto() to switch between the game and inventory room and make the game room persistent?

Edit:
Tried switching between rooms in a test project like I mentioned and it worked pausing everything in the game room as hoped. Think this will work if the inventory is global and on a persistent object, but don't think I can define it in my player object anymore if I set it up this way.
 
Last edited:
You can do pausing that way. I've seen a few people who are good at GM recommend it. Especially if you only ever use one room.

I personally don't like doing it that way. I pause the game and don't change rooms. It is not the best idea to use too many persistent rooms, or large persistent rooms, if it isn't needed. That takes a bit of memory. Also, you're gonna want saving and loading at some point, usually, and you'll probably want to save and load settings too, so doing it in code as much as possible makes that easier. You can still use another room this way anyways though.

When it comes down to it, it's just: Does it work? Do you like how it works? You should answer yes. Does it hinder your development? Does it cause terrible performance issues? You should answer no. If that's all true, then you're good really.
 

Reign

Member
@Cloaked Games How do you generally go about pausing your games? I think I want to pause while the inventory is open, so this way may work, but I'd like to know what other options there are. If I were making the game from scratch I'd change the game state and I assume changing rooms in GM is the equivalent of that.

Saving and loading really isn't a problem. I implemented procedural maze generation with prims algorithm storing individual rooms in an array of cells like The Binding of Isaac in which I save/load rooms made with a custom map editor I wrote. I can easily save the entire map of rooms, items in all containers and the players inventory. Actually, wouldn't mind getting a second opinion on that if it's alright.

Do you think it's better to have 1 room per file and group them in a directory (like rooms/floorone/0.lvl) or store several rooms in 1 file (like FloorOneRooms.lvl)?
I'd possibly consider using GMs room editor, but it's difficult/clunky to use and I'm not sure how to sort the rooms into categories.

Currently, I'm storing one room per file and loading them like this
Code:
// Load up to 99 rooms
for(var i = 0; i < 99; ++i)
{
    path = working_directory+"Rooms/LevelOne/"+string(i)+".gmrd";
    if(file_exists(path))
    {
        // Load map data into room object
        // and store it in roomlist
        ds_list_add(global.allRooms,scrLoadRoom(path));
    }
    else
    {
        // We found all the rooms, quit out.
        break;
    }
}
Loading them individually might be better because if memory is ever an issue, it would be good to only load the rooms you need. Saving the current map and all of the rooms in one file for would be nice for save data though.
 
Last edited:
Personally I save everything in one file (I actually have a very similar game to you). But I can't say either way. I like it in one because I plan to let players create multiple worlds and that makes that easier.

I have a global pause variable and I have everything that needs to stop in a loop like (if global.paused == pause.UNPAUSED). It's annoying, but not too bad if you implement it from the get go. I use various pause levels. So I have pause.PAUSED, pause.PLAYER_DEAD, pause.INVENTORY, etc. So things will pause differently depending on why it's pausing.

Using a room makes pausing easier probably. For me, these things are preference. I have no opinion or experience as to which is better.
 

Reign

Member
@Cloaked Games That's true, one file is much cleaner and easier to work with. Suppose save data could at least be one file for that reason. One positive of separate room files in a directory though is it being easy for players to add community made maps, just drag and drop in folder. I can't decide lol.

Not entirely sure I get how you're pausing the game. If I understand correctly you're putting the condition for it being paused in a while loop to stop the game? Since I already have thousands of lines of code written, I guess I'll go with the room method because I think it'd be easier to implement at this point.

Funny coincidence we're working on similar games. You make your own map editor or using Tiled?
If you're in need of one I'd be happy to share mine with you. I have a brief description of it and how the map data is stored here.
https://forum.yoyogames.com/index.php?threads/simple-2d-map-maker.31820/
 
Last edited:
Everything that I don't want to move or change while it's paused is inside of a loop that checks if it's paused first. It basically doesn't run the step event while it's paused.

My game is procedural generated. From my end I don't even use the room editor. I'm going to build an in-game map editor, like Minecraft creative mode when I get around to it.
 

NightFrost

Member
Regarding pausing, one way people do it is by disablig all the objects that are unnecessary to paused game functionality and that should not be acting when paused. I do it with a pause function that is at its simplest like:
Code:
/// scr_pause_check()
if(global.Paused == true) return true;
else return false;
And everything that needs to stop while paused does first thing in their step event:
Code:
if(scr_pause_check()) exit;
 

Reign

Member
@Cloaked Games Oh, I think I get it. By loop I'm guessing you mean it's nested in an if statement? Mine generates the path, doors, enemies, items and what rooms are used, but I make the rooms by hand so that I can add puzzles, maintain quality control and make everything look nice. I'm still working on the tileset so the maps look simple for the moment, but I plan on making them pretty intricate.


@NightFrost Might try your method. I noticed instance_deactivate_all() in the documents which might be a good way of doing it, then only activate what's needed with instance_activate_object() (like the object that controls the pausing/unpausing). Definitely prefer to keep my inventory defined in my player object which is global and I don't know if I can do it with the room method I was trying otherwise I might have done that, but yeah.
 

Attachments

Last edited:
Top