• To improve security, we will soon start forcing password resets for any account that uses a weak password on the next login. If you have a weak password or a defunct email, please update it now to prevent future disruption.

Ren'Py Intelligent Imagebuttons

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
Little eye candy to incentivise the response.
club_query.jpg

Within the game I am creating I have implemented a strip club with a fairly dynamic background and dancers. Not only does the background change based upon your relative success, however you can change which performers appear on each stage. Whilst all this is working fine, I am seeking a less verbose solution to allocating the dancers to the stages.

I have a screen within which I list the available dancers (5 at present with more to come) and also the stages on which they perform via the circles. I'll pretty up the screen once I have resolved the coding.
club_query2.jpg
The issue I face, is that I use three variables, to list what performer appears on each stage (pole1_dancer, pole2_dancer, pole3_dancer). These variables are set to the relevant dancer name. This sort of works, however I am able to assign the same dancer to multiple stages, thereby effectively cloning the dancer in game. Whilst I can fix this using the following code, it is horribly verbose.
Python:
                imagebutton:
                    auto "cbutton_%s.png"
                    action SetVariable("pole1_dancer", "chiara")
                    if pole2_dancer == "chiara":
                        action SetVariable("pole2_dancer", "none")
                    if pole3_dancer == "chiara":
                        action SetVariable("pole2_dancer", "none")
I would have to do similar code for every single button.

To provide some reference on how this is used in the first image the screen code is as below:
Python:
screen club:
    imagemap:
        auto ("strip{}_cam_%s.webp".format(club_croud))

        hotspot (0,860,1920,220) clicked Jump("hold")
        hotspot (520,0,880,860) clicked Jump("club_bar")

        if pole2_dancer != "none":
            imagebutton:
                auto ("{}_2_{}_%s.webp".format(pole2_dancer,pole2_pos))
                focus_mask True
                action Jump("club_pole2")
        if pole3_dancer != "none":
            imagebutton:
                auto ("{}_3_{}_%s.webp".format(pole3_dancer,pole3_pos))
                focus_mask True
                action Jump("club_pole3")
        if pole1_dancer != "none":
            imagebutton:
                auto ("{}_1_{}_%s.webp".format(pole1_dancer,pole1_pos))
                focus_mask True
                action Jump("club_pole1")
Assistance in optimising the stage selection coding would be appreciated.
 

Rich

Old Fart
Modder
Respected User
Donor
Game Developer
Jun 25, 2017
2,472
6,941
One option might be to use "sub-screens" - create a screen that has the repetitive code in it and then "use" that screen into the main screen. With this approach, you can parameterize the sub-screen.

Over-simplified example (which may have syntax errors, since I'm free-typing it), but:

Code:
screen dancer_button(dancer, pos, template, the_label):
    imagebutton:
          auto (template.format(dancer,pos))
          focus_mask True
          action Jump(the_label)
And then in your main screen

Code:
       if pole3_dancer != "none":
            use dancer_button(pole3_dancer, pole3_pos, "{}_3_{}_%s.webp", "club_pole3")
So, the "dancer_button" screen kind of serves as a "macro" so that you don't have to repeat the full code over and over again.
 
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
To clarify it's the first piece of code that I am trying to simplify. The one where I am attempting to prevent a single dancer being allocated to multiple stages.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,561
2,180
Wouldn't you also have a problem with combinations of dancers?

By which I mean, since you have only 3 dance stages and potentially up to 10 (or more) dancers... wouldn't you need different images for say Paige appearing on stage #1 vs stage #2 or stage #3? The two foreground poles tables look around the same size, but one shows the left side of the dancer's area whereas the other pole shows the right side of the dancer's area. The one in the background is noticeably smaller.

I'd therefore assume (if you're future-proofing your design) that each dancer would have 3 idle and 3 hover pictures, depending on which stage they have been assigned to. Unless I'm missing something.

I haven't played with the code for this, but couldn't you pass the name of each dancer to the screen as a parameter?

Something like:
call screen club("paige", "felicity", "vera")
-or-
call screen club(None, "chiara", None)

Then use the name of the dancer and the position within the parameter list to determine which idle and hover image to use at each imagebutton: ?
I'm sure it's possible to concatenate things together to build each idle: and hover: value.

I'm using None as opposed to "none" out of habit. Either would work, as long as you're consistant.

Where the resulting image names could be something like:
"club_pole1_paige_idle.webp" or "club_pole3_chiara_hover.webp"

Then, all you would need is some extra code to determine which names to pass as which parameters...
Maybe even passing the name of the girl as a parameter to the label that is jumped to...
Edit: Just tested this. Despite my wishful thinking, apprently you can't pass a parameter to a label you Jump to.. So you'd need another mechanism.

Python:
screen club (pole1_stage, pole2_stage, pole3_stage):
    # blah, blah, screen code...
        if pole1_stage != None:
            imagebutton:
                focus_mask True
                auto ("club_pole1_{}_%s.webp".format(pole1_stage))
                action [SetVariable ("dancer_picked", pole1_stage), Jump("club_picked_dancer")]

                # or would this work? (then you wouldn't need the extra variable)
                # action Jump("club_picked_{}".format(pole1_stage))]    # like label club_picked_vera

        if pole2_stage != None:
            imagebutton:
                focus_mask True
                auto ("club_pole2_{}_%s.webp".format(pole2_stage))
                action [SetVariable ("dancer_picked", pole2_stage), Jump("club_picked_dancer")]

        if pole3_stage != None:
            imagebutton:
                focus_mask True
                auto ("club_pole3_{}_%s.webp".format(pole3_stage))
                action [SetVariable ("dancer_picked", pole3_stage), Jump("club_picked_dancer")]

    # blah, blah, more screen code...

# blah, blah, code...

jump display_the_club_screen

# blah, blah, more code...

# ... until ...
label display_the_club_screen:

    $ pole1_dancer = None
    $ pole2_dancer = None
    $ pole3_dancer = None
    $ dancer_picked = None

    # this bit will depend on how you make the games available, I'm using imaginary variables "available_to_dance".
    if paige_available_to_dance == True:
        call add_pole_dancer("paige")

    if felicity_available_to_dancer == True:
        call add_pole_dancer("felicity")

    # etc.

    call screen club (pole1_dancer, pole2_dancer, pole3_dancer)


label add_pole_dancer(the_girl):

    # assign girl to first pole available

    if pole1_dancer == None:
        $ pole1_dancer = the_girl
        return

    if pole2_dancer == None:
        $ pole2_dancer = the_girl
        return

    if pole3_dancer == None:
        $ pole31_dancer = the_girl
        return

    return


label club_picked_dancer:

    if dancer_picked == "paige":

    # blah, blah, I've no fucking clue what you'd want to do here
Edit: Code edited to use SetVariable() instead of trying to pass a value to a label that is jumped to.
(I suppose you could try jumping to a label name calculated using the name of the girl instead... No, no... no more clever ideas without testing).



This would also mean if you have more than 3 girls available, it would only pick the first 3.

Of course, that presumes you are happy for Paige to always take the first spot if she's available.
Alternatively, you could store the names of the available girls in an array (what python calls a list) and randomly sort that list before selecting 3 girls.

All that said... your "Recruited Girls" screen already seems to have code in there to (potentially) allow the player to pick which stage the girl is working on. So couldn't you just re-purpose wherever that data is stored?

I have NOT tested any of this code, it's coming straight from my head to the keyboard. It should work if all my assumptions are correct... but life not like that.
 
Last edited:

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
Wouldn't you also have a problem with combinations of dancers?

By which I mean, since you have only 3 dance stages and potentially up to 10 (or more) dancers... wouldn't you need different images for say Paige appearing on stage #1 vs stage #2 or stage #3? The two foreground poles tables look around the same size, but one shows the left side of the dancer's area whereas the other pole shows the right side of the dancer's area. The one in the background is noticeably smaller.

I'd therefore assume (if you're future-proofing your design) that each dancer would have 3 idle and 3 hover pictures, depending on which stage they have been assigned to. Unless I'm missing something.
I actually have 36 images for each dancer as I have have 6 different poses. Not scared of a little work and the file size of these images in webp format is tiny.

The issue as I mentioned above is not related to displaying the resultant imagebuttons as this is done well by the following.
Python:
            imagebutton:
                auto ("{}_1_{}_%s.webp".format(pole1_dancer,pole1_pos))
                focus_mask True
                action Jump("club_pole1")
This provide and image button which can be clicked to switch the view such that you are seated viewing the selected dancer / stage.

Really all I am trying to create is a mechanism to ensure that a scenario like does not occur:
pole1_dancer = "vera"
pole3_dancer = "vera"

The only method I have come up with so far, is to inspect the other two locations and set to none if the dancer is already assigned to another stage. I suspect the solution is likely to related to creating an array of available dancers. Available dancers are added via another screen.

For reference here the current full code for the stage selection. Not that I have added the additional code to prevent selection of the same dancer on multiple stages to only a couple of the buttons.
Python:
screen dancer_pole:
    style_prefix "char"
    vbox:
        xpos 0.1
        ypos 0.1
        spacing 20

        text "{u}Dancer Stage Allocation:{/u}"
        if recruit_chiara != 0:
            hbox:
                spacing 10
                text "Chiara"
                imagebutton:
                    auto "cbutton_%s.png"
                    action SetVariable("pole1_dancer", "chiara")
                    if pole2_dancer == "chiara":
                        action SetVariable("pole2_dancer", "none")
                    if pole3_dancer == "chiara":
                        action SetVariable("pole2_dancer", "none")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "chiara")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "chiara")
        if recruit_felicity != 0:
            hbox:
                spacing 10
                text "Felicity"
                imagebutton:
                    auto "cbutton_%s.png"
                    action SetVariable("pole1_dancer", "felicity")
                    if pole2_dancer == "felicity":
                        action SetVariable("pole2_dancer", "none")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "felicity")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "felicity")
        if recruit_jool != 0:
            hbox:
                spacing 10
                text "Jool"
                imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "jool")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "jool")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "jool")
        if recruit_paige != 0:
            hbox:
                spacing 10
                text "Paige"
                imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "paige")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "paige")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "paige")
        if recruit_vera != 0:
            hbox:
                spacing 10
                text "Vera"
                imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "vera")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "vera")
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "vera")
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "none")
            imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "none")
            imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "none")

    if pole1_dancer != "none":
        grid 1 1:
            xpos 0.65
            ypos 0.35
            image "stage_m_[pole1_dancer]"

    if pole2_dancer != "none":
        grid 1 1:
            xpos 0.80
            ypos 0.75
            image "stage_s_[pole2_dancer]"

    if pole3_dancer != "none":
        grid 1 1:
            xpos 0.5
            ypos 0.75
            image "stage_s_[pole3_dancer]"

    grid 1 1:
        style "cbut"
        xpos 0.95
        ypos 0.9
        imagebutton auto "b_back_%s.webp" action [Hide("dancer_pole"),Jump("club_bar")]
    grid 1 1:
        style "cbut"
        xpos 0.05
        imagebutton auto "b_prev_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
    grid 1 1:
        style "cbut"
        xpos 0.95
        imagebutton auto "b_next_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
Here is what the current screen and the overall strip club looks like at present. Noting that the stage selection and club view already currently match.
stage.jpg club_stage.jpg

Just thinking about it a little, I could create a label that I could call from the imagebutton that would job of the following:
Python:
                    if pole1_dancer == "chiara":
                        action SetVariable("pole1_dancer", "none")
                    if pole2_dancer == "chiara":
                        action SetVariable("pole2_dancer", "none")
                    if pole3_dancer == "chiara":
                        action SetVariable("pole3_dancer", "none")
I could then change the imagebutton code to:
Code:
                imagebutton auto "cbutton_%s.png" action [Call"Fix(dancer)",SetVariable("pole2_dancer", "felicity")]
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,231
14,991
The issue as I mentioned above is not related to displaying the resultant imagebuttons as this is done well by the following.
Since there isn't positioning in your code, it can still be simplified :
Python:
       for pole in range( 1, 4 ): # will give 1, 2, 3
           if getattr( store, "pole{}_dancer".format( pole ) ) != "none":
                imagebutton:
                    auto ("{}_{}_{}_%s.webp".format( getattr( store, "pole{}_dancer".format(pole) ), pole, getattr( "pole{}_pos".format(pole) ) ) )
                    focus_mask True
                    action Jump("club_pole{}".format( pole) )
Warning, I perhaps messed with the parenthesis.


Really all I am trying to create is a mechanism to ensure that a scenario like does not occur:
pole1_dancer = "vera"
pole3_dancer = "vera"
Then use a set to store the girls already assigned to a pole, and only show girls that are both recruited and not already assigned to a pole :
Python:
default girlsAtPole = set()

screen dancer_pole:
[...]
        text "{u}Dancer Stage Allocation:{/u}"
        if recruit_chiara != 0 and not "chiara" in girlsAtPole :
            hbox:
                spacing 10
                text "Chiara"
                imagebutton:
                    auto "cbutton_%s.png"
                    action [ SetVariable("pole1_dancer", "chiara"), AddToSet( "girlsAtPole", "chiara" ) ]
                imagebutton auto "cbutton_%s.png" action [ SetVariable("pole2_dancer", "chiara" ), AddToSet( "girlsAtPole", "chiara" ) ]
                imagebutton auto "cbutton_%s.png" action [ SetVariable("pole3_dancer", "chiara" ), AddToSet( "girlsAtPole", "chiara" ) ]
[...]
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole1_dancer" ) ), SetVariable("pole1_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole2_dancer" ) ), SetVariable("pole2_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole3_dancer" ) ), SetVariable("pole3_dancer", "none") ]
[...]
And once again it can be simplified :
Python:
default girlsAtPole = set()
#  MUST be /define/ here, you don't want it to be saved. This will
# let you easily add more girls in the future if needed.
define allGirls = [ "chiara", "felicity", "jool", ...]

screen dancer_pole:
[...]
        text "{u}Dancer Stage Allocation:{/u}"
        for girl in allGirls:
            if getattr( store, "recruit_"+girl ) and not girl in girlsAtPole :
                hbox:
                    spacing 10
                    text ( "{}".format( girl.capitalize() ) )
                    imagebutton:
                        auto "cbutton_%s.png"
                        action [ SetVariable("pole1_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
                    imagebutton auto "cbutton_%s.png" action [ SetVariable("pole2_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
                    imagebutton auto "cbutton_%s.png" action [ SetVariable("pole3_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
[...]
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole1_dancer" ) ), SetVariable("pole1_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole2_dancer" ) ), SetVariable("pole2_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole3_dancer" ) ), SetVariable("pole3_dancer", "none") ]
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
Since there isn't positioning in your code, it can still be simplified :
Python:
       for pole in range( 1, 4 ): # will give 1, 2, 3
           if getattr( store, "pole{}_dancer".format( pole ) ) != "none":
                imagebutton:
                    auto ("{}_{}_{}_%s.webp".format( getattr( store, "pole{}_dancer".format(pole) ), pole, getattr( "pole{}_pos".format(pole) ) ) )
                    focus_mask True
                    action Jump("club_pole{}".format( pole) )
Warning, I perhaps messed with the parenthesis.
Pretty straight forward optimisation here. Even I can understand it. ;)


Then use a set to store the girls already assigned to a pole, and only show girls that are both recruited and not already assigned to a pole :
Python:
default girlsAtPole = set()

screen dancer_pole:
[...]
        text "{u}Dancer Stage Allocation:{/u}"
        if recruit_chiara != 0 and not "chiara" in girlsAtPole :
            hbox:
                spacing 10
                text "Chiara"
                imagebutton:
                    auto "cbutton_%s.png"
                    action [ SetVariable("pole1_dancer", "chiara"), AddToSet( "girlsAtPole", "chiara" ) ]
                imagebutton auto "cbutton_%s.png" action [ SetVariable("pole2_dancer", "chiara" ), AddToSet( "girlsAtPole", "chiara" ) ]
                imagebutton auto "cbutton_%s.png" action [ SetVariable("pole3_dancer", "chiara" ), AddToSet( "girlsAtPole", "chiara" ) ]
[...]
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole1_dancer" ) ), SetVariable("pole1_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole2_dancer" ) ), SetVariable("pole2_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole3_dancer" ) ), SetVariable("pole3_dancer", "none") ]
[...]
And once again it can be simplified :
Python:
default girlsAtPole = set()
#  MUST be /define/ here, you don't want it to be saved. This will
# let you easily add more girls in the future if needed.
define allGirls = [ "chiara", "felicity", "jool", ...]

screen dancer_pole:
[...]
        text "{u}Dancer Stage Allocation:{/u}"
        for girl in allGirls:
            if getattr( store, "recruit_"+girl ) and not girl in girlsAtPole :
                hbox:
                    spacing 10
                    text ( "{}".format( girl.capitalize() ) )
                    imagebutton:
                        auto "cbutton_%s.png"
                        action [ SetVariable("pole1_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
                    imagebutton auto "cbutton_%s.png" action [ SetVariable("pole2_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
                    imagebutton auto "cbutton_%s.png" action [ SetVariable("pole3_dancer", girl ), AddToSet( "girlsAtPole", girl ) ]
[...]
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole1_dancer" ) ), SetVariable("pole1_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole2_dancer" ) ), SetVariable("pole2_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", getattr( store, "pole3_dancer" ) ), SetVariable("pole3_dancer", "none") ]
I sort of understand where this is headed, however the issue is that it will make reassigning a dancer from one stage to another more difficult. Hence if you want to change a dancer from stage 1 to stage 2, you would have to assign someone else first to stage 1, before you have the option of selecting them for stage 2. Code is more efficient that what I was planning, however the functionality is slightly reduced.

The code does give me an idea of an alternative layout where I could have the list of available girls to the side of each of the stage images. This selection list would change dependent upon available girls as per your code. I will give it some thought.
 

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
Ended up going with the following code. Rather verbose, however it does exactly what I require. My thought to call a label from within an imagebutton action was not allowed by Ren'Py.
Python:
screen dancer_pole:
    style_prefix "dancer"
    vbox:
        xpos 0.1
        ypos 0.1

        text "{u}Dancer Stage Allocation:{/u}"
        hbox:
            spacing 20
            vbox:
                spacing 20
                if recruit_chiara != 0:
                    text "Chiara"
                if recruit_felicity != 0:
                    text "Felicity"
                text "Jool"
                if recruit_paige != 0:
                    text "Paige"
                if recruit_vera != 0:
                    text "Vera"
                text "None"
            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "chiara"), If(pole2_dancer == "chiara",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "chiara",SetVariable("pole3_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "felicity"), If(pole2_dancer == "felicity",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "felicity",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "jool"), If(pole2_dancer == "jool",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "jool",SetVariable("pole3_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "paige"), If(pole2_dancer == "paige",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "paige",SetVariable("pole3_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "vera"), If(pole2_dancer == "vera",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "vera",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "none")

            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "chiara"), If(pole1_dancer == "chiara",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "chiara",SetVariable("pole3_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "felicity"), If(pole1_dancer == "felicity",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "felicity",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "jool"), If(pole1_dancer == "jool",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "jool",SetVariable("pole3_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "paige"), If(pole1_dancer == "paige",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "paige",SetVariable("pole3_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "vera"), If(pole1_dancer == "vera",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "vera",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "none")

            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "chiara"), If(pole1_dancer == "chiara",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "chiara",SetVariable("pole2_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "felicity"), If(pole1_dancer == "felicity",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "felicity",SetVariable("pole2_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "jool"), If(pole1_dancer == "jool",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "jool",SetVariable("pole2_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "paige"), If(pole1_dancer == "paige",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "paige",SetVariable("pole2_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "vera"), If(pole1_dancer == "vera",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "vera",SetVariable("pole2_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "none")

    if pole1_dancer != "none":
        grid 1 1:
            xpos 0.65
            ypos 0.05
            xanchor 0.5
            image "stage_m_[pole1_dancer]"

    if pole2_dancer != "none":
        grid 1 1:
            xpos 0.5
            ypos 0.55
            xanchor 0.5
            image "stage_s_[pole2_dancer]"

    if pole3_dancer != "none":
        grid 1 1:
            xpos 0.80
            ypos 0.55
            xanchor 0.5
            image "stage_s_[pole3_dancer]"

    grid 1 1:
        style "cbut"
        xpos 0.95
        ypos 0.9
        imagebutton auto "b_back_%s.webp" action [Hide("dancer_pole"),Jump("club_bar")]
    grid 1 1:
        style "cbut"
        xpos 0.05
        imagebutton auto "b_prev_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
    grid 1 1:
        style "cbut"
        xpos 0.95
        imagebutton auto "b_next_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
This is what it looks like in game.
pole_select.jpg
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,561
2,180
I sort of understand where this is headed, however the issue is that it will make reassigning a dancer from one stage to another more difficult.
Still catching up on this thread, and I'm not sure I have much time to look today anyway.

My gut feel is that you are going to need to look into . I'm guessing a list to hold an array of girls that have been recruited and which stage they have been assigned to (if you retain that mechanic). If a girl is in the dictionary, she's been recruited and her name is used as the key. Then have the stage number (0, 1, 2, 3) as a value.

But a quick bit of clarification...
Does it matter which stage a girl is assigned to?

As far as I can see, you wouldn't specifically need to to assign a girl to a stage, but instead just say that a girl is available to dance and have code that limits the number of girls available to 3. Then let the game pick which stage the girl appears on. Unless each stage has significance within your game universe and therefore stage assignment is a necessary mechanic of the game.
 
  • Like
Reactions: Xavster

moskyx

Engaged Member
Jun 17, 2019
3,895
12,532
Ended up going with the following code. Rather verbose, however it does exactly what I require. My thought to call a label from within an imagebutton action was not allowed by Ren'Py.
Python:
screen dancer_pole:
    style_prefix "dancer"
    vbox:
        xpos 0.1
        ypos 0.1

        text "{u}Dancer Stage Allocation:{/u}"
        hbox:
            spacing 20
            vbox:
                spacing 20
                if recruit_chiara != 0:
                    text "Chiara"
                if recruit_felicity != 0:
                    text "Felicity"
                text "Jool"
                if recruit_paige != 0:
                    text "Paige"
                if recruit_vera != 0:
                    text "Vera"
                text "None"
            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "chiara"), If(pole2_dancer == "chiara",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "chiara",SetVariable("pole3_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "felicity"), If(pole2_dancer == "felicity",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "felicity",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "jool"), If(pole2_dancer == "jool",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "jool",SetVariable("pole3_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "paige"), If(pole2_dancer == "paige",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "paige",SetVariable("pole3_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole1_dancer", "vera"), If(pole2_dancer == "vera",SetVariable("pole2_dancer", "none")), If(pole3_dancer == "vera",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole1_dancer", "none")

            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "chiara"), If(pole1_dancer == "chiara",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "chiara",SetVariable("pole3_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "felicity"), If(pole1_dancer == "felicity",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "felicity",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "jool"), If(pole1_dancer == "jool",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "jool",SetVariable("pole3_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "paige"), If(pole1_dancer == "paige",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "paige",SetVariable("pole3_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole2_dancer", "vera"), If(pole1_dancer == "vera",SetVariable("pole1_dancer", "none")), If(pole3_dancer == "vera",SetVariable("pole3_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole2_dancer", "none")

            vbox:
                ypos 0.02
                spacing 33
                if recruit_chiara != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "chiara"), If(pole1_dancer == "chiara",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "chiara",SetVariable("pole2_dancer", "none"))]
                if recruit_felicity != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "felicity"), If(pole1_dancer == "felicity",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "felicity",SetVariable("pole2_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "jool"), If(pole1_dancer == "jool",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "jool",SetVariable("pole2_dancer", "none"))]
                if recruit_paige != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "paige"), If(pole1_dancer == "paige",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "paige",SetVariable("pole2_dancer", "none"))]
                if recruit_vera != 0:
                    imagebutton auto "cbutton_%s.png" action [SetVariable("pole3_dancer", "vera"), If(pole1_dancer == "vera",SetVariable("pole1_dancer", "none")), If(pole2_dancer == "vera",SetVariable("pole2_dancer", "none"))]
                imagebutton auto "cbutton_%s.png" action SetVariable("pole3_dancer", "none")

    if pole1_dancer != "none":
        grid 1 1:
            xpos 0.65
            ypos 0.05
            xanchor 0.5
            image "stage_m_[pole1_dancer]"

    if pole2_dancer != "none":
        grid 1 1:
            xpos 0.5
            ypos 0.55
            xanchor 0.5
            image "stage_s_[pole2_dancer]"

    if pole3_dancer != "none":
        grid 1 1:
            xpos 0.80
            ypos 0.55
            xanchor 0.5
            image "stage_s_[pole3_dancer]"

    grid 1 1:
        style "cbut"
        xpos 0.95
        ypos 0.9
        imagebutton auto "b_back_%s.webp" action [Hide("dancer_pole"),Jump("club_bar")]
    grid 1 1:
        style "cbut"
        xpos 0.05
        imagebutton auto "b_prev_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
    grid 1 1:
        style "cbut"
        xpos 0.95
        imagebutton auto "b_next_%s.webp" action [Hide("dancer_pole"),Show("dancer_main")]
This is what it looks like in game.
View attachment 849504
I'd only suggest you to make the buttons' text translatable by adding the magic _( ) thingy to your text statements:

Python:
screen dancer_pole:
    style_prefix "dancer"
    vbox:
        xpos 0.1
        ypos 0.1

        text _("{u}Dancer Stage Allocation:{/u}")
        hbox:
            spacing 20
            vbox:
                spacing 20
                if recruit_chiara != 0:
                    text _("Chiara")
                if recruit_felicity != 0:
                    text _("Felicity")
                text _("Jool")
                if recruit_paige != 0:
                    text _("Paige")
                if recruit_vera != 0:
                    text _("Vera")
                text _("None")
            vbox:
                ####(...)
That works for any other text, show text or textbutton you may have, as well as for character's names too (common nouns typically used as Professor, Unknown Woman or whatever should be translated and, although I usually don't translate names, those who use a different alphabet like cyrillic might need to addapt them too to their language). Almost no one does it (sadly for me) but if you're aiming for a "perfect" code this would be a nice addition to your game ;)
 
  • Like
Reactions: Xavster

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,231
14,991
Hence if you want to change a dancer from stage 1 to stage 2, you would have to assign someone else first to stage 1, before you have the option of selecting them for stage 2.
Absolutely not.

Look at the code, at no time the girls are assigned a position in girlsAtPole. Anyway sets are unordered, so it wouldn't be possible, but even if it was a list, it would change nothing.
It's not girlsAtPole that decide what stage they use, it's just a stack used to know if they are already on a stage. It could contain [ "Himalaya", "[girl at stage 2]", "girafe", "bamboo", "[girl at stage 1]", "pretty beautiful car", "[girls at stage 3]" ], that the code would be exactly the same and would still works exactly as your original code, with exactly the same functionality.
 
  • Like
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
But a quick bit of clarification...
Does it matter which stage a girl is assigned to?
I am trying to have the player being able to have full control over the position of the girls on stage. Hence they can put their preferred one on the main stage.

Absolutely not.

Look at the code, at no time the girls are assigned a position in girlsAtPole. Anyway sets are unordered, so it wouldn't be possible, but even if it was a list, it would change nothing.
It's not girlsAtPole that decide what stage they use, it's just a stack used to know if they are already on a stage. It could contain [ "Himalaya", "[girl at stage 2]", "girafe", "bamboo", "[girl at stage 1]", "pretty beautiful car", "[girls at stage 3]" ], that the code would be exactly the same and would still works exactly as your original code, with exactly the same functionality.
I think I understand the role of the girlsAtPole. Essentially it is a list of the girls that are no longer selectable to place on an open pole position. This has lesser functionality to my current code, as you are able to switch which pole a girl performs at by selecting the revised pole. If I was to use the list method, I would first have to either replace them or remove them from their existing pole, to make them available such that I could place them on the revised pole.

I realise the code I have arrived at is verbose, however it does exactly what I wish it to do.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,231
14,991
This has lesser functionality to my current code, as you are able to switch which pole a girl performs at by selecting the revised pole.
Then you just need to adapt a little the code for it to be more lax :
Python:
default girlsAtPole = set()
define allGirls = [ "chiara", "felicity", "jool", ...]

screen dancer_pole:
[...]
        text "{u}Dancer Stage Allocation:{/u}"
        for girl in allGirls:
            if getattr( store, "recruit_"+girl ):
                hbox:
                    spacing 10
                    text ( "{}".format( girl.capitalize() ) )
                    # This girl isn't assigned to a stage yet.
                    if not girl in girlsAtPole:
                        imagebutton:
                            auto "cbutton_%s.png" 
                            #  The girl actually on this stage is now free ; it will works even if there's no
                            # girls.
                            # Note: I was influenced by the condition when writing the previous version
                            # you don't need to use /getattr/ here, since the variable name is constant.
                            action [ RemoveFromSet( "girlsAtPole", pole1_dancer ),
                                        # The girl actually selected take the place.
                                        SetVariable("pole1_dancer", girl ),
                                        # And this girl is now on stage.
                                        AddToSet( "girlsAtPole", girl ) ]
                            # Ren'py buttons get their sensitivity from the first screen action in the list.
                            # If there's no girl a this stage, the button would be insensitive, so not
                            # clickable. Therefore, we are forcing it to be sensitive.
                            sensitive True
                        imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole2_dancer ), SetVariable("pole2_dancer", girl ), AddToSet( "girlsAtPole", girl ) ] sensitive True
                        imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole3_dancer ), SetVariable("pole3_dancer", girl ), AddToSet( "girlsAtPole", girl ) ] sensitive True
                    # This girl is already assigned to a stage.
                    else:
                        # Will be shown only if it's not already her stage.
                        showif pole1_dancer != girl:
                            imagebutton:
                                auto "cbutton_%s.png" 
                                # The girl actually at this stage is now free.
                                action [ RemoveFromSet( "girlsAtPole", pole1_dancer ), 
                                            # The girl is on a stage, else we wouldn't be here.
                                            # The girl isn't already on this stage, else the button wouldn't
                                            # be shown.
                                            # Therefore, if she's on 2, clear the stage 2, else she can only
                                            # be on the stage 3.
                                            If( pole2_dancer == girl, SetVariable( "pole2_dancer", "none" ), SetVariable( "pole3_dancer", "none" ) ), 
                                            # Then assign the girl to this stage.
                                            SetVariable("pole1_dancer", girl )  ]
                                            # No need to add her into /girlsAtPole/, she's already in it.
                            # Once again, force the button sensitivity.
                            sensitive True
                        showif pole2_dancer != girl:
                            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole2_dancer ), If( pole1_dancer == girl, SetVariable( "pole1_dancer", "none" ), SetVariable( "pole3_dancer", "none" ) ), SetVariable("pole2_dancer", girl ) ] sensitive True
                        showif pole3_dancer != girl:
                            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole3_dancer ), If( pole1_dancer == girl, SetVariable( "pole1_dancer", "none" ), SetVariable( "pole2_dancer", "none" ) ), SetVariable("pole3_dancer", girl ) ] sensitive True

[...]
        hbox:
            spacing 10
            text "None"
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole1_dancer ), SetVariable("pole1_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole2_dancer ), SetVariable("pole2_dancer", "none") ]
            imagebutton auto "cbutton_%s.png" action [ RemoveFromSet( "girlsAtPole", pole3_dancer ), SetVariable("pole3_dancer", "none") ]
It's to be validated, I'm taking a pause at works, so my mind is more focused on the code I was working on, but normally it's correct.
 
  • Like
Reactions: Xavster

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,561
2,180
Okay. I'll freely admit, this one has been bugging me...

... so I wrote a working test bench.

My mockup game works (within my limited scope).

It uses two arrays:
  • A list of possible girls ... available_girls
  • A dict of girls that have been recruited ... recruited_girls
The dict uses the girls name as it's key and has a single value from 0 to 3. 0 means the girl is not assigned to a stage. 1-3 is the stage assignment.

The "game" has the option to move girls from the "available" list to the "recruited" list. New girls are assigned to stage 0 (unassigned).

The "game" also has a stage assignment screen. If a girl is assigned to a stage that is already in use, the existing girl is moved to stage 0.

Then there's a screen which allows you to see the "club" and pick a girl to interact with (or not).

I am crap as RenPy screen: language, styling and generally all things aesthetic. I barely got something here. But the code behind the scenes works fine - which is I think what you needed. Based on your screenshots... I think you're already WAY past my skills in this area already - so I wouldn't expect you'd have much of a problem getting it to look pretty.

With the exception of my hideous recruitment menu, there is no limit to the number of girls this style of coding would work with. 1 girl. 99 girls - it's all the same to me. Only the "assign girls" UI would present issues.

The "game", even with my images is only 31 MB. Have a play, maybe it's where you were heading.
Download :

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

Edit: It's probably worth me highlighting that the only parts that actual matter to your question are the two arrays and the show screen club(girl1, girl2, girl3) logic and associated screen. Everything else is just there in support of those bits. In fairness, there's very little there that I didn't already mention in earlier comments... except now in a working RenPy game to test in a practical manner.

The "recruited girls" array would look something like:
"paige", 0
"felicity", 3
"vera", 1

... depending on who you'd moved to the active roster and which stages you'd assigned them to.
 
Last edited:
  • Love
Reactions: Xavster

Xavster

Well-Known Member
Game Developer
Mar 27, 2018
1,243
7,560
Okay. I'll freely admit, this one has been bugging me...

... so I wrote a working test bench.

My mockup game works (within my limited scope).

It uses two arrays:
  • A list of possible girls ... available_girls
  • A dict of girls that have been recruited ... recruited_girls
The dict uses the girls name as it's key and has a single value from 0 to 3. 0 means the girl is not assigned to a stage. 1-3 is the stage assignment.

The "game" has the option to move girls from the "available" list to the "recruited" list. New girls are assigned to stage 0 (unassigned).

The "game" also has a stage assignment screen. If a girl is assigned to a stage that is already in use, the existing girl is moved to stage 0.

Then there's a screen which allows you to see the "club" and pick a girl to interact with (or not).

I am crap as RenPy screen: language, styling and generally all things aesthetic. I barely got something here. But the code behind the scenes works fine - which is I think what you needed. Based on your screenshots... I think you're already WAY past my skills in this area already - so I wouldn't expect you'd have much of a problem getting it to look pretty.

With the exception of my hideous recruitment menu, there is no limit to the number of girls this style of coding would work with. 1 girl. 99 girls - it's all the same to me. Only the "assign girls" UI would present issues.

The "game", even with my images is only 31 MB. Have a play, maybe it's where you were heading.
Download :

You don't have permission to view the spoiler content. Log in or register now.
Truly going above and beyond the call of duty.

The little trial game appears to have the functionality required, utilising lists rather than the approach I have taken. However I'm pretty sure I have the edge in terms of interface. One thing my version does is show which stage the girl has been allocated to, on the selection screen. This is done both by the status of the button and also via the graphics to the right.

You will have to have a go at Callisto after I release the next version. ;)

PS: As coding isn't really my strong point, I tend to use simpler coding alternatives that may be a little more verbose. However as I understand this code well, I can bug fix easier. I am still trying to improve my skills though, hence the queries I have raised.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,561
2,180
I tend to use simpler coding alternatives that may be a little more verbose.
Hmmm. I think that's the first time someone's ever implied my solution is more complex than Anne's. I must be in a parallel universe. :giggle:

Whilst my code is shorter, it's not really doing anything "clever". I really don't do complex code. I wonder if you're perhaps just more familiar with your existing code and already taking it's complexity for granted. Prior to writing this example, I was only vaguely aware of lists, tuplets, sets or dictionaries. Everything written for this example was learned today by reading the for arrays and making assumptions based on other programming languages I learned many, many years ago.

Regardless... whatever works... works. No benefit to laboring my point if you've got a solution that you're happy with.