GML Flexible Inventory System

Discussion in 'Tutorials' started by Cloaked Games, Sep 23, 2017.

  1. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    FLEXIBLE INVENTORY SYSTEM

    GM Version
    : (GMS2) v2.1.0.136 (Currently works in GMS1.4 also).
    Target Platform: ALL
    Download: GMS2 Example Project
    Difficulty: Intermediate
    Last Edited: October 19th, 2018


    Summary:
    This tutorial will explain a way to program a highly flexible inventory system in GML. It will include adding new items into the game, modifying inventory slots to contain items, stacking multiple items in one slot, and basics for viewing and interacting with inventory slots, including using items.​

    My focus with this inventory code is ease of use. It is not the lowest memory solution, or the most efficient. However, using these methods, you can make an inventory that is flexible enough to make it easy to add new items, new types of items, and things such as crafting, storage chests, and saving, that does not require you to remember specific details of how it was programmed. I have broken the tutorial into several spoiler tabs, to organize the information.

    Finally, it is important to note that this tutorial is not designed for beginners. If you are brand new to Gamemaker you might be able to get it to work by copying my code, but if you start trying to modify it, you will probably end up lost with all sorts of errors. This tutorial will not work for you if you are copying and pasting the code. It is designed to give a complete explanation of one, (good) solution to this programming problem, but it assumes you know how to use variables, understand inter-connected systems, and general programming practices.

    Tutorial:

    All of the code example in here would be executed once at the start of the game.
    We want to add some items into our game. To do this, we need a few things that will make it possible to access these items.

    First, we need item ids that are specific to each item we add. These will be a number that is used to reference an item in the code, when assigning a slot to hold an item, when giving players items, when testing to see if the player has items, even when displaying items in the inventory. We can't just type "apple" or "sword" for the item ids. This needs to be a number.

    We could assign each item to a number, for example, if we decide apple is 0, and sword is 1, after that, we could just write "0" to reference the apple and "1" to reference the sword. However, this is annoying because it's so hard to remember a whole bunch of arbitrary numbers, especially with a large number of items. How will you remember if machine gun is 127 or 315?

    Fortunately, there is a way to automate this, and provide us with IDs that are easy to remember, using an enumerator. Enumerators (referenced as "enum" in the code), are a list of constants (a variable that never changes), accessible through an enum name. It works like this:
    Code:
    enum item
     {
     none,
     apple,
     sword,
     health_potion,
     staff,
     total
     }
    In this example (which we will be using for the rest of the tutorial), each variable in the enumerator is automatically assigned to an incrementing value, in order. We can access those constants through the enum name, which is "item". item.none = 0, item.apple = 1, item.sword = 2, etc. This way, we have item ids that are easy to remember: just "item." then the item you want to reference.
    Next we want each item to have a sprite. Now that we have a list of Item IDs, this is actually quite simple. We will create a strip of sprites, called "spr_item", where each sub-image is the sprite for one of the items. Here is an example for the items we added to the enumerator above:

    spr_item_example.png

    You can see that the first sub-image is empty. This lines up with the first entry in our enum: item.none. Each sub-image for the items is in the same order as the item IDs in the enumerator. First, nothing, then the apple, then the sword, then the potion, then the staff. We want to keep this consistent for every item we add.

    Why? Because sub-images are assigned numbers in the same way as enumerators. The first slot is 0, then 1, then 2, etc, incrementing by ones. This is useful because it means we have IDs for these sprites too! To find the right sub-image, we just use the item ID. item.sword is the correct sub-image for the sword, item.apple is the correct sub-image for the apple.

    To draw an item is very simple now:
    Code:
    draw_sprite(spr_item, item.staff, x, y); //Draws the staff item
    draw_sprite(spr_item, item.sword, x, y); //Draws the sword item
    In the same way, later we can display the items in an inventory slot just by setting the sub-image equal to the value that stores which item is in the slot. If there is nothing (item.none), it will draw the first sub-image, which is nothing.
    So now we can see what a particular item looks like. But we need to be able to do more than that. We want to know things like what the item is named, how much damage it does (in the case of weapons), how much health it recovers (in the case of food).

    I will explain how we will use these later, but for now, we're going to create two more enumerators to help tell us this information about the items.

    First, item_stat. This one will list all of the different stats we might want to look up about an item, like it's name, damage, type, etc. These are the stats we're using in the example, but there are lots more you might want to add, like cost, range, aoe, etc.
    Code:
    enum item_stat
    
     {
     name,
     description,
     type,
     damage,
     health_gain,
     total
     }
    Second, item_type. This one is for each of the different types of items. We'll have 3 types in this example, but once again, you can add whatever different types of items you could possibly want.
    Code:
    enum item_type
    
     {
     none,
     weapon,
     food,
     total
     }
    Using these values, we will be able to reference everything about an item, what type it is, how much health it heals, what it's name is, etc. We will set up an item index to allow us to do this in the next section.
    Now, using all this information we set up, we are going to create a single item index of sorts to store it all in an easy to access way, and actually start defining those values for the items.

    First, we are going to setup a ds_grid. A grid stores values in a rows and columns, and we can find values inside the grid by specifying which row and column we want to read or change. It is like a 2D array, but it is a little more flexible, and has additional built in functionality.

    Using this code we will create a ds_grid large enough to fit all the information we want, and assign it to a global variable "global.item_index" so we can reference it later.
    Code:
    //Create a ds grid item.total wide, and item_stat.total tall
    global.item_index = ds_grid_create(item.total, item_stat.total);
    ds_grid_clear(global.item_index, 0); //Set every position to 0
    If we drew a picture of our ds_grid called global.item_index, it would look sort of like this:
    spr_item_index.png

    You'll notice that each column is its own item, and each row below it contains the item stats. Now we can find information about items using coordinates: the damage of a sword would be located: [2, 3], or if you remember our enumerators: [item.sword, item_stat.damage].

    But hold on! There's nothing in that slot! (Well, actually there's a 0 there, because we cleared it earlier...) Either way, that's not what we want. The sword does more than 0 damage. Let's try setting that spot to a value that's more useful, like 3. We can use the accessor "#" to access a position in a ds_grid, and then set it equal to a value, like this.
    Code:
    global.item_index[# item.sword, item_stat.damage] = 3;
    Now that slot tells us the damage a sword does, and we could use that sort of code to fill in every value but... that's 25 lines of code just for this grid, and we might add more items and item stats later! How about we find a better way to do it?

    Using a function we create, we can make ourselves an easy way to set all the values for any weapon in the game.
    Code:
    /// @description scr_add_weapon(item_ID, name, description, damage);
    /// @function scr_add_weapon
    /// @param item_ID
    /// @param name
    /// @param description
    /// @param damage
    
    var iid = argument0;
    global.item_index[# iid, item_stat.name] = argument1;
    global.item_index[# iid, item_stat.description] = argument2;
    global.item_index[# iid, item_stat.damage] = argument3;
    
    global.item_index[# iid, item_stat.type] = item_type.weapon;
    Using this function, we can add the sword, and the staff, to the item index:
    Code:
    scr_add_weapon(item.sword, "Sword", "A sword to banish evil!",  3);
    scr_add_weapon(item.staff, "Staff", "A magic staff, pulsing with power.", 5);
    But let's back up a second. You might have noticed that in our scr_add_weapon function, we completely ignored item_stat.health_gain. That's because we don't need to set that value for weapons, they don't recover health. However, we do need a new function for food:
    Code:
    /// @description scr_add_food(item_ID, name, description, health_gain);
    /// @function scr_add_food
    /// @param item_ID
    /// @param name
    /// @param description
    /// @param health_gain
    
    var iid = argument0;
    global.item_index[# iid, item_stat.name] = argument1;
    global.item_index[# iid, item_stat.description] = argument2;
    global.item_index[# iid, item_stat.health_gain] = argument3;
    
    global.item_index[# iid, item_stat.type] = item_type.food;
    And now we can add the food into the item index:
    Code:
    scr_add_food(item.apple, "Apple", "Pretty basic. In every RPG.",  10);
    scr_add_food(item.health_potion, "Health Potion", "It's red. Like normal.", 25);
    In both functions, we automatically set the item type value within the function, we don't need to get that value as an argument, saving us a little bit of typing also. Alternatively, you could use just one function that accepts arguments for every single item_stat. However, this would often require typing in 0 for slots you don't need for a particular type of item, so in the example I have opted to have multiple functions.

    Now that we've finished adding our items, here is the new ds_grid global.item_index:
    spr_item_index_finished.png

    Now, for the rest of our project, we can access any information about any item using this item index. Here are some examples of what we can do:
    Code:
    player_health += global.item_index[# item.apple, item_stat.health_gain]; //Gain health from eating an apple.
    
    if (global.item_index[# equipped_item, item_stat.type] == item_type.weapon)
     {
     enemy_health -= global.item_index[# equipped_item, item_stat.damage]; //Attack an enemy with any weapon
     }
    As you add more types of items, or more item stats, just make sure to remember to update the item add functions to accommodate the things you added.

    Next I will go over how to store these items in an inventory, how to stack multiple items in a slot, and how to have them move around in the inventory.
    Now we have items, and an item index that allows us to store and look up information about those items. We want to create an inventory that defines what items the player currently has, and how many of them are in each slot.

    We're going to use another ds_grid, this time named global.inventory: (Initialized at the start of the game).
    Code:
    global.inventory = ds_grid_create(10, 2);
    ds_grid_clear(global.inventory, 0);
    This grid is 10 wide, and 2 tall. We will use the x position in the grid (the first number), to indicate the slot we are looking at, 0-9. This means we have 10 slots total in the inventory. The y position (the second number) is either 0 or 1. We will use the column to store two pieces of information about that slot, first, the item in the slot, and also how many are in that slot.

    So, if global.inventory[# 0, 0] returns item.apple, that tells us the first slot has an apple in it. Then, if global.inventory[# 0, 1] returns 4, we know there are 4 apples in the first slot.

    This is all we need to code for this section for it to work. However, let's add just a few functions to help us easily access and modify these slots in the inventory. First, this function will allow us to increase or decrease the amount of items in a slot.
    Code:
    /// @description Modifies a slot in the inventory. Can add and remove items, and set the item.
    /// @function scr_slot_modify_amount(slot, amount, override);
    /// @param slot
    /// @param amount
    /// @param override
    
    //Assign local variables
    var slot = argument0;
    var amount = argument1;
    var override = argument2;
    
    if (override == false) //If it is not overriding current values
     {
     global.inventory[# slot, 1] += amount; //Increase amount by input amount
     }
    else //If it is overriding current values
     {
     global.inventory[# slot, 1] = amount; //Set amount to input amount
     }
    
    //Clear slot if the amount is less than or equal to 0
    if (global.inventory[# slot, 1] <= 0)
     {
     global.inventory[# slot, 0] = item.none;
     global.inventory[# slot, 1] = 0;
     }
    This function will increase and decrease the number of items in a slot (0-9). This is done by modifying the value of global.inventory[# slot, 1], which is the amount of items in that slot. If the argument override is set to true, the number of items in a slot is set to the input amount, instead of incremented. An important note is that at the end of the function, we completely clear the slot if the number of items is less than or equal to 0, this ensures we don't end up with negative items in a slot, and clears it to store a different item later.

    This second function will be used to add a certain number of items into the inventory automatically, finding an available slot to put the items in.
    Code:
    /// @description Adds an item and a quantity into the inventory in a valid slot.
    /// @function scr_gain_item(item_ID, amount);
    /// @param item_ID
    /// @param amount
    
    var iid = argument0;
    var amount = argument1;
    
    var slot = 0; //A temporary variable to loop through the slots
    var inventory_width = ds_grid_width(global.inventory);
    
    while (slot < inventory_width)
     {
     //If the tested slot in the inventory is either empty, or contains a matching item id
    if (global.inventory[# slot, 0] == iid || global.inventory[# slot, 0] == item.none)
     {
      global.inventory[# slot, 0] = iid;
      global.inventory[# slot, 1] += amount;
      return true; //Did set the slot
      exit; //Exit function, because it has set the slot
      }
     slot ++; //Incriment slot to test next position
     }
    return false; //Did not set slot
    This function loops through the inventory looking for a valid slot to put some items. It returns true of false to indicate whether it was successful or not. We will use these functions later to set and modify slots to move items around in the inventory. There is no function for setting a slot to hold an item, because that would just be: global.inventory[# slot, 0] = item.apple;

    Our scr_item_gain function above will look for the first available spot to put an item, either an empty slot, or a matching slot. This works perfectly fine, but it's often better if you instead prioritize slots with a matching type, before filling a new empty slot with items. (If you have an apple in slot 10, and pick up another apple, it should probably go to slot 10 before filling empty slot 1 so you have two slots with apples). The way to do this is pretty simple: just loop through twice, first looking for matching slots, and then looking for empty slots.

    Code:
    /// @description Adds an item and a quantity into the inventory in a valid slot.
    /// @function scr_gain_item(item_ID, amount);
    /// @param item_ID
    /// @param amount
    
    var iid = argument0;
    var amount = argument1;
    
    var slot = 0; //A temporary variable to loop through the slots
    var inventory_width = ds_grid_width(global.inventory);
    
    while (slot < inventory_width)
    {
        //Searching for a matching inventory slot
        if (global.inventory[# slot, 0] == iid)
        {
            global.inventory[# slot, 1] += amount;
            return true; //Did set the slot
            exit; //Exit function, because it has set the slot
        }
        slot ++; //Increment slot to test next position
    }
    
    slot = 0;
    while (slot < inventory_width)
    {
        //Searching for an empty inventory slot
        if (global.inventory[# slot, 0] == item.none)
        {
            global.inventory[# slot, 0] = iid;
            global.inventory[# slot, 1] += amount;
            return true; //Did set the slot
            exit; //Exit function, because it has set the slot
        }
        slot ++; //Increment slot to test next position
    }
    return false; //Did not set slot
    Finally we're going to get some tangible results. Now that we can set and modify slots in our inventory, and read information about the items in our inventory, let's get around to actually displaying it so the player can interact with it.

    First, we're going to create a series of instances that will be responsible for managing our inventory slots. We'll create 10 total, one for each slot, and these slots will draw the items inside of them, and how many there. We can later use these for detecting when the player has clicked on a slot.

    Let's first create an object called obj_slot, and then add 10 of them into the room. These will be positioned in the top left corner. As we create them, we will assign them a slot, so all 10 slots are assigned. Put the code in the create/room start event of a controller object, or the player.
    Code:
    var slot = 0;
    while (slot < ds_grid_width(global.inventory))
     {
     var inst = instance_create_layer(x+8+(64*slot), y+8, "Instances", obj_slot);
     inst.var_slot = slot;
     slot ++;
     }
    
    Now, in the draw GUI event of these slot objects, we can display what we want about the items in that slot.
    Code:
    //Get values
    var iid = global.inventory[# var_slot, 0];
    var amount = global.inventory[# var_slot, 1];
    var name = global.item_index[# iid, item_stat.name];
    var description = global.item_index[# iid, item_stat.description];
    
    //Draw stuff
    if (iid != item.none)
     {
     draw_sprite(spr_item, iid, x, y); //Draw item sprite
     draw_text(x+4, y+4, string(amount)); //Draw item quantity
     }
    
    Let's add some items into the inventory, and then see what it looks like!
    Code:
    scr_gain_item(item.apple, 3);
    scr_gain_item(item.staff, 1);
    scr_gain_item(item.sword, 2);
    global.inventory[# 5, 0] = item.health_potion;
    scr_slot_modify_amount(5, 2, true);
    Runner 2017-09-23 14-33-01-212.jpg

    That is exactly what should happen based on our functions. Next we'll move on to how to interact with the inventory, and move items between slots.

    Since this code works entirely based on absolute coordinates, we need to make some modifications in order to make it work with a view. Try having the slot objects store their position as a gox and goy position. Then set their x and y position to the gox and goy position plus the view/camera position. Then draw everything from the gox and goy coordinate instead of the x and y. This should keep everything lined up.
    Now we want to make it so that we can pick up items in the mouse and move them between slots. To start, we need another small grid that will store the item that is in the mouse slot. You can see it is only big enough for one item:
    Code:
    global.mouse_slot = ds_grid_create(1, 2);
    We can draw the mouse slot using almost the same code as the obj_slot code, in the draw event of a controller object:
    Code:
    /// @description Draw the mouse items.
    
    //Get values
    var iid = global.mouse_slot[# 0, 0];
    var amount = global.mouse_slot[# 0, 1];
    
    //Draw stuff
    if (iid != item.none)
     {
     draw_sprite(spr_item, iid, mouse_x-32, mouse_y-32); //Draw item sprite
     draw_text(mouse_x+4-32, mouse_y+4-32, string(amount)); //Draw item quantity
     }
    Then we can use this code in the left pressed event of the slot object to move the items with the mouse:
    Code:
    /// @description Move items with the mouse
    
    var iid = global.inventory[# var_slot, 0];
    var amount = global.inventory[# var_slot, 1];
    var mouse_iid = global.mouse_slot[# 0, 0];
    var mouse_amount = global.mouse_slot[# 0, 1];
    
    if (iid == 0 || mouse_iid == 0 || iid != mouse_iid) //If either slot is empty, or the two slots don't match
     {
     //Switch the slots
     global.inventory[# var_slot, 0] = mouse_iid;
     global.inventory[# var_slot, 1] = mouse_amount;
     global.mouse_slot[# 0, 0] = iid;
     global.mouse_slot[# 0, 1] = amount;
     }
    else if (iid == mouse_iid) //If both slots are the same
     {
     //Take all mouse items and put them in inventory
     global.inventory[# var_slot, 1] += global.mouse_slot[# 0, 1];
     global.mouse_slot[# 0, 0] = item.none;
     global.mouse_slot[# 0, 1] = 0;
     }
    Now we can move items around between slots, and stack two items matching together into the same slot. From this point onward, everything we can do with this inventory involves using the item_index we set up and the enumerators to our advantage. With the system we have now built as a groundwork, everything else we might want to do with this inventory is simple to implement. I will go over a few examples in part 3, however at this point, it would take forever to give complete code examples for every situation.
    Our current system has an unlimited stack amount. You can put as many items as you want of the same type into the same slot. In most cases we don't want this. With this system, a good way to limit the number of items in each slot is to just add a check for the various inventory moving function that ensures the value of global.inventroy[# var_slot, 1] stays below a certain amount.

    However, sometimes we want different numbers for each type of item. (For example, you can use the number of items to represent durability instead for weapon type items). The best way to do this is to add another item_stat that is something like "item_stat.max_stack", and test for that instead. It may look something like this (in the case of the left pressed event for inventory slots):
    Code:
    if (iid == mouse_iid) //If both slots are the same
     {
     //Take all mouse items and put them in inventory
     while (global.inventory[# var_slot, 1] < global.item_index[# iid, item_stat.max_stack])
     {
      global.inventory[# var_slot, 1] += 1;
      global.mouse_slot[# 0, 1] -= 1;
      if (global.mouse_slot[# 0, 1] <= 0)
       {
       global.mouse_slot[# 0, 0] = item.none;
       global.mouse_slot[# 0, 1] = 0;
       exit;
       }
      }
     }
    In addition, we would need to update our item gain function to account for stack limits. (I here am modifying the advanced gain function under Part Two: Storing Items and Quantities). We cannot add all of the items at once, because if the number exceeds the space left in the slot until the stack, adding the items all at once will pass the stack limit. The method we use here instead is to add one item at a time, until the slot is full (or we're out of items), then we move onto another slot.

    Code:
    /// @description scr_gain_item(item ID, quantity);
    /// @function scr_gain_item
    /// @param item_ID
    /// @param quantity
    
    var iid = argument[0];
    var quantity = argument[1];
    
    var slot = 0;
    var inventory_width = ds_grid_width(global.inventory);
    var stack_limit = global.item_index[# test_item, item_stat.max_stack];
    
    while (slot < inventory_width) //Checking for matching slots
        {
            //If testing slot has matching item
            if (global.inventory[# slot, 0] == iid)
            {
                global.inventory[# slot, 0] = iid; //Make that slot hold test item
                while (global.inventory[# slot, 1] < stack_limit && quantity > 0)
                {
                    global.inventory[# slot, 1] ++;
                    quantity --;
                }
                
                if (quantity == 0)
                {
                    return true;
                    exit;
                }
            }
            slot += 1;
        }
    
    slot = 0;
    //Checking for empty slots
    while (slot < inventory_width)
     {
     if (global.inventory[# slot, 0] == item.none) //If testing slot is empty
      {
      global.inventory[# slot, 0] = iid; //Make that slot hold test item
      global.inventory[# slot, 1] += quantity; //Add amount to match
        
        return true;
        exit;
      }
     slot += 1;
     }
    To add a crafting system, you just need to check if the necessary values in a number of designated crafting slots match a set of values that are stored to serve as the crafting recipe. Personally, I store crafting recipes in their own grid, which has the following values:

    craft_itemA, craft_itemB, craft_amountA, craft_amountB, output_item, output_amount

    With these values I have two slots that can be filled with certain items to craft a third new item. craft_itemA has to match the item in the first slot, craft_itemB matches the item in the second slot, the amount in the first and second slot must be greater than or equal to the matching craft_amountA and craft_amountB. A lot of code goes into this, but the system of inventory we have here means it's not too challenging.
    To have chests to store items in, like in Minecraft, you can create another inventory in exactly the same way we created the player inventory, but instead set it up as a local variable for a chest object of some sort. You can create slot instances for the chest upon interacting with it, and trade items among the slots the same way we do with the player inventory.

    Make sure however, that if the chest instance is destroyed, the local inventory grid is also destroyed, with ds_grid_destroy. Otherwise it will hang out in memory forever, a memory leak. (Grids are dynamic resources).
    If you have any experience saving and loading, then saving and loading these inventories is simple. GM has built in functions for this.

    Use ds_grid_write and ds_grid_read for easy saving and loading to a text file.
    I can't cover everything you need to make items do what you want in your game, but I can give an example so you can hopefully understand how to do it the way you want.

    Let's say we want to be able to click on a slot and have that item do something. In our example, the food items would be consumed and increase the player health. To do that, on the click event, we check first to see what type of item it is, and if it is food, increase the player health by the correct amount:
    Code:
    var iid = global.inventory[# var_slot, 0];
    if (global.item_index[# iid, item_stat.type] == item_type.food) //If the item is food
     {
     player_health += global.item_index[# iid, item_stat.health_gain]; //Gain the right amount of health
     global.inventory[# var_slot, 1] -= 1; //Remove one food item
      if (global.inventory[# var_slot, 1] <= 0) //Clear the slot if it is empty
      {
      global.inventory[# var_slot, 0] = 0;
      global.inventory[# var_slot, 1] = 0;
      }
     }
    Using this sort of code, you can define what happens no matter what type of item it is. Bows might be programmed to create arrows with a certain damage, swords might be programmed to damage enemies, armor might be programmed to increase defense if it is in a certain slot.
    If you want a weapon or tool to have durability, use the amount information in the inventory as durability instead of the number of items in a slot.You may wish to display this as a durability bar instead of a number, so add a check into the display code to test which type of item it is and run the correct display code for the items.

    global.inventory[# 0, 1] would return the durability of that slot if it was a weapon instead of the stack count. They're just treated the same way.
    This is one major disadvantage of the system used here for displaying item sprites. You can only use one sub-image per sprite. You could modify the system instead to have a separate sprite per item, and store the correct item sprite in the item_index grid instead. It's slower and more annoying, but it's the only way to add animated item sprites to this system.
    This was suggested by @IngoLingo, thanks!

    Implementing inventory sorting is fairly straightforward, but only by item ID and quantity. Past that it gets a little bit complicated. I will try to explain here.

    With the system we have right now, we can actually sort the inventory based on two things: the value of the item ID of the items in the inventory (the order of which the IDs appear in the initializing enumerator), and the quantity of items. These are the two columns of the inventory grid, and we can sort them with ds_grid_sort:
    Code:
    ds_grid_sort(global.inventory, 0, true); //Sort based on item ID
    ds_grid_sort(global.inventory, 1, true); //Sort based on quantity of items
    With only a little bit more work, we could also sort the items based on item data from the item_index, such as item_stat.name (alphabetically), item_stat.health_gain, item_stat.damage, etc. However, these values are not actually stored in global.inventory, so we can't sort by those values without some additional code.

    One way to do it, would be to resize the inventory grid to be one column wider (either at the creation, or temporarily). Then we can iterate through the inventory grid, plugging the item ID into item_index to store the value of whatever item_stat we want to sort with in the third column.
    Code:
    ds_grid_sort(global.inventory, 2, true); //Sort based on whatever you put in this column

    This is a long in-depth tutorial. I hope that's because I put lots of detail in, not because it's convoluted and confusing. I tried my best to cover everything you might want to do with a fully complete flexible inventory system. If you have any questions however, please feel free to ask and I will hopefully be able to help you. I am also looking for feedback on the tutorial itself, so if anything feels confusing, unnecessary, under or over explained, please tell me. I will improve the tutorial over time as I notice those kinds of things.

    This is the same system (actually a little bit better) that I use in my main project The Last Librarian. It has served me well so far, and hopefully it will work for you too.
     
    Last edited: Oct 19, 2018
  2. ysdulank

    ysdulank Member

    Joined:
    Jun 8, 2017
    Posts:
    3
    Thanks for sharing!
     
    Cloaked Games likes this.
  3. SYSTEM_FAILURE

    SYSTEM_FAILURE Member

    Joined:
    Oct 5, 2017
    Posts:
    2
    Hi there thanks so much for that detailed guide. Im fairly new to game language (1 month) and have began making my 1st game, having so much fun great feeling when you fix a problem. I just wanted to ask something that might seem simple to you but i am struggling. I have downloaded the files for above, and im editing the content. one of the tasks i set myself was to change the amount of slots the inventory has. Now ive managed to change the width, but for some reason i can add rows to make it say.... an 8 x 8 grid. Any help would be greatly appreciated, and apologies if i have missed something in your literature i have read through it several times. :)
     
    Cloaked Games likes this.
  4. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Thanks for reading my tutorial! Also, that's a good question. If I understand what you mean, here is the relevant part of the tutorial.
    I will explain a bit further, and explain how to do what I think you want to do.

    In this code, we use only the width (which is 10 in the example) to indicate which slot we're in. The height is used to store information about the items that are in a particular slot. So editing the width doesn't change how many slots there are at all, only changing the width. Now, if you wanted to make your inventory layed out in a grid, you would actually need to wrap the width over multiple lines. Here is a picture:

    Here we have our current inventory system, with 9 slots because our grid was created to be (9, 2). We created them in a line.

    Inventory Straight.png

    If we want to make the inventory have multiple rows, we need to adjust where we're creating the inventory slots themselves. There is still only 9 slots, the grid is the same but:

    Inventory Wrapped.png

    In this example we have a 3x3 grid for our inventory. But the inventory code looks exactly the same, we just placed them in a different place. You'll need to modify the code used to create the obj_slot instances to align them the way you want them to. Try using two loops, that loop through the xx position and the yy position up to 3 in both directions. Then create them relative to those positions. Just make sure you have a separate counter going to keep track of how many times you have looped total that will be used to assign the slot itself.

    If this isn't what you meant, or you have additional questions, feel free to ask!
     
    Funerailles and TinaSparkle like this.
  5. SYSTEM_FAILURE

    SYSTEM_FAILURE Member

    Joined:
    Oct 5, 2017
    Posts:
    2
    Yes you understood my question correctly, and thanks for the quick reply. I shall re-read your answer and learn to implement it. My project is on hold until i learn how to use grids properly :)
     
    Cloaked Games likes this.
  6. Berker UCAR

    Berker UCAR Member

    Joined:
    Oct 4, 2017
    Posts:
    21
    Thank you for making this valuable tutorial.
     
    Cloaked Games likes this.
  7. Shawnisgod

    Shawnisgod Member

    Joined:
    Sep 29, 2017
    Posts:
    2
    This tutorial is awesome! Thanks for sharing! However, i have some problems on creating slots. I am confused where should i put those codes of creating slots. I put them under create event of obj_slot, but it didn't work.
     
    Cloaked Games likes this.
  8. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Glad you liked the tutorial!

    You put the code for creating slots in the create event of a controller object of some kind. If it's on obj_slot, it can't create itself, so use another instance to create them. I often use the player if I don't have a controller object. Just make sure you don't have a situation where the code will run multiple times, you'll get multiple inventory slots.

    I updated the tutorial to be more clear. Good luck!
     
  9. Shawnisgod

    Shawnisgod Member

    Joined:
    Sep 29, 2017
    Posts:
    2
    Thanks for the quick reply.
     
    Cloaked Games likes this.
  10. dazza_bo

    dazza_bo Member

    Joined:
    Sep 25, 2017
    Posts:
    21
    Thanks for the tutorial! Will come in handy
     
    Cloaked Games likes this.
  11. markchapman

    markchapman Member

    Joined:
    Sep 25, 2017
    Posts:
    23
    is there a downloadable version for gm 1.4?
     
    Cloaked Games likes this.
  12. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    There is not, sorry. However, all the code should work the same. The only difference is the way the script names are formatted at the top of each script. If you get the trial version of GMS2, you can download it and copy the scripts over to a 1.4 project.

    EDIT: Also you will need to change the instance_create_layer function to just instance_create (The GMS1 function that does the same things).
     
    Last edited: Jan 6, 2018
  13. markchapman

    markchapman Member

    Joined:
    Sep 25, 2017
    Posts:
    23
    I have 1.4 pro and i followed the guide word for word, but i was wanting a refrence for some errors that i came upon.
     
    Cloaked Games likes this.
  14. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Copy the text of the errors and I'll see if I can help you out.

    EDIT:
    @markchapman I looked through it again, and there is one spot you'll need to change. instance_create_layer is a new function in GMS2. You'll need to change that to instance_create and take out the argument for layer.
     
    Last edited: Oct 29, 2017
  15. IngoLingo

    IngoLingo Member

    Joined:
    Nov 28, 2017
    Posts:
    21
    I'm assuming the ds_grid_sort will work with this system like if you want to sort by item name or item type. And I just want to be sure, would you write it like this:
    Code:
    ds_grid_sort(global.inventory, item_stat.name, true); // This will sort items in ascending order by item name.
     
    Cloaked Games likes this.
  16. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Sorting the inventory is totally possible. That's a really good thing to point out. I've thought about it before, but I'd never really looked into actually doing it. I'm going to your idea to the other things to do tab so other people can see it easily.

    However, doing it would be a little more complicated than the code that you posted. The inventory grid itself only contains two columns: the ID and the quantity. Thus without any extra work you can only sort by those values. You could sort the item_index grid if you wanted, but that doesn't change any of the positions of the items.

    I think the easiest way to sort on any other value (such as the name alphabetically like you mentioned), would be to add a third column to the inventory grid, and set the value in that column for each row equal to the value you want to sort by. You could do that temporarily, resizing it afterward too. I've added a more comprehensive summary in the third part of the tutorial.
     
    Last edited: Feb 7, 2018
    IngoLingo likes this.
  17. tetherline

    tetherline Member

    Joined:
    Feb 10, 2018
    Posts:
    16
    This is really good.
     
    Cloaked Games likes this.
  18. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Thank you, I'm glad you found it helpful!
     
  19. IngoLingo

    IngoLingo Member

    Joined:
    Nov 28, 2017
    Posts:
    21
    I got this working in my game a few weeks ago, but I forgot to mention that the DS sorting system in GML only sorts by column. So essentially the x and y coordinates for the inventory grid have to be switched before the "ds_grid_sort" will work. Thanks again for this tutorial, it's amazing. Once I have a good build of my game, I'll be sure to show you so you can see how well this system works for a monster collecting 2D fighting game (like pokemon with the combat of street fighter).
     
    Cloaked Games likes this.
  20. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    That sounds awesome. I'd love to see it. I'm glad you found it usable for systems beyond what was specifically addressed in the tutorial. I use this system of organizing data for a lot of things in my games, beyond just inventories. Enemy AI, terrain generation, and other things.
     
    IngoLingo likes this.
  21. Callum Smith

    Callum Smith Member

    Joined:
    Apr 2, 2018
    Posts:
    5
    I was wondering, and this will probably seem a very obvious question, but none the less; At the start of the guide when it comes to setting up the enumerators, does this take place within an object, or within the creation code of the room?
     
    Cloaked Games likes this.
  22. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    That code would need to be run once, ideally at the start of the game. Typically that would be handled by some sort of controller object in the creation event or the game start event. You wouldn't want to put it in the room creation code.
     
  23. Simon Gust

    Simon Gust Member

    Joined:
    Nov 15, 2016
    Posts:
    3,087
    I thought enumerators didn't have to be called at all as long as they are written somewhere, this can be in a script you never call but the effects should still take place.
     
    Cloaked Games likes this.
  24. Catan

    Catan Member

    Joined:
    Jun 20, 2016
    Posts:
    550
    True, it's also easy to test. You don't need to call the piece of code with the enumerator definition at all.
    That being said, I think it's always a good habit to have a script somewhere that runs at game start (gml_pragma possibly?). That could be a good place to put enums in anyways.
     
    Cloaked Games likes this.
  25. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Hm, I didn't know that. I guess they work like macros in that way, which makes sense. I usually just stick my enumerators in with the same script that I initialize my other global variables. Doesn't particularly matter really.
     
  26. Callum Smith

    Callum Smith Member

    Joined:
    Apr 2, 2018
    Posts:
    5
    I see, that makes more sense now, thank you very much! I do have one last question, which is on a similar vain; Would the entire inventory system, not including stuff like crafting and what-not, take place within a single controller object? I ask because I've seen a fair few tutorials that have their systems split up across several objects which tends to be pretty confusing to navigate. It's the only form of critique I would give your guide, is that where the code goes doesn't seem to be mentioned. (I would imagine that's a problem for me because I'm still a novice at the engine. Not quite a beginner, but still not terrifically well versed in the more complex areas)
     
    Cloaked Games likes this.
  27. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    You bring up a good point. I'll take a look and see if I can make it more clear where all the code goes. I think most of it is there, it's just hard to pick out when you're reading that much text.
    All of part one is run once at the start of the game. That's just setting stuff up.
    Part two is split between additional setup (which is in the same place as mentioned above), and the draw events of the slot object and controller objects.
    Code that does things like add or remove items would happen wherever it is that you wanted it to. For example, adding an item to the inventory when the player collided with an item instance in game, or when you bought an item from an NPC.
    Note, a lot of the code in there is not necessarily required for your specific inventory. It's there as an example so you know what you can do with it. You would need to expand on the knowledge you learned from the tutorial to make the inventory the way you wanted.
    For that matter, I actually have several major changes to this system that I am using in my current project, because it's more efficient for that specific project.
     
  28. Callum Smith

    Callum Smith Member

    Joined:
    Apr 2, 2018
    Posts:
    5
    Aah, thank you! It's a lot more clear now. And don't worry, I know~ It's why I've been asking questions, I like to know how code works before I use it so that I can understand and adapt it to my needs. I never liked the idea of just copy-pasting code from someone without knowing how and why it works.
     
    Cloaked Games likes this.
  29. Callum Smith

    Callum Smith Member

    Joined:
    Apr 2, 2018
    Posts:
    5
    Hi again, I have another question! (I'll probably be asking a lot of them.)

    I've been working my way through your examples, and so far it's working smoothly and I understand it. However, the roadblock I've come too is in trying to get the slot objects to follow my camera without losing position of whizzing off into the ether. I took a stab at what you wrote;

    "Since this code works entirely based on absolute coordinates, we need to make some modifications in order to make it work with a view. Try having the slot objects store their position as a gox and goy position. Then set their x and y position to the gox and goy position plus the view/camera position. Then draw everything from the gox and goy coordinate instead of the x and y. This should keep everything lined up."

    My attempt(s) were to, in the create event of the slot objects set gox = x and goy = y. Then in the step event set x = (gox+obj_camera.x) and y = (goy+obj_camera.y). Which, well, obviously didn't work out too well. I was wondering if I could ask for a bit more clarification on how your quote would work? Afer a few hours, I'm honestly not sure how I would go about setting the slots x and y relative to the views position, without ruining their placement.
     
  30. Wraithious

    Wraithious Member

    Joined:
    Jun 24, 2016
    Posts:
    1,166
    This looks really good, for some reason tho the spoilers aren't working for me to actually read through it so I downloaded the gms2 project, I don't have gms2 but I'm going to just look through the source code and examine it that way, Inventories have allways caused me many days of work and headaches, as an example this is a common and typical inventory script I use:
    Code:
    var mtype=argument0;
    var row=argument1;
    var chose=argument2;
    var look=argument3;
    var set=0;
    var maine=0;
    if(mtype=1) {
    if chose=0 str="Earth blade";if chose=1 str="Fire blade";if chose=2 str="Water blade";if chose=3 str="Wind blade";
    if chose=12 str="Earth hilt";if chose=13 str="Fire hilt";if chose=14 str="Water hilt";if chose=15 str="Wind hilt";
    if chose=24 str="Earth handle";if chose=25 str="Fire handle";if chose=26 str="Water handle";if chose=27 str="Wind handle";
    if chose=36 str="Earth pommel";if chose=37 str="Fire pommel";if chose=38 str="Water pommel";if chose=39 str="Wind pommel";
    if chose=48 str="Staff";if chose=49 str="Knife";if chose=50 str="Spear";if chose=51 str="Mace";if chose=52 str="Axe";if chose=53 str="Hammer";
    if(look=0) {//Equip Weapons
    if(row=0) {//blades
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[0,i]=0;}global.equip[0,chose]=1;global.wPartC[0] = 15 + chose;global.sword[global.wep,row]=chose;
    global.des[0]=str+" equipped#Damage: "+string(global.wPartC[0])+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=1 && set=0) {
    global.equip[0,chose]=0;global.wPartC[0] = 0;global.sword[global.wep,row]=0;
    global.des[0]=str+" not equipped"+"#Weapon damage: "+string(((global.wPartC[1]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=1) {//hilts
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[0,i+12]=0;}global.equip[0,chose]=1;global.wPartC[1] = 8 + (chose-12);global.sword[global.wep,row]=chose-12;
    global.des[0]=str+" equipped#Damage: "+string(global.wPartC[1])+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=1 && set=0) {
    global.equip[0,chose]=0;global.wPartC[1] = 0;global.sword[global.wep,row]=0;
    global.des[0]=str+" not equipped"+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=2) {//handles
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[0,i+24]=0;}global.equip[0,chose]=1;global.wPartC[2] = 2 + (chose-24);global.sword[global.wep,row]=chose-24;
    global.des[0]=str+" equipped#Damage: "+string(global.wPartC[2])+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=1 && set=0) {
    global.equip[0,chose]=0;global.wPartC[2] = 0;global.sword[global.wep,row]=0;
    global.des[0]=str+" not equipped"+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=3) {//pommels
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[0,i+36]=0;}global.equip[0,chose]=1;global.wPartC[3] = 6 + (chose-36);global.sword[global.wep,row]=chose-36;
    global.des[0]=str+" equipped#Damage: "+string(global.wPartC[3])+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=1 && set=0) {
    global.equip[0,chose]=0;global.wPartC[3] = 0;global.sword[global.wep,row]=0;
    global.des[0]=str+" not equipped"+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2])+(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=4) {//all other weapons
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=0 && set=0) {
    for(var i=0;i<16;i+=1;){global.equip[0,i]=0;}global.equip[0,chose]=1;global.wPartC[0] = 15 + chose;global.wPartC[1] = 0;global.wPartC[2] = 0;global.wPartC[3] = 0;
    global.des[0]=str+" equipped#Damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[0,chose]>0 && global.equip[0,chose]=1 && set=0) {
    global.equip[0,chose]=0;global.wPartC[0] = 0;
    global.des[0]="No weapons equipped!#Physical damage: "+string((global.pl1[1,7]*global.pl1[1,2]));
    set=1;
    }
    }
    }
    if(look=1) {//View Weapons
    if(row<4) {//Sword
    if global.equip[0,chose]=1 && global.hasit[0,chose]=1 global.des[0]=str+" equipped#Damage: "+string(global.wPartC[row])+"#Weapon damage: "
    +string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    if global.equip[0,chose]=0 && global.hasit[0,chose]=1 global.des[0]=str+" not equipped"+"#Weapon damage: "+string(((global.wPartC[0]
    +global.wPartC[1]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])));
    if(global.hasit[0,chose]=0) {
    for(var i=0;i<16;i+=1){if global.equip[0,i]=1 maine=1;}
    if maine=1 global.des[0]="Sword equipped"+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    if maine=0 global.des[0]="No weapons equipped!#Physical damage: "+string((global.pl1[1,7]*global.pl1[1,2]));
    }
    }
    if(row=4) {//All other weapons
    for(var i=0;i<59;i+=1){if global.equip[0,i]=1 maine=1;}
    if global.equip[0,chose]=1 global.des[0]=str+" equipped#Damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    if global.equip[0,chose]=0 && maine=0 global.des[0]="No weapons equipped!#Physical damage: "+string((global.pl1[1,7]*global.pl1[1,2]));
    if maine=1 global.des[0]="Sword equipped"+"#Weapon damage: "+string(((global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])
    +(global.pl1[1,7]*global.pl1[1,2])));
    }
    }
    var g=1;for(var i=0;i<59;i+=1){if global.equip[0,i]=1 g=0;}global.fist=g;
    }
    if(mtype=2) {
    if chose=0 str="Fire armor";if chose=1 str="Water armor";if chose=2 str="Wind armor";if chose=3 str="Status armor";if chose=4 str="Magic armor";
    if chose=12 str="Fire gloves";if chose=13 str="Water gloves";if chose=14 str="Wind gloves";if chose=15 str="Status gloves";if chose=16 str="Magic gloves";
    if chose=24 str="Fire helm";if chose=25 str="Water helm";if chose=26 str="Wind helm";if chose=27 str="Status helm";if chose=28 str="Magic helm";
    if chose=36 str="Fire boots";if chose=37 str="Water boots";if chose=38 str="Wind boots";if chose=39 str="Status boots";if chose=40 str="Magic boots";
    if(look=0) {//Equip Armor
    if(row=0) {//body
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[1,i]=0;}global.equip[1,chose]=1;global.aPartC[0] = 2 + chose;global.arm=chose*4;
    global.des[1]=str+" equipped#Defense: "+string(global.aPartC[0])+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])
    +(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=1 && set=0) {
    global.equip[1,chose]=0;global.aPartC[0] = 0;global.arm=0;
    global.des[1]=str+" not equipped"+"#Armor defense: "+string(((global.aPartC[1]+global.aPartC[2]+global.aPartC[3])+(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=1) {//gloves
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[1,i+12]=0;}global.equip[1,chose]=1;global.aPartC[1] = 1 + (chose-12);global.arm1=chose-12;
    global.des[1]=str+" equipped#Defense: "+string(global.aPartC[1])+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])
    +(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=1 && set=0) {
    global.equip[1,chose]=0;global.aPartC[1] = 0;global.arm1=0;
    global.des[1]=str+" not equipped"+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[2]+global.aPartC[3])+(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=2) {//helms
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[1,i+24]=0;}global.equip[1,chose]=1;global.aPartC[2] = 2 + (chose-24);global.arm2=((chose-24)*4);
    global.des[1]=str+" equipped#Defense: "+string(global.aPartC[2])+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])
    +(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=1 && set=0) {
    global.equip[1,chose]=0;global.aPartC[2] = 0;global.arm2=0;
    global.des[1]=str+" not equipped"+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[3])+(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    }
    if(row=3) {//boots
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[1,i+36]=0;}global.equip[1,chose]=1;global.aPartC[3] = 1 + (chose-36);global.arm3=chose-36;
    global.des[1]=str+" equipped#Defense: "+string(global.aPartC[3])+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])
    +(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    if(global.hasit[1,chose]>0 && global.equip[1,chose]=1 && set=0) {
    global.equip[1,chose]=0;global.aPartC[3] = 0;global.arm3=0;
    global.des[1]=str+" not equipped"+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2])+(global.pl1[1,8]*global.pl1[1,2])));
    set=1;
    }
    }
    }
    if(look=1) {//View Armor
    if(row<4) {
    if global.equip[1,chose]=1 && global.hasit[1,chose]=1 global.des[1]=str+" equipped#Defense: "+string(global.aPartC[row])+"#Armor defense: "
    +string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])+(global.pl1[1,8]*global.pl1[1,2])));
    if global.equip[1,chose]=0 && global.hasit[1,chose]=1 global.des[1]=str+" not equipped"+"#Armor defense: "+string(((global.aPartC[0]
    +global.aPartC[1]+global.aPartC[2]+global.aPartC[3])+(global.pl1[1,8]*global.pl1[1,2])));
    if(global.hasit[1,chose]=0) {
    for(var i=0;i<47;i+=1){if global.equip[1,i]=1 maine=1;}
    if maine=1 global.des[1]="Armor equipped"+"#Armor defense: "+string(((global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3])
    +(global.pl1[1,8]*global.pl1[1,2])));
    if maine=0 global.des[1]="No armor equipped!#Physical defense: "+string((global.pl1[1,8]*global.pl1[1,2]));
    }
    }
    }
    }
    if(mtype=3) {
    if chose=0 str="Earth amulet";if chose=1 str="Fire amulet";if chose=2 str="Water amulet";if chose=3 str="Wind amulet";if chose=4 str="Status amulet";if chose=5 str="Magic amulet";
    if chose=12 str="Earth ring";if chose=13 str="Fire ring";if chose=14 str="Water ring";if chose=15 str="Wind ring";if chose=16 str="Status ring";if chose=17 str="Magic ring";
    if chose=0 || chose=12 str2="#Resist Earth magic: ";if chose=1 || chose=13 str2="#Resist Fire magic: ";if chose=2 || chose=14 str2="#Resist Water magic: ";
    if chose=3 || chose=15 str2="#Resist Wind magic: ";if chose=4 || chose=16 str2="#Resist Physical status: ";if chose=5 || chose=17 str2="#Resist Magical status: ";
    if(look=0) {//Equip Accessories
    if(row=0) {//Neclass
    if(global.hasit[2,chose]>0 && global.equip[2,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[2,i]=0;global.mresist[i]=0;}global.equip[2,chose]=1;global.acc=chose*3;
    if chose=4 global.mresist[chose] = 25+(global.pl1[1,9]*global.pl1[1,2]);
    if chose !=4 global.mresist[chose] = 25+(global.pl1[1,10]*global.pl1[1,2]);
    global.des[2]=str+" equipped"+str2+string(global.mresist[chose])+"%";
    set=1;
    }
    if(global.hasit[2,chose]>0 && global.equip[2,chose]=1 && set=0) {
    global.equip[2,chose]=0;global.mresist[chose] = 0;global.acc=0;global.des[2]=str+" not equipped";
    set=1;
    }
    }
    if(row=1) {//Ring
    if(global.hasit[2,chose]>0 && global.equip[2,chose]=0 && set=0) {
    for(var i=0;i<11;i+=1;){global.equip[2,i+12]=0;global.mresist[i+12]=0;}global.equip[2,chose]=1;global.acc2=(chose-12)*3;
    if chose=16 global.mresist[chose] = 25+(global.pl1[1,9]*global.pl1[1,2]);
    if chose !=16 global.mresist[chose] = 25+(global.pl1[1,10]*global.pl1[1,2]);
    global.des[2]=str+" equipped"+str2+string(global.mresist[chose])+"%";
    set=1;
    }
    if(global.hasit[2,chose]>0 && global.equip[2,chose]=1 && set=0) {
    global.equip[2,chose]=0;global.mresist[chose] = 0;global.acc2=0;global.des[2]=str+" not equipped";
    set=1;
    }
    }
    }
    if(look=1) {//View Accessories
    if(row<2) {
    if global.equip[2,chose]=1 && global.hasit[2,chose]=1 global.des[2]=str+" equipped"+str2+string(global.mresist[chose])+"%";
    if global.equip[2,chose]=0 && global.hasit[2,chose]=1 global.des[2]=str+" not equipped";
    if(global.hasit[2,chose]=0) {
    for(var i=0;i<23;i+=1){if global.equip[2,i]=1 maine=1;}
    if maine=1 global.des[2]=str+" equipped"+str2+string(global.mresist[chose])+"%";
    if maine=0 global.des[2]="No accessories equipped!";
    }
    }
    }
    }
    if(mtype=4) {
    if(chose=0){str="HP potion";str2="#Restore health";global.name[3]="Health";}if(chose=1){str="MP potion";str2="#Restore magic";global.name[3]="Manna";}
    if(chose=2){str="Antidote";str2="#Cure poisoning";global.name[3]="Antidote";}if(chose=3){str="Cure potion";str2="#Cure disease";global.name[3]="Cure";}
    if(chose=4){str="Anti-paralysis potion#";str2="#Cure paralysis";global.name[3]="Release";}if(chose=5){str="Vocalize potion#";str2="#Cure silence";global.name[3]="Speak";}
    if(look=0) {//Equip items
    if(global.hasit[3,chose]>0 && global.equip[3,chose]=0 && set=0) {
    for(var i=0;i<59;i+=1;){global.equip[3,i]=0;}global.equip[3,chose]=1;global.itm=chose;
    global.des[3]=str+" equipped"+str2+"#Own "+string(global.hasit[3,chose]);
    set=1;
    }
    if(global.hasit[3,chose]>0 && global.equip[3,chose]=1 && set=0) {
    global.equip[3,chose]=0;global.name[3]="";global.itm=0;global.des[3]=str+" not equipped"+"#Own "+string(global.hasit[3,chose]);
    set=1;
    }
    }
    if(look=1) {//View items
    if(row<5) {
    if global.equip[3,chose]=1 && global.hasit[3,chose]=1 global.des[3]=str+" equipped"+str2+"#Own "+string(global.hasit[3,chose]);
    if global.equip[3,chose]=0 && global.hasit[3,chose]=1 global.des[3]=str+" not equipped"+"#Own "+string(global.hasit[3,chose]);
    if(global.hasit[3,chose]=0) {
    for(var i=0;i<59;i+=1){if global.equip[3,i]=1 maine=1;}
    if maine=1 global.des[3]=str+" is equipped"+"#Own "+string(global.hasit[3,chose]);
    if maine=0 global.des[3]="No items equipped!";
    }
    }
    }
    if(look=2 && global.hasit[3,chose]>0 && set=0) {//Use items
    if(chose=0 && global.pl1[1,0]<global.pl1[1,1]){global.pl1[1,0]=global.pl1[1,1];global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=0;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//hp
    if(chose=1 && global.pl1[1,5]<global.pl1[1,6]){global.pl1[1,5]=global.pl1[1,6];global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=1;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//mp
    if(chose=2 && global.aflict[0] !=0){global.aflict[0]=0;global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=2;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//antidote
    if(chose=3 && global.aflict[1] !=0){global.aflict[1]=0;global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=3;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//disease
    if(chose=4 && global.aflict[2] !=0){global.aflict[2]=0;global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=4;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//cure paralysis
    if(chose=5 && global.aflict[3] !=0){global.aflict[3]=0;global.hasit[3,chose] -=1;if(instance_exists(cha1))
    {with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=useMe;image_single=5;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}//cure silenced
    if global.aflict[0]=0 && global.aflict[1]=0 && global.aflict[2]=0 && global.aflict[3]=0 global.pl1[0,2]="Normal";
    if global.aflict[0]=1 && global.aflict[1]=0 && global.aflict[2]=0 && global.aflict[3]=0 global.pl1[0,2]="Poisoned";
    if global.aflict[0]=0 && global.aflict[1]=1 && global.aflict[2]=0 && global.aflict[3]=0 global.pl1[0,2]="Diseased";
    if global.aflict[0]=0 && global.aflict[1]=0 && global.aflict[2]=1 && global.aflict[3]=0 global.pl1[0,2]="Paralysis";
    if global.aflict[0]=0 && global.aflict[1]=0 && global.aflict[2]=0 && global.aflict[3]=1 global.pl1[0,2]="Silenced";
    global.des[3]="You used "+str+" to"+str2;if(global.menu1=0 && instance_exists(menu)){menu.alarm[2]=100;menu.snl3=3;menu.mtxts=global.des[3];menu.textcolor=c_lime;}
    set=1;
    }
    }
    if(mtype=5) {
    if(chose=0){str="Heal";str2="#Restore your health";global.name[4]="Heal";}if(chose=1){str="Cure";str2="#Restore your status";global.name[4]="Cure";}
    if(chose=2){str="Poison";str2="#Poison the enemy";global.name[4]="Poison";}if(chose=3){str="Paralyse";str2="#Paralyse the enemy";global.name[4]="Paralise";}
    if(chose=4){str="Banish";str2="#Banish the enemy";global.name[4]="Banish";}if(chose=5){str="Tremmor";str2="#Inflict earth damage";global.name[4]="Tremmor";}
    if(chose=6){str="Incenerate";str2="#Inflict fire damage";global.name[4]="Fire";}if(chose=7){str="Tidal surge";str2="#Inflict water damage";global.name[4]="Wave";}
    if(chose=8){str="Lightning bolt";str2="#Electrocute the enemy";global.name[4]="Bolt";}
    if(look=0) {//Equip magic
    if(global.hasit[4,chose]>0 && global.equip[4,chose]=0 && set=0) {
    for(var i=0;i<59;i+=1;){global.equip[4,i]=0;}global.equip[4,chose]=1;global.mgk=chose;
    global.des[4]=str+" equipped"+str2
    set=1;
    }
    if(global.hasit[4,chose]>0 && global.equip[4,chose]=1 && set=0) {
    global.equip[4,chose]=0;global.name[4]="";global.mgk=0;global.des[4]=str+" not equipped"
    set=1;
    }
    }
    if(look=1) {//View magic
    if(row<5) {
    if global.equip[4,chose]=1 && global.hasit[4,chose]=1 global.des[4]=str+" equipped"+str2
    if global.equip[4,chose]=0 && global.hasit[4,chose]=1 global.des[4]=str+" not equipped"
    if(global.hasit[4,chose]=0) {
    for(var i=0;i<59;i+=1){if global.equip[4,i]=1 maine=1;}
    if maine=1 global.des[4]=str+" is equipped"
    if maine=0 global.des[4]="No magic equipped!";
    }
    }
    }
    if(look=2 && global.hasit[4,chose]>0 && set=0) {//Use magic
    if(chose=0 && global.pl1[1,0]<global.pl1[1,1] && global.pl1[1,5]>=5)//restore hp
    {global.pl1[1,0]=global.pl1[1,1];global.pl1[1,5] -=5;
    if(instance_exists(cha1)){with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=mgkSpells;image_single=0;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}
    if(chose=1 && global.pl1[1,5]<global.pl1[1,6] && global.pl1[1,5]>=5 &&(global.aflict[0]>0 || global.aflict[1]>0 || global.aflict[2]>0 || global.aflict[3]>0))//cure status
    {for(var i=0;i<4;i+=1;){global.aflict[i]=0;}global.pl1[0,2]="Normal";global.pl1[1,5] -=5;
    if(instance_exists(cha1)){with(instance_create(cha1.x,cha1.y,spellcast)){followwho=0;sprite_index=mgkSpells;image_single=1;depth=cha1.depth-20;image_xscale=23/32;image_yscale=51/32;}}}
    if(global.areanum=3) {//in fight room only
    if(chose=2 && global.pl1[1,5]>=5)//poison enemy
    {global.pl1[1,5] -=5;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=2;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=2;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=3 && global.pl1[1,5]>=5)//paralise enemy
    {global.pl1[1,5] -=5;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=3;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=3;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=4 && global.pl1[1,5]>=5)//bannish enemy
    {global.pl1[1,5] -=5;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=4;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=4;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=5 && global.pl1[1,5]>=10)//tremmor
    {global.pl1[1,5] -=10;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=5;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=5;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=6 && global.pl1[1,5]>=10)//incinerate enemy
    {global.pl1[1,5] -=10;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=6;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=6;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=7 && global.pl1[1,5]>=10)//tidal wave to enemy
    {global.pl1[1,5] -=10;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=7;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=7;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    if(chose=8 && global.pl1[1,5]>=10)//lightning bolt to enemy
    {global.pl1[1,5] -=10;if(instance_exists(monster)){with(instance_create(monster.x,monster.y,spellcast))
    {followwho=2;sprite_index=mgkSpells;image_single=8;depth=monster.depth-20;image_xscale=sprite_get_width(monster.sprite_index)/32;image_yscale=sprite_get_height(monster.sprite_index)/32;}}
    if(instance_exists(villin)){with(instance_create(villin.x,villin.y,spellcast))
    {followwho=3;sprite_index=mgkSpells;image_single=8;depth=villin.depth-20;image_xscale=sprite_get_width(villin.sprite_index)/32;image_yscale=sprite_get_height(villin.sprite_index)/32;}}}
    }
    global.des[4]="You cast "+str+" to"+str2;if(global.menu1=0 && instance_exists(menu)){menu.alarm[2]=100;menu.snl3=3;menu.mtxts=global.des[4];menu.textcolor=c_lime;}
    set=1;
    }
    }
    And the corresponding draw event code:
    Code:
    if(global.menu1>0) {
    draw_set_color(c_red);draw_set_font(font1);
    if subm !=1 draw_sprite(spr_menu,0,0,15);
    if subm !=1 draw_sprite(smenu,global.mimg,x,y);
    if (global.menu1 !=3 || (global.menu1=3 && global.submenu=0)) && (global.mimg=0 || global.mimg mod 2 = 0) draw_text(x+104,y+5,"Stats");
    if (global.menu1 !=3 || (global.menu1=3 && global.submenu=0)) && global.mimg mod 2 !=0 draw_text(x+106,y+7,"Stats");draw_set_font(-1);
    }
    if(global.menu1=1){//save game
    draw_set_color(c_navy);draw_text(x+21,y-2,string((snl+1)));
    if(snl=0){draw_triangle(x+10,y+26,x+20,y+26,x+15,y+21,0);}
    if(snl=1){draw_triangle(x+10,y+26,x+20,y+26,x+15,y+21,0);draw_triangle(x+27,y+21,x+37,y+21,x+32,y+26,0);}
    if(snl=2){draw_triangle(x+27,y+21,x+37,y+21,x+32,y+26,0);}
    }
    if(global.menu1=2) {//load game
    draw_set_color(c_navy);draw_text(x+70,y-2,string((snl2+1)));
    if(snl2=0){draw_triangle(x+59,y+26,x+69,y+26,x+64,y+21,0);}
    if(snl2=1){draw_triangle(x+59,y+26,x+69,y+26,x+64,y+21,0);draw_triangle(x+76,y+21,x+86,y+21,x+81,y+26,0);}
    if(snl2=2){draw_triangle(x+76,y+21,x+86,y+21,x+81,y+26,0);}
    }
    if(global.menu1=3) {//stats, equip, use, info & options
    if global.submenu !=0 && subm !=1 && snl3 !=2 draw_sprite_ext(spr_menu,0,256,384,1.024,mscaley,90,-1,1);
    if global.submenu !=0 && subm !=1 && snl3 =2 draw_sprite_ext(spr_menu,0,256,128,mscalex,mscaley2,0,-1,1);
    if(global.submenu=0) {//player stats
    draw_set_color(c_navy);
    draw_text(x-22,70,string(global.pl1[0,0])+"'s Status: "+ global.pl1[0,2]
    +"#Hp: "+string(global.pl1[1,0])+" / "+string(global.pl1[1,1])
    +"#Mp: "+string(global.pl1[1,5])+" / "+string(global.pl1[1,6])
    +"#Lv: "+string(global.pl1[1,2])+" Exp: "+string(global.pl1[1,3])+" / "+string(global.pl1[1,4])
    +"#Atk: "+string(global.pl1[1,7])+" / "+string(((global.pl1[1,7] * global.pl1[1,2])+(global.wPartC[0]+global.wPartC[1]+global.wPartC[2]+global.wPartC[3])+(global.pl1[1,7]*global.pl1[1,2])))
    +" Def: "+string(global.pl1[1,8])+" / "+string(((global.pl1[1,8] * global.pl1[1,2])+global.aPartC[0]+global.aPartC[1]+global.aPartC[2]+global.aPartC[3]))
    +"#Stat resist: "+string((global.pl1[1,9] * global.pl1[1,2]))+" / "+string(((global.pl1[1,9] * global.pl1[1,2])+global.mresist[4]+global.mresist[16]))
    +"#Mag resist: "+string((global.pl1[1,10] * global.pl1[1,2]))+" / "+string(((global.pl1[1,10] * global.pl1[1,2])+global.mresist[0]+global.mresist[1]+global.mresist[2]+global.mresist[3]+global.mresist[5]+global.mresist[12]+global.mresist[13]+global.mresist[14]+global.mresist[15]+global.mresist[17]))
    +"#Gold: $"+string(global.pl1[1,12]));
    if subm !=1 draw_sprite_ext(spr_menu,0,261,347,0.256,0.083,0,-1,1);
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(mp[5,0],mp[5,1],mp[5,0]+32,mp[5,1]+16,0);
    draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(mp[5,0]-1,mp[5,1]-1,mp[5,0]+34,mp[5,1]+18,1);
    draw_set_font(font1);draw_set_color(c_aqua);
    draw_text(mp[5,0],mp[5,1]-1,"Wep");
    draw_set_font(-1);draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x-12,y+162,x+29,y+204,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    draw_sprite_ext(spr_sword_blade,global.sword[global.wep,0],x,220,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_hilt,global.sword[global.wep,1],x,220,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_hndl,global.sword[global.wep,2],x,220,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_pom,global.sword[global.wep,3],x,220,0.5,0.5,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+162,x+111,y+204,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[1,(global.arm/4)]=1 draw_sprite_ext(armors,global.arm+1,x+90,208,ascalex41,ascaley42,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+152,y+162,x+193,y+204,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[1,(12+global.arm1)]=1 draw_sprite_ext(gloves,global.arm1,(x+174)-(16*ascalex32),214-(16*ascaley32),ascalex32,ascaley32,0,-1,1);
    draw_text(x-22,234,"Weapon   Armor    Gloves");
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x-12,y+227,x+29,y+268,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[1,(24+(global.arm2/4))]=1 draw_sprite_ext(helms,global.arm2+1,x+8,288,ascalex41,ascaley42,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+227,x+111,y+268,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[1,(36+global.arm3)]=1 draw_sprite_ext(boots,global.arm3,(x+92)-(16*ascalex32),278-(16*ascaley32),ascalex32,ascaley32,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+152,y+227,x+193,y+268,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[2,(global.acc/3)]=1 draw_sprite_ext(accys,global.acc,x+162,283,ascalex32,ascaley32,0,-1,1);
    draw_text(x-10,298,"Helm     Boots    Neclass");
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x-12,y+292,x+29,y+333,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[2,(12+(global.acc2/3))]=1 draw_sprite_ext(accys2,global.acc2,x-4,340,ascalex32,ascaley32,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+292,x+111,y+333,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[3,global.itm]=1 draw_sprite_ext(items,global.itm,(x+104)-(16*ascalex32),354-(16*ascaley32),0.5,0.5,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+152,y+292,x+193,y+333,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[4,global.mgk]=1 draw_sprite_ext(magics,global.mgk,(x+186)-(16*ascalex32),354-(16*ascaley32),0.5,0.5,0,-1,1);
    draw_text(x-10,362,"Ring       Item     Magic");
    }
    if(global.submenu>0 && global.submenu<6){
    draw_set_color(c_black);draw_set_alpha(0.5);
    if hl1<5 draw_rectangle(mp[hl1,hl2+hl2],mp[hl1,hl2+hl2+1],mp[hl1,hl2+hl2]+32,mp[hl1,hl2+hl2+1]+32,0);
    if hl1=5 draw_rectangle(mp[hl1,hl2+hl2],mp[hl1,hl2+hl2+1],mp[hl1,hl2+hl2]+32,mp[hl1,hl2+hl2+1]+16,0);
    draw_set_color(c_lime);draw_set_alpha(1);
    if hl1<5 draw_rectangle(mp[hl1,hl2+hl2]-1,mp[hl1,hl2+hl2+1]-1,mp[hl1,hl2+hl2]+34,mp[hl1,hl2+hl2+1]+34,1);
    if hl1=5 draw_rectangle(mp[hl1,hl2+hl2]-1,mp[hl1,hl2+hl2+1]-1,mp[hl1,hl2+hl2]+34,mp[hl1,hl2+hl2+1]+18,1);
    draw_set_font(font1);draw_set_color(c_aqua);
    draw_text(mp[5,0],mp[5,1]-1,prev[global.submenu]);
    draw_text(mp[5,2],mp[5,3]-1,next[global.submenu]);
    if(global.submenu=4 || global.submenu=5) {
    if(pick2=0) {
    draw_set_color(c_lime);draw_text(mp[5,4],mp[5,5]-1,"EQUIP");
    draw_set_color(c_gray);draw_text(mp[5,6],mp[5,7]-1," use");
    }
    if(pick2=2) {
    draw_set_color(c_gray);draw_text(mp[5,4],mp[5,5]-1,"equip");
    draw_set_color(c_lime);draw_text(mp[5,6],mp[5,7]-1," USE");
    }
    }
    draw_set_font(-1);
    }
    if(global.submenu=1) {//Equip weapons
    draw_set_color(c_navy);draw_text(x-22,70,global.des[0]);
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+100,y+5,"wepon");if global.mimg mod 2 !=0 draw_text(x+102,y+7,"wepon");
    draw_set_font(-1);
    if global.hasit[0,0]=1 draw_sprite_ext(spr_sword_blade,0,mp[0,0]+(12*wscalex),mp[0,1]+(60*wscaley),wscalex,wscaley,0,-1,1);//blades
    if global.hasit[0,1]=1 draw_sprite_ext(spr_sword_blade,1,mp[0,2]+(12*wscalex),mp[0,3]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,2]=1 draw_sprite_ext(spr_sword_blade,2,mp[0,4]+(12*wscalex),mp[0,5]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,3]=1 draw_sprite_ext(spr_sword_blade,3,mp[0,6]+(12*wscalex),mp[0,7]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,12]=1 draw_sprite_ext(spr_sword_hilt,0,mp[1,0]+(16*wscalex),mp[1,1]+(60*wscaley),wscalex,wscaley,0,-1,1);//hilts
    if global.hasit[0,13]=1 draw_sprite_ext(spr_sword_hilt,1,mp[1,2]+(16*wscalex),mp[1,3]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,14]=1 draw_sprite_ext(spr_sword_hilt,2,mp[1,4]+(16*wscalex),mp[1,5]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,15]=1 draw_sprite_ext(spr_sword_hilt,3,mp[1,6]+(16*wscalex),mp[1,7]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,24]=1 draw_sprite_ext(spr_sword_hndl,0,mp[2,0]+(20*wscalex),mp[2,1]+(60*wscaley),wscalex,wscaley,0,-1,1);//handles
    if global.hasit[0,25]=1 draw_sprite_ext(spr_sword_hndl,1,mp[2,2]+(20*wscalex),mp[2,3]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,26]=1 draw_sprite_ext(spr_sword_hndl,2,mp[2,4]+(20*wscalex),mp[2,5]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,27]=1 draw_sprite_ext(spr_sword_hndl,3,mp[2,6]+(20*wscalex),mp[2,7]+(60*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,36]=1 draw_sprite_ext(spr_sword_pom,0,mp[3,0]+(28*wscalex),mp[3,1]+(50*wscaley),wscalex,wscaley,0,-1,1);//pommels
    if global.hasit[0,37]=1 draw_sprite_ext(spr_sword_pom,1,mp[3,2]+(28*wscalex),mp[3,3]+(50*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,38]=1 draw_sprite_ext(spr_sword_pom,2,mp[3,4]+(28*wscalex),mp[3,5]+(50*wscaley),wscalex,wscaley,0,-1,1);
    if global.hasit[0,39]=1 draw_sprite_ext(spr_sword_pom,3,mp[3,6]+(28*wscalex),mp[3,7]+(50*wscaley),wscalex,wscaley,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+227,x+111,y+268,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if(global.fist=0){
    draw_sprite_ext(spr_sword_blade,global.sword[global.wep,0],x+82,285,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_hilt,global.sword[global.wep,1],x+82,285,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_hndl,global.sword[global.wep,2],x+82,285,0.5,0.5,0,-1,1);
    draw_sprite_ext(spr_sword_pom,global.sword[global.wep,3],x+82,285,0.5,0.5,0,-1,1);
    }
    draw_text(x+30,298,"Craft weapon");
    }
    if(global.submenu=2) {//Equip armor
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+102,y+5,"armor");if global.mimg mod 2 !=0 draw_text(x+104,y+7,"armor");
    draw_set_font(-1);
    draw_set_color(c_navy);draw_text(x-22,70,global.des[1]);
    if global.hasit[1,0]=1 draw_sprite_ext(armors,1,mp[0,0]+(11*ascalex),mp[0,1]+(25*ascaley),ascalex,ascaley,0,-1,1);//body
    if global.hasit[1,1]=1 draw_sprite_ext(armors,5,mp[0,2]+(11*ascalex),mp[0,3]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,2]=1 draw_sprite_ext(armors,9,mp[0,4]+(11*ascalex),mp[0,5]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,3]=1 draw_sprite_ext(armors,13,mp[0,6]+(11*ascalex),mp[0,7]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,4]=1 draw_sprite_ext(armors,17,mp[0,8]+(11*ascalex),mp[0,9]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,12]=1 draw_sprite(gloves,0,mp[1,0],mp[1,1]);//gloves
    if global.hasit[1,13]=1 draw_sprite(gloves,1,mp[1,2],mp[1,3]);
    if global.hasit[1,14]=1 draw_sprite(gloves,2,mp[1,4],mp[1,5]);
    if global.hasit[1,15]=1 draw_sprite(gloves,3,mp[1,6],mp[1,7]);
    if global.hasit[1,16]=1 draw_sprite(gloves,4,mp[1,8],mp[1,9]);
    if global.hasit[1,24]=1 draw_sprite_ext(helms,1,mp[2,0]+(11*ascalex),mp[2,1]+(25*ascaley),ascalex,ascaley,0,-1,1);//helmets
    if global.hasit[1,25]=1 draw_sprite_ext(helms,5,mp[2,2]+(11*ascalex),mp[2,3]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,26]=1 draw_sprite_ext(helms,9,mp[2,4]+(11*ascalex),mp[2,5]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,27]=1 draw_sprite_ext(helms,13,mp[2,6]+(11*ascalex),mp[2,7]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,28]=1 draw_sprite_ext(helms,17,mp[2,8]+(11*ascalex),mp[2,9]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[1,36]=1 draw_sprite(boots,0,mp[3,0],mp[3,1]);//boots
    if global.hasit[1,37]=1 draw_sprite(boots,1,mp[3,2],mp[3,3]);
    if global.hasit[1,38]=1 draw_sprite(boots,2,mp[3,4],mp[3,5]);
    if global.hasit[1,39]=1 draw_sprite(boots,3,mp[3,6],mp[3,7]);
    if global.hasit[1,40]=1 draw_sprite(boots,4,mp[3,8],mp[3,9]);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x-12,y+227,x+29,y+268,0);
    draw_set_alpha(1);
    if global.equip[1,(12+global.arm1)]=1 draw_sprite(gloves,global.arm1,x-7,262);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+217,x+111,y+273,0);
    draw_set_alpha(1);
    if(instance_exists(cha1)){
    draw_sprite(spr_npc,4,x+91,275);
    if global.equip[1,(global.arm/4)]=1 draw_sprite(armors,global.arm+1,x+91,275);
    if global.equip[1,(24+(global.arm2/4))]=1 draw_sprite(helms,global.arm2+1,x+91,275);
    if global.equip[2,(global.acc/3)]=1 draw_sprite(accys,global.acc+1,x+91,275);
    if global.equip[2,12+(global.acc2/3)]=1 draw_sprite(accys2,global.acc2+1,x+91,275);
    } 
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+152,y+227,x+193,y+268,0);
    draw_set_alpha(1);
    if global.equip[1,(36+global.arm3)]=1 draw_sprite(boots,global.arm3,x+157,262);
    draw_set_color(c_navy);
    draw_text(x-15,303,"Gloves    Armor    Boots");
    }
    if(global.submenu=3) {//Equip accessory
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+109,y+5,"acc.");if global.mimg mod 2 !=0 draw_text(x+111,y+7,"acc.");
    draw_set_font(-1);
    draw_set_color(c_navy);draw_text(x-22,70,global.des[2]);
    if global.hasit[2,0]=1 draw_sprite_ext(accys,1,mp[0,0]+(11*ascalex),mp[0,1]+(25*ascaley),ascalex,ascaley,0,-1,1);//neclasses
    if global.hasit[2,1]=1 draw_sprite_ext(accys,4,mp[0,2]+(11*ascalex),mp[0,3]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,2]=1 draw_sprite_ext(accys,7,mp[0,4]+(11*ascalex),mp[0,5]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,3]=1 draw_sprite_ext(accys,10,mp[0,6]+(11*ascalex),mp[0,7]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,4]=1 draw_sprite_ext(accys,13,mp[0,8]+(11*ascalex),mp[0,9]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,5]=1 draw_sprite_ext(accys,16,mp[0,10]+(11*ascalex),mp[0,11]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,12]=1 draw_sprite_ext(accys2,19,mp[1,0],mp[1,1]+(25*ascaley),ascalex,ascaley,0,-1,1);//rings
    if global.hasit[2,13]=1 draw_sprite_ext(accys2,22,mp[1,2],mp[1,3]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,14]=1 draw_sprite_ext(accys2,25,mp[1,4],mp[1,5]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,15]=1 draw_sprite_ext(accys2,28,mp[1,6],mp[1,7]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,16]=1 draw_sprite_ext(accys2,31,mp[1,8],mp[1,9]+(25*ascaley),ascalex,ascaley,0,-1,1);
    if global.hasit[2,17]=1 draw_sprite_ext(accys2,34,mp[1,10],mp[1,11]+(25*ascaley),ascalex,ascaley,0,-1,1);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+217,x+111,y+273,0);
    draw_set_alpha(1);
    if(instance_exists(cha1)){
    draw_sprite(spr_npc,4,x+91,275);
    if global.equip[1,(global.arm/4)]=1 draw_sprite(armors,global.arm+1,x+91,275);
    if global.equip[1,(24+(global.arm2/4))]=1 draw_sprite(helms,global.arm2+1,x+91,275);
    if global.equip[2,(global.acc/3)]=1 draw_sprite(accys,global.acc+1,x+91,275);
    if global.equip[2,12+(global.acc2/3)]=1 draw_sprite(accys2,global.acc2+1,x+91,275);
    }
    draw_set_color(c_navy);
    draw_text(x+45,303,"Accessories");
    }
    if(global.submenu=4) {//Equip / use item
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+107,y+5,"item");if global.mimg mod 2 !=0 draw_text(x+109,y+7,"item");
    draw_set_font(-1);
    draw_set_color(c_navy);draw_text(x-22,70,global.des[3]);
    if global.hasit[3,0]>0 draw_sprite(items,0,mp[0,0],mp[0,1]);//Items
    if global.hasit[3,1]>0 draw_sprite(items,1,mp[0,2],mp[0,3]);
    if global.hasit[3,2]>0 draw_sprite(items,2,mp[0,4],mp[0,5]);
    if global.hasit[3,3]>0 draw_sprite(items,3,mp[0,6],mp[0,7]);
    if global.hasit[3,4]>0 draw_sprite(items,4,mp[0,8],mp[0,9]);
    if global.hasit[3,5]>0 draw_sprite(items,5,mp[0,10],mp[0,11]);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+227,x+111,y+268,0);
    draw_set_alpha(1);draw_set_color(c_navy);
    if global.equip[3,global.itm]=1 draw_sprite(items,global.itm,x+75,262);
    draw_text(x+35,298,"Equipped Item");
    }
    if(global.submenu=5) {//Equip / use magic
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+101,y+5,"magic");if global.mimg mod 2 !=0 draw_text(x+103,y+7,"magic");
    draw_set_font(-1);
    draw_set_color(c_navy);draw_text(x-22,70,global.des[4]);
    if global.hasit[4,0]=1 draw_sprite(magics,0,mp[0,0],mp[0,1]);//Magics
    if global.hasit[4,1]=1 draw_sprite(magics,1,mp[0,2],mp[0,3]);
    if global.hasit[4,2]=1 draw_sprite(magics,2,mp[0,4],mp[0,5]);
    if global.hasit[4,3]=1 draw_sprite(magics,3,mp[0,6],mp[0,7]);
    if global.hasit[4,4]=1 draw_sprite(magics,4,mp[0,8],mp[0,9]);
    if global.hasit[4,5]=1 draw_sprite(magics,5,mp[0,10],mp[0,11]);
    if global.hasit[4,6]=1 draw_sprite(magics,6,mp[0,12],mp[0,13]);
    if global.hasit[4,7]=1 draw_sprite(magics,7,mp[0,14],mp[0,15]);
    if global.hasit[4,8]=1 draw_sprite(magics,8,mp[0,16],mp[0,17]);
    if global.hasit[4,9]=1 draw_sprite(magics,9,mp[0,18],mp[0,19]);
    draw_set_alpha(0.5);draw_set_color(c_black);
    draw_roundrect(x+70,y+227,x+111,y+268,0);
    draw_set_alpha(1);
    draw_set_color(c_navy);
    if global.equip[4,global.mgk]=1 draw_sprite(magics,global.mgk,x+75,262);
    draw_text(x+30,298,"Equipped magic");
    }
    if(global.submenu=6) {//Info and credits
    if(subm !=1) {
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+109,y+5,"Info");if global.mimg mod 2 !=0 draw_text(x+111,y+7,"Info");
    draw_set_font(-1);
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(mp[5,0],mp[5,1],mp[5,0]+32,mp[5,1]+16,0);
    draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(mp[5,0]-1,mp[5,1]-1,mp[5,0]+34,mp[5,1]+18,1);
    draw_set_font(font1);draw_set_color(c_aqua);
    draw_text(mp[5,0],mp[5,1]-1,"Stat");
    draw_set_font(-1);
    draw_set_color(c_navy);
    draw_triangle(x+108,y+26,x+118,y+26,x+113,y+21,0);draw_triangle(x+125,y+21,x+135,y+21,x+130,y+26,0);
    }
    if(snl3<3) {
    if(subm=1) {
    draw_sprite_ext(spr_menu,0,256,128,mscalex,mscaley2,0,-1,1);draw_sprite(smenu,global.mimg,256,128);
    draw_set_font(font1);draw_set_color(c_red);if global.mimg mod 2 = 0 draw_text(x+227+100,y+99+4,"Option");if global.mimg mod 2 !=0 draw_text(x+227+102,y+99+6,"Option");
    draw_set_font(-1);
    if(setting=0) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+12,y+99+45,x+227+74,y+99+83,1);
    draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+11,y+99+44,x+227+75,y+99+84,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(setting=1) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+91,y+99+45,x+227+152,y+99+83,1);
    if global.msound=0 draw_set_color(c_red);if global.msound=1 draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+90,y+99+44,x+227+153,y+99+84,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(setting=2) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+170,y+99+45,x+227+232,y+99+83,1);
    if global.gsound=0 draw_set_color(c_red);if global.gsound=1 draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+169,y+99+44,x+227+233,y+99+84,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(setting=3) {
    draw_set_alpha(0.5);
    draw_line_width_color(x+227+11,y+99+120,x+227+11+((sm2/100)*225),y+99+120,26,c_aqua,c_red);
    draw_set_alpha(1);
    draw_set_color(c_lime);
    draw_rectangle(x+227+11,y+99+107,x+227+238,y+99+134,1);
    }
    if(setting=4) {
    draw_set_alpha(0.5);
    draw_line_width_color(x+227+11,y+99+153,x+227+11+((sm3/100)*225),y+99+153,26,c_aqua,c_red);
    draw_set_alpha(1);
    draw_set_color(c_lime);
    draw_rectangle(x+227+11,y+99+140,x+227+238,y+99+167,1);
    }
    if(setting=5) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+12,y+99+179,x+227+74,y+99+217,1);
    if global.vsync=0 draw_set_color(c_red);if global.vsync=1 draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+11,y+99+178,x+227+75,y+99+218,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(setting=6) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+91,y+99+179,x+227+152,y+99+217,1);
    draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+90,y+99+178,x+227+153,y+99+218,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(setting=7) {
    draw_set_color(textcolor);draw_text(270,160,mtxts);
    if(global.osys=0) {
    draw_set_color(c_lime);
    draw_line_width(x+227+11,y+99+153,x+227+237,y+99+153,26);
    draw_rectangle(x+227+11,y+99+140,x+227+238,y+99+167,1);
    if(keyboard_string ="" && global.cname=0){draw_set_color(textcolor);draw_text(x+227+12,y+99+146,global.pl1[0,0]);}
    if(keyboard_string ="" && global.cname=1){draw_set_color(c_gray);draw_text(x+227+12,y+99+146,global.holdname);}
    if(keyboard_string !=""){draw_set_color(textcolor);draw_text(x+227+12,y+99+146,keyboard_string);}
    }
    if(global.osys=1) {
    draw_set_color(c_lime);
    draw_line_width(x+227+11,y+99+120,x+227+238,y+99+120,26);
    if(global.droidText ="" && global.cname=0){draw_set_color(textcolor);draw_text(x+227+12,y+99+112,global.pl1[0,0]);}
    if(global.droidText ="" && global.cname=1){draw_set_color(c_gray);draw_text(x+227+12,y+99+112,global.holdname);}
    if(global.droidText !=""){draw_set_color(textcolor);draw_text(x+227+12,y+99+112,global.droidText);}
    }
    draw_set_color(c_black);
    draw_rectangle(x+227+170,y+99+179,x+227+232,y+99+217,1);
    draw_set_color(c_lime);
    draw_rectangle(x+227+169,y+99+178,x+227+233,y+99+218,0);
    if(global.osys=0) {draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    if(global.cname=0){draw_set_color(textcolor);draw_text(x+227+170,y+99+183,"Change# name");}
    if(global.cname=1 && keyboard_string =""){draw_set_color(c_gray);draw_text(x+227+170,y+99+183,global.con[6]+"#to set");}
    if(global.cname=1 && keyboard_string !=""){draw_set_color(textcolor);draw_text(x+227+170,y+99+183,global.con[6]+"#to set");}
    }
    if(setting=8 && global.osys=0) {
    draw_set_color(c_black);draw_set_alpha(0.5);
    draw_rectangle(x+227+170,y+99+96,x+227+232,y+99+136,1);
    if global.fullscr=0 draw_set_color(c_yellow);if global.fullscr=1 draw_set_color(c_lime);draw_set_alpha(1);
    draw_rectangle(x+227+169,y+99+95,x+227+233,y+99+135,0);
    draw_set_color(textcolor);
    if global.fullscr=0 draw_text(x+227+171,y+99+97,"Window# mode");
    if global.fullscr=1 draw_text(x+227+176,y+99+97,"  Full#screen");
    }
    }
    if(snl3>-2 && snl3<2 && subm !=1){draw_set_color(textcolor);draw_text(270,143,mtxts);}
    if(snl3=2 && setting !=7 && subm=1){draw_set_color(textcolor);draw_text(270,160,mtxts);}
    }
    }
    }
    Hahaha, so if I can even cut down on all that even by 1/2 your tutorial will save me tons of time, thanks for making it.
     
  31. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    As far as I'm aware, that should work perfectly. I don't think it's obvious at all that it didn't work. That is, assuming your camera object works correctly. Is your camera object the top left corner of the screen? It could cause problems if that's not the case.

    Honestly, that method (using objects for slots) has never sat well with me. I feel like I could improve it, but I haven't figured out a good way that still allows the mouse code. I could maybe do it with just checking rectangles compared to the mouse cursor but... I don't think it's any better than this system.

    Yes. I think my code will cut that down by far more than half. :) Good luck. I took a look at people's inventory code which looked like that, and immediately decided to find another way. I wonder why the spoilers aren't working. Do you want me to copy the text of the tutorial and PM it to you? (Just PM me and I will).
     
    Wraithious likes this.
  32. Wraithious

    Wraithious Member

    Joined:
    Jun 24, 2016
    Posts:
    1,166
    Thanks, yea its weird, im on my phone right now and can read it all fine, on my computer I cant fully open yours but can open my spoilers, weird. At least I can read it on my phone
     
    Cloaked Games likes this.
  33. Callum Smith

    Callum Smith Member

    Joined:
    Apr 2, 2018
    Posts:
    5
    That may well be why. I have the camera set to be created by the player instance on creation. After tinkering around a bit, I've got it all working now. The only gripe is that the slots tend to move about with the view, whereas the sprites representing the items in the inventory remain still. But I imagine that is the consequence of having a smooth camera that trails behind the player, instead of being absolutely on top of it. Thank you very much for the help!~

    [EDIT]: Actually, hold that thought. I fixed it. If you put the " x = (gox+=view_xview) " ECT, in the draw begin event instead of the step event, it should work fine. I learned it was causing problems because the game would first move the view then move the slot objects.
     
    Last edited: Apr 7, 2018
    Cloaked Games likes this.
  34. Reggies

    Reggies Member

    Joined:
    Jan 28, 2018
    Posts:
    19
    Hi, this probably the dumbest question. But can someone explain me this.

    The "inst.var_slot = slot;" is confusing why does it have a dot between the two variables?

    I'm newbie to coding i want to learn the purpose of this code.
     
    Cloaked Games likes this.
  35. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Reggies likes this.
  36. BlackCoatDalov

    BlackCoatDalov Member

    Joined:
    Jan 5, 2017
    Posts:
    19
    Great tutorial, thank you! One quick question though, even if it may be obvious: is there a way to modify the base items during runtime? For context, I'm making a roguelike, and one thing I wanted to do was have procedurally-generated equipment (i.e. "Flimsy Sharp Short Sword", "Heavy Heated Axe", "Cracked Leather Shield" etc.). Would there be a way to do this? When I tried storing any instances of a weapon object in my inventory early on, it would simply store an id...which was, as far as I know, useless to me.
     
    Cloaked Games likes this.
  37. dizzy1666

    dizzy1666 Member

    Joined:
    Feb 22, 2017
    Posts:
    7
    Nice! Thank you!
     
    Cloaked Games likes this.
  38. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Storing a weapon would simply store an id, and it may be useless to you. The way my system works is basically having a lookup table, which allows you to use any item id in the table to find any piece of information about that item.

    If you wanted procedurally-generated equipment (which is really cool by the way), you would have to modify the lookup table itself, and that would modify all of the items with that id throughout the game, which is probably not what you are looking for. (Although it has certain purpose, and I modified the lookup table for a bit of randomization in my current project). I think you would need your own system to do this. Here's my initial thoughts if you want to get started.

    You probably don't want pre-set item ids, since you want things randomized. You can randomize names with code as simple as
    Code:
    name= choose("Blunt", "Sharp") + " " + choose("Sword", "Axe")
    However, you would probably want those to affect changes on the item instead. So possibly you could make a grid of sort storing modification values for each attribute: a "sharp" weapon deals +2 damage, a "flimsy" weapon has 50% durability. Then you also have a list of base weapons: axes, swords, or whatever, which have pre-programmed stats and effects, which are then modified by some number of random modifiers. Also you can just modify all values by a small amount when spawning an item.

    You can include information about what level they require from the player to spawn, or how deep into the dungeon itself they will spawn, depending on level you can cap a different number of modifiers. That's just my initial suggestions. I haven't coded a system like that before, but if you were looking for where to start, hopefully that helped a bit.
     
  39. Rob

    Rob Member

    Joined:
    Jul 12, 2016
    Posts:
    650
    If you had a value in the array for "Quality" set to a number then you could have a text array that assigned a prefix based on the quality as well as a bonus based on that number elsewhere in your code.

    It can get a little complicated if you have items with different effects that use this system but it's doable!
     
    Cloaked Games likes this.
  40. Nabil Kabour

    Nabil Kabour Member

    Joined:
    Aug 6, 2018
    Posts:
    92
    You are a legend. Thank you so much.
     
    Cloaked Games likes this.
  41. Jorg Gjer

    Jorg Gjer Member

    Joined:
    Nov 8, 2017
    Posts:
    28
    Thank you so much.
    It just worked at first try.
    But i have a question i hope some can help me with.
    If i want to use more than one inventory. Like the player and a chest.
    The objects are global.
    How can i set this up so i can use one code for multiple instances of inventory ?

    Greeting
     
    Cloaked Games likes this.
  42. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    I'm glad I could be helpful!

    For managing the data, it's quite simple. Just use local variables for the inventory ds_grid. (The item index can still be global, it'll just be used for all inventories).

    As for the instances that control the clicking on inventories, there's two ways I can think of. First is just to duplicate the objects and use different ones for different types of inventories. More managble, you can have the slot objects contain a variable which stores their slot and their current assigned inventory. So for the player, when the inventory is opened you create slots and tell them they're connected to the player's inventory grid. Then the chest can create new ones that are connected to the chest's local inventory grid.

    I hope that makes sense. Any questions, feel free to ask!
     
  43. Jorg Gjer

    Jorg Gjer Member

    Joined:
    Nov 8, 2017
    Posts:
    28
    Yes it makes sense.

    But i think its a better to make a new inventnory object when i need it, in stead of have one big with all the items.
    I think i will change the scripts , with an argument for what inventory they are going to work on.

    I'm gone use this to pick up loot from a killed mob and want to be able to just destroy the inventory when its empty.

    What is your thought on this approach ?

    Greeting
     
    Last edited: Sep 29, 2018
    Cloaked Games likes this.
  44. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    All the code for this system is data oriented, so yeah, that approach is what I'd recommend. You don't need multiple objects, just create a new ds_grid to store items for a mob or a chest or an NPC or whatever. Then destroy the ds_grid when you're done. The item index can be used to look up information for items in any of those inventories.

    Basically though, just do what works best. Once you get to complicated projects, the methods can get very complicated and specific. This tutorial really just explains the foundations of using enumerators and a lookup item index to store information about different item ids.
     
  45. Jorg Gjer

    Jorg Gjer Member

    Joined:
    Nov 8, 2017
    Posts:
    28
    Yes , and i dident know much about using ds_grids and enumurators , so you teached me this in a werry easy to anderstand way. Again thank you.
     
    Cloaked Games likes this.
  46. Jorg Gjer

    Jorg Gjer Member

    Joined:
    Nov 8, 2017
    Posts:
    28
    I tested it out and found I had to add a variable to the slot object that holds the grid.
    Code:
    inv = ds_grid_create(4, 2);
    ds_grid_clear(inv, 0);
    
    var slot = 0;
    while (slot < ds_grid_width(inv)){
         var inst = instance_create_layer(x+8+(32*slot), y+8, "lItems", obj_InvSlot);
         inst.var_slot = slot;
         inst.inventory_ds_grid = inv;     // Added this line.
         slot ++;
    }
    So what i did was to add one argument to the scripts that holds the inventory to work on.
    Then let the slot know what inventory grid it belongs to.
    It looks like thats it.

    What I'm not shure , if every slot now holds a refrence or the hole ds grid. (that added line)

    Anyone know ?
     
    Cloaked Games likes this.
  47. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    Yeah, it holds a reference. The ds_grid only exists once, they can all access the same data through that variable you created.

    Actually, I'll update the tutorial later to include using multiple inventories, for people who are interested in it in the future.
     
  48. Nabil Kabour

    Nabil Kabour Member

    Joined:
    Aug 6, 2018
    Posts:
    92
    I don't quite follow you on implementing the crafting system. How would you use grids to store recipes?
     
    Cloaked Games likes this.
  49. Cloaked Games

    Cloaked Games Member

    Joined:
    Jul 4, 2016
    Posts:
    782
    So this system stores each item in the inventory as an item id (which can be used to look up information about that particular item in the item index), and a quantity for each slot. For a crafting system you would check each item in a crafting grid compared to a stored crafting recipe in another grid. For example, say you needed 3 sticks and 2 string to craft a bow. You would store that in a recipe grid with the values:

    (item_stick, 3) and (item_string, 2). These are id/quantity pairs identical to how we manage them everywhere else in the inventory.

    Then you compare that to the crafting grid containing items the player moved into position themselves. If one of the items is item_stick, and there are at least 3, and the other item is item_string and there are at least 2, then you output an option for a bow item to be crafted, and subtract the relevant quantities from the crafting grid inventory when the bow is selected.

    It's not something I can really explain step by step (it depends too much on your specific situation), but hopefully that explains it a little bit better for you to get started and expand the system.
     
    Nabil Kabour likes this.
  50. Nabil Kabour

    Nabil Kabour Member

    Joined:
    Aug 6, 2018
    Posts:
    92
    So if I understand correctly, you have a ds_grid for the crafting slots. Say we have two crafting slots:
    Code:
    global.crafting_slots = ds_grid_create(2, 2);
    The recipe grid, if I follow you, will be the total number of recipes wide, and since we have 2 crafting slots, 4 elements tall. So:
    Code:
    global.recipes = ds_grid_create(recipe.total, 4);
    The underlying logic of checking the correct item and quantity and outputting the right item shouldn't be too difficult.

    Is the basic implementation I described as intended?

    Thanks.
     
    Cloaked Games likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice