Advancing time without interfering with scheduled events (scenes beginning and ending)

I want to be able to arbitrarily advance the time of day, either at the player’s request (like a “wait 10 minutes” command), because their action is something that should take awhile (for example “search notes for [topic]” should probably take longer than “examine kitchen sink”), or just because an event has occurred that takes some time.

I realize it’s possible to just say now the time of day is 12:01 PM or whatever. However, I want to make sure that amy running scenes are properly updated, and I don’t want it to clobber Every Turn rules, either. (Actually, something that happens in an Every Turn rule should probably be given the opportunity to cancel the wait early, at least if it’s player-initiated, but that’s not really related to my issue here.)

So far, I’ve implemented the first of those – an action that waits for a specified period of time. It works really well… until the end of the day, that is. Once the time of day wraps around to the next day, scenes that are ongoing frequently stop working. For example, if they were supposed to end during the wait, they don’t; and the scenes command notes that the time they’ve been playing has decreased and is often negative. Not all scenes will break, however, only the ones that have been running for more than a certain length of time and which depend on time to determine whether they’ve ended. It also seems to break down if I wait 24 hours or more, which isn’t that big of a deal for player-initiated waits (I’d probably set a maximum time) but might become a problem if I want to do a “you got knocked out cold for 3 days” event.

I’m currently working around it by making sure any especially long-running scenes don’t end based on time of day or time since they began, but I wondered if there’s a way to fix the issue itself, whether it’s a bug in the standard rules or I’m doing something wrong in my code

This is the implementation of my waiting action. (I don’t remember why I have an “advancing time” variable.) That code would probably be moved to a phrase later so I can invoke it from other places, but this is fine for testing that it works.

Advancing time is a truth-state that varies.

Waiting more is an action applying to one number.

Understand "wait for/-- a/an/-- [a time period]" or "z [a time period]" as waiting more[ when the use waiting for a duration command option is active].

Carry out waiting more (this is the standard waiting more rule):
	now advancing time is true;
	[This block allows waiting more than a day, but I don't think that's useful…]
	[let the days be the time understood divided by 24 hours;
	while the days are greater than zero:
		let the next day be the time of day minus one minute;
		while the time of day is not the next day:
			follow the turn sequence rulebook;
		follow the turn sequence rulebook;
		decrease the days by one;]
	[This is the most important part of the logic.]
	let the target time be the time of day plus the time understood;
	if the time understood is at least 10 minutes:
		let the target time be the target time plus a random time from -5 minutes to 5 minutes;
	decrease the target time by one minute;
	while the time of day is not the target time:
		follow the turn sequence rulebook;
	now advancing time is false.

Report waiting more (this is the report waiting more rule):
	say "You wait for about [the number understood in words] minutes."

I was fiddling around with that code in a small test scene, something like this, playing around with the conditions for when the scenes begin and end, and using the scenes command followed by various variations on z 12 hours.

Advancing time is a truth-state that varies.

Waiting Area is a room. The mystical item is a thing. At 9:00 PM: say "[The mystical item] appears!"; now the mystical item is in the Waiting Area.

Happenstance and Opportunity are recurring scenes. Happenstance begins when play begins.Happenstance begins when Happenstance ends. Happenstance ends when the time since Happenstance began is at least 90 minutes. Opportunity begins when the player carries the mystical item. Opportunity ends when the time since Opportunity began is at least 90 minutes.

When play begins:
	now the command prompt is "[time of day]> ".

One other thing I wondered is whether it’s even okay to invoke the turn sequence rulebook from an action, as it seems to violate the constraints laid out in chapter 10.9 of Writing with Inform. I haven’t tried any alternative approaches yet, though.

This approach also has poor performance – for longer periods of time, there’s a very noticeable lag when waiting.

1 Like

It looks like you’re doing what’s explained in Recipe Book 6:11 which might be worth looking at if you haven’t seen it.

It’s been a long time since I first wrote that code (I actually set it aside for a few years and just recently came back to it). However, I’m pretty sure I originally used those recipes as a model for that code.

The main problem is that it breaks down when you want to advance time by more than a day (or perhaps when you want to advance time past midnight).

are you using any timed events as described in WI 9.11 or just scenes?

The domain of Inform’s time kind of value is restricted to 12:00 am to 11:59 pm. Time is inherently cyclical: Inform doesn’t have a concept of absolute time. And the time kind of value does double duty as not just the time of day but as a duration. So Inform also doesn’t have a concept of a duration longer than 23 hours, 59 minutes.

But what if it… did?

Lab is a room.

Time rate is a number that varies.
The time rate variable translates into Inter as "time_rate".

Time-rating is an action out of world applying to one number.
Understand "timerate [number]" as time-rating.
Carry out time-rating:
  if the number understood < 1, instead say "Invalid: time rate must be at least 1.";
  now the time rate is the number understood;
  say "Time rate is now [time rate] minutes. Time does not pass for out of world actions like this one.".

A scene can be imminent.
A scene can be wrapping up.
First when a scene (called sc) begins: now sc is not imminent.
First when a scene (called sc) ends: now sc is not wrapping up.

scene-monitoring-status-value is a kind of value.
scene-monitoring-status-values are scene-off, scene-on, scene-verbose.
scene monitoring status is initially scene-off.

Understand "scenes on" as scene-monitoring.
Scene-monitoring is an action out of world applying to nothing.
Carry out scene-monitoring:
  if scene monitoring status is scene-off, say "(Scene monitoring now switched on. Type 'scenes off' to switch it off)[line break]";
  now scene monitoring status is scene-on;
  scene-report;

Scene-anti-monitoring is an action out of world applying to nothing.
Understand "scenes off" as scene-anti-monitoring.
Carry out scene-anti-monitoring:
  now scene monitoring status is scene-off;
  say "(Scene monitoring now switched off. Type 'scenes on' or 'scenes verbose' to switch it on again.)[line break]";

Scene-reporting is an action out of world applying to nothing.
Understand "scenes" as scene-reporting.
Carry out scene-reporting: scene-report.

Verbose-scene-monitoring is an action out of world applying to nothing.
Understand "scenes verbose" as verbose-scene-monitoring.
Carry out verbose-scene-monitoring: now scene monitoring status is scene-verbose.

To say absolute time:
  let current-time be epoch-start + time-since-epoch;
  let days be current-time / minutes-in-day;
  let abs-minutes be current-time - (days * minutes-in-day);
  let hours be abs-minutes / minutes-in-hour;
  let mins be abs-minutes - (hours * minutes-in-hour);
  say "Day [days], [if hours < 10]0[end if][hours]:[if mins < 10]0[end if][mins]";
  say " ([time-since-epoch] minutes since beginning of game)";

This is the auto-scene-report rule:
  say "Now time is [absolute time].";
  scene-report.

The auto-scene-report rule is listed after the advance time rule in the turn sequence rules.
The auto-scene-report rule does nothing when scene monitoring status is not scene-verbose.

First when a scene (called sc) begins:
  say "[bracket]Scene ['][sc]['] beginning at [absolute time][close bracket].".

First when a scene (called sc) ends:
  say "[bracket]Scene ['][sc]['] ending at [absolute time] (after running for [duration since sc began] minutes)[close bracket].".

To scene-report:
  repeat with sc running through happening scenes begin;
    let t be time since sc began;
    say "Scene ['][sc]['] is happening (and has been for [duration since sc began] minutes).";
  end repeat;

Epoch-start is a number that varies.
last after starting the virtual machine: now epoch-start is the time-of-day.
Time-since-epoch is initially 0.

A scene has a number called begin-time.
A scene has a number called end-time.

To decide what number is time-of-day: (- the_time -). [ getting it as a number instead of as a time kind of value ]
To decide what number is minutes-in-day: (- TWENTY_FOUR_HOURS -).
To decide what number is minutes-in-hour: (- ONE_HOUR -).

This is the advance time since epoch rule: increase time-since-epoch by time rate.

The advance time since epoch rule is listed before the advance time rule in the turn sequence rules.

First when a scene (called sc) begins: now the begin-time of sc is time-since-epoch.

First when a scene (called sc) ends: now the end-time of sc is time-since-epoch.

To decide what number is the duration since (sc - scene) began: decide on the time-since-epoch - begin-time of sc.
To decide what number is the duration since (sc - scene) ended: decide on the time-since-epoch - end-time of sc.

xx is a scene.
xx begins when xx is imminent.
xx ends when xx is wrapping up.

Carry out jumping: now xx is imminent.
Instead of thinking: now xx is wrapping up.

test me with "scenes verbose / z / timerate 720 / z / jump / z / z / think / z".

I would suggest leaving time advancement at the end of the turn sequence like normal (the above doesn’t even change the advance time rule; it just allows the time rate to be changed) and definitely would suggest against following the turn sequence rules from within an action rule.

3 Likes

I’m using a few of those, but so far only for short times.

Your example of how to reimplement the scenes command is interesting, as I hadn’t even thought of doing that. I may try your code out later.

But on the topic of calling the turn sequence rules, I had a sudden brilliant(?) thought: what if I just let them call themselves? Instead of calling them at some point along the way, just don’t prompt the player for a command at the beginning of the turn. It seems to work and is much simpler than what I had before. The below code doesn’t account for times of 24 hours or more, but it wouldn’t be too hard to extend it to do so if I needed to (just add an additional “day waited for” variable).

An advancing time mode is a kind of value. The advancing time modes are one turn at a time, waiting for, and waiting until. The current advancing time mode is an advancing time mode that varies. The duration waited for is a time that varies.

The parse command rule does nothing when the current advancing time mode is not one turn at a time.
The generate action rule does nothing when the current advancing time mode is not one turn at a time.

Waiting more is an action applying to one number.

Understand "wait for/-- a/an/-- [a time period]" or "z [a time period]" as waiting more.

Carry out waiting more (this is the standard waiting more rule):
	now the current advancing time mode is waiting for;
	now the duration waited for is the time understood;
	if the duration waited for is at least 10 minutes:
		now the duration waited for is the duration waited for plus a random time from -5 minutes to 5 minutes.

Report waiting more (this is the report waiting more rule):
	say "You wait for about [the number understood in words] minutes."

Every turn while the current advancing time mode is waiting for:
	decrease the duration waited for by 1 minute;
	if the duration waited for is at most 0 minutes:
		now the current advancing time mode is one turn at a time.

The maximum wait time is initially 12 hours.

Check waiting more (this is the player is impatient rule):
	if the time understood is greater than the maximum wait time, say "You really haven't got that kind of patience." instead.

Waiting until a specific time is even simpler, if you eliminate my requirement that you need a clock. This also requires the first two blocks from the above code to function. My actual code is more complicated because it requires you to hold a clock, and uses the clock (which may be running fast or slow) as the authority on what time it is, but with that logic stripped out you get something short and sweet.

Hanging around until is an action applying to one time.

Understand "wait until/till [time]" or "z [time]" as hanging around until.

Check hanging around until (this is the hanging around is redundant rule):
	if the time of day is the time understood:
		say "It is [time understood] now!" instead.

Carry out hanging around until (this is the standard hanging around rule):
	now the current advancing time mode is waiting until;
	the wait ends at the time understood.

Report hanging around until (this is the report hanging around rule):
	say "You yawn until [the time understood]."

At the time when the wait ends:
	now the current advancing time mode is one turn at a time.

Other than the possibility of chugging along without ever stopping, does that approach seem like it could cause any problems? Performance-wise it still seems laggy, but possibly less so than the previous approach.

I do still need to add some logic in Every turn that will interrupt the wait if something significant happens. I’ll probably need never shall the wait ends to make that work… but still thinking about how I want to detect such significant events.

1 Like

To avoid any angst that the interpreter has frozen on slower platforms, you could have a rule that prints a period for each turn (or every 15 turns, or whatever) while waiting is going on, like so:

Time passes …

When I tried that, despite lag, all the periods printed at the same time. I’m not sure if that means my platform is not one of the “slower platforms” you’re referring to, or if it means that some sort of text buffering is going on behind the scenes preventing text from being printed until certain conditions are satisfied.

These are the changes I made:

Printed waiting message is a truth state that varies.
Printed waiting message is false.

Every turn while the current advancing time mode is not one turn at a time:
	if the remainder after dividing the time of day by 5 is 0 minutes:
		if printed waiting message is false:
			say "Time passes";
			now printed waiting message is true;
		say ".[run paragraph on]".

Every turn while the current advancing time mode is waiting for:
	decrease the duration waited for by 1 minute;
	if the duration waited for is at most 0 minutes:
		if printed waiting message is true:
			say paragraph break;
			now printed waiting message is false;
		now the current advancing time mode is one turn at a time.

At the time when the wait ends:
	if printed waiting message is true:
		say paragraph break;
		now printed waiting message is false;
	now the current advancing time mode is one turn at a time.