There’s an example of the time switching to days of the week in Inform 7. (See “Example: The Hang of Thursdays”). You could expand on that to have it toggle a “week” counter and when the 4th week is up, simply toggle to the next month.
Example: The Hang of Thursdays:
The Stage is a room. Rule for printing the name of the stage: say "[current weekday] [current time period]" instead.
A weekday is a kind of value. The weekdays are Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday. The current weekday is a weekday that varies. The current weekday is Saturday.
A time period is a kind of value. The time periods are morning, afternoon, evening, night. The current time period is a time period that varies. The current time period is afternoon.
This is the new advance time rule:
if the current time period is less than night:
now the current time period is the time period after the current time period;
otherwise:
now the current time period is morning;
now the current weekday is the weekday after the current weekday.
The new advance time rule is listed instead of the advance time rule in the turn sequence rules.
Test me with "z / z / z / z / z".
You’d have to add to that with some stuff like:
A month is a kind of value. The months are January, February, March, April, May, June, July, August, September, October, November, December. The current month is January.
…then modify the original code even further with a “current week” toggle and once the fourth week is up, have the month increment or loop back to January again. At the point where you have this all coded in your game you could set the current day of the week and month of the year manually with:
Now the current month is August. Now the current weekday is Monday.
I wouldn’t get too complicated like this though. Wouldn’t it be simpler to note to the player in some other way what the current day of the week is? Like off a newspaper in a newspaper stand? Or mention it at the start of a new scene? Unless you’re going for a MMORPG type game world I don’t see the need for it and even still, most MMORPGs that I know of use a simple 24 hour clock and disregard the day of the week and current month (current month being reflective based on game patches - for example “Frostfell” happens each real world December in EverQuest but only after a patch).
I did write up something for handling clocks in Tads 3. It would have to be heavily converted over to Inform 7 to get it to work there. Here’s my gameclok.t file:
#ifndef ADV3_H
#include <adv3.h>
#endif
// checks for if a variable was NOT defined from file en_us.h
// .. and if so then include the file to define that variable, method, etc.
#ifndef singleDir
#include <en_us.h>
// //#error "You can put an error here too if the variable <> something. "
#endif
/*
* Our gameClockAgent needs a reference pointer.
* We could create it 'nil' like so:
* gameClockAgent.construct(self,nil,15000);
* .... but without a pointer to this object it will
* not get registered within the global "eventManager"
* object, in the "events_" list.
* So... we must create a method someplace. We'll
* create our method and start up the agent like
* so:
* gameClockAgent.construct(self,&myDaemon,15000);
* Our workaround is to create an actual Thing object
* that has some start/stop routines, and a daemon routine
* which acts as our timer counter.
*
* To start this agent you'd do this at start-up somewhere:
* gameClockAgentManager.startMyDaemon();//
*
* Then later on to query and display the time just do this:
* say(gameClockAgentManager.getTimeHourMin());
*
*/
/*
* Note: only create one of these CromexxGameClockManager objects per game.
* All of our CromexxClock class objects will query the global realTimeManager
* to find ONE of this kind of object. If you have multiples running you're
* possibly not going to get the correct game world time.
*
* Also note that if you use CromexxAlarmClock objects you need to have them
* defined after this object as they need to refer to this object to register
* themselves for hourly chiming.
*/
class CromexxGameClockManager: InitObject
isInitialized = 0 // a cromexx counter we made up
isActive = true // if nil then the daemon exists (if exists) just won't run
myDaemonRunning = nil
worldHour = 19
worldMinute = 45
worldDay = 1 // 1st of the month - 31st usually
worldMonth = 1 // 1-12
worldYear = 2307
daemonID = nil
alarmClockList = [] // a list of alarm clocks that would like hourly notification
monthShortNames = ['JAN','FEB','MAR',
'APR','MAY','JUN',
'JUL','AUG','SEP',
'OCT','NOV','DEC']
monthNames = ['January','February','March',
'April','May','June',
'July','August','September',
'October','November','December']
monthDays = [31,28,31,
30,31,30,
31,31,30,
31,30,31]
/* Part of the InitObject routine
* This fires off automatically when the game starts.
* Note that no text will ever be displayed within this
* method as InitObjects are usually created before the
* parser is even ready for displaying text yet.
*/
execute() {
isInitialized++;
startMyDaemon();
"*** This text should not show. *** __CromexxGameClockManager class. gameclok.t";
}
registerAlarmClock(oclock){
if(oclock == nil) exit;
if(!oclock.ofKind(CromexxAlarmClock)) exit;
if(alarmClockList== nil) exit;
if(alarmClockList.indexOf(oclock) != nil) exit;
alarmClockList+= oclock;
}
unRegisterAlarmClock(oclock){
if(oclock == nil) exit;
if(!oclock.ofKind(CromexxAlarmClock)) exit;
if(alarmClockList== nil) exit;
if(alarmClockList.indexOf(oclock) != nil) alarmClockList-= oclock;
}
startMyDaemon(){
if(self.myDaemonRunning != true){
/* start daemon */
daemonID = new RealTimeDaemon(self,&myGameClockDaemon,15000);// there are other ways to initialize this but this calls self & a method in self
/* set that the daemon is running (so we can keep easier track) */
self.myDaemonRunning = true;
}
/* make sure we are active */
self.isActive = true;
}
stopMyDaemon(){
// **** BEST WAY *** if you set a "daemonID" tracking var to this object
if(daemonID != nil) daemonID.removeEvent;
daemonID = nil;
// **** ANOTHER WAY ****
// eventManager.removeMatchingEvents(obj, &prop);
// **** ANOTHER WAY ****
/* (eventManager.events_; Schedulable.allSchedulables;) */
// foreach(local obj in eventManager.events_){
// // if our obj_ is this "someObj" object, then remove the event/agent/daemon
// if(obj.obj_ == self){
// obj.removeEvent();// actually removes the event/agent/daemon
// self.myDaemonRunning = nil;
// /* "isActive = nil" is optional. We picked up this flag idea from ditch day drifter
// * This variable is used only in our "someObj" object, not part of
// * the Event class (as far as I know), and is used as an emergency backup
// * check in case the agent/daemon is still running or fires off one last time.
// * (i.e. a "server hiccup" type prevention)
// */
// self.isActive = nil;
// }
// }
}
isNight {
if(worldHour > 18) return true;
if(worldHour < 7) return true;
return nil;
}
/* **** Display a default Day or Night message */
displayDayNightMsg(){
if(isNight == true){
"It is night time. ";
}else{
"It\'s day time. ";
}
}
// returns in the format of: 'January 1, 2037'
getDateSimple(){
local s0 = monthNames[worldMonth] + ' ' + toString(worldDay) + ', ' + toString(worldYear);
return s0;
}
getTimeHourMin(){
local iHour = worldHour;
local ampm = 'AM';
if(iHour >= 13){
ampm = 'PM';
iHour = iHour - 12;
}
local iMinute = worldMinute;
if(iMinute < 10){
iMinute = '0'+toString(iMinute);
}
local s0 = toString(iHour) + ':' + iMinute + ' ' + ampm;
return s0;
}
getTimeHourMinSec {
local iHour = worldHour;
local ampm = 'AM';
if(iHour >= 13){
ampm = 'PM';
iHour = iHour - 12;
}
local iMinute = worldMinute;
if(iMinute < 10){
iMinute = '0'+toString(iMinute);
}
local s0 = toString(iHour) + ':' + iMinute + ':00 ' + ampm;
return s0;
}
// 15000 = 15 sec real time. (so 72 min. real time = 1 day virtual time; & 20 min virtual time = 1 min real time)
// note: do NOT call it as a RealTimeEvent!!! Even though it is one (call it as a RealTimeDaemon or else your program could lock up and crash!)
myGameClockDaemon(){
// if the daemon is not active (even if running) we exit anyway
if(self.isActive != true){
exit();
}
// *** do stuff ***
worldMinute += 5;// add 5 minutes to the game clock
if(worldMinute >= 60){
worldMinute = 0;
worldHour++;
if(alarmClockList != nil){
if(alarmClockList.length != 0){
foreach(local oclock in alarmClockList){
oclock.hourlyNotificationScript(worldHour);
}
}
}
if(worldHour >= 24){
worldHour = 1;
worldDay++;
if(worldDay > monthDays[worldMonth]){
worldDay = 1;
worldMonth++;
if(worldMonth > 12){
worldYear++; // *** happy new year!
worldMonth = 1;
}
}
}
}
}// end myGameClockDaemon()...
;
/*
* Our simple gameClockAgentManager... starts up automatically on start.
* You do not need to start this object's daemon as it's of the InitObject
* class and we start the daemon automatically through that.
*/
gameClockAgentManager: CromexxGameClockManager;
// *** start it somewhere else...
// gameClockAgent.construct(self,nil,15000); // 300000 milliseconds = 300 seconds; (self,nil,tempC);
// *** OR.. within an object that starts it...
// gameClockAgentManager.startMyDaemon();
/* Now we need the CromexxClock class to use in the game world to display the time
*
* Instead of hard coding a link to the actual instanced object, we'll
* query the realTimeManager and see if we can find an event that
* matches the CromexxGameClockManager class, and if so we'll store that
* value in our private "oCromexxClockManager_" variable. Then we call
* that in our Read action and do a "getTimeHourMin()" check, and display
* that back to the player. We display that time after the normal readDesc.
*
* Note that we'll want to keep 'timepiece' in our vocabWords list so that
* our "Time" verb (in crom_wrd.t) can match up a generic "get time" command
* and search for the nearest logical timepiece.
*/
class CromexxClock: Readable, Thing
vocabWords = 'timepiece'
// this is an internal variable, do not set it directly
oCromexxClockManager_ = nil
dobjFor(Examine){
preCond=[objVisible]
verify(){ inherited; }
check(){ inherited; }
action(){ inherited;
// if player, do a "read" action too. Note that "nested" will not count as another turn
if(gActor==gPlayerChar) nestedActorAction(gActor,Read,self);
}
}
/*
* Display game world time after doing a normal readDesc
*/
dobjFor(Read){
preCond=[objVisible]
verify(){ inherited; }
check(){
oCromexxClockManager_ = nil;
// make sure there is a global CromexxGameClockManager to query
// note that we do not use the vector in "eventManager.events_" to
// query this. We use "realTimeManager.events_" ...
if(realTimeManager.events_ != nil){
foreach(local cur in realTimeManager.events_){
if((cur.obj_.ofKind(CromexxGameClockManager)) &&
(cur.prop_ == &myGameClockDaemon)){
oCromexxClockManager_ = cur.obj_;
}
}
}
local ooo = self;
gMessageParams(ooo);
local s0 = '\^{subj ooo}{the/he} seems to be stopped or broken. ';
if(oCromexxClockManager_ == nil){
if(gActor==gPlayerChar) say(s0);
exit;
}
inherited;
}
action(){
inherited;
local obj = self;
gMessageParams(obj);
local s0 = 'The current time is ' + oCromexxClockManager_.getTimeHourMin() + ' (in game world time). ';
if(gActor==gPlayerChar) say(s0);
}
}
;
/* Class: CromexxWatch
*
* This is like CromexxClock only you need to
* actually be holding the watch to read or examine it (for getting the time).
* Note that to use this object properly for a literal watch object
* you should blend it in with class CromexxClothing or AdvancedCromexxClothing
* and set the "clothLevel = 17" (for watch).
*
* Example use:
* + woomansWatch001: CromexxWatch, CromexxClothing
* vocabWords = '(lady\'s) watch'
* name = 'lady\'s watch'
* desc = "This is a lady\'s watch with a pink leather band. "
* girl_only = true
* clothLevel = 17
* // note that class CromexxWatch displays the time value after the readDesc
* readDesc = "You read the time on the watch. "
* ;
*
* Note that we'll want to keep 'timepiece' in our vocabWords list so that
* our "Time" verb (in crom_wrd.t) can match up a generic "get time" command
* and search for the nearest logical timepiece.
*/
class CromexxWatch: CromexxClock
vocabWords = 'timepiece'
dobjFor(Read){
preCond=[objHeld,objVisible]
verify(){ inherited; }
check(){ inherited; }
action(){ inherited; }
}
dobjFor(Examine){
preCond=[objHeld,objVisible]
verify(){ inherited; }
check(){ inherited; }
action(){ inherited; }
}
;
/* class: CromexxAlarmClock
* Use this for cuckoo clocks, alarm clocks, grandfather clocks, etc.
*
* If you set "iRunOnlyWhenPlayerHere" to true, then you must have a way
* to re-activate the daemon. We can do this by adding a CromAlarmClockTrigger
* to a room object. So when the player enters the room the trigger finds and
* sets this clock's daemon back to running again.
*/
class CromexxAlarmClock: CromexxClock, InitObject
vocabWords = 'timepiece'
// internal variable
oCromexxClockManager_ = nil
iRunOnlyWhenPlayerHere = nil
iAlarmOnTheHour = true // by default we'll have an alarm go off on the hour
alarmText {
local ooo = self;
gMessageParams(ooo);
local s0 = '\^{subj ooo}{the/he} rings as its alarm goes off. ';
say(s0);
}
execute() {
findSetWorldClockManager();
oCromexxClockManager_.registerAlarmClock(self);
}
// game world clock calls this on the hour if we're registered
hourlyNotificationScript(val){
if(gPlayerChar.location == self.location){
if(iAlarmOnTheHour==true) alarmText;
}
}
findSetWorldClockManager(){
// reset local pointer and find our world game clock manager
oCromexxClockManager_ = nil;
if(realTimeManager.events_ != nil){
foreach(local cur in realTimeManager.events_){
if((cur.obj_.ofKind(CromexxGameClockManager)) &&
(cur.prop_ == &myGameClockDaemon)){
oCromexxClockManager_ = cur.obj_;
}
}
}
}
;
/* ***** we put this verb in file: crom_wrt.t ***************
* // note that this will only work if all clocks & watches have
* // the vocabWord of 'timepiece' in their vocabWord list.
* DefineIAction(Time)
* execAction(){
* libGlobal.totalTurns--;
* local tokList = Tokenizer.tokenize('examine timepiece');
* executeCommand(gActor,gActor,tokList,nil);// nil = start of sentence (true/nil)
* }
*;
*VerbRule(Time)
* ('read') ('the') 'time' |
* ('read') 'time' |
* ('get') ('the') 'time' |
* ('get') 'time' |
* ('x') 'time' |
* ('examine') 'time' |
* ('x') ('the') 'time' |
* ('examine') ('the') 'time' |
* ('get') 'time' |
* 'time'
* : TimeAction
* verbPhrase = 'time'
*;
*
********************************************** ***********/