[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
User avatar
voluorem
Regular
Posts: 29
Joined: Fri Jun 24, 2022 3:32 pm
Projects: Sweet Release
Discord: voluorem
Contact:

[Solved] For loop range/pages issue

#1 Post by voluorem »

I made an inventory system with multiple pages that shows how many of each item you have, but I realized the amounts only correlate to the items on the first page and then just copies those amounts to the next pages.
This is what I mean (ignore the placeholder pngs lol):

Correct amounts (page 1):
Image
Incorrect amounts (page 2):
Image

And here's my screen code:

Code: Select all

grid 4 2:
    xalign 0.5
    yalign 0.5
    xspacing -10
    yspacing -10
    for i, key in zip(range(8*page_number, 8*(page_number+1)), angestuff_ingredients.inventory): ## I know this is at least part of the issue
        if i < len(angestuff_ingredients.inventory):
            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 list(angestuff_ingredients.inventory)[i].img xalign 0.5
                        frame:
                            background None
                            xysize (310, 75)
                            xalign 0.5
                            yalign 0.6
                            if check_space(list(angestuff_ingredients.inventory)[i].name) >= 2:
                                if len(list(angestuff_ingredients.inventory)[i].name) >= 20:
                                    text (list(angestuff_ingredients.inventory)[i].name) yanchor 0.5 xalign 0.5 yalign 1.0 text_align 0.5 size 25 font "gui/fonts/Vanillaextract-1GZ84.ttf"
                                else:
                                    text (list(angestuff_ingredients.inventory)[i].name) yanchor 0.5 xalign 0.5 yalign 1.0 text_align 0.5 size 25 font "gui/fonts/Vanillaextract-1GZ84.ttf"
                            else:
                                text (list(angestuff_ingredients.inventory)[i].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(list(angestuff_ingredients.inventory)[i].shortname.lower()), false_value=None)
                    frame: ## this is where the amount is
                        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 str(angestuff_ingredients.inventory[key]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
        else:
            null
            
showif len(angestuff_ingredients.inventory) > 0:
    showif page_number != 0:
        imagebutton:
            idle "gui/button/inventory_arrowleft.png"
            anchor (0.5, 0.5)
            yalign 0.5
            xalign 0.1
            action SetVariable("page_number", page_number-1)
    showif len(angestuff_ingredients.inventory) > 8 and len(angestuff_ingredients.inventory)/8 > page_number+1:
        imagebutton:
            idle "gui/button/inventory_arrowright.png"
            anchor (0.5, 0.5)
            yalign 0.5
            xalign 0.9
            action SetVariable("page_number", page_number+1)
angestuff_ingredients.inventory is a dictionary where the key is a class object and the value is the amount you have. I know it has something to do with the for loop range, I just can't figure out how to fix it.
Last edited by voluorem on Fri Mar 01, 2024 3:42 am, edited 1 time in total.

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

Re: For loop range/pages issue

#2 Post by downover »

I'm not sure how your data type works but you're accessing the item name, which is working, through angestuff_ingredients.inventory[i] and the inventory amount, which is not, through angestuff_ingredients.inventory[key]. I would just loop over i and use i to look up all info needed.
Last edited by downover on Sun Feb 25, 2024 7:13 pm, edited 1 time in total.

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

#3 Post by voluorem »

When I try to just use i, it gives me a KeyError:

Code: Select all

I'm sorry, but an uncaught exception occurred.

While running game code:
  File "game/script.rpy", line 108, in script call
    call roomload
  File "game/technical.rpy", line 662, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  [Previous line repeated 7 more times]
  File "game/technical.rpy", line 3263, in script
    call screen expression gacha + "gacha"
  File "renpy/common/000statements.rpy", line 670, in execute_call_screen
    store._return = renpy.call_screen(name, *args, **kwargs)
  File "game/screens.rpy", line 1339, in execute
    screen ingredients_list:
  File "game/screens.rpy", line 1339, in execute
    screen ingredients_list:
  File "game/screens.rpy", line 1354, in execute
    frame:
  File "game/screens.rpy", line 1358, in execute
    vbox:
  File "game/screens.rpy", line 1360, in execute
    grid 4 2:
  File "game/screens.rpy", line 1365, in execute
    for i, key in zip(range(8*page_number, 8*(page_number+1)), angestuff_ingredients.inventory):
  File "game/screens.rpy", line 1366, in execute
    if i < len(angestuff_ingredients.inventory):
  File "game/screens.rpy", line 1367, in execute
    button:
  File "game/screens.rpy", line 1372, in execute
    frame:
  File "game/screens.rpy", line 1398, in execute
    frame:
  File "game/screens.rpy", line 1406, in execute
    text str(angestuff_ingredients.inventory[i]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
  File "game/screens.rpy", line 1406, in <module>
    text str(angestuff_ingredients.inventory[i]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
KeyError: 0

-- Full Traceback ------------------------------------------------------------

Full traceback:
  File "game/script.rpy", line 108, in script call
    call roomload
  File "game/technical.rpy", line 662, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  File "game/technical.rpy", line 3188, in script call
    $ renpy.pause(hard=True)
  [Previous line repeated 7 more times]
  File "game/technical.rpy", line 3263, in script
    call screen expression gacha + "gacha"
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\ast.py", line 2259, in execute
    self.call("execute")
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\ast.py", line 2241, in call
    return renpy.statements.call(method, parsed, *args, **kwargs)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\statements.py", line 342, in call
    return method(parsed, *args, **kwargs)
  File "renpy/common/000statements.rpy", line 670, in execute_call_screen
    store._return = renpy.call_screen(name, *args, **kwargs)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\exports.py", line 3345, in call_screen
    rv = renpy.ui.interact(mouse="screen", type="screen", roll_forward=roll_forward)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\ui.py", line 299, in interact
    rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 3579, in interact
    repeat, rv = self.interact_core(preloads=preloads, trans_pause=trans_pause, pause=pause, pause_start=pause_start, pause_modal=pause_modal, **kwargs) # type: ignore
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 4052, in interact_core
    root_widget.visit_all(lambda d : d.per_interact())
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 681, in visit_all
    d.visit_all(callback, seen)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 681, in visit_all
    d.visit_all(callback, seen)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 681, in visit_all
    d.visit_all(callback, seen)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\screen.py", line 476, in visit_all
    callback(self)
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\core.py", line 4052, in <lambda>
    root_widget.visit_all(lambda d : d.per_interact())
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\screen.py", line 487, in per_interact
    self.update()
  File "C:\Users\thepr\Downloads\renpy-7.4.11-sdk\renpy\display\screen.py", line 680, in update
    self.screen.function(**self.scope)
  File "game/screens.rpy", line 1339, in execute
    screen ingredients_list:
  File "game/screens.rpy", line 1339, in execute
    screen ingredients_list:
  File "game/screens.rpy", line 1354, in execute
    frame:
  File "game/screens.rpy", line 1358, in execute
    vbox:
  File "game/screens.rpy", line 1360, in execute
    grid 4 2:
  File "game/screens.rpy", line 1365, in execute
    for i, key in zip(range(8*page_number, 8*(page_number+1)), angestuff_ingredients.inventory):
  File "game/screens.rpy", line 1366, in execute
    if i < len(angestuff_ingredients.inventory):
  File "game/screens.rpy", line 1367, in execute
    button:
  File "game/screens.rpy", line 1372, in execute
    frame:
  File "game/screens.rpy", line 1398, in execute
    frame:
  File "game/screens.rpy", line 1406, in execute
    text str(angestuff_ingredients.inventory[i]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
  File "game/screens.rpy", line 1406, in <module>
    text str(angestuff_ingredients.inventory[i]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
KeyError: 0

Windows-10-10.0.22621 AMD64
Ren'Py 8.1.1.23060707
Sweet Release 1.0
Sun Feb 25 15:12:09 2024

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

Re: For loop range/pages issue

#4 Post by downover »

Oh, you're converting it to a list before inidexing above. So to fix the issue, change it to:

Code: Select all

 
                        text str(list(angestuff_ingredients.inventory)[i]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
However, this is wildly inefficient cause you're converting a dict to a list like 5 times per loop. You should probably just loop over the dict and use key instead of i. Not sure of the best way to do it in ren'py though.

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

#5 Post by voluorem »

That just does this:
Image

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

Re: For loop range/pages issue

#6 Post by downover »

Sorry. Again, I don't know your dictionary layout. The code I gave you uses the object itself as the key. Is the key supposed to be the item name? For instance, if the key is .name, then change the line to:

Code: Select all

                        text str(list(angestuff_ingredients.inventory)[i].name) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25                       

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

Re: For loop range/pages issue

#7 Post by downover »

It would probably be useful to, right below the for line and the count check, before you do any logic, log what the values of key and i are.

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

#8 Post by voluorem »

The key is the object (containing all of the item data minus the amount) and the value is the amount. I'm trying to get it to display the value for each key. The i is the index of the dictionary.

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

Re: For loop range/pages issue

#9 Post by downover »

Gotcha.

The line I gave you is converting the object to a string, which is what you see in your latest screenshot. So you have the key using [i]. Quite a one-liner! This is why it might make sense to define all this stuff at the top of your function, once.

[code]
text str(list(angestuff_ingredients.inventory[angestuff_ingredients.inventory)[i]]) xanchor 0.5 xpos 200 yalign 0.5 font "gui/fonts/Vanillaextract-1GZ84.ttf" size 25
[/code]

The other suggestion I have is that using the object as the key isn't a good idea. Your definitions of items should live in one dict, and your inventory another, and both should use item_id as the key IMO. This is called [url=https://en.wikipedia.org/wiki/Database_normalization]normalization[/url] and will help you down the road. You don't want to have to call the player's inventory if you're listing all the items in the game in a help page, for inistance!

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

#10 Post by voluorem »

This isn't working either, unfortunately :( It's giving me the same KeyError as before.

And for the normalization part, do you mean having the inventory screen loop through all of the items, check if each item is in the inventory, and then continue from there? That's a much better idea than what I've been doing, lol.

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

Re: For loop range/pages issue

#11 Post by downover »

Exactly. Keep a dict that's all the info about each item. Links to its pic, its item Id, display name, etc. Then use the item ID as its key. Again, super new to ren'py so I don't know how its save works, but I'd keep it outside of save data, because it might change from update to update.

Then, on your character's save, you just need to keep a dict of item_id to quantity. Then when you loop through your character's inventory, you fetch the data from the item_definitions dict for each item in their inventory.

Pseudocode is basically...

Code: Select all

for item_id in inventory.items():
	item_definition = item_definitions[item_id]
	item_name = item_definition.display_name
	item_quantity = inventory[item_id]
	# now you can draw the table here without worrying about indexing into lists/dicts any more

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

#12 Post by voluorem »

I put all the items in a dictionary (minus the definitions, I kept those as objects because I thought that would be a lot easier), but now it's not showing anything in the screen. Here's the new screen code:

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 e in angestuff_present.inventory:
                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
And the dictionary:

Code: Select all

define item_id = {
    1: cookies,
    2: brownies,
    3: whitecake,
    4: chocolatecake,
    5: chocpudding,
    ## etc
It's not that there's something wrong with the inventory because it's completely blank (if there's nothing in the inventory a message appears).

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

Re: For loop range/pages issue

#13 Post by jeffster »

I didn't read all the posts carefully but it seems that it should be simpler:

angestuff_present.inventory is a list of items. For example:

Code: Select all

angestuff_present.inventory[8] = 3
means the 8th item in the inventory is whitecake (that is item_id[3]).
Then your screen code would be like

Code: Select all

screen inv():
    grid 4 2:
        for i in range(8*page_number, 8*page_number+8):
            if i < len(angestuff_present.inventory):
                # angestuff_present.inventory[i] is the id of the item, and you can use it to show its pic, name, amount.
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

#14 Post by voluorem »

That's what I have in my last reply (although I also have it check to make sure each item is in the inventory before it displays it). Also, item_id and the inventory are two separate dictionaries (I think it would've been way too convoluted and unnecessary to have a nested dictionary inside another nested dictionary since I also need to keep track of how many of each item you have).

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

Re: For loop range/pages issue

#15 Post by jeffster »

I understand that in zip() you are iterating item_id, which becomes e, together with i which is a position of an item on screen.
It seems incorrect unless every item has fixed page number and position on that page.
(I would expect the present items to be a list with more arbitrary item placement on screen, e.g. some items could be absent and not create an empty spot in the screen inventory).

PS. Also I don't know Python that well but I think you should iterate item_id not from its start but skipping previous pages?
If the problem is solved, please edit the original post and add [SOLVED] to the title. 8)

Post Reply

Who is online

Users browsing this forum: Amazon [Bot], Majestic-12 [Bot]