The Best Inventory I Know So Far

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
HiddenLife
Newbie
Posts: 16
Joined: Wed Sep 09, 2020 8:10 pm
Contact:

The Best Inventory I Know So Far

#1 Post by HiddenLife »

First of all, sorry for any error. English is not my first language.
The code I will show and explain you is not created by me. I only completed and improved it.
The creator of this inventory (Milkymalk) explained it here (viewtopic.php?t=44730) and I will also copy-paste some parts of his post because he did really a wonderful job in explaining it.
And now, let's get started!

Unlike Milkymalk I will not do a step by step guide. Instead I will directly go to the completed code.
First of all make sure you understand the difference between Python code, Ren'py script language and Ren'Py screen language before you even attempt to do anything that mixes the three. Which thing this inventory does!
Also, everything that has one or more "#" is a comment and therefore Renpy will ignore that line when executing the code. I will use comments to explaing how the code work.
And, for last, I will not deep dive into the code, explaining every detail. I will only give you everything you need to understand it and be able to modify it. If you wish a deeper explanation, check Milkymalk's post or study python language.

Code: Select all

# First we need to tell to RenPy that this code has to be executed at the start of the game and we do so with "init".
# The -5 after init is to execute this before any other "init" with higher number.
# "python" explain to RenPy that what this init will contain is python code and, therefore, should be executed as so.
init -5 python:
# First, let's declare a class called "Item". Declaring a class let us store group of datas together.
    class Item(object):
# Now, with the method "__init__" (mind the two _ before and after) we specify the names and number of those datas.
# Every method need his first argument to be "self", so, don't eliminate it!
        def __init__(self, name, price, pic):
# And now let's give a real name to this arguments. In this case is the best just to repeat their name.
            self.name = name
            self.price = price
            self.pic = pic

# Let's do a similar thing declaring a new class to store the amount of every item.
# We use a new class instead of adding the amount in the class "Item" to be able to change the amount of items in the inventory without changing the item.
# In fact every item we will add to the class "Item" will be unique for each group of arguments.
# So, there cannot be two items called "ball" with different prices but counted in your inventory as the same.
# To use different prices you have to declare a new class as we are doing with the amount.
    class InvItem(object):
        def __init__(self, item, amount):
            self.item = item
            self.amount = amount

# And, for last but not least, let's declare a class that will be the container of our items, so, the inventory.
    class Container(object):
# We don't need to specify any other argument. We want to store the items in this container without specifing anything else.
        def __init__(self):
# Here we declare a list of data. The two empty [] tell to RenPy that this array is empty at his creation.
            self.inventory = []

# There is now point in creating a class only to store a list. We created a class with a list to be able to use easly other method with that list.
# First, let's add a method that let the program know where a specified item is in the list.
# Every item in the list has an index (an unique number). In order to modify or see that item, the program needs to know what his index is.
        def finditem(self, item):
# "return" will return to the program the index of the specified item.
# "[i.item for i in self.inventory]" will repeat the method for every item in the list.
# If we don't do so, the program will not be able to find the item we specified and return his index.
            return(self.inventory[[i.item for i in self.inventory].index(item)])

# Then, let's add a method that let the program know if there is that item in the list or not.
# The arguments are "self", "item" and "amount". If you notice, "amount" has a =1 attached.
# That means that, if we don't specify an amount for the item we want to check the presence, the default number will be 1.
        def has_item(self, item, amount=1):
# With this double If-Else we first check if there is that item at all in the inventory, then we check if that item has an amount equal or above the one we specified.
            if item in [i.item for i in self.inventory]:
                if self.finditem(item).amount >= amount:
                    return(self.finditem(item).amount)
                else:
                    return(False)
            else:
                return(False)

# At this point, let's add a method that let us add items to the list (the inventory).
        def add_item(self, item, amount=1):
# In short terms, this If-Else will add the amount of item we specified to the item in the inventory or,
# if is a new item, will add it to the inventory and use the amount we specified.
            if item in [i.item for i in self.inventory]:
                self.finditem(item).amount += amount
            else:
                self.inventory.append(InvItem(item, amount))
# In case there is no error, will return "success".
            return('success')

# Similar to the previous method, this let us remove one item from the inventory if we have enough.
        def rem_item(self, item, amount=1):
            if self.has_item(item):
                if self.finditem(item).amount > amount:
                    self.finditem(item).amount -= amount
                    return('more left')
                elif self.finditem(item).amount == amount:
                    self.finditem(item).amount -= amount
                    self.inventory.pop(self.inventory.index(self.finditem(item)))
                    return('gone')
                elif self.finditem(item).amount < amount:
                    return('not enough')
            else:
                return('item not found')
Now, the code for the inventory is done. Congrats!
What remains to do is to create the items, the gui of the inventory and learn how to add and remove item from the inventory.
Yes, we will use the two methods we just created but, if you are here, I asume you don't know yet how to do that.

Code: Select all

# Once again let's make this part of the code be executed at the start of the game,
# BUT only AFTER the code of the inventory.
init 0:
# First of all, we need to create an inventory using the class Container.
# We will call this inventory "backpack".
# Yes, you can create more than one inventory without doubling all the previous code.
    $ backpack = Container()

# Let's add money too to our inventory. We will use a simple numeric variable for this.
    $ money = 0

# Now, we will make a list with ALL the items in our game. In this case only two.
# Let's analyze this.
# "ball" is the name of the variable to which the item will be bound.
# "Item()" calls the Item class. In the parhentesis we will need to specify all of the arguments declared if those don't have a default value.
# ""Ball"" is the name. "5" is the price of the item. ""images/ball_icon.png"" is the pic.
# The pic is the directory, the name and the extension of the image that will be used to show the item in the inventory.
# This means that you will not be able to change the image inside the game, like resizing it.
# I suggest you to have all of the items' images of the same size.
    $ ball = Item("Ball", 5, "images/ball_icon.png")
    $ brick = Item("Brick", 20, "images/brick_icon.png")
And now, the inventory GUI:

Code: Select all

# Now, let's create a screen that will be the GUI of our inventory.
# I hope you already know how screen works, because I will go rapidly explaining this.
# If you already are an expert of screens, please still read this part to be sure to don't make any mistake.

# Let's declare a screen and call it "backpack".
screen backpack:
    # We give it a tag "inventory" in order to replace any other inventory GUI currently open, in case you have more than one inventory.
    tag inventory
    # "zorder" with a numer of "1000" will make sure that this GUI will not be hidden behind something.
    zorder 1000
    # "modal" is the keyword that, if "True", block the user to interact with anything that is not part of the GUI.
    # In this way there is no risk that the user accidentaly click something that is behind it.
    modal True
    # With "add" we specify the directory, the name and the extension of the image that will be the background of our inventory GUI.
    add "images/inventory_bg.png"
    # This horizontal box will show in the inventory how many money the player has.
    # xalign, yalign and spacing values are according to my GUI. Feel free to change them.
    hbox:
        xalign 0.5
        yalign 0.05
        spacing 10
        # This image is a small icon that goes next to the money amount. Just an aestetic thing.
        add "images/inventory_money.png":
            zoom 0.075
        # This too is purely aestethic. If the player has zero money or less, the money amount will be read. If it's not, the value will be green.
        if money > 0:
            text "[money]$" color "#ffffff"
        else:
            text "[money]$" color "#ff0000"

# First, let's specify that the items. Instead at the center of the GUI, we want that will be shown starting to the top left corner ("tl").
    side "tl":
        # "465" and "109" are according to my GUI background. You need to change them for your GUI.
        area (465, 109, 1, 1)
        # This grid will contain all of our items' icons as well as their name and amount.
        # The two value "10" and "100" are the columns and lines of the grid. Change the columns according to your GUI.
        # There is no need to change the lines because, if you wish, you can scroll this inventory with the mousewheel.
        # Something you want ABSOLUTELY to avoid with this inventory is that the player run out of item slots.
        # So, put enough line to store all the items at once.
        # If the player exceed the slots number, RenPy will give an error and the save will not be playable anymore.
        grid 10 100:
            # This is the distance from one item to another.
            spacing 1
            # THIS PART IS EXTREMELY IMPORTANT!!!
            # If you omit this, RenPY will give you an error because not all the slots are filled.
            allow_underfull True
            # And, for last, this "for i..." simply add all of the items in the backpack container to the grid.
            # In this way there's no need to add manually every item to the inventory.
            for i in backpack.inventory:
                # This boxes are to keep locked in place the images, names and counts of all the items.
                # Again, fell free to change the "zoom", "size" and any other properies.
                vbox:
                    hbox:
                        add i.item.pic zoom 0.86
                        text str(i.amount) size 20
                    text i.item.name size 15

    # I sugest you to put, somewhere in this GUI, a button to close the inventory GUI. Otherwise the player will be stuck in here.
We are almost done. To be more precise, the coding part is done. Now you need to learn how to use the methods during the game.

Code: Select all

# In the normal story, you can execute a method as changing a variable. In this way:
    $ backpack.add_item(ball, 1)
# Is the same for every method.
# We use the method "add_item" or "rem_item" and we use it on the container "backpack".
# Also, we want to change the item "ball" and the amount we change is "1"
    $ backpack.rem_item(ball, 1)

# So, if we buy something the code will be:
    menu:
        merchant "Do you want to buy two balls?"
        "Yes":
            if money >= 10:
                $ backpack.add_item(ball, 2)
                $ money -= (ball.price * 2)
                jump shop
            else:
                merchant "You don't have enough money."
                jump shop
        "No":
            jump shop

# Else, if you wish to use it in a screen or similar, here's an example with a button to remove 1 ball:
                imagebutton:
                    idle "images/button.png"
                    action Function(backpack.rem_item, ball)
Your inventory is ready. If you have any error, any suggestion or everything else, feel free to say/ask it here.
This is the best inventory system I know. Is absolutely not perfect but, for the most of you, there will be no need to search a better one.

P.S. I tried to explain this in the simpliest way and, despite I know python, I'm not a real programmer, so, if I use some terms in wrong ways, correct me without put on a tantrum.

Post Reply

Who is online

Users browsing this forum: No registered users