[Solved] For loop range/pages issue

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.
Message
Author
jeffster
Miko-Class Veteran
Posts: 501
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: For loop range/pages issue

#16 Post by jeffster »

The simple way seems to be:

Code: Select all

define items = {
    "Wheat flour": ("flour.png", "Finest wheat flour from the plains of Cyrodiil"),
    "Sugar": ("sugar.png", "Freshly enchanted sugar from Wizards of the Tower"),
    #...and so on
}
default inventory = []     # [ [item_name, amount] ]

init python:
    def set_item(item_name, amount):
        """ Universal function to add or remove inventory items """
        try:
            # Is that item already in the inventory? => 'i' would be its index
            i = [x[0] for x in inventory].index(item_name)
            if amount == 0:
                # Remove it
                inventory.pop(i)
            else:
                inventory[i][1] = amount
        except:
            # Not in the inventory
            if amount != 0:
                # Add the amount of that to the inventory
                inventory.append([item_name, amount])
                # If it's already absent, no need to remove it
In that case you iterate the inventory for the present items and show them like that:

Code: Select all

screen inv():
    grid 4 2:
        for i in range(8*page_number, 8*page_number+8):
            if i < len(inventory):
                button:
                    add items[inventory[i][0]][0]   # pic
                    text inventory[i][0]                  # name
                    text inventory[i][1]                  # amount
                    tooltip items[inventory[i][0]][1] # description
PS. Maybe getting i in that function is not the best way, but it's just an example...
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

User avatar
voluorem
Regular
Posts: 29
Joined: Fri Jun 24, 2022 3:32 pm
Projects: Sweet Release
Discord: voluorem
Contact:

Re: For loop range/pages issue

#17 Post by voluorem »

I already tried that and it didn't work. I have a function called has_item that I tried and it didn't show anything either:

Code: Select all

class Container(store.object):
    def __init__(self):

       self.inventory = {}
    def has_item(self, item, amount=1):
        if item in self.inventory:
            return (self.inventory[item] >= amount)
        else:
            return False
            
And here that is in the screen (which doesn't show anything):

Code: Select all

grid 4 2:
    xalign 0.5
    yalign 0.5
    xspacing -10
    yspacing -10
    for i, e in zip(range(8*page_number, 8*(page_number+1)), item_id):
        if i < len(angestuff_present.inventory):
            if angestuff_present.has_item(e, 1):
                button:
                    xysize (310, 407)
                    align (0.5, 0.5)
                    background "gui/button/inventory_itembg.png"
                    action NullAction()
                    frame:
                        xysize (310, 407)
                        align (0.5, 0.5)
                        background None
                        frame:
                            background None
                            xysize (310, 407)
                            xalign 0.5
                            ypos 50
                            add e.img xalign 0.5
                            frame:
                                background None
                                xysize (310, 75)
                                xalign 0.5
                                yalign 0.6
                                if check_space(e.name) >= 2:
                                    if len(e.name) >= 20:
                                        text e.name yanchor 0.5 xalign 0.5 yalign 1.0 text_align 0.5 size 25 font "gui/fonts/Vanillaextract-1GZ84.ttf"
                                    else:
                                        text e.name yanchor 0.5 xalign 0.5 yalign 1.0 text_align 0.5 size 25 font "gui/fonts/Vanillaextract-1GZ84.ttf"
                                else:
                                    text e.name yanchor 0.5 xalign 0.5 yalign 1.0 text_align 0.5 size 25 font "gui/fonts/Vanillaextract-1GZ84.ttf"
                            button:
                                xysize (280, 320)
                                align (0.5, 0.9)
                                action ToggleVariable("item_selected", true_value=remove(e.shortname.lower()), false_value=None)
                        frame:
                            ysize 35
                            xsize 260
                            xpadding 10
                            xanchor 0.5
                            align (0.5, 1.0)
                            background None
                            text "Held" xpos 20 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
                            text angestuff_present.inventory[e] xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
            else:
                pass
Also, again, I don't want to use dictionaries for defining the items because that's overly convoluted with how much information is stored in them. I can't imagine it would be much different iterating through 2 nested dictionaries than an object in a dictionary.

jeffster
Miko-Class Veteran
Posts: 501
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: For loop range/pages issue

#18 Post by jeffster »

I avoid to use objects because in some cases saving and rollback with objects might not work very well:

Note that it's the change to the variables that matters – changes to fields in objects will not cause those objects to be saved.
https://renpy.org/doc/html/save_load_rollback.html

Keeping data in dicts and lists never failed me. And actually my example works, here's a proof of concept:
Edit: v.2 updated, with tooltips and text input to add items by name:
Image

Code: Select all

define items = {
    "book": ("book.png", "Description of book"),
    "bug":  ("bug.png",  "Description of bug"),
    "dice": ("dice.png", "Description of dice"),
    "film": ("film.png", "Description of film"),
    "look": ("look.png", "Description of look"),
    "pawn": ("pawn.png", "Description of pawn"),
    "sun":  ("sun.png",  "Description of sun"),
    "talk": ("talk.png", "Description of talk"),
    "tool": ("tool.png", "Description of tool"),
    "wand": ("wand.png", "Description of wand"),
}

default inventory = []     # [ [item_name, amount] ]

init python:
    def set_item(item_name, amount):
        """ Universal function to add or remove inventory items """
        try:
            i = [x[0] for x in inventory].index(item_name)
            if amount <= 0:
                inventory.pop(i)
            else:
                inventory[i][1] = amount
        except:
            if amount > 0:
                inventory.append([item_name, amount])

    def get_item(item_name):
        """ Get amount of those items """
        try:
            i = [x[0] for x in inventory].index(item_name)
            return inventory[i][1]
        except:
            return 0


screen inv(page_number=0, current_item=""):
    grid 4 2:
        align (0.5, 0.5)
        spacing 12
        for i in range(8*page_number, 8*page_number+8):
            if i < len(inventory):
                button:
                    size_group "items"
                    background "#345"
                    hover_background "#579"
                    vbox:
                        xalign 0.5
                        add items[inventory[i][0]][0]       # pic
                        text inventory[i][0]:               # name
                            size 42
                            yoffset 24
                        text str(inventory[i][1]):          # amount
                            xalign 1.0
                            yoffset -64
                            color "#FF0"

                    tooltip items[inventory[i][0]][1]   # description
                    action Return((inventory[i][0], inventory[i][1]))

    text f"{len(inventory)} items. Page {page_number+1}" align (0.5, 0.0) size 64

    if page_number > 0:
        textbutton "<" align (0.0, 0.5) action Return(-1) text_size 96

    if page_number < (len(inventory)-1)//8:
        textbutton ">" align (1.0, 0.5) action Return(1)  text_size 96

    if current_item:
        textbutton "-" align (0.4, 0.9) action Return("-") text_size 96
        textbutton "+" align (0.6, 0.9) action Return("+") text_size 96
        add items[current_item][0] align (0.5, 0.9)

    $ tooltip = GetTooltip()
    if tooltip:
        text "[tooltip]" align (0.5, 0.2)

    frame:
        style "itemName"
        input id "itemName":
            default current_item

style itemName:
    align (0.5, 0.8)
    xsize 360
    background "#345"
    hover_background "#579"

label start:
    python:
        n = 0
        for i in items:
            set_item(i, n)
            n += 1

        pageN = 0
        current_item = ""

label loop:
    call screen inv(pageN, current_item)

    if type(_return) is int:
        $ pageN = min(max(pageN+_return, 0), (len(inventory)-1)//8)

    elif type(_return) is str:
        if _return == '-':
            $ set_item(current_item, get_item(current_item)-1)
        elif _return == '+':
            $ set_item(current_item, get_item(current_item)+1)
        else:
            python:
                if _return in items:
                    current_item = _return
                else:
                    renpy.notify(f"No such item: {_return}")

    else:
        $ current_item = _return[0]

    jump loop
I attach the distributive of the game so you can test how it works.
Unpack it and run the "game" folder with Ren'Py 8 SDK.
Attachments
Inventory_game_v2.zip
(454.25 KiB) Downloaded 11 times
Last edited by jeffster on Tue Feb 27, 2024 9:26 am, edited 2 times in total.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

downover
Regular
Posts: 38
Joined: Sun Feb 25, 2024 1:36 am
Contact:

Re: For loop range/pages issue

#19 Post by downover »

> Also, again, I don't want to use dictionaries for defining the items because that's overly convoluted with how much information is stored in them. I can't imagine it would be much different iterating through 2 nested dictionaries than an object in a dictionary.

We're not suggesting iterating through 2 nested dictionaries. You would only iterate through one, and use the other to lookup information about that item. It's much simpler. And as jeffster said, using objects in pointers in save data is not a very good idea.

What if you made a game update that changed the image of cake, for instance? You don't want that to have to change everyone's save data. You want game data like that defined somewhere concrete where it can be referenced as needed.

User avatar
voluorem
Regular
Posts: 29
Joined: Fri Jun 24, 2022 3:32 pm
Projects: Sweet Release
Discord: voluorem
Contact:

Re: For loop range/pages issue

#20 Post by voluorem »

downover wrote: Tue Feb 27, 2024 1:15 am What if you made a game update that changed the image of cake, for instance? You don't want that to have to change everyone's save data. You want game data like that defined somewhere concrete where it can be referenced as needed.
That's a very good point that I didn't think about. I was just being stubborn because I really didn't want to redefine everything (there are a LOT of items. Like 200+) but I gave up and made them all in dictionaries lol. It works great now, thank you to both of you!

This isn't really urgent (I'm fine with putting this off for a while), but I was wondering how to sort the inventory based on the item ID. I tried a few things but I couldn't get any of them to work. I have all of my items like this (the second item is the ID):

Code: Select all

define items = {
    "cookies": ("Cookies", 0, "gui/button/rarity_1.png", None, "gui/button/item_cookies.png", cookies_ingredients, 1),

jeffster
Miko-Class Veteran
Posts: 501
Joined: Wed Feb 03, 2021 9:55 pm
Contact:

Re: [Solved] For loop range/pages issue

#21 Post by jeffster »

If inventory is a list, you can sort it every time adding a new item,
using list.sort() method
https://docs.python.org/3/howto/sorting ... -functions

with "key" function (a function that provides the key for sorting the items).

PS. Edited :-)

Code: Select all

python:
    # "cookies": ("Cookies", 0, "gui/button/rarity_1.png", None, "gui/button/item_cookies.png", cookies_ingredients, 1),

    # Let's suppose we add an item "cookies" to the inventory like this:
    inventory.append(["cookies", amount])

    # Now we immediately sort the list:
    inventory.sort(key=lambda x: items[x[0]][1])
meaning
(1) we take an element of inventory - here denoted as x,
(2) we take the key for items dictionary - it's x[0]
(3) and we get items[x[0]] which is the description of this item.
(4) Where at the index [1] is the id we want to sort by.
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

User avatar
voluorem
Regular
Posts: 29
Joined: Fri Jun 24, 2022 3:32 pm
Projects: Sweet Release
Discord: voluorem
Contact:

Re: [Solved] For loop range/pages issue

#22 Post by voluorem »

Ah, okay. I tried something like that but I got the indexing slightly wrong. Thank you!

Post Reply

Who is online

Users browsing this forum: gregkulbar, Ocelot