Subtime

Overview

"Subjective Time" module. This implements a form of in-game time-keeping that attempts to mimic the player's subjective experience of time passing in the scenario while still allowing for occasional, reasonably precise time readings, such as from a wristwatch in the game world.

Both the subtime module and most of the text of this page of documentation are the work of Michael J. Roberts.


New Classes, Objects and Properties

In addition to a number of properties intended purely for internal use, this extension defines the following new classes, objects and properties:

Usage

Include the subtime.t file after the library files but before your game source files.

Create a number of ClockEvent objects in your game, each representing an event that occurs at a particular time. The Clock Manager automatically builds a list of all of these objects during pre-initialization, so you don't have to explicitly tell the clock manager about these. Whenever the story reaches one of these events, you should call the eventReached() method of the event object. This will set the clock time to the event's current time, and take note of how long we have until the next plot event. Alternatively, you can define the ClockEvent's reachedWhen property as a condition that becomes true (or a method that returns true) when this ClockEvent is reached. For this to work, the events.t module must be present in the game. If several ClockEvents have reachedWhen conditions that become true on the same turn, only the first one (i.e. the one with the earliest eventTime) will be reached on that turn (the next one will be reached on the next turn, and so on).

To create a ClockEvent object you need to give it a programmatic name and define its eventTime property. The eventTime property specifies the time at which this event occurs. This is expressed as a list with three elements: the day number, the hour (on a 24-hour clock), and the minute. The day number is relative to the start of the game — day 1 is the first day of the game. So, for example, to express 2:40pm on the second day of the game, you'd write [2,14,40]. Note that 12 AM is written as 0 (zero) on a 24-hour clock, so 12:05am on day 1 would be [1,0,5].

Thus, for example, to specify that when the player character first meets Bob the time is 2:40pm on the second day of the4 game you might define:

meetBobEvent: ClockEvent
    eventTime = [2, 14, 10]
;
 

Or, by taking advantage of the ClockEvent template defined in advlite.h you could abbreviate this to:

meetBobEvent: ClockEvent [2, 14, 10];
 

Either way, when the player character first meets Bob you would then call:

   meetBobEvent.eventReached();
 

Then, when you want to get the current time at any point in the game (when the player character looks at a clock, for example), you would call clockManager.checkTime(). This returns a list in the same format as ClockEvent.eventTime: [day,hour,minute]. Remember that between time checks, the game time clock is in a vague, fuzzy state, drifting along at an indeterminate pace from the most recent check. When this method is called, though, the clock manager is forced to commit to a particular time, because we have to give a specific answer to the question we're being asked ("what time is it?"). Therefore, you should avoid calling this routine unnecessarily; call it only when you actually have to tell the player what time it is - and don't tell the player what time it is unless they ask, or there's some other good reason.

You must create at least one ClockEvent in your game, or this module will cause a run-time error. You should in any case create a ClockEvent to set the time at the start of play. This will be the ClockEvent with the earliest eventTime; the earliest ClockEvent is automatically marked as reached at the start of the game; you don't need to call its eventReached() method or set its reachedWhen property.

If you want your game to start on a particular day (that is, it's important to track the date as well at the time), you can set the starting date (i.e. the date that counts as Day 1) by overriding the clockManager's baseDate property to a Date object; for example to start the game on August 24th 2015 you might write:

 modify clockManager
    baseDate = static new Date(2015, 8, 24)
 ;
 

If gameMain.gameStartTime is defined, the date defined there will be used as the starting date instead. If the starting date is not defined in either place, the game will notionally start on January 1st, 2000.

The clockManager works by advancing the notional time by 60 minutes per hundred turns (by default) until half the time until the next ClockEvent is reached. The passage of time per turn is then progressively slowed down to prevent the time of the next ClockEvent being reached prematurely. Note that time is only computed when clockManager.checkTime() is called (either directly or via clockManager.checkTimeFmt(fmt)).

If the notional rate of 60 minutes per 100 turns doesn't suit the pace of your game there are various ways in which you can change it. To change it globally for the entire game you can override the baseScaleFactor of clockManager to some other number of minutes per hundred turns, or else override the scaleFactor property of the clockManager for the number of minutes per hundred turns to use once the last ClockEvent is passed. For finer-grained control you can override either the scaleFactor or the turnsToEvent property of one or more ClockEvents. The ClockEvent's scaleFactor determines the baseScaleFactor the clockManager will use when the game is between the previous event and this ClockEvent. Alternatively, you can get the game to calculate a suitable scaleFactor for you by defining the ClockEvent's turnsToEvent property. This can be set to an estimate of the number of turns you expect a typical player to take to get from the previous ClockEvent to this one; the scaleFactor will then be calculated accordingly (on the basis of the estimated number of turns and the number of minutes between the two ClockEvents). It may well be difficult to estimate the number of turns a "typical" player will take, but given the way the clockManager works, it's probably better to underestimate than to overestimate this number.

Formatting the Time

If you want a string-formatted version of the time (as in '9:05pm'), you can call clockManager.checkTimeFmt(fmt), where fmt is a string specifying how you want the time to appear. Note that this method does not display anything; rather it returns a single-quoted string containing the current game time formatted in the way specified by fmt.

For a full list of possible format strings, consult the chapter on the Date intrinsic class in the TADS 3 System Manual. A few of the more convenient ones are listed below:

Note that these can be combined as needed, for example timeManager.formatDate('%A, %d-%b-%y %I:%M %P') might return the string 'Sunday, 22-Jun-14 03:30 PM', and of course this method may be used wherever you like in your game code, not just in the status line.



Rationale

It's always been difficult to handle the passage of time in interactive fiction. It's such a vexing problem that most IF avoids the problem entirely by setting the game action in a world that forgot time, where there are no working clocks and where night never falls. Like so many of the hard parts of interactive fiction, the difficulty here comes from the uneasy union of simulation and narrative that's at the heart of all IF.

In ordinary static narratives, the passage of time can swing between extremes: a scene that only takes moments of real-time within the story, such as an intense action scene or an extended inner monologue, can go on for many pages; and elsewhere in the same story, days and years can pass in the space of a sentence or two. Narrative time has no relationship to the length of a passage of text; narrative time is related only to the density of story-significant action.

It might seem at first glance that this break between in-story time and chapter length is a special privilege of the staticness of a book: the text just sits there on a page, unchanging in time, so of course there's no real-time anchor for the story's internal clock. But that can't be it, because we see exactly the same narrative manipulation of time in film, which is a fundamentally time-based medium. Films don't present their action in real-time any more than books do. What's more, film follows the same rules as text-based fiction in compressing and expanding story-time: many minutes of film can spool by as the last seven seconds tick off a bomb's count-down timer, while the seasons can change in the course of a five-second dissolve. Even Fox's "24," a television series whose entire conceit is that the action is presented in real-time, can be seen on closer inspection to hew to its supposed real-time anchors only every ten minutes or so, where the ads are inserted; in between the ads, the normal bag of tricks is liberally employed to compress and expand time.

In simulations, we have a different situation, because the pacing of the story is under the indirect control of the player. (I call the control indirect because it's not presented to the player as a simple control knob with settings for "slower" and "faster." The player can directly control how quickly she enters commands, but this has only a very minor effect on the pace of the story; the major component of the story's pacing is the player's rate of absorbing information from the story and synthesizing solutions to its obstacles. In most cases, this is the gating factor: the player is motivated, by a desire to win the game, to go as fast as possible, but is unable to go any faster because of the need to perform time-consuming mental computation. Thus the player's control is indirect; there's the illusion of a control knob for "faster" and "slower," but in practice the knob's setting is forced to a fixed value, determined by the player's ability to solve puzzles: no player ever wants to choose a "slower" setting than they have to, and the "faster" settings are unavailable because of the gating mental computation factor.)

But even so, the player in a simulation does have complete control over the specific actions that the player character performs. The player makes the character walk around, examine things, try a few random verbs on an object, walk around some more, and so on. All of these things clearly ought to take time in the simulation. The question is: how much time?

The most obvious approach is to tie the passage of game-time to the actions the player character performs, by making each turn take some amount of time. At the simplest, each turn could take a fixed amount of time, say 1 minute. A more sophisticated approach might be for some kinds of actions to take more time than others; walking to a new room might take 2 minutes, say, while examining a wall might take 30 seconds. Some early IF games took this approach. Infocom's Planetfall, for example, has a cycle of day and night that's tied to the number of turns taken by the player.

Unfortunately, such a direct mapping of turns to game-time hasn't proved to be very satisfactory in practice. This approach doesn't seem to make the treatment of time more realistic -- in fact, it's just the opposite. For example, in Planetfall, as objective game-time passes, it's necessary to eat and sleep; the game has a limited supply of food, so if you spend too much time exploring, you can lock yourself out of victory simply by exhausting all available food and starving to death. The workaround, of course, is to restart the game after you've explored enough to know your way around, at which point you can cruise through the parts you've been through before. The end result is that we get two opposite but equally unrealistic treatments of time: on the first pass, far too much objective time passes as we wander around exploring, given how little we accomplish; and on the second pass, far too little objective time elapses, given how rapidly we progress through the story.

One problem would seem to be that verbs are far too coarse a means to assign times to actions: picking up five small items off a table might take almost no time at all, while picking up the table itself could require a minute or two; walking to the next room might take ten seconds, while walking across an airport terminal takes ten minutes. An author could in principle assign timings to actions at the level of individual verb-object combinations, but this would obviously be unmanageable in anything but the tiniest game.

A second problem is that it might be better to see the player's actual sequence of command input as somewhat distinct from the player character's subjective storyline. In the turns-equals-time approach, we're essentially asserting a literal equivalence between the player's commands and the player character's story. In typical IF game play, the player spends quite a lot of time exploring, examining, and experimenting. If we were to imagine ourselves as an observer inside the game world, what we'd see would be highly unnatural and unrealistic: no one actually behaves like an IF player character. If we wanted to achieve more realism, we might consider that the medium imposes a certain amount of this exploration activity artificially, as a means for the player to gather information that would, in reality, be immediately and passively accessible to the player character - just by being there, the player character would instantly absorb a lot of information that the player can only learn by examination and experimentation. Therefore, we could say that, realistically, a lot of the exploration activity that a player performs isn't really part of the story, but is just an artifact of the medium, and thus shouldn't count against the story world's internal clock.

One ray of hope in all of this is that the human sense of time is neither precise nor objective. People do have an innate sense of the passage of time, but it doesn't work anything like the turns-equals-time approach would have it. People don't internally track time by counting up the actions they perform. In fact, people generally don't even have a very precise intuitive sense for how long a particular action takes; in most cases, people are actually pretty bad at estimating the time it will take to perform ordinary tasks. (Unless you're asking about areas where they have a lot of hands-on experience actually timing things on a clock, such as you might find in certain professions. And a lot of people aren't even good at that — just try getting a decent schedule estimate out of a programmer, for example.)

The human sense of time is not, then, an objective accounting of recent activity. Rather, it's a highly subjective sense, influenced by things like emotion and focus of attention. A couple of familiar examples illustrate how a person's subjective experience of time can wildly depart from time's objective passage. First, think about how time seems to stretch out when you have nothing to do but wait for something, like when you're stuck in an airport waiting for a late flight. Second, think about how time flies by when you're doing something that intensely focuses your attention, like when you're playing a really good computer game or you're deeply engaged in a favourite hobby: hours can seem to disappear into a black hole. Being bored tends to expand time, while being intensely occupied tends to compress it. There are other factors that affect the subjective time sense, but occupation of attention seems to be one of the most important.

So where does this lead us? If it's not clear which "turns" are part of the story and which are merely artifacts of the medium, the turn counter can't be a reliable source of game-time. But if the human sense of time is inherently subjective, then maybe we're better off tying the passage of time to something other than the raw turn counter anyway.

The key to our solution is to treat the turn counter as correlated to the player's subjective time, rather than the story's objective time. In other words, rather than saying that a LOOK command actually takes 30 seconds in the game world, we say that a LOOK command might *feel* like it takes some amount of time to the player, but that the actual time it takes depends on what it accomplishes in the story. How much time really passes for a single LOOK command? It all depends on how many LOOK commands it takes to accomplish something.

So, if the player spends long periods of time exploring and frobbing around with objects, but not making any progress, we've had a lot of subjective time pass (many turns), but very little story time (few story-significant events). This is exactly what people are used to in real life: when you're bored and not accomplishing anything, time seems to drag. If the player starts knocking down puzzles left and right, making great progress through the story, we suddenly have a feeling of little subjective time passing (few turns) while story time is flying past (many story-significant events). Again, just what we're used to in real life when deeply engaged in some activity.

This basic approach isn't new. Gareth Rees's Christminster includes plot elements that occur at particular clock-times within the story, and a working clock-tower to let the player character tell the time. Time advances in the game strictly according to the player's progress through the plot (i.e., solving puzzles). At key plot events, the clock advances; at all other times, it just stays fixed in place. The game works around the "precision problem" (which we'll come to shortly) with the somewhat obvious contrivance of omitting the minute-hand from the clock, so the time can only be read to the nearest hour. Michael Gentry's Anchorhead also ties events to the story's clock-time, and provides a story clock of sorts that advances by the player's progress in solving puzzles. Anchorhead handles the precision problem by using an even coarser clock than Christminster, specifically the position of the sun in the sky, which I think can only be read to a precision of "day" or "night." The precision-avoiding contrivance here is much less obvious than in Christminster, and it especially helps that the setting and atmosphere of the game practically demand a continuous dark overcast.

But what if we wanted a normal clock in the game - one with a minute hand? This is where the precision problem comes in. The problem with subjective time is that, in the real world, a tool-using human in possession of a wristwatch can observe the objective time whenever she wants. In games like Anchorhead and Christminster, the clock only advances at key plot points, so if we permitted the player character a timepiece with a minute hand, we'd get unrealistic exchanges like this:

   
>x watch
It's 3:05pm.
    
>east. get bucket. west. fill bucket with water. drink water. east.
... done ...
  
>etc, etc, etc...
... done ...
    
>x watch
It's 3:05pm.
 

That's why Christminster doesn't have a minute hand, and why Anchorhead doesn't have clocks at all. When the time is only reported vaguely, the player won't notice that the clock is frozen between plot events: it's not a problem when we're told that it's still "some time after three" for dozens of turns in a row.

This is where this implementation tries to add something new. We try to achieve a compromise between an exclusively narrative advancement of time, and an exclusively turn-based clock. The goal is to have the story drive the actual advancement of the clock, while still giving plausible time readings whenever the player wants them.

The compromise hinges on the way people use clocks in the real world: a person will typically glance at the clock occasionally, but not watch it continuously. Our approach also depends upon the observation that people are generally bad at estimating how long a particular activity takes. If people are bad at judging the time for one activity, then they're even worse at judging it for a series of activities. This is an advantage for us, because it means that the "error bars" around a player's estimate of how long something ought to be taking are inherently quite large, which in turn means that we have a large range of plausible results — as long as we don't say it's 3:05pm every time we're asked.

Here's the scheme. We start by having the game assign the times of important events. The game doesn't have to tell us in advance about everything; it only has to tell us the starting time, and the game clock time of the next important event in the plot. For example, the game could tell us that it's now noon, and the next important plot point will be when the player character manages to get into the castle, which happens at 6pm, just as the sun is starting to set. Note that the story asserts that this event happens at 6pm; it doesn't matter how many turns it takes the player to solve the puzzles and get into the castle, since *in the story*, we always get in at 6pm.

If the player looks at a clock at the start of the game, they'll see that it's noon. The clock module then starts keeping track of the number of turns the player takes. The next time the player looks at a clock, we'll check to see how many turns it's been since the last look at the clock. (We'll also consider plot points where the story actually sets the clock to be the same as the player looking at the clock, since these have the same effect of making us commit to a particular time.) We'll scale this linearly to a number of minutes passing. Then, we'll crank down this linear scaling factor according to how close we're getting to the next event time - this ensures that we'll leave ourselves room to keep the clock advancing without bumping up against the next event time. So, we have a sort of Zeno's paradox factor: the closer we get to the next event time, the slower we'll approach it.

The point is to create a plausible illusion of precision. A player who checks infrequently, as should be typical, should see a plausible series of intermediate times between major plot points.

One final note: if you want to display a clock in the status line, or show the current time with every prompt, this is the wrong module to use (use the Objective Time module instead). A key assumption of the scheme implemented here is that the time will be checked only occasionally, when it occurs to the player to look. If the game is constantly checking the time programmatically, it'll defeat the whole purpose, since we won't be able to exploit the player's presumed uncertainty about exactly how much time should have elapsed between checks.

This covers most of what you need to know to use this extension. For additional information see the source code and comments in the subtime.t file.