Ren'Py Relationship stats screen not updating

Fancy Dan

New Member
Aug 2, 2018
13
4
Hey all, like many of you, I made a game where relationships happen and relationship points are the key to progressing. I'm having trouble with the screen that's supposed to display these points kind of lagging I guess?

Here's the situation:

Every time there's an update in relationship points, it doesn't show up on the stats screen until I load it twice.

I.e. the protagonist gains +1 relationship point with character1. I open up the stats screen, it says the relationship with character1 is 0. I close the screen, open it up again and now it's 1.
If I first load a game where the relationship with character1 is 5, then decide to start a new game, I open the stats screen and it says the relationship is at 5. I close the screen, open it again and now it's 0, as it should be.

Here's the code for the screen (I took the idea from a game where the stats menu works):
Python:
screen stats():

    tag menu

    use game_menu(_("Characters"), scroll="viewport"):

        style_prefix "characters"

        hbox:
            style_prefix "stats"

            text "{color=#ffffff}Character1 Trust: [character1_trust] {/color}"
            text "{color=#ffeb3b}Character2 Trust: [character2_trust] {/color}"
            text "{color=#ff8f00}Character3 Trust: [character3_trust] {/color}"
This is how I defined the stat values at the beginning:
Python:
init:
    $ character1_trust = 0
    $ character2_trust = 0
    $ character3_trust = 0
And this is what I use during the game to change them:
Python:
$ character1_trust += 1
One more piece of possibly helpful information. I made a quick test screen that's visible throughout the game:
Python:
screen say(who, what):
    style_prefix "say"

    frame:
        xalign 1.0 yalign 0
        xminimum 300
        yminimum 55
        ymaximum 55

        text "[character1_trust]\n[character2_trust]\n[character3_trust]"
The values changed instantly here, but still didn't in the stats menu until I loaded it the second time.

It feels like there's some sort of loading priority or check moment that I'm missing, but I'm still too much of a beginner. I'd greatly appreciate any help.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
I don't have an answer yet, but whatever it is, is linked to the use game_menu.

An almost identical screen without embedding this screen within game_menu updates the values as expected.

Python:
screen stats():

    fixed:

        style_prefix "stats"

        vbox:
            label "Character1 Trust: [character1_trust]"
            label "Character2 Trust: [character2_trust]"
            label "Character3 Trust: [character3_trust]"

            textbutton "Return":
                action Return()

Only mentioning it in the hope it triggers someone else's memory as to what's really going on or that you spot something in the original code which explains why it works and this doesn't.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Some more experimenting, and I'm equally confused.

If I add the same fields to the existing screen about(), then those values are always kept up to date.

If however, I add a textbutton "Stats" action ShowMenu("stats") to the screen navigation(), then the values are not kept up to date.

That said... if you show the stats this way, return to the game and show the stats again... the 2nd time, they show the correct values. Since "About" and "Stats" are invoked in the same way from the same navigation screen - I'm struggling to see what the difference is.

Meanwhile, I'm supposed to be doing something else... hopefully someone will figure out this... I've gotta try to stay on task.
 

AnimeKing314

Giant Perv
Game Developer
Jun 28, 2018
395
605
Some more experimenting, and I'm equally confused.

If I add the same fields to the existing screen about(), then those values are always kept up to date.

If however, I add a textbutton "Stats" action ShowMenu("stats") to the screen navigation(), then the values are not kept up to date.

That said... if you show the stats this way, return to the game and show the stats again... the 2nd time, they show the correct values. Since "About" and "Stats" are invoked in the same way from the same navigation screen - I'm struggling to see what the difference is.

Meanwhile, I'm supposed to be doing something else... hopefully someone will figure out this... I've gotta try to stay on task.
Can't be entirely sure but it might be because the values aren't persistent (which for these types of stats they shouldn't be). I've had a few times where putting non-persistent stats into a a screen tagged as a menu or using game_menu caused them to not behave properly. This is probably just because these menus are accessed outside of the context of any single playthrough. That's also why your initial solution would work because then the values are only being accessed from within the playthrough that the player is on.

Also a bit of a minor detail here for Fancy Dan, you should probably define the variables like this:
Python:
default character1_trust = 0
default character2_trust = 0
default character3_trust = 0
instead of using an init block.
 

Fancy Dan

New Member
Aug 2, 2018
13
4
Sure, I can define them with "default", though that doesn't fix the issue. I saw others doing it like this and just went with it.

I repeated your tests and here's what I found:
Indeed, taking out the "game_menu" function makes it update correctly.
However, when I inserted the code into the About menu, it still wasn't updating.
It's gotta be something to do with the game_menu then.

Thanks for all your help so far :) I hope I didn't through you too far off track.
Hopefully someone else has an idea and I can also use this info to ask around in other places.

Edit: I still need as part of the game menu somehow. Making it separate introduces a whole slew of formatting and concept issues.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Sure, I can define them with "default", though that doesn't fix the issue. I saw others doing it like this and just went with it.

I'd already tested default as the first thing I did. And yeah, it doesn't help.

default is the better choice for creating variables, btw.

Variables created using init: are not saved as part of the save file unless the value is changed by the code at some point after label start:. Whereas default variables are all saved, regardless. There are also some indirect improvements to how you need to code if you are adding new variables in staggered releases (v1, v2, v3, chapter 1, chapter 2, chapter 3, etc).

default was a later addition to RenPy to solve some of the common issues caused by using init: instead.

tl;dr version: default is the newer, better, simpler way of initializing variables. Use default any time you aren't using define. :p
 

Fancy Dan

New Member
Aug 2, 2018
13
4
Cool! Thanks :) Using default now. Would you say it's better to use default for DynamicCharacters who's names change during the game too? I'm using define for them atm.

So as not to veer off topic, considering the stats being updated, the renpy discord isn't helping much either.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
I just awaken (Ah, the delights to works from home when you can code until the end of the night without realizing it :( ), so it's to take with caution :


I don't have an answer yet, but whatever it is, is linked to the use game_menu.
[...]
Only mentioning it in the hope it triggers someone else's memory [...]
Wrote like it is, it make the stats be (kind of) "parts of" the game_menu screen ; the use statement is here defined as a block that will contain the stats.
Screen variables have their own scope, that depend of the screen. What led to the addition of the SetScreenVariable screen action by example. Therefore, it's probably a question of scope, the stats variables not being updated yet in the scope they are used.

I remember that I had the same kind of problem when working on my mod for Super Powered. How I solved the problem is more blurry, I just remember that I past a long time on it. But since it was a mod, I had constraints that don't apply here, so it's probable that just using use as a pure statement and keeping the stats outside of it should works :
Code:
screen stats():

    tag menu

    use game_menu(_("Characters"), scroll="viewport")

    style_prefix "characters"

    hbox:
        style_prefix "stats"

        text "{color=#ffffff}Character1 Trust: [character1_trust] {/color}"
        text "{color=#ffeb3b}Character2 Trust: [character2_trust] {/color}"
        text "{color=#ff8f00}Character3 Trust: [character3_trust] {/color}"

Some more experimenting, and I'm equally confused.

If I add the same fields to the existing screen about(), then those values are always kept up to date.

If however, I add a textbutton "Stats" action ShowMenu("stats") to the screen navigation(), then the values are not kept up to date.
Here it's clearly a question of context. ShowMenu open the screen in the lowest context, while Show open it on the actual context. Therefore the displayed values can be "one interaction late" if you open the screen too fast. They can also some times be totally unrelated if the variable were defined directly in a label, but it's rare.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Would you say it's better to use default for DynamicCharacters who's names change during the game too?

You don't need to use DynamicCharacter these days either.

Just have something like..

Python:
default lu_name = "Unnamed"

define lu = Character("[lu_name]", color="#04B486", what_prefix='"', what_suffix='"')

# later....

    $ lu_name = renpy.input("What is her name? {i}(Press ENTER for 'Lucy'){/i}")
    $ lu_name = lu_name.strip() or "Lucy"

    # or however you want to handle renaming characters.

It's okay to use define, as the character's text value will never change... it will always be "[lu_name]". But that text will be populated with the variable lu_name due to standard RenPy string substitution.
 

Fancy Dan

New Member
Aug 2, 2018
13
4
You don't need to use DynamicCharacter these days either.
Thanks for that! Seems to make it simpler than playing around with persistent values for replays.

I remember that I had the same kind of problem when working on my mod for Super Powered.
Nice to hear someone had the same problem :) If you could somehow remember how you fixed it, that would help a ton!
Check the first post to find the code I used and a detailed description of the issue.

Code:
textbutton _("Characters") action ShowMenu("stats")
This is what I'm using to open the screen and it has to be used, or everything becomes a mess :D

I just need to figure out how to have the menu update the stats to their current value the first time it's opened up.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
If you could somehow remember how you fixed it, that would help a ton!
Like I said, it more than surely wouldn't help in your case.
It was a mod. Not only I had to follow the structure of screens that I can't change, but I also hadn't much freedom in how I can insert my own parts on them. Therefore, even if I remembered how I solved the problem, it would just over complicated your own screen.

Starts by trying what I said (using use as a pure statement and not a block). And eventually putting it (the use line) at the end of the screen.


Code:
ShowMenu
has to be used, or everything becomes a mess :D
Hmm, what you mean by "it becomes a mess" ?

The main, and almost only, difference between Show and ShowMenu is the context change ; using ShowMenu tell Ren'py that the screen it will open is to see as not being part of the game, but that's all.
Therefore, not only Show have no reason to turn everything into mess, but also you shouldn't use ShowMenu, because the screen you display is part of the game.
 

Fancy Dan

New Member
Aug 2, 2018
13
4
Hmm, what you mean by "it becomes a mess" ?
I'm trying to integrate the stats screen to be part of the game menu. So, when the player right clicks or presses ESC and they have Save, Load, Preferences etc, they also see the Characters menu that behaves in the same way.

Using Show instead of ShowMenu makes the screen appear on top of the rest of the menus and simple right clicking doesn't close it, I can't click the other menus etc. Even with a lot of formatting, it would feel unnatural while playing.


Starts by trying what I said (using use as a pure statement and not a block). And eventually putting it (the use line) at the end of the screen.
Okay! This does work to the point where it updates the stats right away and most of the menu functionality is there, except I need to format it to fit into the space where the rest of the game menu screens are. If there's nothing better, at least that'll be a lot easier than what I was doing before :) Thanks man!
 

AnimeKing314

Giant Perv
Game Developer
Jun 28, 2018
395
605
I'm trying to integrate the stats screen to be part of the game menu. So, when the player right clicks or presses ESC and they have Save, Load, Preferences etc, they also see the Characters menu that behaves in the same way.

Using Show instead of ShowMenu makes the screen appear on top of the rest of the menus and simple right clicking doesn't close it, I can't click the other menus etc. Even with a lot of formatting, it would feel unnatural while playing.



Okay! This does work to the point where it updates the stats right away and most of the menu functionality is there, except I need to format it to fit into the space where the rest of the game menu screens are. If there's nothing better, at least that'll be a lot easier than what I was doing before :) Thanks man!
If you make it part of the game menu then it won't use the values specific to the playthrough that the player is on (or at least it may cause issues with this) because the game menu is accessible outside of the playthrough. So if the player has multiple saves then either it won't know which one to use the values for or will default to a specific one (either the first save or the latest save, not sure which) and so the values wouldn't be correct if the player decides to switch to a different save. Because of this it's better to create a button in game that calls the stats screen (usually people create their own menu at the top or side of the screen but there are any number of ways you could do this including adding it to the different textbuttons that appear below the textbox).
 

Fancy Dan

New Member
Aug 2, 2018
13
4
I made it so that the button isn't visible unless the player has actually loaded or started a new game. It doesn't appear in the main menu, just the game menu.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
I'm trying to integrate the stats screen to be part of the game menu.
Can I ask why ? I know that it's a trend recently, but I totally miss the interest behind this.

From my point of view, the stats are a part of the game, not of the engine. It's a pure informative static screen, totally dependent of the current play at this exact instant. This by opposition to all the other screens available from the menu, that present you an action, and are totally independent of the play and moment you use them.
Therefore their place should be inside the game, not inside the engine menu. But, as I said, it's just my point of view.


So, when the player right clicks or presses ESC and they have Save, Load, Preferences etc, they also see the Characters menu that behaves in the same way.
When you try to do something more complicated, you always should ask yourself if the pain worth the gain. So, what it bring to the player, more than having the stats available when they click on a "stats button" directly available through the User Interface ?
From my point of view, having to right click, then to select the "stats" menu, will always be worse than having to just click on a "stats button". So how is it making the game look better to integrate it in the navigation menu ?


Using Show instead of ShowMenu makes the screen appear on top of the rest of the menus
Not with the tag menu you use. Unless the version of Ren'py you use broke the tag property, its whole purpose is to replace the screen having the same tag by the screen you just opened. Therefore even with Show there's absolutely no reason for the stats screen to appear on top of the rest of the menu ; once again unless you use a version of Ren'py that broke the tag.


and simple right clicking doesn't close it,
Just ask it nicely to Ren'py :
Code:
screen stats():
    key "dismiss" action Hide( "stats" )
    [...]

I can't click the other menus etc.
Replace the use of "main_menu" by the use of "navigation" and it should works.


This being said, all this wouldn't effectively solve your problem, since the link to access the stats is only available from the game menu, and therefore the player is already outside of the game context when he can click on it.
It was more to show you that, no, using Show in place of ShowMenu don't necessarily mess with everything.


[...] except I need to format it to fit into the space where the rest of the game menu screens are.
It's a wild guess, but this is probably near to the solution :
Code:
screen stats:
    tag menu

    use game_menu(_("Characters"), scroll="viewport")
         
    frame:
        style "game_menu_content_frame"

        hbox:
            style_prefix "stats"

            text "{color=#ffffff}Character1 Trust: [character1_trust] {/color}"
            text "{color=#ffeb3b}Character2 Trust: [character2_trust] {/color}"
            text "{color=#ff8f00}Character3 Trust: [character3_trust] {/color}"
 
Apr 24, 2020
192
257
There's a roundabout solution that I've used to these types of issues before.

Use two variables for the same information. One of them being persistent and the other being initiated by default. Whenever you would apply changes to one of them you need to apply the changes to both, so some sort of function that does that would be needed.

Whenever you start or load a new game you then need to use the after load label to set the persistent variables to be equal to the saved variables to keep them in sync.

Then it's just a matter of using the persistent variables for the menu screens.
 

Fancy Dan

New Member
Aug 2, 2018
13
4
It's a wild guess, but this is probably near to the solution :
Thanks for all your help, this actually gives me a lot to work with! I'll experiment and see which solution works best.

I probably should've mentioned that the stats menu is also accessed via the quick menu at the bottom of the screen. Much like the rest of the menus. Like you said, it's just an opinion. To me it makes the game seem more natural, I guess?

Either way, your first solution of not using game_menu as a code block is already really close and this is even better. I'll just add in whatever is necessary.

P. S. Never tried linking key functions to actions. That's good to know :) Yea, I'm still a huge beginner when it comes to coding.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Thanks for that! Seems to make it simpler than playing around with persistent values for replays.

Sadly, one is not linked to the other.

People tend to use persistent variables for replays, because the values of variables can not be predicted during a replay.

Imagine that a replay is effectively you starting a new game, but immediately jumping ahead into the middle of game. No variables have been set, no saved game has been loaded. The variables could be practically anything, since they've never been explicitly set.

Normally, variables are stored in the store space. If you create a variable called room_height, what you are really doing is creating a variable store.room_height. persistent is just another storage space. store can (and often is) dumped into the save files. Whereas persistent is dumped to a common file used by your game, independent of saves.

So yeah. If you are thinking of replay... then maybe you need to keep using persistence.

Though that only changes my example to this instead:

Python:
default persistent.lu_name = "Unnamed"

define lu = Character("[persistent.lu_name]", color="#04B486", what_prefix='"', what_suffix='"')

# later....

    $ persistent.lu_name = renpy.input("What is her name? {i}(Press ENTER for 'Lucy'){/i}")
    $ persistent.lu_name = persistent.lu_name.strip() or "Lucy"

    # or however you want to handle renaming characters.

Very little changes, you just stick persistent. in front of everything.

As for the other stuff... I dunno. I can see why you might want to put a stats screen into the game menu. But then I'd probably have a custom stats screen completely independent and have a button as part of the UI which invokes the stats screen. Especially since that way would work and I could stop having to figure out why it's not working the other way.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Okay... an actual solution.

Apparently the problem may be due to .
Whilst and both include a nopredict parameter, does not.

There's something called , but I couldn't get it to work. So I'm assuming isn't not linked to ShowMenu().
Either that, or I'm completely misunderstanding what nopredict does.

The workaround is to change which string substitution method is used.
Instead of using square brackets for RenPy's string substitution, instead use % for python's native version.

So it would look like this...

Python:
screen stats():

    style_prefix "stats"

    fixed:

        vbox:
            label "Character1 Trust: %s" % character1_trust
            label "Character2 Trust: %s" % character2_trust
            label "Character3 Trust: %s" % character3_trust

            textbutton "Return":
                action Return()

%s is "string".
For more formatting options, see:


For "magic" reasons, screen prediction isn't able to predict it this way... and so you see the actual values rather than a cached copy of them.
 
Last edited:

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
There's something called , but I couldn't get it to work. So I'm assuming isn't not linked to ShowMenu().
It's more simple, yet not this intuitive, than that. It's just an addition to the prediction feature ; Ren'py itself use it only once, to predict the "main_menu" screen before the game start.
When you know that a screen will not be predicted, but will be (possibly) displayed soon, you use renpy.start_predict_screen() to force the prediction of the screen. Then, when the screen have now no chance to be displayed, you use renpy.stop_predict_screen() to let Ren'py return to its natural behavior.

It's implied in the Screen Language Optimization page, more precisely the :
If screens are shown from Python, it's a good idea to start predicting the screen before it is shown. To start predicting a screen, use the renpy.start_predict_screen() function. To stop predicting a screen, use the renpy.stop_predict_screen() function.
And a little more explicitly said in the changelog, if you (it's from the version 6.18):
A pair of functions, renpy.start_predict_screen() and renpy.stop_predict_screen() allows for manual prediction of time images that will be used by screens, including parameterized screens.

For "magic" reasons, screen prediction isn't able to predict it this way...
It's just that the screen prediction only care about the images. But like for the effective use of renpy.stop_predict_screen(), it's not really obvious to understand it by reading the documentation.

There's this part of the page
Ren'Py will run a screen multiple times, as it deems necessary. It runs a screen as part of the image prediction process, before the screen is first shown.
And that one in the of the Screen Language Optimization page
Screens perform better when they're predicted in advance. That's because Ren'Py will execute the screen during prediction time, and load in images that are used by the screen.
Both explicitly address the images as being predicted, but not explicitly limit the prediction to them, letting the reader face to a blurry understanding.