!==================================================================================== ! TIMESYS 1.0 INFORM TIME-ORIENTED GAME EXTENSION ! ! An Inform library to provide an understanding of English time ! strings, and give an enhanced Wait command. ! ! (c) Kevin Forchione, 1998, but freely usable. ! Compatible with Inform 6.15, library 6/7. ! Based on WaitTime.h by L. Ross Raszewski, 1996 ! and TimeWait.h by Andrew Clover, 1995 ! with much appreciation for their efforts. ! ! IMPLEMENTATION REQUIREMENTS: ! ! Time-oriented games only ! Replace DrawStatus; and set constant to STYLE you wish (see STYLES below) ! Replace WaitSub(); (the replacement routine is already provided in the ! extension. ! Change WAIT verb to META in GRAMMAR.H ! #include TimeSys.h somewhere after #include "Grammar"; ! Set the day-of-the-week (if desired) in Initialise() with ! TimeSys('day'); (see below) ! Day-of-week advancement (if desired) At the top of TimePasses(); ! call TimeSys.NextDay() for automatic handling of day-of-week advancement. ! ! COMMENTS: ! ! This is hardly an ideal solution (That being an overhaul of the ! InformLibrary itself to extend its time-keeping capabilities.) ! ! Nonetheless, by making the WAIT command META we take control out of the ! InformLibrary's Turn__end process and allow the extension to process ! Waiting() and StopWaiting() requests without falling back into final execution ! of InformLibrary.End_Turn_Sequence, which causes problems with StopWaiting() ! requests. (i.e. the request stops the extension at the desired time, but ! InformLibrary.Turn__end gets the final word and bumps the time up one more step.) ! ! DRAWBACKS: However, there are some drawbacks... ! ! Unfortunately the functions of the TimeSys object are completely dependent ! upon being called through the wait subroutines as were those of it's predecessor ! tw_waiting (It's never in scope). ! ! Making the WAIT verb META compounds the difficulty in generation of actions ! to an object's before() and Orders(). ! ! ENHANCEMENTS: The TimeSys object has been significantly enhanced over ! the old tw_waiting object. ! ! The variables in TimeSys have all been made private. Access methods ! It now contains coding for the advancement of the day-of-week, which should ! be called from TimePasses() with TimeSys.NextDay(). ! ! The redundant call to TimePasses() in the Waiting process that occurred in ! WaitTime.h has been removed and this process moved to the TimeSys object ! instead of being handled in a standalone routine. In addition this means ! that the wait process is effected only once each cycle by the ! InformLibrary.End_Turn_Sequence(). ! ! You can set a wait default (independent of time_step) which will allow you ! to wait a default number of minutes (such as in Infocom's "Sherlock: The Riddle ! of the Crown Jewels"). ! ! The non-waiting time_rate is kept independent of the waiting process, so ! that you can SetTime() for any rate you wish for the normal (non-waiting) ! passage of time. Waiting converts minutes to turns (as WaitTime.h did), ! then when the waiting process is finished the original non-waiting time_rate ! is restored. ! ! Other features are documented below. ! !==================================================================================== Object TimeSys "TimeSys" has proper private TS_day 0 ! Day-of-the-week numeric value ,TS_WaitDefault 10 ! Default value for Wait with no operand ,TS_WaitMax 1440 ! Maximum number of minutes allowed in a Wait ,TS_wait_time_initial 0 ! Number of minutes to be waited ,TS_wait_time_remaining 0 ! Number of minutes for waiting remaining ,TS_normal_time_rate 0 ! Saved value of non-wait game time-rate ,TS_prev_time_save 0 ! Saved value used to determine new day with name "TimeSys" ,Waiting [ i; if (noun > self.TS_WaitMax) {give self locked; "That's too long to wait.";}; if (noun==0) {give self locked; "Time doesn't pass.^";}; print "Time passes...^"; give self on; self.TS_wait_time_initial=noun; self.TS_wait_time_remaining=noun; self.TS_normal_time_rate=time_rate; SetTime(the_time,1); for (i=noun : (i>0)&&(deadflag==0)&&(self has on) : i--) { self.TS_wait_time_remaining--; #ifdef InformLibrary; InformLibrary.End_Turn_Sequence(); #ifnot; #ifdef EndTurnSequence; EndTurnSequence(); #ifnot; Message fatalerror "waittime.h requires \ InformLibrary.End_Turn_Sequence() or \ EndTurnSequence() to be defined (this should be done \ by the Inform Library)"; #endif; #endif; DisplayStatus(); #IFV5; DrawStatusLine(); #ENDIF; } SetTime(the_time,self.TS_normal_time_rate); if ((self hasnt on)&&(self.TS_wait_time_remaining>0)&&(deadflag==0)) print "^(waiting stopped)^"; ] !-------------------------------------------------------------------------------------- ! This routine allows the author to code StopWaiting requests for any event s/he chooses. ! It also give the player the choice of continued waiting if they wish. !---------------------------------------------------------------------------------------- ,StopWaiting [; if (self.TS_wait_time_remaining==0) rfalse; DisplayStatus(); #IFV5; DrawStatusLine(); #ENDIF; print "^Do you want to continue waiting?"; if (YesOrNo()==0) give self ~on; ] ,SetDefault [ d; self.TS_WaitDefault=d; ] ,GetDefault [; return self.TS_WaitDefault; ] !----------------------------------------------------------------------------------------- ! This function allows the author to set the game day using the name for the ! day-of-the-week, instead of having to use its numeric value. This allows TimeSys ! to keep this variable private. !----------------------------------------------------------------------------------------- ,SetDay [ d; switch(d) { 'Sunday','sunday','Sun','sun',"Sunday","sunday","Sun","sun": self.TS_day=0; 'Monday','monday','Mon','mon',"Monday","monday","Mon","mon": self.TS_day=1; 'Tuesday','tuesday','Tue','tue',"Tuesday","tuesday","Tue","tue": self.TS_day=2; 'Wednesday','wednesday','Wed','wed',"Wednesday","wednesday","Wed","wed": self.TS_day=3; 'Thursday','thursday','Thu','thu',"Thursday","thursday","Thur","thur": self.TS_day=4; 'Friday','friday','Fri','fri',"Friday","friday","Fri","fri": self.TS_day=5; 'Saturday','saturday','Sat','sat',"Saturday","saturday","Sat","sat": self.TS_day=6; default: self.TS_day=d; }; ] !----------------------------------------------------------------------------------------- ! This function allows time-oriented games to have the day-of-the-week pushed forward with ! the passage of time. Although it goes beyond the scope of purely 'waiting' it aspires to ! a more mature time-keeping system we can only now dream of. ! ! This should be called from TimePasses() as TimeSys.NextDay() for automatic advancement. !------------------------------------------------------------------------------------------ ,NextDay [; if (the_time < self.TS_prev_time_save) {self.TS_day++;}; if (self.TS_day > 6) {self.TS_day=0;}; self.TS_prev_time_save=the_time; ] !------------------------------------------------------------------------------------------ ! This routine returns either the number value or the associated name for the ! day_of_the_week, and can be included in any StatusLine routine that needs to know the ! this information !------------------------------------------------------------------------------------------ ,GetDay [ f i; if (f==0) return self.TS_day; if (f==1) switch(self.TS_day) { 0: i="Sunday "; 1: i="Monday "; 2: i="Tuesday "; 3: i="Wednesday"; 4: i="Thursday "; 5: i="Friday "; 6: i="Saturday "; } else switch(self.TS_day) { 0: i="Sun"; 1: i="Mon"; 2: i="Tue"; 3: i="Wed"; 4: i="Thu"; 5: i="Fri"; 6: i="Sat"; }; return i; ]; !------------------------------------------------------------------------------------------ ! STATUSLINE STYLES ! ! Some of the styles listed below mimic styles from classic Infocom games ! (versions may differ). To select a style of statusline simply define the ! constant listed below as one of the following: ! ! BASIC_STYLE: Mimics statuslines of the following: ! Cutthroats Release 23 / Serial number 840809 ! Deadline: An Interlogic Mystery Release 27 / Serial Number 831005 ! MoonMist Release 9 / Serial Number 861022 ! Suspect: An Interactive Mystery Release 14 / Serial Number 841005 ! The_Witness: An Interlogic Mystery Release 22 / Serial Number 840924 ! Wishbringer: The Magick Stone of Dreams Release 69 / Serial Number 850920 ! ! STANDARD_STYLE: Displays day/time information ! ! FULL_STYLE: Displays day/time/score information ! ! SHERLOCK_STYLE: Mimics statuslines of Infocom's Sherlock game, which used ! an hh:mm:ss format, even though the game didn't handle seconds: ! Sherlock: The Riddle of the Crown Jewels Release 21 / Interpreter 6 / ! Version j / Serial Number 871214 ! !=========================================================================================== Constant FULL_STYLE; #IFDEF BASIC_STYLE; [ DrawStatusLine i width pos; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; width = 0->33; if (width == 0) width = 80; spaces (width-1); @set_cursor 1 2; PrintShortName(location); pos = width-19; @set_cursor 1 pos; print "Time: "; i=sline1%12; if (i<10) print " "; if (i==0) i=12; print i, ":"; if (sline2<10) print "0"; print sline2; if ((sline1/12) > 0) print " pm"; else print " am"; @set_cursor 1 1; style roman; @set_window 0; ]; #ENDIF; ! BASIC_STYLE #IFDEF STANDARD_STYLE; [ DrawStatusLine i width pos; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; width = 0->33; if (width == 0) width = 80; pos = width-22; spaces (width-1); @set_cursor 1 2; PrintShortName(location); @set_cursor 1 pos; print (string) TimeSys.GetDay(1); i=sline1%12; if (i<10) print " "; if (i==0) i=12; print i, ":"; if (sline2<10) print "0"; print sline2; if ((sline1/12) > 0) print " pm"; else print " am"; @set_cursor 1 1; style roman; @set_window 0; ]; #ENDIF; !STANDARD_STYLE #IFDEF FULL_STYLE; [ DrawStatusLine i width pos; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; width = 0->33; if (width == 0) width = 80; spaces (width-1); @set_cursor 1 2; PrintShortName(location); pos = width-41; @set_cursor 1 pos; print (string) TimeSys.GetDay(1); i=sline1%12; if (i<10) print " "; if (i==0) i=12; print i, ":"; if (sline2<10) print "0"; print sline2; if ((sline1/12) > 0) print " pm"; else print " am"; pos = width-10; @set_cursor 1 pos; print "Score: ", score; @set_cursor 1 1; style roman; @set_window 0; ]; #ENDIF; ! FULL_STYLE #IFDEF SHERLOCK_STYLE; [ DrawStatusLine i width pos; @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; width = 0->33; if (width == 0) width = 80; spaces (width-1); @set_cursor 1 2; PrintShortName(location); pos = width-41; @set_cursor 1 pos; print (string) TimeSys.GetDay(1); i=sline1%12; if (i<10) print " "; if (i==0) i=12; print i, ":"; if (sline2<10) print "0"; print sline2; if ((sline1/12) > 0) print ":00 p.m."; else print ":00 a.m."; pos = width-10; @set_cursor 1 pos; print "Score: ", score; @set_cursor 1 1; style roman; @set_window 0; ]; #ENDIF; ! SHERLOCK_STYLE !------------------------------------------------------------------------------------------ !Action routines for Wait command... !------------------------------------------------------------------------------------------ [WaitSub; noun=TimeSys.GetDefault(); TimeSys.Waiting(); ]; [ WaitMovesSub; TimeSys.Waiting(); ]; [ WaitHoursSub; noun=noun*60+second; TimeSys.Waiting(); ]; [ WaitUntilSub; if (parsed_number>=the_time) noun=parsed_number-the_time; if (parsed_number|<"one"-"twenty">|"half"|"quarter" ["minutes"|"minute"] ! "past"|"to" <"1"-"12">|<"one"-"twelve"> ["am"|"pm"] ! b. <"1"-"12">|<"one"-"twelve"> ["o'clock"] ["am"|"pm"] ! c. <"1"-"12">|<"one"-"twelve"> <"0"-"59">|<"one"-"twenty"> ["am"|"pm"] ! d. <"1"-"12">":"<"0"-"59"> ["am"|"pm"] ! e. "midnight"|"midday"|"noon" ! If no am/pm is specified, the next time likely to come up is chosen; that ! is, the one that's just ahead of the current time. However, if this ! happens, the TimeSys object is given the 'general' attribute. Thus you ! can change the time returned by twelve hours in an action like SetClock ! if necessary. ! ! The next dictionary command is there to allow us to compare a typed ! string with "o'clock", something we can't normally do as it is bounded by ! single quotes. !----------------------------------------------------------------------------------- constant TS_OCLOCK 'o^clock'; [ ParseTime i j k flg loop dig hr mn; give TimeSys ~general; i=NextWord(); if (i=='midday' or 'noon' or 'midnight') ! then case (e) applies { if (i=='midnight') hr=0; else hr=12; mn=0; parsed_number=(hr*60+mn); } else { k=(wn-1)*4+1; ! test for case (d) j=parse->k; j=j+buffer; k=parse->(k-1); flg=0; for (loop=0:looploop; if (dig==':') flg=1; } if ((k>2)&&(k<6)&&(flg==1)) ! then case (d) applies { hr=0; mn=0; loop=0; .tw_diglph; dig=j->loop; loop++; if (dig~=':') hr=hr*10; if (dig=='0') { hr=hr+0; jump tw_diglph; } if (dig=='1') { hr=hr+1; jump tw_diglph; } if (dig=='2') { hr=hr+2; jump tw_diglph; } if (dig=='3') { hr=hr+3; jump tw_diglph; } if (dig=='4') { hr=hr+4; jump tw_diglph; } if (dig=='5') { hr=hr+5; jump tw_diglph; } if (dig=='6') { hr=hr+6; jump tw_diglph; } if (dig=='7') { hr=hr+7; jump tw_diglph; } if (dig=='8') { hr=hr+8; jump tw_diglph; } if (dig=='9') { hr=hr+9; jump tw_diglph; } if (dig~=':') return -1; while (looploop; mn=mn*10; if (dig=='0') { mn=mn+0; jump tw_digokm; } if (dig=='1') { mn=mn+1; jump tw_digokm; } if (dig=='2') { mn=mn+2; jump tw_digokm; } if (dig=='3') { mn=mn+3; jump tw_digokm; } if (dig=='4') { mn=mn+4; jump tw_digokm; } if (dig=='5') { mn=mn+5; jump tw_digokm; } if (dig=='6') { mn=mn+6; jump tw_digokm; } if (dig=='7') { mn=mn+7; jump tw_digokm; } if (dig=='8') { mn=mn+8; jump tw_digokm; } if (dig=='9') { mn=mn+9; jump tw_digokm; } return -1; .tw_digokm; loop++; } ! decode digital time } else { j=NextWordStopped(); if ((j==TS_OCLOCK or -1)||(j=='am' or 'pm')) ! then case (c) applies { hr=TryNumber(wn-2); mn=0; if (j~=TS_OCLOCK) wn--; } else { k=TryNumber(wn-1); if (k~=-1000) ! then case (b) applies { mn=k; hr=TryNumber(wn-2); } else ! well, must be case (a) { mn=TryNumber(wn-2); if (i=='quarter') mn=15; if (i=='twenty-five') mn=25; if (i=='half' or 'thirty') mn=30; if (j=='minute' or 'minutes') j=NextWord(); ! ignore 'minutes' hr=TryNumber(wn); wn++; if (j~='past' or 'to') hr=-1; if (j=='to') { hr--; mn=60-mn; if (hr==0) hr=12; } } } } if ((hr>12)||(hr<1)||(mn>59)||(mn<0)) parsed_number=-1; else { if (hr==12) ! now sort out am/pm hr=0; i=NextWord(); if (i=='pm') hr=hr+12; else if (i~='am') ! am or pm implied, then? { give TimeSys general; wn--; i=(hr*60+mn); j=((hr+12)*60+mn); i=i-the_time; j=j-the_time; if (i<0) i=i+(24*60); if (j<0) j=j+(24*60); if (i>j) hr=hr+12; } parsed_number=(hr*60+mn); } } if (parsed_number==-1) return -1; else return 1; ]; !---------------------------------------------------------------------------- ! Now the grammar for the new Wait actions. ! You can use parsetime in other new actions. For example, you could perhaps ! allow setting of watches, etc. with grammar like: ! extend "set" first ! * is_timepiece "to" parsetime -> SetClock; !---------------------------------------------------------------------------- extend 'wait' replace * -> Wait * 'until'/'til'/'till'/'for' parsetime -> WaitUntil * 'for' number 'hour'/'hours' -> WaitHours * 'for' number 'minute'/'minutes' -> WaitMoves * 'for' number 'hour'/'hours' number 'minute'/'minutes' -> WaitHours * 'for' number 'hour'/'hours' 'and' number 'minute'/'minutes' -> WaitHours * number 'minute'/'minutes' -> WaitMoves * number 'hour'/'hours' -> WaitHours * number -> WaitMoves * number 'hour'/'hours' number 'minute'/'minutes' -> WaitHours * number 'hour'/'hours' 'and' number 'minute'/'minutes' -> WaitHours * parsetime -> WaitUntil;