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

Legacy GM Help with an inventory that can sort items

T

trentallain

Guest
I really have no idea where to start with the inventory in a game I am making. The issue I have is I can't find a suitable guide online for what I am looking for. I am thinking of using 2D arrays to achieve this, but as I said, I don't know what I need to do to achieve what I want. Here is a sketch of what the inventory currently looks like:
(I have also attached the image)
 

Attachments

S

Supercoder

Guest
The 2D array method should work, though most people would probably recommend a ds list. They're more flexible, and also allow for alphabetical sorting without very complicated methods.
 
T

trentallain

Guest
The 2D array method should work, though most people would probably recommend a ds list. They're more flexible, and also allow for alphabetical sorting without very complicated methods.
Okay, what if I wanted it sorted in another order, such as lowest damage to highest damage? Btw thanks.
 
S

Supercoder

Guest
That I'm not sure about. The best I can think of is going through the entire array and reading each "weapon", translating the weapon types to their corresponding power using a list of constants, then recreating the list in a new order. Which would be a pain but should work.
 
T

trentallain

Guest
That I'm not sure about. The best I can think of is going through the entire array and reading each "weapon", translating the weapon types to their corresponding power using a list of constants, then recreating the list in a new order. Which would be a pain but should work.
Yeah. That would be a bit of a pain, there's probably an easier way somehow.
 
T

trentallain

Guest
I still don't know how to do the inventory haha. Especially with the different tabs.
 
N

Never Mind

Guest
DS Grids are a good potential option because they can be sorted easily by column values ds_grid_sort(index, column, ascending)
Arrays are great, but a lot of the other Data structures already have functions to help you sort them!
Each one also has it's own set of useful functions.

DS Grids can store real numbers or strings. A nice trick is to use ds_grid_write
to write a grid out as a string, then you can store it within another grid.
This would allow each cell in the main inventory grid to contain as many details about items as you want. You can also store lists inside lists, grids inside lists.. or any other combination.
There are many different ways to organize your data.
I have personally found uses for all the different Data Structures Game Maker has to offer.
Each one has a different usefulness to it. Eventually you will get comfortable with choosing the best one for your problem.

Maybe after you've experimented with the other data types, take a look at buffers.
 
Last edited by a moderator:
T

trentallain

Guest
DS Grids are a good potential option because it can be sorted easily by column values ds_grid_sort(index, column, ascending)
Arrays are great, but a lot of the other Data structures already have functions to help you sort them!
Each one also has it's own set of useful functions.

DS Grids can store real numbers or strings. A nice trick is to use ds_grid_write
to write a grid out as a string, then you can store it within another grid.
This could allow each cell in your main inventory grid to contain as many details about items as you want. You can also store lists into lists, or grids into lists.. or any other combination.
There are many different ways to organize your data.
I have personally found uses for all the different Data Structures Game Maker has to offer.
Each one has a different usefulness to it. Eventually you will get comfortable with choosing the best one for your problem.

Maybe after you've experimented with the other data types, take a look at buffers.
Okay that explains a fair bit, thank you!
 
N

Never Mind

Guest
point_in_rectangle(px, py, x1, y1, x2, y2) and draw_button(x1, y1, x2, y2, up)
are functions that maycome in handy with setting up an inventory as a clickable menu.
Also, the Accessors page shows you how to "access" data inside structures in a short and simple way (like an array).

Another useful thing is The "For" Statement it's handy for iterating through your data one item at a time.
An example would be drawing / listing the contents of an array
Code:
-----------------------------------------
for (var i = array_length_1d(a) - 1; i > -1; i--; )
{
draw_text(100, 100 + ( i * 32 ), a );
}
-----------------------------------------
 
Last edited by a moderator:
T

trentallain

Guest
point_in_rectangle(px, py, x1, y1, x2, y2) and draw_button(x1, y1, x2, y2, up)
are functions that maycome in handy with setting up an inventory as a clickable menu.
Also, the Accessors page shows you how to "access" data inside structures in a short and simple way (like an array).

Another useful thing is The "For" Statement it's handy for iterating through your data one item at a time.
An example would be drawing / listing the contents of an array
Code:
-----------------------------------------
for (var i = array_length_1d(a) - 1; i > -1; i--; )
{
draw_text(100, 100 + ( i * 32 ), a );
}
-----------------------------------------
Ok, this is what I have so far (its not much, but i'm a bit lost).
Code:
inventory_array = ds_grid_create(7,5) //7 columns, 5 rows

inventory_array[0,0] = "training sword" //item
inventroy_array[0,1] = 10               //gold value
inventory_array[0,2] = "sword"          //Item categorie

inventory_array[1,0] = "dagger"
inventory_array[1,1] = 8
inventory_array[1,2] = "sword"

inventory_array[2,0] = "purple shield"
inventory_array[2,1] = 20
inventory_array[2,2] = "sheild"

inventory_array[3,0] = "standard shield"
inventory_array[3,1] = 10
inventory_array[3,2] = "shield"
 
N

Never Mind

Guest
Good job! .. BUT
:oops: ds grids are not the same as arrays. Their syntax is different. I'm actually surprised that is not throwing you an error.
You can basically get rid of this line:
----------------------------------------------------------------------------------------------
inventory_array = ds_grid_create(7,5);
----------------------------------------------------------------------------------------------
.. and then you would be using inventory_array as a 2D Array


However, if you want to keep that line and actually use it as a grid..
Then inserting items into it would look like this:
----------------------------------------------------------------------------------------------
ds_grid_set( inventory_grid , 0, 0, "training sword");
ds_grid_set( inventory_grid , 0, 1, 10);
ds_grid_set( inventory_grid , 0, 2, "sword");
----------------------------------------------------------------------------------------------
or this ( accesor version )
----------------------------------------------------------------------------------------------
inventory_grid[# 0,0] = "training sword";
inventory_grid[# 0,1] = 10;
inventory_grid[# 0,2] = "sword";
----------------------------------------------------------------------------------------------

Hope that helps.

This might actually be a good time to show you how using scripts might make things easier.
You don't need one right now, but it's good to start thinking about how they can help you organize things.

SCRIPT scr_inventory_insert:
----------------------------------------------------------------------------------------------
///scr_inventory_insert( item, value, category)
var insert_position = array_height_2d( inventory_array );
inventory_array[ insert_position ,0] = argument0;
inventory_array[ insert_position ,1] = argument1;
inventory_array[ insert_position ,2] = argument2;
----------------------------------------------------------------------------------------------

If you use your own script similar to that you could replace the code you posted with:
----------------------------------------------------------------------------------------------
scr_inventory_insert( "training sword", 10, "sword" );
scr_inventory_insert( "dagger", 8 "sword" );
scr_inventory_insert( "purple shield", 20, "shield" );
scr_inventory_insert( "standard shield", 10, "shield" );
----------------------------------------------------------------------------------------------
 
T

trentallain

Guest
Good job! .. BUT
:oops: ds grids are not the same as arrays. Their syntax is different. I'm actually surprised that is not throwing you an error.
You can basically get rid of this line:
----------------------------------------------------------------------------------------------
inventory_array = ds_grid_create(7,5);
----------------------------------------------------------------------------------------------
.. and then you would be using inventory_array as a 2D Array


However, if you want to keep that line and actually use it as a grid..
Then inserting items into it would look like this:
----------------------------------------------------------------------------------------------
ds_grid_set( inventory_grid , 0, 0, "training sword");
ds_grid_set( inventory_grid , 0, 1, 10);
ds_grid_set( inventory_grid , 0, 2, "sword");
----------------------------------------------------------------------------------------------
or this ( accesor version )
----------------------------------------------------------------------------------------------
inventory_grid[# 0,0] = "training sword";
inventory_grid[# 0,1] = 10;
inventory_grid[# 0,2] = "sword";
----------------------------------------------------------------------------------------------

Hope that helps.

This might actually be a good time to show you how using scripts might make things easier.
You don't need one right now, but it's good to start thinking about how they can help you organize things.

SCRIPT scr_inventory_insert:
----------------------------------------------------------------------------------------------
///scr_inventory_insert( item, value, category)
var insert_position = array_height_2d( inventory_array );
inventory_array[ insert_position ,0] = argument0;
inventory_array[ insert_position ,1] = argument1;
inventory_array[ insert_position ,2] = argument2;
----------------------------------------------------------------------------------------------

If you use your own script similar to that you could replace the code you posted with:
----------------------------------------------------------------------------------------------
scr_inventory_insert( "training sword", 10, "sword" );
scr_inventory_insert( "dagger", 8 "sword" );
scr_inventory_insert( "purple shield", 20, "shield" );
scr_inventory_insert( "standard shield", 10, "shield" );
----------------------------------------------------------------------------------------------
Thank you so much! How would I use that to draw the items in the inventory? Also how would I know which item is selected?
 
F

Fishman1175

Guest
Lol 14X14 pixels is not going to be enough room. Here is some code that can draw an inventory table for you. It's customizable and extendable too.

Create event of some table object
Code:
inv_x = 32; //top left corner x
inv_y = 32; //top left corner y
h_cells = 7; //The number of cells across
v_cells = 5; //The number of cells down
cell_size = 14; //The width and the height of a table cell
border_padding = 4; //Padding between the border and the cells.
end_x = inv_x+(h_cells*(cell_size+border_padding));
end_y = inv_y+(v_cells*(cell_size+border_padding));

colour_background=c_black;
colour_border_fill=c_gray;
Draw Event of that table object:
Code:
draw_set_colour(colour_background);
draw_rectangle(inv_x,inv_y,end_x,end_y,false) //can substitute with draw_roundrect()
draw_set_colour(colour_border_fill);
var i;
//draw the vertical borders
for (i=0; i<=h_cells; i+=1)
{
    draw_rectangle(inv_x+i*(cell_size+border_padding),inv_y,inv_x+i*(cell_size+border_padding)+border_padding,end_y+border_padding,false)
}
//draw the horizontal borders
for (i=0; i<=v_cells; i+=1)
{
    draw_rectangle(inv_x,inv_y+i*(cell_size+border_padding),end_x+border_padding,inv_y+i*(cell_size+border_padding)+border_padding,false)
}
draw_set_colour(c_white);
Check the attached image to see what it looks like.

My personal recommendation for an inventory is use a 2d Array with defined constants. To define a constant, go to the Resources tab, and then click on "define macros". You can add key-value pairs there and use them in your game. You type the name of the constant in your code, and GM will put the value in there at compile time. It makes it easier to read. For example.

Define the constants
ITEM: 0
VALUE: 1
CATAGORY: 2
SUBCATAGORY:3

Now, use a 2d array where:
-The first index is the location of the item in the inventory.
-The second index is the property pertaining to that item.

inventory_grid[0,ITEM]="training sword";
inventory_grid[1,VALUE]=10;
inventory_grid[2,CATAGORY]="sword";
inventory_grid[3,SUBCATAGORY]="swords";

You can also define constants for your categories and sub-catagories.
 

Attachments

Last edited by a moderator:
T

trentallain

Guest
Lol 14X14 pixels is not going to be enough room. Here is some code that can draw an inventory table for you. It's customizable and extendable too.

Create event of some table object
Code:
inv_x = 32; //top left corner x
inv_y = 32; //top left corner y
h_cells = 7; //The number of cells across
v_cells = 5; //The number of cells down
cell_size = 14; //The width and the height of a table cell
border_padding = 4; //Padding between the border and the cells.
end_x = inv_x+(h_cells*(cell_size+border_padding));
end_y = inv_y+(v_cells*(cell_size+border_padding));

colour_background=c_black;
colour_border_fill=c_gray;
Draw Event of that table object:
Code:
draw_set_colour(colour_background);
draw_rectangle(inv_x,inv_y,end_x,end_y,false) //can substitute with draw_roundrect()
draw_set_colour(colour_border_fill);
var i;
//draw the vertical borders
for (i=0; i<=h_cells; i+=1)
{
    draw_rectangle(inv_x+i*(cell_size+border_padding),inv_y,inv_x+i*(cell_size+border_padding)+border_padding,end_y+border_padding,false)
}
//draw the horizontal borders
for (i=0; i<=v_cells; i+=1)
{
    draw_rectangle(inv_x,inv_y+i*(cell_size+border_padding),end_x+border_padding,inv_y+i*(cell_size+border_padding)+border_padding,false)
}
draw_set_colour(c_white);
Check the attached image to see what it looks like.

I can help with more later if you'd like
Thank you, but I have actually made the inventory grid as a sprite, since the inventory is more detailed than in my diagram. What I was more looking for was how I would get the inventory to work code wise, if you could help with that. (Also 14x14 does work for each slot because its a pixel art game)
 
F

Fishman1175

Guest
Oh, my bad. I guess I read too fast. To be honest, re-reading your post, I have no idea what you actually want your inventory to do.
Like, if you need filtering, sorting, and mutability, then that changes how you should architect your implementation. If you don't, then we can make it simpler. I like 2D arrays because they are low level and don't limit you in what you want to do in any way. They are more challenging to work with however.
 
T

trentallain

Guest
Okay, so basically, it has 5 main tabs/categories which can be used to distinguish between the items types e.g. weapons and amour.

It then has more specific categories, aka sub categories (e.g. if you were in the weapon tab there would be sub categories for swords and 2 handed weapons).

The main tab and the sub tabs will have an "all" category that shows every item you have (and for sub tabs, every item in that main tab category)

All item slots in the inventory will be blank to begin with. As you acquire items, the slots will fill up in order of most expensive, to least expensive.

An item selector would show what item you have selected in the inventory, and display a portrait of that item, and allow it to be equipped to either right or left hand.

The area with the inventory slots is 7 x 5 and each slot is 14 x 14.

What i need help with is understanding and knowing what code I need to achieve this. (Thank you to everyone helping)

I'm sorry if this is too confusing. :confused: All help is appreciated. :)
 

Attachments

F

Fishman1175

Guest
Ok, that's great info. I promise not to give you any more useless code ;). Let's roll with Never Mind's idea about using scripts on top of a 2D array. This means the 2D array will store all of the information, but we interact with it only by calling scripts.

We will have two arrays. One will be permanent and will reflect the current state of the entire inventory. The second will be temporary and will be generated from scripts. This temporary array will have filtered results that we draw to the screen. It changes when the filtering criteria (i.e. the tabs) change.

Judging from your post, we need scripts to
  • insert new items into the inventory
  • delete items out of the inventory
  • Find items in the inventory and modify them
  • Filter the list to only show categories/subcategories
  • Order the list from most expensive to least expensive. (Essentially, this is numeric ordering based on the 'value' property of an item)
  • Draw the items on screen to reflect what is actually in your inventory
  • Handle mouse clicks to tell you which item you have selected in the inventory
Anything I missed?

Now, the scripts themselves just reside in the sidebar of GM. Where do you want the scripts to be called from? Do you have an inventory object where we can handle these operations (and call the code)? I want to make sure you're cool with this before I start writing any example/template code.
 
T

trentallain

Guest
Ok, that's great info. I promise not to give you any more useless code ;). Let's roll with Never Mind's idea about using scripts on top of a 2D array. This means the 2D array will store all of the information, but we interact with it only by calling scripts.

We will have two arrays. One will be permanent and will reflect the current state of the entire inventory. The second will be temporary and will be generated from scripts. This temporary array will have filtered results that we draw to the screen. It changes when the filtering criteria (i.e. the tabs) change.

Judging from your post, we need scripts to
  • insert new items into the inventory
  • delete items out of the inventory
  • Find items in the inventory and modify them
  • Filter the list to only show categories/subcategories
  • Order the list from most expensive to least expensive. (Essentially, this is numeric ordering based on the 'value' property of an item)
  • Draw the items on screen to reflect what is actually in your inventory
  • Handle mouse clicks to tell you which item you have selected in the inventory
Anything I missed?

Now, the scripts themselves just reside in the sidebar of GM. Where do you want the scripts to be called from? Do you have an inventory object where we can handle these operations (and call the code)? I want to make sure you're cool with this before I start writing any code.
YES this is exactly what I needed!!!
I have an object called obj_follow_player which basically draws all the HUD and inventory elements which keeps the amount of objects needed to a minimum, so the scripts can be called from there. Thank you so much!! (You can message me directly if you need to)
 
F

Fishman1175

Guest
Cool.

I think we should make the inventory a global variable. It's not the best solution design wise, but I think it's an excellent choice here because this is relatively new to you and using a global variable will make writing the code and understanding the process easier.

You can use the create event of your player, or some other initializing object (like, in the main menu or when the game starts up).
Code:
global.inventory=0; //initialize the array
I just assigned it to zero here. It's not actually an array yet. If the variable isn't an array, then the inventory is completely empty. We will use the function
Code:
is_array(global.inventory);
quite a lot to make sure we don't access the inventory when it doesn't exist (thus, crashing your game).

First up, inserting into the inventory is easy. Create a script similar to inventory_insert
Code:
///inventory_insert(item_id,name,value,description,etc..)
//Create as many parameters as you need properties for a single item.

//now the code. First check if we have an inventory.
var new_index; //Where in the inventory we will put the new item
if is_array(global.inventory)
{
    //we already have an array, so just add onto the end
    new_index = array_height_2d(global.inventory); //the end
}
else
{
    //No array yet. We must assign the first index;
    new_index = 0;
}

for (var i=0; i<argument_count; i+=1)
{
    global.inventory[new_index,i]=argument[i]; //A fast way to copy all values from the parameters directly into the inventory
}

return new_index; //Not necessary, but it might be helpful to know the index of where it was put in the future.
It's a very good idea to give items an item ID because it offers a search path that is easy, although it isn't necessary.

So here we can see the basic structure of the inventory. Hopefully this can get you started for a little bit. When I get some more time later, I can show you how to start the scripts where you filter by category and handle mouse clicks.
 
Last edited by a moderator:
T

trentallain

Guest
Cool.

I think we should make the inventory a global variable. It's not the best solution design wise, but I think it's an excellent choice here because this is relatively new to you and using a global variable will make writing the code and understanding the process easier.

You can use the create event of your player, or some other initializing object (like, in the main menu or when the game starts up).
Code:
global.inventory=0; //initialize the array
I just assigned it to zero here. It's not actually an array yet. If the variable isn't an array, then the inventory is completely empty. We will use the function
Code:
is_array(global.inventory);
quite a lot to make sure we don't access the inventory when it doesn't exist (thus, crashing your game).

First up, inserting into the inventory is easy. Create a script similar to inventory_insert
Code:
///inventory_insert(item_id,name,value,description,etc..)
//Create as many parameters as you need properties for a single item.

//now the code. First check if we have an inventory.
var new_index; //Where in the inventory we will put the new item
if is_array(global.inventory)
{
    //we already have an array, so just add onto the end
    new_index = array_height_2d(global.inventory); //the end
}
else
{
    //No array yet. We must assign the first index;
    new_index = 0;
}

for (var i=0; i<argument_count; i+=1)
{
    global.inventory[new_index,i]=argument[i]; //A fast way to copy all values from the parameters directly into the inventory
}

return new_index; //Not necessary, but it might be helpful to know the index of where it was put in the future.
It's a very good idea to give items an item ID because it offers a search path that is easy, although it isn't necessary.

So here we can see the basic structure of the inventory. Hopefully this can get you started for a little bit. When I get some more time later, I can show you how to start the scripts where you filter by category and handle mouse clicks.
Okay you're awesome. What would the "item ID" look like? Is it numbers or a string like "magic sword"?
 
F

Fishman1175

Guest
It could be either, as long as you stay consistent (meaning you only use numbers or you only use strings). Just make sure to always give your items a unique ID.
 
T

trentallain

Guest
It could be either, as long as you stay consistent (meaning you only use numbers or you only use strings). Just make sure to always give your items a unique ID.
Ok so I would probably just leave it as the name of the item then.
 
N

Never Mind

Guest
Ok so I would probably just leave it as the name of the item then.
^That works as long as you can't pick up more than one of each item. If you did set them up with some kind of identifier, then you could pick up two "daggers" that have their own individual levels of sharpness and make them wear down with use, or any other unique attributes you want.

I think the main problem your running into is transitioning between data and the graphical interface. You should honestly start a side experiment project (or three) where you can mess around visually presenting / drawing data that you've put into arrays, grids, or lists.

The first thing I would do:
  1. hard code a list of string names
  2. in a Draw Event try to loop through the list and draw out all the names somewhere onto the screen.
  3. test adding a new name to the list and make sure it gets drawn automatically
That is the very first basic step to drawing an inventory. Draw a list of names!..
Once you have that going you can experiment with something like:
compare where names are drawn to the mouse position. When you click the mouse attempt to delete the closest name from the list.
After you've done that I think you'll have a much better foundation to jump into making an inventory to fit your needs. It should answer a lot of your questions.

If Fishman1175 offers to walk you through setting up yours you should be very grateful. That sort of thing can't really be expected for this scope of project.



@Fishman1175 Hey sorry to bug you but it's my first time seeing you around.. I was just curious if you happen to have any games or tutorials you've made that I can check out! You seem to be great at helping people and have a strong work drive. I bet anything your working on is cool :)
 
Last edited by a moderator:
T

trentallain

Guest
^That works as long as you can't pick up more than one of each item. If you did set them up with some kind of identifier, then you could pick up two "daggers" that have their own individual levels of sharpness that wear down with use, or any other unique attributes.

I think the main problem your running into is transitioning between data and the graphical interface. You should honestly start a side experiment project (or three) where you can mess around visually presenting / drawing data that you've put into arrays, grids, or lists.

The first thing I would do:
  1. hard code a list of string names
  2. in a Draw Event try to loop through the list and draw out all the names somewhere onto the screen.
  3. test adding a new name to the list and make sure it gets drawn automatically
That is the very first basic step to drawing an inventory. Draw a list of names!..
Once you have that going you can experiment with something like:


After you've done that I think you'll have a much better foundation to jump into making an inventory fit for your needs. I think it will answer a lot of your questions.

If Fishman1175 offers to walk you through setting yours up you should be very grateful. That sort of thing can't really be expected for this scope of project.



@Fishman1175 Hey sorry to bug you but it's my first time seeing you around.. I was just curious if you happen to have any games or tutorials you've made that I can check out! You seem to be great at helping people and have a strong work drive. I bet anything your working on is cool :)
So if they had an identifier that was a number, some items like potions could stack? And that would let there be a legendary sword or something that couldn't stack?
 
N

Never Mind

Guest
Exactly.
If you have a unique tag on each item you can control whether each thing is a collect-able, rechargeable , replenishing, adding, stacking, poisoning, reforging, character... Haha:rolleyes:

Thanks for liking my posts by the way!
 
T

trentallain

Guest
Exactly.
If you have a unique tag on each item you can control whether each thing is a collect-able, rechargeable , replenishing, adding, stacking, poisoning, reforging, character... Haha:rolleyes:

Thanks for liking my posts by the way!
No problem :D. Is the ID unique for that item type... or for that actual specific item that you acquired, so just say the first health potion u pick up is different to the next one you pick up?
 
T

trentallain

Guest
Cool.

I think we should make the inventory a global variable. It's not the best solution design wise, but I think it's an excellent choice here because this is relatively new to you and using a global variable will make writing the code and understanding the process easier.

You can use the create event of your player, or some other initializing object (like, in the main menu or when the game starts up).
Code:
global.inventory=0; //initialize the array
I just assigned it to zero here. It's not actually an array yet. If the variable isn't an array, then the inventory is completely empty. We will use the function
Code:
is_array(global.inventory);
quite a lot to make sure we don't access the inventory when it doesn't exist (thus, crashing your game).

First up, inserting into the inventory is easy. Create a script similar to inventory_insert
Code:
///inventory_insert(item_id,name,value,description,etc..)
//Create as many parameters as you need properties for a single item.

//now the code. First check if we have an inventory.
var new_index; //Where in the inventory we will put the new item
if is_array(global.inventory)
{
    //we already have an array, so just add onto the end
    new_index = array_height_2d(global.inventory); //the end
}
else
{
    //No array yet. We must assign the first index;
    new_index = 0;
}

for (var i=0; i<argument_count; i+=1)
{
    global.inventory[new_index,i]=argument[i]; //A fast way to copy all values from the parameters directly into the inventory
}

return new_index; //Not necessary, but it might be helpful to know the index of where it was put in the future.
It's a very good idea to give items an item ID because it offers a search path that is easy, although it isn't necessary.

So here we can see the basic structure of the inventory. Hopefully this can get you started for a little bit. When I get some more time later, I can show you how to start the scripts where you filter by category and handle mouse clicks.
With the mouse clicks, are they within separate objects or just one script that is only called once?
 
F

Fishman1175

Guest
Here are two possible ways you could go about it.

Option 1 (clunky): Seperate objects

Each cell has a square object created in it. The object would need to have a sprite the same size as an inventory slot, so it covers the whole individual slot. The object must also be checked as visible. (You can give the sprite an alpha of 1 though). If you do this, it will ensure that the object's "Mouse -> Left Pressed" event gets fired when it is clicked. Now, these objects are created using a loop and given an index that corresponds to which item in the inventory they represent.

Some example code:
Code:
cell_size=14;
w_cells=7;
h_cells=5;
inventory_x = 32; //This needs to be the top left corner of your inventory sprite. The idea is to create boxes that align with where the inventory sprite is
inventory_y = 32;

for (var i=0; i<h_cells; i+=1) //control the row
{
    for (var u=0; u<w_cells; u+=1) //control the column
    {
        var new_instance;
        new_instance=instance_create(inventory_x + (u*cell_size),inventory_y + (i*cell_size),obj_inventory_click_placeholder);
        new_instance.cell_number=(i*w_cells)+u; //The created box gets a number. This is what inventory slot the box represents.
    }
}
This is just a rough draft, but the basic idea is that you click on a box instance and the Left Pressed event fires. In that event, you read the variable cell_number to see what number box you clicked. The number box you clicked is the index of the inventory array you need to read.


Option 2 (coolio): Using mouse co-ordinates.
I like this option because it doesn't involve any extra sprites, objects, or mumbo jumbo.
The basic idea is that we define where the inventory sprite is drawn. Then, the moment the global mouse left pressed event fires, we compare the co-ordinates of the mouse to the co-ordinates of the inventory sprite! We know that each row has 7 squares, so every row down the mouse is, we have to add 7 the number that we think they are clicking. Example code:
Code:
inventory_x = obj_inventory.x //I'm just making this up, replace with the actual co-ordinates of the top left of your inventory sprite
inventory_y = obj_inventory.y

cell_size=14;
w_cells=7;
h_cells=5;

//'div' keyword is very important here. It is integer division
vertical_number=(((mouse_y-inventory_y) div cell_size) * w_cells);
if vertical_number < 0 then return -1; //They clicked outside the inventory. Use -1 to indicate invalid selection.
if vertical_number >= h_cells*w_cells then return -1;

horizontal_number=((mouse_x-inventory_x) div cell_size);
if horizontal_number < 0 then return -1; //again, out of bounds selection
if horizontal_number >= w_cells then return -1;

box_clicked=vertical_number + horizontal_number;
return box_clicked;

//box clicked is the index in the inventory array we are looking for
The attached image (sorry it is so rough) shows the visual inventory box with the number of the inventory index that it should return when it is clicked.

If you have questions or get stuck, feel free to ask. Oh, and there are other ways this can be accomplished too (like everything in programming), these are just some suggestions.
 

Attachments

T

trentallain

Guest
Here are two possible ways you could go about it.

Option 1 (clunky): Seperate objects

Each cell has a square object created in it. The object would need to have a sprite the same size as an inventory slot, so it covers the whole individual slot. The object must also be checked as visible. (You can give the sprite an alpha of 1 though). If you do this, it will ensure that the object's "Mouse -> Left Pressed" event gets fired when it is clicked. Now, these objects are created using a loop and given an index that corresponds to which item in the inventory they represent.

Some example code:
Code:
cell_size=14;
w_cells=7;
h_cells=5;
inventory_x = 32; //This needs to be the top left corner of your inventory sprite. The idea is to create boxes that align with where the inventory sprite is
inventory_y = 32;

for (var i=0; i<h_cells; i+=1) //control the row
{
    for (var u=0; u<w_cells; u+=1) //control the column
    {
        var new_instance;
        new_instance=instance_create(inventory_x + (u*cell_size),inventory_y + (i*cell_size),obj_inventory_click_placeholder);
        new_instance.cell_number=(i*w_cells)+u; //The created box gets a number. This is what inventory slot the box represents.
    }
}
This is just a rough draft, but the basic idea is that you click on a box instance and the Left Pressed event fires. In that event, you read the variable cell_number to see what number box you clicked. The number box you clicked is the index of the inventory array you need to read.


Option 2 (coolio): Using mouse co-ordinates.
I like this option because it doesn't involve any extra sprites, objects, or mumbo jumbo.
The basic idea is that we define where the inventory sprite is drawn. Then, the moment the global mouse left pressed event fires, we compare the co-ordinates of the mouse to the co-ordinates of the inventory sprite! We know that each row has 7 squares, so every row down the mouse is, we have to add 7 the number that we think they are clicking. Example code:
Code:
inventory_x = obj_inventory.x //I'm just making this up, replace with the actual co-ordinates of the top left of your inventory sprite
inventory_y = obj_inventory.y

cell_size=14;
w_cells=7;
h_cells=5;

//'div' keyword is very important here. It is integer division
vertical_number=(((mouse_y-inventory_y) div cell_size) * w_cells);
if vertical_number < 0 then return -1; //They clicked outside the inventory. Use -1 to indicate invalid selection.
if vertical_number >= h_cells*w_cells then return -1;

horizontal_number=((mouse_x-inventory_x) div cell_size);
if horizontal_number < 0 then return -1; //again, out of bounds selection
if horizontal_number >= w_cells then return -1;

box_clicked=vertical_number + horizontal_number;
return box_clicked;

//box clicked is the index in the inventory array we are looking for
The attached image (sorry it is so rough) shows the visual inventory box with the number of the inventory index that it should return when it is clicked.

If you have questions or get stuck, feel free to ask. Oh, and there are other ways this can be accomplished too (like everything in programming), these are just some suggestions.
Thank you again so much! How do I draw the items in each box?
 
F

Fishman1175

Guest
First, I think this is great advice. It gives you an area for building "proof-of-concept" logic without the fear that you'll screw up the code in your real game.

I think the main problem your running into is transitioning between data and the graphical interface. You should honestly start a side experiment project (or three) where you can mess around visually presenting / drawing data that you've put into arrays, grids, or lists.

The first thing I would do:
  1. hard code a list of string names
  2. in a Draw Event try to loop through the list and draw out all the names somewhere onto the screen.
  3. test adding a new name to the list and make sure it gets drawn automatically
That is the very first basic step to drawing an inventory. Draw a list of names!..
Once you have that going you can experiment with something like:

After you've done that I think you'll have a much better foundation to jump into making an inventory to fit your needs. It should answer a lot of your questions.
I think Never Mind is right about the trouble with transitioning from data to GUI (graphical user interface).

Here are some tips I can give for your specific implementation.

For loops are the go-to construct for working with data structures. Use a for loop to iterate over each item in your inventory.
Code:
for(var i=0; i<array_height_2d(global.inventory); i+=1)
{
    //use the variable 'i' as the index in global.inventory to retrieve one item at a time. Remember, it's a 2D array so you have to provide another number for the property of the item you want to retrieve
}
You'll need some kind of mapping between an item (in the most abstract sense of your game) and a sprite. Usually this is done automatically by Game Maker, right? You create an object and assign it a sprite. You can use this approach, or you can use a data structure called a DS_Map. Here is a link to the documentation.

You create a map between an item's item ID and the sprite index of the sprite you want to appear on screen. The sprite index of a sprite is the same as the name of the sprite (the name that you give it in the GM sidebar). Now you read the item ID from global.inventory using the index provided by the mouse clicking code. You use something like:
Code:
ds_map_find_value(global.inventory_map, global.inventory[clicked_index,0]) //instead of 0, use the index where item IDs are stored.
You would need to create a populate this map somewhere at the beginning of your game. Every single item in your game needs to be in it, and the ID's must match exactly to the IDs of your items (that you decided on).
 
T

trentallain

Guest
First, I think this is great advice. It gives you an area for building "proof-of-concept" logic without the fear that you'll screw up the code in your real game.


I think Never Mind is right about the trouble with transitioning from data to GUI (graphical user interface).

Here are some tips I can give for your specific implementation.

For loops are the go-to construct for working with data structures. Use a for loop to iterate over each item in your inventory.
Code:
for(var i=0; i<array_height_2d(global.inventory); i+=1)
{
    //use the variable 'i' as the index in global.inventory to retrieve one item at a time. Remember, it's a 2D array so you have to provide another number for the property of the item you want to retrieve
}
You'll need some kind of mapping between an item (in the most abstract sense of your game) and a sprite. Usually this is done automatically by Game Maker, right? You create an object and assign it a sprite. You can use this approach, or you can use a data structure called a DS_Map. Here is a link to the documentation.

You create a map between an item's item ID and the sprite index of the sprite you want to appear on screen. The sprite index of a sprite is the same as the name of the sprite (the name that you give it in the GM sidebar). Now you read the item ID from global.inventory using the index provided by the mouse clicking code. You use something like:
Code:
ds_map_find_value(global.inventory_map, global.inventory[clicked_index,0]) //instead of 0, use the index where item IDs are stored.
You would need to create a populate this map somewhere at the beginning of your game. Every single item in your game needs to be in it, and the ID's must match exactly to the IDs of your items (that you decided on).
Okay. I'm using your mouse co-ordinates option rather than the objects, to save space and time.
Here are all my current items:
scr_inventory_insert( "training sword", 10, "sword", 0 );
scr_inventory_insert( "dagger", 8, "sword", 1 );
scr_inventory_insert( "purple shield", 20, "shield", 2 );
scr_inventory_insert( "standard shield", 10, "shield", 3 );
Is that alright for the item ID, numbers at the end?
 
T

trentallain

Guest
First, I think this is great advice. It gives you an area for building "proof-of-concept" logic without the fear that you'll screw up the code in your real game.


I think Never Mind is right about the trouble with transitioning from data to GUI (graphical user interface).

Here are some tips I can give for your specific implementation.

For loops are the go-to construct for working with data structures. Use a for loop to iterate over each item in your inventory.
Code:
for(var i=0; i<array_height_2d(global.inventory); i+=1)
{
    //use the variable 'i' as the index in global.inventory to retrieve one item at a time. Remember, it's a 2D array so you have to provide another number for the property of the item you want to retrieve
}
You'll need some kind of mapping between an item (in the most abstract sense of your game) and a sprite. Usually this is done automatically by Game Maker, right? You create an object and assign it a sprite. You can use this approach, or you can use a data structure called a DS_Map. Here is a link to the documentation.

You create a map between an item's item ID and the sprite index of the sprite you want to appear on screen. The sprite index of a sprite is the same as the name of the sprite (the name that you give it in the GM sidebar). Now you read the item ID from global.inventory using the index provided by the mouse clicking code. You use something like:
Code:
ds_map_find_value(global.inventory_map, global.inventory[clicked_index,0]) //instead of 0, use the index where item IDs are stored.
You would need to create a populate this map somewhere at the beginning of your game. Every single item in your game needs to be in it, and the ID's must match exactly to the IDs of your items (that you decided on).
Could you give an example please? Thank you.
 
T

trentallain

Guest
Sure thing! But, an example of what precisely?
Maybe two items that when you change their category in code, will change their category in the inventory. (I know this is a big ask, but I am willing to add you to the credits of the game).
 
F

Fishman1175

Guest
No need for credit :)

Now, I'm still not 100% what you want an example of. "Change their category in code" to me means something like:
Code:
//change the first item's category. Based on your insert script, I'm guessing that index 2 is the category index.
global.inventory[0,2]="staff";
And then "will change their category in the inventory" is the part I don't quite understand. The inventory is the array that was changed (right above this line). Perhaps you want it so that depending on what tab you click, the graphical representation of the inventory changes?

If you can provide more detail about what you would like to happen, i'll get back to you.

P.S.
Happy New Year! Don't give up! :D
 
T

trentallain

Guest
No need for credit :)

Now, I'm still not 100% what you want an example of. "Change their category in code" to me means something like:
Code:
//change the first item's category. Based on your insert script, I'm guessing that index 2 is the category index.
global.inventory[0,2]="staff";
And then "will change their category in the inventory" is the part I don't quite understand. The inventory is the array that was changed (right above this line). Perhaps you want it so that depending on what tab you click, the graphical representation of the inventory changes?

If you can provide more detail about what you would like to happen, i'll get back to you.

P.S.
Happy New Year! Don't give up! :D
Yeah I think that's what I meant. So the "sword" items only appear in the inventory grid, if it's that item type selected. If you could make an example of that I think I should be set. One other thing though, how do I make it so I can equip the items? Because I have the left/right hands that you can equip to but I'm pretty sure currently the code only shows the array. Also I am still a bit stuck in the transition from the code to the graphics. Thanks and happy new year to you too.

PS: I tried practising the drawing of the items using:
for(var i=0; i<array_height_2d(global.inventory_array); i+=1)
{
draw_sprite(global.inventory_array[i,4],0,view_xview+10,view_yview+i*16)
}
//Where [i,4] is the sprite name
 
Last edited:
F

Fishman1175

Guest
First, to get only the "sword" items, you will need a script that filters your global.inventory_array. One way to do it is to
  • Create a new empty array
  • Loop through global.inventory_array
  • For each inventory item in the inventory array, check if the category equals something (like "sword").
  • If it is equal, we have a match. Copy that entire inventory item into the new inventory array.
Here is a code sample:
Code:
///filter_inventory_by_category(category)
var filter_text=argument0; //pass in what you want to filter
var filtered_inventory=0; //initialize our filtered inventory. Note that this is a script local array.
//That means that as soon as this script ends, filtered_inventory no longer exists.


for (var i=0; i<array_height_2d(global.inventory_array); i+=1)
{
    if (global.inventory_array[i,2] == string(filter_text)) //matching
    {
        //determine the next open index in filtered_inventory
        var next_index;
        if is_array(filtered_inventory)
        {
            next_index=array_height_2d(filtered_inventory);
        }
        else
        {
            next_index=0;
        }
       
        //copy all parts of the item that matched (at the i index) into filtered_inventory
        for (var u=0; u<array_length_2d(global.inventory_array,i); u+=1)
        {
            filtered_inventory[next_index,u]=global.inventory_array[i,u]
        }
    }  
}

//at the end, return our filtered array
return filtered_inventory;
You would call the script like:
Code:
my_filtered_inventory=filter_inventory_by_category("sword");
Now we get to the draw code. Let's work on what you've tried. Just for clarity, this is what you posted above:
Code:
for(var i=0; i<array_height_2d(global.inventory_array); i+=1)
 {
 draw_sprite(global.inventory_array[i,4],0,view_xview+10,view_yview+i*16)
 }
 //Where [i,4] is the sprite name
We no longer want to draw what is in global.inventory_array. Now we want to draw what is in my_filtered_inventory.
You put the X co-ordinate as view_xview+10. The only variable in there is view_xview which won't change as the loop runs. 10 won't change either. You could only make the inventory draw in a vertical line this way, which I don't think you want. So you have to also make the X point dependent on 'i' like you did with the Y point.

You can use the same idea that the mouse clicking code uses but reverse the logic to know where to draw your sprites. You have to take into account how many columns your visual inventory has before the next row starts. It's going to be tough, but you'll need to extensively try and possibly fail a number of times before you understand it. The important thing is to first think about precisely step-by-step what you want your code to do before writing it.
 
T

trentallain

Guest
First, to get only the "sword" items, you will need a script that filters your global.inventory_array. One way to do it is to
  • Create a new empty array
  • Loop through global.inventory_array
  • For each inventory item in the inventory array, check if the category equals something (like "sword").
  • If it is equal, we have a match. Copy that entire inventory item into the new inventory array.
Here is a code sample:
Code:
///filter_inventory_by_category(category)
var filter_text=argument0; //pass in what you want to filter
var filtered_inventory=0; //initialize our filtered inventory. Note that this is a script local array.
//That means that as soon as this script ends, filtered_inventory no longer exists.


for (var i=0; i<array_height_2d(global.inventory_array); i+=1)
{
    if (global.inventory_array[i,2] == string(filter_text)) //matching
    {
        //determine the next open index in filtered_inventory
        var next_index;
        if is_array(filtered_inventory)
        {
            next_index=array_height_2d(filtered_inventory);
        }
        else
        {
            next_index=0;
        }
     
        //copy all parts of the item that matched (at the i index) into filtered_inventory
        for (var u=0; u<array_length_2d(global.inventory_array,i); u+=1)
        {
            filtered_inventory[next_index,u]=global.inventory_array[i,u]
        }
    }
}

//at the end, return our filtered array
return filtered_inventory;
You would call the script like:
Code:
my_filtered_inventory=filter_inventory_by_category("sword");
Now we get to the draw code. Let's work on what you've tried. Just for clarity, this is what you posted above:
Code:
for(var i=0; i<array_height_2d(global.inventory_array); i+=1)
 {
 draw_sprite(global.inventory_array[i,4],0,view_xview+10,view_yview+i*16)
 }
 //Where [i,4] is the sprite name
We no longer want to draw what is in global.inventory_array. Now we want to draw what is in my_filtered_inventory.
You put the X co-ordinate as view_xview+10. The only variable in there is view_xview which won't change as the loop runs. 10 won't change either. You could only make the inventory draw in a vertical line this way, which I don't think you want. So you have to also make the X point dependent on 'i' like you did with the Y point.

You can use the same idea that the mouse clicking code uses but reverse the logic to know where to draw your sprites. You have to take into account how many columns your visual inventory has before the next row starts. It's going to be tough, but you'll need to extensively try and possibly fail a number of times before you understand it. The important thing is to first think about precisely step-by-step what you want your code to do before writing it.
It works!! Thank you so much!!! Just two more things though, how do I make the items get drawn horizontal and then when it gets to the end, it goes down to the next line using the for loop? And how do I remove items and make some items stack (like items such as potions are in the same slot and have a number indicator)?
Thanks again!
 
Last edited:
T

trentallain

Guest
Oh and also if an item is removed, will all the items shift up?
 
F

Fishman1175

Guest
You can make items stack by just using a quantity index in your inventory array.

You would have to implement the shifting yourself. Find the index you want to remove, and then for each item after it, copy the contents to the cell before it.

About your other question, you can know when the next line is because it is after every 7th cell. Use integer division. Index 5 div 7(the number of columns in your graphical representation) equals 0. You are on the first row. 10 div 7 equals 1. You are on the second row, and so on. 10 % 7 = 3. 3 is the remainder here, so we are on the third column. Use integer division to get the row, and modulo to get the column.
 
T

trentallain

Guest
You can make items stack by just using a quantity index in your inventory array.

You would have to implement the shifting yourself. Find the index you want to remove, and then for each item after it, copy the contents to the cell before it.

About your other question, you can know when the next line is because it is after every 7th cell. Use integer division. Index 5 div 7(the number of columns in your graphical representation) equals 0. You are on the first row. 10 div 7 equals 1. You are on the second row, and so on. 10 % 7 = 3. 3 is the remainder here, so we are on the third column. Use integer division to get the row, and modulo to get the column.
Ok thanks and how do I find the index of the item and remove it.

I think that's all I need so thank you so much, you are so helpful!
 
T

trentallain

Guest
You can make items stack by just using a quantity index in your inventory array.

You would have to implement the shifting yourself. Find the index you want to remove, and then for each item after it, copy the contents to the cell before it.

About your other question, you can know when the next line is because it is after every 7th cell. Use integer division. Index 5 div 7(the number of columns in your graphical representation) equals 0. You are on the first row. 10 div 7 equals 1. You are on the second row, and so on. 10 % 7 = 3. 3 is the remainder here, so we are on the third column. Use integer division to get the row, and modulo to get the column.
How would I make it so I can equip items by dragging them, such as helmets into a head slot, pants into a leg slot, and any item except armour into a left and right hand slot? (Where the items in the slots can be read and the current item/stats will adjust accordingly).
 
Last edited:
Top