[Solved] Confused about buttons

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
lls
Regular
Posts: 30
Joined: Thu Oct 13, 2011 9:11 pm
Contact:

[Solved] Confused about buttons

#1 Post by lls » Mon Oct 24, 2011 12:24 am

Is the 'action' property not supposed to activate only when I click the button? I have an inventory list made of buttons (each button has the item's name on it), and I want it so that whenever I click one of them, that item will be removed.

Code: Select all

for item in inventory.list_of_items:
     textbutton ("Use") action inventory.delete_item(item)
However, when I run this code, all the items are gone--every single item has already been deleted even though I haven't done anything. Why is this? If I do something like

Code: Select all

if(deleteThis!=None):
     inventory.list_of_items.remove(deleteThis)

for item in list_of_items:
     textbutton ("Use") action SetVariable("deleteThis", item)
and there is more than one instance of the item (say list_of_items = [1, 2, 3, 4, 5, 3, 3, 3]), all of them are deleted (leaving [1, 2, 4, 5])--but only after I click on another item.

There's a bit of code that checks if the inventory is empty, and if so, writes "(Inventory empty)". However when everything has been deleted, I only get "(Inventory empty" - the closing bracket is missing for some reason. The code goes like this:

In the Inventory class, there are two functions:

Code: Select all

        # Get a list of all unique items
        def getUniqueList(self):
            uniqueSet = Set(item for item in self.list_of_items)
            return [item for item in uniqueSet]

        # Get the number of times an item appears in the inventory
        def getUniqueCount(self):
            uniqueSet = Set(item for item in self.list_of_items)
            return [self.list_of_items.count(item) for item in uniqueSet]
And in the inventory screen:

Code: Select all

                if(inventory.list_of_items): # Inventory is not empty
                    
                    # Remove item
                    if(deleteThis!=None):
                        inventory.list_of_items.remove(deleteThis)
                    
                    $ uniqueList = inv.getUniqueList()
                    $ countList = inv.getUniqueCount()
                    
                    for i, item in enumerate(uniqueList):
                        $ currCount = countList[i]
                        textbutton ("[item.name]  (x[currCount])") action SetVariable("deleteThis", item)
                            
                else:
                    text "(Inventory empty)"
Last edited by lls on Fri Oct 28, 2011 10:35 pm, edited 1 time in total.

User avatar
DragoonHP
Miko-Class Veteran
Posts: 758
Joined: Tue Jun 22, 2010 12:54 am
Completed: Christmas
IRC Nick: DragoonHP
Location: Zion Island, Solario
Contact:

Re: Confused about buttons

#2 Post by DragoonHP » Mon Oct 24, 2011 12:54 am

Concerning your main problem, it is to be expected... because you are telling Ren'Py to continue deleting items till it runs out of them...

lls
Regular
Posts: 30
Joined: Thu Oct 13, 2011 9:11 pm
Contact:

Re: Confused about buttons

#3 Post by lls » Mon Oct 24, 2011 5:22 pm

because you are telling Ren'Py to continue deleting items till it runs out of them..
I don't understand this. I'm trying to make buttons that, when clicked, will delete the item it's mapped to. Therefore absolutely no removal code should be run when the buttons have not been clicked. I see that I'm going about this whole button thing the wrong way, but I can't fathom what the right way should be - are the purpose of buttons not to lie dormant until they are clicked on? How then would I write code that doesn't run until that happens?

User avatar
DragoonHP
Miko-Class Veteran
Posts: 758
Joined: Tue Jun 22, 2010 12:54 am
Completed: Christmas
IRC Nick: DragoonHP
Location: Zion Island, Solario
Contact:

Re: Confused about buttons

#4 Post by DragoonHP » Mon Oct 24, 2011 10:17 pm

Code: Select all

for item in inventory.list_of_items:
     textbutton ("Use") action inventory.delete_item(item)
[code]

There's nothing wrong with the button, the problem is in the function... using the for statement isn't the ideal choide here, until you put a break statement here...

Read this... http://effbot.org/zone/python-for-statement.htm for better explanation...

User avatar
Spiky Caterpillar
Veteran
Posts: 253
Joined: Fri Nov 14, 2008 7:59 pm
Completed: Lots.
Projects: Black Closet
Organization: Slipshod
Location: Behind you.
Contact:

Re: Confused about buttons

#5 Post by Spiky Caterpillar » Wed Oct 26, 2011 12:43 am

lls wrote:Is the 'action' property not supposed to activate only when I click the button? I have an inventory list made of buttons (each button has the item's name on it), and I want it so that whenever I click one of them, that item will be removed.

Code: Select all

for item in inventory.list_of_items:
     textbutton ("Use") action inventory.delete_item(item)
While the action itself is called only after the button is clicked, that code actually sets the action to be the RESULT of inventory.delete_item(item), which is probably not what you want.

You probably want something like:

Code: Select all

init python:
    def deletor(item):
        inventory.delete_item(item)
    deletes_item = renpy.curry(deletor)
...
screen inv:
    for item in inventory.list_of_items:
        textbutton "Use" action deletes_item(item)
You may need to throw a renpy.restart_interaction() or return 1 into the deletor function to make sure Ren'Py redraws the appropriate things (I don't remember exactly how action return values are interpreted)
Nom nom nom nom nom LEAVES.

lls
Regular
Posts: 30
Joined: Thu Oct 13, 2011 9:11 pm
Contact:

Re: Confused about buttons

#6 Post by lls » Wed Oct 26, 2011 10:39 pm

that code actually sets the action to be the RESULT of inventory.delete_item(item)
Thank you so much for this, things make a lot more sense now. Thanks for the code too--it indeed works :D It seems kind of long/complicated/redundant though, because... making a function that does practically nothing but calling another function. Is there a way to do this without using curry?

User avatar
Spiky Caterpillar
Veteran
Posts: 253
Joined: Fri Nov 14, 2008 7:59 pm
Completed: Lots.
Projects: Black Closet
Organization: Slipshod
Location: Behind you.
Contact:

Re: Confused about buttons

#7 Post by Spiky Caterpillar » Thu Oct 27, 2011 5:28 pm

Currying seemed a little roundabout to me the first time I saw it, but the actual overhead from it is negligible (Just drawing a bit of text onscreen uses far more CPU time) and the alternatives tend to be less readable and more annoying to maintain.

You could just curry inventory.delete_item directly and not bother with the deletor function, but currying instance methods could introduce subtle bugs in a few cases. You could use a function that returns a function, like

Code: Select all

init python:
    def uses_item(itemname):
          def ret():
                inventory.delete_item(itemname)
          return ret
screen inv:
    for item in list_of_items:
         textbutton "Use" action uses_item(item)
but IIRC functions cannot be stored inside saved games, so it'll crash if you save the game - and if functions could be saved, using saves made in an older version of the game would give you the old version of the generated function rather than the current version.
Nom nom nom nom nom LEAVES.

Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot]