Ren'Py Modding question: Screen overriding?

Viressa

Well-Known Member
May 24, 2018
1,522
3,208
So, in my modding, I've been overriding labels using config.label_overrides, so I can have all of my modded labels in a separate RPY file without having to replace the game's existing files. Much cleaner to work this way.

I've been looking for a similar functionality for replacing screens with my own modded versions and have yet to find anything in the documentation. Does it exist?

(Apologies if this is the wrong subforum)
 
  • Like
Reactions: Greasy Handcock

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
I've been looking for a similar functionality for replacing screens with my own modded versions and have yet to find anything in the documentation. Does it exist?
The answer to your effective question is : No, there's no function to do this.


Now, the answer to the implied question is different. Yes, there's a way to override a screen, but it's a hack, so there's no guaranty that it can works on all futures versions of Ren'py and you need to use it with caution to not break the game.

The screens are stored into the renpy.display.screen.screens dictionary, with keys being a tuple formed with the name of the screen and the expected screen variant ; in 99% of the time, you'll just have to use the default screen variant, so None.

By example, the object representing the screen used for the console is stored here :
Code:
renpy.display.screen.screens[( "_console", None)]
So, to replace it with your "moddedConsole" screen, you need to do :
Code:
renpy.display.screen.screens[( "_console", None)]= renpy.display.screen.screens[( "moddedConsole", None)]
But as I said, it's to use with caution since there isn't watchdogs to prevent you messing with the game. It's you who need to create these watchdogs and ensure that you do things right.
Also note that this manipulation is context related. So, if you do it in the console as test, it shouldn't works since you aren't in the game context. The change should be reverted once you quit the console, and so fallback to the game context. But, obviously, the change is propagated from the current context to any derivative one.
 

Evilko

Member
Apr 11, 2017
120
232
So in my mod i needed to override default navigation screen and i used tip suggested at renpy forum - "stick it in as an init block ":
Python:
init 1:
    screen navigation():
        [whatever]
and it worked well. Worked with other screens too.

anne O'nymous is there something wrong with this solution or is it ok?
 
Last edited:
  • Like
Reactions: JohnDupont

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
anne O'nymous is there something wrong with this solution or it is ok?
There's both something and nothing wrong. Technically, screens aren't part of an init block, but as a side effect, they benefit from it. But like it's a "side effect", there's no guaranty that it will works like this forever.
A better approach is to use the :
Code:
init offset = 1

screen navigation():
    [whatever]
The effect will be exactly the same, because the init offset = 1 is implicit when you write init 1:. But it will be the way it's expected to be done, so there's (almost) no risk that it change in the future.

Another way is to define the screen in a rpy file that will be proceeded after the file defining the original screen.
Unlike labels, screens aren't protected against rewriting. So, like files are proceeded by alphabetical order, when you have something like :
[myOwnScreens.rpy]
Code:
screen myUI:
   [...]
[zmyOwnScreens.rpy]
Code:
screen myUI:
   [...]
It's the second screen that will be used. No need to define an init offset for this.


This said, and I know that it's more a question of rhetoric than anything else, this method don't permit to override the screen. It just redefine it.
That's why I haven't talked about it in my previous answer. It works at init time, so before the main menu is displayed, while the other method works anytime, even after the game started. Which, among other things, permit to change the screen only after a given time, and/or revert to the original screen. Things that are impossible with the method you talk about here.

It don't mean that one method is better than the other. What method use depend of what you effectively intend to do with your mod and the screen. If it's a definitive change, then this approach is easier. If at the opposite you're more like me and prefer to offer the choice to the player, then the method I explained previously is the only that can do it.
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,565
9,081
The answer to your effective question is : No, there's no function to do this.


Now, the answer to the implied question is different. Yes, there's a way to override a screen, but it's a hack, so there's no guaranty that it can works on all futures versions of Ren'py and you need to use it with caution to not break the game.

The screens are stored into the renpy.display.screen.screens dictionary, with keys being a tuple formed with the name of the screen and the expected screen variant ; in 99% of the time, you'll just have to use the default screen variant, so None.

By example, the object representing the screen used for the console is stored here :
Code:
renpy.display.screen.screens[( "_console", None)]
So, to replace it with your "moddedConsole" screen, you need to do :
Code:
renpy.display.screen.screens[( "_console", None)]= renpy.display.screen.screens[( "moddedConsole", None)]
But as I said, it's to use with caution since there isn't watchdogs to prevent you messing with the game. It's you who need to create these watchdogs and ensure that you do things right.
Also note that this manipulation is context related. So, if you do it in the console as test, it shouldn't works since you aren't in the game context. The change should be reverted once you quit the console, and so fallback to the game context. But, obviously, the change is propagated from the current context to any derivative one.
Another possibility might be creating your own screen variant. Something like
Python:
init python:
    config.variants.insert(0, "modded_screen")

screen whatever():
    variant "modded_screen"
    ...
Then your screen will only show if you have "modded_screen" in config.variants and you still can access the original screen if needed. The only downside I see is that configuration variables shouldn't be changed outside of init blocks. So changing it after the game started is not guaranteed to work. But it seemed to be working with in-game console if I don't touch any pre-existing variants
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,978
16,234
Late on this, because I didn't had the time to test it before.


Another possibility might be creating your own screen variant.
[...]
Then your screen will only show if you have "modded_screen" in config.variants and you still can access the original screen if needed.
Haven't really had the time to do deep tests, but I don't see why it wouldn't works in more exceptional configurations than what I tried.


On the positive side, it's something really easy to use, and it don't rely on a hack what make it sustainable. A little annoying if you want to more flexibility. By default it's all or none. If you want to offer to the player the possibility to choose which screens are replaced, and which aren't, you'll have to create a variant for each screen. But it's annoying more than penalizing.

On the negative side, there's something that is possible with the hack, but not with this approach, it's screen insertion:
Code:
screen UID():
    [The original screen from the game]

screen UID_modded():
    use UID_original
    [whatever addition you want]
screen UID_original():
    pass
With the hack, you can make "UID_modded" replace "UID", while "UID" will replace "UID_original". This way you replace the screen, while still benefiting from it.
It's how I achieve to add my own information in most screens in my mod for SuperPowered. This without having to copy the original screen, nor to be constantly tracking what screen is displayed. When asked to display a screen, Ren'Py will display mine, that will display the original then add my own stuff on top of it.

But if you don't need to go this far, your approach is probably easiest.


The only downside I see is that configuration variables shouldn't be changed outside of init blocks.
It's more that a part of the configuration variables become meaningless once the init phase is done, but the others can perfectly be changed. And config.variants is part of the ones that can be changed ; it will be used each time Ren'Py is asked to display a screen.
 
  • Like
Reactions: crabsinthekitchen