TimeSys

 

User’s Guide

 

 

TimeSys is a collection of interrelated modules that provide a mechanism for time-based game play, as opposed to the usual turn-based system.

 

Thanks to Michael J. Roberts for his many suggestions.

 

Copyright © 2001-2004 by Kevin Forchione. All rights reserved.


Contents

 

Getting Started.. 4

Tracking. 4

Actions. 8

Events. 13

Misc. 17

TimeSys Overview... 25

The TimePiece Class.. 27

TimePiece Properties Associated With Tracking. 27

setInitDateTime. 27

getInitJdn. 27

getInitClockRatio. 27

getInitDateTime. 28

getInitDateTimeString. 28

setCurrDateTime. 28

getCurrJdn. 28

getCurrClockRatio. 28

getCurrDateTime. 28

getCurrDateTimeString. 28

setCalendar. 28

setClock. 28

setTimeRate. 28

setDateVals. 28

setDayOfWeek. 28

setTimeVals. 28

getCalendar. 29

getClock. 29

getTimeRate. 29

getDateTime. 29

getDateVals. 29

getDayOfWeek. 29

getTimeVals. 29

cvtMinToGctu. 29

cvtGctuToMin. 29

TimePiece Properties Associated With Actions. 29

cvtValsToDateTime. 29

addToTime. 29

subtractFromTime. 29

addDateTimeToTime. 30

subtractDateTimeFromTime. 30

TimePiece Properties Associated With Events. 30

getInitDateTimeVals. 30

getCurrDateTimeVals. 30

getDateTimeVals. 30

cvtDtToGctu. 30

The Calendar Classes.. 31

Calendar Properties Associated With Tracking. 31

toDayOfWeek. 31

toDate. 31

toJulianDayNumber. 31

toDateTime. 31

The Clock Class.. 32

Clock Properties Associated With Tracking. 32

toTime. 32

toClockRatio. 32

toDateTime. 32

Clock Properties Associated With Actions. 32

compareRatios. 32

Clock Properties Associated With Events. 32

toSeconds. 32

toMinutes. 32

toHours. 32

secondsToNxtClockRatio. 32

minutesToNxtClockRatio. 33

hoursToNxtClockRatio. 33

The WallClock Class.. 34

Accessing the Global WallClock Instance. 34

The New Tokenizer Token Rule. 34

Setting the Date and Time Zero-point.. 34

Waiting.. 36

Actors and Waiting States. 36

Breaking the Waiting State. 37

Ways of Issuing the stopWaiting() Message. 37

Reacting to the Waiting State. 37

Waiting Events.. 38

Fuses and Daemons Defined in Events. 38

DateTimeFuse. 38

ClockTimeFuse. 38

ClockTimeDaemon. 38

DailyDaemon. 38

TimeEventDaemon. 38

DailyTimeEventDaemon. 38

Special Case Daemon Defined in Events. 38

TimeSysIdxEventMixIn. 38

CycleSenseDateTimeDaemon. 39

Special Case Daemons Defined in Miscellaneous. 39

TimeSysBigBenDaemon. 39

TimeSysSunDaemon. 39

 

 


Getting Started

TimeSys adopts a modular approach that allows you to tailor the degree of Time-oriented game play you desire in your game. Specifically, there are the following levels of implementation:

 

  1. Tracking
  2. Actions
  3. Events
  4. Miscellaneous

 

Tracking

The tracking modules facilitate the functions necessary for time-oriented display in game play. Time-oriented status display will show the passage of time in date & time terms as the player moves about the game world.

 

To implement tracking you will need to add the following modules to your game compilation after those for the TADS 3 system and library files:

 

  • ts_track.tl (library file)

 

Also #include the following header file to each of your game source files that need to reference TimeSys functions:

 

  • timesys.h

 

After compiling and running you should see something like the following in your game’s status line:

 

 

 

By default the “Wallclock” time is set to Friday, June 17, 1887 5:00 a.m. This is exactly 24 hours prior to the start of the Infocom game Sherlock: The Riddle of the Crown Jewels, and is the date of the London Times provided with the game materials.

 

As the PC moves around the game world time will progress in minute increments based on the actionTime set for each action:

 

 

You can also issue the <<time>> command, a system command that will display the time in the game window:

 


Actions

The actions modules facilitate the functions necessary for time-oriented waiting in game play. Time-oriented status display will show the passage of time in date & time terms as the player moves about the game world. In addition wait commands can be issued by the PC to any actor (including itself).

 

To implement actions you will need to add the following modules to your game compilation after those for the TADS 3 system and library files:

 

  • ts_track.tl (library file)
  • toksync.t (library extension)
  • contaction.tl (library file)
  • ts_actions.tl (library file)

 

Also #include the following header file to each of your game source files that need to reference TimeSys functions:

 

  • timesys.h

 

After compiling and running you should be able to issue wait commands to either the PC or an NPC:

 

 

You can issue many forms of wait commands such as:

 

        >wait 5

        >wait 7 minutes

        >wait 1 hour

        >wait 2 hours and 10 minutes

        >wait until 5:15

        >wait until 7:10 pm

        >wait until noon

 

The actions will be processed and the display updated to reflect the new game time:

 

 

You can also issue wait commands to an NPC. The command will be executed

 

 

The wait command is issued asynchronously to an NPC. In this case 1 minute of time is consumed by the issue of the action command by the PC, while Joe will wait 5 minutes.

 

 


Events

The events modules facilitate the functions necessary for time-oriented events in game play. Time-oriented status display will show the passage of time in date & time terms as the player moves about the game world and wait commands can be issued by the PC to any actor (including itself). In addition, fuses and daemons can be triggered in a time-oriented manner.

 

To implement events you will need to add the following modules to your game compilation after those for the TADS 3 system and library files:

 

  • ts_track.tl (library file)
  • toksync.t (library extension)
  • contaction.tl (library file)
  • ts_actions.tl (library file)
  • ts_events.tl (library file)

 

Also #include the following header file to each of your game source files that need to reference TimeSys functions:

 

  • timesys.h

 

After compiling and running you should be able to implement and execute time-oriented fuses and daemons:

 

 

 

By default the “Wallclock” time is set to Friday, June 17, 1887 5:00 a.m. This is exactly 24 hours prior to the start of the Infocom game Sherlock: The Riddle of the Crown Jewels, and is the date of the London Times provided with the game materials.

 

 

TimeSys implements a variety of time-oriented fuses and daemons. Like their TADS 3 ADV3 counterparts, these have sensory and non-sensory versions. The chief difference is that they are implemented based on dates and times and the game wallclock, rather than turns.

 

For instance, here is the fuse for the fire alarm displayed below:

 

        new AlarmFuse(1887, 6, 17, 5, 10, 0);

 

 

TimeSys fuses and daemons have the ability to issue continuous action interrupts and interrupt queries. The time the query is issued is displayed in the status line.

 

 

If the player character chooses to stop waiting  the query itself does not advance the game time.

 


Misc

The miscellaneous modules facilitate the functions necessary for sine specialized time-oriented events in game play. Time-oriented status display will show the passage of time in date & time terms as the player moves about the game world and wait commands can be issued by the PC to any actor (including itself). Fuses and daemons can be triggered in a time-oriented manner, and the miscellaneous modules implement specialized applications of TimeSys events.

 

To implement events you will need to add the following modules to your game compilation after those for the TADS 3 system and library files:

 

  • ts_track.tl (library file)
  • toksync.t (library extension)
  • contaction.tl (library file)
  • ts_actions.tl (library file)
  • ts_events.tl (library file)
  • ts_misc.tl (library file)

 

OR

 

  • toksync.t (library extension)
  • contaction.tl (library file)
  • timesys.tl (library file)

 

Also #include the following header file to each of your game source files that need to reference TimeSys functions:

 

  • timesys.h

 

After compiling and running you should be able to implement and execute the specialized time-oriented events:

 

 

The TimeSysBigBenDaemon and TimeSysSunDaemon handle specialized hour announcement and daylight function activities.

 

 

Among there specialized behaviors, the Big Ben daemon will summarize its hourly announcement when the player character’s waiting period spans two or more hours. When this happens the Big Ben daemon doesn’t query the actor for a waiting interrupt. By default the Sun daemon does query the actor for a waiting interrupt at the start of each of its event ranges, if the player character can see the sun.

 

 

When the player character is waiting the Sun daemon generates two messages if the player can see the sun. The first message is a waiting message, “While you were waiting…” and the second is the normal, non-delayed event message, “The sun comes up…”. Also the Sun daemon controls the lighting of each and every OutdoorRoom class location.

 

 

The Sun daemon “buffers” its responses when the player character cannot see the sun, so that it won’t announce its events if the player character cannot see the sun, but will announce its events at the point at which the player character can see the sun. For example:

 

 

Here the player character is in the kitchen, but the sun is only visible in the backyard. . .

 

 

Four hours have passed. The sun has come up and lit the backyard, but we don’t receive any announcement of it until we go out into the backyard.

 

 

With delayed announcements, the Sun daemon will announce any of its events as long as soon as the actor can see the sun and anytime after the event has occurred until the next event occurs. At that point the daemon will display a “You notice…” message.

 


TimeSys Overview

TimeSys employs very sophisticated algorithms to represent not only the time of day, but the year, month, day, and day of the week. The TimePiece class provides an interface between the TADS 3 ADV3 Schedulable class’ gameClockTime (measured in sequential “turns”) and the Calendar, and Clock class objects that translate turns into the appropriate calendar date and time values.

 

 

 

 

 

 

The process is straightforward. First a zero-point is established. The zero-point is the date/time value we give to the Schedulable game clock time value 0, which represents the start of the story.

 

As the story progresses the adv3 library automatically increments the Schedulable game clock, and TimeSys immediately adjusts the time of every instance of TimePiece to reflect the new date and time values. Every TimePiece is thus synchronized with the Schedulable game clock, though their zero-points and rate of time passage may differ.

 

When a command to “wait” is issued, the amount of time specified is converted into Schedulable game clock time units, and the actor who is to do the waiting has a new WaitAction queued to his command queue until the waiting period has passed or is interrupted.

 

When a TimeEventDaemon or TimeEventFuse is set, the amount of time specified in year, month, day, hour, minute, second, is also converted into Schedulable game clock time units, and the daemon or fuse then proceeds to behave  like any normal turn-based daemon or fuse.

 


The TimePiece Class

The TimePiece class is responsible for translating between the Schedulable game clock and its calendar and clock delegates.

 

 

 

 

The TimePiece class defines the following methods:

 

TimePiece Properties Associated With Tracking

 

setInitDateTime(calendarParms, clockParms, [rate]) – sets the initial datetime BigNumber value, the zero-point of this timepiece, and the rate at which time passes per game turn.

 

The calendarParms argument consists of a list [calendar, year, month, day]

The clockParms argument consists of a list [clock, hh, mm, ss]

The rate argument, if provided, should be a single integer or Bignumber value.

 

getInitJdn() – returns the Julian Day Number of this timepiece’s zero-point.

 

getInitClockRatio() – returns the clock ratio time-in-seconds/seconds-per-day of this timepiece’s zero-point.

 

getInitDateTime() – returns the datetime BigNumber value of this timepiece’s zero-point.

 

getInitDateTimeString() – returns a formatted string value of this timepiece’s initial datetime value.

 

setCurrDateTime(gctu) – sets the current datetime BigNumber value of this timepiece representing the game-clock-time-units passed.

 

getCurrJdn() - returns the Julian Day Number of this timepiece’s current datetime value.

 

getCurrClockRatio() – returns the clock ratio time-in-seconds/seconds-per-day of this timepiece’s current datetime value.

 

getCurrDateTime() – returns the datetime BigNumber value of this timepiece’s current datetime value.

 

getCurrDateTimeString() – returns a formatted string value of this timepiece’s current datetime value.

 

setCalendar(calendar) – sets this timepiece’s internal calendar conversion pointer to one of the Calendar class objects.

 

setClock(clock) – sets this timepiece’s internal clock conversion pointer to one of the Clock class objects.

 

setTimeRate(rate) – sets the ratio of minutes per game-clock-time-unit at which time passes in the game for this timepiece. If the rate has a fractional element, then time progresses in fractions of minutes per game-clock-time-unit. If the rate is negative then time goes backward in the game.

 

setDateVals(jdn) – sets this timepiece’s internal year, month, and day values associated with the Julian Day Number passed as calculated by the timepiece’s calendar conversion object.

 

setDayOfWeek(jdn) – sets this timepiece’s internal day-of-the-week value associated with the Julian Day Number passed as calculated by the timepiece’s calendar conversion object.

 

setTimeVals(clockRatio) – set this timepiece’s internal hour, minute, and second values associated with the clock ratio passed as calculated by the timepiece’s clock conversion object.

 

getCalendar() – returns this timepiece’s calendar conversion object.

 

getClock() – returns this timepiece’s clock conversion object.

 

getTimeRate() – returns this timepiece’s minutes per game-clock-unit BigNumber value.

 

getDateTime(year, month, day, hour, minute, second) – returns the datetime value computed from this timepiece’s calendar and clock conversion of the passed date and time arguments.

 

getDateVals(jdn) – returns a list [year, month, day] associated with the Julian Day Number as calculated by this timepiece’s calendar conversion object.

 

getDayOfWeek(jdn) – returns the day-of-week numeric value associated with the Julian Day Number as calculated by this timepiece’s calendar conversion object.

 

getTimeVals(clockRatio) – returns a list [hour, minute, second] associated with the clock ratio as calculated by this timepiece’s clock conversion object.

 

cvtMinToGctu(minutes) – converts minutes to game-clock-time-units.

 

cvtGctuToMin(gctu) – converts game-clock-time-units to minutes.

 

TimePiece Properties Associated With Actions

 

cvtValsToDateTime(days, hours, minutes, seconds, [actionTime]) – Returns the corresponding datetime value for the days, hours, minutes, seconds passed. If passed an Action's actionTime value then this will be converted to seconds and added to the datetime computation.

 

addToTime(days, hours, minutes, seconds) – Creates a clone of the timepiece then adds this amount to the clone’s initial datetime value and recomputes all values from this new zero-point. This method makes adjustments for any Action actionTime value. If we add 5 minutes to a timepiece’s clock time, this method will adjust for any action time affecting the Scheduler game clock time, so that exactly 5 minutes will be added. Returns the clone.

 

subtractFromTime(days, hours, minutes, seconds) – Creates a clone of the timepiece then subtracts this amount from the clone’s initial datetime value and recomputes all values from this new zero-point. This method makes adjustments for any Action actionTime value. If we subtract 5 minutes from a timepiece’s clock time, this method will adjust for any action time affecting the Scheduler game clock time, so that exactly 5 minutes will be subtracted. Returns the clone.

 

addDateTimeToTime(dt) – Creates a clone of this timepiece then adds the datetime argument to the clone's initial datetime value (zero-point). Returns the clone.

 

subtractDateTimeFromTime(dt) – Creates a clone of this timepiece then subtracts the datetime argument from the clone's initial datetime value (zero-point). Returns the clone.

 

TimePiece Properties Associated With Events

 

getInitDateTimeVals() – Returns the timepiece's initial date / time value list.

 

getCurrDateTimeVals() – Returns the timepiece's current date / time value list.

 

getDateTimeVals(jdn, clockRatio) – Returns the timepiece's date / time value list for the given julian day number and clock ratio.

 

cvtDtToGctu(beginDt, endDt) – Returns the game clock time unit associated  with the difference between a begining and  ending datetime

 

 


The Calendar Classes

The Calendar class serves as means of converting between various date representations and Julian Day Numbers. The Calendar class is further subclassed into GregorianCalendar and JulianCalendar.

 

The class defines the following methods:

 

Calendar Properties Associated With Tracking

 

toDayOfWeek(jdn) – returns the day of the week as a BigNumber value between 1 and 7 inclusive where Sunday = 1, Monday = 2, etc.

 

toDate(jdn) – converts the Julian Day Number to the corresponding calendar-specific date in a list [year, month, day].

 

toJulianDayNumber(year, month, day) – converts the calendar-specific date to the corresponding Julian Day Number.

toDateTime(year, month, day) – returns a datetime value for the calendar-specific date.

 


The Clock Class

The Clock class serves as means of converting between various clock time representations and clock ratios. The Clock class is not currently subclassed.

 

The class defines the following methods:

 

Clock Properties Associated With Tracking

 

toTime(clockRatio)converts the clock ratio to the corresponding clock-specific time in a list [hour, minute, second].

 

toClockRatio(hour, minute, second)converts the clock-specific time to the corresponding clock ratio.

 

toDateTime(hour, minute, second)  returns a datetime value for the clock-specific time.

 

Clock Properties Associated With Actions

 

compareRatios(startClockRatio, endClockRatio) – compares the two clock ratios. The comparison rounds both clock ratios to 5 decimal places.

 

If startClockRatio > endClockRatio         returns -1

If startClockRatio == endClockRatio       returns 0

If startClockRatio < endClockRatio         returns 1

 

Clock Properties Associated With Events

 

toSeconds(dt) – converts the datetime value to seconds.

 

toMinutes(dt) – converts the datetime value to minutes. All values are rounded to the nearest minute.

 

toHours(dt) – converts the datetime value to hours. All values are rounded to the nearest hour.

 

secondsToNxtClockRatio(startClockRatio, endClockRatio) – computes the seconds between 2 clock ratios. If startClockRatio == endClockRatio then secondsPerDay value is returned.

 

minutesToNxtClockRatio(startClockRatio, endClockRatio) – computes the minutes between 2 clock ratios. If startClockRatio == endClockRatio then secondsPerDay value converted to minutes is returned. All values are rounded to the nearest minute.

 

hoursToNxtClockRatio(startClockRatio, endClockRatio) – computes the hours between 2 clock ratios. If startClockRatio == endClockRatio then secondsPerDay value converted to hours is returned. All values are rounded to the nearest hour.


The WallClock Class

The WallClock class serves as the TimePiece class object that produces the status line display’s date and time values. It is the game’s clock and is meant to govern the functioning of daemons, fuses, and other game events.

 

Accessing the Global WallClock Instance

During the pre-initialization stage of program compilation, an instance of WallClock class is stashed in libGlobal.wallClock, and can then be accessed through the gWallClock macro.

 

The New Tokenizer Token Rule

During preinit, the token synchronizer adds a new tokenizer rule to the ADV3 library’s cmdTokenizer.rules_. The new token rule allows the tokenizer to handle time-oriented phrases such as “17:00” or “5:00 p.m.

 

Setting the Date and Time Zero-point

The base date and time are stored in the WallClock instance in the form of a date-time BigNumber instance. This serves as our zero-point from which time progresses. The base date and time are calendar-dependent.

 

 

Currently TimeSys supports both the Gregorian and Julian calendars.

 

 

An author can set the zero-point date and time by using the class’ setInitDateTime() method, or by setting the classes initDate_ and initTime_ properties. These properties are lists in the format:

 

initDate_       [calendar, yyyy, mm, dd]

initTime_      [clock, hh, mm, dd]

 

The calendar can be either GregorianCalendar or JulianCalendar. The clock can be Clock. The hh value should be set with 24-hour clock values, with midnight equal to 0, and 11 p.m. equal to 23.

 

The time rate is the rate at which time passes per game clock time unit and is measured in minutes. By default the time rate is 1 minute per game clock time unit (turns). The time rate can be modified using the setTimeRate() method, or by overriding the initRate_ property of WallClock.

 

The following would create a wallClock with an initial date of January 1, 2000, 2:00 p.m with a time rate of 1 minute per game clock time unit.

 

Modify WallClock

{

       initDate_     = [GregorianCalendar, 2000, 1, 1]

       initTime_     = [Clock, 14, 0, 0]

       initRate_     = 1

}

 

The base date and time can be changed at any time by sending gWallClock the message setInitDateTime([calendar, yyyy, mm, dd], [clock, hh, mm], [rate]). Currently the only calendars supported are GregorianCalendar and JulianCalendar. The only clocks supported are Clock.

 


Waiting

It would be a poor system indeed that simply translates turns into date and time values. The Infocom games that implemented time-based systems also allowed players to wait for specified intervals of time. TimeSys does the same. The system can handle commands such as:

 

>wait 5

>wait until 3:00

>wait until 10:00 pm

>wait for 3 hours and 10 minutes

>wait until midnight

>wait until noon.

 

Actors and Waiting States

Such commands given to NPCs have the effect of putting them into wait states that can be observed:

 

Entryway

This large, formal entryway is slightly intimidating: the walls are lined with somber portraits of gray-haired men from decades past; a medieval suit of armor, posed with a battle axe at the ready, towers over a single straight-backed wooden chair. The front door leads back outside to the south. A hallway leads north.

 

Joe is here.

 

>joe, wait 5

Joe waits...

 

>x joe

Joe is waiting.

 

>wait 10

Time passes...

 

>x joe

You see nothing unusual about Joe.

 

>

 

So Joe can be commanded to wait, while the PC goes about his business.  The library defines a WaitingActorState that dictates the behaviors of waiting actors. Whenever an actor obeys a command to wait it enters this waiting state and remains in that state until waiting is completed or terminated.

 

Breaking the Waiting State

The waiting state for any actor can be terminated by issuing the message stopWaiting(issuingActor, queryActor) to any actor. The queryActor argument applies only to the PC, and if it is true then the request “Do you want to continue waiting?” is issued, otherwise the waiting is terminated if the actor allows itself to be interrupted.

 

Ways of Issuing the stopWaiting() Message

Besides inserting this message into action code, an author can also make use of specialized fuses and daemons. Time Fuses and daemons are date/time based.

 

new DateTimeFuse(obj, prop, year, month, day, hour, minute, second, true);

 

So, in order to create a time event fuse set to stop me from waiting, and set for January 1st, 2002 at 5:00 pm, you would code the following:

 

new DateTimeFuse(me, &alertStopWaiting, 2002, 1, 1, 17, 0, 0, nil);

 

And then code the method alertStopWaiting() as follows:

 

modify me

{

       alertStopWaiting()

       {

              stopWaiting(me, true);

       }

}

 

Various time-based fuses and daemons are provided in ts_events.t

 

Reacting to the Waiting State

TimeSys provides methods on Actor to enable an actor to react to each phase of the waiting process.

 

execActionInitial(action) {}

execAction(action) {}

execActionCompleted(action) {}

execActionInterrupted(issuingActor, action) {}

 

Thus a message can be sent to the actor for the first execution of a wait state, repeated executions, the final execution, or an interrupted execution. The action involved is sent as well, in order to differentiate between waiting and some other continuous action.

 


Waiting Events

TimeSys provides several time-oriented daemons and fuses that allow the author to orchestrate time-oriented events.

 

Fuses and Daemons Defined in Events

These fuses and daemons are specially designed to work with continuous actions and allow for the interrupt of such actions.

 

DateTimeFuse – execute when the global wallclock reaches the specified date and time.

 

ClockTimeFuse – execute when the global wallclock reaches the specified future time. This fuse is not date-specific, and will execute within 1 day of having been set.

 

ClockTimeDaemon – runs every intervalSeconds from its initial execution time. This daemon will first execute when the global wallclock reaches the specified date and time.

 

DailyDaemon – runs 1 per day from its initial execution time. This daemon will first execute when the global wallclock reaches the specified date and time.

 

TimeEventDaemon – runs every intervalSeconds from its initial execution time. This daemon is not date-specific, and will execute within 1 day of having been set.

 

DailyTimeEventDaemon – runs 1 per day from its initial execution time. This daemon is not date-specific, and will execute within 1 day of having been set.

 

There are also Sensory versions of these daemons and fuses.

 

Special Case Daemon Defined in Events

These mix-in and daemon classes allow for particularly specialized behaviors.

 

 

TimeSysIdxEventMixIn – Defines an Indexed Event mix-in class that performs multiple events based on where the daemon is positioned in an actor's waiting period.

 

CycleSenseDateTimeDaemon -- Cyclical Sensory-context-sensitive datetime event daemon - this is a daemon with an explicit sensory context.  This is a specialized SenseDateTimeDaemon that processes "event objects". The daemon computes which event object to process, and then adopts the characteristics of the "event object" and processes the methods of the event object. In addition, if the player is not able to sense the obj_ at the time indicated for an event object, then a delayed method is executed for the event when the actor can sense the indicated obj_.

 

This daemon makes use of the TimeSysEventObjects that allow the daemon to process individual “event objects”, assuming the characteristics of each object at the time it is due to execute, and allowing the daemon to “delay” its execution through a secondary method if the actor doesn’t sense the event object’s sensory object

Special Case Daemons Defined in Miscellaneous.

 

TimeSysBigBenDaemon – Simulates announcement and behaviors similar to that of "Big Ben" in Infocom's "Sherlock: The Riddle of the Crown Jewels"

 

TimeSysSunDaemon – Simulates announcement and behaviors similar to that of the "sun" in Infocom's "Sherlock: The Riddle of the Crown Jewels"