• To improve security, we will soon start forcing password resets for any account that uses a weak password on the next login. If you have a weak password or a defunct email, please update it now to prevent future disruption.

Organizing Renpy Code/Files/Scripts

fooslock

Newbie
Feb 15, 2020
65
25
Yes and no. Strictly speaking there's not reason for this, but generally people prefer when things are grouped together.
So, like it's some utility code, it being with the other ones in an "utility.rpy" file, by example, would feel more natural.




You'll have to deal a lot with it once you graduated. So, better to starts with something basic like an adult game.
And it's not just a question of repetitiveness, it present two other advantages:

Firstly, it permit to groups the data.
It's something regarding "sarah", it will be in the "sarah" object. Cleaner data management, less to memorize, faster access in terms of code writing.

Secondly, it permit to add a level of abstraction to your code.
If your change your mind regarding the way to do something, you just have to update the class, and your code will automatically adapt.

By example, instead of relying on a change_stat() function, it can be a method:
Python:
init python:

    class Stats( renpy.python.RevertableObject ):
        def __init__( self, name ):
            self.name = name
            self.love = 0
            self.lust = 0

        def change_stat( self, what, step = 1, notify = True ):
            setattr( self, what, getattr( self, what ) + step )
            if notify and not step == 0:
                renpy.notify( "{}'s {} has {}".format( self.name, what, "increased" if step > 0 else "decreased" )

        def update_love( self, step = 1 ):
            self.change_stat( "love", step )

        def update_lust( self, step = 1 ):
            self.change_stat( "lust", step )
If you decide to change the way you notify about a stat change, just update the change_stat() method.

If you decide to cap the lust, just change the update_lust() method, like that by example:
Python:
        def update_lust( self, step = 1 ):
            if self.lust < 20:
                self.change_stat( "lust", step )
You can also decide that the positive stats are linked to the negative ones:
Python:
        def update_love( self, step = 1 ):
            if self.anger <= 0:
                self.change_stat( "love", step )
            else:
                renpy.notify( "{} is clearly too angry to appreciate it".format( self.name ) )
All is clearly in the same place, directly and easily available, and just changing one method is enough to safely change the behavior for the whole game.
That's pretty frickin fancy friend. Thanks again for all the hints. got a nice toolbox coming along.
 

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,560
2,179
If a variable will change while the game is running... use default.
If a variable will never change while the game is running... use define.
That's interesting. Any more small things like this? I have yet to fully deepdive in the Renoy documentation but that's coming on real soon. Any other smaller areas in regards to Renpy where going over it with a fine tooth comb will save problems later?

Obviously, yes. But I already included the "repeat offenders" so to speak. The stuff I see on a semi-regular basis.

One other easy traps that tends to crop up are those developers who want to include a "points" based system, without really knowing why. They feel they should be tracking things like "love" or "hate" or "corruption", without thinking through the consequences to their game. For example, a game tracking "love points" for the main love interest... by a particular point within the game, the player could have accumulated 8 points. So our fictional newbie developer codes something like if tessa_love_points >= 7:. At first glance it make perfect sense... if our player is effectively on the "love route", then events should be gated only for those players who at committed to that route. Except, you've now made it pretty much mandatory for the player to pick almost every "love" choice up to that point. Moreover, you've probably just made it essential that someone write a walkthrough for your game, since getting anything less than 7 means hardly any content. Balancing a points based system is hard, especially in a more linear game. They can work in open world games, but then it largely becomes about the grind.
Edit: Ah, just noticed it's already covered within the thread. Limited time today, so spent it writing rather than reading.

Another pitfall is the "pick out clothing" trope or similar. Where the MC and the LI inevitably end up in the same clothing shop we've seen literally hundreds of times before to pick out some clothing... for... let's say a party. The player gets to pick between the black sexy dress, the red slutty dress and the blue conservative dress. Again, it makes perfect sense - you give the player agency to pick an outfit they most want to see the LI wearing, and in so doing immerse themselves more into your game. Except... now for almost every scene at party is going to need to be rendered THREE times!!! Once for the red dress, one for the black and one for the blue. Three times the work for very little payoff. Giving the player choices (even meaningless ones) is good... player choices which double or triple your rendering work is bad.


Finally, a bit of code...

Python:
default persistent.saved_mc_name = "Trevor"
default mc_name = "Unknown"

define narrator = Character(None, what_color='#FFFF66')

define mc = Character("[mc_name]", what_prefix="\"", what_suffix="\"")
define mc_t = Character("[mc_name]", what_prefix="(", what_suffix=")", what_italics=True, what_color="#BBBBBB")

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

label start:

    scene black with fade

    "*** START ***"

    centered "Welcome to [config.name] [config.version]."

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

    "You are in a comfortable tunnel like hall."

    e "Welcome [mc]."
    mc "Thanks [e]."

    mc_t "Wow, [e] is pretty."

    "Waiter" "Could I assist you finding a table?"

    "*** THE END ***"

    return

Okay, there's a lot going on there, but for the most part I've included everything for a reason.

persistent is a storage area for variables. It persists between gameplays. Normally, when you start a game all the variables are reset. Persistent variables aren't and will retain whatever value they previously had. It's great for volume levels for example (though RenPy has it's own store for that).

I define a specific Character() known as "narrator". This is one of a number of that RenPy uses. In this case, narrator effectively speaks any line of dialogue not assigned to any other character. I override the default character definition so I can change the spoken text to a light yellow color. It's a small distinctive change that adds a surprising amount of polish for such a small change. The choice of color presumes that "normal" spoken text is white.

Text that contains " [ ] " will normally display the value of a variable within the brackets. In my initial code, I use this to allow the player to change the MC's name.

what_prefix and what_suffix add text before and after any spoken dialogue. It means any dialogue appears within your game in the same way that it would be shown in a book, within quotes. Again, a tiny bit of unnecessary polish.

I define TWO almost identical characters for the MC. mc and mc_t. The second one is for when the MC is thinking. The dialogue is displayed differently than spoken text by changing the color slightly, enclosing the text in parentheses rather than quotes and the line is shown in italics. It's unusual for the player to be able to hear the internal monologue of any other character - but you could do it with other characters too. It's much easier than typing mc "{i}(She's pretty.){/i}". Again, not necessary but the tiny amount of work pays off very quickly when working on a script that has the MC thinking a lot. If your characters whisper a lot - you could create variations on mc_w too.

All other speaking characters get the what_prefix/suffix treatment. In this example there's only one other character. There'd normally be lots.

Try to add transitions like with fade or with dissolve to any image you display as a background using scene. Fade is good when moving between locations. Dissolve is good practically everywhere else. Omitting transitions is fine, but can feel a bit abrupt - though you tend to only notice it when you've been playing a game that uses with dissolve everywhere and then play a game without them.

centered is another one of those special predefined characters. It writes text across the center of the screen rather than at the bottom. It's really useful for things like "Time passes..." or "Morning." type messages.

The next block is about asking the player to name the MC. You'll see variations on it in most games. Mine is a little bit of an evolution of the normal code, in so much as the name the player picks is stored in a persistent variable. What it means is that when the player next starts a new game, they will be offered the same name as you used last time as the default. A tiny touch, but a nice once.

Again, I'm using text with variable embedded within it to show the values of those variables. Normally, those variables would be numbers or strings... in this case it is a Character() which you might think would incompatible. But a nice thing built into RenPy is that character objects used as string display the character's name instead. If you can get into the habit of using the variable rather than typing out character names all the time - you could expand your game at some point to allow the player to name/rename all or most of the characters rather than just the MC. Though it is something you need to do right for the outset.
(btw, if you do allow the player to name other characters - especially at the start of the game - show a picture of the character and explain their relationship to you as you prompt the player for a name. Ideally list all the "types" of nameable characters first too. Nothing worse than naming your sister only to find out that you have a younger sister to name too).

The "Waiter" is another way of naming a character that is often overlooked. Typically a temporary throwaway character like a waiter, policeman or shop assistant. Instead of using a variable (Character), just use a string. There's no formatting and it's not ideal - but equally it's a simple alternative to defining another Character() that will never be used again. Honestly, I wouldn't do it this way - but it's so often overlooked as an option, I thought I'd include it.

And that's pretty much it. By adding a few optional parameters to each character definition, you can make the context of what the text being shown to the player much more obvious.
 
Last edited:
  • Like
Reactions: fooslock

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Respected User
Donor
Jun 10, 2017
10,202
14,939
so looking at this I'm guessing the date can end with zero. or even negative?
Totally.


I was thinking of using stats in my game to where you collect them but just have to meet thresholds to get the majority of the content so like, as long as you're generally not a douche you don't have to make every perfect choice.
Ideally, I advice to goes for 3/4 of the maximal possible points as threshold. It leave enough freedom to the player, while still making it mandatory to be serious enough in the picked choices.


Something that is also to remember is that a point loose isn't necessarily something bad.
In Pact With A Which, there's a moment when you're facing a choice that will either give you +3 points, or -7 points. But if you follow the second one, you'll clear the misunderstanding and get a +15 at the end ; what make you earn 5 more points that if you pick the safest option.

It can be by example something like the MC refusing to go see a movie. The player will loose points because the girl dreamed about this movie date for the whole week. But then they'll cross a florist, the MC will buy her some flowers (+ points), they'll go to the park and play in the grass (+ points), and they'll end the day in a fancy restaurant (+ points).
Being surprising and romantic, she'll end loving the MC more than if the player just goes with the basic, and finally boring, date that she had in mind.

My perverse grumpy mind also want to add that it force the players to take risks instead of picking one choice, doing a bit of rollback to pick the other one, then finally choose what have the best immediate outcome.
 
  • Like
Reactions: fooslock

peterppp

Member
Mar 5, 2020
469
879
to give an example... you are calculating the value to raise and so you don't know what the value will be. it could be 0.
change_stat("relationship", calculated_value)
guy doesn't understand

(code used to eventually calculate the value to use)
change_stat( girlLove, good - bad )
guy understands

lol, it's woody all over again. i explain, they don't get it. you explain the same thing, they get it.
i bow to the master.
i should hire you to be my interpreter and write my posts.
 
  • Like
Reactions: fooslock

Cenc

Well-Known Member
Game Developer
Jun 22, 2019
1,575
2,714
guy doesn't understand


guy understands

lol, it's woody all over again. i explain, they don't get it. you explain the same thing, they get it.
i bow to the master.
i should hire you to be my interpreter and write my posts.
My apologies, I had not considered the case of aggregating the relationship points, since I myself provide instant notification the scenario would never arise - so, my question was genuine.

I don't know who woody is. I just thought I'd pop in and show an idea to someone who wanted it. I now know my place.
 
  • Like
Reactions: fooslock

fooslock

Newbie
Feb 15, 2020
65
25
I think, for now, I'm gonna pause here. I have a lot to work on before I'm ready for more questions and answers, and while gathering this info is nice, it's useless to get all this effort to actually, well, not get to work. So, thanks, everybody; you've all been super swell, and I hope you all have a pleasant day or night or whatever. Thanks again!