Windows Beginner looking for help with inventory system for P&C game

C

Christian Schütze

Guest
Hello Everyone

Long story short i recently together with a friend decided to create a Point & Click adventure game for younger people/kids.
My friend, Søren, are quite skilled in graphics and works on a daily basis at one of the biggest newspapers
in our country as a designer. Me myself, Christian, im just a regular cook, preparing meals for sick children at a hospital.

We are not in the world able to create a game with advanced codes, a lot of viable and so on, which is why we want to focus mostly on graphics, dialogues, history, music/sounds and so on. And this is why i am asking for help now. And yes, i already did a few tutorials with no success so far.

I am looking for a easy way to get a inventory up and running. We are considering to have the same inventory design at the bottom of each room sprite instead of creating an actually inventory system.
In the example below Søren just designed a beautiful room and as you can see i painted 8 boxes at the bottom.

I am using DnD at the moment and adding some GML when i find useful codes along the way as i am searching this forum for answers.

I am not necessarily looking for perfect and super detailed answers as i know i am a total beginner and that it can be difficult to answer with the information's given. It would be great though, but i would be more than happy to get a few tips on how i should go further on with this kind of inventory.


Question: What do you think is the easiest way to make the following happen:

1. When you mouse_left click on an object you can drag it as long as you hold mouse_left pressed.
If you stop pressing it will go back to its start location unless you drag it all the way to one of the 8 boxes. Then it will take place in the box chosen.

2. Once an object has taken place inside of a box it cant be placed anywhere but where we want it to collide with something else to make x_event happen.

3. If you have 2 or more objects in the boxes then you can switch their places by clicking and drag one object and release it on top of another. You can also drag an object to any empty box.

4. When going from room to room, all objects placed in the boxes will follow until they are dragged to some event.

I have attached a link to a short video to show what we are capable of right now. As you can see we are at a very beginner stage.
https://streamable.com/ofy0r

And here is the picture where i added 8 boxes as an example. At some point we will design a proper bar which looks like a sack or something cool.
Eksempel.jpg

If you took your time to read all of this, then i´d like to thank you and wish you happy gamemaking

Greetings from Christian, Denmark
 

Nidoking

Member
I think the fundamental idea to start with is to give each item an xHome and yHome value to show where they're supposed to be, and a dragging boolean value to let them know when they're being dragged, at which point they'll follow the mouse cursor. You'd set dragging to true in the mouse button down event. Then, in the mouse button release event, you'd check to see whether the item is in a valid position, in which case you set xHome and yHome to that new position. You can set the items to be persistent when they're placed in the inventory. That should cover enough to get you started.
 
I think you're going to have to learn some coding here. I have created an example in GMS1 which you can download it a zipped project folder here: https://www.dropbox.com/s/g37ol4ub05ss120/Inventory_v2.gmx.zip?dl=0

This is the project and the below code in action:


The code is heavily commented and if you don't want to download it, I've pasted it below. The project has only 4 sprites (3 different icons each with only 1 sub-image 1 "slot" image with 2 sub-images) and 1 inventory object (plus a stand in object for something that you could collide with called obj_collide - this would be the object you would drag the items onto to use them).

The inventory object only has the following codes used in the create and draw events:

Create:
Code:
//create "database" of items with info about item name and the icon
item_database[0,0]="Gear"
item_database[0,1]=spr_icon_gear

item_database[1,0]="Spool"
item_database[1,1]=spr_icon_spool

item_database[2,0]="Hammer"
item_database[2,1]=spr_icon_hammer

//how many slots does inventory have
inventory_size = 8

//initialize the array for the inventory
//we set everything to -1 as we treat -1 as an empty slot
for (i=0;i<inventory_size;i++)
    {
    inventory[i]=-1
    }

//add items to inventory slots
inventory[0]=0//gear item in the first slot
inventory[1]=1//spool item in the second slot
inventory[2]=2//hammer item in the third slot

//initialize the item dragging variables
dragging=0//0 if not dragging something, 1 if we are
dragging_item=-1//index of item we are dragging
dragging_from=0//slot from which we are dragging the item
Draw:
Code:
//define temporary variables for the draw event
var xx,yy,i,slot_size;
var item,item_name,item_icon;
var mx,my,mouse_over;
draw_set_font(fnt_inventory)

//xx and yy are where the inventory starts to draw
xx=x
yy=y
//set mouse_x and mouse_y to mx and my
mx=mouse_x
my=mouse_y
//how big each slot is (used for drawing in a row every 64 pixels)
slot_size = 64

//loop through all inventory slots
for(i=0;i<inventory_size;i++)
    {
    //set the drawing x position
    xx=x+i*slot_size
  
    //if the mouse is within the slot set mouse_over to 1 otherwise set mouse_over to 0
    if mx>xx and mx<xx+slot_size and my>yy and my<yy+slot_size mouse_over=1 else mouse_over=0
  
    //draw the slot sprite
    draw_sprite(spr_slot,mouse_over,xx,yy)
  
    //if the inventory slot is not empty
    if inventory[i]!=-1
        {
        //set "item" to whatever is in this slot
        item = inventory[i]
        //get the item name by checking the item id against the item_database
        item_name = item_database[item,0]
        //get the item icon by checking the item id against the item_database
        item_icon = item_database[item,1]
        //draw the item icon in the slot and draw the text below it
        draw_sprite(item_icon,0,xx,yy)
        }
  
    //if the mouse is in the slot
    if mouse_over
        {
        //if the slot is not empty
        if inventory[i]!=-1
            {
            //draw the item name below the slot
            draw_text(xx,yy+slot_size,item_name)
            //if the mouse button is pressed
            if mouse_check_button_pressed(mb_left)
                {
                //start dragging
                dragging=1
                //set the index of the item you are dragging
                dragging_item=item
                //set the slot from where you are dragging
                dragging_from=i
                //delete the item in the slot (since you picked it up)
                inventory[i]=-1
                }
            }
        //if the mouse button is let go and we're dragging something
        if mouse_check_button_released(mb_left) and dragging
            {
            //if the current slot is not empty, set the "dragging_from" slot
            //to what's in the current slot so that they switch places
            if inventory[i]!=-1 inventory[dragging_from]=inventory[i]
            //set the current slot to the item we are dragging
            inventory[i]=dragging_item
            //stop dragging
            dragging=0
            }
        }
    }

//if we are dragging
if dragging
    {
    //get the item icon and name from the item database
    item_name=item_database[dragging_item,0]
    item_icon=item_database[dragging_item,1]
    //draw the icon and text at the mouse position
    draw_sprite(item_icon,0,mx,my)
    draw_text(mx,my+slot_size,item_name)
    //if we let go of mouse
    if mouse_check_button_released(mb_left)
        {
        //if you are colliding with something on the screen that you intend to interact with
        if collision_point(mx,my,obj_collision,1,1)
            {
            ///do the collision thing (i.e. unlock the door)
            }
            else //otherwise
            {
            //set the inventory slot you dragged the item from back to the item you picked up
            inventory[dragging_from]=dragging_item
            //stop dragging
            dragging=0
            }
        }
    }
 
Last edited:
C

Christian Schütze

Guest
Thank you for both answers. I´ve been busy with life lately but i will try to work with both solutions as soon as possible.

Nidoking, the xHome, yHome is definitely something that sounds very basic but that i haven´t worked with yet and have to learn about.

ChessMasterRiley, at first glance your inventory looks amazing. Exactly what i had in mind. I will try it soon and let you know if i had success incorporating it into my project.

I am new to this community so i dont know if i can +rep you in any way, but if i can i am happy to.

/Christian
 
Thank you for both answers. I´ve been busy with life lately but i will try to work with both solutions as soon as possible.

Nidoking, the xHome, yHome is definitely something that sounds very basic but that i haven´t worked with yet and have to learn about.

ChessMasterRiley, at first glance your inventory looks amazing. Exactly what i had in mind. I will try it soon and let you know if i had success incorporating it into my project.

I am new to this community so i dont know if i can +rep you in any way, but if i can i am happy to.

/Christian
Great! Glad I could help. If you're having difficulty implementing the code, please DM me and I'll see if I can help.

Tbh, I would advise against Nidoking's method as you will have to twist yourself into knots just trying to get everything to work properly. I suppose you could use a bunch of slot objects and an item object, but it would be a pain to manage imo.

I know using arrays and drawing everything rather than using objects can be difficult to visualize, but you will be thankful you learned it in the end.
 

Nidoking

Member
Tbh, I would advise against Nidoking's method as you will have to twist yourself into knots just trying to get everything to work properly. I suppose you could use a bunch of slot objects and an item object, but it would be a pain to manage imo.
Look, if you want to go making things more complicated, I'd appreciate it if you didn't blame other people for your design being a pain.
 
Look, if you want to go making things more complicated, I'd appreciate it if you didn't blame other people for your design being a pain.
Look, there's a right way to do this and it's not the way you suggested. My design is not actually complicated; it's efficient, expandable, and works perfectly for the stated requirements. If I was new to something and seeking advice, I would want someone to chime in to tell me not to do it a certain way if it wasn't the right way to do it.

I was blunt in my post, but not disrespectful.
 

Alice

Darts addict
Forum Staff
Moderator
I've got to agree with ChessMasterRiley on this one.

In the long run his data-driven design is easier to manage and maintain - with inventory data in a separate structure devoid of UI-related info and UI using this information to draw and manipulate inventory. In particular, storing inventory info in a separate data structure - assuming it's global in some way - means you don't have to make any objects persistent. That saves you the hassle of properly initializing the inventory in some single-time entry point (otherwise you might end up with multiple instances of inventory stacking together) and then destroying it when you go out of the game (e.g. to main menu).

If you try to store inventory-specific info (item name, displayed sprite/image etc.) along with some UI information (x/y positions), you might end up with something seemingly simpler - because there are technically fewer "entities" - but it's less flexible and overall harder to debug/maintain. Often, it's actually simpler to have many smaller and highly specialized components than few larger ones trying to manage intricacies of game state and user interface at the same time. Keep your SoCs on, people!

(there are some ways to make the code even better separated - for example, making several scripts to manage inventory state instead of putting all this logic in inventory object - but it's a good starting point)
 

Nidoking

Member
I still don't see anything in the examples that demonstrates how things that aren't yet in the inventory would be handled. If you want a fixed inventory and just want to shuffle stuff around within it, fine, but that's not the stated problem.
I make my inventories with separate data structures as well. It's not as if the two methods are mutually exclusive. Collectable items can destroy themselves and add their data to a global inventory when they're collected. If that's too complicated for you, then I really don't know what you're doing to make it that way.
 

Alice

Darts addict
Forum Staff
Moderator
Nidoking: I guess the problem is, your explanation of how inventory is supposed to work leaves out a lot of details to fill in, and inexperienced programmers may fill in these details in a way that's inconvenient for them in the long run.

For example, they might setup an inventory system based on principle "one inventory item possessed = one inventory item instance in the room", and the item instances have all their inventory management logics in a combination of mouse-ish events and such. And it might work nicely during some prototype tests, but when the time comes to implement other essential features - e.g. saving the inventory state along with the rest of the game state, or introducing keyboard controls - it turns out lots of logic needs to be moved around or rewritten.

So, I'd say the issue with your reply isn't that it's mutually exclusive with data structures. Rather, it's vague to the point a "naive" non-data-structure solution could be implemented based on that. For that matter, I didn't really see much of an explanation of how adding new items to the inventory would be handled in your response...

----------------------------------------

If I were to implement the inventory my way, the general idea would be something like these points below.
Note: it might not be beginner level per-se, but I think it's still easy enough for a beginner to learn in a reasonable time, and then they are not-so-beginner anymore.

1. Set up a collection of all possible items in the game, implemented as a ds_map (i.e. a structure that allows finding items by their keys/names). For example:
"gear": (id: "gear", name: "Gear", sprite: spr_Gear, description: "A part of a mechanism.")
"hammer": (id: "hammer", name: "Hammer", sprite: spr_Hammer, description: "The solution to all problems.")

2. Set up the inventory as a list with a fixed number of items, corresponding to available slots. If there is no item at a given slot, it's "undefined" (a special value in GM corresponding to... the lack of value, in a way). The inventory stores a collected item as an identifier, the same from the collection in point 1.
For example, if the inventory has 8 slots, and player would have gear at 2nd slot and hammer at 4th, the contents of the inventory would be like this:
[undefined, "gear", undefined, "hammer", undefined, undefined, undefined, undefined]

3. Prepare a bunch of scripts to manage the inventory. For example:
Inventory_add(identifier) - adds the item with a given identifier to the first available slot in the inventory
Inventory_remove(identifier) - removes the item with a given identifier from the inventory
Inventory_getAt(index) - gets item information at the given position; that includes item ID, name, sprite and description
Inventory_swap(index1, index2) - swaps items at the given position; if one of items is undefined, it's simply the same as moving the other item

Whenever I want to manage the inventory, I'd add a script for that action and then use that script. This way, essential inventory actions can be executed from different sources (e.g. I could call Inventory_add when collecting a clicked item, or when someone would give it to me as a part of dialogue branch).

4. Prepare user interface to interact with the inventory. I see two approaches here:
- create a single inventory object with a fixed structure, doing some maths to determine the current slots positions, detect the slot I drag the item from and the slot I drop the item at; also, to draw the current inventory on screen (kind of what ChessMasterRiley suggests)
- create several separate slot objects, each assigned to a specific index of the inventory and performing actions related to that slot; drag-and-drop would be handled either by the slot I drag the item from or some specialized dragged-item object
(I think I'd prefer this one, because once you have the slots created, you avoid all the math and just interact between objects; it's good for adaptive UI, too)

I guess once I would setup points 1-3, the user-interface part could end up somewhat similar to what Nidoking had in mind? But I needed to figure out the points 1-3 myself, first... ^^'

@Christian Schütze: once again, I'm aware this might sound above the beginner's level. But the individual pieces of that approach shouldn't be too hard to grasp individually, and once you learn these, you are at much better position to implement other features in the game. You mentioned you work on a point-and-click adventure game - I imagine something like saving/loading, dialogue system or objects interaction will also be needed, and properly designed inventory system can seamlessly interact with all of these.
 
To add an inventory item, I would add a script like Alice noted called inventory_add(item_id):
Code:
///inventory_add(item_id)
with obj_inventory
    {
    var i,item,full;
    //what item are we adding?
    item=argument0
    //set the variable for checking if we're full
    full=1
   
    //loop through the entire inventory starting from 0
    for (i=0;i<inventory_size;i++)
        {
        //if the inventory slot is free
        if inventory[i]=-1
            {
            //add the item to this slot
            inventory[i]=item
            //we're not full!
            full=0
            //break the loop since we found a free slot
            break;
            }
        }
   
    //if we made it through the whole loop up there
    //and none of them were open, run the script to
    //react to a full inventory
    if full
        {
        //script to handle what to do if inventory is full
        //like causing the item to reappear on the ground
        }
    }
Then, calling it is as simple as:
Code:
if keyboard_check_pressed(ord("1")) inventory_add(0)
if keyboard_check_pressed(ord("2")) inventory_add(1)
if keyboard_check_pressed(ord("3")) inventory_add(2)
 

Nidoking

Member
I did say I was just providing some ideas to get started. I don't have the time to make a complete game for someone, so if they ask a question as open-ended as "hey, how do I make an entire core mechanic for a game?" I'm not going to provide a thorough answer or a toolkit. Besides, the very definition of "forum" is a place where many ideas come together and the result is ideally greater than any individual contribution. In this case, there have been a lot of great ideas for how to manage the inventory once it's populated, and I like that. I handle my inventory as individual instances that know where to draw themselves, but in my game, I don't need to worry about things like saving to a file, so it works for me. I've seen one suggestion so far, total, for the originally posed problem of dragging items from the screen to the inventory, and that was mine. Everyone is telling me that my idea is too complex and terrible (or rather, that other ideas that are not mine but have been attributed to me are complex and terrible), but there is yet to be a single alternative idea that would be any better. Even this most recent post only shows how to put the new item into the inventory once it's been determined that it should be added, and not how to perform the actual drag and drop that would trigger the script to run.
I really don't like the sentiment that "There is one single best answer to any question, and anyone not contributing that specific answer is not welcome to participate in the discussion" on a forum. I'm also missing how this:
I suppose you could use a bunch of slot objects and an item object, but it would be a pain to manage imo.
is supposedly bad, while this:
- create several separate slot objects, each assigned to a specific index of the inventory and performing actions related to that slot; drag-and-drop would be handled either by the slot I drag the item from or some specialized dragged-item object
is somehow better. I honestly don't see any difference between these two ideas. But whatever. I'm tired of being told what I think and then being told it's wrong when I have nothing to do with the ideas in question. I think I'll just ignore this thread from now on.
 
@Nidoking don't take everything so personally. I'm not attacking you, so there's no need to get personally defensive. As you said, this is a forum where ideas can come together. However, just because you post an idea, doesn't mean I can't disagree with it and strongly advise against its use.

Regarding your latest point, it isn't better and that's exactly what should be avoided. All of the drawing and logic handling is done in the script outlined in my original post. You already have the inventory data contained in the array (or ds_list if that tickles your fancy) previously created - it makes no sense to convert that information into further objects for interacting with the slots.
 
Still waiting for the part where your method drags and drops things from the room to the inventory.
If you want the item to be added to the inventory by clicking on it, simply execute the inventory_add(item_id) script I noted above.

I wasn't sure how OP already had his world items set up. But, implementing a drag-from-world is rather easy as well. Here's one solution for interacting with world items using my system:

The world item needs only the following:

Creation code:
Code:
//what item is this in our inventory system
item_id=0
Step Code:
Code:
//if mouse button is pressed
if mouse_check_button_pressed(mb_left)
    {
    //on us
    if collision_point(mouse_x,mouse_y,id,0,0)
        {
        //set the inventory pickup to our id
        obj_inventory.pickup=id
        }
    }

//if the inventory is interacting with us, don't draw ourselves in the world
if obj_inventory.pickup=id visible=0 else visible=1
Then, I added the following to the create code of the inventory:
Code:
pickup=noone//set pickup from world variable to noone
And adjusted the draw event to handle pickup from the world:
Code:
//define temporary variables for the draw event
var xx,yy,i,slot_size;
var item,item_name,item_icon;
var mx,my,mouse_over;
draw_set_font(fnt_inventory)

if keyboard_check_pressed(ord("1")) inventory_add(0)
if keyboard_check_pressed(ord("2")) inventory_add(1)
if keyboard_check_pressed(ord("3")) inventory_add(2)

//xx and yy are where the inventory starts to draw
xx=x
yy=y
//set mouse_x and mouse_y to mx and my
mx=mouse_x
my=mouse_y
//how big each slot is (used for drawing in a row every 64 pixels)
slot_size = 64

//loop through all inventory slots
for(i=0;i<inventory_size;i++)
    {
    //set the drawing x position
    xx=x+i*slot_size
   
    //if the mouse is within the slot set mouse_over to 1 otherwise set mouse_over to 0
    if mx>xx and mx<xx+slot_size and my>yy and my<yy+slot_size mouse_over=1 else mouse_over=0
   
    //draw the slot sprite
    draw_sprite(spr_slot,mouse_over,xx,yy)
   
    //if the inventory slot is not empty
    if inventory[i]!=-1
        {
        //set "item" to whatever is in this slot
        item = inventory[i]
        //get the item name by checking the item id against the item_database
        item_name = item_database[item,0]
        //get the item icon by checking the item id against the item_database
        item_icon = item_database[item,1]
        //draw the item icon in the slot and draw the text below it
        draw_sprite(item_icon,0,xx,yy)
        }
   
    //if the mouse is in the slot
    if mouse_over
        {
        //if the slot is not empty
        if inventory[i]!=-1
            {
            //draw the item name below the slot
            draw_text(xx,yy+slot_size,item_name)
            //if the mouse button is pressed
            if mouse_check_button_pressed(mb_left)
                {
                //start dragging
                dragging=1
                //set the index of the item you are dragging
                dragging_item=item
                //set the slot from where you are dragging
                dragging_from=i
                //delete the item in the slot (since you picked it up)
                inventory[i]=-1
                }
            }
        //if the mouse button is let go
        if mouse_check_button_released(mb_left)
            {
            //if we're dragging something
            if dragging
                {
                //if the current slot is not empty, set the "dragging_from" slot
                //to what's in the current slot so that they switch places
                if inventory[i]!=-1 inventory[dragging_from]=inventory[i]
                //set the current slot to the item we are dragging
                inventory[i]=dragging_item
                //stop dragging
                dragging=0
                }
            //if we've picked something up from the world
            if pickup!=noone
                {
                //if we have a free slot here, add the item
                if inventory[i]=-1
                    {
                    //add the pickup item
                    inventory[i]=pickup.item_id
                    //destroy the world item since it was picked up
                    instance_destroy(pickup)
                    }
                //set pickup to noone again
                pickup=noone
                }
            }
        }
    }

//if we are dragging
if dragging
    {
    //get the item icon and name from the item database
    item_name=item_database[dragging_item,0]
    item_icon=item_database[dragging_item,1]
    //draw the icon and text at the mouse position
    draw_sprite(item_icon,0,mx,my)
    draw_text(mx,my+slot_size,item_name)
    //if we let go of mouse
    if mouse_check_button_released(mb_left)
        {
        //if you are colliding with something on the screen that you intend to interact with
        if collision_point(mx,my,obj_collision,1,1)
            {
            ///do the collision thing (i.e. unlock the door)
            }
            else //otherwise
            {
            //set the inventory slot you dragged the item from back to the item you picked up
            inventory[dragging_from]=dragging_item
            //stop dragging
            dragging=0
            }
        }
    }

//if we're picking something up from the world
if pickup!=noone
    {
    //get the item icon and name from the item database
    item_name=item_database[pickup.item_id,0]
    item_icon=item_database[pickup.item_id,1]
    //draw the icon and text at the mouse position
    draw_sprite(item_icon,0,mx,my)
    draw_text(mx,my+slot_size,item_name)
    //if we let go of the picked up item from the world not in an empty slot
    if mouse_check_button_released(mb_left)
        {
        //set pickup to noone again
        pickup=noone
        }
    }
I've updated my example to include an obj_item stand-in: https://www.dropbox.com/s/g37ol4ub05ss120/Inventory_v2.gmx.zip?dl=0

-Riley
 
C

Christian Schütze

Guest
Wow, i did not expect all this feedback. Thank you everyone, i appreciate it a lot.

Riley, the update that you made helped me putting things into perspective. A few days ago i tried to figure out how to do what you just did in this new version, dragging objects from the room and into the inventory. I will take a closer look at the resource tree and how its working when i have more time.

Alice, yes to me that wall of text seems to be way above beginner level but i do have to time to read, try and learn so it´s just fine. We are not trying to create this game asap, we rather take the time to find the smoothest possible way so we can focus on visuals and sounds in the future and implement those things into a Point and Click engine that just works perfectly. I´m gonna make a new inventory test soon and try your 4 steps and see how far i can get.

Thank you for your time everyone, you did not write those answers in less than 2 minutes. This is a very friendly forum indeed.

/Christian
 
Top