VN Ren'Py STWA: Unbroken [Pt. 6] [STWAdev]

4.70 star(s) 107 Votes

RoryTate

Member
May 15, 2018
160
335
Here's a quick mod that I made that adds a scoreboard to keep track of variables. Just drop the RPY file in the "game" folder and open the game. It displays at the top of the screen and can be toggled off with the backtick key.

For the MC, F and H are your foot/heel status and I is your injuries.
For McNab, R is rivalry.
For Amrit, C is confidence.
For Vi, F is family, A is acceptance, G is grief.
For Rena, FWB is some variable that isn't used yet.
For Kana, I is injuries.
For Els, T is trust and S is secrecy.
For Ines, P is protectiveness.
Otherwise, F is friendship, L is love, and Y is yuri.
There are also two new Ines-specific variables that were added in the Mallorca update: inessubmission and inesselfreliance. These appear to be scores that are increasing/decreasing just like the others. I'm not sure how important they will be of course, but I thought I'd mention them in case you wanted to include them as well.

Anyways, thanks for the handy scoreboard-at-a-glance! There really are an incredible amount of choices and scores being tracked in this game.
 

Ilhares

Engaged Member
Aug 19, 2019
2,882
10,929
There are also two new Ines-specific variables that were added in the Mallorca update: inessubmission and inesselfreliance. These appear to be scores that are increasing/decreasing just like the others. I'm not sure how important they will be of course, but I thought I'd mention them in case you wanted to include them as well.

Anyways, thanks for the handy scoreboard-at-a-glance! There really are an incredible amount of choices and scores being tracked in this game.
Boy, those would be a tough call to interpret so far. On the one hand, I want her to be a strong, capable independant-minded woman. On the other.. I want her to submit and become a happy little housefrau.
 

Mike the Red

Member
Apr 26, 2019
148
132
There are also two new Ines-specific variables that were added in the Mallorca update: inessubmission and inesselfreliance. These appear to be scores that are increasing/decreasing just like the others. I'm not sure how important they will be of course, but I thought I'd mention them in case you wanted to include them as well.

Anyways, thanks for the handy scoreboard-at-a-glance! There really are an incredible amount of choices and scores being tracked in this game.
Okay, I updated the scoreboard with the two new variables. I is for independence and S for submission. I also set default values for all these variables--something the game's developer really should have done.
 

cxx

Message Maestro
Nov 14, 2017
59,010
29,395
1st mc gets good laugh and then some1 else gets good laughs as his bed got occupied and filled with roses.
 

RoryTate

Member
May 15, 2018
160
335
Okay, I updated the scoreboard with the two new variables. I is for independence and S for submission. I also set default values for all these variables--something the game's developer really should have done.
In defense of the dev, from what I see of the code for Unbroken, it does properly initialize any new variables. However, it does so only when they are added in a new chapter/update. This works just fine for how the game itself needs to use them, but unfortunately it doesn't support mods that will be trying to display those values right from the first scene of the game. This is really not the dev's fault, because even if they were to duplicate those variable init lines at the start of the game, that still doesn't fix a save from chapter 2 – for example – that a person loads with the mod active.

I'm not that familiar with Python, but like most programming languages, it appears to have a way to check for null (none) values, which is what you probably used to get around this problem, but I thought I'd pass it along for anyone else who might be curious (Note: this is untested code, found from a quick search, so use at your own risk).

Code:
if my_variable is not None:
    # execute some code
 

Mike the Red

Member
Apr 26, 2019
148
132
In defense of the dev, from what I see of the code for Unbroken, it does properly initialize any new variables. However, it does so only when they are added in a new chapter/update. This works just fine for how the game itself needs to use them, but unfortunately it doesn't support mods that will be trying to display those values right from the first scene of the game. This is really not the dev's fault, because even if they were to duplicate those variable init lines at the start of the game, that still doesn't fix a save from chapter 2 – for example – that a person loads with the mod active.
Declaring global variables at random points within the code is very bad form and really should be fixed. At the very least, global variables should be declared at the start and defined later, though I recommend giving the variables a default value (not relevant to Python, since you can't declare without defining). It is normal to declare, define, and (if the language requires it) delete local variables as needed.

I'm not that familiar with Python, but like most programming languages, it appears to have a way to check for null (none) values, which is what you probably used to get around this problem, but I thought I'd pass it along for anyone else who might be curious (Note: this is untested code, found from a quick search, so use at your own risk).

Code:
if my_variable is not None:
    # execute some code
This gives you a variable undefined error. You have to use a try/except/else block:
Code:
try: my_variable
except: my_variable = 0
else: print("Variable exists with value: "+str(my_variable))
Ren'Py has a built-in command called "default" that does this for you.
Code:
default my_variable = 0
is equivalent to
Code:
init 100 python:
    try: my_variable
    except: my_variable = 0
And, it can be important to note that delayed init, since most init blocks will be run before the default command.
 
Last edited:
Mar 2, 2022
89
1,702
Thanksfor700.png

As promised here's a little render that I would wanted to get out out for patron milestone, starring the two most popular Unbroken girls.
As of writing 3089 renders are posed, 2992 rendered. Slow scene is slow. What I'm trying to decide on if it'll be worth to just focus on posing this week and knock out the rest of the stills and then focus on actually rendering as I tap away at other things, or to stay the current course. The question just is what actually will save time in the long run.
Animations are mostly just waiting for time to move to active production again. Base scenes are set, characters ready to go and choreography planned. Once the render situation is solved we're onto these.
Nothing else happening at the moment, just trying to get, my first build ready and assembled. chipping away as best I can.
As always thank you so much for the support, I hope you have a lovely rest of the week.
Sláinte!
 

RoryTate

Member
May 15, 2018
160
335
Declaring global variables at random points within the code is very bad form and really should be fixed. At the very least, global variables should be declared at the start and defined later, though I recommend giving the variables a default value (not relevant to Python, since you can't declare without defining). It is normal to declare, define, and (if the language requires it) delete local variables as needed.
I understand the frustration, but writing a novel can be like fighting a war. And no battle plan survives the first arrow. So I accept that a VN developer will get inspiration, or need to change events, and sometimes have to create entirely new gameplay elements at any point within the code. Even with a good story outline in place, this kind of thing is going to happen. It may not look pretty or follow "proper" convention, and it may seem random – thought it's not – but I much prefer that over having to restart from the beginning because all save files are suddenly incompatible with a new update, which happens with many VNs unfortunately. In those cases I tend to lose interest in the project because I hate wasting my time having to redo all the previous choices. So again I have to come to the dev's defense on this one. I'm glad if the Unbroken developer is prioritizing function over form.

This gives you a variable undefined error. You have to use a try/except/else block:
I figure it wouldn't be that simple, and python would require having to catch an error. Yeah, that sucks. But good to know I guess.
 
  • Like
Reactions: Walter Victor

Mike the Red

Member
Apr 26, 2019
148
132
I understand the frustration, but writing a novel can be like fighting a war. And no battle plan survives the first arrow. So I accept that a VN developer will get inspiration, or need to change events, and sometimes have to create entirely new gameplay elements at any point within the code. Even with a good story outline in place, this kind of thing is going to happen. It may not look pretty or follow "proper" convention, and it may seem random – thought it's not – but I much prefer that over having to restart from the beginning because all save files are suddenly incompatible with a new update, which happens with many VNs unfortunately. In those cases I tend to lose interest in the project because I hate wasting my time having to redo all the previous choices. So again I have to come to the dev's defense on this one. I'm glad if the Unbroken developer is prioritizing function over form.
Sorry, but you're mistaken here. Despite how poorly most VN are coded (especially rife with spaghetti code), most games manage to organize their variables better. There is even a bold note in the warning developers to default all their variables. While I definitely appreciate the author's ability to tell a good story, their coding is rather clumsy and amateurish.

As to your implication that coding is like combat, there's a little truth to that, with veteran programmers keeping level-headed and organized, while the green programmers rush about, just trying to make it to the next day. My hope is that I can pass along some advice that will help others improve.

As to the issue of new versions of games breaking old saves, this can be resolved through the use of the "config.after_load_callbacks" array, as seen in my mod's code. Any Python function placed in this array is called at game start and after loading a saved game. This means that a developer can use a function to update any code from one version to another (which usually means updating variables). There is also a special "after_load" label that does the same thing in Ren'Py code (instead of Python), but each project is only allowed to use this label once, so adding functions to the array is the way to go for modders (and devs, if they desire).
 

RoryTate

Member
May 15, 2018
160
335
Sorry, but you're mistaken here. Despite how poorly most VN are coded (especially rife with spaghetti code), most games manage to organize their variables better. There is even a bold note in the warning developers to default all their variables. While I definitely appreciate the author's ability to tell a good story, their coding is rather clumsy and amateurish.

As to your implication that coding is like combat, there's a little truth to that, with veteran programmers keeping level-headed and organized, while the green programmers rush about, just trying to make it to the next day. My hope is that I can pass along some advice that will help others improve.
My point is that setting defaults in one section at the top of the code like you suggest is not always possible in a VN. Take the Ines variables I passed along as an example. It's reasonable to assume that the dev had not planned on making aspects of Ines' personality a path/choice for the player in the first release of Unbroken many moons ago, and they only decided to add that in recently. However, the story and game was already many versions deep by that point. So the only way to accommodate that late addition – given that the callback solution you noted wasn't in place – is to simply declare, initialize, and begin using those scores for Ines at the start of the code for the new chapter. It's functional, and it makes complete sense.

Some may argue that duplicate declarations should also be added at the top of the script when a new variable is required like this, mainly for documentation purposes. However, duel initialization can cause maintenance issues, where it's even allowed in the first place, so I can see why a dev would avoid it and only want a single entry in these cases.

The core issue here I think is that writing an iterative novel with multiple releases is a very different beast than pure software development, and some practices simply may not translate well between the two disparate projects. I can relate to the desire for organized and well-formed coding though. It just may not always be possible given the limits imposed on this type of endeavour.
 

Mike the Red

Member
Apr 26, 2019
148
132
My point is that setting defaults in one section at the top of the code like you suggest is not always possible in a VN. Take the Ines variables I passed along as an example. It's reasonable to assume that the dev had not planned on making aspects of Ines' personality a path/choice for the player in the first release of Unbroken many moons ago, and they only decided to add that in recently. However, the story and game was already many versions deep by that point. So the only way to accommodate that late addition – given that the callback solution you noted wasn't in place – is to simply declare, initialize, and begin using those scores for Ines at the start of the code for the new chapter. It's functional, and it makes complete sense.
I think there might be a misunderstanding about the as it works its way through the code.

It begins with engine startup (which is typically not edited by the developer). Next, "python early" blocks are run, though many developers don't use these. The first thing the developer typically influences is the "init phase", which occurs before anything is shown to the user (excluding an optional presplash). During the init phase, anything in an "init block" is set, such as defining Python classes and functions and constants. At the end of the init phase (typically) is when statements are run to set the value of variables. Once script execution begins, . Next, Ren'Py begins calling the special labels splashscreen, before_main_menu, and main_menu. From the main menu, things typically transfer to either the start special label or the "load" function. The load function will overwrite any variables from default statements with their saved value (the current value of "defaulted" variables is always included when creating a save file).

So, the point is that a developer can continually add new variables to an existing project over time, so long as they do so using the "default" keyword. Importantly, This means that if a variable is added with a simple Python command, as this game does with $ inessubmission = 0, it will not be saved if the user bypasses that line, such as when choosing a different route or loading an old saved game. This is especially bad when all other references to this variable are $ inessubmission += 1, meaning that they too will fail without the initial value.

As to the config.after_load_callback list and after_load label, these are built-in capabilities of Ren'Py that execute automatically prior to calling the "start" label and after the "load" function runs.

Some may argue that duplicate declarations should also be added at the top of the script when a new variable is required like this, mainly for documentation purposes. However, duel initialization can cause maintenance issues, where it's even allowed in the first place, so I can see why a dev would avoid it and only want a single entry in these cases.
As I think my above statement covers, declaring variables at init time actually occurs before any of the labels are processed, so it's before the script. Ren'Py will stop you from "defaulting" a variable more than once, so there's no way to have duel [sic] initialization. Putting them together "at the top of the script" isn't technically necessary, but it is the standard practice, due to the headaches that can be avoided if the code is well organized. Many developers, myself included, will have an entire file specifically for constants and variables, so that they can always be located easily.

The core issue here I think is that writing an iterative novel with multiple releases is a very different beast than pure software development, and some practices simply may not translate well between the two disparate projects. I can relate to the desire for organized and well-formed coding though. It just may not always be possible given the limits imposed on this type of endeavour.
The release of software in an iterative manner is more of the norm, rather than the exception, at least in my professional and personal experience. While there are certainly some peculiarities to a visual novel, I don't find it to be different from other forms of software development in any meaningful way. Applying industry-wide good coding practices and having an adequate understanding of how the engine runs means that writing visual novel code is far easier. It's because Ren'Py is so forgiving that software can be released with "sub-optimal" code.

Finally, I appologize if my responses come across as condescending, but I sincerely hope that I can contribute to others writing better code.
 
Last edited:
4.70 star(s) 107 Votes