Ren'Py choices menu with different buttons

Ker_

Member
Game Developer
Jun 4, 2021
250
764
Hi
I want to make the choices available only for the full version, and in free to play leave only the single possible choice.
Spent the whole evening, I can't figure out how to make the selection icons look different.
Example: Secret reloaded
Screenshot (217).png
Maybe someone know easy solution?
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Given the layout, that looks like a custom screen:.

I think if I were coding it (and making some BIG assumptions about your design)... I'd use two versions of each button within the screen.

A sort of:
Code:
#for each button
if full version:
    use full button
else
    use disabled button

Where the full version button uses one picture and has a specific action and the disabled button uses a different picture (maybe gray?) and uses action NullAction() and so it does nothing - while at the same time behaving like a normal button.

The Secret Reloaded does it slightly different from my suggestion, by having a custom screen with all the buttons, but there is only one button per choice - except the parameters for each button change depending on a patreon status flag.

You don't have permission to view the spoiler content. Log in or register now.


But re-reading your question... I'm not sure that's what you are asking.

Are you perhaps asking how to include images within a menu: choice?

For that, you need only include an {image=filename} tag within the text. Same way you'd use {b} {/b} for bold or {i} {/i} for italics.

It might look something like:

Python:
label start:

    scene black with fade

    menu:
        "{image=analfuck_icon.png} ANAL FUCK"
            jump ch02_anal_fuck

        "{image=pussyfuck_icon.png} PUSSY FUCK"
            jump ch02_pussy_fuck

        # etc.

If you want to turn that into something the can tell the difference between your full and free versions... then you'd code each menu choice to make that distinction...


Python:
label start:

    scene black with fade

    menu:
        "{image=analfuck_icon.png} ANAL FUCK":
            jump ch02_anal_fuck

        "{image=pussyfuck_icon.png} PUSSY FUCK":
            jump ch02_pussy_fuck

        "{image=blowjob_icon.png} BLOWJOB":
            jump ch02_blowjob

        "{image=thighfuck_icon.png} THIGH FUCK" if patreon_version == True:
            jump ch02_thigh_fuck

        "{image=thighfuck_icon.png} THIGH FUCK {image=patreon_icon.png}" if patreon_version == False:
            jump ch02_thigh_fuck_not_available

        "{image=footjob_icon.png} FOOTJOB" if patreon_version == True:
            jump ch02_footjob

        "{image=footjob_icon.png} FOOTJOB {image=patreon_icon.png}" if patreon_version == False:
            jump ch02_footjob_not_available

In this hypothetical code, the "full" version would just work, whereas the "free" version would show an additional patreon logo image as part of the button choice and jump to a label that tells the player it's unavailable.

Hopefully, one of those two answers covers what you are aiming for.
 
Last edited:

Ker_

Member
Game Developer
Jun 4, 2021
250
764
Thank you so much for such a comprehensive answer!
I tried the second option of adding a picture to the menu. If I do not find a more suitable option, I will use it. But it's not quite to my liking because I would like to not only add a patreon icon at the end or before the text but also change the color of the background completely. In secret reloaded choices for patrons highlighted in gold.
But judging by the code, he doesn't use text at all and just inserts pictures in different colors. This will also not suit me because i want to apply it in all situations with a choice and it's too long to draw each manually.
p.s. your "Newbie guide to spellchecking" is also very helpful, thanks for it too :)
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Keep in mind, this is RenPy... so pretty much anything is possible.

And yes, the original you are looking at is using a custom screen and only images. No text and no menus. (But keep in mind that menus are just screens too).
I would code a custom screen too.

But if you do still want to consider using menus...

Last time we played with something like this, we ended up with this:

Python:
# --- file screens.rpy ---

screen choice(items):
    style_prefix "choice"

    vbox:
        for i in items:
            if i.action:
                if i.kwargs and i.kwargs.get( "enabled" ) is False:
                    textbutton i.caption action NullAction() sensitive False
                else:
                    textbutton i.caption action i.action
            else:
                textbutton i.caption

By altering the menu/choices screen this way... you will then be able to code something like:

Python:
label start:

    scene black with fade

    menu:
        "{image=analfuck_icon.png} ANAL FUCK":
            jump ch02_anal_fuck

        "{image=pussyfuck_icon.png} PUSSY FUCK":
            jump ch02_pussy_fuck

        "{image=blowjob_icon.png} BLOWJOB":
            jump ch02_blowjob

        "{image=thighfuck_icon.png} THIGH FUCK" (enabled=patreon_version):
            jump ch02_thigh_fuck

        "{image=footjob_icon.png} FOOTJOB" (enabled=patreon_version):
            jump ch02_footjob

The ability to add parameters to the menu choices wasn't always part of RenPy. But it exists now, so can be used to pass information by putting it within parenthesis before the colon.

Edit: To clarify things a little, if the variable patreon_version is set to True - then that's effectively the same as (enabled=True). Likewise False. When the menu choice is disabled, it uses a different style - which can be recolored or styled however you like.
 
Last edited:
  • Like
Reactions: Everia and Ker_

Ker_

Member
Game Developer
Jun 4, 2021
250
764
Keep in mind, this is RenPy... so pretty much anything is possible.

And yes, the original you are looking at is using a custom screen and only images. No text and no menus. (But keep in mind that menus are just screens too).
I would code a custom screen too.

But if you do still want to consider using menus...

Last time we played with something like this, we ended up with this:

Python:
# --- file screens.rpy ---

screen choice(items):
    style_prefix "choice"

    vbox:
        for i in items:
            if i.action:
                if i.kwargs and i.kwargs.get( "enabled" ) is False:
                    textbutton i.caption action NullAction() sensitive False
                else:
                    textbutton i.caption action i.action
            else:
                textbutton i.caption

By altering the menu/choices screen this way... you will then be able to code something like:

Python:
label start:

    scene black with fade

    menu:
        "{image=analfuck_icon.png} ANAL FUCK":
            jump ch02_anal_fuck

        "{image=pussyfuck_icon.png} PUSSY FUCK":
            jump ch02_pussy_fuck

        "{image=blowjob_icon.png} BLOWJOB":
            jump ch02_blowjob

        "{image=thighfuck_icon.png} THIGH FUCK" (enabled=patreon_version):
            jump ch02_thigh_fuck

        "{image=footjob_icon.png} FOOTJOB" (enabled=patreon_version):
            jump ch02_footjob

The ability to add parameters to the menu choices wasn't always part of RenPy. But it exists now, so can be used to pass information by putting it within parenthesis before the colon.
Thank you very much!!
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,192
Python:
                if i.kwargs and i.kwargs.get( "enabled" ) is False:
Hmm, I guess I was the one coming to this solution because I see a flaw in it ;)

So far this code works fine because there's only one case where i.kwargs exist ; when the "enabled" optional argument is given. But if you extend the use of i.kwargs, defining more optional arguments, then you'll trigger a KeyError exception every time the kwargs exist, but not the "enabled" key.
Therefore here get should be used in its get( Key, Default ) extended syntax. The second value being the one returned if the keyword isn't in the dictionary ; what prevent the KeyError exception.

Since a choice that don't have the "enabled" optional argument is to be shown normally, I would use True as default value, what lead to i.kwargs.get( "enabled", True ).
Anyway, the use of None is to avoid, since some would want to reduce the condition to if ikwargs and i.kwargs.get( "enabled", None ):, that would match both False and None.
 
  • Like
Reactions: 79flavors and Ker_

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Hmm, I guess I was the one coming to this solution [...]

Yup. That's all you. I barely understand how it works :whistle:
I just copy-paste it, each time this topic comes up. :devilish:

Since a choice that don't have the "enabled" optional argument is to be shown normally, I would use True as default value, what lead to i.kwargs.get( "enabled", True ).

That would certainly be my instinct, now that you've mentioned it.

Anyway, the use of None is to avoid, since some would want to reduce the condition to if ikwargs and i.kwargs.get( "enabled", None ):, that would match both False and None.

Are you saying "avoid this" or "this would be better"?
It sounds like a check for False and None would be an improvement - although I'm only reacting to your phrasing of the reply, rather than following the python logic.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,192
Yup. That's all you. I barely understand how it works :whistle:
It's not too difficult. It's just an extension of the anonymous parameters/arguments already used with functions/methods ; named args (as in "arguments") and kwargs (as in "keyword arguments") by convention.

When you declare a function, you've to give the arguments it expect, and their names. By example, def myFunction( text, enabled=True ):.
Then something like myFunction( "choice 1", enabled=False ) will assign choice 1 to the variable named text, and the value False to the variable named enabled.
Your function could looks like that:
Code:
def myFunction( text, enabled=False ):
    if enabled is True:
        renpy.notify( text )
One day, you decide to add a "hint" parameter/argument, and for this you'll change the declaration of your function into def myFunction( text, enabled=True, hint="No hint" ): ; if you don't do it, Ren'py/Python will complain that there's too many arguments.

But you can also decide to declare your arguments anonymously with def myAnonFunction( *args, **kwargs ):. All the "regular arguments" will be stored into a list named args, in their order or appearance, while all the "keyword arguments" will be stored into a dict named kwargs. You call the function in the exact same way, but now the code of your function would looks like that:
Code:
def myAnonFunction( *args, **kwargs ):
    if "enabled" in kwargs and kwargs["enabled"] is True:
        renpy.notify( args[0] )
Then, when you'll add the "hint" optional parameter/argument you'll have to... well, you'll just have to change the code of your function, but not its declaration.

You don't have permission to view the spoiler content. Log in or register now.

Anonymous parameters/arguments are generally used when you don't control the parameters/arguments ; by example when you overwrite an inherited method. Or when there's really too many parameters/arguments and they are too versatile. It's by example the case for the Character object, where you can redefine part of the "say" style when you create the object. It would be a pain in the ass to declare a parameter/argument for "who_color", "who_size", "what_color", "what_size", and all the other possibilities. Instead, the class __int__ method is declared with :def __init__( self, name=NotSet, kind=None, **properties): ; note that, as I said, args and kwargs are just convention, you can use more explicit names, like it's the case here.
[More about args/kwargs can be found here.]

What lead me to the menu choice. Most of the time, you'll use them without parameters/arguments, and must be said that having to declare the ones you'll possibly use would be a difficult task. Therefore, PyTom opted for the anonymous approach, exactly like if each choice was a function. What mean that menu syntax should be seen as (with bracket marking the "optionality":
Code:
menu [label][(named_parameters)]:
    choice [(*args, **kwargs)][condition]:
        statements
And, each menu item (the i in your example) is used to carry the args list and kwargs dict. This exactly like it already carry the text of the choice in text, and what have to be done if the choice is selected in action.


Are you saying "avoid this" or "this would be better"?
Avoid this.
Around half of devs shorten Boolean tests by simply removing the is True/is False part. It's bad, but not totally wrong.
But it have side effect, since it don't to discriminate between False and None:
Code:
label whatever:
    $ myVar = False
    if not myVar:
        "myVar value is False"
    $ myVar = None
    if not myVar:
        "myVar value is... er, False ?"
It's always better to have a default value that is really at the opposite of the value that is significant to you ; as least each time it's possible.

Edit:
A typo in the syntax.
 
Last edited:
  • Like
Reactions: Ker_ and 79flavors