DEVELOPMENT CORNER
DSE Tutorial
by ShiraiJunichi

The Files
main.rpy
events.rpy
A More Subtle Use of the Day Planner
Using Arcs to Structure Your Game: Basic Theory

  
The Files
The executable file to start a dse game is named "run_dse.exe". It loads the game stored in the "dse" folder. Here's a quick rundown of the files within the "dse" folder:

main.rpy This file is the one most like the "script.rpy" file of a normal ren'py game. You can think of it sort of like the skeleton of your game, as it contains very little of the actual game, but provides basic flow control for events to run.
events.rpy This file contains all the events that will run during the game. It will contain the majority of your code.
event_dispatcher.rpy Implements the code needed to actually run events.
day_planner.rpy Provides structure for the days in the game and creates a GUI for selecting what events to do during a given day.
old_day_planner.rpy An alternate version of the day planner
stats.rpy Defines the Stat class- useful for storing numerical variables in your game.

Of these five files, only two of them need to be edited- main.rpy, and events.rpy.


  
main.rpy
There are six parts of the main.rpy file:

  • The init block
  • The start block
  • The day block
  • A block for each period of the day
  • The night block
  • The dp_callback block


  • The init Block
    Here is where you'll put all your customizations- such as a frame image, text colors, config variables, etc. You can also include characters and images, but those may be more properly placed inside the "events.rpy" file. One thing you can put here that's unique to the DSE is the group of dp (day planner) variables. In order to set those, you'll need a basic understanding of how days are structured by the day planner.

    - Day structure
    Every day has a given number of periods. Each period is specified by a string (such as "Morning"). Each period of the day has a corresponding variable telling what action is being done at that part of the day, and a set of possible actions that variable could be. Here's the default values of the dp variables, as found in the day_planner.rpy file:

            # A list of the periods of the day that are present in the
            # day planner. This can be changed at runtime.
            dp_period_names = [ "Morning", "Afternoon", "Evening" ]
    
            # A list of variables that will take the selected choices
            # for each of the periods named above.
            dp_period_vars = [ "morning_act",
                               "afternoon_act",
                               "evening_act", ]
    
            # A map from the period names to a list of the activities
            # available for that period. This may be conputed before
            # each call to day_planner. Each entry is a tuple, where
            # the first element is the name shown to the user, and the
            # second is the name assigned to the variable for that
            # period.
            dp_period_acts = {        
                'Morning': [
                ( 'Attend Class', "class" ),
                ( 'Cut Class', "cut"),
                ],
                
                'Afternoon': [
                ( 'Study', "study" ),
                ( 'Hang Out', "hang" ),
                ],
                
                'Evening' : [
                ( 'Exercise', "exercise" ),
                ( 'Play Games', "play" ),
                ],
                }

    With these values, the first period of the day is "Morning". The variable that stores the action is morning_act. The possible values that morning_act can take is "class" and "cut". The strings 'Attend Class' and 'Cut Class' are what is displayed to the user when choosing the corresponding action.

    The start Block
    Here you put code you only want to run once- at the very beginning of the game. First thing it should do is initialize the event engine. It should also initialize the day variable to 0, create any Stat objects you plan on using and set which stats should be displayed by the day planner.

        # Required to initialize the event engine.
        call events_init from _call_events_init_1
    
        # Initialize the default values of some of the variables used in
        # the game.
        $ day = 0
    
        # Initialize the statistics used by the game. 
        $ strength = Stat('Strength', 10, 100)
        $ intelligence = Stat('Intelligence', 10, 100)
    
        # Pick the stats to show to the user.
        $ shown_stats = [ strength, intelligence ]
    	

    - Stat Objects
    The implementation of the Stat object is within the stats.rpy file. A stat object takes three parameters- name, current value, and max value. A stat object will automatically keep a statistic's value between 0 and the max value (inclusive).

    The day Block
    Code placed here will run at the beginning of every day. At the very least you'll want to increment the day variable by one. You may want to notify the user what day it is. And if you're using the GUI dayplanner you'll want to set all of your period variables to None, and call the day planner.

        # Increment the day it is.
        $ day += 1
    
        # We may also want to compute the name for the day here, but
        # right now we don't bother.
    
        "It's day %(day)d."
    
        # Here, we want to set up some of the default values for the
        # day planner. In a more complicated game, we would probably
        # want to add and remove choices from the dp_ variables
        # (especially dp_period_acts) to reflect the choices the
        # user has available to him.
    
        $ morning_act = None
        $ afternoon_act = None
        $ evening_act = None
    
        # Now, we call the day planner, which may set the act variables
        # to new values. 
        call day_planner from _call_day_planner_1
    	

    You may not want the player to have the exact same choices of what to do for every day of the game. You can change the choices available to the player by altering the dp_period_acts variable within this block. Note, however, that this code will be run at the beginning of each day- so simply altering the dp_period_acts variable will change it for every day. You'll need to condition it on some boolean expression, or incorporate variables into the constructor. IMPORTANT! If you plan on changing any of the dp variables at runtime, you'll need to move the dp variable declarations outside of the init block and place them somewhere else!

    Blocks for Each Period of the Day
    For organizational purposes, you should have a block for each period of the day, though it will still work if you don't. The convention is to label each block after the period it is for. So for the block governing the "Morning" period, you could name the label morning. Unless it's the first period in the day you'll want to check if you should skip this period (some events may require you to skip periods- but an event can't make you skip the first period of a day). Then you may wish to notify the user of what period it is. Then you'll want to set varibles so that they are current. Then you run events for the current period.

    	label afternoon:
    
        # It's possible that we will be skipping the afternoon, if one
        # of the events in the morning jumped to skip_next_period. If
        # so, we should skip the afternoon.
        if check_skip_period():
            jump evening
    
        # The rest of this is the same as for the morning.
    
        centered "Afternoon"
    
        # set variables so they are current
        $ period = "afternoon"
        $ act = afternoon_act
    
        # start running events for this period
        call events_run_period from _call_events_run_period_2
    	

    The last line ends in a "2" because it is the second period of the day. Change it for each period of the day accordingly.

    The night Block
    This block will run after all events of the day are finished- no events run during this period.

        # This is now the end of the day, and not a period in which
        # events can be run. We put some boilerplate end-of-day text
        # in here.
    
        centered "Night"
    
        "It's getting late, so I decide to go to sleep."
    
        # We call events_end_day to let it know that the day is done.
        call events_end_day from _call_events_end_day_1
    
        # And we jump back to day to start the next day. This goes
        # on forever, until an event ends the game.
        jump day
    	

    The night Block
    A callback called from the day planner. It can be used to show dialog and stats to the user when the day planner is shown. The following code does just that:

        $ narrator("What should I do today?", interact=False)
    
        call stats_show from call_stats_show_1
    	

      
    events.rpy

    If you didn't declare your character objects, images, and transitions in the "main.rpy" file, you can declare them in an init block within the "events.rpy" file. It makes a bit of sense to put them within this file instead, because they will be used more here. Besides those declarations, this file is basically a series of init blocks creating event objects, and labeled blocks that correspond to those created events. Those labeled blocks will contain the code that should be run when their corresponding events are selected to run. After you learn how to create events, writing this file becomes a snap.

    Creating Event Objects
    Events must be declared within init blocks. This is the basic way of creating an event:

    $ event('name_of_event', 'boolean_expression',
    	event.depends("event1"), event.depends("event2"),
    	event.solo(), event.once(), priority=50)

    The first string is the name of the event. When this event is chosen to run, it will look for a label of the same name. The second string is an expression that will evaluate to a boolean. If it evaluates as false, this event can't run. You could use something like... "act == 'exercise' and strength >= 50". The event.depends("some_event") parameters will prevent this event from running unless the input event(s) ran yesterday, or before. You can include as many, or as few, depend statements as you want. The event.solo() parameter means that if one event has already ran during this period, this event won't run. However, it won't stop events from running after it, if they aren't solos. The event.once() parameter means that this event will only execute once during the entire game. And lastly, the priority parameter sets the priority of this event. The lower the number, the higher the priority. Higher priority events will execute before lower priority ones. If no priority is given, it defaults to 100.

    Note: if two events are both able to run and have the same priority, which one gets priority is undefined. Avoid this scenario!

    Writing Blocks for Events
    Writing blocks for events is just like regular Ren'Py code. However, there are three things I'd like to mention:

  • Make sure the label is named after the event it is for.
  • At the end of the block, either return, or restart ren'py. Otherwise, Ren'py will execute the blocks for other events that are written below the event that just ran.
  • Consider using the skip_periods variable. By using the skip_periods variable, you can make certain events last more than one period. Note, however, that no event can last overnight or take part of the next day.


  •   
    A More Subtle Use of the Day Planner

    The use of the GUI part of the day planner is actually completely optional. By omiting use of the GUI, you can make the flow of your game much more transparent, but still take advantage of the event engine.

    Steps to separating from the day planner GUI:

  • Ignore the dp_period_vars variable
  • Ignore the dp_period_acts variable
  • Don't even create variables that would correspond with the strings in the dp_period_vars variable - you won't need them.
  • Omit the code that calls the day planner
        call day_planner from _call_day_planner_1
    	
  • Omit the dp_callback block within the main.rpy file


  • You'll want to make sure that you've declared the act variable somewhere in your code (probably the start block) before you make any calls to run events- otherwise you'll run into some errors.

    To make up for disabling the day planner GUI, you'll need to have events themselves change the act variable. Most likely, one variable won't be sufficient to store all the information you need to choose which events should run. I suggest making multiple variables that are conditions for events and are changed by the running of certain events.


      
    Using Arcs to Structure Your Game: Basic Theory

    It can be a bit mind boggling trying to imagine a jumble of events coming together, and somehow forming a game. This can be demystified with the idea of an event arc- a series of events where each one is dependant upon the previous one. With an arc, you are guaranteed that all events in the arc will execute in the proper order.

    A simple game could have two or three arcs that are relatively independant of each other. Each of these arcs would start at the beginning, and end at the end. It would be easy for the developer to write the game one arc at a time. Such a game would be fairly linear. The example dse game bundled with ren'py is a good example of this type of game.

    A more complex (and better) game would contain arcs that are intertwined. Progress within one arc could affect progress within another one. Perhaps some arcs begin halfway through the game, and perhaps others will finish subtantially before the game is over. Programming this kind of game is much more difficult, as you'll want to program all arcs concurrently. But it will help if you can keep a picture in your mind of all these arcs, and how they fit together, and affect each other. You'll probably benefit from drawing a visual map of all the arcs in your game.

    Arc Behavior: Events and Outcomes
    An exploration of the different ways arcs behave can give ideas about how to intertwine the ars in your game. Here is a list of actions an arc can take:

  • Start
  • Progress
  • Pause (progress is not currently possible, but may be made possible later in the game)
  • End prematurely (progress is no longer possible, but the arc didn't finish to completion)
  • Spawn another arc (one arc becomes two)
  • Fork (arc can progress only one of two (or more) ways)
  • End


  • If an arc takes these actions based solely on decisions made in the day planner, or choices made in events within that arc, it is still relatively independant. To intertwine the arcs, you'll want the actions of one arc to affect another one. For example, when one arc spawns another one, that could trigger an entirely different arc to end prematurely. You can also use choices made in events from one arc affect other arcs, in order to help network the arcs together.

    The stronger you weave the arcs in your game, the more seamless your game will seem.


     
    ShiraiJunichi is an up-and-coming ren'ai developer working on his first ren'ai game.

    "DSE Tutorial" © 2005 ShiraiJunichi. All rights reserved.

     


    Content© 2005 RenaiGames.net & RenaiGames.com, unless otherwise specified. All rights reserved.