Date and Period

Many games where the player can freely roam around track days and the period within the day. Here is a simple example that implements this in a flexible way. It is designed to be convenient to use with self-voicing and can be translated. The filenames mentioned are just suggestions, feel free to change them to suit your layout.

Days of week, periods of day

The first step is to define the days of the week, and the periods within a day. The strings are marked with the _() function to make them eligible for translation. These are kept as defines in case a later version of the game adds new or different periods to the day.

util/datePeriod.rpy

    # Name the days of the week.
    # Could be added to if you want an eight day week for example.
    #
define  daysOfWeek = (
        _("Monday"),        # 0
        _("Tuesday"),       # 1
        _("Wednesday"),     # 2
        _("Thursday"),       # 3
        _("Friday"),        # 4
        _("Saturday"),      # 5
        _("Sunday"),        # 6
        # _("Octoday"),     # 7
    )

    # Name the periods of a day.
    # Can be added to or shortened as needed.
    #
define  periodsOfDay = (
        _("Breakfast"),     # 0
        _("Morning"),       # 1
        _("Lunchtime"),     # 2
        _("Afternoon"),     # 3
        _("Evening"),       # 4
        _("Night"),         # 5
    )

Variables

Next, declare the variables using default to make them part of the save game. Use integers to track the day number, day of week, and period within the day. Two string variables are probably useful to print the human-readable version of the day of the week and period within the day:

vars.rpy

default day = 1             # Day number at start of game
default dayOfWeek = 0       # Day of week at start of game
default period = 0          # Period of day at start of game

default dayOfWeekStr = daysOfWeek[dayOfWeek]    # Current day of week string
default periodStr = periodsOfDay[period]        # Current period string

Updating period

To update the variables conveniently and consistently add a couple of Python functions to the end of datePeriod.rpy.

addperiod(delta=1, rollDay=True)
Used to advance the time period. The default is by one period, and to increment the day if advancing past the last period of the day, so Monday Night will change to Tuesday Morning. If rollDay is False then it won't advance the day, instead stopping at the night period. Negative values can also be used for delta if needed causing time to rewind. It updates dayOfWeekStr and periodStr. When self-voicing is on it announces the change of period, or the change of day and period.
nextDay(delta)
Used to advance the day and set the period to the first of the day. If delta is negative it will rewind by that many days. It updates dayOfWeekStr and periodStr. When self-voicing is on it announces the change of day and period.
util/datePeriod.rpy (cont)

init python:

    def addPeriod(delta=1, rollDay=True):
        """
        Advance a number of periods.

        Updates periodStr and dayOfWeekStr too.

        :param delta: Number of periods to advance (default 1)
        :param rollDay: True (default) to advance to the next day,
            False to stop at last period of current day

        NOTE: delta can be negative if you need to travel backwards in time.
        """
        global period, day, dayOfWeek, dayOfWeekStr, periodStr
        oldDay = day
        periods = len(periodsOfDay)
        period += delta
        if rollDay:
            # Use mod math to advance period, day, dayOfWeek.
            day += period // periods
            dayOfWeek += period // periods
            period %= periods
            dayOfWeek %= len(daysOfWeek)
            dayOfWeekStr = daysOfWeek[dayOfWeek]
        else:
            if period >= periods:
                period = periods - 1
            elif period < 0:
                period = 0
        periodStr = periodsOfDay[period]
        # Announce time/day change if self-voicing.
        if day == oldDay:
            alt(_("It is now [periodStr!t]."))
        else:
            alt(_("It is now [periodStr!t] of day [day], a [dayOfWeekStr!t]"))


    def nextDay(delta=1):
        """
        Advance to the start of the next day.

        Updates periodStr and dayOfWeekStr too.

        :param delta: Number of days to advance (default 1)

        NOTE: delta can be negative if you need to travel backwards in time.
        """
        global period, day, dayOfWeek, dayOfWeekStr, periodStr
        period = 0
        day += delta
        dayOfWeek += delta
        dayOfWeek %= len(daysOfWeek)
        dayOfWeekStr = daysOfWeek[dayOfWeek]
        periodStr = periodsOfDay[period]
        # Announce time/day change if self-voicing.
        alt(_("It is now [periodStr!t] of day [day], a [dayOfWeekStr!t]"))


By using integer division and modulus operations with the length of the periods and days of week tuples the length of either can be changed and aren't hard-coded in the date and period code. A side-effect of this approach is it also works correctly with negative values of delta.

Additional functions

A couple of additional functions will prove useful. The isLastPeriod() function returns True if the current period is the last of the day. The isWorkDayPer() determines if the current day of week is a work day (so Monday through Friday). You can add more as needed by your story.

util/datePeriod.rpy (cont)

    def isLastPeriod():
        """
        Is this the last period of the day?

        :return: True if it is the last period.
        """
        return period == len(periodsOfDay) - 1



    def isWorkDayPer():
        """
        Is this a work day?

        :return: True if it is.
        """
        return dayOfWeek <= 4

Date/period screen

Displaying the day, day of week, and period can be done with a custom screen. The text area string is marked with the _() function to mark it as translatable, and the two string interpolations have the !t to have the translations used for the day name and period. When self-voicing is on the change of day or period has already been announced by the Python functions that update it so supply an empty string as the alternative text so it isn't read out all the time.

screens/datePeriodScr.rpy

    # Simple screen top right to show day, day of week (first three letters),
    # and period.
    #
screen datePeriodScr():
    frame:
        xalign 1.0
        yalign 0.0
        text _("Day [day] [dayOfWeekStr:.3s!t] [periodStr!t]"):
            alt ""

Example

Here's an example use of this system where the player has a waking dream about being too ill to go to work. It assumes there's a character pc that represents the player's character. Call this example from label start to run it.


    #
    # Example using date/period.
    #
label exDatePeriod:
    show screen datePeriodScr
    pc "Urrgh, Mondays. Who needs them?"

    "You stay in bed, feeling ill."
    $ addPeriod()

    "You manage to eat a little chicken soup, take an aspirin, and go back to bed."
    $ addPeriod(2)

    "You wake and check your socials, but it is all too much, and you let sleep take you again."
    $ addPeriod(5)

    "Wait ... It's lunchtime? How long did I sleep?"
    "No ... this can't be happening. Am I having a lucid dream?"
    $ addPeriod(-8)
    "Your alarm goes off, time to get ready for work."

    "End."
    hide screen datePeriodScr
    return