An inventory system help

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
NC131
Newbie
Posts: 10
Joined: Mon May 08, 2023 12:11 am
itch: nc131.
Contact:

An inventory system help

#1 Post by NC131 »

Heya! I'm having an issue with creating an "inventory system"
Where ...

- you can drag and place the item around the inventory slots
Image


- Each new item will be sorted on its category and rarity
Image


- A crafting menu where it requires particular items with the same rarity to create a new item
Image

- It's easy to add or remove new items (Just like typing the code on the label and then the new item will appear)

- Item has their own Weight, Rarity, Amount, and Category (for example, an item for food ingredients is not consumable, snacks are consumable, and cooked meal cannot be stacked)

(If the weight in the inventory is surpassing certain amount, the player cannot add more item into their inventory)

- And you can get the item from the shop by buying it or selling the item in the shop for money

I'm really sure this is really complicated coding ... :?
And I can't use my logic here, not like when creating the pop-up notification one.

Massive thanks in advance. (if it's necessary, you can DM me)

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#2 Post by jeffster »

This might be a good starting point:
viewtopic.php?f=51&t=66699

I didn't do sorting there (being too lazy for that), but the cells can be highlighted according to the item category. Which is the next best thing, and doesn't change the placement created by manual sorting.

Your weight calculations (and the like) can be done the same way as stat bonuses calculations there.

NC131
Newbie
Posts: 10
Joined: Mon May 08, 2023 12:11 am
itch: nc131.
Contact:

Re: An inventory system help

#3 Post by NC131 »

jeffster wrote: Wed Jun 14, 2023 7:13 am This might be a good starting point:
viewtopic.php?f=51&t=66699

I didn't do sorting there (being too lazy for that), but the cells can be highlighted according to the item category. Which is the next best thing, and doesn't change the placement created by manual sorting.

Your weight calculations (and the like) can be done the same way as stat bonuses calculations there.
Oh yeah! I saw your post on the RPG-style one! I must admit it does cover some parts of what I need, but... I still do not understand how to add a new item category, make the same item category but with different rarity, or make the item "consumable"

and how to make and elif statement if the player has the particular item category and rarity :? :? My head, ambatublow

User avatar
_ticlock_
Miko-Class Veteran
Posts: 910
Joined: Mon Oct 26, 2020 5:41 pm
Contact:

Re: An inventory system help

#4 Post by _ticlock_ »

NC131 wrote: Tue Jun 13, 2023 2:12 am - you can drag and place the item around the inventory slots
NC131 wrote: Tue Jun 13, 2023 2:12 am - Each new item will be sorted on its category and rarity
These two can contradict each other.

Here is another RenPy inventory that you may find useful: Infinite, Stackable Inventory/Crafting/Vendor

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#5 Post by jeffster »

NC131 wrote: Wed Jun 14, 2023 9:55 am I still do not understand how to add a new item category, make the same item category but with different rarity, or make the item "consumable"

and how to make and elif statement if the player has the particular item category and rarity :? :? My head, ambatublow
Let's look at the script.

Items in the game are represented by dictionary "items":

Code: Select all

    items = {
        "sword": {
            "idle": "images/sword.webp",
            "hover": "images/sword_h.webp",
            "dragin": "images/sword_d.webp",
            "slots": ["hand_r"],    # In which slots can be placed
            "attr": "https://www.flaticon.com/authors/freepik",
            "bonus": {"strength": 3},
            "desc": _("Good short sword {=g}+3 STR"),
            "type": "Weapon Melee",
        },
        "dagger": {
            # ...And so on
Dictionary in Python is a collection of data, with each element addressed by its unique key. In my demo, keys are "sword", "dagger" & "shield". To put new items in the game or to modify existing ones, edit that "items" dictionary. Copy an item, e.g., "sword", change its name, pictures and so on.

(As the dictionary keys should be unique, if the game has several swords of the same kind, name them differently, like "sword01", "sword02", "sword03"... even if their contents would be the same).

To add an item to the inventory, there is function addItems(). Call it like this:

Code: Select all

        addItems("dagger", "sword", "shield")
Function "addItems" allows one or more parameters of string type. They should be names of items that exist as keys in "items" dictionary. To add an item to the inventory, this function copies an element from "items" into "owned". (Also it places the item into the backpack).

That's how we add items to the inventory in one simple line.

Of course you can change items' fields. For example, add weight & rarity and flags for "consumable" & "stackable":

Code: Select all

    # Rarity constants:
    TRASH = 0
    COMMON = 1
    UNCOMMON = 2
    RARE = 3
    EPIC = 4

    items = {
        "sword": {
            "consumable": False,
            "stackable": False,
            "weight": 1.5,
            "rarity": UNCOMMON,
            "idle": "images/sword.webp",
            "hover": "images/sword_h.webp",
            "dragin": "images/sword_d.webp",
            "slots": ["hand_r"],    # In which slots can be placed
            "attr": "https://www.flaticon.com/authors/freepik",
            "bonus": {"strength": 3},
            "desc": _("Good short sword {=g}+3 STR"),
            "type": "Weapon Melee",
        },
Then to check if item is an epic melee weapon you can do:

Code: Select all

    if myItem["type"] == "Weapon Melee" and myItem["rarity"] == EPIC:
        #...
To make it more convenient, you can write functions like:

Code: Select all

init python:
    def checkRarity(itemName, rarity):
        return (items[itemName]["rarity"] == rarity)
And then for example if you need to get a list of all "epic" rarity items in your inventory:

Code: Select all

python:
    myEpic = set()
    for k in owned:
        if checkRarity(k, EPIC):
            myEpic.add(k)
Now "myEpic" would have names of everything "epic" there. Likewise, you can have helper functions to check if an item is a weapon, etc.

PS. To have different categories, just write whatever you want in the item "type" field:

Code: Select all

        "egg": {
            "consumable": True,
            "stackable": True,
            "weight": 0.1,
            "rarity": COMMON,
            "idle": "images/egg.webp",
            "hover": "images/egg_h.webp",
            "dragin": "images/egg_d.webp",
            "desc": _("Tasty egg"),
            "type": "Food Raw",
        },

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#6 Post by jeffster »

PS. My inventory doesn't have "stackables" implemented.

So to add "stackables" to your game you will need for example to add the amount to "owned" dictionary stackable items. If can look like this if you pick up an egg:

Code: Select all

    addItem("egg", 1)
Then you will need function addItem accepting 2 parameters: item name and amount:

Code: Select all

init python:
    def addItem(name, amount=1):
        if item[name]["stackable"]:
            # check if items of those kind are in the inventory already:
            if name in owned:
                # If so, just increase the amount:
                owned[name]["amount"] += amount
            else:
                # Otherwise add new item:
                addItems(name)
                # Check if it was successful (or the backpack full or something)
                if name in owned:
                    owned[name] = amount
        else:
            # Not a stackable
            for i in range(0, amount):
                addItems(name)
Something like this. Therefore, to add stackables, use addItem function that adds only 1 kind of item at a time. (Because if you want to use addItems function that can add several different items at a time, you would need to complicate parameters structure. Do we want that? Nah...).

Then stackables in your inventory might look like:

Code: Select all

        "egg": {
            "consumable": True,
            "stackable": True,
            "weight": 0.1,
            "rarity": COMMON,
            "idle": "images/egg.webp",
            "hover": "images/egg_h.webp",
            "dragin": "images/egg_d.webp",
            "desc": _("Tasty egg"),
            "type": "Food Raw",
            "amount": 3
            }
As you see all the data there would be copied from "items" except the "amount" field. We add the "amount" field there when we add that kind of item to the inventory if it didn't exist there. Otherwise we just increase the amount.

Likewise, you can write/modify functions that spend stackable items: decrease their amount or remove them at all when there are no more left.

NC131
Newbie
Posts: 10
Joined: Mon May 08, 2023 12:11 am
itch: nc131.
Contact:

Re: An inventory system help

#7 Post by NC131 »

jeffster wrote: Wed Jun 14, 2023 3:30 pm PS. My inventory doesn't have "stackables" implemented.

So to add "stackables" to your game you will need for example to add the amount to "owned" dictionary stackable items. If can look like this if you pick up an egg:

Code: Select all

    addItem("egg", 1)
Then you will need function addItem accepting 2 parameters: item name and amount:

Code: Select all

init python:
    def addItem(name, amount=1):
        if item[name]["stackable"]:
            # check if items of those kind are in the inventory already:
            if name in owned:
                # If so, just increase the amount:
                owned[name]["amount"] += amount
            else:
                # Otherwise add new item:
                addItems(name)
                # Check if it was successful (or the backpack full or something)
                if name in owned:
                    owned[name] = amount
        else:
            # Not a stackable
            for i in range(0, amount):
                addItems(name)
Something like this. Therefore, to add stackables, use addItem function that adds only 1 kind of item at a time. (Because if you want to use addItems function that can add several different items at a time, you would need to complicate parameters structure. Do we want that? Nah...).

Then stackables in your inventory might look like:

Code: Select all

        "egg": {
            "consumable": True,
            "stackable": True,
            "weight": 0.1,
            "rarity": COMMON,
            "idle": "images/egg.webp",
            "hover": "images/egg_h.webp",
            "dragin": "images/egg_d.webp",
            "desc": _("Tasty egg"),
            "type": "Food Raw",
            "amount": 3
            }
As you see all the data there would be copied from "items" except the "amount" field. We add the "amount" field there when we add that kind of item to the inventory if it didn't exist there. Otherwise we just increase the amount.

Likewise, you can write/modify functions that spend stackable items: decrease their amount or remove them at all when there are no more left.
Humu humu... I'll see what I can do, Massive thanks for sharing this knowledge! TwT I will try this out later!

NC131
Newbie
Posts: 10
Joined: Mon May 08, 2023 12:11 am
itch: nc131.
Contact:

Re: An inventory system help

#8 Post by NC131 »

jeffster wrote: Wed Jun 14, 2023 3:30 pm PS. My inventory doesn't have "stackables" implemented.

So to add "stackables" to your game you will need for example to add the amount to "owned" dictionary stackable items. If can look like this if you pick up an egg:

Code: Select all

    addItem("egg", 1)
Then you will need function addItem accepting 2 parameters: item name and amount:

Code: Select all

init python:
    def addItem(name, amount=1):
        if item[name]["stackable"]:
            # check if items of those kind are in the inventory already:
            if name in owned:
                # If so, just increase the amount:
                owned[name]["amount"] += amount
            else:
                # Otherwise add new item:
                addItems(name)
                # Check if it was successful (or the backpack full or something)
                if name in owned:
                    owned[name] = amount
        else:
            # Not a stackable
            for i in range(0, amount):
                addItems(name)
Something like this. Therefore, to add stackables, use addItem function that adds only 1 kind of item at a time. (Because if you want to use addItems function that can add several different items at a time, you would need to complicate parameters structure. Do we want that? Nah...).

Then stackables in your inventory might look like:

Code: Select all

        "egg": {
            "consumable": True,
            "stackable": True,
            "weight": 0.1,
            "rarity": COMMON,
            "idle": "images/egg.webp",
            "hover": "images/egg_h.webp",
            "dragin": "images/egg_d.webp",
            "desc": _("Tasty egg"),
            "type": "Food Raw",
            "amount": 3
            }
As you see all the data there would be copied from "items" except the "amount" field. We add the "amount" field there when we add that kind of item to the inventory if it didn't exist there. Otherwise we just increase the amount.

Likewise, you can write/modify functions that spend stackable items: decrease their amount or remove them at all when there are no more left.
So for the "Consumable" item, how do I make it when the player "Use" the item (like eat or drink) from 2 different rarity (which give different energy increase)
it'll call a label to do the dialogue for each item and give the player a different result?

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#9 Post by jeffster »

NC131 wrote: Fri Jun 16, 2023 4:02 am So for the "Consumable" item, how do I make it when the player "Use" the item (like eat or drink) from 2 different rarity (which give different energy increase)
it'll call a label to do the dialogue for each item and give the player a different result?
We call screen "inventory" in a loop. When an interaction happens in the screen and we need to process it, the screen call returns, and we get a return value. Here it is in the script:

Code: Select all

    call screen inventory(equips)
    if isinstance(_return, bool):
        # Clicked "Exit" button
        return
    python:
        curDrag = None
        item, place = _return   # strings: what dropped, where dropped
Here item and place are components of our return value.

* "item" is an item that was clicked or drag-n-dropped.
* "place" is a slot to equip the item. (If "place" returned value is None, it means the item was dropped outside slots and we just unequip it if it was equipped).

So to add items' consumption, the easiest way is to add a slot for "eating". Let's call that slot "eat". In my demo, here is the dictionary of slots:

Code: Select all

    # Slots for equipped items (as drop areas)
    drops = {
        "hand_r": {                 # drag_name
            "xy":   (997, 186),     # pos
            "xys":  (118, 118),     # xysize
        },
        "hand_l": {
            "xy":   (1435, 466),
            "xys":  (118, 118),
        },
    }
For example, add "eat" area as the first slot, size of the whole character figure, so the other slots could be on top of it. That way dropping an item on a character but not in a (more) particular slot would mean "eat it":

Code: Select all

    drops = {
        "eat": {                    # drag_name
            "xy":   (1000, 100),    # pos
            "xys":  (600, 800),     # xysize
        },
        "hand_r": {                 # drag_name
            "xy":   (997, 186),     # pos
            "xys":  (118, 118),     # xysize
        },
        # and so on...
And we add "eat" slot to the list of available slots of every "edible" item. E.g.:

Code: Select all

        "egg": {
            "consumable": True,
            "stackable": True,
            "weight": 0.1,
            "rarity": COMMON,
            "idle": "images/egg.webp",
            "hover": "images/egg_h.webp",
            "dragin": "images/egg_d.webp",
            "slots": ["eat"],
            "attr": "https://www.psdgraphics.com/psd-icons/brown-white-and-golden-eggs-psd-icons/",
            "desc": _("Tasty egg"),
            "type": "Food Raw",
            },
Now when we click an egg in the backpack or drag it to "eat" slot, it will be processed by our script.

Let's see how we do that in the "inventory" screen:

* When an item in the inventory gets clicked, function "clickedIt" runs.
* When an item gets dragged and dropped, function "draggedTo" runs.

Both these functions check if the item should go to an available slot. Then they return a pair of values, item name and slot name. If there's no slot available, they return None, and that causes the screen to continue. (Screen call ends only when those functions return a non-empty value).

So what we have to do is to add a check of returned values, was that a command to consume. You see the returned values in the code above:

Code: Select all

        item, place = _return   # strings: what dropped, where dropped

        # So we add this: Was that a consumable?

        if place == "eat":

            # And here we will write a code to process consumption
            #...

We can check there item rarity etc. If we want each consumable to have some property, we can just add it to the "egg" information in "items" dictionary. E.g.:

Code: Select all

        "egg": {
            "consumable": True,
            "effects": {"energy": 5},
Then processing item consumption we check the "effects" field for that item and create those effects.
Last edited by jeffster on Fri Jun 16, 2023 1:54 pm, edited 1 time in total.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#10 Post by jeffster »

By the way there were like 2 errors in my addItem function. Here's the corrected version:

Code: Select all

    def addItem(name, amount=1):
        """ Add items[name] to the backpack in amount.
        """
        global owned
        if items[name]["stackable"]:
            # check if items of those kind are in the inventory already:
            if name in owned:
                # If so, just increase the amount:
                owned[name]["amount"] += amount
            else:
                # Otherwise add new item:
                addItems(name)
                # Check if it was successful (or the backpack full or something)
                if name in owned:
                    owned[name]["amount"] = amount
        else:
            # Not a stackable
            for i in range(0, amount):
                addItems(name)

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#11 Post by jeffster »

I posted demo v.1.1. You can see there stackables and consumables at work. Eating happens without confirmation screen, but you can add it in a standard Ren'Py way.
https://renpy.org/doc/html/screen_special.html#confirm

PS. Crafting screen can be done the same way, just a different picture and set of slots.

jeffster
Veteran
Posts: 409
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: An inventory system help

#12 Post by jeffster »

I posted v.1.3.
viewtopic.php?f=51&t=66699

It has Crafting screen and so it shows the ability to combine "backpack" with different screen types: equipment slots, crafting etc. The same way, storage or shopping screens can be added.

RewindTheGame
Regular
Posts: 62
Joined: Thu Dec 31, 2020 3:37 pm
Contact:

Re: An inventory system help

#13 Post by RewindTheGame »

Hi Jeffster,

I've started using this inventory system (or parts of it, anyway) in my game, but have a question. The initial items available in game are defined in the code like this:

Code: Select all

default items = {
    "sword": {
        "idle": "images/sword.webp",
        "hover": "images/sword_h.webp",
        "dragin": "images/sword_d.webp",
        "slots": ["hand_r"],    # In which slots can be placed
        "attr": "https://www.flaticon.com/authors/freepik",
        "bonus": {"strength": 3},
        "desc": _("Good short sword {=g}+3 STR"),
        "type": "Weapon Melee",
        },
    "dagger": {
        "idle": "images/dagger.webp",
        "hover": "images/dagger_h.webp",
        "dragin": "images/dagger_d.webp",
        "slots": ["hand_r", "hand_l"],
        "attr": "https://www.flaticon.com/authors/freepik",
        "bonus": {"dexterity": 1},
        "desc": _("Nasty knife {=g}+1 DEX"),
        "type": "Weapon Melee",
        },
... etc
The problem is that I obviously cannot predict in advance every single item that's going to be in my game, so I need a way of extending this list later. I'd imagined that just adding items to the list when it was defined would update my saved games but it doesn't - when I load a game, it just crashes because it doesn't recognise the items I've added to the list since the game was saved. So is there a way to add new items to the items array programmatically within my script at runtime?

Thanks
Simon

RewindTheGame
Regular
Posts: 62
Joined: Thu Dec 31, 2020 3:37 pm
Contact:

Re: An inventory system help

#14 Post by RewindTheGame »

Well, I'm damned if I can work out how to delete my last post - I can't see a delete button anywhere - but I worked it out in the end. I'm not too familiar with python and just had to do a little research on dictionaries. I'm still working with arrays in my head.

RewindTheGame
Regular
Posts: 62
Joined: Thu Dec 31, 2020 3:37 pm
Contact:

Re: An inventory system help

#15 Post by RewindTheGame »

I'm now using this inventory system fairly extensively in my game and it's worked wonders - but there's one thing I can't seem to work out so I'm hoping you can help point me in the right direction.

I have two characters in my game (actually, more than two but let's not complicate matters), and when each character is active they should have their own inventory. I had imagined that this would be as simple as doing something like this:

Code: Select all

   * PLAY AS CHARACTER ONE UP UNTIL THIS POINT *
    $tempitems=items
    $tempdrops=drops
    $tempbackpack=backpack
    $items=itemsmarie
    $drops=dropsmarie
    $backpack=backpackmarie
    
    * NOW PLAYING AS CHARACTER TWO (MARIE) *
    $items=tempitems
    $drops=tempdrops
    $backpack=tempbackpack
    
    * PLAYING CHARACTER ONE AGAIN *
Everything seems to be stored in dictionaries called drops (for equipment slots), backpack (for backpack slots) and items (for list of items and where they're stored), so I thought that just swapping these dictionaries out for new ones (called itemsmarie, dropsmarie and backpackmarie) and then back again afterwards would work. To be fair, it does seem to work until marie consumes an item or anything is removed from an item slot - at which point there are suddenly two of them in the backpack and everything goes mad. This seems to be something to do with the Sack class, but classes in Python seem to be nothing like classes in other languages I use (where classes are basically objects which you don't call directly but instead create instances of - such as Sack1=Sack(), Sack2=Sack() to create two instances of the sack class). The sack class seems to be called directly by the inventory code, so short of copying and pasting the entire class into a new class and hacking and slashing about with the code to refer to the correct sack depending on the character in use at the time, I can't see how I can make this work properly.

Short of adding support for multiple characters directly into the Inventory system, how would you suggest I go about doing this correctly?

TIA,
Simon

Post Reply

Who is online

Users browsing this forum: Imperf3kt