Ren'Py Stats Framework
A downloadable tool
Do you use stats (like love interest affection, personality stats, health, etc) in your Ren'Py game? Are you tired of typos (like $adam_affection = 1
instead of $adam_affection += 1
) causing subtle hard-to-find bugs? Are you tried of typos (like $adan_affection += 1
) causing game crashes?
If so, this is the framework for you!
This framework (BobCStats) adds a custom command for managing stats so typos can be caught at lint time (along with other settings to minimize their impact if you forget to lint). Use it to help manage your stats.
Directions for use are included in the source files, or follow along below!
This framework is provided under the Unlicense and released into the public domain, and no attribution or further licensing is necessary. Feel free to modify it to suit your use case! (If you do wish to attribute me, a link to bobcgames.com would be greatly appreciated.)
This framework is provided "as is" without warranty of any kind. Basic help can be provided in the project discussion board, or visit the Ren'Py Discord server for more complex customizations.
Also check out my other Ren'Py Frameworks, Tools, and Game Dev Resources!
How To Use
The TL;DR is: Download the file, update the list of stats you want to track, and use the stat
command in your script to add or subtract from it!
Download The File
To get started, download the 0bobcstats.rpy
file and drop it into the game/
folder of your Ren'Py game project.
(Do not rename this file, or stats may not behave properly, or your game may not run.)
Update The Stat List
Open the 0bobcstats.rpy
file up in your favorite text editor. The first thing to do is add your stats to the defined BOBCSTATS_STATS at the top of the file.
define BOBCSTATS_STATS = ("evil", ("hp", 100),)
For example, the predefined code defines two stats. The first stat is called evil
and has no default value (meaning it defaults to 0). The second stat is called hp
and has a default value of 100.
(Note: This framework only supports numeric stats. For boolean stats, also known as "flags", you'll need to define and manage those in the usual manner.)
Update this list with all of your desired stats, and delete the two starting ones if you do not wish to use them.
The framework should raise fairly descriptive errors during game launch if you configure something improperly in here. Start your game often as you're adding stats if you're worried about the formatting.
Note: Do not also default any stats defined here. For example, if you add a stat here called "evil", do not also include a default evil = 0
statement in your code. This will cause a startup error if done.
Add or Subtract Stat Values in Game Code
After you have defined all of your stats in the list, you'll want to increase or decrease their values in game code!
In your game code, simply type stat statname add <num>
to add the specified number to the stat with the given name. Similarly, type stat statname sub <num>
to subtract that number from the stat.
For example, to add 5 to the evil
stat and subtract 10 from the hp
stat, your script.rpy would look something like:
label start: scene bg room show eileen happy e "You've created a new Ren'py game." e "Also you just lost an important battle!" stat evil add 5 stat hp sub 10 e "Your evilness is now [evil] and your HP is now [hp]." e "Once you add a story, pictures, and music, you can release it to the world!" return
Checking Stats in Game Code
Stats aren't very useful if you can't check their values. The good news is... that works the same way as usual! You can use stats (by their name) anywhere in code where you would have used a default
ed variable.
For example, you can check if the player has at least 10 evil with if evil > 10:
just like usual. You can also use them with text interpolation just like usual in dialogue or a screen, such as "Your HP is [hp]."
or text "HP: [hp]"
or bar value hp
.
This is all you need to do! You now have stats in your game that will prevent typos like =
instead of +=
. However, read on for further (optional) customizations that can further help.
Optional: Limit Stat Values
By default, each stat has no upper or lower bound. However, in certain cases, you may wish to enforce a bound. (For example, it probably doesn't make sense for HP to ever fall below 0.)
You can do this by modifying the BOBCSTATS_STAT_LIMITS
dict in the code. This dict should have a key with a stat name, and a value of a tuple. The tuple should contain the lower bound followed by the upper bound, or None if there is no bound in that direction.
For example, to bound the hp
stat with a lower bound of 0 and no upper bound, and to ensure evil
never exceeds 100, you can define:
define BOBCSTATS_STAT_LIMITS = { "hp" : (0, None), "evil" : (None, 100), }
Whenever a stat change is applied during gameplay, these bounds will be automatically enforced for you.
Optional: Limit The Amount A Stat Changes
Despite the framework, it's still possible to fat-finger a number and change a stat by more than you intend. For example, you might type stat evil add 11
when you mean to type stat evil add 1
, resulting in more evilness than intended.
If you expect that you won't be changing stats by more than a given number, you can set the BOBCSTATS_SWING_LIMIT
setting to that number.
For example, if you never expect a single stat change to be more than 5, you can define:
define BOBCSTATS_SWING_LIMIT = 5
If you make a typo, or attempt to change a stat by more than this amount (such as stat evil sub 6
), you will get a lint error. During gameplay, the stat change will also be bounded by this number (so stat evil sub 6
will only subtract 5 evil).
Optional: Use Different Variable Names
You may want the stat names to be shortened, but the actual variable storing the values to be longer. For example, if you're making a dating sim and define a character named adam
, you should not also have an adam
variable, but you probably don't want to have to type stat adam_affection add 1
constantly.
The BOBCSTATS_VARIABLE_PREFIX
setting can be used to resolve this problem with variable name conflicts. When this value is set, all variables that the framework manages will be prefixed by this value.
For example, if you're making a dating sim and want to prefix all variables with affection_
(resulting in variables like affection_evil
and affection_hp
for the default stat examples) you would define:
define BOBCSTATS_VARIABLE_PREFIX = "affection_"
This will allow you to make stat changes as usual with stat adam add 1
, but variable checks will be prefixed such as if affection_adam > 10:
, preventing conflicts with character names and other variables.
Optional: Change The Command
The command to use in your game script, by default, is stat
. That is, to change a stat using the framework, you type stat evil add 1
.
In certain cases (like a dating sim), you may wish for the command to better reflect its use case, such as affection tracking. The BOBCSTATS_COMMAND_NAME
setting can change the command used.
For example, if you want to instead update affection with affection adam add 1
in your script, you would update line 65 to be:
BOBCSTATS_COMMAND_NAME = "affection"
This does not change anything else about how variables are named or how any of the other optional features work.
Frequently Asked Questions
(For some definition of "Frequently Asked")
Shouldn't I type $ stat evil add 1
?
No! Starting a line with $
in Ren'Py tells the engine to interpret that line as Python code.
In this case, we have defined a creator-defined statement (CDS) for stats instead, which allows you update stats more directly in your game's script. We use this instead of a python function invocation to update stats.
The benefits of CDSes are many, but in particular, CDSes can be validated as part of the lint process (as the achieve
command here is!) while Python code cannot be, and will instead fail at runtime.
(If you aren't already, you should be in the habit of regularly running lint on your game via the Ren'Py launcher, as it will help detect many problems that would either crash your game, or cause unintended behavior.)
Download
Click download now to get access to the following files: