This page is out of date

You've reached a page on the Ren'Py wiki. Due to massive spam, the wiki hasn't been updated in over 5 years, and much of the information here is very out of date. We've kept it because some of it is of historic interest, but all the information relevant to modern versions of Ren'Py has been moved elsewhere.

Some places to look are:

Please do not create new links to this page.

DSE - Dating Sim Engine

The DSE is a framework for writing games based on events that are triggered by statistics. It comprises a number of independent modules that can be combined to create games.

DSE can be downloaded from the frameworks page.

Statistics Framework (stats.rpy)

The statistics framework represents statistics as variables that store the value of the statistics. The variables are registered using the register_stat function. They can be updated using normal python operations, but the normalize_stats function should be called to ensure that stats remain between their max and minimum values. The display_stats function displays the statistics on the screen.

This should be called from init code to declare a stat.

name - The name of the stat, used to caption it when the stat is displayed.

var - A string containing the name of the variable that contains the stat.

default - The default value that the statistic c

max - The maximum value the stat can take on.

This should be called after one or more stats have been updated. It ensures that each stat has a value that is between 0 and the maximum value defined for that stat.

Displays a window containing all the stats, in the order registered. The name, bar, value, and max parameters control the display of the corresponding portions of the statistics.

Events Framework (event_dispatcher.rpy)

The core of DSE is the event dispatch system. This system is responsible for calling Ren'Py code that implements each of the events. The event dispatcher chooses a list of events that should be run during a given period, and then is responsible for calling each of those events in turn until either all events are exhausted, or one of the events indicates that the period should come to an end.


An event consists of a block of Ren'Py code. The name of the event is the name of the label that is called to execute that event. There are three ways of returning from an event. Simply executing a 'return' statement will end the event, and activate any later event in the same period. Jumping to events_end_period will end the current period, ignoring any future-scheduled event. Jumping to events_skip_period will both end the current period, and cause the next period to be skipped.

Events are declared by calling the event(name, ...) function from inside an init block. This function takes a variable number of arguments. The first argument is always the name of the event, which is also the label that is called to start the event. The second and later arguments are conditions that must be true for the event to occur. These conditions may either be python expressions in strings, or the result of one of condition functions given below.

The event function takes one keyword argument, priority which gives the priority of the event. Events with a smaller priority number are considered before those with a larger priority number, with the default priority being 100.

Consideration of events occurs in two phases. First, the conditions are evaluated on each event. If any condition is false, the event is discarded, otherwise it is added to the event list. Then, the list is filtered and random elements chosen such that only one event is in each choice group, for the events that are in choice groups. The events are always kept in priority order, with lower numbers being higher priority. The first event in the list is recorded as being executed, and then executed. Execution continues until all events are exhausted, or the period is forced to end.

The following condition functions ship as part of the DSE.

Returns True until the event has been executed once, and then returns False. It can be used to ensure that the user will only see and event once.

Returns True if there are no higher-priority events scheduled. Use this on a very-low-priority event that should only occur if no high-priority events occur.

Returns True if no higher-priority events have been . If it returns True, it prevents all other events from being considered.

Supply this function with one or more events names (strings). It returns true if those events have happened already, at any time in the game.

Supply this function with one or more events names (strings). It returns true if those events have executed, yesterday or before.

Probability is a float between 0.0 and 1.0. Returns True with the supplied probability, each time it is evaluated. (Each period.)

This defines a group from which only one event can be chosen. DSE randomly picks one of the elements from the group such that all of the other conditions are True. An event can only be in one group at a time. (Objects returned from here should not have operators applied to them.) The count is used to determine the relative likelyhood of an event in a group being chosen, with a count of 10 being 10 times more likely then a count of 1.

The objects returned from condition functions can be combined using the and, or, and not operators. For example:

    event('bachelor_party', event.depends('engaged_a') or event.depends('engaged_b'))

Call from Main

There are a few entry points that are used in main to call the event dispatcher. First of all, there is the function check_skip_period(), that should be called to determine if a period should be run at all. This should be called at the start of each period, and if it returns True the period should be skipped entirely.

The Ren'Py label events_run_period should be called to run a single period. Finally, the Ren'Py label events_end_day should be called to end a day. Ending a day stops skipping periods, and also changes the events that are considered executed by event.depends.

Day Planner (day_planner.rpy)

The day planner allows the user to pick choices for one or more periods of time. The user must pick one choice for each period, and may change his choice. When the user picks "Done Planning", the values corresponding to each of his choices are assigned to the variables corresponding to the different periods.


The dp_period and dp_choice functions are used to define periods and choices, respectively. These functions should be called from inside an init block, usually an init python block.

Defines a new period, with the given name and the given variable. Future calls to dp_choice will add choices to this period.

Defines a new choice within a period. Name is the name that is shown to the user. Value is the value that will be assigned to a variable if this is the selected choice. Enable and Show are expressions that determine if the choice will be enabled or shown, respectively. (If either expression evaluates to False, the choice is not selectable.)

This variable controls the title of the Done Planning button.

Calling the Day Planner

The day planner can be invoked by calling the day_planner label with a list of period names. When the day planner returns, the variables corresponding to those periods will have new values assigned to them.

If the label dp_callback exists, it will be called each time the day planner draws the screen. This can be used to add stats to the screen, by calling display_stats().


The rest of the files in the DSE distribution form an example game.

Conceptual Example

This is an illustrated example of how to implement variables for class periods:

These are arbitrary values so feel free to change them! This isn't fully documented source code, this is just showing what can be done.

$ weekday = "Monday"

This is a string containing a weekday. The "weekday" is a container that changes.

if weekday == "Monday":
    "Today is %(weekday)s."

For the purposes of this example, the time periods starting from 12 AM are treated as period 0. A variable with an integer contains the actual period which keeps track of the time.

So if it's 5 AM, then the actual period might be 10. If the player takes an action, it advances the actual period by the action's period cost and reduces the players energy by an equivalent amount.

$ actual_period = 10
$ player_energy = 48

Here, it's 5 AM and the player wakes up with full energy.

Let's say the player has the action of "Sleep In", "Eat Breakfast", and "Go to School Early". Sleeping In may cost 4 periods, but also restore energy. Eat Breakfast may cost 1 period. Go to School Early won't cost any periods.

if action == "Sleep In":
    $ rest = True
    $ period_cost = 4

Once the action is executed, have a function that will check to see if the player will fall asleep. First, we check to see if this is a rest action.

if rest == True:
#Execute an action and calculate its benefits, like below.
"I feel much more refreshed! {w=.5}But now I'm late for class... "
$ intelligence -= 1
$ player_energy += 32
$ miapoints += 1
$ eileenpoints -= 2
elif rest == False: 
    #This isn't a rest action, so let's check to see if the player passes out.
    if energy - period_cost <= 0: 
    #If say your current energy is 2 and the period cost is 3, 
    #this condition will be true
        action = "Passed Out" 
        #And so your next real action is passing out.  Unless you want to
        #do a hybrid action.
        #Run this function again except with the Passed Out credentials,
        #i.e. rest = True, period_cost = 20, resets energy.
    elif energy - period_cost > 0: 
        #There is enough energy to execute the action.
        energy -= period_cost 
        #Deduct the period_cost from the player's energy.    
        #Execute the action, calculate its benefits
        actual_period += period_cost
        #And a function that checks a period's rollover.