Ren'Py Question about RenPy save compatibility

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
I've been looking at one of the VN's from a spelling/grammar point of view, trying to clean up some of the more egregious spelling mistakes and such for a game released in chapters. In the hope that I can eventually get the list of corrections to the author somehow in a form they can use.

But in the process I've also started to look at the underlying .rpy files and have started straying into actual coding errors territory.

I don't have a RenPy programming background, but have done some coding experience in other languages. I started to look at some of the more obvious bugs, then caught myself when I realised I don't actually know the implications of fixing RenPy code.

The most obvious one to me is how RenPy keeps track of where it is up to within a save file. If I add a few lines of code, then any sense of line numbering goes out of the window. The RenPy wiki talks about saving the actual statement, but then what happens if the statement being changed/fixed happens to be the same statement that the save file is trying to sync to.

Then there's the whole mess of variables (I assume variable states are saved in the save file). Doesn't the episodic nature of previous releases mean that variables being set/absent/renamed in ancient save files could effectively break the "current" release.

And that's long before I start to worry about potential improvements to get around things like Patreon's morality clampdown. Changing/adding variables for Mother/Landlady Sister/Roommate, etc.

I'm going to go digging and experimenting anyway, but while I do that - I thought it might be useful to ask here too.

Chances are, I just accept "I don't know" as being where my level of skill is currently at. Then forget any ideas of trying to start something I clearly don't understand. But still... it's nice to plan ahead...

So if anyone has any cautionary tales or recommended reading... please shout up.
Links to topics here on the forums are especially welcome.
 
  • Like
Reactions: Palanto

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,565
7,378
I've been looking at one of the VN's from a spelling/grammar point of view, trying to clean up some of the more egregious spelling mistakes and such for a game released in chapters.
What? Grammar or spelling mistakes in a game? Say it ain't so!!!! LOL

Good luck with your mods.

In answer to some of your questions:

Ren'py can handle lines of code being inserted in and around save points, within limits. Essentially, Ren'py will roll back until it finds a place it can confidently restart. Sometimes, what this will do is take you back to the most recent label that was executed. Among other things, this is why you see the "call screen xyzzy from someStringHere" construct - Ren'py likes to insert the "from" clauses because it helps it figure out where to go back to if you're in the middle of a "call screen" when you save. Obviously, it is possible to make modifications that will keep Ren'py from being successful. But minor modifications - particularly adjustments to the text - can usually be tolerated.

Of course, if you make modifications to the code and the save point is past there, the save won't include the logic changes (if any) that you introduced.

Adding a variable in a later release isn't a problem at all. You just use the "default" keyword in defining the variable. When the newer Ren'py game restores a save, if the variable is defined in the save, it gets the saved value. If it isn't defined, it gets the value specified in the default statement. This is a core feature, designed specifically to help the "game released in chapters" issue.

There also is an "after_load" special label - if this label is present, Ren'py will call it after loading a save but before proceeding with it, which gives you a place where you can manipulate the variables, if you need to, before the game actually resumes. This potentially allows you to set up your new variables to an appropriate state based on where the player was in the game, assuming you can figure that out from the previous state of the variables.

Fiddling with attributes of classes (as opposed to "top level variables) is slightly trickier, since the class attributes get populated as part of the unpickling, and you don't have "default" support. Again, the "after_load" area provides you a potential opportunity to get values into new class attributes before they're used in the game.

You probably don't want to remove variables or attributes, since this probably would cause Bad Things To Happen when a save that contains them is restored. If you're somehow replacing them, just leave the definitions idle, and old values from saves will be restored, but just won't be used.

Anything declared using the "define" keyword is not part of the save - it's considered part of the code. Ditto anything that's created in an "init python" block. The only things that end up in the save are variables declared with "default", or variables that are modified after the game is started. So you can make all the logic changes you want to classes, functions, etc., as long as they'll work with the previous variables, or you clean up the variables in "after_load."

Hope that clarifies things somewhat.
 
  • Like
Reactions: Domiek and Palanto

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
What? Grammar or spelling mistakes in a game? Say it ain't so!!!! LOL
I know... it's a quaint idea that will never catch on. Still I can live in my little alternate reality for a while.

In answer to some of your questions:

[...]
Much appreciated.

Of course, if you make modifications to the code and the save point is past there, the save won't include the logic changes (if any) that you introduced.
As I said in my original post, after the spelling purge, the next phase will be actual bug fixes. I'd sort of got an idea in my head how loading beyond a change I'd made could potentially have to be considered - and it seems to line up with what you've said... cheers.

Adding a variable in a later release isn't a problem at all. You just use the "default" keyword in defining the variable. When the newer Ren'py game restores a save, if the variable is defined in the save, it gets the saved value. If it isn't defined, it gets the value specified in the default statement. This is a core feature, designed specifically to help the "game released in chapters" issue.
Yeah, at the moment I'm just thinking of moving variable around so they are defined at the beginning of chapters that use them, which isn't the case right now. Or at least, it's hugely inconsistent. Again, it's nice to have it confirmed that I was mainly on the right track.

There also is an "after_load" special label - if this label is present, Ren'py will call it after loading a save but before proceeding with it, which gives you a place where you can manipulate the variables, if you need to, before the game actually resumes.[...]
Ohhhhh.... new shinys.... Good catch. I can't imagine me needing it yet, but knowing it exists is half the battle.

Hope that clarifies things somewhat.
More than somewhat.
Cheers.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,565
7,378
Yeah, at the moment I'm just thinking of moving variable around so they are defined at the beginning of chapters that use them, which isn't the case right now. Or at least, it's hugely inconsistent. Again, it's nice to have it confirmed that I was mainly on the right track.
If variables are defined using "default", it doesn't really matter where the definition line is located, since Ren'py processes all the "default" before any of the actual game logic is executed.

One of the other paradigms I've seen for variable definition is "set it at first use." That is, you might see:
Code:
label episode_3:
    $ ep3_var1 = False
    $ ep3_var2 = 12345
    ...
    "Now we begin episode 3"
In this case, the variables aren't created using "define", they're just set at some point in the game ahead of where they need to be tested. This works fine as well, as long as there isn't a path that gets around the variable setup. Saves from before this point won't include these variables (since Ren'py hadn't encountered them yet) and saves afterwards will.

I think this is an older Ren'py paradigm, from before "default" came along. This is the kind of logic where moving variable setup (unless you use "default") could get you in trouble unless you're very, very careful, since now you'd have to worry about saves created before the original variable definition and after where you've moved it to.

(Personally, from an organizational point of view, I favor either the "do all the defaults at the beginning of each episode file" or else "put all the variable definitions into a single 'variables.rpy' file", but your style-age may vary...
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
So if I'm following this logic correctly:

  • default -- can be defined anywhere. The whole source is scanned at startup for "defaults" and those values are used before any other logic happens. Then the program starts normally or a load is done and any variable within the save file override the default values.
  • define -- strictly local variables. They aren't saved.
  • $ varx = value -- inline set "normal" variables. Saved to save files, but unlike "default", only exist once the program logic is executed to define them is encountered.
So I see 2 potential pitfalls... please correct me if I'm misunderstanding...

If I used "define", let's say at the start of chapter 3. I'm half way through chapter 3 and I save and quit till the following day. No code changes, just a straight up "save, quit and resume later". I return and load my save file. My load point is beyond the define and so isn't in the save file. Wouldn't any code that checks my "defined" variable beyond the save result in an error? I'm struggling to see how define would ever not break games that allow saving.

If, as part of my programming of Chapter 6, I decide to add a "$ varx = value" to Chapter 2. But I've already released Chapter 5, so there are potentially saves out there without my variable in it. I do a load of Chapter 5 to start playing the new Chapter 6 code and since that variable didn't exist when my save was done, it breaks things? ... I guess this was why "default" exists.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,565
7,378
So if I'm following this logic correctly:

  • default -- can be defined anywhere. The whole source is scanned at startup for "defaults" and those values are used before any other logic happens. Then the program starts normally or a load is done and any variable within the save file override the default values.
Pretty much correct. Phrased slightly differently: First, when you begin a new game, all variables created using "default" are assigned their default variables. Second, when you restore a save, if a variable created using "default" does not have a value in the save, then it is assigned the default. If it already has a value in the save, then it retains that value.

But absolutely correct that defaults can be declared anywhere, and will be picked up and processed regardless of where they seem to be in the program flow. General "best practice" is to set them up at the top of files, outside of any game flow, however.

  • define -- strictly local variables. They aren't saved.
The term "local" has a specific meaning to programmers. Instead, think of then as "constants". They shouldn't be altered during the game (bad things may happen). But you're correct that they aren't saved.

Just like "default", Renpy scans for all the "define" statements ahead of time.

  • $ varx = value -- inline set "normal" variables. Saved to save files, but unlike "default", only exist once the program logic is executed to define them is encountered.
Yes, exactly corect.

So I see 2 potential pitfalls... please correct me if I'm misunderstanding...

If I used "define", let's say at the start of chapter 3. I'm half way through chapter 3 and I save and quit till the following day. No code changes, just a straight up "save, quit and resume later". I return and load my save file. My load point is beyond the define and so isn't in the save file. Wouldn't any code that checks my "defined" variable beyond the save result in an error? I'm struggling to see how define would ever not break games that allow saving.
When Renpy parses your files, it collects all the "define" and "default" statements, and handles them specially. "define" statements are run during an "init phase," which happens before any game logic is executed. Thus, by the time your save is restored (or before a new game is started), all the "defines" have been set up, regardless of where they occur in the actual .rpy files. So, unlike,
Code:
   $varx = value
"defines" are NOT part of the program flow.

If, as part of my programming of Chapter 6, I decide to add a "$ varx = value" to Chapter 2. But I've already released Chapter 5, so there are potentially saves out there without my variable in it. I do a load of Chapter 5 to start playing the new Chapter 6 code and since that variable didn't exist when my save was done, it breaks things? ... I guess this was why "default" exists.
This part is correct. And you're right - this is exactly why "default" was introduced.

There is a potential work-around, however. Renpy implements an "after_load" label. If you have an .rpy file with an "after_load" label in it, Ren'py will call that label after it has restored all the variables in a save, but before it begins to execute. So you can do something like:
Code:
label after_load:
    python:
        try:
            variable_that_might_not_be_defined
        except NameError:
            variable_that_might_not_be_defined = new_default_value
    return
What this Python block does is test whether the variable has been defined. Using a variable that hasn't been defined throws a NameError, which you can catch and use to fix.

Clearly ugly, right? Much, MUCH simpler to just declare all your saveable variables with "default" and let Renpy handle it.

One of the things that confuses new Renpy users is that Renpy has two distinct phases of operation - the "init" phase and then the "game play" phase. Anything that's explicitly inside an "init" or "init python" block runs in the "init" phase. That's reasonably obvious. But "declare" statements are processed as if they were inside an "init" block even if they're not written that way. Similarly, things like transform declarations, image statements, screen language blocks, etc., are all handled during the init phase, not based on where they are in the game flow.

All that being said, it is best practice not to mix "init things" with "game flow things." Thus, it is a lot clearer to do things like:
Code:
# First, write all the init-phase stuff
default ch3_var1 = 1
default ch3_var2 = 2

image bouncing_ball:
    "ball.png"
    ...animation stuff

transform jump_up_and_down:
    ...ATL language

# Now put my game logic stuff in
label chapter3:
    main_character "Welcome to Chapter 3"
    ...
than it is to mix the two willy-nilly.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Again, much appreciated @Rich

I can see SO much wrong with the code I'm looking at just based on your few examples here.
My OCD is having OCD. :closedtongue:

Still... one step at a time... spelling first. Coding "Soon™"
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Here's another quick one for you then...

Can you suggest a game (or games) that are "good examples" of RenPy games written well?
I know people have different styles and personal preferences, but something you can point to and go... that's a good example.

Obviously it doesn't have to be a good game. But something complex and well planned, I suppose.

I figure it's the sort of question you either go "well, ofc... THAT one" or have to scratch you head. Don't worry about it if it's a head scratcher.
 

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,565
7,378
I don't have OCD, I have CDO. It's in alphabetical order. Much better. LOL

If you want to look at something complex, there's Summertime Saga. It's very complex. :) They've been through two complete internal rewrites (I think), moving from "tangle of code" toward something substantially more structured. That being said, it may be that it's SO complex it's hard to emulate or learn from. From the VN side of the house, Dating My Daughter seems reasonably organized - not necessarily 100% the way I'd do it, but at least they seem to have followed some reasonable conventions.

Personally, I think it's more about organization. Adopt conventions. Keep your file sizes from getting gargantuan. Name your files logically. And your variables. Use directory structures where it makes sense. The idea is that if you need to modify something, you should know right were it should be located.

Also, the way you organize the code for a "wander the world" game like Summertime Saga may be very different from how you organize a VN-style game like, say, Dating My Daughter. In a VN-style game, I would typically:
  • Have one file that has all the text parts in it for a particular episode. (Or, possibly more than one if there's a LOT of text or a lot of branches.)
  • Declare the new variables that are introduced in that episode at the top of the episode file, probably prefixed with a name like "ep1_" or "ep22_" so that I know where it lives. Either that, or centralize all the variables in one file.
  • Put comments in so that you remember what the variables mean, and what their values are. (Good naming for the variable itself helps, but if you have a 3-way or 4-way branch - rather than just a true/false - to keep track of, you want the meaning of the values remembered.) You WILL forget what you did 3-4 months ago. So either document it, or make sure it's incredibly obvious from the code.
For a "wander the world" type game:
  • I tend to code these as state machines. Each story line has its own state variable indicating where it is in its progression. Or possibly something more complex, if required. Summertime Saga has a VERY complex state machine system, but what they've done is move a lot of the "if you do this, what happens next" into tables and object definitions, as opposed to lots of logic scattered around. (Helpful, because sometimes you can do "I need to talk to X" from multiple places, so this way you don't have to repeat the logic.)
Other conventions I use:
  • Put screens and such in their own files. I tend to use a one-file-per-screen approach. In particular, when I customize one of the built-in Renpy screens, I remove its source from "screens.rpy" and put it in its own file so that it's nice and separated. That way, I know that anything that's still in "screen.rpy" hasn't been altered from what Renpy generated when I created the game. (Also means that it's unlikely Renpy will overwrite anything I've customized if I have to regenerate part of the UI after a Renpy version update.)
  • Declare any styles used by a screen in the same file as the screen, with a style prefix that matches the screen name. Makes it less likely that things in two different parts of the game will step on one another.
  • DRY! (Don't Repeat Yourself) If I need to do the same thing from multiple places, create a single "callable" item (screen, text sequence, function, whatever) and call it from multiple places. That way, if I have to fix something, it gets fixed everywhere, as opposed to "fixed it here but forgot to fix it there."
A lot of this derives from the conventions I've accumulated over my (long) career as a programmer. Some things are kind of universal, I guess.

But my conventions may not work for you - they ARE personal, after all. It's much more important to HAVE a set of conventions than to worry about whether mine are better than yours are better than his.

OK, time to put the soapbox away. Hope at least some of that long rant helped...
 
  • Like
Reactions: Greasy Handcock

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
OK, time to put the soapbox away. Hope at least some of that long rant helped...
I does. It did.
My long programming career has mainly been COBOL.
Less a set of accepted conventions and more a series of happy accidents that people stuck with eventually.
 

Domiek

In a Scent
Donor
Game Developer
Jun 19, 2018
1,988
10,142
@79flavors are you modding someone else's game simply because you like the game and are interested in making it better, or were you asked to do so?

I'm asking because I'm more than interested in having a couple of competent "beta testers" for my game that would play it a few days before each version release, if that's something that interests you.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
@79flavors are you modding someone else's game simply because you like the game and are interested in making it better, or were you asked to do so?
It started out as a "this is a game I'm enjoying that I think needs some spelling fixing".
It wasn't something I was asked to do, and I may be completely wasting my time.

I did a pass checking spelling/grammar and wrote up a list of fixes as a PDF and PM'd those to the developer (no reply as yet).
Then I realised I could UnRen the whole thing and make the "fixes" directly to the source .rpy files.
Then I started looking at some of the obvious bugs, with an eye to perhaps correcting them too.
I had a bit of a wander into Photoshop and a mess with Daz3D...

Then I feel down the rabbit hole of "good coding conventions" and things started to get out of hand :)

Right now, there's no plan for a modded version by me - except as a learning exercise purely for myself.
... it's just a "I'm enjoying messing with this right now".
The intent is to pass everything back to the original developer, if I can figure out a way of getting in contact.

I'm asking because I'm more than interested in having a couple of competent "beta testers" for my game that would play it a few days before each version release, if that's something that interests you.
I think my quick answer is "I'm not sure yet".
I hadn't played a single RenPy VN until 3 weeks ago. Now I'm knee deep in the source code of a couple. Another few weeks, and I could have decided to throw in the towel.
Let me get back to you.
 

Domiek

In a Scent
Donor
Game Developer
Jun 19, 2018
1,988
10,142
@79flavors Wow you've really jumped straight into the deep end within just 3 weeks huh? :p

My offer was to play a few days before release and just take note of bugs you may run in to, rather than diving into the source code. No matter what you decide, it's all good!

Also, if you have any questions about Daz feel free to PM me. I may be of some help.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
About the save thing, Ren'py don't save the file/line_number couple, but the position in the of the current label and its content/type.
So, Ren'py will be lost only if you removed the label when the save happened, or if the save happened in the statement number 5 and there's now only 4 statements ; so if there's now less statements than the save position in the AST.

Let's take this code :
Code:
label myLabel:
    "blablabla"
    $ myVar += 1
    "bliblibli"
    return
and imagine you save at the "bliblibli" line. Whatever you add lines before or after it, Ren'py will load at this line. Same if you just replaced "bliblibli" by anything else. It's only if you do the two in the same time (add lines before and change the line), that Ren'py will be momentarily lost. Then, like @Rich said, it will simply go a little back on the code to find a safe point where to load the game.
So if you changed the code to this :
Code:
label myLabel:
    "blablabla"
    $ myVar += 1
    "This is an added line"
    $ anotherVar = 12
    "not anymore bliblibli"
    return
Ren'py will load at "This is an added line".
In the same time, you can add as many comments or empty lines you want, Ren'py will not care at all, they aren't part of the AST. Same if you moved the label where the save happened into another file.

As for the define and default statements, the difference is a little more complicated. A variable created with the default statement will be saved whatever its type (scalar-like, object, other) and whatever you've changed its value or not. This while a variable created with the define statement will be saved only if you change its (direct) value ; see below.
Basically speaking, Ren'py works like this with variables :
  • If a variable is created from an init block, it is NOT put in the "variables to save" list ;
  • If a variable is created with a define statement, it is NOT put in the "variables to save" list ;
  • If a variable is created with a default statement, it is put in the "variables to save" list ;
  • If a variable is created after the "start" label (by using "$ myVar = something" by example), it is put in the "variables to save" list ;
  • If the (direct) value of a variable is changed, it is put in the "variables to save" list ;
    The pitfall here is that changing the value of an object's attribute doesn't change the value of the variable (it's still the same object), and so the change is unseen by Ren'py.
  • When you launch the game (by running the executable), the init blocks, default statement and define statement are executed and the variables created ;
  • When you save a game, every variables in the "variable to save" list are wrote in the save file, and only them ;
  • When you load a saved game, every existing variables will be updated with the saved value, obviously if there is a saved value.
So, strictly speaking, you don't need to use the default statement unless you declare an object that you want to save. But, obviously, it's better to use it every time, at least to be sure that you'll not forget it when it's really needed. This especially since creating variables directly in a label lead too often to compatibility problems with previously saved game.
 
  • Like
Reactions: Domiek

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
My working theory right now is that all variables I'm going to rely upon in a save file (practically all of them), will be setup up by a "default"... probably at the top of each chapter file.

In fact, I can't see a reason not to use default for everything (variables that aren't __var1, etc.).
All variables are either set or used (usually by if...). Either way, I need their value to be in the save file.

My reasoning being that if I add a variable to Chapter 3 AFTER chapter 3 is released, then there is a risk that a player might encounter my updated "if var1 == True" check, but without var1 existing - because their save point was after it's "$ var1 = True" (or something).
With default - that issue largely goes away. It's not perfect, but at least it's not a crashed game error.

As for the AST thing. I think that will need my brain to be more switched on that it is right now. I'm already running into save files that I thought should work failing due to rollback incompatibility. Since the stuff I'm messing with isn't "my" game, I'm not too concerned right now - but it's definitely something I'd like to get straight in my head.
 
  • Like
Reactions: Domiek

Rich

Old Fart
Modder
Donor
Respected User
Game Developer
Jun 25, 2017
2,565
7,378
My working theory right now is that all variables I'm going to rely upon in a save file (practically all of them), will be setup up by a "default"... probably at the top of each chapter file.

In fact, I can't see a reason not to use default for everything (variables that aren't __var1, etc.).
All variables are either set or used (usually by if...). Either way, I need their value to be in the save file.

My reasoning being that if I add a variable to Chapter 3 AFTER chapter 3 is released, then there is a risk that a player might encounter my updated "if var1 == True" check, but without var1 existing - because their save point was after it's "$ var1 = True" (or something).
With default - that issue largely goes away. It's not perfect, but at least it's not a crashed game error.
Exactly. Good plan. One variant you could also consider in that particular case:
Code:
default var1 = None
This actually gives you three states - True & False (if the player played the part of Chapter 3 that sets var1 using the updated game) and None, which tells you that the player was using a save from after the point where you set var1. In the third case, you can toss in some extra content, if you need to, to ask a question again or whatever. Just an option.

As for the AST thing. I think that will need my brain to be more switched on that it is right now. I'm already running into save files that I thought should work failing due to rollback incompatibility. Since the stuff I'm messing with isn't "my" game, I'm not too concerned right now - but it's definitely something I'd like to get straight in my head.
Ya, the issue of what will work if you start modifying the code right around a save point is a tricky one. PyTom's done a pretty good job of all of it, but not every case can be made to work...
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
[...] The only things that end up in the save are variables declared with "default", or variables that are modified after the game is started.
  • If a variable is created with a default statement, it is put in the "variables to save" list ;
  • If a variable is created after the "start" label (by using "$ myVar = something" by example), it is put in the "variables to save" list ;
  • When you launch the game (by running the executable), the init blocks, default statement and define statement are executed and the variables created ;
  • When you save a game, every variables in the "variable to save" list are wrote in the save file, and only them ;
  • When you load a saved game, every existing variables will be updated with the saved value, obviously if there is a saved value.
Okay. So things aren't quite working how I envisioned it.

When I start a game for the first time, any variable defined using "default" is viewable within the variable viewer console.

BUT... if I add a new "default var1 = 0" and then load a recent saved game ... the new variables are created with their default values as I'd hoped.

In this instance, I added a variable pc_corruption after the fact.
The "$ pc_corruption += 1" fails as pc_corruption doesn't exist after loading the save file - which was the whole reason I was growing so attached to those "default" commands.

Edit: Okay. I've looked at the Renpy documentation and by rights, it should be working. They specifically explain it behaves like a "$ var = 0" type command in the on_load block. Except, that's not what I'm seeing.
The game I'm messing with is using RenPy 6.99.12.4.2187.
Latest was 6.99.14.3.
I might have a mess using RenPy 7.0.
I'm guessing it's that 12.4 rather than 14.3.


There must be some quirk I'm missing, something in the fine print I've missed.

Any thoughts?
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,957
16,191
In this instance, I added a variable pc_corruption after the fact.
The "$ pc_corruption += 1" fails as pc_corruption doesn't exist after loading the save file - which was the whole reason I was growing so attached to those "default" commands.
[...]
There must be some quirk I'm missing, something in the fine print I've missed.

Any thoughts?
Er... I just tried it (just in case) and it works as expected with the 6.99.12.4 ; the variable added with the default statement is seen by the loaded game.
  1. Did you use default statement correctly ?
    It should be at init level, outside of both an init block and a label block. Something like :
    Code:
    init:
        [blablabla]
    
    default myVar = 1
    
    label start:
        [blablabla]
  2. Are you sure that you didn't messed with the letters' case ?
    In Python, names are case sensitive, "pc_corruption" is not the same thing than "PC_corruption".
  3. Is the variable really created ?
    To be sure, run the game, but neither start it nor load a save game (so stay in the main menu). Then, open the console and type :
    Code:
    hasattr( store, "pc_corruption" )
    You must have "True" as answer ; it mean that the variable have been created.
  4. Is the "rpyc" file outside of a "rpa" archive ?
    If Ren'py found a "whatever.rpyc" file in one of the rpa archive, it will not care of the "whatever.rpy" file outside of it. This will make your change transparent to it. Like it see the "$ pc_corruption += 1" line, I assume that it's not the case, but I said it just in case.

That's all what cross my mind right now...
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
The mismatch between .rpy, .rpyc and .rpyc (within an .rpa) does cause issues on the first run. (it crashes).

But that first run recompiles any .rpy as a .rpyc and those override the ones within the .rpa.
I've been doing that for about a week now, without any other issue.

I've just downloaded 6.99.14.3 and I'm going to try that. (7.0 had too many new things in the screens.rpy that I didn't recognise - so I'm figuring 6.99.* is the way to go for the moment.)
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,607
2,256
Okay. I can't explain why it's working for you in 6.99.12.4... but it's not working for me.

I've just copied everything (I think) over to a 6.99.14.3 version of the same thing... and it's working as I hoped/expected.

I think I'll settle for "a stupid solution that works isn't a stupid solution" and stick with this 6.99.14.4 version.

Cheers for the quick reply.

Edit: ... and not for the first time, I'm an idiot...
If I add a genuinely new variable... it's fine.
If I add a variable that is already in the .rpa as a "default" too, it decides it doesn't need it - despite the fact that variable doesn't have a value either in the active program or the save file I loaded.

I was trying to build it as a patched type archive, where someone unpacks a ZIP over the top of their existing installation to override the existing version. In real world logic and save files - it would have been fine. But because I'm doing a very specific test that wouldn't matter to a "real" player, I'm shooting myself in the foot.

Edit2: Or not. Different behaviour between 14.3 and 12.4. Even brand new variable aren't available in the older version. I officially give up.