Renpy - variable does not save if called from a screen

SpinDardan

New Member
Feb 6, 2022
12
12
I have the follow code

Code:
default testVar = 0
label start:   
    "1"
    show screen testButton
    #$increase() <--------- HERE IT SAVE!
    "2"
    return

screen testButton:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
        action increase # IF CALL FROM <--------- HERE NOT SAVE

init python:
    def increase():
        global testVar
        testVar += 1
Every function that I call from a screen, if the user saves the game and loads again, the variable is not saved.
If i call from a label it save...

Could anyone help me with this?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,131
14,813
Every function that I call from a screen, if the user saves the game and loads again, the variable is not saved.
What is totally normal and the expected behavior.

, while screens are unfinished interactions. Therefore, what is saved here will always be the game state before the player decide to click on the button. It's only when the game will pass to the next interaction, that the variable change will be took into account for the save.



By the way...

Code:
label start: 
    "1"
    show screen testButton
    #$increase() <--------- HERE IT SAVE!
    "2"
    return
In such context, should have been used.


Code:
screen testButton:
All screens , even if it's an empty one.


Code:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
There's a screen statement, and it have a property.
Python:
    textbutton "{size=20}{color=#fff}x[testVar]{/color}{/size}":
        background "poorHomeFoodIcon"
        [...]

Code:
        action increase # IF CALL FROM <--------- HERE NOT SAVE
There's a screen action for this.
action SetVariable( "testVar", testVar + 1 )


Code:
init python:

    def increase():
        global testVar
        testVar += 1
global serve a double, complementary, purpose. Declaring the variable in the scope, and importing it in this scope.
But, as long as the function, class and method, are declared in a RPY file, and therefore in an init python, game variables will be global to this scope by default.
The only issue that will happen is when assigning a value to a variable, and to a variable only. For object attributes, list or dict elements, there's no issues. But this can be solved easily since . Therefore, it suffice to address the variable through it instead of directly:
Python:
init python:

    def increase():
        store.testVar += 1
Be noted that this apply only for assignation. For everything else, as I said, the variable is already global. Therefore
Python:
init python:

    def whatever():
        myObject.someAttribute = 1
        myList[42] = 1337
        myList.append( 3 )
        myDict["42"] = myVar
all this works without problems.
 

SpinDardan

New Member
Feb 6, 2022
12
12
Thanks for the answer.
I can't use call screen because I use more than one screen on my screen with

$renpy.show_screen("Screen1", _layer="master")
$renpy.show_screen("Screen2", _layer="master")

action SetVariable( "testVar", testVar + 1 ) <--- this i cannot use because i'm running a function wich save more than one variable.

store.varName does not work too.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,131
14,813
I can't use call screen because I use more than one screen on my screen with

$renpy.show_screen("Screen1", _layer="master")
$renpy.show_screen("Screen2", _layer="master")
Firstly, one don't prevent the other. Secondly exist.


action SetVariable( "testVar", testVar + 1 ) <--- this i cannot use because i'm running a function wich save more than one variable.
Well, screen actions can be chained...
It's said somewhere in the doc, but strangely I don't really feel like searching for it...
By the way, the screen action exist for a good reason.


store.varName does not work too.
Why do I feel like you totally missed the point, that was the very first line on my post ?
And also, incidentally, didn't understood the following explanations...
 

SpinDardan

New Member
Feb 6, 2022
12
12
Oh sorry i was wrong about .store, its to not use global key, ok.
The problem with the call screen is,
how do i put some buttons in the game that will be visible on all labels? For example, spend an hour, spend a day, etc...
if i use "call screen" it freeze the game until i use Return() or finish the interaction, with "show screen" they keep appearing while the game runs.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,131
14,813
The problem with the call screen is,
how do i put some buttons in the game that will be visible on all labels? For example, spend an hour, spend a day, etc...
I said that "in such context, call screen should have been used". But obviously when the context change, and an UI isn't the same context than a demonstration code, the claim not necessarily stay true...
 

SpinDardan

New Member
Feb 6, 2022
12
12
I said that "in such context, call screen should have been used". But obviously when the context change, and an UI isn't the same context than a demonstration code, the claim not necessarily stay true...
And how is this type of interaction done?
A simple click in a simple button to save some variables inside a function
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,131
14,813
And how is this type of interaction done?
A simple click in a simple button to save some variables inside a function
*sigh*

If there's something that you don't understand, just ask about it instead of faking to master the topic.

So, I start, again...

Saves happen at the starts of an interaction. Whatever will be saved is the values as they are at the start of this interaction.
And it happen that screens are (to be seen as) unfinished interactions. It apply to called screens, since the interaction will end only when the screen will return. But this also apply to shown screens, since they are fully outside of the interaction process.
Therefore, players will always loose the benefit from what is done during the interaction where they decide to save. It's the explained behavior (link in my first post), the expected behavior, and the effective behavior that every single game made with Ren'Py is facing. It's how it is, period.
With time, players learned to adapt, and if they don't it's not a big deal because creators are expected to take count of this and design their game in such way that the loose, if it happen, is limited to the strict minimum ; what is a single and unique action.

After, there's of course way to deal with this when you need to have a screen that would handle more than one action. It suffice to generate interactions. And how to do this is strongly hinted in the link I provided in the first place.


I'm zen, it's the end of summer, the sky is blue, the sun is shining, no__name fucking rage quited, what the fuck man... No, I said I'm zen, everything is nice, full off peace, and I'm the gentler guy one can interact with...
 
  • Haha
Reactions: LightmanP

SpinDardan

New Member
Feb 6, 2022
12
12
"just ask about it instead of faking to master the topic."
Did you even read what I typed?
I'm not pretending to master the subject because I don't know, I just asked a simple question that you didn't answer...
If you have a button on a screen, how do you save the variables for that function? Do you need to return to the label? Do I need to call a save method?
How do the devs mask this interaction so it doesn't look like there is one?
An example, you have a button that passes 1 hour in time, how is this done?
If you don't know either, you don't need to respond rudely.
 

SpinDardan

New Member
Feb 6, 2022
12
12
A simpler example, if I call the Jump function of a button action, save it and load it, it returns to the previous label. How do games do this? Some games allow you to change screens, save and continue on the screen you saved without going through any dialogue.
 

SpinDardan

New Member
Feb 6, 2022
12
12
For those who are experiencing the same problem, the solution I found was as follows:
Create a new label that will call the function that saves the variables
Code:
label saveDelegate:
    $yourSaveFunction()
    $renpy.retain_after_load()
    return
Use $renpy.retain_after_load() to retain the variables.
To return from where the show screen was you need to call your label in the action with ui.callsinnewcontext, so when the "saveDelegate" finishes it will return to the previous screen.
action ui.callsinnewcontext("saveDelegate")

The same you can use to jump to another label with a action button.

I don't know if this is the best way to do this, but this is what worked.

Solved
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,559
2,175
I can't use call screen because I use more than one screen on my screen with

$renpy.show_screen("Screen1", _layer="master")
$renpy.show_screen("Screen2", _layer="master")

That would be quite unusual and I wonder if perhaps there's another misunderstanding going on here.

"Usually" ... show screen is used to put a screen onto the display and just leave it there. The usual use case would be something like a status bar.

Where you want the player to interact with the game to trigger an action, you would use call screen.

We've seen people make the mistake of using show screen and $ renpy.pause(hard=True) - which doesn't actually work how they think it does (The hard pause doesn't STOP the game awaiting interaction, and depending on what the player is doing, very unexpected things can happen). I don't think that's happening with you... but it is worth a mention, just in case.

We've also seen people who don't put all their interactions within a single screen and end up feeling they need to use show screen multiple times - because that's the only way they can see to do it. Sometimes it has been as bad a one imagebutton per screen. I don't think you're doing anything quite that bad, but I do wonder if you're doing something like that.

One way to solve it is by using . Where a single master screen can incorporate one or more other screens. The RenPy main menus use this, so things like the "Save" and "Options" screens only need to be defined once, but used in multiple places. Though as I've strongly hinted at earlier, it's much better for newer RenPy developers to put all your interactive buttons and such on to a single screen and use call screen. If not all buttons are visible all the time, there's always .

Another way (not ideal), is to use something like:

Python:
    show screen myscreen1
    call screen myscreen2

Where you use show screen to show all but the "last" screen. The use call screen to show the final one (and by doing so, cause the script to wait for a player interaction - though only the actions that are part of the final screen will cause the script to move on. Hence my "not ideal" comment.

store.varName does not work too.

That would usually how you'd reference a variable within a function. Though you've already mentioned that it does work later.

But using your example, I'd expect to see something like...

Python:
default testVar = 0

init python:
    def increase():
        store.testVar += 1

screen testButton:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
        action Function("increase")

label start:
    "1"
    call screen testButton
    "2 (save here)"
    return

You seem adverse to using , since you need to affect multiple variables. Although that is simple enough...

Python:
screen testButton:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
        action [SetVariable(testVar1, testVar1 + 1), SetVariable(testVar2, testVar2 + 1), SetVariable(testVar3, testVar3 + 1)]

Multiple actions can be chained together by including them in a list ( [ ] ).

Of course, this is very simplistic - and you may need the actual Function to do more complex calculations.

A quick note: I'd don't currently have access to RenPy. So all the above code is untested and purely based on my experience and access to the RenPy manual pages. It's a "I think it works", rather than "I know it works".
 

SpinDardan

New Member
Feb 6, 2022
12
12
That would be quite unusual and I wonder if perhaps there's another misunderstanding going on here.

"Usually" ... show screen is used to put a screen onto the display and just leave it there. The usual use case would be something like a status bar.

Where you want the player to interact with the game to trigger an action, you would use call screen.

We've seen people make the mistake of using show screen and $ renpy.pause(hard=True) - which doesn't actually work how they think it does (The hard pause doesn't STOP the game awaiting interaction, and depending on what the player is doing, very unexpected things can happen). I don't think that's happening with you... but it is worth a mention, just in case.

We've also seen people who don't put all their interactions within a single screen and end up feeling they need to use show screen multiple times - because that's the only way they can see to do it. Sometimes it has been as bad a one imagebutton per screen. I don't think you're doing anything quite that bad, but I do wonder if you're doing something like that.

One way to solve it is by using . Where a single master screen can incorporate one or more other screens. The RenPy main menus use this, so things like the "Save" and "Options" screens only need to be defined once, but used in multiple places. Though as I've strongly hinted at earlier, it's much better for newer RenPy developers to put all your interactive buttons and such on to a single screen and use call screen. If not all buttons are visible all the time, there's always .

Another way (not ideal), is to use something like:

Python:
    show screen myscreen1
    call screen myscreen2

Where you use show screen to show all but the "last" screen. The use call screen to show the final one (and by doing so, cause the script to wait for a player interaction - though only the actions that are part of the final screen will cause the script to move on. Hence my "not ideal" comment.




That would usually how you'd reference a variable within a function. Though you've already mentioned that it does work later.

But using your example, I'd expect to see something like...

Python:
default testVar = 0

init python:
    def increase():
        store.testVar += 1

screen testButton:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
        action Function("increase")

label start:
    "1"
    call screen testButton
    "2 (save here)"
    return

You seem adverse to using , since you need to affect multiple variables. Although that is simple enough...

Python:
screen testButton:
    button:
        add "poorHomeFoodIcon"
        pos (0.48,0.45)
        text "{color=#fff}x[testVar]{/color}" size 20:
            yoffset -18
            xoffset +18
        action [SetVariable(testVar1, testVar1 + 1), SetVariable(testVar2, testVar2 + 1), SetVariable(testVar3, testVar3 + 1)]

Multiple actions can be chained together by including them in a list ( [ ] ).

Of course, this is very simplistic - and you may need the actual Function to do more complex calculations.

A quick note: I'd don't currently have access to RenPy. So all the above code is untested and purely based on my experience and access to the RenPy manual pages. It's a "I think it works", rather than "I know it works".
wow thanks, i will test the use and transclude, the call screen really works when you use for example a Jump("scene1") the show screen not work when you save and load, maybe mutiple call screen with use and transclude can help, i will try it