This page is out of date

You've reached a page on the Ren'Py wiki. Due to massive spam, the wiki hasn't been updated in over 5 years, and much of the information here is very out of date. We've kept it because some of it is of historic interest, but all the information relevant to modern versions of Ren'Py has been moved elsewhere.

Some places to look are:

Please do not create new links to this page.


Money and Inventory Systems in Action

So, you want to add a system that keeps track of money in the game so that the character can shop or earn coins, do you? This kind of thing would probably be pretty useful in a game using the DSE (Dating Sim Engine offered in Frameworks.

Here are two possible solutions for you.

You can just copy and paste the code if you want and modify it to your purposes, but I encourage going through it to gain a deeper understanding of object-oriented programming.

This is a beginner example and is probably the kind of thing you're going to want to use if you're only doing this once or twice. It also introduces some of the concepts that we're going to expand upon.

Example 1 source code

$ coins = 0 
$ items = [] 
"Today you made 10 coins!  Good job!"
$ coins += 10 
"You have %(coins)d coins remaining.  What would you like to buy?" 
menu:
   "Spaghetti":
      $ coins -= 3 
      $ items.append("spaghetti")
   "Olives":
      $ coins -= 4
      $ items.append("olives")
   "Chocolate":
      $ coins -= 11
      $ items.append("chocolate")
if "chocolate" in items: 
   "You have a bar of chocolate!  Yummy!"

Walkthrough of Example 1 (beginner)

init:
pass

label start:
$ coins = 0

You can keep track of money... let's call it coins... with a variable. The $ (cash sign) means that we're using a python statement outside on an "init python:" block.

We're going to initially set this value to 0. The "label start:" tells Ren'Py where to begin at.

I'm leaving init blank for now.

$ items = [] 

Here's something new. We're declaring the variable "items" as an empty set, usually defined by brackets such as "[" and "]". Since it's empty, there's nothing-inbetween yet.

"Today you made 10 coins!  Good job!"
$ coins += 10 

We've added a string that lets the player know what's happening. By using the += operator, we can add coins. We're adding 10 coins to zero.

"You have %(coins)d coins remaining.  What would you like to buy?" 

%(coins)d is what we say if we want to put the number of coins into a string or dialogue of printed text. "%(coins)d" is a dynamic string that will insert the value of that variable, whether it's text or a number.

If my money was instead measured in "pennies", then I would use "%(pennies)d". The variable name goes inside the parentheses.

menu:

This kind of label lets Ren'Py know to offer the user a set of choices, which we will then define. Indentation is important. For menu labels, it is four spaces over.

   "Spaghetti":
      $ coins -= 3 

By using the -= operator, you can subtract coins, just like before when we gave the player 10 coins by adding it to zero.

      $ items.append("spaghetti")

If you have a list named "items", you can add things to the list by saying items.append("thing"). It's a python statement so we also use a $ sign.

   "Olives":
      $ coins -= 4
      $ items.append("olives")
   "Chocolate":
      $ coins -= 11
      $ items.append("chocolate")

Then we just put the rest of what we know into practice by making the rest of the menu options.

if "chocolate" in items: 
   "You have a bar of chocolate!  Yummy!"

The question statement if "object" in "list" tells you whether or not the list contains that object. When we append "chocolate" to "items" then the brackets look like this to Ren'Py: ["chocolate"]

If the player buys everything, it would look like this:

["chocolate", "olives", "spaghetti"]

Example 2 source code

init python:
    class Item:
        def __init__(self, name, cost):
            self.name = name
            self.cost = cost

    class Inventory:
        def __init__(self, money=10):
            self.money = money
            self.items = []

        def buy(self, item):
            if self.money >= item.cost:
                self.money -= item.cost
                self.items.append(item)
                return True
            else:
                return False

        def earn(self, amount):
            self.money += amount

        def has_item(self, item):
                if item in self.items:
                    return True
                else:
                    return False

label start:

    python:
        inventory = Inventory()
        spaghetti = Item("Spaghetti", 3)
        olives = Item("Olives", 4)
        chocolate = Item("Chocolate", 11)

    "Oh, look! I found ten coins!"

    $ inventory.earn(10)

    $ current_money = inventory.money 

    "Now I have %(current_money)d coins."

    "My stomach growls loudly."

    if inventory.buy(chocolate):
        "Mmm, chocolate. I'll save that for later... "
    else:
       "Not enough money... "

    "Suddenly, I feel hungry."

    jump preshop
    jump shop2

    if inventory.has_item(chocolate):
        "Good thing I bought that chocolate earlier."
    else:
        "If only I had some chocolate..."

label preshop:
    $ spaghetticost = spaghetti.cost
    $ olivescost = olives.cost
    $ chocolatecost = chocolate.cost
label shop2:
    menu shop:
        e "I go into the store."
        "Buy spaghetti for %(spaghetticost)d coins.":
            if inventory.buy(spaghetti):
                "Hey, those are uncooked. I can't eat those yet!"
                jump game_continues

        "Buy olives for %(olivescost)d coins.":
            if inventory.buy(olives):
                "I hate olives."
                "And they cost more than the spaghetti."
                "But at least I don't have to cook them... "
                jump game_continues

        "Buy chocolate for %(chocolatecost)d coins.":
            if inventory.buy(chocolate):
                "Mmmm, dark semi-sweet chocolate! My favorite!"
                jump game_continues

        "Buy nothing.":
            jump game_continues

label fallthrough:
    e "Not enough money..."
    jump shop2

label game_continues:
    "And so I left the store."
    $ current_money = inventory.money
    "I have %(current_money)d left"

Walkthrough of Example 2 (advanced)

Here's an advanced solution using the long-term goal of classes of objects to reduce the amount of work needed over the entire project. I hope you're ready to put it all into practice.

You can actually read more about classes here: Classes in Python

init python:

This statement lets Ren'Py know to run this at the start ("init") and that it's python code ("python:") so none of these statements need a cash sign $ to prefix them.

It's a little different from the above method.

    class Item:

Here, we declare a class of objects, which is like a group.

        def __init__(self, name, cost):

We define some properties for it at initialization: name and cost.

            self.name = name
            self.cost = cost

"self" refers back to the original class ("Item") and the period is a break in the hierarchy. "name" comes directly after that, so name is a property of "Item". The same thing is true of "cost".

    class Inventory:

We declare another class called "Inventory".

        def __init__(self, money=10):

The "Inventory" (self) is defined as having "10" money at initialization again. In other words, the variable container "money" is set as equal to the number "10."

This can be freely changed, of course. Just remember where it is, maybe leave a comment for yourself.

            self.money = money

As before, we're declaring that the property "money" of the "self" (class Inventory) is contained within a global variable called "money".

            self.items = []

This is an empty set called "items" which can be filled, just like before—but now it's contained with the class called Inventory as an embedded property of that group.

        def buy(self, item):
            if self.money >= item.cost:

We define another function: "buy" with the property of "item."

If the money contained with the "Inventory" is less than the "cost" variable contained within the "Item" class then we...

You may also notice the period between self and money. That tells Ren'Py the location of money in the hierarchy.

The >= syntax is identical to the += and -= we were using before, except that this time we're using it only to evaluate the container within the variable and not change it.

                self.money -= item.cost

We subtract the amount of the cost away from the "Inventory" money. In effect, the player has lost money.

                self.items.append(item)

We put the "Item" into the "Inventory".

Just like in the beginner's code, we're adding the "item" into the empty set.

                return True

The player bought the item.

            else:
                return False

The player didn't buy the item.

        def earn(self, amount):

We're making another definition now, for the earning of money.

The variable in operation here is the "amount" in question.

            self.money += amount

Again, "self" still refers to class "Inventory". So what is earned is added from "amount" into "money."

        def has_item(self, item):

This is a definition that checks to see if the item is in the inventory or not.

                if item in self.items:
                    return True
                else:
                    return False

This one is pretty self-explanatory and works like checking about adding an item into the "items" container.

Was that a little confusing?

Let's see it in action.

label start:

This tells Ren'Py where to begin, just like last time.

    python:

You can declare a python block even from a "start" label.

        inventory = Inventory()

The variable "inventory" is declared to be the same as the "Inventory" class, which is followed by (). Again, no $ sign is necessary.

        spaghetti = Item("Spaghetti", 3)

Spaghetti is declared to be of class "Item."

Notice that it's taking the arguments "name" and "cost" just like we declared before?

It costs 3 "money".

        olives = Item("Olives", 4)
        chocolate = Item("Chocolate", 11)

We then apply that same kind of code to two other objects. Olives cost more than spaghetti and chocolate is obviously a luxury few can afford...

    "Oh, look! I found ten coins!"
    $ inventory.earn(10)

We state that 10 "money" is earned and sent to the inventory, after it goes to amount.

    $ current_money = inventory.money

This is a hack to make the field a global so we can use it as a dynamic string below.

    "Now I have %(current_money)d coins."
    "My stomach growls loudly."

Like in Example 1, this string changes depending on the amount declared for the variable "current_money".

    if inventory.buy(chocolate):

If you give the player 11 instead of 10 coins under the class Inventory, he can buy it.

        "Mmm, chocolate. I'll save that for later... "
    else:
       "Not enough money... "
    "Suddenly, I feel hungry."

The "else" function just handles what happens if it's not possible to buy it. By the default option, the player can't afford it.

    jump preshop
    jump shop2

A "jump" will go to the label and not return.

See below for these sections.

    if inventory.has_item(chocolate):

If the "Item" "chocolate" is contained within the "Inventory" item set... then the statement below is printed:

        "Good thing I bought that chocolate earlier."
    else:
        "If only I had some chocolate..."

Well, we only had 10 coins, huh?

Let's take a look at those shop labels.

label preshop:
$ spaghetticost = spaghetti.cost
$ olivescost = olives.cost
$ chocolatecost = chocolate.cost

We redefine some of the properties of the items into global variables.

label shop2:
menu shop:

    "I go into the store."

By not including a colon, Ren'Py will display this dialogue at the same time as the menu.

    "Buy spaghetti for %(spaghetticost)d coins.":
        if inventory.buy(spaghetti):
            "Hey, those are uncooked. I can't eat those yet!"
            jump game_continues

This is just like we've done before, and here we're using a dynamic string for the cost of the spaghetti: that means you can change it in the future or maybe even lower it.

Then we jump down to the label game_continues which is covered further on below.

    "Buy olives for %(olivescost)d coins.":
         if inventory.buy(olives):
            "I hate olives."
            "And they cost more than the spaghetti."
            "But at least I don't have to cook them... "
            jump game_continues

    "Buy chocolate for %(chocolatecost)d coins.":
         if inventory.buy(chocolate):
            "Mmmm, dark semi-sweet chocolate! My favorite!"
            jump game_continues

More of the same.

    "Buy nothing.":
        jump game_continues

Always remember to give the player a neutral option when shopping. With a user interface, this would probably be a cancel imagebutton.

label fallthrough:
"Not enough money..."
jump shop2

This is what happens when you can't afford an item.

label game_continues:
"And so I left the store."
"I have %(current_money)d left"

The player's shopping trip ends here. You may notice the return of are global hack, this is because this code must be placed before all current_money variables to ensure they will work.

The last bit of code is mainly for testing reasons, to ensure that the money was removed from your inventory.