Ren'Py Things/Configs/Code to keep in mind while setting up the project

Soulonus

Newbie
Dec 23, 2021
47
76
What are the things/configs/code to keep in mind while setting up a new project?

They can sort of 'best practices or just a random thing you noticed while developing one of your games. Things that will make your life easier in the long run as a developer or things that the players will encourage as of large (1 such thing I can think of would be tips to reduce breaking saves across versions).
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Start with two nice easy ones...
  • All variable names (including definitions), names, and everything else you can think of should be lowercase. The exception is Class definitions, but most new developers won't be using those anyway.

  • All filenames should be lowercase, especially images.
    (additionally, DON'T use spaces in image filenames. There are reasons you should and reasons you shouldn't. "don't" is the best solution when you don't know the reasoning why it might matter).

Neither of these are set in stone by RenPy. If you have a strong programming background in a language that does not encourage the use of lowercase and you'd be working against muscle memory to do it that way... that's fine... don't, because you'll already be discipled enough to name things consistently. But there are some internal processes with RenPy that are much more intuitive (especially concerning images) when things are universally named in lowercase.


And continue with a couple that will make more sense half way through your project, rather than at the beginning.
  • To keep complex names readable, use by separating each part of a name with an underscore ( _ ).

  • When naming anything, consider "futureproofing" those names. label visit_gym and label ch1_visit_gym are both fine names... until you also need a another separate "visit gym" in chapter 4. If prefixing naming "ch1" or "v1" or "day1" or anything remotely similar will make your life easier in the future... do it from the start.

  • Create all variables that are impacted by player choices using . For example default ch4_visited_gym = False. Extra points for keep all your default statements together near the top of (each/the) script file.

But to summarize all five points into one... be consistent.
imo, that last one should be enforced at gunpoint - but the others are entirely optional, if somewhat strongly recommended.


A final word of caution. A lot of people have written a lot of RenPy games. Most of those were inexperienced enthusiasts who copied something that already existed. Keep in mind most of those people didn't really understand what they were doing (and still don't)... If you go looking at other people's code yourself, keep in mind you'll see a lot of badly written code that (probably) works - even if it was written this year. Not that there's much you can do about it... just be cautious when the answer you read on google is dated 2014.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
I'll add a couple of entirely personal ones that hardly anyone ever does...

When using a character's name within your dialogue, use their Character() object id rather than typing out the full name. I freely admit, it'll be a pain to get into the habit and it will result in extra typos.... where you accidently use "[s]" (Sharon) instead of "a" (Alice).

For example, if you had the following code:

Python:
define e = Character("Eileen")

label start:

    scene black with fade
    e "Hello, my name is Eileen.

    "*** THE END ***"
    return

Consider coding it like this instead...

Python:
define e = Character("Eileen")

label start:

    scene black with fade
    e "Hello, my name is [e].

    "*** THE END ***"
    return

There's a couple of things going on here which if you are new to RenPy will not be obvious.

Firstly, using square brackets within dialogue [ and ] allow you to put the values of variables into the whatever your characters are saying.

Secondly, there's a bit python/RenPy voodoo going on here - where even though e is a complex Character() object with lots of options and values... if you use [e] within a string (text), it'll always display as the character's name.

(For the more technically minded amongst anyone reading this, Character() has an internal __str__ function that returns self.name when automatically invoked by a string object).

My basic thinking is that it makes it much easier to consider renaming characters later... or more specifically, giving players the option to rename characters. It also avoids the occasional rare typos of longer or unfamilar names... Like occasionally typing "Geneveive" instead of "Genevieve" or even "Bth" instead of "Beth".


My second personal suggestion is equally useful, but difficult to quantify if it's worth the effort...

If your game has a MC (main character) that thinks to themselves... create a second Character() definition for the "thinking". Use that alternative character object to style the text to appear differently.

I'm going to throw in a couple of other personal preferences here and explain them, but the main point is the mc_t definition.

I'm thinking something like this:

Python:
define narrator = Character(None, what_color="#FFFF99")

define mc = Character("[mc_name]", what_prefix="\"", what_suffix="\"")
define mc_t = Character("[mc_name]", what_prefix="{i}(", what_suffix="){/i}", what_color="#AAAAAA")

define e = Character("Eileen", color="#FF66CC", what_prefix="\"", what_suffix="\"")

default mc_name = "Unknown"

label start:

    scene black with fade

    $ mc_name = renpy.input("What is your name? {i}(Press ENTER for 'Simon'){/i}", exclude="[]{}")
    $ mc_name = mc_name.strip() or "Simon"

    "Welcome [mc]."

    mc "Thanks. It's nice to be here."
    mc_t "This is going to be fun."

    "*** THE END ***"
    return

Okay... so the mc_t (Main Character thinking) is the primary reason for this explanation. Because both the names used by [mc] and mc_t are both "[mc_name]", both will appear as whatever name the player picks.

The is a parameter that basically says "whatever the character says... stick {i}( in front of it". In this case, it will use the open italics text tag ( ) and also put an open bracket at the beginning. There is also a what_suffix="){/i}" to do the opposite at the end of the text.

The end result is that rather than seeing:
Simon: This is going to be fun.

-you instead see-
Simon: (This is going to be fun.)

It's a reasonably subtle difference that makes a clear distinction between when the character is speaking to someone else (or muttering out loud) and having some internal monologue with themselves.
It is common in games to use either italics or brackets to denote "thought text".
I prefer to use both. Hence the {i}( and ){/i}.

I also tend to use what_color= to show the text in light gray rather pure white to further differentiate things subtlety.

There's nothing stopping you having similar extra character definitions for other characters thinking to themselves. Or some other variation on this same theme when a character is whispering (mc_w perhaps?). (small text, slight color difference, perhaps " ** " as prefix and suffix?).

The other use of this style of multiple Character() definitions for the same characters is when you first meet a new character and you don't know their name yet. Especially if you use custom colors for each character's name and/or character side images. (So e might be "Eileen", but e_unk might be "Unknown" or "???"). Though honestly, for the 2 or 3 lines of spoken dialogue before you find out their name... it can be considered overkill.


Now a couple of those extra personal preferences I've thrown in there, purely because I can and to demonstrate the flexibility of what can be done.

define narrator = Character(None, what_color="#FFFF99") is a "special" character that is normally automatically created by RenPy (Unless you define one of your own). Any text not spoken a "normal" character (for example "*** THE END ***" is spoken by the narrator character. In this case, I'm using it to change the color of any spoken narrator text from white to very pale yellow (#FFFF99). It's a tiny thing and only one line of code... but (imo) creates a bit of extra polish.

Next, there is the what_prefix="\"" and what_suffix="\"". Because double quote ( " ) is used to denote strings in code... languages can get confused by " " ". Within python (and RenPy), you can use single quotes too... so ' " ' is perfectly fine. But I chose to do it the other way, using " ". Basically all this is, is to put a backslash \ before any problematic character.
The end result is that spoken text is shown on screen with double quotes at either end, just like they would be in a book. Again, not really needed, but (imo) an improvement.

Finally, there's the stuff. Without going into too much technical detail, this just asks the player for a name (that it stores in the mc_name variable), strips off any leading or trailing spaces a player might have inadvertently typed (No names like "Simon ") and sets the name of "Simon" if the player didn't type anything (or only spaces).
The dollar sign at the beginning $ is RenPy for "do python command".

I will say that whilst it's a lot of extra options when creating each new character, it's something that adds a bit of time to something that you'll only ever have to do once - and (again imo) the improvements are worth that bit of extra effort right at the beginning of your project.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
And since I always forget it (as I did again this time)... and someone brings it up later...

Translations.

Any definition that uses a fixed string value (a literal) should be put within these code blocks. _( and ).
This flags the text so that it will be included within any translation efforts.

So where I had:
define e = Character("Eileen")

It really should have been:
define e = Character(_("Eileen"))

Since I'm a native English speaker and tend to avoid translations like the plague... I always forget to suggest it whenever someone asks about best practices.
Text which is part of the game's normal dialogue (pretty much everything after label start:) is automatically flagged for translation without any extra effort on behalf of the developer.

The most common place missed is text which is part of a screen definition.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
But to summarize all five points into one... be consistent.
This is absolutely totally 100% true. Too many games are broke because their author use names like "Love" and "lust", and sometime write love += 1 or Lust += 1.

Decide for one naming convention, and stick to it.
It doesn't matter if it's not a conventional one. I have the habit to use camelCase, and the "bad habit" to sometimes mix it with snake_case ; something like "emmaBedroomMc_firstTimeSex" by example. This permit to separate more clearly the different elements behind the name. It's a scene with the MC in Emma's bedroom (element 1) and this scene is the first time they have sex (element 2)
It also doesn't matter if you use one for variables and another for labels. As long as all your variables follow the their own convention, while all your labels follow theirs, it's good.
What matter is that you don't have to think, or refer to a reference list, when you want to use a particular variable/label/screen/whatever.

I would add, regarding names, whatever variables, labels, others: Make them significant enough.
The "emmaBedroomMc_firstTimeSex" label I used as example above can feel over significant, but this also mean that I'll clearly remember it. Therefore that there's less risk of bugs due to a variable/label misnamed.


Most of those were inexperienced enthusiasts who copied something that already existed. Keep in mind most of those people didn't really understand what they were doing (and still don't)...
If you don't understand how/why it works, do not copy it, ask here for help.
And this apply also to what 79flavors, me, and any other can give as answer ; never use code that you don't understand.

You'll see this cool thing in a game/tutorial/cookbook/whatever and put it in your game. Some will have a bug, apparently related to this part, and like you don't understand how it works, you'll not be able to fix the bug. Or you'll want to adapt it, and again like you don't understand how it works, you'll break it more than it possibly is already.


And since we are now addressing the code, test, test and test again.
Start your game, open the console, and type jump name_of_a_label ; eventually using the console to tweak the variables first. And test it. Each label independently, then all together.
Never assume that your code is bug free, always assume that you made an error, and search what it can be.

Make a small debug screen:
Code:
init python:
    config.overlay_screens.append( "myDebug" )

default debugOpened = False

screen myDebug():
    frame:
        xalign 1.0
        yalign 0.0

        vbox:
            textbutton ( "[[{}]".format( "-" if debugOpened else "+" ) ):
                action ToggleField( store, "debugOpened " )
            if debugOpened:
                texbutton "varName [varName]":
                    action SetField( store, "varName", varName + 1 )
                    alternate SetField( store, "varName", varName - 1 )

                texbutton "otherVar [otherVar]":
                    action SetField( store, "otherVar", otherVar + 1 )
                    alternate SetField( store, "otherVar", otherVar - 1 )
This while make the screen be always visible on the right top corner. With a left click on the button you'll increase the value, with a right click you'll decrease it. An on top of it you'll have a "[+]"/"[-]" button to open/close the window.

Use it when you test, in order to see if a value change when expected, or in order to change yourself the values and see if your conditions are correct.


If you use complex conditions, like if not otherGirlInRoom and not girlMood == "angry" and girlLove > 10 and mcLoveOtherGirl < 5:, don't hesitate to make a dedicated test script to see how the conditions react to the context:
Code:
label myContext:
    $ otherGirlInRoom = False
    $ girlMood = "happy"
    $ girlLove = 10
    $ mcLoveOtherGirl = 4
    call myTest( 1 )

    $ otherGirlInRoom = True
    call myTest( 2 )

    $ otherGirlInRoom = False
    $ girlMood = "angry"
    call myTest( 3 )

    "Test finished."
    return

label myTest( count ):
    if not otherGirlInRoom and not girlMood == "angry" and girlLove > 10 and mcLoveOtherGirl < 5:
        "test [count] pass."
    else:
        "test [count] fail."
    return


If you go looking at other people's code yourself, keep in mind you'll see a lot of badly written code that (probably) works - even if it was written this year.
If it was limited to the code... I recently tested two games using a 7.4.x version, while having the old and ugly pre 6.99 interface.



Edit: Fixed a typo in the code.
 
Last edited:

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
If it was limited to the code... I recently tested two games using a 7.4.x version, while having the old and ugly pre 6.99 interface.

My most recent nightmare was something still being updated in 6.99.10 because the original author had written some very complex classes that used python2 inheritance that involved inheriting from itself (apparently something that is tied to some early limitation of python2). It won't even compile in more recent versions of RenPy.

But yes, I wholeheartedly agree... learning to learn is a long term strategy that will aid you a lot more than learning to copy/paste. Using code you don't understand, no matter how well intentioned, will come back to bite you as some point. Because if you think that bad code is bad now... it's nothing compared with the headache you'll have trying to correct it later. Worst case scenario is that you don't understand it enough to ask the right questions of other people.

Entire RenPy games can be written using a combination of less than 10 commands. If you stick to what RenPy is good at and are willing to stick with "generic" rather than "highly customized"... it's pretty easy to do even complex games with less than 20 (one of these days, I really must get around to counting them - rather guessing at numbers).
 
  • Like
Reactions: Soulonus

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
Entire RenPy games can be written using a combination of less than 10 commands. If you stick to what RenPy is good at and are willing to stick with "generic" rather than "highly customized"... it's pretty easy to do even complex games with less than 20 (one of these days, I really must get around to counting them - rather guessing at numbers).
What make me think about another thing to keep in mind: There's a full documentation !
Read it, refer to it, search in it, and test what it say.


Among the worse copy/past thing that can be seen there's this:
Code:
    show screen whatever
    pause
There's variations that handle it better than others, but I don't know one that isn't broke in a way or another.
Reading the documentation would have permit those authors to learn about the right way to do it, call screen whatever.

Therefore, before trying to write the code to do something "new" with Ren'Py, firstly search in the documentation if it isn't already possible.
I know that, like the documentation is wrote by a programmer, it's not always easy to understand it ; even me with my decades of experience I sometime have troubles. But it's what personal tests are for. Take the part you don't understand, write a small piece of code using it, and see what happen. Tweak this part of code, and see what change. Continue until you understand why it change.
 

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,550
8,807
Ironically, none of the things I'm gonna mention are done in the game I'm helping with :unsure: But making my own VN is too much work so I'm working with what works for someone else. Just some things I wish were done a little differently there

Split your rpa files, please
When you first create a game, it will put everything in archive.rpa file. When you update it, it will put everything in that file again. But you can actually split it into multiple files like this
Python:
build.archive("gui", "all")
build.archive("scripts", "all")
build.archive("images_v1", "all")
build.classify("*.rpy", "scripts")
build.classify("*.rpyc", "scripts")
build.classify("gui/*.png", "gui")
build.classify("images/v1/*.jpg", "images_v1")
That way it's easier to release smaller patch updates with only the changed files instead of making players redownload the whole game with each update unless you're remaking the entire game with each update


Gallery. Put the scenes that you want to replay from the gallery in separate labels and put renpy.end_replay() at the end of such scenes. That way implementing the gallery is just a matter of creating the screen with buttons that trigger the replay
Python:
label before_lewd_scene:
    "dialogue that leads to a lewd scene"
    jump lewd_scene
    
label lewd_scene:
    if _in_replay:
        "if you want the scene to play out differently in gallery, you still can do it"
    "lewd scene"
    $ renpy.end_replay()
    jump next_scene


screen scene_gallery:
    imagebutton auto "image_for_the_button_%s.jpg" action Replay("lewd_scene")
In general, I'd keep labels pretty short so each label is not longer than one conversation, so if I need to change some dialogue, I can jump to that conversation directly when I test the changes
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
In general, I'd keep labels pretty short so each label is not longer than one conversation, so if I need to change some dialogue, I can jump to that conversation directly when I test the changes
This is so important...

It make your life so easier if you've small labels, each one handling one scene, or even one variation of a scene if it's a big one. Not only you'll know exactly where to look to correct something, but it will also offer you the possibility to change your mind.
If you're serious, you'll often have to come back on a scene, to remove a part or add one, because as it, it don't feel good enough. But the bigger are your labels, the more difficult it will be to find the exact place where you need to make the change. Without counting the times where you'll have something like:
Code:
label whatever:
    [...]
    if this:
       [tons of lines]
    elif that:
       [tons of lines]
    else:
       [tons of lines]
    [...]
And you've to make the change in the three blocks. The more there's blocks, the higher are the risks that you'll forgot to change/correct one of them. In the same time, the higher are the risks that you'll do a copy/past, but forgot that a word or a variable is different for each block.

Something like:
Code:
label whatever:
    [...]
    if this:
        jump whatever_this
    elif that:
       jump whatever_that
    else:
      jump whatever_default

label whatever_this:
    [...]
    jump whatever_continuation

label whatever_that:
    [...]
    jump whatever_continuation

label whatever_default:
    [...]
    jump whatever_continuation

label whatever_continuation:
    [...]
would already make your life easier. You'll still have to search the place where you need to make the change, but you clearly see that you've to make it three times, therefore you'll not forgot one. And like the content is more focused, you'll more easily remember that you need to adapt your copy/past.


Also learn how to use call and do it as often as you need. This also will simplify your life:
Code:
label whatever:
    [...]
    if this:
        jump whatever_this
    elif that:
       jump whatever_that
    else:
      jump whatever_default

label whatever_this:
    [...]
    call whatever_commonToThisAndThat
    [...]
    call whatever_commonToAll
    [...]
    jump whatever_continuation

label whatever_that:
    [...]
    call whatever_commonToThisAndThat
    [...]
    call whatever_commonToAll
    [...]
    jump whatever_continuation

label whatever_default:
    [...]
    call whatever_commonToAll
    [...]
    jump whatever_continuation

label whatever_commonToThisAndThat:
    [...]
    return

label whatever_commonToAll:
    [...]
    return

label whatever_continuation:
    [...]
It permit to split your works even more and to avoid repeating the same thing uselessly.
And obviously, the less you repeat yourself, the less you've risk to make an error. This while its easier to correct the error you made, since you only made it once.

There's a game that, after each girlxRelation -= y, have this:
Code:
    if girl1Relation < 0:
        if girl2Relation < 0:
            if girl3Relation < 0:
                jump gameOver
Not only it could be just one if, but do you imagine the number of errors it open to have this every time ? And what if you change your mind, adding a fourth girl, or being more tolerant and testing < -1 ? How hundreds of lines have to be changed ?
Something like :
Code:
label whatever:
    [...]
    girlxRelation -= y
    call gameOverTest
    [...]

label gameOverTest:
    if girl1Relation >= 0 or girl2Relation >= 0 or girl3Relation >= 0:
        return
    show gameOver
    pause
    $ renpy.full_restart()
Will always be better because you've only one place to edit if you change your mind, or made an error.


Splitting also help you to stay focused on what you are effectively doing. There's only one mood and one goal, in this part of the dialog, you'll find more easily the right dialog lines. You'll also more surely when the tone is off, or the CG not accurate enough.
There's games that have this kind of structure:
Code:
label day1:
   [...]
   if this:
      [...]
   else:
      [...]
   [...]
   if this:
      [...]
      if that:
          [...]
      else:
          [...]
      [...]
   else:
      [...]
      if that:
          [...]
      else:
          [...]
      [...]
   [...]
   jump day2
It's just not possible to test efficiently this kind of label.
Not only you'll always forget something, but you'll also have too much to proceed at once. Both even if you save before all key points.
 

TDoddery

Member
Apr 28, 2020
170
160
There's games that have this kind of structure:
Code:
label day1:
   [...]
   if this:
      [...]
   else:
      [...]
   [...]
   if this:
      [...]
      if that:
          [...]
      else:
          [...]
      [...]
   else:
      [...]
      if that:
          [...]
      else:
          [...]
      [...]
   [...]
   jump day2
It's just not possible to test efficiently this kind of label.
Might be OK if it's a time-travel game.

Just kidding.
 
  • Haha
Reactions: crabsinthekitchen

crabsinthekitchen

Well-Known Member
Apr 28, 2020
1,550
8,807
Might be OK if it's a time-travel game.

Just kidding.
This literally looks like something out of our script files and the story does have time travel. I'd like to eventually rewrite it with shorter labels but scared of breaking saves since it's already on Steam
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
I'd like to eventually rewrite it with shorter labels but scared of breaking saves since it's already on Steam
When loading, Ren'Py can only goes as far as the rollback stack size to find a point where he should start. But normally it also rely on the current label name. What mean that as long as you keep the current names for at least the few first lines of the label, it should works. It's to try at home first, of course, just to be sure that it haven't been changed, or broke, with all the rewriting done in the 7.4.x branch.

But this isn't issues free. The players being sent back to the very start of the label, they would have to replay a potentially big part of the story. And since it's only the "place in the story" that have been reset, every variables keep the value they had ; what mean that if they make a different choice this time, they can potentially have a game where they both kissed the girl and punched her face.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
Yeah. I completely agree with Anne's suggestion about indented code.

Long before your if statements get "out of hand", get into the habit of using and/or instead.

So instead of:

Python:
    if day6_something == True:
        # 10 lines of dialogue
        # .
        # .
        # .
        # .
        # .
        # .
        # .
        # .
        # . ----

    # onward

Do:

Python:
    if day6_something == True:
        jump day6_ten_lines_of_dialogue

    jump day6_carry_on_after_10lines


label day6_ten_lines_of_dialogue:

    # those 10 lines of dialogue


label day6_carryon_after_10lines

    # onward

This is by far the simplest example of the mess that can result from doing it the other way. But if you get into habit early, all those potential future problems become much easier.

An added bonus is that coding this way makes it much easier for people to MOD your game. Just remember that those MODs are THEIR problem, not yours.

Additionally, because of the way save files and rollback works... more labels means a significantly reduced chance that save compatibly will cause problems for you if you need to make changes to existing code.


Going back to the subject of packaging up your code within .rpa archives. I kinda disagree with crabsinthekitchen about breaking up archives in order to make rolling out future updates easier. I agree with breaking up the packages just as they have said... just not the justification.

They are right... updates... specifically "fix patches" or "bug fixes" are almost always just code changes. If all your script files are in scripts.rpa and those are the only changes made... then in theory you could just distribute that single file as a bugfix patch. A bug fix of 15MB instead of the full game at 1.4GB sounds ideal. And it WILL work... for some people. It should work for everyone - but it won't. Now... rather returning to work on your next big update... you'll be hand holding people who messed up copying a single file to a single folder. Whilst it is completely inefficient to distribute the whole game again... it's also what 99% of other developers already do... and so it's what players are comfortable with. Most importantly, despite the inefficiency, it works. There are lots of better technical solutions for this... but "keep it simple" applies here... and I'd argue that simple is "do it just like everyone else does, rather than trying to be clever".

For reference, this is my portion of the options.rpy covering packaging up your game...

Python:
  ## Classify files as None to exclude them from the built distributions.

    build.classify('**~', None)
    build.classify('**.bak', None)
    build.classify('**.psd', None)
    build.classify('**/.**', None)
    build.classify('**/#**', None)
    build.classify('**/thumbs.db', None)
    build.classify("game/**.rpy", None)

    ## Files matching documentation patterns are duplicated in a mac app
    ## build, so they appear in both the app and the zip file.

    build.documentation('*.html')
    build.documentation('*.txt')

    # Declare three archives.
    build.archive("scripts", "all")
    build.archive("images", "all")
    build.archive("sounds", "all")

    # Put script files into the scripts archive.
    build.classify("game/**.ttf", "scripts")
    build.classify("game/**.rpyc", "scripts")

    # Put images into the images archive.
    build.classify("game/**.jpg", "images")
    build.classify("game/**.png", "images")
    build.classify("game/**.mp4", "images")
    build.classify("game/**.avi", "images")
    build.classify("game/**.webp", "images")
    build.classify("game/**.webm", "images")

    # Put sounds into the sounds archive.
    build.classify("game/**.ogg", "sounds")
    build.classify("game/**.wav", "sounds")
    build.classify("game/**.mp3", "sounds")

    build.include_i686 = False

You could go further an split video and images. This is just my starting point template.

I deliberately exclude the source code from the finished game, primarily to make any sort of "code review" by anyone fractionally more difficult. Sites like Patreon probably never look at source code and recreating the .rpy files is childsplay with the right tool - but it adds one tiny extra obstacle without impacting most players at all.

Since I regularly accidently leave photoshop files with folders, I'm explicitly excluding those too. I've left it in this example, as a reminder that you can do this sort of thing for files you accidently leave hanging around too.

Finally, that last line tells RenPy not to bother building a 32bit version of the game. Anyone playing a game on their 20 year old Pentium4 processor has bigger problems than me. Historically the 32bit executable triggered a LOT of false positives within antivirus programs. Not including it is simpler than spending the 4 days after a new release explaining to people on the forums that their AV is shit.
 
Last edited:
  • Like
Reactions: Tribe and Soulonus

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
An added bonus is that coding this way makes it much easier for people to MOD your game. Just remember that those MODs are THEIR problem, not yours.
Yes, but alas no.
Mods should be the problem of their authors, but it's not this rare to find someone complaining about a bug in the game, blaming the lazy/idiot game author, when in fact the bug come from a mod he use ; hell, there's even, time to time, one that blame the shitty quality of the compressed version he downloaded on the author and not the compression...
So, the easier is the life for modders, the less you'll risk to be blamed for their own errors.


Additionally, because of the way save files and rollback works... more labels means a significantly reduced chance that save compatibly will cause problems for you if you need to make changes to existing code.
And less content to replay if the changes force Ren'Py to restart the whole label. Therefore less risk to have incompatible stats in the game (see my comment right above 79flavors' one).


I deliberately exclude the source code from the finished game, primarily to make any sort of "code review" by anyone fractionally more difficult.
[It's a detail, and it's only annoying for players trying to be smart, but it probably have to be said]
But the player will then use unrpyc. There will be rpy files alone in the game directory, forcing Ren'Py to rebuild the "rpyc" files. But, the original "rpyc" files being still in the "rpa" archive, it will not rely on them when rebuilding.
The first visible issue will be a lot of "duplicated labels" errors ; while it don't rely on the "rpyc" files in the archive, it still know everything about them. The second issue will be the reset of all the magic numbers ; every lines will seem like new ones and therefore been none been skipped.

At the opposite, when the "rpy" are included in the "rpa" files, the player will need to use unrpa (or similar) and both the "rpy" files and "rpyc" will be extracted. Ren'Py will not have to rebuild the files, and if it have to do it, the magic numbers will stay the same and there will be no duplicated labels issues.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,581
2,219
And less content to replay if the changes force Ren'Py to restart the whole label. Therefore less risk to have incompatible stats in the game (see my comment right above 79flavors' one).

In fairness, I edited that in after noticing your reply 1 min before mine.

But the player will then use unrpyc. There will be rpy files alone in the game directory, forcing Ren'Py to rebuild the "rpyc" files. But, the original "rpyc" files being still in the "rpa" archive [...]

hehe. It honestly never occurred to me that people can unrpyc without first unpacking the .rpa file(s). I tend to use UnRen to unpack the archives, then recreate the .rpy files if needed and then finally delete the (now unnecessary) .rpa archives. A lack of imagination on my part. That said, I'd probably still do it - figuring anyone aware enough to use a tool like unrpyc or UnRen would recognize the potential problem. There... I did it... I assumed players were idiots and can't be trusted to do even a simple task AND assumed players are semi-competent in the same thread. The fact that it would be two entirely separate group of "players" shouldn't absolve my hypocrisy. :devilish:
 
  • Haha
Reactions: LightmanP

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
There... I did it... I assumed players were idiots and can't be trusted to do even a simple task AND assumed players are semi-competent in the same thread.
Isn't it what we do everyday at work ? We build our software as if users will all be almighty omniscient persons... then add a lot of validations, error controls, and exception catching, expecting them to do the stupidest things ever.

And this also, it have it's place on this thread. Because it's exactly how you should see the players...

Write your game as if everything will always goes as expected, because it's easier to do so ; and also because it's what will happen most of the time.
But never forget that there's players that will do stupid things ; like using the console to change a value, just because, while browsing the game thread, they seen someone saying something that looks more or less like that. So, also ensure that what you did can survive player's idiocy.
 

Sagisu

Newbie
Nov 12, 2017
30
25
Ok.. so I'll tackle this question from other perspective =p
There is ton of info how to write code (even book like Clean Coder that I reccomend), but I'd like to add how I'm starting with projects.

First of all - use GIT. It really helps with coding with more people, keeping track of history of the code and much more.

After that I start fresh project and first thing I do is rearrange project files - I'm keeping all images in one dir (yes, I move all the gui files to images/gui), split all screens to separate files (from screens.rpy) and I'm creating my own structure. I'm keeping all styles, transforms, pyton classes/functions and so on in files/folders they relate to. I'm also changing all images, so they can be used in different colors (masks ftw!), but it's more complex and probably more like project dependent.
I've got whole module for closed code elements like eg. game achievements, so I've put all that code in separate module dir (screen view, toaster, class, dealing with persistent data etc).

Oh, and don't forget about configs. I never add eg. color inline in code, because I want to have possibility to change entire colorbase if I'd need to (maybe user can change it, maybe colorblind mode?). I try avoid variable that are put inline - either put those in some config files or at least keep them on top of the rpy.
 

Meushi

Well-Known Member
Aug 4, 2017
1,146
12,727
Going back to the subject of packaging up your code within .rpa archives. I kinda disagree with @crabsinthekitchen about breaking up archives in order to make rolling out future updates easier. I agree with breaking up the packages just as they have said... just not the justification.

They are right... updates... specifically "fix patches" or "bug fixes" are almost always just code changes. If all your script files are in scripts.rpa and those are the only changes made... then in theory you could just distribute that single file as a bugfix patch. A bug fix of 15MB instead of the full game at 1.4GB sounds ideal. And it WILL work... for some people. It should work for everyone - but it won't. Now... rather returning to work on your next big update... you'll be hand holding people who messed up copying a single file to a single folder. Whilst it is completely inefficient to distribute the whole game again... it's also what 99% of other developers already do... and so it's what players are comfortable with. Most importantly, despite the inefficiency, it works. There are lots of better technical solutions for this... but "keep it simple" applies here... and I'd argue that simple is "do it just like everyone else does, rather than trying to be clever".
Until the game become largish, and then you're fielding complaints about people struggling to download your huge game.

You're right that apparently lots of players lack basic computer skills, which is probably why many of the devs which do offer update patches also offer the full revised package each release. That way the people with half a clue are happy with their smaller download, and the clueless download the whole thing every time.
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,369
15,285
First of all - use GIT. It really helps with coding with more people, keeping track of history of the code and much more.
Or any other versioning/revision control software tool, like by example. There's really no need to use an online one, at least if you backup on your own.
But you can also do it by yourself with a basic backup script that just add the date at the end of the file name during the backup. It offer less flexibility but still permit you to retrieve the previous version of your code.


I've got whole module for closed code elements like eg. game achievements,
Never really had the occasion to test it, so what's so wrong with ?
 

Sagisu

Newbie
Nov 12, 2017
30
25
Or any other versioning/revision control software tool, like by example. There's really no need to use an online one, at least if you backup on your own.
True. I've wrote it as an example to overall tip - do backup. I'm just using git on daily basis, so it's the one that pops first in my mind =p

Never really had the occasion to test it, so what's so wrong with ?
Well actually few things: as I said whole UI (menu to check achievements, toaster to show user that he was granted trophy), more attributes like icon, description, flag if trophy is secret (not visible or without name/description in achievement list). But TBH I just enclosed this basic Renpy functionality and added much more mentioned before.
 
  • Like
Reactions: anne O'nymous