0 votes
by (130 points)
Hello, In my game the player choses an activity and during this activity they will wait. I made an example with a simple timer. However like cookie clicker or other idle games I want the player to be able to turn off the game and com back to it when the time passes. I think I could do this with the system time, but I'm not sure how I can set the point in the future (say 30 seconds, 30 minutes, or 1 day) and have the game open a hook/ go to the next passage.


I have an example in Harlowe using a timer, but even if you save say 15 seconds later it will start again at 30 seconds. I would like the counter to keep counting even if the game is shut down.


Harlowe example: https://www.dropbox.com/s/ims0mznggnlhh1y/Timer%20test.html?dl=0

Sugarcube messing around (not working yet): https://www.dropbox.com/s/0af6iev9suxmahr/Timer%20test%20sugarcube.html?dl=0

1 Answer

+1 vote
by (36k points)

If you're using the SugarCube story format then you can simply update the relevant variable in an "onSave" event, like this:

Config.saves.onSave = function (save) {

	function UpdateVar(VarName) {
		if (hasOwnProperty.call(State.variables, VarName)) {
			save.state.history[save.state.index].variables[VarName] = State.variables[VarName];
		} else if (hasOwnProperty.call(save.state.history[save.state.index].variables, VarName)) {
			delete save.state.history[save.state.index].variables[VarName];

	if (!tags().includes("NoSafeSave")) {
		// Makes sure that the current state is saved as long as the passage doesn't have a "NoSafeSave" tag.
		UpdateVar("CurrentTime");  // this will make sure that the current value of $CurrentTime is what is saved
		UpdateVar("OtherVar");  // repeat as necessary for each variable that might have changed after the passage was last loaded

If the passage has a "NoSafeSave" tag then that code will not be used when saving from that passage.

IMPORTANT!: That code should only be used on passages which do NOT modify those variable's existing values merely by entering the passage, and also do NOT use any of those variable's values when entering the passage to determine how the passage is displayed if you want it to be displayed the same upon loading. Doing the first will cause those changes to happen again. So, for example, if you gain 100 coins when entering a certain passage, and the variable that stores the coins is updated in the above code, then you'd want to add the "NoSafeSave" tag to that passage, so that reloading their save in the that passage doesn't get them another 100 coins.  Similarly, in the second case, if you had different events trigger based on the amount of coins you had when you initially entered the passage, and that amount could change before leaving the passage, then that passage would also need a "NoSafeSave" tag, so that they'd see the same event trigger when they reloaded.

Alternately, you could use the JavaScript Date object to track whether the next event has occurred yet or not:

<<link "Go to next passage" "Next">>
	/* Get the current time and date */
	<<set _CurDateTime = new Date(Date.now())>>
	/* Copy that to $NextEvent */
	<<set $NextEvent = clone(_CurDateTime)>>
	/* Set $NextEvent to _CurDateTime + 15 minutes */
	<<set $NextEvent = new Date($NextEvent.setMinutes($NextEvent.getMinutes() + 15))>>

(then, in the "Next" passage)

/* Update the current time and date */
<<set _CurDateTime = new Date(Date.now())>>
/* Show the current date and time */
Current time: <<=_CurDateTime.toLocaleString("en-US", { weekday: "long", month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit", seconds: "2-digit" } )>>
/* Show the date and time of the next event */
Next event: <<=$NextEvent.toLocaleString("en-US", { weekday: "long", month: "short", day: "numeric", year: "numeric", hour: "numeric", minute: "2-digit", seconds: "2-digit" } )>>

(... and later on ...)

/* Update the current time and date */
<<set _CurDateTime = new Date(Date.now())>>
/* Get the time remaining in milliseconds */
<<set _Remaining = $NextEvent - _CurDateTime>>
/* Check to see if there is any time remaining till the next event */
<<if _Remaining <= 0>>
	/* event code goes here */
	/* not time yet, show time remaining */
	<<set _hours   = Math.floor(_Remaining / 3.6e6)>>
	<<set _minutes = Math.floor((_Remaining % 3.6e6) / 6e4)>>
	<<set _seconds = Math.floor((_Remaining % 6e4) / 1000)>>
	Time remaining: _hours hours, _minutes minutes, and _seconds seconds

That will show a link which sets the $NextEvent value just before going to the "Next" passage.  Then, in the "Next" passage it shows the current date and time and the date and time of the next event.

The next piece of code shows how later on the $NextEvent value can be matched against the _CurDateTime (which should have been updated when the passage loaded) to tell if that event should have triggered yet or not.

Obviously you don't have to do it exactly that way, but that should hopefully give you enough information to rewrite it the way you want.

Hope that helps!  :-)

Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.