Help with day/time cycle

Twine Version: 2.5.1

I was trying to make a new attempt in making a day/time cycle using cnull’s code as a reference here:

So far, everything looks good, however, my variables in the arrays for the game time and game day doesn’t seem to change. This is what I have in my StoryInit:

/* Game Date */

// Determines what the current month is
<<set setup.dateMonth = ["January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "November", "December"]>>
<<set $gameMonth = 0>>

// Resets the month to January
<<if setup.dateMonth[$gameMonth] is "January">>
<<set $gameMonth = 0>>
<</if>>

// Determines what day it is
<<set setup.dateDay = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]>>
<<set $gameDay = 0>>

// Resets the day to Sunday
//<<if setup.dateDay[$gameDay] is "Saturday" && setup.time[$gameTime] is "Midnight">>
//<<set $gameDay = 0>>
//<</if>>

// Determines the time of the day
<<set setup.time = ["Dawn", "Morning", "Noon", "Afternoon", "Evening", "Night", "Midnight"]>>
<<set $gameTime = 1>>
<<set $playerAlarm = 1>>

// Resets the time of day to player's alarm (default is morning)
<<if setup.time[$gameTime] == "Midnight">>
<<set $gameTime == $playerAlarm>>
<<set $gameDay += 1>>
It's getting late. You decide to head to bed before you feel more tired. 
<</if>>

And this is what I have in my StoryCaption:

Today is ''<<=setup.dateDay[$gameDay]>>,'' ''<<=setup.dateMonth[$gameMonth]>>''
It is ''<<=setup.time[$gameTime]>>''

At the moment, I’m not focusing on the months, just on the day and time. From my testing, when I increase the game time by 1, the time goes up normally, but once it reaches midnight, instead of going back to morning it goes to [undefined]. The day stays on Sunday instead of changing to Monday as well.

I recall a post from someone when I tried doing this before where they suggested I use someone’s macro, but it was really confusing for me to use (hence why I used this instead).

There’s a typo there; you’ve got a comparison operator (double equals sign), but what’s needed is an assignment (single equals sign):

<<set $gameTime = $playerAlarm>>

Thanks I just fixed that. It still doesn’t seem to work though

Hmm, sorry, that was the first thing that jumped out at me.

What specifically doesn’t work?
Is the relevant if block executed at all (i.e., is the message “It’s getting late. [etc.]” printed)?

You could also try hardcoding

<<set $gameTime = 1>>

just for testing, instead of setting it to the value of $playerAlarm.

Another thing is that this part of the code is commented out in your post:

//<<if setup.dateDay[$gameDay] is "Saturday" && setup.time[$gameTime] is "Midnight">>
//<<set $gameDay = 0>>
//<</if>>

And in this part, you’re always setting the day to 0 when the code is executed:

// Determines what day it is
<<set setup.dateDay = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]>>
<<set $gameDay = 0>>

Edited to add:
And if you’re only doing the checks in StoryInit, then they won’t be executed later.

I have two test passages that add +1 to my $gameDay variable, which changes the time of day and that part works just fine. However when the time array eventually gets to “Midnight”, I don’t get a message telling me to go to bed and the time goes to [Undefined] instead of to Morning, which is default. It also doesn’t change the day from Sunday to Monday.

That was how I originally had it. I just added the alarm element because I decided I wanted to put that in later, but neither of those worked either.

Yeah when I was making this part, I realized that it would clash with what I had before, so I commented it so it wouldn’t mess with it.

I’m sorry, I don’t understand this part. Should I have it set to something else?

I made the changes in my StoryInit, and then hitting play to see if it works. I’m not sure if that’s the same thing, but that’s how I’m testing if it works.

You’ll need to check whether the day (etc.) should roll over every time that you increase the time within the game. Code that’s just in StoryInit will only be executed once, just before the start of the story.

Oh that makes sense. Where should I put that? Or would I have to learn how to use widgets?

You’d need to put that essentially everywhere where the time is increased. Depending on the structure of your story and passages, it might work to just put it directly in a few passages which get called after the player does something to spend time.

But since it will probably be needed in several different places, I think a widget will be more practical.

As a start, you could just put a widget definition like the following (using code from your post above) into a new passage to which you add the tag “widget”:

<<widget "increase-time">>
	Time increased!
    <<if setup.time[$gameTime] == "Midnight">>
		<<set $gameTime = $playerAlarm>>
		<<set $gameDay += 1>>
		It's getting late. You decide to head to bed before you feel more tired. 
	<</if>>
    
    // do further checks and messages here, as necessary
    // (to track the possible change in weekdays and months etc.)
    
<</widget>>

And then, in all the other passages, you can just insert <<increase-time>> into the passage to call the widget.

So just to confirm, my passage can be named anything as long it has a “widget” tag onto it like so:


And I can change my test passage from this:

To this:

Yes, that should work.

Although for the example, I only put a generic “Time increased” message and the check from the OP into the widget, not the actual variable increase. So you’d need to insert the line <<set $gameTime += 1>> into the widget.

And I think if you put the widget call into a <<button>> as in your testing passage, then it will run but will not print any output. So I’d probably put the widget call at the beginning of the next passage (to which the button or a link would lead).

I decided to change the name of the widget to make it faster to type and I added the <<set $gameTime += 1>> in there (I didn’t realize it wasn’t there). So the passages would look like this?


Yes, that should be it, or at least it worked for me in a brief test (but it needs some further checks and refinements, depending on how much you want to track). :slight_smile: :+1:

(By the way, it’s not very important, but out of C++ and JS habit I used the double slash format for comments there in the code: // do further checks ..., but Twine/Sugarcube uses different comment syntax.)

I’m doing a little bit more testing with it and found something a little annoying. Could I modify the widget so that it essentially interrupts any passages so that whatever the passage would normally show doesn’t show?
6

Edit: Basically so that it doesn’t show this part

7

I’m not sure if there is an elegant general solution for this, because it depends on how exactly it should look, and on how you want to let the player continue.

I mean, if we don’t want to show whatever the passage would normally show, that means that we also don’t show the links which would normally be shown in that passage and which would normally let the player continue.

If we provide an “Okay” button such as in the pictures above, then where does that lead, such that it’s applicable in all cases, no matter what the intended time-spending target passage originally was?

But putting those questions aside for the moment, here are two different ways to do this (there may well be better ones):

1)

Set a variable which tracks whether we just changed the day (or other relevant criterion), and check for that in the passages, putting the content behind the check.

So, you’d introduce <<set $dayChanged = false>> in StoryInit, then set it to true in the appropriate place within the if block in the widget, right after increasing the gameDay:

...
    <<if setup.time[$gameTime] == "Midnight">>
		<<set $gameTime = $playerAlarm>>
		<<set $gameDay += 1>>
        <<set $dayChanged = true>>
...

and then write all the passages which contain the widget so that they check whether the widget just changed the day:

<<increase-time>>

<<if $dayChanged>>
	You sleep for a while.
	<<set $dayChanged = false>>
<<else>>
	Regular passage content.
	Today is ''<<=setup.dateDay[$gameDay]>>,'' ''<<=setup.dateMonth[$gameMonth]>>''
	It is ''<<=setup.time[$gameTime]>>''
<</if>>

<<button "TimePlus">>
	<<goto [[Later2]]>>
<</button>>

2)

Alternatively, we could put more of the responsibility into the widget, but we would still need to modify the game passages.

We could use the replace macro from inside a done macro within the widget, and thereby replace the content of spans (which we designated before) in the passages.

In this variant, the widget would look like this:

<<widget "increase-time">>
	<<set $gameTime += 1>>
    <<if setup.time[$gameTime] == "Midnight">>
		<<set $gameTime = $playerAlarm>>
		<<set $gameDay += 1>>
         <<done>>
        	<<replace "#regular-content">>It's getting late. You decide to head to bed before you feel more tired.<</replace>>
         <</done>>
 	<</if>>
<</widget>>

And a passage would look like this:

<<increase-time>>

<span id="regular-content">
	Regular passage content.
    Today is ''<<=setup.dateDay[$gameDay]>>,'' ''<<=setup.dateMonth[$gameMonth]>>'' 
    It is ''<<=setup.time[$gameTime]>>''
</span>

<<button "TimePlus">>
	<<goto [[Later2]]>>
<</button>>

So, when the widget rolls the time over to the next day, it will replace the contents of the “regular-content” span in the surrounding passage with its own message “It’s getting late. …”.

To answer this, the player would wake up in a bedroom passage, which would have links to other passages.

Hm. Looking over both ideas, I think I like this one more. I’ll try both just to be sure though.

Edit: Ah now that I try it, the first idea is a little inconvenient in larger stories since I’ll have to do a lot of copy and pasting.

What does the <<done>> part mean and is it for?

Okay, after trying both now, I can definitely say I prefer the second one more. Only thing is that now when the time is about to be set to “Midnight”, it skips ahead to morning without showing the sleep message. This is what I have for the widget:

/* Changes the time of day by one */
/* Use <<add-time>> multiple times to pass more time for the same passage */
<<widget "add-time">>
	<<set $gameTime += 1>>
    <<if setup.time[$gameTime] == "Midnight">>
		<<set $gameTime = $playerAlarm>>
		<<set $gameDay += 1>>
         <<done>>
        	<<replace "#regular-content">>It's getting late. You decide to head to bed before you feel more tired.
            	<<button "Okay.">>
                	<<goto [[Disclaimer]]>>
                <</button>>
            <</replace>>
         <</done>>
 	<</if>>
    
    /* do further checks and messages here, as necessary */
    /* (to track the possible change in weekdays and months etc.) */
    
<</widget>>

And this is what I have in the passage:

<<add-time>>

<span id="regular-content">
	test
	<<button "Test">>
		<<goto [[Test]]>>
	<</button>>
</span>

Edit for clarity: The <<goto [[Disclaimer]]>> will be changed to the bedroom when I implement the bedroom passage.

The “done” makes sure that the passage is completely loaded/ready. Without that, the widget would not be able to access the parts of the passage in which it occurs. As the linked documentation says:

Silently executes its contents when the incoming passage is done rendering and has been added to the page. Generally, only really useful for running code that needs to manipulate elements from the incoming passage, since you must wait until they’ve been added to the page.