Ren'Py Accessing screen variables externally

Pr0GamerJohnny

Conversation Conqueror
Sep 7, 2022
6,729
10,073
I'm trying to find a way to access an existing screen's variables without modifying that screen; this is for purposes of a mod where I want everything to be contained in an add-on rpy file without having to overwrite the original game.

From what I read in the renpy documentation, the "use" command allows one screen to include another - but is there a way to read/write its variables without having them originally set as local variables in the original screen's parameter list?

For example, if the existing game has:

Python:
screen baseGameScreen:
  
    default turnedOn = False

    if turnedOn == False:
        #doTheseThings
    else:
        #doThatThing
  
    #other
    #stuff

I then want my external screen contained in my mod file to be able to access "turnedOn", in a manner like:
Python:
int 200 python:
    config.overlay_screens.append("myModScreen")
  
  
screen myModScreen:
    use baseGameScreen
  
    if turnedOn == False
        key "g" action SetScreenVariable(turnedOn, True)
    elif turnedOn == True
        key "g" action SetScreenVariable(turnedOn, False)

As it's written now, the original screen's variables arent defined in the scope of mod screen, and using SetLocalVariable also doesn't seem to work, so is there another way to access those vars without modifying the script file containing "baseGameScreen"?
 

bindr

Member
Game Developer
Sep 27, 2018
108
975
Screen variables live only inside the screen and can be modified via `SetScreenVariable`. You can pass them *in*, but not *out*, as parameters.
 

Pr0GamerJohnny

Conversation Conqueror
Sep 7, 2022
6,729
10,073
Screen variables live only inside the screen and can be modified via `SetScreenVariable`. You can pass them *in*, but not *out*, as parameters.
Right, but to be declared as parameter one would need to define that in the original screen statement, not the secondary screen "using" that original screen, correct? So that doesn't really give the ability to pass in without modification unless I'm misunderstanding.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
[...] is there a way to read/write its variables without having them originally set as local variables in the original screen's parameter list?
No. Local variables are... well, not global.

But why does it matter ? Local variables are only relevant when the screen is displayed, and are reset to their default value when the screen is show/called.

Take your example:
Python:
screen baseGameScreen:
 
    default turnedOn = False

    if turnedOn == False:
        #doTheseThings
    else:
        #doThatThing
 
    #other
    #stuff
Since the content will depend on the local variable "turnedOn", the screen already include a way to change its value. Why do you want to add another one on top of it ?
And if in fact the value is computed, then it's the computation that you've to target, not the local variable.


I then want my external screen contained in my mod file to be able to access "turnedOn", in a manner like:
Python:
int 200 python:
    config.overlay_screens.append("myModScreen")
 
 
screen myModScreen:
    use baseGameScreen
 
    if turnedOn == False
        key "g" action SetScreenVariable(turnedOn, True)
    elif turnedOn == True
        key "g" action SetScreenVariable(turnedOn, False)
  1. To access the variable, your screen need to be used by the screen defining it, not the opposite ;
  2. As "myModScreen" is defined, "baseGameScreen" would be always shown on screen ;
  3. SetScreenVariable expect a variable name, not its value ;
  4. ToggleScreenVariable should have been used in place of SetScreenVariable ;
  5. The '()' in the screen declaration aren't here as nice decoration.


so is there another way to access those vars without modifying the script file containing "baseGameScreen"?
Well...

zzzMyModWillBeLoadedAfterTheOriginalGameCode.rpy:
Python:
screen baseGameScreen():
 
    default turnedOn = False

    keys "K_g" action ToggleScreenVariable( "turnedOn" )

    if turnedOn == False:
        #doTheseThings
    else:
        #doThatThing
 
    #other
    #stuff

There's another way, that don't even need to redefine "baseGameScreen", so is more forward compatible. But like you haven't even used SetScreenVariable correctly, I'm pretty sure that telling you how to address Ren'Py's core variable to change the screen associated to a given name is absolutely not a good idea.
 

Pr0GamerJohnny

Conversation Conqueror
Sep 7, 2022
6,729
10,073
No. Local variables are... well, not global.

But why does it matter ? Local variables are only relevant when the screen is displayed, and are reset to their default value when the screen is show/called.

Take your example:


Since the content will depend on the local variable "turnedOn", the screen already include a way to change its value. Why do you want to add another one on top of it ?
And if in fact the value is computed, then it's the computation that you've to target, not the local variable.




  1. To access the variable, your screen need to be used by the screen defining it, not the opposite ;
  2. As "myModScreen" is defined, "baseGameScreen" would be always shown on screen ;
  3. SetScreenVariable expect a variable name, not its value ;
  4. ToggleScreenVariable should have been used in place of SetScreenVariable ;
  5. The '()' in the screen declaration aren't here as nice decoration.




Well...

zzzMyModWillBeLoadedAfterTheOriginalGameCode.rpy:
Python:
screen baseGameScreen():

    default turnedOn = False

    keys "K_g" action ToggleScreenVariable( "turnedOn" )

    if turnedOn == False:
        #doTheseThings
    else:
        #doThatThing

    #other
    #stuff

There's another way, that don't even need to redefine "baseGameScreen", so is more forward compatible. But like you haven't even used SetScreenVariable correctly, I'm pretty sure that telling you how to address Ren'Py's core variable to change the screen associated to a given name is absolutely not a good idea.
That's an odd tone to end on given your otherwise helpful post, I'm not sure whether to thank you or to recommend you as a stackoverflow contributor...:rolleyes:

  1. To access the variable, your screen need to be used by the screen defining it, not the opposite
  2. As "myModScreen" is defined, "baseGameScreen" would be always shown on screen ;
  3. SetScreenVariable expect a variable name, not its value ;
  4. ToggleScreenVariable should have been used in place of SetScreenVariable ;
  5. The '()' in the screen declaration aren't here as nice decoration.
Re 1: No shit, this is precisely why I'm asking.
Re 3: A side effect of renpy documentation not directly listing required parameter variable types.
Re 4: Perhaps for this specific example, but this was merely an example, I'm asking about the more general theory.
Re 5: I don't understand what you're trying to say here. Adding parameters to my screen won't do anything if the original function doesnt take parameters. Unless you're suggesting overwriting the original screen with a later-loading screen which DOES take parameters, then that's clear.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
Re 1: No shit, this is precisely why I'm asking.
Since you believed that using the screen from your own could possibly works, yeah, "no shit", you clearly needed to be precised that local scopes do not works backward.


Re 3: A side effect of renpy documentation not directly listing required parameter variable types.
Really ?

: "Causes the variable called name associated with the current screen to be set to value."
It's not "the name variable" it's the variable called the value of name.

But anyway there's no mystery here. In all languages, variableName in a parameter/argument list will always mean "the value hosted by the variable". If you want to in fact point to the variable itself, you'll always have to pass it as reference. Either through a pointer ($ prefix (as doubled one) in Perl, @ prefix in C and others, etc), or its name as a string (Python).


Re 5: I don't understand what you're trying to say here.
" "
 

Pr0GamerJohnny

Conversation Conqueror
Sep 7, 2022
6,729
10,073
: "Causes the variable called name associated with the current screen to be set to value."
It's not "the name variable" it's the variable called the value of name.
+2 pts for answering that without using the word "string".

That's all I wanted in the original documentation.

Proper document would say things like:

ReturnType FunctionName(string screenName, var screenVarTypeValue)


The confusion wasn't over what a local scope was. It was that the original documentation has ambiguous wording as to which direction the control goes.

Causes the variable called name to be set to value in the current local context.

This function is only useful in a screen that has been used by another screen, as it provides a way of setting the value of a variable inside the used screen. In all other cases, should be preferred, as it allows more of the screen to be cached
Confusion could easily be avoided by terming the screens screen 1 and screen 2. This isnt a criticism of you, but rather your defense of shoddy documentation.

" "
This did indeed catch my attention the first time I read it on the documentation - and is half the motivation for my original question; as it certainly sounds like one could exploit that for what I'm trying to do, though that's still fairly ambigious.

"Any name used in that screen can be redefined when the screen is shown." What name? A screen name? A screenVariable name? Redefined outside the local scope? If not, then why the caution if it's all contained locally...
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
"Any name used in that screen can be redefined when the screen is shown." What name?
Any one. It can be a variable name, like it can be a function name.



If not, then why the caution if it's all contained locally...
Because not everything is contained locally, but anyway the reason not lie there.


In a short and simplified version:

Screens are regularly refreshed, from whenever an interaction starts to every 0.015 second (and possibly a bit lower). But in the same time, the values possibly displayed can be updated. It's by example the case for an User Interface that would display the love points. The said love points are updated in the game, and the screen must show the right value.
This mean that each time a screen is displayed, Ren'Py have to recompute everything, what need time. Not necessarily a lot of time, but still time. This while the recomputation isn't always necessary. By example add "image/whatever.jpg" will always lead to the same result.

To deal with this, Ren'Py have the notion of "pure renpy" code. It's code that will always lead to the same result, as long as the parameters do not change. Like by example:
Python:
def addition( a, b ):
    return a + b
addition( 1, 1) will always return "2".

With this, Ren'Py can rely on a kind of cache system. If it's "pure renpy" code and if the parameters haven't changed, then I don't need to compute I can use what I previously used.
For pure computation, it's a bit useless since they are nearly atomic. But when it's, by example, a computation that have to be turned into a displayable text, it starts to need significant time. And it's where "pure renpy" code take all their meaning, since this time is not wasted if the result of the previous computation can be used.

With the second version of the screen language (the one actually used by everyone), this notion have been extended to screens themselves. If the screen's parameters are the same, then everything relying on them will be the same and I can use a cached version of them.
But for this to apply, the said screen need to have parameters... even if it's an empty list of parameters.
 
  • Like
Reactions: Pr0GamerJohnny