! Trinitystat.h ! This library gives the ability to switch between the normal statusline and a ! Trinity style statusline. It works for both the Z-Machine and Glulx, and ! should work on all three compilers. (G.N.'s Z-Machine compiler, A.P.'s Glulx ! compiler, and A.P.'s biplatform compiler.) It has only been tested on the ! biplatform compiler, though. ! You must, of course, Replace DrawStatusLine; for this to work. ! An object is supplied, statline, with a property trinity. If trinity is true, ! then you will have a Trinity-style line. Guess what happens if trinity is ! false? ! If you have defined Constant DEBUG;, then you get an extra debugging verb. ! Typing "trinity" will switch statusline styles. ! Copyright (C) 2000 by Jonathan Rosebaugh. Released under GPL version 2. ! see http://www.gnu.org/licenses/old-licenses/gpl-2.0.html ! This is necessary to compile with Graham's current Inform 6.21 compiler. #ifndef WORDSIZE; Constant TARGET_ZCODE; Constant WORDSIZE 2; #endif; ! Debugging verb #ifdef DEBUG; Object statline with trinity 0; Verb meta "trinity" * -> Trinity; [TrinitySub; statline.trinity = (~~(statline.trinity)); ]; #endif; ! Z-Code version #ifdef TARGET_ZCODE; Array printed_text table 64; #IFV5; [ DrawStatusLine width posa posb i j; if (statline.trinity) { i = 0->33; if (i==0) i=80; font off; @split_window 1; @buffer_mode 0; @set_window 1; style reverse; @set_cursor 1 1; spaces(i); printed_text-->0 = 64; @output_stream 3 printed_text; if (location == thedark) print (name) location; else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } @output_stream -3; j=(i-(printed_text-->0))/2; @set_cursor 1 j; print (name) location; spaces(j-1); style roman; @buffer_mode 1; @set_window 0; font on; } else { @split_window 1; @set_window 1; @set_cursor 1 1; style reverse; width = 0->33; posa = width-26; posb = width-13; spaces width; @set_cursor 1 2; if (location == thedark) print (name) location; else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } if ((0->1)&2 == 0) { if (width > 76) { @set_cursor 1 posa; print (string) SCORE__TX, sline1; @set_cursor 1 posb; print (string) MOVES__TX, sline2; } if (width > 63 && width <= 76) { @set_cursor 1 posb; print sline1, "/", sline2; } } else { @set_cursor 1 posa; print (string) TIME__TX; LanguageTimeOfDay(sline1, sline2); } @set_cursor 1 1; style roman; @set_window 0; } ]; #ENDIF; #endif; ! TARGET_ZCODE ! Glulx version #ifdef TARGET_GLULX; Array bluelalablankage-->1; [ DrawStatusLine width height posa posb centerarea; ! If we have no status window, we must not try to redraw it. if (gg_statuswin == 0) return; ! If there is no player location, we shouldn't try either. if (location == nothing || parent(player) == nothing) return; glk($002F, gg_statuswin); ! set_window StatusLineHeight(gg_statuswin_size); glk($0025, gg_statuswin, gg_arguments, gg_arguments+4); ! window_get_size width = gg_arguments-->0; height = gg_arguments-->1; posa = width-26; posb = width-13; glk($002A, gg_statuswin); ! window_clear if (statline.trinity) { if (location == thedark) { centerarea = (width/2) - (PrintAnyToArray(bluelalablankage,0, location)/2); } else { FindVisibilityLevels(); if (visibility_ceiling == location) centerarea = (width/2) - ((PrintAnyToArray(bluelalablankage,0, location)) / 2); else centerarea = (width/2) - (PrintAnyToArray(bluelalablankage,0, visibility_ceiling)/2); } glk_window_move_cursor( gg_statuswin, centerarea, 0); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } else { glk($002B, gg_statuswin, 1, 0); ! window_move_cursor if (location == thedark) { print (name) location; } else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } if (width > 66) { glk($002B, gg_statuswin, posa-1, 0); ! window_move_cursor print (string) SCORE__TX, sline1; glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor print (string) MOVES__TX, sline2; } if (width > 53 && width <= 66) { glk($002B, gg_statuswin, posb-1, 0); ! window_move_cursor print sline1, "/", sline2; } } glk($002F, gg_mainwin); ! set_window ]; #endif; ! TARGET_GLULX ! zclock.h ! A nearly exact port of the daemons used in Infocom games. ! Ported by Allen Garvin, December 6, 2003 ! Use freely! ! Some useful constants. Their names come from the ZIL source for minizork Constant C_ENABLED 0; Constant C_TICK 1; Constant C_RTN 2; Constant C_INTLEN 6; ! Modify this to increase the number of possible daemons. ! It must be a multiple of C_INTLEN (by default, there can be 30 daemons) Constant C_TABLELEN 180; Array C_table --> C_TABLELEN; Global C_ints = C_TABLELEN; ! Queue is the programmer's interface to the daemons. It takes 2 arguments. ! rtn: The routine to be queued as a timer or daemon ! ticks: If positive, the timer countdown length. After 'ticks' turns ! rtn will be called. ! If negative, then behave like an Inform daemon. 'rtn' will be ! called every turn. ! If zero, the daemon or timer will be disabled. ! A queued event must be explicitly enabled. The return value of the ! function is the interrupt array for the event. If the first byte is ! set to 1, it will be enabled. If 0, it will be disabled. ! Thus: ! Queue(Foo, 5)-->C_ENABLED = true; Call 'Foo' in 5 turns ! Queue(Bar, -1)-->C_ENABLED = true; Call 'Bar' every turn ! Queue(Biff, 20); Place 'Biff' in the queue, but do not start ! the timer yet. The timer will not decrement. ! No real purpose I can see, but Infocom did ! this frequently in their Main routines ! Queue(Foo); or Queue(Foo, 0); Stop the 'Foo' timer [ Queue rtn ticks cint ; cint = QueueInterrupt(rtn); cint-->C_TICK = ticks; StartDaemon(zork_daemon); return cint; ]; ! QueueInterrupt was rarely called directly in Infocom games [ QueueInterrupt rtn end c int ; end = C_table + C_TABLELEN; c = C_table + C_ints; while( true ) { if( c ~= end ) { if( (c-->C_RTN) == rtn ) return c; c = c + C_INTLEN; } else { C_ints = C_ints - C_INTLEN; int = C_table + C_ints; int-->C_RTN = rtn; return int; } } ]; ! I decided to implement the queue by using an object with an Inform daemon. Object zork_daemon "Daemon" with name 'zdaemon', daemon [ c end tick flag; c = C_table + C_ints; end = C_table + C_TABLELEN; while( true ) { if( c == end ) { return flag; } if( c-->C_ENABLED ) { tick = c-->C_TICK; if( tick ~= 0 ) { c-->C_TICK = (tick - 1); if( tick <= 1 && (c-->C_RTN)() ) flag = 1; } } c = c + C_INTLEN; } ]; ! Notes: ! Once a routine is queued, there is no method to remove it from the ! queue. Thus, there is an absolute number of queues in a game. But ! Requeueing a routine will use the same slot, instead of using a new one. ! If you queue a routine with a negative value, there is a bug. When 32767 ! turns have passed, the number will overflow to positive, and the queue ! will then be a countdown instead of a daemon event. Though easy to fix ! I decided to leave it bug-compatible with Infocom games. In Zork I, for ! instance, after 2^15 turns the thief will quit moving around the maze. ! And neither the thief nor troll will defend themselves when attacked. ! ---------------------------------------------------------------------------- ! ! Shuffle.h ! original 1.0 Sep00 by Roger Firth (roger.firth@tesco.net) for Inform 6 ! ! The license for this code has been changed to Artistic License 2.0 ! See http://mailman.greennet.org.uk/pipermail/inform-maintenance/2014-July/001985.html ! ! ---------------------------------------------------------------------------- ! ! Installation: add the line: ! ! Include "Shuffle"; ! ! anywhere in your game AFTER the Include "Parser" statement. ! ! ---------------------------------------------------------------------------- ! ! Shuffle() is like random(), in that it returns a random number in the ! range 1..N. Unlike random, it returns a different value on each call, ! until all N possible numbers have been used, at which point it starts ! a new cycle. It's like dealing a shuffled pack of cards: the Two of Clubs ! might show up at any time, but can appear only once per deal. ! ! Shuffle() uses arrays of bit flags to track the numbers which have been ! used within a cycle. A default set of 56 flags is supplied, so that if you ! use the routine only once within your program, and you need shuffled numbers ! only in the range 1..56, then all you need do is to call the routine. ! For example, to simulate dealing a pack of cards, you might write: ! ! for (i=0 : i<52 : i++) { ! j = Shuffle(52) - 1; ! suit = j/13; ! 0,1,2,3 ! rank = 1 + j%13; ! 1,2, ... 13 ! ... ! } ! ! More commonly, however, you'll need to use Shuffle() for several different ! purposes, and here you are advised to create separate flag arrays for each. ! Declare the arrays as in these examples: ! ! Array mySmallFlags -> 2; ! for up to 8 flags ! Array myBiggerFlags -> 3; ! for up to 16 flags ! Array myMediumFlags -> 5; ! for up to 32 flags ! Array myLargeFlags -> 33; ! for up to 256 flags ! ! (That is, the data structure is a byte array in which the first byte holds ! a count 0..255, and the remaining bytes each supply eight flag bits. ! Note that if you need to generate shuffled numbers in a range greater than ! 1..256, you must modify the routine so that the count is held as a word.) ! Then, supply the array name as the second parameter to Shuffle(): ! ! j = Shuffle(7, mySmallFlags); ! j = Shuffle(20, myMediumFlags); ! ! The application for which Shuffle() was written was to randomise NPC ! responses in a number of default situations. For example: ! ! Array old_man_give -> 2; ! Array old_man_show -> 2; ! Array old_man_tell -> 2; ! ! Object old_man "old man" ! has animate ! with ... ! life [; ! ... ! Give: switch (Shuffle(2, old_man_give)) { ! 1: print_ret (The) self, " doesn't want ", (the) noun, "."; ! 2: print_ret (The) self, " ignores your offering."; ! } ! Show: switch (Shuffle(3, old_man_show)) { ! 1: print_ret (The) self, " isn't interested in ", (the) noun, "."; ! 2: print_ret (The) self, " peers back distainfully."; ! 3: "~I've seen hundreds of them in my time.~"; ! } ! Tell: switch (Shuffle(5, old_man_tell)) { ! 1: print_ret "There's no sign that ", (the) self, " has heard you."; ! 2: print_ret (The) self, " points to his broken hearing-aid."; ! 3: "~YOU'LL HAVE TO SPEAK UP - I'M A BIT DEAF.~"; ! 4: "~I don't know any more than you do.~"; ! 5: "Fancy that, then."; ! } ! ]; ! ! ---------------------------------------------------------------------------- ! !#ifdef DEBUG; message "Compiling Shuffle.h"; #endif; Array flagBit -> $01 $02 $04 $08 $10 $20 $40 $80; Array flagDefault -> 8; ! default of count byte + 56 flag bits [ Shuffle range flags n x y z; if (range < 2) return 1; if (flags == 0) flags = flagDefault; while (true) { n = random(range); ! possible return value x = (n-1)%8; ! bit 0..7 within flag byte y = 1+(n-1)/8; ! byte 1.. within flag array if ((flags->y & flagBit->x) == 0) break; } ! loop until unused flag found if (flags->0 == range-1) ! last unused flag? for (z = 1+(range-1)/8 : z>=0 : z--) flags->z = 0; ! clear count and all flags flags->0 = flags->0 + 1; ! increment count, set flag flags->y = (flags->y | flagBit->x); return n; ]; ! ---------------------------------------------------------------------------- ! !==============================================================================! ! SMARTCANTGO.H ! David Wagner ! Version 3 ! 8-Nov-1995 ! Revisited for Inform 6 by Roger Firth ! Version 5 ! 3-Feb-1999 ! Revisited for Library 6/12 enhancements by David Griffith ! Version 6 ! 24-Jul-2014 ! ! This license for this code has been changed to Artistic License 2.0 ! http://mailman.greennet.org.uk/pipermail/inform-maintenance/2014-July/001985.html ! ! Defines a routine that can be used for the cant_go property of rooms to ! helpfully list the exits, instead of saying just "You can't go that way." ! Store the code in a file "smartcantgo.h" and include it into games with the ! following line (placed anywhere after the standard Include "Parser";): ! ! Include "smartcantgo"; ! ! Once the routine has been Included, the following code: ! ! Object Crystal_Cave "Crystal Cave" ! with s_to Narrow_Passage, ! cant_go [; SmartCantGo(); ] ! ...; ! ! will produce the message "You can go only south." if the player goes ! the wrong way. ! ! Rather than explicitly add the cant_go routine to each room, you can ! add it to a Room class from which your actual rooms are derived: ! ! Class Room ! with cant_go [; SmartCantGo(); ], ! has light; ! ! In theory, you can instead change the default behaviour of the cant_go ! routine by adding the following line to your Initialise routine, but in ! practise there's a library bug/feature which prevents this working properly: ! ! ChangeDefault(cant_go, SmartCantGo); ! !==============================================================================! ! NOTES ! ! 1. If the room is dark, it prints just "You can't go that way." ! How could the player know where the exits are? ! ! 2. This routine ignores direction properties which point to concealed ! doors or are strings. Therefore this code won't work quite as intended ! in the following room: ! ! Object Library "Library" ! with description ! "A small wood-panelled library. An open window to the west ! affords a stunning view of the Tuscan coastline.", ! w_to ! "Ouch! You discover that the ~window~ is really ! an incredibly lifelike mural painted on the wall.", ! ...; ! ! 3. This routine does include direction properties that are routines ! on the assumption that the routine will return something sensible. ! If your routine returns a concealed door, you will have to do ! something different. ! ! 4. This routine is adequate for the rare occasions when the player types ! the wrong direction. One might be tempted to use this routine to ! simplify room descriptions, as in: ! ! Object Boring_Room "Boring Room" ! with description [; ! print "A small room with white walls. "; ! SmartCantGo(); ! ], ! ...; ! ! Don't do this! Part of the interest of a room is a good description ! of the exits. ! !==============================================================================! [ SmartCantGo room i dest desttype ndir; if (location == thedark) { CSubjectCant(actor, true); " go that way."; } ! Find what room the player is in. room = location; while (parent(room)) room = parent(room); ! Count the number of exits -- if a direction property is a string or ! a concealed door, don't count it; if it is a routine, count it. ndir = 0; objectloop (i in compass) { dest = room.(i.door_dir); if (dest) { desttype = ZRegion(dest); if ((desttype ~= 3) && (desttype == 2 || dest hasnt door || dest hasnt concealed)) ndir++; } } if (ndir == 0) "There are no exits."; ! Print the exits. CSubjectCan(actor, true); print " go only "; objectloop (i in compass) { dest = room.(i.door_dir); if (dest) { desttype = ZRegion(dest); if ((desttype ~= 3) && (desttype == 2 || dest hasnt door || dest hasnt concealed)) { PrintDirectionName(i); switch (--ndir) { 0: "."; 1: print " or "; default: print ", "; } } } } ]; !==============================================================================! [ PrintDirectionName obj; switch (obj) { n_obj: print "north"; s_obj: print "south"; e_obj: print "east"; w_obj: print "west"; ne_obj: print "northeast"; se_obj: print "southeast"; nw_obj: print "northwest"; sw_obj: print "southwest"; u_obj: print "up"; d_obj: print "down"; in_obj: print "in"; out_obj: print "out"; default: print "some unspecified direction"; } ]; !==============================================================================! ! seeno.h for Inform 6 with library 6/12 ! ! This code is distributed under the Artistic License version 2.0. ! See http://opensource.org/licenses/Artistic-2.0 ! ! A library to change the standard Inform message ! "You can't see any such thing." to the more specific ! "You see no xxx yyy here." Note that this can repeat typing errors ! back to the player. ! ! Written by C Knight. ! Comments and bug reports welcomed: please see ! www.metebelis3.free-online.co.uk to email. ! ! Based on dunno.h (I don't know the word "kludge") ! version 1.1 - 2 Apr 2001 by Neil Cerutti (cerutti@together.net) ! seeno.h is now entirely unlike dunno.h. ! ! Updated by David Griffith to support 6/12's first and third person voices. ! July 24, 2014 ! ! To use this file, put the command ! ! Include "seeno.h"; ! ! in your program's source code after including "parser". ! ! If you have defined your own ParserError() routine, you might instead ! call CantSeeError() routine in a LibraryMessages object ! (Inform Designer's Manual, 4th ed., section 25): ! ! Object LibraryMessages with before [; ! Miscellany: if (lm_n == 30) return CantSeeError(); ! ]; ! ! Version history 1.0 - 16 May 2003 ! Version history 1.1 - 22 Sep 2003. Fixed TAKE ALL FROM X problem. ! Can override these constants by defining them before including the file: ! ! "You see no " is now generated by CSubjectVerb() Default SEENO_MSG1 " no "; ! or "You see nothing that can be described as ~" Default SEENO_MSG2 " here."; #ifdef ParserError; #message "Warning: To use cantsee.h, call it from ParserError or LibraryMessages."; #ifnot; [ ParserError pe; if (pe == CANTSEE_PE) return CantSeeError(); rfalse; ]; #endif; [ CantSeeError wordnum word nwords stop pos end; wordnum=saved_oops; #ifdef TARGET_GLULX; nwords=parse-->0; #ifnot; nwords=parse->1; ! end of line #endif; if (wordnum > 0) { #ifdef TARGET_GLULX; word=parse-->(wordnum*3-2); #ifnot; word=parse-->(wordnum*2-1); #endif; ! this most likely the result of TAKE ALL FROM X, in which case use the default message if (word && (word->#dict_par1 & $80) ~= $80) rfalse; CSubjectVerb(actor, false, false, "see", 0, "sees"); print (string) SEENO_MSG1; do { ! print at least one word, more if the noun phrase needs it pos=WordAddress(wordnum); end = WordLength(wordnum) + pos; for (: pos0; wordnum++; if (wordnum > nwords) stop=1; ! end of line else { #ifdef TARGET_GLULX; word=parse-->(wordnum*3-2); #ifnot; word=parse-->(wordnum*2-1); #endif; if (word) { ! recognised if (word->#dict_par1 & $88 ~= $80) stop=1; ! not noun, or poss prep if (word == AND1__WD or ',//' or './/' or BUT1__WD or BUT2__WD) ! conjunction stop=1; } } ! not end of line if (~~stop) print " "; } until (stop); oops_from = saved_oops; print_ret (string) SEENO_MSG2; } else rfalse; ]; ! end seeno.h !--------------------------------------------------------------------------- ! YesNo An Inform 6 library to ask a semi-rhetorical ! Yes or no question, by L. Ross Raszewski ! (rraszews@skipjack.bluecrab.org) ! ! Several infocom games have asked the player yes or no questions which are ! partially rhetorical (Take Hitchhiker's for example) While Inform's library ! provides for askinga question and demanding an answer, it makes no such ! allowance for non-manditory yes or no questions. This does exactly that. ! send the message YesNo.Ask(ifyes,ifno,ifneither);, where IfYes and IfNo ! are routines to run or messages to print if the player answers yes or no. ! Ifneither is the string or routine that is printed if the player says ! neither yes nor no in the next move. ! ! This is my first fully object-oriented library extension, but it seems to ! work anyway. ! ! You can omit any of the three arguments (actually, you have to put a zero in ! to hold the place if you want to assign an action to ifno, but not ifyes. ! ! The answer is only accepted druing the next turn after the question is asked, ! and uses a timer, so if you're pushing the limit, sorry. ! ! If you like it, or if you don't, e-mail me and say so! Object YesNo with react_before [; Yes: if (self has on) return self.ifAff(); No: if (self has on) return self.ifNeg(); default: self.ifNeither(); ], ifAff 0, ifNeg 0, ifNeither 0, Ask [ ifyes ifno ifnone; self.ifAff=ifyes; self.ifNeg=ifno; self.ifNeither=ifnone; StartTimer(self,1); give self on; ], time_left 1, time_out [; self.ifAff=0; self.ifNeg=0; self.ifNeither=0; give self ~on;], found_in [; rtrue;], has concealed; !---------------------------------------------------------------------------- ! Sound An Inform 6 library by L. Ross Raszewski to handle ! Inform's sound capabilities. Requires sound card. ! CAUTION: MAY BEHAVE UNPREDICTABLY ON SOME INTERPRETERS ! ! Most notably, the "Sequencer" option intermittenly crashes Frotz on my ! system rather dramatically. I'm not sure if it's my interpreter or my ! computer. At any rate, be careful. I've had no trouble with the other ! operations, but that's no guarantee. ! ! This system consumes three global variables and one array: ! play_sounds - If play_sounds=1, sounds will be played, if 0, they will not. ! sound_vol - a number from 1 to 8, setting the volume ! playing - A bit array holding three bites used internally. ->0 indicates ! whether or not PlaySound has been called yet. ! ->1 indicates the currenly playing sound effect number ! ->2 is used by internal operations ! ! This library plays sound effects of the formats described in Stefan ! Jokisch's treatise on Infocom sound formats. The actual conventions of the ! format are handled by the interpreter, but, and there's a lot more on this in ! the Specification of the Z-Machine, sound_effects 1 and 2 are beeps, and 3 ! and up are sound files in an arcane and proprietary format. (There's a beta ! version of SOX that an write to them, however.) Once you have the files where ! your interpreter wants them (usually the "sound" subdirectory), the ! @sound_effect opcode plays these sounds. However, the nature of ! @sound_effect is such that its use isn't intuative. Indeed, there's three ! lines of assembly I had to stick in to make it work (liberally stolen from ! Sherlock) This library makes this useable. ! ! Usage: ! To play a sound: ! PlaySound(sound_number); ! Options: ! PlaySound(sound_number,reps); ! - plays a sound effect a set number of times ! PlaySound(sound_number,Routine,[option]); ! - Plays a sound effect, and runs Routine when the effect ! has finished. The third argument is stored in the global ! variable sequence. ! Features: ! If you're using my footnote library, a footnote will be called ! the first time PlaySound is used. The number of that footnote ! should be stored in a constant "SoundNote". Such a footnote ! could say something like "Type "SOUND OFF" to disable sounds" ! ! Routines included: ! Sequencer: Call PlaySound(Sequencer,array);. This will cause ! each sound effect in the array to be played in order. ! array->0 MUST be the number of sounds to play, however. ! Note: This only works about one time in five on my system ! if anyone could explain why, I'd be much obliged. ! Repeat and Fade: Call PlaySound(sound_number,RepAndFade); to ! cause a sound effect to repeat and fade out. ! Fade In: Call PlaySound(sound_number, FadeIn); and the sound effect ! will repeat while fading in. ! ! End-User Functions: ! Sound - Toggles sound on and off ! Sound On - Turns sound on ! Sound Off- Turns sound off ! Sound Up - Turns volume up ! Sound Down- Turns volume down ! Sound (number)- Sets volume ! ! Debugging Suite: ! Audio (number)- Plays the corresponding sound effect ! Audio (2 numbers) Plays the corresponding effect a number of times ! Audio FIN # - Fades in the sound effect ! Audio RF # - Repeats and fades sound effect ! global play_sounds=1; global sound_vol=5; global sequence; array playing -> 3; [ PlaySound i Rout k j l; if (play_sounds==0) rtrue; #IFDEF SoundNote; if (playing->0==0) print "^", (Note) SoundNote, "^"; playing->0=1; #ENDIF; if (i ofclass Routine) {sequence=Rout; indirect(i); rtrue;}; if (~~(Rout ofclass Routine)){j=Rout; Rout=0;}; if (j==0) j=1; if (Rout==0) Rout=StopSound; l=sound_vol; if (Rout==FadeIn) l=1; playing->1=i; sequence=k; @log_shift j 8 sp; @or sp l sp; @sound_effect i 2 sp Rout; ]; [ StopSound i; i=playing->1; @sound_effect i 4; ]; [ FadeIn; playing->2=1; FIner(); ]; [ FIner i j; playing->2=playing->2+1; i=playing->1; j=playing->2; @log_shift 1 8 sp; @or sp j sp; if (j<=sound_vol) @sound_effect i 2 sp FIner; ]; [ RepAndFade; playing->2=sound_vol; RnF(); ]; [ RnF j i; playing->2=playing->2-1; i=playing->1; j=playing->2; @log_shift 1 8 sp; @or sp j sp; if (j>=1) @sound_effect i 2 sp RnF; ]; [ Sequencer; playing->2=0; SeqNext(); ]; [ SeqNext j i k; playing->2=playing->2+1; j=playing->2; if (j>sequence->0) rtrue; i=sequence->j; @log_shift 1 8 k; @or k sound_vol k; @sound_effect i 2 k SeqNext; ]; [ SoundToggleSub; if (play_sounds==1) <>; if (play_sounds==0) <>; ]; [ SoundVolSub; if (noun>8 || noun <=0) "Please specify a volume level between 1 and 8."; sound_vol=noun; "Sound volume set to ", sound_vol, "."; ]; [ SoundUpSub; if (sound_vol==8) "Maximum volume"; sound_vol++; "Sound volume set to ", sound_vol, "."; ]; [ SoundDownSub; if (sound_vol==1) "Minimum volume"; sound_vol--; "Sound volume set to ", sound_vol, "."; ]; [ SoundsOnSub; play_sounds=1; "Sound on."; ]; [ SoundsOffSub; play_sounds=0; "Sound off."; ]; verb "sound" "sounds" "volume" * -> SoundToggle * "on" -> SoundsOn * "off" -> SoundsOff * "up" -> SoundUp * "down" -> SoundDown * number -> SoundVol; #IfDef DEBUG; [ AudioSub; PlaySound(noun); ]; [ AudioRSub; PlaySound(noun,RepAndFade); ]; [ AudioFINSub; PlaySound(noun,FadeIn); ]; [ AudioXRSub; PlaySound(noun,second); ]; verb "audio" * number ->Audio * number number ->AudioXR * "Fin" number ->AudioFIN * "RF" number ->AudioR; #ENDIF; !--------------------------------------------------------------------------- ! ! WhoWhat.H - Modified from WhatIs.H by A.C. Murie, by David A. Cornelson ! ! Note: I felt I abused it enough so that I would call it another name and add different ! instructions. WhatIs.H ambiguously answered who and what questions and I wanted ! them to be answered appropriately. So, I 'fixed' it. ! ! USAGE ! ! Include this file after "parser.h". ! ! Add the attribute 'queryable' to your object to allow for WHO am/is/are or WHAT is/am/are ! queries. ! ! Add the whatisit property with a string or function to describe what the object is. ! ! Add whoisit property to describe who the object is. ! ! Additionally, objects that do not appear in the game may be stored in the object ! 'QueryObjs'. They will be in scope all the time, and may be used for general game ! concepts ! ! As a final option, the actions WhoIs and WhatIs may be used in an objects 'before' code ! to print the whoisit or whatisit text. ! ! COMMENTS... ! ! If you intend to allow the player to change to different player objects using ChangePlayer, ! then make sure you write functions to determine which response is appropriate for whatisit ! and whoisit. ! ! The WhoIs routine check's if the object is animate before answering. ! ! I put smartass comments in for defaults. ! Attribute queryable; property whoisit; property whatisit; [ IsAmAre w; w=NextWord(); if (w=='is' or 'am' or 'are') return 0; return -1; ]; [ WhatIsWhatSub; "What is what?"; ]; [ QueryTopic; while( NextWordStopped() ~= -1 ) ; return 1; ]; [ WhatisQSub; "Good question."; ]; [ WhoAmISub; if( ZRegion(player.whoisit) == 2 or 3 ) { PrintOrRun(player,whoisit); rtrue; } "A serious question, to which you have no answer."; ]; [ WhatAmISub; if( ZRegion(player.whatisit) == 2 or 3 ) { PrintOrRun(player,whatisit); rtrue; } "You're probably human, but one can never tell these days."; ]; [ WhatIsSub; if( noun hasnt queryable ) "Good question."; if( ZRegion(noun.whatisit) == 2 or 3 ) { PrintOrRun(noun,whatisit); rtrue; } "You're not sure."; ]; [ WhoIsSub; if( noun hasnt animate) "That's not a 'who', it's a 'what'!"; if( noun hasnt queryable ) "Good question."; if( ZRegion(noun.whoisit) == 2 or 3 ) { PrintOrRun(noun,whoisit); rtrue; } "You're not sure."; ]; [ QueryScope; if( scope_stage == 1 ) rfalse; if( scope_stage == 2 ) { ScopeWithin( QueryObjs ); rfalse; } ]; Object QueryObjs "Queryable objects"; verb meta "who" * IsAmAre -> WhatIsWhat * IsAmAre "I" -> WhoAmI * IsAmAre "I?" -> WhoAmI * IsAmAre "I" "?" -> WhoAmI * IsAmAre scope=QueryScope -> WhoIs * IsAmAre scope=QueryScope "?" -> WhoIs * QueryTopic -> WhatisQ; verb meta "what" * IsAmAre -> WhatIsWhat * IsAmAre "I" -> WhatAmI * IsAmAre "I?" -> WhatAmI * IsAmAre "I" "?" -> WhatAmI * IsAmAre scope=QueryScope -> WhatIs * IsAmAre scope=QueryScope "?" -> WhatIs * QueryTopic -> WhatisQ; ! ------------------------------------------------------------------------- ! torch.h ! ! Copyright 2004 David Griffith ! This code is distributed under the Artistic License version 2.0. ! See http://opensource.org/licenses/Artistic-2.0 ! ! This code allows one to simulate torches (the flaming, wooden kind). ! One can have good ones (ready to be ignited), burning ones, and ! burned-out ones. ! ! ! Include this file sometime after VerbLib. ! ! ! Somewhere at the beginning of your code, you'll need to set the ! following constants: ! ! Constant TORCH_COUNT = 18; ! how many torches in the game ! Constant TORCH_VARIANCE = 7; ! some randomness ! Constant TORCH_LIFE = 50; ! how many moves a torch will last ! Constant TORCH_DECLINE = 5; ! torch starts sputtering ! Constant TORCH_DYING = 1; ! torch is about to go out ! ! Here is a handy way to give the player a burning torch at the ! begining of the game. Use it in the Initialize() function. ! ! move players_torch to player; ! players_torch.power = TORCH_LIFE; ! StartDaemon(players_torch); ! ! Make sure that you've actually created players_torch somewhere outside ! of the functions. Do it like this: ! ! BurningTorch players_torch; ! ! Now, when you want to have some torches sitting around for the player ! to pick up, do this: ! ! UnlitTorch ->; ! ! That will create one unlit torch. When picked up, the game will ! respond with "You pull the unlit torch off the wall". Similarly, you ! can use "BurningTorch ->;" to have a burning torch stuck on a wall. ! The same goes for a dead torch. Count up all the times you do this ! and put that number as TORCH_COUNT (see above). ! ! You shouldn't need to modify anything below this point. If you do, ! please let me know why and how you modified the code. ! ! Version History: ! 1.0 - 2004 ! 1.1 - 24 Jul 2014 - Update to use 6/12's CSubject functions. ! Class Torch with name "torch", power, parse_name [ i j w; if (parser_action == ##TheSame) { if ((parser_one.&name)-->0 == (parser_two.&name)-->0) { return -1; } return -2; } w = (self.&name)-->0; for (:: i++) { j = NextWord(); if (j == 'torches') parser_action=##PluralFound; else if (j~='torch' or w) return i; } ], daemon [ t newthing i; t = --(self.power); if (t == 0) { give self ~on ~light; newthing = DeadTorch.create(); give newthing moved; move newthing to parent(self); remove self; } if (self in player || self in location) { objectloop (i in player) { if (i ofclass BurningTorch) { if (i.power > TORCH_DECLINE) rtrue; } } if (t == TORCH_DECLINE + 1) "^Your torch is beginning to flicker."; if (t <= TORCH_DECLINE && t > TORCH_DYING) "^Your torch is flickering and sputtering."; if (t <= TORCH_DYING && t > 0) "^Your torch is going out."; if (t == 0) "^Your torch goes out with a fizzle."; } ], before [newthing; Smell: print "It smells like "; if (self ofclass UnlitTorch) "oil and dirt."; if (self ofclass BurningTorch) "burning oil and dirt."; if (self ofclass DeadTorch) "oily soot."; "Error in Torch class (Smell)^"; Examine: if (self ofclass UnlitTorch) "It's a torch. An oil-soaked rag is wrapped around it."; if (self ofclass DeadTorch) "It's a burned out and useless torch."; if (self ofclass BurningTorch) { print "The torch throws "; if (self.power < TORCH_DECLINE) { print "feeble"; } else { print "dancing"; } print " shadows about the place."; } "Error in Torch class (Examine)^"; Burn: if (second ~= nothing) { if (second hasnt on) CSubjectCant(actor, true); " burn ", (the) self, " with ", (the) second, "."; } if (self has on) "But it's already lit!"; <>; SwitchOff: if (verb_word == 'turn' or 'switch') "Perhaps snuffing the torch would be more useful."; if (self ofclass UnlitTorch or DeadTorch) "But it's not burning."; if (self ofclass BurningTorch) { newthing = DeadTorch.create(); move newthing to parent(self); give newthing moved; remove self; CSubjectVerb(actor, true, "snuff", 0, "snuffs"); " out the torch."; } "Error in Torch class (SwitchOff)^"; ], after [newthing; Take: if (self hasnt moved) CSubjectVerb(actor, true, "pull", 0, "pulls"); " ", (the) noun, " off the wall."; SwitchOn: if (second == nothing) { give self ~on; "Perhaps lighting the torch with something would be more useful."; } if (self ofclass UnlitTorch) { newthing = BurningTorch.create(); if (newthing == nothing) { "Programming error! Can't light the torch."; } move newthing to parent(self); newthing.power = TORCH_LIFE + random(TORCH_VARIANCE); remove self; StartDaemon(newthing); "The torch burns brightly."; } if (self ofclass BurningTorch) "But it's already burning."; if (self ofclass DeadTorch) { give self ~on; "The dead torch sputters a bit, but refuses to light."; } "Error in Torch class (SwitchOn)^"; ], has switchable float; Class UnlitTorch (TORCH_COUNT) class Torch with name "unlit" "good" "torch", short_name "unlit torch", plural "unlit torches"; Class BurningTorch (TORCH_COUNT) class Torch with name "burning" "torch", short_name "burning torch", plural "burning torches", has on light; Class DeadTorch (TORCH_COUNT) class Torch with name "dead" "bad" "torch", short_name "dead torch", plural "dead torches"; ! Redesign of menus so that submenus can open embedded in their parent menus. ! You need the lib DMenus (version 7) to use this. ! This is Glulx compatible. ! khelwood@hotmail.com ! Feel free to credit me (Khelwood) if you use this in a game. ! TMenus Version 7 ! version 5: Glulx compatible ! version 6: added 'before' checking ! version 7: added alternative pgup, pgdn, home and end keys, ! including for ZCode. ! Added FullEmblaze (see DMenus). ! Added 'after' execution. ! See DMenus for details. ! ! Mark submenus as 'transparent' to have them open inside their ! parent menu. ! ! Transparent submenus can supply 'before' and 'after' ! which are treated the same as described in DMenus. ! Particularly, if a submenu's 'before' property ! returns something nonzero, then the submenu will ! not be opened. ! ! To open a menu as a TMenu, use the routine ShowTMenu(mnu). ! TMenus should be of the class Menu. System_file; IfNDef dmenus_h; Include "DMenus"; EndIf; IfNDef tmenus_h; Constant tmenus_h; Constant QKEYT__TX = " Q = back"; [ TMenuContent mnu obj i; i=0; objectloop (obj in mnu && obj hasnt concealed) { i++; if (obj ofclass Menu && obj has transparent && obj has open) i=i+TMenuContent(obj); } return i; ]; [ TMenuNext mnu item obj; if (item ofclass Menu && item has open && item has transparent && item hasnt concealed) for (obj=child(item):obj:obj=sibling(obj)) if (obj hasnt concealed) return obj; while (item && item~=mnu) { for (obj=sibling(item):obj:obj=sibling(obj)) if (obj hasnt concealed) return obj; item=parent(item); } rfalse; ]; ! Little recursive function to make sure ! that branches of tmenus start ! off closed. There's no reason ! they should be open, but it's ! simple to check anyway. [ CloseBranches mnu obj; objectloop (obj in mnu) if (obj ofclass Menu && obj has transparent && obj hasnt concealed) { CloseBranches(obj); give obj ~open; } ]; ! Number of generations between item and mnu. ! If parent(item)==mnu, returns 1. ! If parent(parent(item))==mnu, returns 2. And so on. ! If item is not ultimately inside mnu, returns 0. [ TMenuDescent mnu item i; for (i=0:item:i++) { if (item==mnu) return i; item=parent(item); } rfalse; ]; ! Gets the position (index, from 0) ! of an item in a TMenu. ! Returns -1 if item not found. [ TMenuPos mnu item pos obj; obj=child(mnu); while (obj && obj has concealed) obj=sibling(obj); if (obj==0) return -1; pos=0; while (obj) { if (obj==item) return pos; pos++; obj=TMenuNext(mnu,obj); } return -1; ]; ! Gets the item at a given position in the TMenu. ! Returns 0 if it runs out of items. [ TMenuInPos mnu pos obj; if (pos<0) rfalse; obj=child(mnu); while (obj && obj has concealed) obj=sibling(obj); while (obj && pos>0) { pos--; obj=TMenuNext(mnu,obj); } return obj; ]; ! And here's the prize: ! Call ShowTMenu(mnu) to display mnu as ! a menu with embedded submenus. [ ShowTMenu mnu tmp lines page pages pos old_pos obj pkey page_lines cur_item top_obj cur_menu infull; if (tmp==0) { tmp=RunRoutines(mnu,before); if (tmp) return tmp; } if (top_menu==0) top_menu=mnu; cur_item=0; cur_menu=mnu; .TotalRedisplay; ! cur_item==0 means either ! 1) the routine has just started ! or 2) the menu's in a mess. ! In either case, get everything ready ! from scratch. if (cur_item && cur_item notin mnu) { cur_menu=parent(cur_item); if (~~IndirectlyContains(mnu,cur_menu)) { cur_item=0; cur_menu=mnu; CloseBranches(mnu); } } else if (cur_item==0 && cur_menu~=0 or mnu) { if (~~IndirectlyContains(mnu,cur_menu)) cur_menu=mnu; } if (cur_menu==0) cur_menu=mnu; if (cur_item && cur_item notin cur_menu) cur_item=0; top_obj=0; ! force top_obj to be recalculated tmp=2; ! count options lines=TMenuContent(mnu); if (lines==0) jump ExitMenu; ! set cur_item (if it's unset) for (obj=child(cur_menu):obj && ~~cur_item:obj=sibling(obj)) if (obj hasnt concealed && obj hasnt locked) cur_item=obj; if (cur_item==0) jump ExitMenu; ! Find the available height. #IfDef TARGET_GLULX; StatusLineHeight(1); ! set the status line to 1 ! so we can measure the screen: glk_window_get_size(gg_mainwin, gg_arguments, gg_arguments+4); screen_height = gg_arguments-->1; ! screen_height has been set to the sum of the heights ! of the two display windows. ! This'll actually be one less than the actual screen height. ! So the mainwin will be visible at the bottom. #IfNot; ! TARGET_ZCODE screen_height=0->32; #EndIf; ! TARGET_ ! Check height is vaguely sensible. #IfDef TARGET_GLULX; if (screen_height<=0 || screen_height>=250) { ! There's at least one Glulx 'terp that ! will tell you the height of the status window ! but not the height of the main window. ! So try and expand the status window and ! hopefully we can see how big it actually gets. StatusLineHeight(250); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); screen_height = gg_arguments-->1; } #EndIf; ! TARGET_ ! If that didn't work, or if we're in ZCode, ! we can do little but make a conservative estimate. ! 18 lines is enough space to list 11 items, ! or 8 if fullemblaze is active. if (screen_height<=0 || screen_height>=250) screen_height=18; switch (FullEmblaze) { FULLEMBLAZE_NEVER: infull=0; FULLEMBLAZE_ALWAYS: infull=2; FULLEMBLAZE_SOMETIMES: if (lines+7>screen_height) infull=2; else infull=0; } ! A little check here: in the highly unlikely ! cirumstance that your screen is 8 or 9 ! lines tall, that FullEmblaze information will ! completely kill your menu. if (screen_height<10) infull=0; if (screen_height<8) screen_height=8; ! 8 it the minimum height with the menu in this layout, ! and that's one option. One option per page is usable. if (lines+7+infull>screen_height) { page_lines=screen_height-7-infull; pages=1+(lines-1)/page_lines; } else { pages=1; page=1; screen_height=lines+7+infull; page_lines=lines; } .ReDisplay; ! Find pos and page. pos=TMenuPos(mnu,cur_item); if (pages>1) { page=1+pos/page_lines; pos=pos%page_lines; } EmblazeMenu(mnu,page_lines+7+infull,page,pages,infull); if (cur_menu~=mnu) { #IfDef TARGET_GLULX; glk_set_style(style_Subheader); #IfNot; ! TARGET_ZCODE style reverse; #EndIf; ! TARGET_ PrintAtPos(QKEYT__TX,screen_width-18,2+infull); } ! Set the font style to something appropriate #IfDef TARGET_GLULX; glk_set_style(style_Normal); #IfNot; ! TARGET_ZCODE style roman; font off; #EndIf; ! TARGET_ ! Find top_obj for the page if (pages>1 || top_obj==0) top_obj=TMenuInPos(mnu,(page-1)*page_lines); if (top_obj==0) jump TotalRedisplay; ! print the options. tmp=0; for (obj=top_obj:obj && tmp",old_pos-2,tmp+6+infull); } } tmp++; } old_pos=-1; ! The moving-the-pointer-up-and-down loop tmp=2*TMenuDescent(mnu,cur_item); for (::) { if (old_pos~=pos) { if (old_pos>=0) { PrintAtPos(" ",tmp,old_pos+6+infull); } old_pos=pos; ! draw the pointer MenuCursor(tmp,pos+6+infull); if (cur_menu~=mnu) print "->"; else print " >"; } ! Button press: #IfDef TARGET_GLULX; pkey=KeyCharPrimitive(gg_statuswin, true); #IfNot; ! TARGET_ZCODE @read_char 1 -> pkey; #EndIf; ! TARGET_ ! Glulx-only keys: pageup, pagedown, home, end, and resizing #IfDef TARGET_GLULX; ! On resize, go back and recalculate everything if (pkey==RESIZE__KY) jump TotalRedisplay; if (pkey==PAGEUP__KY) pkey=MINUS__KY; if (pkey==PAGEDOWN__KY) pkey=SPACE__KY; if (pkey==HOME__KY) pkey=HKEY1__KY; if (pkey==END__KY) pkey=EKEY1__KY; #EndIf; !TARGET_ ! Pageup on page 1 means "go to top" if (pkey==MINUS__KY && page==1) pkey=HKEY1__KY; ! Pagedown on the last page means "go to bottom" if (pkey==SPACE__KY && (page==pages || pos+page*page_lines>lines)) pkey=EKEY1__KY; ! page up if (pkey==MINUS__KY) { ! Try to go up page_lines number of lines tmp=0; while (tmp=page_lines) jump ReDisplay; continue; } ! Cursor up if (pkey==PKEY1__KY or PKEY2__KY or UPARROW__KY) { pos--; cur_item=elder(cur_item); while (cur_item && (cur_item has concealed || cur_item has locked)) { if (cur_item hasnt concealed) pos--; cur_item=elder(cur_item); } if (cur_item==0) { cur_item=youngest(cur_menu); while (cur_item && (cur_item has concealed || cur_item has locked)) cur_item=elder(cur_item); pos=TMenuPos(mnu,cur_item); if (page~=1+pos/page_lines) jump ReDisplay; pos=pos%page_lines; } if (pos<0 || pos>=page_lines) jump Redisplay; continue; } ! Quitting, and exitting sub-menus if (pkey==QKEY1__KY or QKEY2__KY or ESC__KY or LEFTARROW__KY) { tmp=2; if (cur_menu==mnu) break; RunRoutines(cur_menu,after); cur_item=cur_menu; cur_menu=parent(cur_menu); if (cur_menu==0) ! Certainly shouldn't happen cur_menu=mnu; give cur_item ~open; jump TotalRedisplay; } if (pkey==RET1__KY or RET2__KY or RIGHTARROW__KY) { ! Transparent submenu tmp=0; if (cur_item ofclass Menu && cur_item has transparent) { cur_menu=cur_item; give cur_menu open; tmp=RunRoutines(cur_menu,before); if (tmp==1) { give cur_menu ~open; cur_item=cur_menu; cur_menu=parent(cur_menu); ! Hold tmp's value... } if (tmp==0) { cur_item=TMenuNext(mnu,cur_menu); if (cur_item && cur_item in cur_menu) while (cur_item && (cur_item has locked || cur_item has concealed)) cur_item=sibling(cur_item); if (cur_item==0 || cur_item notin cur_menu) { cur_item=cur_menu; cur_menu=parent(cur_item); give cur_item ~open; continue; } jump TotalRedisplay; } } if (tmp==0) ! If tmp was set above by a 'before' ! execution, then we keep it until here, ! so it can be considered with these ! other possibilities. { if (cur_item ofclass Menu) tmp=ShowTMenu(cur_item); else tmp=ShowOption(cur_item); } while (cur_menu~=mnu or 0 && tmp>2) { tmp--; cur_item=cur_menu; give cur_menu ~open; cur_menu=parent(cur_menu); } if (tmp>2) { tmp--; break; } if (tmp<2) { #ifdef LIB_PRE_63; print "[Please press SPACE to continue.]^"; #ifnot; L__M(##Miscellany, 53); #endif; #IfDef TARGET_GLULX; pkey=KeyCharPrimitive(gg_mainwin,true); #IfNot; ! TARGET_ZCODE @read_char 1 -> pkey; #EndIf; ! TARGET_ } ! Checks: if (cur_item notin cur_menu) cur_item=0; while (cur_item && (cur_item has concealed || cur_item has locked)) cur_item=sibling(cur_item); ! If cur_item==0, it will be set in totalRedisplay jump TotalRedisplay; } } ! end of pointer-loop .ExitMenu; RunRoutines(mnu,after); if (top_menu~=mnu) return tmp; top_menu=0; #IfDef TARGET_GLULX; glk_set_window(gg_mainwin); glk_window_clear(gg_mainwin); StatusLineHeight(1); #IfNot; ! TARGET_ZCODE font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; #EndIf; ! TARGET_ new_line; new_line; new_line; ! Look, as long as we're alive and somewhere if (parent(player) && location && ~~deadflag) { LookSub(1); rtrue; } return tmp; ]; ! end of ShowTMenu EndIf; ! tmenus_h !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! DOORS An INFORM 6.10 library creating easy-to-code doors ! By L. Ross Raszewski ! ! Updated by David Griffith in October 2012. ! Corrected bug that prevented moveclass.h NPCs from being able to ! operate doors. Also reformatted text. ! ! ! According to "The Inform Designer's Manual, 3rd edition" ! (reprinted without permission) ! To set up a door: ! ! (a) give the object the door attribute; ! (b) set its door_to property to the destination; ! (c) set its door_dir property to the direction which that would be, ! such as n_to; ! (d) make the room's map connection in that direction point to the door ! itself. ! ! Yeah, right. The majority of doors you'll meet in real life are two-sided ! and anyone who's tried to code up a two-sided door in Inform knows what ! they're in for. You'll need something like a ! door_to [; if (location == shrine) return jungle; return shrine;], ! and a ! door_dir [; if (location == shrine) return u_to; return d_to;], ! Now, I'll agree this isn't all that bad, but I'm sure you've wished it ! was easier. Many a newbie has died trying to code up something lke this. ! ! Not anymore. ! ! Observe if you will the Connector class. ! ! Suppose you need a door between roomA and roomB, where roomA is to the east ! of roomB. ! ! ! Connector door "door" ! with name "door", ! e_to roomA, ! w_to roomB, ! when_open "The door is open", ! when_closed "The door is closed", ! has openable; ! ! Voila! This simple syntax is equivalent to the conventional syntax: ! ! Object door "door" ! with name "door", ! door_to [; if (location == roomA) return roomB; return roomA;], ! door_dir [; if (location == roomB) return e_to; return w_to;], ! when_open "The door is open", ! when_closed "The door is closed", ! found_in roomA roomB; ! has openable door static; ! ! I can hear you now. "What? I save three lousy lines?" Well, yeah, but in ! a game with a lot of doors, you've saved yourself some syntax. And it ! certaintly is more intuitive. ! ! The only catch, for the sake of economy, is that you have to call the ! function InitDoors(); in your initialise. ! ! Also by the author: ! The AltMenuing system ! Center Centers a line of text in either window. ! Domenu Improved menuing with multiple description lines ! Altmenu Object oriented menuing system ! Hints Altmenu hint system ! Sound The Inform Sound System ! YesNo pseudo-rhetorical Yes or no questions ! Date Datekeeping and printing ! Footnote Autonumbering footnotes ! Locktest Default key selection ! Newlock Key-side lock definition ! Ordinal Ordinal number printing ! Whatis "What is a" questions ! Senses Recursive sensory perception ! Pmove Move objects into the tree as the youngest object ! Movie Non-interactive cut-scenes ! Manual inform "instruction manual" system ! ! Coming soon: ! Converse Hit-word based conversation ! MenuTalk Menu-based Conversation ! UniCursr Unicursor- Text adventures with the contol simplicity ! of omnifunction click graphic games (ie. Type "clock" ! to examine, take, or manipulate the clock) ! ! Please write me and tell me what you think. Class Connector with door_dir [; if (self in self.sidea) return self.dira; return self.dirb; ], door_to [; if (self in self.sidea) return self.sideb; return self.sidea; ], sidea 0, dira 0, sideb 0, dirb 0, found_in 0 0, has door static; [ InitDoors o i j; objectloop (j ofclass Connector) { objectloop (o in compass) { i=o.door_dir; if (j provides i) { j.sidea=j.i; j.dirb=i; }; }; objectloop (o in compass) { i=o.door_dir; if ((j provides i) && (j.dirb~=i)) { j.sideb=j.i; j.dira=i; }; }; j.&found_in-->0=j.sidea; j.&found_in-->1=j.sideb; }; ]; ! FOOTNOTE.H An Inform Library Extention for Inform 5.5 or greater ! To produce sequentially numbered footnotes. ! L. Ross Raszewski ! VERSION 2.0 ! New features: *Define Redisplay_Footnotes to 1 before inclusion to prevent ! the player from seeing the footnote indicator more than once ! *Define footnote1__tx to the text to be printed before the ! number and footnote2__tx to the text to be printed after it ! in order to change the default [footnote #] message ! (multi-lingual support) ! ! ! ! ! This library, based on the excercise in the manual, allows you to integrate ! footnotes into a game, which are numbered IN THE ORDER THEY APPEAR. It has ! two advantages over the simple, obvious system (ie. the one used in ! Hitchhiker's Guide): First, footnotes which have not been mentioned cannot ! be accessed. Second, the footnotes will always count upward in series, no ! matter what order they're called in. The example in the manual, I found, ! sometimes numbers the same footnote twice. I've avoided this, so that the ! same footnote will have the same number, regardless of how it is called ! (sort of like Achieved(#);) ! Include AFTER Grammar.h ! define the constant MAX_FOOTNOTES to the number of footnotes in the game ! before inclusion. ! To print a footnote in running text, call Note() with the number of the ! footnote (THe number is internal, and may not be the one that apears on the ! screen. Ex: If two footnotes have already been seen, then the statement: ! Print "All the world's a stage.", (note) 1; ! will print "All the world's a stage. [Footnote 3]", with the [Footnote 3] ! in underline print. Note() prints the text contained in Footnote1__TX ! and Footnote2__TX, which defaults to having a leading space, but no trailing ! one. ! The footnote is read by issuing the command FOOTNOTE 3 (or NOTE 3, or ! READ FOOTNOTE 3). FOOTNOTE alone will instruct the player on the use of ! Footnotes. ! ! You must define the function Printnote(n); before inclusion. This function ! actually prints the footnote text after FootnoteSub has printed the words ! [Footnote #]^ and has converted the number the player typed into its ! original number in the footnote list A sample PrintNote sub might read: ! [ PrintNote n; ! switch(n){ ! 1: "William Shakespeare"; ! 2: "Sloathes have no taste"; ! 3: {Style bold; print "Sesame Street"; style roman; " was \ ! brought to you by the letter ~K~ and the number 4.";}; ! }; ! ]; ! ! You can have a footnote do anything you like, even call another footnote. ! (I've tried it, it seems to work). ! Word of warning: The footnotes_seen array is a bit array, so I think you ! can't have more than 256 footnotes. ! ! ! System_file; Array footnotes_seen -> MAX_FOOTNOTES; Global footnote_count; #IFNDEF Redisplay_Footnotes; Constant Redisplay_Footnotes 0; #ENDIF; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Multi-lingual block. Define the following ! contants before inclusion to use alternate ! Messages !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #IFNDEF Footnote1__TX; Constant Footnote1__TX "[Footnote "; #ENDIF; #IFNDEF Footnote2__TX; Constant Footnote2__TX "]"; #ENDIF; #IFNDEF Footnote3__TX; Constant Footnote3__TX "Footnotes count upward from 1."; #ENDIF; #IFNDEF Footnote4__TX; Constant Footnote4__TX "That footnote ["; #ENDIF; #IFNDEF Footnote5__TX; Constant Footnote5__TX "] has not been mentioned."; #ENDIF; #IFNDEF Footnote6__TX; Constant Footnote6__TX "To view a footnote, type ~FOOTNOTE #~, where # is the number of the footnote you wish to read."; #ENDIF; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! End of Multi-lingual block !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! [ FNWarnSub; print_ret (string) Footnote6__TX; ]; [ Note n; if (footnotes_seen->n~=0 && Redisplay_Footnotes==1) rfalse; if (footnotes_seen->n==0) { footnote_count++; footnotes_seen->n=footnote_count;}; print " "; style underline; print (string) Footnote1__TX,footnotes_seen->n, (string) Footnote2__TX; style roman; ]; [ FootnoteSub n; if (noun>footnote_count) { print (string) Footnote4__TX,noun, (string) Footnote5__TX, "^"; rtrue; } if (noun==0) print_ret (string) Footnote3__tx; for(n=1:(footnotes_seen->n~=noun && n<=(MAX_FOOTNOTES-1)):n++); style underline; print (string) Footnote1__TX,noun,(string) Footnote2__tx, "^"; style roman; PrintNote(n); ]; Verb meta "footnote" "note" * ->FNWarn * number ->Footnote; Extend "read" * "footnote" number ->Footnote * "note" number ->Footnote * "footnote" ->FNWarn * "footnotes" ->FNWarn; #Stub PrintNote 1; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! DOORS2 An INFORM 6.15 library creating doors with ! parsed names for directions ! By Max Kalus ! ! What can this module do? ! ======================== ! In some games doors will be used a great deal. If you do, you might ! make the experience, that, if you have two doors in one room, the ! player might want to know which direction each of these leads to. ! He might even act acordingly, and enter "look at north door". The ! problem with "north" will be, that on the other side, the door ! will be "south", so giving "north" and "south" as names might not ! be the best thing to do. The case gets especially unnerving, if ! you have two doors in the same room, both leading north-south ! directions. ! ! This module will relieve everybody with above problems. This ! module provides a class called DirDoor (inherits from Connector if ! "doors.h" is included, see below). DirDoor's parse_name routine ! looks up direction names and parses them accordingly. Of course ! you can still use your name Property for each individual door. ! DirDoor takes care of these names, too. ! ! Example: ! ! DirDoor My_Door "door" ! with ! name "door" "enchanted" "unbreakable" "boring", ! s_to Room_A, ! n_to Room_B; ! ! If in Room_A, you could type "look at southern door", while in ! Room_B you could tell the parser "open north door". Nifty, eh? ! ! Using Doors2 with "doors.h" ! =========================== ! ! This module is intended to be used with L. Ross Raszewski's ! "doors.h", but can be used without it. If you use it with doors.h, ! please make sure you include "doors2.h" *after* "doors. #ifndef Connector; class Connector; #endif; Class DirDoor class Connector, with parse_name [n word; word = NextWordStopped(); while (word ~= -1) { if (IsAWordIn(word,self,name) == 1) n++; if (location == self.s_to && (word == 'north' or 'northern')) n++; if (location == self.n_to && (word == 'south' or 'southern')) n++; if (location == self.e_to && (word == 'west' or 'western')) n++; if (location == self.w_to && (word == 'east' or 'eastern')) n++; if (location == self.se_to && (word == 'northwest' or 'northeastern')) n++; if (location == self.sw_to && (word == 'northeast' or 'northeastern')) n++; if (location == self.ne_to && (word == 'southwest' or 'southwestern')) n++; if (location == self.nw_to && (word == 'southeast' or 'southeastern')) n++; word = NextWordStopped(); } return n; ], has door; ! -------------- IsAWordIn ----------------- #ifndef IsAWordIn; [ IsAWordIn w obj prop k l m; k=obj.∝ l=(obj.#prop)/2; for (m=0:mm) rtrue; rfalse; ]; #endif; !--------------------------------------------------------------------------- ! NewbieGrammar.h, by Emily Short (emshort@mindspring.com) ! ! Being grammar statements and a couple of verbs to accomodate and ! respond to nonstandard kinds of input. It interprets who and what ! questions, as well as correcting some standard errors and redirecting ! other questions to Help. ! ! The current contents of this file are dictated by people's r*if posts about ! their frustrations with early attempts to play IF, and on the messages ! log on phpZork, which reveals what an assortment of random players ! have been known to attempt. ! ! Note that the general trend of these grammar lines are to catch things that ! are not what one might consider "legitimate" IF commands, or could not ! be translated easily into another IF action. There is a separate library, ! called ExpertGrammar, that provides some extra verb synonyms and equivalencies ! -- many of which were also suggested by analysis of newbie commands. ! The two libraries are separated because it is possible that one will want ! the ExpertGrammar without all the extra encumbrances of this library file. ! ! Version 0.6 -- still imperfect. If you have suggestions about ! more things the library should handle, please contact me to ! suggest them. ! ! Thanks to Roger Firth for his corrections. ! ! 5/21/03 ! ! ! ! RIGHTS: ! ! This library file may be treated as public domain. It may be ! included with or without credit to the original author. It may be ! modified at the user's discretion. It may be freely redistributed. ! ! CONTENTS: ! ! Parsing for the following forms: ! ! PLEASE... calls TooPolite, which tells the player such ! forms are not necessary ! ! I AM, YOU ARE, HE/SHE/IT IS... calls NewbieGrammar, which reminds ! the player to use imperative verbs. ! ! WHO AM I? calls MustXMe, which teaches the syntax for EXAMINE ME, ! then executes that command. ! ! WHO ARE YOU? calls ParserHelp, which explains the parser a bit. ! ! WHERE AM I? calls MustLook, which teaches LOOK, then executes that ! command. ! ! WHERE DO I FIND MORE GAMES LIKE THIS (and a wide range of variants) ! calls ArchiveIntro, which explains about the IF archive. ! ! WHERE CAN I GO NOW? (and variants) calls Exits, which is defined ! in this file just to tell the player to look around. If you ! would like to improve the functionality, you might want to look ! at one of the libraries that handles an EXITS command. ! ! WHAT DO I DO NEXT? (and a wide range of variants) calls ! Help. ! ! WALK TO (and variants) calls LocalGo, which tells you ! that movement within a room is unnecessary. ! ! GO BACK (and variants) calls NoReturn, which tells you ! to be specific about which direction you want to return. ! ! CHECK FOR, LOOK FOR (and variants) calls SpecificSearch, which ! tells you to be specific about how to search for something ! ! Moreover, there is a catch-all, omnipresent item, which ! goes everywhere the player goes and reacts to ! ! -- attempts to use body parts to do things. This is to counteract ! newbie commands such as ! OPEN DOOR WITH FOOT, INFLATE RAFT WITH MOUTH, etc. ! -- vague instructions, such as KILL SOMEONE or TAKE SOMETHING ! -- vague locations such as HERE or ANYWHERE ! ! POSSIBLE DIRECTIONS: ! ! A couple of things one might also do if one really wanted to take this ! further: ! ! -- Use the UnknownVerb entrypoint to try to catch cases where the player is ! using a known noun to begin a sentence. Call NewbieGrammar in this case. ! ! -- Use ChooseObjects to make the allbodyparts item dispreferred in any ! disambiguation ! ! -- Use BeforeParsing to look for certain standard adverbs, or else modify ! the "I only understood you as far as..." library message to remind the ! player that adverbs are not allowed. Many of the defective commands ! found in a study of newbie command structure included things such as ! ! >SWIM ANYWAY ! >JUMP ACROSS ! >HELP PLEASE ! ! -- Implement a fully functioning "GO BACK" verb that would return the ! player to his previous location. A. O. Muniz has already released ! a library to do this with Platypus; with the standard Inform library ! it is a bit more challenging, but not impossible. ! ! ! INSTALLATION: ! ! Include "NewbieGrammar" after Grammar in your gamefile. ! ! Note that if you are actually using who, what, or where verbs in your ! gave for legitimate purposes, you will need to change the grammar ! lines accordingly. ! ! Set a ChooseObjects to make sure that allbodyparts doesn't show up ! on commands like GET ALL. ! ! It is also suggested that you Replace HelpSub before including this ! file. What is here is pretty minimal. If you wish to avoid compiling ! the grammar lines for 'help' (perhaps because you plan to use something ! else), define the constant NO_HELP_GRAMMAR before including this file. ! !--------------------------------------------------------------------------- Object allbodyparts, with short_name [; print "that"; rtrue; ], number 0, source 0, parse_name [ i j w; for (::) { j = 0; w = NextWord(); if ((self.number == 0 or 1) && ((w == 'my' or 'head' or 'hands' or 'hand' or 'ear' or 'ears') || (w == 'fist' or 'fists' or 'finger' or 'fingers' or 'thumb' or 'thumbs') || (w == 'arm' or 'arms' or 'leg' or 'legs' or 'foot' or 'feet') || (w == 'eye' or 'eyes' or 'face' or 'nose' or 'mouth' or 'teeth') || (w == 'tooth' or 'tongue' or 'lips' or 'lip'))) { self.number = 1; j = 1; } if ((self.number == 0 or 2) && (w == 'someone' or 'something' or 'anyone' or 'anything')) { self.number = 2; j = 1; } if ((self.number == 0 or 3) && (w == 'here' or 'everywhere')) { self.number = 3; j = 1; } if (j~=0) i++; else return i; } ], found_in [; rtrue; ], react_before [; if ((noun && noun == self) || (second && second == self)) { self.message(self.number); self.number = 0; rtrue; } rfalse; ], message [ x; switch(x) { 1: "Generally speaking, there is no need to refer to your body parts individually in interactive fiction. WEAR SHOES ON FEET will not necessarily be implemented, for instance; WEAR SHOES is enough. And unless you get some hint to the contrary, you probably cannot OPEN DOOR WITH FOOT or PUT THE SAPPHIRE RING IN MY MOUTH."; 2: "The game will not arbitrarily guess what you want. Be specific -- use a noun for an object you can see around you."; 3: switch(action) { ##Look, ##Search: <>; default: "You don't really need to refer to places in the game this way."; } } ], has proper scenery; [ IsAmAre; if (NextWord()=='is' or 'am' or 'are' or 'was' or 'were') return GPR_PREPOSITION; return GPR_FAIL; ]; [ DoCan; if (NextWord()=='do' or 'does' or 'would' or 'will' or 'shall' or 'can' or 'could' or 'should' or 'may' or 'must') return GPR_PREPOSITION; return GPR_FAIL; ]; [ Anybody; if (NextWord()=='I//' or 'me' or 'he' or 'she' or 'it' or 'we' or 'you' or 'they' or 'person' or 'one' or 'someone' or 'somebody' or 'anyone' or 'anybody') return GPR_PREPOSITION; return GPR_FAIL; ]; [ More; if (NextWord()=='more' or 'other' or 'another' or 'others') return GPR_PREPOSITION; return GPR_FAIL; ]; [ SomeDirection w flag num; for(::) { w = NextWord(); if (w==0) { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } if (w == 'left' or 'right' or 'straight' or 'ahead' or 'back' or 'backwards' or 'forward' or 'backward' or 'forwards' or 'on' or 'onward' or 'onwards' or 'around') { flag = 1; num++; } } ]; [ InternalNouns w flag num; for(::) { w = NextWord(); if (w==0) { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } if (w == 'song' or 'music' or 'songs') { flag = 1; num++; } if (w == 'thought' or 'idea' or 'concept') { flag = 1; num++; } if (w == 'the' or 'a' or 'some' or 'any') { flag = 0; num++; } } ]; [ ThePoint w flag num; for(::) { w = NextWord(); if (w==0) { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } if (w == 'point' or 'point?' or 'idea' or 'idea?' or 'goal' or 'goal?' or 'purpose' or 'purpose?') { flag = 1; num++; } else { if (w == 'the' or 'a') { flag = 0; num++; } else { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } } } ]; [ IsThisGame w flag num; for(::) { w = NextWord(); if (w==0) { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } if (w == 'this' or 'these' or 'this?' or 'these?') { flag = 1; num++; } if (w == 'kind' or 'kinds' or 'of' or 'sort' or 'sorts' or 'like' or 'such' or 'a' or 'as' or 'all' or 'is' or 'are') { flag = 0; num++; } if (w == 'game' or 'games' or 'story' or 'stories' or 'game?' or 'games?' or 'story?' or 'stories?' or 'interactive' or 'fiction' or 'text' or 'adventure' or 'adventures' or 'fiction?' or 'adventures?') { flag = 1; num++; } } ]; [ ThisGame w flag num; for(::) { w = NextWord(); if (w==0) { if (flag) { return GPR_PREPOSITION; } return GPR_FAIL; } if (w == 'this' or 'these' or 'this?' or 'these?') { flag = 1; num++; } if (w == 'kind' or 'kinds' or 'of' or 'sort' or 'sorts' or 'like' or 'such' or 'a' or 'as' or 'all') { flag = 0; num++; } if (w == 'game' or 'games' or 'story' or 'stories' or 'game?' or 'games?' or 'story?' or 'stories?' or 'interactive' or 'fiction' or 'text' or 'adventure' or 'adventures' or 'fiction?' or 'adventures?') { flag = 1; num++; } } ]; [ GetFind; if (NextWord()=='get' or 'find' or 'acquire') return GPR_PREPOSITION; return GPR_FAIL; ]; [ HelpName; if (NextWord()=='help' or 'assistance' or 'instructions' or 'help?' or 'assistance?' or 'instructions?') return GPR_PREPOSITION; return GPR_FAIL; ]; [ Meant; if (NextWord()=='supposed' or 'meant' or 'intended') return GPR_PREPOSITION; return GPR_FAIL; ]; [ NextThing; if (NextWord()=='now' or 'now?' or 'next' or 'next?' or 'here' or 'here?') return GPR_PREPOSITION; return GPR_FAIL; ]; [ PlayUse; if (NextWord()=='play' or 'play?' or 'use' or 'use?' or 'operate' or 'operate?' or 'type' or 'type?' or 'do' or 'do?' or 'understand' or 'understand?' or 'learn' or 'learn?' or 'work' or 'work?') return GPR_PREPOSITION; return GPR_FAIL; ]; Verb meta 'hello' 'hi' 'howdy' 'greetings' * -> HelloThere; Verb meta 'you' 'he' 'she' 'it' 'they' 'we' 'its' 'theyre' 'were' 'youre' 'hes' 'shes' * topic -> NewbieGrammar; Verb meta 'who' 'wh' 'whos' * IsAmAre 'I'/'I?' -> MustXMe * IsAmAre 'you'/'you?' -> ParserHelp * IsAmAre noun -> Examine; Verb meta 'where' * IsAmAre 'I'/'I?'/'this'/'here'/'this?'/'here?' ->MustLook * IsAmAre noun -> RightHere * IsAmAre ThePoint ThisGame -> BoredHelp * IsAmAre topic -> SpecificSearch * DoCan Anybody 'go'/'go?' -> Exits * DoCan Anybody 'go' NextThing -> Exits * DoCan Anybody GetFind ThisGame -> ArchiveIntro * DoCan Anybody GetFind More ThisGame -> ArchiveIntro * DoCan Anybody 'look' 'for' ThisGame -> ArchiveIntro * DoCan Anybody 'look' 'for' More ThisGame -> ArchiveIntro * DoCan Anybody GetFind HelpName -> OutsideHelp * DoCan Anybody GetFind HelpName 'with'/'for'/'on' ThisGame -> OutsideHelp ; Verb meta 'wheres' * 'here' -> MustLook * IsAmAre ThePoint ThisGame -> Help; Verb meta 'what' * NextThing -> Help * IsAmAre 'I'/'I?' -> MustXMe * IsAmAre 'you'/'you?' -> ParserHelp * IsAmAre 'here'/'here?' -> MustLook * IsAmAre 'this'/'this?' -> Help * IsAmAre Anybody Meant 'to' PlayUse -> Help * IsAmAre Anybody Meant 'to' PlayUse NextThing -> Help * IsAmAre Anybody Meant 'to' PlayUse 'in'/'on'/'for'/'with' ThisGame -> Help * IsAmAre noun -> Examine * IsAmAre ThePoint -> BoredHelp * IsAmAre ThePoint ThisGame -> BoredHelp * IsAmAre ThisGame 'about'/'about?'/'for?'/'for' -> BoredHelp * IsAmAre ThisGame 'about'/'about?'/'for?'/'for' -> BoredHelp * IsAmAre 'I' topic -> Help * IsThisGame -> IntroHelp * DoCan Anybody PlayUse -> VerbHelp * DoCan Anybody PlayUse NextThing -> VerbHelp * DoCan Anybody PlayUse 'in'/'on'/'for'/'with' ThisGame -> VerbHelp * IsAmAre ThisGame -> IntroHelp; Verb meta 'whats' * 'here' -> MustLook * noun -> Examine * ThePoint ThisGame -> BoredHelp; Verb meta 'how' * DoCan Anybody PlayUse -> Help * IsAmAre Anybody Meant 'to' PlayUse -> Help * IsAmAre Anybody Meant 'to' PlayUse ThisGame -> Help * DoCan Anybody PlayUse ThisGame -> Help * DoCan Anybody GetFind ThisGame -> ArchiveIntro * DoCan Anybody GetFind More ThisGame -> ArchiveIntro * DoCan Anybody GetFind HelpName -> OutsideHelp * DoCan Anybody GetFind HelpName 'with'/'for'/'on' ThisGame -> OutsideHelp * DoCan ThisGame 'work'/'work?' -> OutsideHelp; #ifndef NO_HELP_GRAMMAR; !!! added Verb 'help' * -> Help * topic -> Help; #endif; !!! added Verb 'please' 'kindly' * topic -> TooPolite; Extend only 'i//' * topic -> NewbieGrammar; Extend 'walk' * SomeDirection -> NoReturn * 'back' noun=ADirection -> Go * 'around'/'about'/'away' -> VagueGo * 'on'/'over'/'across' noun -> Enter * 'to'/'towards'/'around'/'past'/'under' noun -> LocalGo * 'over'/'up'/'down' 'to'/'towards' noun -> LocalGo; Extend 'turn' * SomeDirection -> NoReturn; Extend 'climb' * SomeDirection -> NoReturn * 'back' noun=ADirection -> Go; Verb 'keep' * 'going'/'walking'/'heading'/'running' noun=ADirection -> NoReturn * 'going'/'walking'/'heading'/'running' noun=ADirection -> MustGo; Verb 'continue' * noun=ADirection -> MustGo * 'going'/'walking'/'heading'/'running' noun=ADirection -> MustGo; Verb 'return' 'back' * -> NoReturn * 'to' topic -> NoReturn; Extend 'check' * 'for' noun -> Examine * 'for' topic -> SpecificSearch; Extend 'look' * 'for' noun -> Examine * 'for' topic -> SpecificSearch; Extend 'search' * 'for' noun -> RightHere * 'for' topic -> SpecificSearch; Verb 'find' * noun -> RightHere * topic -> SpecificSearch; Extend 'wear' * noun 'on' noun -> Wear; Extend 'sing' * InternalNouns -> InternalAccusative; Extend 'think' * InternalNouns -> InternalAccusative; [ TooPoliteSub; "The parser does not understand polite formulations such as PLEASE LOOK AROUND NOW or KINDLY OPEN THE BOX. Start your commands with an imperative verb and they will work better."; ]; [ InternalAccusativeSub; "In constructions like SING A SONG, the ~A SONG~ part is what is known as an internal accusative -- a direct object that is not actually necessary. In short: SING will do just as well. The game has difficulty with parsing abstracts like ~a song~."; ]; [ MustGoSub; print "[Generally, it is necessary to phrase commands like this as a simple direction: GO NORTH, NORTH, etc., rather than KEEP GOING NORTH, HEAD BACK NORTH, etc.]^^"; <>; ]; [ RightHereSub; print "", (The) noun, " is "; if (IndirectlyContains(player, noun)) "already in your possession!"; "in plain sight!"; ]; [ SpecificSearchSub; "If you want to look for something, try LOOK (to see the room as a whole); LOOK IN containers; LOOK UNDER large items; and SEARCH such items as piles and heaps.^^If you still can't find whatever it is, you're probably out of luck..."; ]; [ NoReturnSub; "The game does not keep track of your path through the rooms, nor your orientation within them. Instead, you should rely on absolute compass directions. If you wish to visit a new location or return to a previous one, you will have to type the directions to take you there -- NORTH, UP, etc."; ]; [ ParserHelpSub; "The voice with which you are communicating is the narrator of the game.^^[If you are having trouble with the game, try HELP.]"; ]; [ NewbieGrammarSub; "If the game is not understanding you, try issuing your commands in the imperative: e.g., >THROW THE KNIFE, but not >I WOULD REALLY LIKE TO THROW THE KNIFE. Chatty sentences such as >YOU ARE A VERY STUPID GAME will only prove themselves true.^^If you really feel that the game is looking for a word that is not a verb (as the solution to a riddle, eg.) try some variations, such as SAY FLOOBLE."; ]; [ MustLookSub; print "[You can do this in the future by typing LOOK, which is quicker and more standard.]^"; <>; ]; [ MustXMeSub; print "[You're the main character of the game. Of course, the game author may have given you a description. You can see this description in the future by typing EXAMINE ME, which is quicker and more standard.]^^"; <>; ]; Verb 'intro' * -> TotalIntro; [ TotalIntroSub; #ifdef BasicBrief; BasicBrief(); new_line; #endif; #ifdef StartingInstructions; StartingInstructions(); rtrue; #endif; "This is a work of interactive fiction. You should explore and try to do things."; ]; [ BoredHelpSub; #ifdef StartingInstructions; StartingInstructions(); rtrue; #ifnot; <>; #endif; ]; [ OutsideHelpSub; #ifdef OnlineHelp; OnlineHelp(); rtrue; #ifnot; <>; #endif; ]; Verb 'verbs' * -> VerbHelp; [ VerbHelpSub; #ifdef StandardVerbs; StandardVerbs(); rtrue; #ifnot; <>; #endif; ]; Verb 'hint' 'hints' * -> HintHelp; [ HintHelpSub; #ifdef StuckInstructions; StuckInstructions(); rtrue; #ifnot; <>; #endif; ]; [ ArchiveIntroSub; #ifdef MoreGames; MoreGames(); rtrue; #ifndef MoreGames; "You can find more games like this at the Interactive Fiction archive, http://www.ifarchive.org, and a guide to the archive at http://www.wurb.com/if/."; #endif; ]; [ HelloThereSub; "Hi!^^If you are new to Interactive Fiction, you may want to type HELP."; ]; [ ExitsSub; "If you LOOK, you may notice some compass directions you can use to move to new rooms."; ]; [ LocalGoSub; if (noun has door) <>; "There's no need to walk towards items that are already in your vicinity."; ]; Verb 'demo' * -> Demo; [ DemoSub; #ifdef CheeseSample; CheeseSample(); #ifnot; "No demo is available."; #endif; ]; [ HelpSub; "If you would like a list of verbs you could try, type VERBS.^^If the problem is more that you don't know how to get going with the game, type INTRO.^^If you'd like a sample of a game being played, type DEMO.^^Finally, if you know what you want to achieve but can't figure out how to achieve it, type HINT. ^^If you are having further difficulty with IF, try looking at some of the online help sites such as http://www.brasslantern.org."; ]; [ IntroHelpSub; #ifdef BasicBrief; BasicBrief(); rtrue; #ifnot; "This is a work of interactive fiction, in which you play the role of the main character. You interact by typing text at the prompt."; #endif; ]; ! Object- and cursor-based menus system. ! khelwood@hotmail.com ! Feel free to credit me (Khelwood) if you use this in a game. ! DMenus Version 7 ! ! version 5: glulx compatible ! version 6: added 'before' interceptions and MENU_TOPLINE ! version 7: added alternative pgup, pgdn, home and end keys, ! including for ZCode. ! Added FullEmblaze feature (see below). ! Added 'after' execution. ! To open a menu, use the routine ShowMenu(mnu). ! 1) concealed options are not shown ! 2) locked options are skipped by the cursor ! 3) LineGap can be placed in menus ! 4) menus respect child-order ! (so you can add things to menus in specific positions). ! SwitchOptions are easy to make, but there's ! a class provided, as long as you declare a constant ! INCLUDE_SWITCHOPTION before you include this lib. ! If you define INCLUDE_HINTOPTION ! it will provide HintOption: see (far) below. ! And yes, amazingly, this is Glulx compatible, ! if you have the infglk lib and bi-platform library. ! ! Use of attributes: ! any option or submenu that is 'concealed' ! will not appear; ! any option or submenu that is 'locked' ! cannot be selected, and the cursor will skip it; ! menus and options are flagged as 'open' ! for the period that their names are being ! printed in the header - so short_name can ! check this and amend their name. ! ! Classes: ! Menu provides no properties or routines, ! but only instances of this class will be ! opened as submenus. ! LineGaps are provided to be blank (unselectable) ! lines in a menu, just for formatting. ! The options in a menu do not have a class: ! they can just be Objects. ! SwitchOption and HintOption are, well, optional. ! ! The arrow keys (left, right, up, down) are ! understood alternatives to the standard Inform ! menu keys (Q, RETURN, P and N respectively). ! The menus also understand SPACE (and Page Down in Glulx) ! as 'page down', minus (and Page Up in Glulx) as 'page up', ! H (and Home in Glulx) as 'go to top', and E (and End in Glulx) ! as 'go to bottom'. ! ! The description property for Menus themselves is not used, ! though 'before' and 'after' may be (see below). ! Description routines for options are entitled ! to alter menus, remove themselves, or ! add new items. The menu will still work ! and won't become confused. ! If an option's description returns 0 or 1, ! (or if it is a string) the menu will ! print a "Press space" message ! and wait for a key before redisplaying itself. ! If an option's description returns 2, ! the menu won't wait for a key. ! If it returns 3, the menu will immediately exit itself. ! If it returns 3+n, then the menu and the n menus ! above it will all exit. ! Hence even in a deeply nested menu, returning 1000 (for instance) ! will always close all menus (and presumably return to the game). ! Menu items are displayed from eldest (child(mnu)) to youngest. ! To add an item during the game to the BOTTOM of a menu, ! use the routine MoveToBottom(item,mnu). ! Simply moving the item to the menu ('move item to mnu') ! will add it to the TOP of the menu. ! ! ! MENU_TOPLINE: ! This is printed on the two lines just above the options ! in the menu. If you want to use it, declare it before ! including this lib. If it is a routine, it will ! be passed the current menu as a parameter, so if you ! are so inclined, you could write ! [ Menu_Topline mnu; ! if (mnu provides topline) ! PrintOrRun(mnu,topline); ! else ! print " Select from:"; ! ]; ! so that menus could provide their own top line. ! ! FULLEMBLAZE: ! Since these menus now have keys for pgup, pgdn, home, and end, ! the emblazon, which normally lists the keys, can be told to ! include these. ! Set the global variable FullEmblaze to ! FULLEMBLAZE_ALWAYS, FULLEMBLAZE_NEVER or FULLEMBLAZE_SOMETIMES. ! If it is 'FULLEMBLAZE_SOMETIMES' then only menus longer ! then one page will show the extra key instructions. ! To use the FullEmblaze only on specific menus, you might use ! ! Menu "Main menu"; ! Menu -> "Fully emblazed submenu" ! with before ! [; FullEmblaze=FULLEMBLAZE_ALWAYS; ], ! after ! [; FullEmblaze=FULLEMBLAZE_NEVER; ]; ! ...... ! ! 'BEFORE' and 'AFTER': ! 'before' on a menu is run at the start of ShowMenu, ! and hence is also called for any submenu that is opened. ! 'before' on an option is run at the start of ShowOption, ! before the option is emblazed. ! If it returns nonzero, then the menu or option will ! not be displayed by the routine. ! Like 'description', a return of 2 indicates ! 'carry on immediately', a return of 3 means 'exit the menu ! that called this', and 3+n means 'exit that menu and n ! menus above it'. ! 'after' is run after a menu is exited (for a menu), or after ! an option is displayed (for an option). ! The return values for 'after' are not considered. System_file; IfNDef dmenus_h; Constant dmenus_h; Ifndef PKEY__TX; Constant LIB_PRE_63; ! Then we are using library 6/1 or 6/2, which won't have defined these: Constant NKEY__TX = " N = next option"; Constant PKEY__TX = "P = previous"; Constant QKEY1__TX = " Q = resume game"; Constant QKEY2__TX = "Q = previous menu"; Constant RKEY__TX = "RETURN = select option"; Constant NKEY1__KY = 'N'; Constant NKEY2__KY = 'n'; Constant PKEY1__KY = 'P'; Constant PKEY2__KY = 'p'; Constant QKEY1__KY = 'Q'; Constant QKEY2__KY = 'q'; EndIf; ! PKEY__TX Constant FULLEMBLAZE_NEVER = 0; Constant FULLEMBLAZE_SOMETIMES = 1; Constant FULLEMBLAZE_ALWAYS = 2; Global FullEmblaze = FULLEMBLAZE_SOMETIMES; ! A few more, for FullEmblaze: Constant SPACE__KY = ' '; Constant PGDN__TX = "SPACE = page down"; Constant MINUS__KY = '-'; Constant PGUP__TX = "- = page up"; Constant HOME__TX = "H = top"; Constant HKEY1__KY = 'H'; Constant HKEY2__KY = 'h'; Constant END__TX = "E = end"; Constant EKEY1__KY = 'E'; Constant EKEY2__KY = 'e'; #IfDef TARGET_GLULX; #IfNDef infglk_h; Include "infglk"; #EndIf; ! The keys for menu operation: Constant RESIZE__KY = $80000000; Constant END__KY = $fffffff3; Constant HOME__KY = $fffffff4; Constant PAGEDOWN__KY = $fffffff5; Constant PAGEUP__KY = $fffffff6; Constant ESC__KY = $fffffff8; Constant RET1__KY = $fffffffa; Constant RET2__KY = $fffffffa; Constant UPARROW__KY = $fffffffc; Constant DOWNARROW__KY = $fffffffb; Constant RIGHTARROW__KY = $fffffffd; Constant LEFTARROW__KY = $fffffffe; #IfNot; ! TARGET_ZCODE Constant RET1__KY = $000d; Constant RET2__KY = $000a; ! Probably unnecessary Constant ESC__KY = $001b; Constant UPARROW__KY = $0081; Constant DOWNARROW__KY = $0082; Constant LEFTARROW__KY = $0083; Constant RIGHTARROW__KY = $0084; #Endif; ! TARGET_ ! To get the routine MoveToBottom, ! define the constant Write_MoveToBottom ! before including this file. IfDef Write_MoveToBottom; IfNDef temp_pile; Object temp_pile; EndIf; [ MoveToBottom o p x; while ( (x=child(p))~=0 ) move x to temp_pile; move o to p; while ( (x=child(temp_pile))~=0 ) move x to p; ]; EndIf; IfNDef MENU_TOPLINE; Constant MENU_TOPLINE=" Select from:"; EndIf; Global screen_width; Global screen_height; #IfDef TARGET_GLULX; Array ForUseByOptions -> 32; #IfNot; ! TARGET_ZCODE Array ForUseByOptions string 128; #EndIf; Class Menu; Class LineGap has locked; [ MenuCursor x y; #IfDef TARGET_GLULX; glk_window_move_cursor(gg_statuswin,x,y); #IfNot; ! TARGET_ZCODE x++; y++; @set_cursor y x; #EndIf; ! TARGET_ ]; [ PrintAtPos str x y; #IfDef TARGET_GLULX; glk_window_move_cursor(gg_statuswin,x,y); #IfNot; ! TARGET_ZCODE x++; y++; @set_cursor y x; #EndIf; ! TARGET_ if (str ofclass String) print (string) str; if (str ofclass Routine) str.call(); if (str ofclass Object) print (name) str; ]; [ MenuPagePages mnu page pages tmp; tmp=(mnu has open); give mnu open; ! Mark the menu open while we print its header. print (name) mnu; if (~~tmp) give mnu ~open; if (pages>1) print " [",page,"/",pages,"]"; ]; [ EmblazeOption opt bar_height page pages tmp; ! Get screen_width, clear the screen, and split it. Set font style. #IfDef TARGET_GLULX; glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); screen_width = gg_arguments-->0; glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); StatusLineHeight(bar_height); glk_set_window(gg_statuswin); glk_set_style(style_Subheader); #IfNot; ! TARGET_ZCODE screen_width = 0->33; @erase_window $ffff; @split_window bar_height; @set_window 1; font off; style reverse; ! For ZCode, reverse the top line. @set_cursor 1 1; spaces screen_width; #EndIf; ! TARGET_ ! Find the length of the option title. #IfDef TARGET_GLULX; tmp=PrintAnyToArray(ForUseByOptions,32,MenuPagePages,opt,page,pages); if (tmp>=screen_width) tmp=0; else tmp=(screen_width - tmp)/2; #IfNot; ! TARGET_ZCODE if (standard_interpreter) { @storew ForUseByOptions 0 128; @output_stream 3 ForUseByOptions; MenuPagePages(opt,page,pages); @output_stream -3; if (ForUseByOptions-->0>=screen_width) tmp=0; else tmp = (screen_width - ForUseByOptions-->0)/2; } else tmp=0; #EndIf; ! TARGET_ MenuCursor(tmp,0); MenuPagePages(opt,page,pages); ]; Global top_menu = 0; [ EmblazeMenu mnu bar_height page pages infull tmp; if (infull) infull=2; EmblazeOption(mnu,bar_height,page,pages); ! Set printing style #IfDef TARGET_GLULX; glk_set_style(style_Subheader); #IfNot; ! TARGET_ZCODE font off; style reverse; tmp=2+infull; @set_cursor tmp 1; spaces screen_width; tmp++; @set_cursor tmp 1; spaces screen_width; #EndIf; ! TARGET_ PrintAtPos(NKEY__TX,1,1+infull); PrintAtPos(PKEY__TX,screen_width-13,1+infull); PrintAtPos(RKEY__TX,1,2+infull); if (top_menu==mnu) tmp=QKEY1__TX; else tmp=QKEY2__TX; PrintAtPos(tmp,screen_width-18,2+infull); ! In ZCode, mark the bottom of the menu. #IfNDef TARGET_GLULX; style roman; font off; @set_cursor bar_height 1; for (tmp=0:tmp1; ! screen_height has been set to the sum of the heights ! of the two display windows. ! This'll actually be one less than the actual screen height. ! So the mainwin will have a little room at the bottom. #IfNot; ! TARGET_ZCODE screen_height=0->32; #EndIf; ! TARGET_ ! Some interpreters won't tell you their height. ! So here, check it's something vaguely sensible. #IfDef TARGET_GLULX; if (screen_height<=0 || screen_height>=250) { ! There's at least one Glulx 'terp that ! will tell you the height of the status window ! but not the height of the main window. ! So try and expand the status window and ! hopefully we can see how big it actually gets. StatusLineHeight(250); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); screen_height = gg_arguments-->1; } #EndIf; ! TARGET_ ! If that didn't work, or if we're in ZCode, ! we can do little but make a conservative estimate. ! 18 lines is enough space to list 11 items, ! or 8 if fullemblaze is active. if (screen_height<=0 || screen_height>=250) screen_height=18; switch (FullEmblaze) { FULLEMBLAZE_NEVER: infull=0; FULLEMBLAZE_ALWAYS: infull=2; FULLEMBLAZE_SOMETIMES: if (lines+7>screen_height) infull=2; else infull=0; } ! A little check here: in the highly unlikely ! cirumstance that your screen is 8 or 9 ! lines tall, that FullEmblaze information will ! completely kill your menu. ! So turn off the fullemblaze. if (screen_height<10) infull=0; if (screen_height<8) screen_height=8; ! 8 it the minimum height with the menu in this layout, ! and that's one option. One option per page is usable. if (lines+7+infull>screen_height) { ! Set pages to however many we need, ! and use as much space as possible per page. page_lines=screen_height-7-infull; pages=1+(lines-1)/page_lines; } else { pages=1; ! Set the status window to the right ! size to fit everything in. screen_height=lines+7+infull; page_lines=lines; } page=1; .ReDisplay; ! Find out what page we're on pos=0; for (obj=child(mnu):obj:obj=sibling(obj)) if (obj hasnt concealed) { if (obj==cur_item) break; pos++; } if (obj==0) { tmp=2; jump ExitMenu; } page=1+pos/page_lines; if (page>1) pos=pos%page_lines; EmblazeMenu(mnu,page_lines+7+infull,page,pages,infull); ! Set the font style to something appropriate #IfDef TARGET_GLULX; glk_set_style(style_Normal); #IfNot; ! TARGET_ZCODE style roman; font off; #EndIf; ! TARGET_ ! If we have multiple pages, we have to make sure ! we have top_obj correct. if (pages>1) { old_pos=(page-1)*page_lines; tmp=0; top_obj=0; for (obj=child(mnu):obj:obj=sibling(obj)) if (obj hasnt concealed) { if (tmp==old_pos) { top_obj=obj; break; } tmp++; } } if (top_obj==0) { tmp=2; jump ExitMenu; } ! Display the options tmp=0; for (obj=top_obj:obj && tmp=0) { MenuCursor(3,old_pos+6+infull); print (char)' '; } old_pos=pos; ! draw the new pointer MenuCursor(3,pos+6+infull); print (char)'>'; } ! Button press: #IfDef TARGET_GLULX; pkey=KeyCharPrimitive(gg_statuswin, true); #IfNot; ! TARGET_ZCODE @read_char 1 -> pkey; #EndIf; ! TARGET_ ! Glulx-only keys: pageup, pagedown, home, end, and resizing #IfDef TARGET_GLULX; ! On resize, go back and recalculate everything if (pkey==RESIZE__KY) jump TotalRedisplay; if (pkey==PAGEUP__KY) pkey=MINUS__KY; if (pkey==PAGEDOWN__KY) pkey=SPACE__KY; if (pkey==HOME__KY) pkey=HKEY1__KY; if (pkey==END__KY) pkey=EKEY1__KY; #EndIf; ! TARGET_ ! Pageup on page 1 means "go to top" if (pkey==MINUS__KY && page==1) pkey=HKEY1__KY; ! Pagedown on the last page means "go to bottom" if (pkey==SPACE__KY && (page==pages || pos+page*page_lines>lines)) pkey=EKEY1__KY; ! page up if (pkey==MINUS__KY) { ! Desired position is page_lines higher ! than current position. pos=pos+(page-2)*page_lines; tmp=0; cur_item=0; ! count down to it from the top for (obj=child(mnu):obj:obj=sibling(obj)) if (obj hasnt concealed) { if (obj hasnt locked) cur_item=obj; if (tmp>=pos) break; tmp++; } ! cur_item is the last selectable option ! before (or at) the desired position. if (cur_item==0) { ! If we didn't find one, keep counting ! down the options to the first ! selectable thing we find. while (obj && (obj has concealed || obj has locked)) obj=sibling(obj); ! If we don't find one, we'll have to exit if (obj==0) { tmp=2; break; } cur_item=obj; } jump ReDisplay; } ! page down if (pkey==SPACE__KY) { tmp=0; obj=cur_item; ! try to go down page_lines number of lines. while (tmp=page_lines) jump ReDisplay; continue; } ! Moving the pointer up if (pkey==PKEY1__KY or PKEY2__KY or UPARROW__KY) { pos--; cur_item=elder(cur_item); while (cur_item && (cur_item has concealed || cur_item has locked)) { if (cur_item hasnt concealed) pos--; cur_item=elder(cur_item); } ! If we've gone off the top if (cur_item==0) { cur_item=youngest(mnu); pos=lines-1; while (cur_item && (cur_item has concealed || cur_item has locked)) { if (cur_item hasnt concealed) pos--; cur_item=elder(cur_item); } if (cur_item==0) ! something gone wrong { tmp=2; break; } if (page~=1+pos/page_lines) jump ReDisplay; pos=pos%page_lines; } if (pos<0 || pos>=page_lines) jump ReDisplay; continue; } ! Quitting if (pkey==QKEY1__KY or QKEY2__KY or ESC__KY or LEFTARROW__KY) { tmp=2; break; } ! Selecting if (pkey==RET1__KY or RET2__KY or RIGHTARROW__KY) { ! Selecting a menu if (cur_item ofclass Menu) tmp=ShowMenu(cur_item); else ! selecting an option tmp=ShowOption(cur_item); if (tmp>2) { tmp--; break; } if (tmp<2) { #ifdef LIB_PRE_63; print "[Please press SPACE to continue.]^"; #ifnot; L__M(##Miscellany, 53); #endif; #IfDef TARGET_GLULX; pkey=KeyCharPrimitive(gg_mainwin,true); #IfNot; ! TARGET_ZCODE @read_char 1 -> pkey; #EndIf; ! TARGET_ } ! It's possible that an option might change ! when it's selected - moving it, concealing ! or locking it. If that happens, we have to ! find a selectable option to leave the cursor at. if (cur_item notin mnu) cur_item=0; while (cur_item && (cur_item has concealed || cur_item has locked)) cur_item=sibling(cur_item); ! We can leave cur_item at 0 if necessary, ! and it will be set to the first selectable ! option in the menu. jump TotalRedisplay; } } ! end of the pointer-moving loop .ExitMenu; RunRoutines(mnu,after); if (top_menu~=mnu) return tmp; top_menu=0; #IfDef TARGET_GLULX; glk_set_window(gg_mainwin); glk_window_clear(gg_mainwin); StatusLineHeight(1); #IfNot; ! TARGET_ZCODE font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; #EndIf; ! TARGET_ new_line; new_line; new_line; ! Look, as long as we're alive and somewhere if (parent(player) && location && ~~deadflag) { LookSub(1); rtrue; } return tmp; ]; ! Call with ShowOption(option,true) ! to skip before-checking. [ ShowOption opt fl; if (fl==0) { fl=RunRoutines(opt,before); if (fl) return fl; } EmblazeOption(opt,1,1,1); #IfDef TARGET_GLULX; glk_set_style(style_Normal); glk_set_window(gg_mainwin); #IfNot; ! TARGET_ZCODE @set_window 0; font on; style roman; #EndIf; ! TARGET_ new_line; new_line; new_line; if (opt.description) { fl=PrintOrRun(opt,description); } else { fl=1; "[No text written for this option.]"; } RunRoutines(opt,after); return fl; ]; IfDef INCLUDE_HINTOPTION; #IfNDef HINT_MESSAGE; Constant HINT_MESSAGE "[Press ENTER to finish, or 'h' for another hint.]^"; #EndIf; ! Use as follows: ! Menu "Some menu"; ! HintOption -> MyHints ! with getHints ! [ n; ! switch (n) ! { ! 0: return 3; ! number of hints ! 1: "first hint"; ! 2: "second hint"; ! 3: "third hint"; ! } ! ]; ! MyHints.number is by default 1, so the ! first hint is immediately shown. ! If you start it off with number 0, ! no hints will be shown at the start. Class HintOption with hintMessage HINT_MESSAGE, number 1, getHints 0, description [ n i; if (self.getHints) n=self.getHints(); else n=0; if (n==0) "No hints available for this option."; if (self.number=10) print (char) ' '; print (char) 40,i,(char) '/',n,(char) 41,(char) ' '; ! font on; self.getHints(i); } while (self.number i; #EndIf; ! TARGET_ if (i ~= 'H' or 'h' or DOWNARROW__KY or RIGHTARROW__KY) return 2; ! WE NEED THIS NEW_LINE EXACTLY HERE ! otherwise (in WinFrotz apparently) ! strange things happen. Tut. new_line; (self.number)++; !font off; if (n>=10 && self.number<10) print (char) ' '; print (char) 40,(self.number),(char) '/',n, (char) 41, (char) ' '; ! font on; self.getHints(self.number); } rtrue; ]; EndIf; ! hintoption IfDef INCLUDE_SWITCHOPTION; Class SwitchOption with short_name [; print (object) self; if (self has on) print " (on)"; else print " (off)"; rtrue; ], before [; if (self has on) give self ~on; else give self on; return 2; ]; EndIf; ! switchoption EndIf; ! dmenus_h ! mistype.h for Inform 6 with library 6/12 ! ! A library to automatically correct minor typing errors by the player, ! in a similar way to nitfol but on other interpreters. ! ! Written by C Knight. Not copyrighted. ! Comments and bug reports welcomed: ! please see www.metebelis3.free-online.co.uk to email. ! ! To use, include the file after including 'parser', and put ! 'Replace KeyboardPrimitive;' before 'parser'. ! So your source might look like: ! Replace KeyboardPrimitive; ! Include "Parser"; ! Include "Mistype"; ! ! (Defining the constant 'QUICKCHECK' before including this file only looks ! for inserted or transposed letters, not omissions or most substitutions, ! and so may be useful if targetting slow platforms. Defining the constant ! 'NAMEABLES' allows objects with their own parse_names and general ! parsing routines, like the cubes in 'Balances', to pass unchecked.) ! ! You can disable the whole correction feature for a while by setting ! mistype_off to 1. The user has separate controls - both must be on ! for correction to happen. ! 'mistype off'; ! 'mistype low' (only corrects single-letter errors); ! 'mistype on' (default) ! You are also reminded of DM4 p232: 'If you want objects to be ! unreferrable-to, put them somewhere else!' ! ! Adds about 2.5K to output Z-code file. (v 1.11 was 580 bytes - still ! available from website above) ! ! Version history ! v 1.0, (beta) 2 May 03 First version ! v 1.1, 6 May 03. Added Glulx optimisations, 'off switch' (Gunther's idea) ! Also buffer length checks and garbage detector. ! v 1.11 8 May 03 Nitfol and Zip extreme issues addressed. No deletions from 2-letter words (RF). ! v 1.2 12 May 03 Keyboard, grammar and scope sensitivity added. ! (beta) Now deals with multiple misspellings in last resort case ! Fixed couple of retained zeroes in Glulx buffer (hacky) ! Changed criteria for transposing space (now only if also corrects next word) ! User command added ! v 1.21 16 May 03 Added 'mistype single', and separated user from programmer controls ! Superfluous spaces not printed. 'then' and stops accounted for. ! Last resort tweaks: optimised, prepositions searched, works with Nitfol, ! reduced distance for doubled letters. ! Made compatible with nameable objects and to ignore quoted words. ! v 1.22 24 Jul 14 Updated and verified by David Griffith for 6/12. ! ! ! (TODO: structure better while retaining speed. fix code indentation and comments) ! ! The entry point chosen (KeyboardPrimitive) should not conflict with many other ! contributed libraries, or non-English versions of Inform. You could instead ! trap LanguagetoInformese(), BeforeParsing(), or Tokenise__(). ! ! Can override these constants by defining them before including the file: Default MISTYPE_MSG1 "[The story read that as ~"; Default MISTYPE_MSG2 "~].^"; Default MISTYPE_FUZZ 22; ! lower means fewer corrections ! 10 is quite selective - about 2 errors or twisted; 30 can replace word completely; ! For use by main file if needed, and separate from user control: Global mistype_off = false; Global mistype_level = 2; ! player control - 1 turns off 'last-resort' check Global mistype_close; ! for passing info between Fits() & main routine Global mistype_loose; !--------------------------------------------------- Array KeyNear -> "qldsvnxvfswrgdhfjguokhjlkpknbmpilowatedayryibcqezcutxalpa p lmpe"; ! @`A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [;\#] ~ ? #ifndef QUICKCHECK; Array NounPhraseConnectives table ! additional words not mentioned elsewhere ALL1__WD ALL2__WD ALL4__WD AND1__WD BUT1__WD BUT2__WD ME1__WD OF1__WD OTHER2__WD THEN1__WD; #endif; [ WordInScope w r; ! approximate scope rules to help choose the preferable object ! doesn't prevent player from checking if a word is recognised ! called from Fits() while (r) { if (WordInProperty(w,r,name)) rtrue; #ifdef pname; if (r provides pname && WordInProperty(w,r,pname)) rtrue; #endif; #ifdef scenic; if (r provides scenic && WordInProperty(w,r,scenic)) rtrue; #endif; ! find the next object if (child(r) && (r hasnt container || r has transparent or open or supporter or visited) ) r=child(r); ! ?? if (r==Darkness) r=player; else { while (r && sibling(r)==0) r=parent(r); if (r) (r=sibling(r)); } } ! could also check articles etc at this point when nouns are expected ! for (r=1: r<=LanguageDescriptors-->0: r=r+4) if (w == LanguageDescriptors-->r or LanguageDescriptors-->(r+3)) rtrue; rfalse; ]; #ifdef the_named_word; Constant NAMEABLES; #endif; #ifdef NAMEABLES; ! optional check for non-dictionary words Global mistype_parse; Global mistype_named; [ NamedParsesO obj t; if (mistype_named<=0 && obj provides parse_name ) { t=wn; ! just in case wn=mistype_parse; mistype_named=obj.parse_name(); wn=t; } ]; #message "Compiling spell-check to ignore nameable objects."; #endif; ! NAMEABLES #ifndef TARGET_GLULX; Replace KeyboardPrimitive; !-------------------------------------------- ! Last resort matching for Zcode !-------------------------------------------- #ifndef QUICKCHECK; Global mistype_bestv; ! for 'last resort' matching Global mistype_bestw; Global mistype_start; Global mistype_final; Array mistype_names -> 16; [ CheckWordDist w offs oldoff i j scr inc; ! create a score scr, which is lower the more similar word w is to ! the string in the buffer between mistype_start and _final @output_stream 3 mistype_names; print (address) w; @output_stream -3; for (i=0: scr0 && mistype_start->i==mistype_start->(i-1)) inc=2; ! doubled letters count for little scr=scr+inc; ! if not found for (j=2: j<2+mistype_names->1: j++) { if (mistype_start->i==mistype_names->j) { offs=(j-2 - i); if (offsj = '?'; break; } } } ! for i for (j=2: j<2+mistype_names->1: j++) if (mistype_names->j ~= '?') scr=scr+6; if (scri); ]; #endif; !-------------------------------------------- ! Replacement KeyboardPrimitive for Zcode !-------------------------------------------- [ Fits p i flag gram lineleft j tok; ! Does word 'i' make grammatical sense in parse-buffer context 'p'? if (mistype_loose) rtrue; ! accept it on second pass mistype_close=1; ! worth a second pass if there's something this close ! yes, I know you shouldn't set globals in functions, but it's economical flag=(p-->(i*2+1))->#dict_par1; ! +128 if given a noun, +8 if a preposition, +4 if plural, +1 if a verb if ((i==0 || p-->(i*2-1)==',//' or './/' or THEN1__WD) && p~=parse2) return (flag&1 || WordInScope(p-->(i*2+1), Compass) ); ! verb gram=(p-->1)->#dict_par2; if ((~~p-->1) || (~~gram)) return (p-->(i*2+1)~=0); ! unrec verb or command - no preference. ! and return 0 (don't suppress) if checking for GPR ! a rough and ready pseudo-parse searching for prepositions gram=(0-->7)-->(255-gram); lineleft = (gram)->0; j=1; while (lineleft && j<=i) { gram=gram+3; if ((tok=gram->0 &$F) ==ENDIT_TOKEN) { ! end of grammar line j=1; lineleft--; } else if (tok==PREPOSITION_TT) { ! preposition if ((gram+1)-->0 == p-->(j*2+1)) { if (i==j) rtrue; ! perfect match j++; } } else { ! looking for noun if (tok==GPR_TT) rtrue; ! could be anything - even matches unrecognised (0) while ( j1 && (p-->(j*2+1))->#dict_par1 & 136 == 128) { ! noun, not prep ! could add LanguageDescriptor check here as in line c330. if (j==i && WordInScope (p-->(j*2+1), location) ) ! a noun where a noun should be rtrue; j++; } } }; rfalse; ! not a particularly good match ]; [ KeyboardPrimitive buffer parse i start len j c1 c2 t n alt fail quot; read buffer parse; ! variables renamed #iftrue #version_number == 6; ! part of patch L61022 by Kevin Bracey @output_stream -1; @loadb buffer 1 -> sp; @add buffer 2 -> sp; @print_table sp sp; new_line; @output_stream 1; #endif; if (mistype_off || ~~mistype_level) rtrue; ! programmer and player control respectively ! JZIP has problems with overflow - can't CR if buffer is full ! if it's too many *words*, suppress the xzip/jzip warning if (parse->1 > parse->0) { ! print "[Suppressing further 'too many words' warnings.]^"; buffer->1 = parse->(1+4*parse->0)+parse->(4*parse->0)-2; ! truncate to 15 words buffer->(2+buffer->1)=0; } ! check for misspelled words if (buffer->1 < buffer->0) buffer->(2 + buffer->1) = 0; ! see library tokenise__ for (i=0: i1 && fail < alt+4: i++) if (parse-->(n=i*2+1) == '~//') {quot = ~~quot; } else ! ignore anything in quotes if (~~ (parse-->(n=i*2+1) || quot)) { ! unrecognised word ! try fixing word start=parse->(i*4+5); len=parse->(i*4+4); t = start+len-1; if (buffer->start <= '9' && buffer->t <='9') jump NxtChk; ! numbers can't be tokenised ! oops_from=saved_oops=i+1; & 'oops_word=saved_oops;' in ParserError() ! it would be nice to fiddle this in case of a miscorrection #ifdef NAMEABLES; ! check for valid non-dictionary words mistype_loose=0; ! ensure Fits checks against GPR if (i>0 && Fits(parse, i)) jump NxtChk; ! may match a GPR (general parse routine) parser_action=0; mistype_parse=i+1; ! try parsing from this word number mistype_named=0; if (parse~=parse2) ! don't bother if disambiguating LoopOverScope (NamedParsesO); if (mistype_named>0) { i=i+mistype_named-1; ! if matched multiple words continue; ! check anything after match } #endif; mistype_close=0; for (mistype_loose=0->31=='N': mistype_loose<=mistype_close: mistype_loose++) { ! try a second pass if only found poor fits ! ignore whole loop for nitfol !first try transpositions for (j=start+len +1 - 2*(i+1==parse->1 || len<3): j>start: j--) { c1=buffer->j; buffer->j=buffer->(j-1); buffer->(j-1)=c1; @tokenise buffer parse; if (parse-->n && Fits (parse,i) && (j<=t || parse-->(i*2+3)) ) jump NxtChk; ! fixed! ! - only if next word is fixed buffer->(j-1)=buffer->j; ! undo transposition buffer->j=c1; } ! end of for loop ! now common substitutions - such as nearby keys for (j=t: j>=start: j--) { c2=buffer->j; if (jstart && c2 == buffer->(j-1) or buffer->(j+1) ) { ! repeated wrong letter? buffer->j = buffer->(j-1) + buffer->(j+1) - c2; @tokenise buffer parse; if (parse-->n && Fits (parse,i)) jump NxtChk; ! fixed! } if (c2=='#') c1=28*2; ! UK keyboard, near Ret key else c1 = (c2 & $1F)*2; do { buffer->j = KeyNear->c1; @tokenise buffer parse; if (parse-->n && Fits (parse,i) ) jump NxtChk; ! fixed! } until (c1++ & 1); ! do two choices buffer->j=c2; ! restore } !now try deletions ! could impose a (len>2) test so don't turn words into single letters c1 = ' '; for (j=t: j>=start: j--) { ! j represents the one that's missed out c2=buffer->j; buffer->j=c1; c1=c2; @tokenise buffer parse; if (parse-->n && Fits (parse,i) ) jump NxtChk; ! fixed! } ! and restore for (j=t: j>start: j--) buffer->j=buffer->(j-1); buffer->start=c2; #ifndef QUICKCHECK; ! additions if (buffer->1 < buffer->0) { ! not if could overflow (buffer->1)++; buffer->(2 + buffer->1) = 0; ! see library tokenise__ for (j=buffer->1+1: j>t: j--) buffer->(j+1)=buffer->j; for (j=t+1: j>=start: j--) { for (c1='a': c1<='z': c1++) { buffer->j=c1; @tokenise buffer parse; if (parse-->n && Fits (parse,i) ) jump NxtChk; ! fixed! } buffer->j=buffer->(j-1); } ! restore for (j=start: j<=buffer->1: j++) buffer->j=buffer->(j+1); (buffer->1)--; } ! all substitutions, a-z for (j=t: j>=start: j--) { c2=buffer->j; for (c1='a': c1<='z': c1++) { buffer->j=c1; @tokenise buffer parse; if (parse-->n && Fits (parse,i) ) jump NxtChk; ! fixed! } buffer->j=c2; } #endif; } #ifndef QUICKCHECK; if (mistype_level>1) { ! try something more drastic - compare to relevant words mistype_bestv=1000; mistype_bestw=0; mistype_start=buffer+start; mistype_final=buffer+t; if (i==0 || parse-->(i*2-1)==',//' or './/' or THEN1__WD) { ! verb? ! call CheckWordDist for all verbs for (j=0-->4 +7:j < 0-->2 -5: j=j+9) if (j->#dict_par1 &1) CheckWordDist (j); } else { ! consider all likely words ! first, nouns LoopOverScope(CheckWordDistO); ! now articles and other determiners for (j=1: j<=LanguageDescriptors-->0: j=j+4) { CheckWordDist(LanguageDescriptors-->j); if (LanguageDescriptors-->(j+3)+2 > 0-->4) CheckWordDist(LanguageDescriptors-->(j+3)); } ! end loop over LanguageDescriptors ! check against all prepositions for verb, regardless of position c1=(parse-->1)->#dict_par2; if (parse-->1 && c1) { ! known verb or command c1=(0-->7)-->(255-c1); ! address of grammar for verb j = (c1)->0; ! number of grammar lines while (j) { c1=c1+3; if ((c1->0 &$F) > 9) ! end of grammar line j--; else if ((c1->0 &$F)==2) ! preposition CheckWordDist((c1+1)-->0); } ! while any grammar left } ! if (c1) ! miscellanous words from our array for (j=1: j<=NounPhraseConnectives-->0: j++) { CheckWordDist(NounPhraseConnectives-->j); } } if (mistype_bestv < MISTYPE_FUZZ) { ! found something vaguely similar @output_stream 3 mistype_names; print (address) mistype_bestw; @output_stream -3; if (mistype_names->1 > len) { ! move up buffer->1 = buffer->1 +mistype_names->1-len; if (buffer->1 > buffer->0) buffer->1=buffer->0; for (j=buffer->0 +1: j>=start+mistype_names->1: j--) { buffer->j=buffer->(j+len-mistype_names->1); } } for (j=0: j1: j++) ! and copy into buffer buffer->(start+j) = mistype_names->(2+j); for (:j(start+j)=' '; ! blank any overlap @tokenise buffer parse; } } #endif; .NxtChk; !outer loop if (parse-->n) alt++; else fail++; } ! end of 'if' each word needs fixing if (alt && fail<=alt+2) { print (string) MISTYPE_MSG1; ! [The story read that as " for (i=2: i<=1+buffer->1: i++) if (buffer->i > ' ' || (i<1+buffer->1 && buffer->(i+1) > ' ')) ! ignore repeated spaces print (char) buffer->i; print (string) MISTYPE_MSG2; ! "] } ]; ! end of KeyboardPrimitive #ifnot; !-------------------------------------------- ! Last resort matching for Glulx !-------------------------------------------- #ifndef QUICKCHECK; Global mistype_bestv; ! for 'last resort' matching Global mistype_bestw; Global mistype_start; Global mistype_final; Array mistype_names -> DICT_WORD_SIZE+4; [ CheckWordDist w offs oldoff i j scr len inc; ! create a score scr, which is lower the more similar word w is to ! the string in the buffer between mistype_start and _final len=PrintAnyToArray(mistype_names, DICT_WORD_SIZE, w); ! print word for (i=0: scr0 && mistype_start->i==mistype_start->(i-1)) inc=2; ! doubled letters count for little scr=scr+inc; ! if not found for (j=0: ji==mistype_names->j) { offs=(j - i); if (offsj = '?'; break; } } } ! for i for (j=0: jj ~= '?') scr=scr+6; if (scri); ]; #endif; !-------------------------------------------- ! Replacement KeyboardPrimitive for Glulx !-------------------------------------------- [ Fits p i w flag gram lineleft j tok; ! Does word 'i' make grammatical sense in parse-buffer context 'p'? if (mistype_loose) rtrue; ! accept it on second pass mistype_close=1; ! worth a second pass if there's something this close if (w==0) w=p-->(i*3+1); else p-->(i*3+1)=w; ! synchronise with parse flag=w->#dict_par1; ! +128 if given a noun, +8 if a preposition, +4 if plural, +1 if a verb if ((i==0 || p-->(i*3-2)==',//' or './/' or THEN1__WD) && p~=parse2) return (flag&1 || WordInScope(w, Compass) ); ! verb gram=(p-->1)->#dict_par2; if ((~~p-->1) || (~~gram)) return (w~=0); ! unrec verb or command - no preference. ! and return 0 (don't suppress) if checking for GPR ! a rough and ready pseudo-parse searching for prepositions gram=(#grammar_table+WORDSIZE)-->(255-gram); lineleft = (gram)->0; j=1; gram--; while (lineleft && j<=i) { gram=gram+5; if ((tok=gram->0 &$F) ==$F) { ! end of grammar line j=1; gram--; lineleft--; } else if (tok==2) { ! preposition if ((gram+1)-->0 == p-->(j*3+1)) { if (i==j) rtrue; ! perfect match j++; } } else { ! looking for noun if (tok==GPR_TT) rtrue; ! could be anything - even matches unrecognised (0) while (j0 && (p-->(j*3+1))->#dict_par1 & 136 == 128) { ! noun, not prep if (j==i && WordInScope (w, location) ) ! a noun where a noun should be rtrue; j++; } } }; rfalse; ! not a particularly good match ]; [ KeyboardPrimitive buffer parse i start len j c1 c2 t n alt fail quot done dictstart wordstart dictlen entrylen keylen res c3; done = false; ! this bit lifted from Andrew Plotkin's biplatform parserm.h glk($00D0, gg_mainwin, buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); ! request_line_event while (~~done) { glk($00C0, gg_event); ! select switch (gg_event-->0) { 5: ! evtype_Arrange DrawStatusLine(); 3: ! evtype_LineInput if (gg_event-->1 == gg_mainwin) { buffer-->0 = gg_event-->2; done = true; } } t = HandleGlkEvent(gg_event, 0, buffer); if (t == 2) { done = true; } else if (t == -1) { done = false; } } ! Close any quote window if (gg_quotewin) { glk($0024, gg_quotewin, 0); gg_quotewin = 0; } if (mistype_off || ~~mistype_level) rtrue; ! programmer and player control respectively ! check for misspelled words Tokenise__(buffer, parse); dictlen = #dictionary_table-->0; ! for use in later tokenisation entrylen = DICT_WORD_SIZE + 7; dictstart = #dictionary_table + WORDSIZE; for (i=0: i0 && fail < alt+4: i++) ! loop over each word, with garbage escape if (parse-->(n=i*3+1) == '"//' or '~//') quot = ~~quot; else ! ignore anything in quotes if (~~ (parse-->(n=i*3+1) || quot)) { ! unrecognised word ! try fixing word start=parse-->(i*3+3); len=parse-->(i*3+2); keylen=len+1; if (keylen>DICT_WORD_SIZE) keylen=DICT_WORD_SIZE; res=0; wordstart=buffer+start; t = start+len-1; c3=buffer->(start+len); ! keep the original word separator, while it is zeroed if (buffer->start <= '9' && buffer->t <='9') jump NxtChk; ! numbers can't be tokenised ! saved_oops=i+1; ! it'd be nice to do this in case of a miscorrection - need more work ! suggest putting 'oops_word=saved_oops;' in ParserError() #ifdef NAMEABLES; ! check for valid non-dictionary words mistype_loose=0; ! ensure Fits checks against GPR if (i>0 && Fits(parse, i)) jump NxtChk; ! may match a GPR (general parse routine) parser_action=0; mistype_parse=i+1; ! try parsing from this word number mistype_named=0; if (parse~=parse2) ! don't bother if disambiguating LoopOverScope (NamedParsesO); if (mistype_named>0) { i=i+mistype_named-1; ! if matched multiple words continue; ! check anything after match } #endif; ! convert to lower case for (j=start: jj; if (c1>='A' && c1<='Z') buffer->j=c1+32; } mistype_close=0; for (mistype_loose=0: mistype_loose<=mistype_close: mistype_loose++) { ! try a second pass if only found poor fits !first try transpositions for (j=start+len +1 - 2*(i+1==parse-->0 || len<3): j>start: j--) { c1=buffer->j; buffer->j=buffer->(j-1); buffer->(j-1)=c1; if (j>=start+len-1) { ! first pass may move spaces, second moves back. Tokenise__(buffer, parse); c3=buffer->(start+len); ! keep the original word separator, while it is zeroed if (parse-->n && Fits (parse,i) && (j<=t || parse-->(i*3+4)) ) jump NxtChk; ! fixed! } else { if (start+len(start+len)=0; ! end marker so search is accurate @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) jump NxtChk; ! fixed } buffer->(j-1)=buffer->j; ! undo transposition buffer->j=c1; } ! end of for loop if (start+len(start+len)=0; ! end marker so search is accurate ! now common substitutions - nearby keys and false repetitions for (j=t: j>=start: j--) { c2=buffer->j; if (jstart && c2 == buffer->(j-1) or buffer->(j+1) ) { ! repeated wrong letter? buffer->j = buffer->(j-1) + buffer->(j+1) - c2; @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) jump NxtChk; ! fixed! } if (c2=='#') c1=28*2; ! UK keyboard, near Ret key else c1 = (c2 & $1F)*2; do { buffer->j = KeyNear->c1; @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) jump NxtChk; ! fixed! } until (c1++ & 1); ! do two choices buffer->j=c2; ! restore } !now try deletions c1 = 0; for (j=t: j>=start: j--) { ! j represents the one that's missed out c2=buffer->j; buffer->j=c1; c1=c2; @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) { ! fixed buffer->t = ' '; ! replace the extra zero jump NxtChk; } } ! and restore for (j=t: j>start: j--) buffer->j=buffer->(j-1); buffer->start=c2; #ifndef QUICKCHECK; ! additions if (buffer-->0 >= INPUT_BUFFER_LEN-WORDSIZE) jump NxtChk; ! not if could overflow (buffer-->0)++; for (j=buffer-->0+WORDSIZE-1: j>t: j--) buffer->(j+1)=buffer->j; keylen++; if (keylen>DICT_WORD_SIZE) keylen=DICT_WORD_SIZE; len++; if (start+len(start+len)=0; ! end marker so search is accurate for (j=t+1: j>=start: j--) { for (c1='a': c1<='z': c1++) { buffer->j=c1; @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) jump NxtChk; ! fixed } buffer->j=buffer->(j-1); } ! restore for (j=start: j0+WORDSIZE-1: j++) buffer->j=buffer->(j+1); (buffer-->0)--; len--; keylen=len+1; if (keylen>DICT_WORD_SIZE) keylen=DICT_WORD_SIZE; if (start+len(start+len)=0; ! end marker so search is accurate ! all substitutions, a-z for (j=t: j>=start: j--) { c2=buffer->j; for (c1='a': c1<='z': c1++) { buffer->j=c1; @binarysearch wordstart keylen dictstart entrylen dictlen 1 1 res; if (res && Fits (parse,i,res)) jump NxtChk; ! fixed } buffer->j=c2; } #endif; buffer->(start+len)=c3; } #ifndef QUICKCHECK; if (mistype_level>1) { ! try something more drastic - compare to relevant words mistype_bestv=1000; mistype_bestw=0; mistype_start=buffer+start; mistype_final=buffer+t; if (i==0 || parse-->(i*3-2)==',//' or './/' or THEN1__WD) { ! verb? for (j=dictstart: j < dictstart+dictlen*entrylen: j=j+entrylen) if (j->#dict_par1 &1) CheckWordDist (j); } else { ! consider all likely words ! first, nouns LoopOverScope(CheckWordDistO); ! now articles and other determiners for (j=1: j<=LanguageDescriptors-->0: j=j+4) { CheckWordDist(LanguageDescriptors-->j); if (LanguageDescriptors-->(j+3) > dictstart -2) CheckWordDist(LanguageDescriptors-->(j+3)); } ! end loop over LanguageDescriptors ! check against all prepositions for verb, regardless of position c1=(parse-->1)->#dict_par2; if (parse-->1 && c1) { ! known verb or command c1=(#grammar_table+WORDSIZE)-->(255-c1); ! address of grammar for verb j = (c1)->0; ! number of grammar lines c1--; while (j) { c1=c1+5; if ((c1->0 &$F) > 9) ! end of grammar line - ENDIT_TOKEN {j--; c1--;} else if ((c1->0 &$F)==2) ! preposition { CheckWordDist((c1+1)-->0); } } ! while any grammar left } ! if (c1) ! miscellaneous words from array for (j=1: j<=NounPhraseConnectives-->0: j++) { CheckWordDist(NounPhraseConnectives-->j); } } if (mistype_bestv < MISTYPE_FUZZ) { ! found something vaguely similar c1 = PrintAnyToArray (mistype_names, DICT_WORD_SIZE, mistype_bestw); ! c1 is now length of new word if (c1 > len) { ! move up buffer-->0 = buffer-->0 +c1-len; if (buffer-->0 > INPUT_BUFFER_LEN-WORDSIZE) buffer-->0 = INPUT_BUFFER_LEN-WORDSIZE; for (j=INPUT_BUFFER_LEN-1: j>=start+c1: j--) { buffer->j=buffer->(j+len-c1); } } for (j=0: j(start+j) = mistype_names->j; for (:j(start+j)=' '; ! blank any overlap res=mistype_bestw; if (len(start+len)=c3; if (parse-->n || res) { alt++; Tokenise__(buffer, parse); ! update ready for next word } else fail++; } ! end of 'if' word needs fixing if (alt && fail<=alt+2) { print (string) MISTYPE_MSG1; ! [The story read that as " for (i=WORDSIZE: i0: i++) if (buffer->i > ' ' || (i+10 && buffer->(i+1) > ' ')) ! ignore repeated spaces print (char) buffer->i; print (string) MISTYPE_MSG2; ! "] } ]; ! end of KeyboardPrimitive #endif; !-------------------------------------------- ! End user commands !-------------------------------------------- [ MistypeOffSub; mistype_level=0; "[Automatic typing check is now off.]"; ]; [ MistypeOnSub; mistype_level=2; "[Automatic typing check is now fully on.]"; ]; [ MistypeSingleSub; mistype_level=1; "[Automatic typing check is now making only single-letter corrections.]"; ]; Verb meta 'mistype' 'spellcheck' 'correction' 'typos' 'typing' 'atc' * 'off' -> MistypeOff * 'on'/'high'/'full' -> MistypeOn * 'half'/'semi'/'single'/'low' -> MistypeSingle * -> MistypeOff; ! End of file: mistype.h ! - ! MOVECLASS, a library file to provide random, directed and 'intelligent' ! movement for NPCs ! ! Version 8.10, written by Neil Brown neil@highmount.demon.co.uk ! and Alan Trewartha alan@alant.demon.co.uk ! with Glulx additions by Matthew T. Russotto 13 March 2001 ! ! Last altered 5th April 2001. ! ! The functions of this library are too complex to go into here, so please ! refer to the brief manual which should be near to where you found this ! file, and is named 'moveman.txt'. ! ! If you are including the library file FOLLOWER.H in your game code, please ! include this file AFTERWARDS and not before, otherwise errors will occur. ! - System_file; ! This is necessary to compile with Graham's current Inform 6.21 compiler. #ifndef WORDSIZE; Constant TARGET_ZCODE; Constant WORDSIZE 2; #endif; ! NPC PROPERTIES Property before_action; ! Run before moving. Property after_action; ! Run after a successful move. Property caprice alias time_left; ! %age chance of moving when random Property npc_open; ! A property of doors Global path_size_limit = 10; ! Depth of path searching Constant RANDOM_MOVE = 0; ! The different possible move_types Constant AIMED_MOVE = 1; Constant NO_MOVE = 2; Constant PRESET_MOVE = 3; Constant ANY_PATH = $$00000000; ! The different types of AIMED_MOVEs. Constant UNLOCKED_PATH = $$00001000; ! Bitmaps so that they can be combined Constant OPEN_PATH = $$00010000; ! in principle Constant DOORLESS_PATH = $$00100000; !Ifndef NPCRoom; ! Class NPCRoom with link_data 0 0 0; !EndIf; #ifdef TARGET_ZCODE; Class MTR_npcdirclass with npc_dirs 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0; ! The calculated directions that the NPC takes. ! Note this is a word array, but the dirs are held as ! single bytes, so a path of 64 moves is possible. #ifnot; !TARGET_GLULX Class MTR_npcdirclass with npc_dirs 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0; ! The assumption that a direction will be a small ! (byte-sized) integer is bogus in Glulx ! However, Glulx allows big properties, so we don't ! lose. --MTR #endif; Class moveclass class MTR_npcdirclass with move_type RANDOM_MOVE, ! The default move_type is to move randomly caprice 20, ! Chance the NPC will move each turn when RANDOM_MOVE prepath_name 0, ! The name of the predetermined path array prepath_size 0, ! The length of the set path npc_stage 0, ! Position along set path array npc_target, ! The target destination npc_blocked [; NPC_Path(self,RANDOM_MOVE); ], ! Alternatively do nothing and wait for the path to ! unblock. Or, more intelligently look for a less ! restrictive path. npc_ifblocked 0, ! Free for use by npc_blocked npc_arrived [; NPC_Path(self,RANDOM_MOVE); ], ! Redefine this within the actual NPC object ! for more sophisticated results. Deals with what ! happens when an NPC arrives at its destination. In ! this case, it returns to random movement. walkoff "walks off", walkon "arrives", follow_action, ! } In case Follower has been included but the NPC follow_object, ! } isn't of FollowClass. daemon [ i n k; if (RunRoutines(self,before_action)) rtrue; switch(self.move_type) { 0, RANDOM_MOVE: ! Random movement #ifdef DEBUG; if (parser_trace>1) print "[RANDOM_MOVE daemon for ", (the) self ,"]^"; #endif; if (random(100)>self.caprice) rfalse; objectloop (i in compass) if (LeadsTo(i,parent(self),ANY_PATH)) { n++; #ifdef DEBUG; if (parser_trace>1) print "[Choice ",n, ": ",(GiveDir) i ,"]^"; #endif; } if (n==0) rfalse; k=random(n); n=0; ! Choose one direction #ifdef DEBUG; if (parser_trace>1) print "[Choosing ",k, "]^"; #endif; objectloop (i in compass) { if (LeadsTo(i,parent(self),ANY_PATH)) n++; if (n==k) { MoveNPCDir(self,i); break; } } 1, AIMED_MOVE : ! Moving on a calculated path #ifdef TARGET_ZCODE; i=self.&npc_dirs->self.npc_stage; #ifnot; ! TARGET_GLULX i=self.&npc_dirs-->self.npc_stage; #endif; ! TARGET #ifdef DEBUG; if (parser_trace>1) print "[AIMED_MOVE daemon moving ", (the) self, " ", (GiveDir) i,"]^"; #endif; if (i==0 || MoveNPCDir(self,i)) ! Routine only called if i~=0 self.npc_stage++; if (parent(self)==self.npc_target) self.npc_arrived(); 2, NO_MOVE : ! Not moving at all #ifdef DEBUG; if (parser_trace>1) print "[NO_MOVE daemon for ", (the) self, " doing nothing]^"; #endif; 3, PRESET_MOVE : ! Moving on a predetermined path #ifdef TARGET_ZCODE; i=(self.prepath_name)->self.npc_stage; #ifnot; ! TARGET_GLULX i=(self.prepath_name)-->self.npc_stage; #endif; ! TARGET #ifdef DEBUG; if (parser_trace>1) print "[PRESET_MOVE daemon moving ", (the) self," ", (GiveDir) i, "]^"; #endif; if (i==0 ||MoveNPCDir(self,i)) ! Routine only called if i~=0 self.npc_stage++; if (self.npc_stage>=self.prepath_size) self.npc_arrived(); default: "** MoveClass Error: move_type set to an unacceptable value for ", (the) self, " **"; } ]; [ NPC_path npc movement_type targetroom path_type steps last_room found i j k; if (metaclass(movement_type)==Object && movement_type ofclass NPCRoom) { path_type=targetroom; targetroom=movement_type; movement_type=AIMED_MOVE; ! To stay compatible with old code } #ifdef DEBUG; if (parser_trace>1) {print "^[NPC_Path setting ", (the) self," to "; switch (movement_type) { NO_MOVE: print "NO_MOVE"; RANDOM_MOVE: print "RANDOM_MOVE"; PRESET_MOVE: print "PRESET_MOVE"; AIMED_MOVE: print "AIMED_MOVE towards ", (name) targetroom; default: print "**UNDEFINED**"; } print "]^"; } #endif; if (~~(npc ofclass moveclass)) { print "*** MoveClass Error: NPC_path called for non-moveclass object '", (the) npc, "' ***"; rfalse; } if (movement_type==NO_MOVE) ! Call to set NO_MOVE { npc.move_type=NO_MOVE; rtrue; } if (movement_type==RANDOM_MOVE) ! Call to set RANDOM_MOVE { npc.move_type=RANDOM_MOVE; if (path_type~=0) npc.caprice=path_type; rtrue; } if (movement_type==PRESET_MOVE) ! Call to set PRESET_MOVE return NPCprepath(npc,targetroom,path_type); if (movement_type~=AIMED_MOVE) rfalse; ! VZEFH check if (parent(npc)==0) { print "^*** MoveClas Error: NPC_path called for object '", (the)npc, "' which has parent==0 ***"; rfalse; } if (~~(parent(npc) ofclass NPCRoom)) rfalse; ! link_data-->0 is previous ROOM in the linked list of rooms searched ! link_data-->1 is previous STEP on the possible path ! link_data-->2 is previous STEP_DIR along the possible path last_room=parent(npc); ! All rooms being searched are linked in a list last_room.&link_data-->0=0; ! starting with 'last_room' and stepping back last_room.&link_data-->1=-1; ! along the link_room of each room. if (last_room==targetroom) { found=true; ! Allowing the npc_arrived property to run steps=0; ! if the npc starts in targetroom npc.&npc_dirs-->0=0; } else { for (steps=1: steps1==0) ! Only want 'NPCRoom's with a 0 STEP { k.&link_data-->1=i; ! Add such rooms as a STEP on from 'i' k.&link_data-->2=j; ! Store direction moved to get there k.&link_data-->0=last_room; ! Add this room to the linked list last_room=k; if (k==targetroom) found=true; #Ifdef DEBUG; if (parser_trace>1) print "[Found: ",(name) k, "]^"; #Endif; } if (found) break; } if (found) break; if (i.&link_data-->0==i.&link_data-->1) i=0; ! If link_data STEP = ROOM then the else ! remaining rooms on the linked list i=i.&link_data-->0; ! have already been explored and we } ! can end this iteration. if (found) break; } } if (found) { npc.move_type=AIMED_MOVE; ! Set NPC to AIMED_MOVE npc.npc_target=targetroom; ! and fill in all the details npc.prepath_size=steps; npc.npc_stage=0; #ifdef DEBUG; if (parser_trace>1) print "[Path has ",steps, " steps...^"; #endif; i=last_room; ! Now go back to the end of the list while (i.&link_data-->1~=-1) { #ifdef DEBUG; if (parser_trace>1) print (name) i," is ", (GiveDir) i.&link_data-->2, " of...^"; #endif; #ifdef TARGET_ZCODE; npc.&npc_dirs->(steps-1)=i.&link_data-->2; #ifnot; ! TARGET_GLULX npc.&npc_dirs-->(steps-1)=i.&link_data-->2; #endif; ! TARGET steps--; ! Write npc_dirs with the STEP_DIRs i=i.&link_data-->1; ! And go back along the STEPs } #ifdef DEBUG; if (parser_trace>1) print "where we started!]^"; #endif; } #Ifdef DEBUG; if(parser_trace>1 && found==false) print "[No path found!]^"; #Endif; while (last_room~=0) { #ifdef DEBUG; if (parser_trace>4) {print "[",(name) last_room," = "; print (object) last_room.&link_data-->0, ", "; print (object) last_room.&link_data-->1, ", "; print (object) last_room.&link_data-->2, "]^"; } #endif; last_room.&link_data-->1=0; ! Go back along the linked list last_room=last_room.&link_data-->0; ! resetting the STEP data. Only } ! NPCRooms with a 0 STEP are added to ! the linked list (see above) return found; ]; [ NPCPrePath npc path_array path_length fakevar; fakevar=fakevar; ! In case code tries passing a room name too if (npc ofclass moveclass) { npc.npc_stage=0; npc.move_type=PRESET_MOVE; npc.prepath_name=path_array; npc.prepath_size=path_length; } else { "*** MoveClass Error: NPCPrePath called for non-moveclass object '", (the) npc, "' ***"; } ]; Ifndef NOISY_DIR_TOS; Message "** MoveClass::LeadsTo assuming quiet *_to (NOISY_DIR_TOS not defined) **"; Endif; [ LeadsTo direction thisroom path_type k tmp tmp2; #ifdef DEBUG; if (parser_trace>2) print "[LeadsTo ", (name)direction, " ", (name)thisroom, "]^"; #endif; if (~~(direction provides door_dir)) rfalse; if (~~(thisroom provides direction.door_dir)) rfalse; k=thisroom.(direction.door_dir); #ifdef NOISY_DIR_TOS; if (ZRegion(k)==2) rfalse; #endif; #ifndef NOISY_DIR_TOS; if (ZRegion(k)==2) k=RunRoutines(thisroom, direction.door_dir); #endif; if (ZRegion(k)~=1) rfalse; if (k has door) { if (path_type & DOORLESS_PATH) rfalse; if ((path_type & OPEN_PATH) && k hasnt open) rfalse; if ((path_type & UNLOCKED_PATH) && k has locked) rfalse; tmp=parent(k); move k to thisroom; tmp2=k.door_to(); if (tmp==0) remove k; else move k to tmp; k=tmp2; } if (~~(k ofclass NPCRoom)) rfalse; return k; ]; [ MoveNpcDir npc direction i j p message; message=2; p=parent(npc); i=LeadsTo(direction,p, ANY_PATH); if (i==0) { npc.npc_blocked(); #ifdef DEBUG; if (parser_trace>1) print "[MoveNPCDir blocked: Direction leads nowhere]^"; #endif; rfalse; } j=p.(direction.door_dir); if (ZRegion(j)==2) j=j(); if (j has door) { if (j provides npc_open) ! npc_open returns { message=j.npc_open(npc); ! 2 to go through door as normal if (message==false) ! 1 to go through door and prevent { npc.npc_blocked(); ! walkon/walkoff run/printing #ifdef DEBUG; ! 0 to stop npc using door if (parser_trace>1) print "[MoveNPCDir blocked: ", (the) j, "'s npc_open returned false]^"; #endif; rfalse; } } else if (j hasnt open) { npc.npc_blocked(); #ifdef DEBUG; if (parser_trace>1) print "[MoveNPCDir blocked: ", (the) j, " is closed with no npc_open]^"; #endif; rfalse; } } MoveNPC(npc, i, ##Go, direction); if (p==location && message==2) ! If npc_open used then it must return 2 { if (ZRegion(npc.walkoff)==3) ! if it wants walkon/walkoff to execute print "^", (The) npc, " ", (string) npc.walkoff, " ", (GiveDir) direction, ".^"; else npc.walkoff(direction); } if (parent(npc)==location && message==2) { if (ZRegion(npc.walkon)==3) print "^", (The) npc, " ", (string) npc.walkon, ".^"; else npc.walkon(direction); } if (npc provides after_action) npc.after_action(); rtrue; ]; Ifndef MoveNPC; ! Provides MoveNPC if program isn't including 'Follower' [ MoveNPC npc dest actn objn; move npc to dest; actn=actn; objn=objn; ]; Endif; [ GiveDir i; switch(i) { n_obj: print "to the north"; s_obj: print "to the south"; e_obj: print "to the east"; w_obj: print "to the west"; ne_obj: print "to the northeast"; nw_obj: print "to the northwest"; se_obj: print "to the southeast"; sw_obj: print "to the southwest"; u_obj: print "upwards"; d_obj: print "downwards"; in_obj: print "inside"; out_obj: print "outside"; } ]; ! howtoplay.h for Inform 6, by Michael Coyne (coyne@mts.net) ! How to Play text Copyright 2003 Fredrik Ramsberg ! ! This code was placed in the public domain by its authors ! See http://mailman.greennet.org.uk/pipermail/inform-maintenance/2014-May/001966.html ! and ! http://mailman.greennet.org.uk/pipermail/inform-maintenance/2014-May/001968.html ! ! ---------------------------------------------------------------------------- ! Installation: add the lines: ! ! Include "Menus"; ! optional ! Include "Howtoplay"; ! ! anywhere in your game AFTER the Include "VerbLib"; directive ! ! ---------------------------------------------------------------------------- ! Based on the structure of Roger Firth's ccpl.h ! ! Implements a HELP verb which uses Menus.h (or any compatible menu ! package) to display a menu of Fredrik Ramsberg's "How to Play" ! instructions from "A Beginner's Guide to Playing Interactive Fiction". ! Fredrik's full text can be found at: ! http://IFGuide.ramsberg.net ! ! Define NO_HOWTO_GRAMMAR to prevent the Help verb from being included. You ! will then have to provide an alternate means of running HowToMenu.select() ! ! If you are not using Menus, then HowToMenu.select will just dump the ! text to the screen. ! ! If you wish to include this in an existing menu in your game instead of ! just having it available through the help verb, you can do this: ! ! Menu MyMenu "My Menu Object" ! with description "Please select one of the following items.^", ! number 2; ! ! Option -> "How to Play" ! with description [; ! howtoplay.description(); ! ]; ! ! $Log$ ! Revision 1.1 2004/03/15 18:32:52 glen ! New file. ! ! Revision 1.2 2003/05/24 13:36:34 michael ! Removed DOS carriage-returns introduced by opening on DOS machine. ! ! Revision 1.1 2003/05/24 13:32:44 michael ! Initial revision. ! ! ---------------------------------------------------------------------------- ! system_file; #ifndef MENU; Class Menu with select [; howtoplay.description(); ]; #endif; #ifndef OPTION; Class Option; #endif; [ bf text; style bold; print (string) text; style roman; ]; Menu HowToMenu with short_name [; print "How to Play ", (string) Story; rtrue; ]; Option -> howtoplay with short_name [; print "How to Play ", (string) Story; rtrue; ], description [; print "These general instructions are an excerpt from: ~A Beginner's Guide to Playing Interactive Fiction~. The full text can be found at http://IFGuide.ramsberg.net", (bf) "^^The game starts"; print " ^When you start a game, you will first see an introduction, usually consisting of one or a few screenfuls of text, giving you some background on who you are, where you are, and perhaps even what your objectives in the game are. Whenever the game has printed a screenful of text, it will wait until you press ENTER or some other key, so that you get a chance to read everything before it scrolls off the top of the screen.^", (bf) "^How to interact"; print " ^When the introduction is over, you will get a prompt, usually ~>~, but it may be a little different from game to game. The prompt means that the game is now waiting for you to tell it what you want to do. You do this by typing in imperative commands, as if you were commanding someone. Let's say the introduction told you that you are in a kitchen, and that you can see a closed glass jar standing on the kitchen counter. Commands you could try at this point include TAKE THE JAR, or OPEN THE JAR, or perhaps EXAMINE THE JAR (Throughout this document, things that are written in capital letters are complete commands that can be typed into an IF game. They don't have to be typed in capital letters when entered into a game). If you want to, you can skip the articles: TAKE JAR will work just at well as TAKE THE JAR. If there are several different jars you could mean, the game may ask you which one you mean. Just type one or more words that uniquely identifies one of the items. For instance, if the game says ~Which jar do you mean, the blue glass jar or the green glass jar?~, you might reply BLUE to take the blue one. You can also choose to ignore the question altogether, just typing a new command.^", (bf) "^Movement"; print " ^To go to another location, most games expect you to type in which direction you want to go. You can type GO SOUTH, but just SOUTH will also do the trick, as will S (which is the commonly accepted abbreviation for SOUTH). Other directions and their abbreviations are NORTH (N), EAST (E), WEST (W), NORTHEAST (NE), SOUTHEAST (SE), NORTHWEST (NW), SOUTHWEST (SW), UP (U), DOWN (D), IN and OUT. If you are aboard a ship of some kind you may also be able to use FORE, AFT, STARBOARD and PORT.^ ^Other ways to move around may include commands like ENTER CAR, GO CAR, SIT ON MOTORCYCLE, GET ON BIKE, CLIMB ONTO SHIP, JUMP ONTO PLATFORM, DIVE INTO LAKE, BOARD SHIP, EXIT CAR, EXIT, LEAVE, GET OUT. Exactly which commands are "; #ifdef DIALECT_US; print "recognized"; #ifnot; print "recognised"; #endif; print " vary from game to game as well as from situation to situation in those games. When interacting with IF games, always try to express yourself as simply as possible. If you have tried several ways of expressing yourself and the game refuses to understand what you want to do, you are most probably on the wrong track; it's time to try something completely different.^", (bf) "^Common verbs"; print " ^As you know by now, you can use the verb TAKE to pick up items in the game. Of course, you can also use DROP to drop items. Most modern games actually "; #ifdef DIALECT_US; print "recognize"; #ifnot; print "recognise"; #endif; print " a hundred different verbs or more. With some of the most used verbs, you can also use multiple items, like this: TAKE GREEN BALL AND SCREWDRIVER or DROP ALL or PUT ALL BUT HAMMER IN BAG. You'll find that ALL is often a very useful word, although it only works with certain verbs, most notably TAKE and DROP. Here are some of most important verbs, with examples:^"; font off; print "^ LOOK or L L or LOOK AT BOB or LOOK IN JAR or LOOK UNDER BED^ TAKE TAKE KNIFE^ DROP DROP KNIFE^ EXAMINE or X EXAMINE KNIFE or X KNIFE^ SEARCH SEARCH DESK^ INVENTORY or I I^ OPEN OPEN DRAWER^ CLOSE CLOSE DRAWER^ LOCK LOCK DOOR WITH RUSTY KEY^ UNLOCK UNLOCK DOOR WITH RUSTY KEY^ ASK ASK JOHN ABOUT POLICE OFFICER^ TELL TELL JOHN ABOUT MURDER^ SAY SAY HELLO TO JOHN^ GIVE GIVE RABBIT TO BOB^ SHOW SHOW KNIFE TO POLICE OFFICER^ WAIT or Z Z^ AGAIN or G G^"; font on; print "^Other verbs you will need from time to time include ATTACK, BUY, COVER, DRINK, EAT, FILL, JUMP, KISS, KNOCK, LISTEN, MOVE, PULL, PUSH, REMOVE, READ, SIT, SLEEP, STAND, THROW, TIE, TOUCH, TURN, TYPE, UNTIE, WEAR. There are lots more. Hopefully they will seem natural to you when you need them.^", (bf) "^How time works"; print " ^Almost all IF games count time in turns, rather than hours and minutes. Every time you type something and press ENTER, one turn passes. This also means that until you press ENTER, no time passes. You could think of a turn as being something like a minute, but how long it actually is depends on what you do during that turn. If you want time to pass, but don't want to perform any actions, just type WAIT or Z. This will prove useful while waiting for someone to arrive or something to get ready in the oven etc (in the game world, not in the real world!).^ ^There are games that use real-time instead of turn-based play, but they are few and far between, and they will tell you about their real-time system at the beginning of the game.^", (bf) "^Talking to people"; print " ^The most useful ways of talking to people usually involve the verbs ASK and TELL. When using them, try to pin down the best keyword for what you are interested in, rather than longer constructs. For example, TELL BOB ABOUT HOW I SAW SHEILA GIVE A STRANGE AMULET TO ANOTHER WOMAN is not likely to yield any useful results, but TELL BOB ABOUT AMULET or perhaps TELL BOB ABOUT SHEILA may indeed be useful. In other words, you tell the game the subject you want to talk about or ask about, not exactly what to say. The game will try to make reasonable assumptions on what you want to say regarding the subject.^"; print "^Also note that many games are quite primitive when it comes to modelling people. The author has to put in an enormous amount of work to make people in the game behave realistically and respond well to conversation. In general, don't expect too much from people in the game, but there are of course games that shine in this area too. You'll also see that some authors prefer menu-based conversation, to facilitate interaction.^ ^To tell someone else to do something, type the name of the person, a comma, and then a command. Example: BOB, BREAK THE JAR. Just like in real life, most people won't automatically do something just because you tell them to. If you think Bob knows what to do with the jar, you can also try GIVE JAR TO BOB or SHOW JAR TO BOB.^", (bf) "^Special verbs"; print "^All games "; #ifdef DIALECT_US; print "recognize"; #ifnot; print "recognise"; #endif; print " some verbs that don't do anything in the game world, but tells the game something about how you want it to behave, or some special task you want it to peform. These verbs include:^"; font off; print "^ UNDO Takes back the last move you made.^ QUIT or Q Ends the current game.^ RESTART Starts the game over from the beginning.^ SAVE Saves your current position to a file on disk.^ RESTORE Loads a previously saved game position.^ HELP or ABOUT Shows some information about the game and its author, in some cases even hints to some of the puzzles.^ VERBOSE Tells the game you want a long description of every room you enter, even if you've been there before.^ BRIEF Tells the game you want a long description the first time you enter a room, and a short description when you come back. This is the default mode.^ SUPERBRIEF Tells the game you always want short descriptions of all rooms.^"; font on; print (bf) "^Getting stuck and unstuck"; print " ^While playing IF, you will get stuck. This is part of the deal -- where there are puzzles, there will also be stuckness. If you grow tired of being stuck in the same spot for too long, you can either type HELP in the game to see if there are any hints available, or you can ask other players for hints. A good place to ask for hints is the newsgroup rec.games.int-fiction (can be reached at http://groups.google.com/groups?group=rec.games.int-fiction ). That's also one of the best places to meet other IF players, discuss games you've played, get tips on games you should play and more.^ ^Oh, one last thing about playing interactive fiction. Make a map as you play. You are very likely to need it."; ]; [ HowToSub; HowToMenu.select(); ]; #IFNDEF NO_HOWTO_GRAMMAR; Verb meta 'help' 'howto' 'instructions' * -> HowTo; #ENDIF; ! ---------------------------------------------------------------------------- ! !--------------------------------------------------------------------------- ! HelpRoutines.h, by Emily Short (emshort@mindspring.com) ! Version 1.0 ! 8/16/02 ! ! Being a file of straightforward routines that will make your game print ! wodges and wodges of text about IF playing. ! ! Some portions based on paraphrase of instructional text by ! Stephen Granade. !--------------------------------------------------------------------------- ! ! RIGHTS: ! ! This library file may be treated as public domain. It may be ! used in source with or without credit to the original author. It may be ! modified at the user's discretion. It may be freely redistributed. ! The textual content of the routines may be lifted from their context ! and reused elsewhere. ! ! ! INSTALLATION: ! ! Include "HelpRoutines" in your gamefile. ! ! ! CONTENTS: ! ! -- Three basic formatting routines for doing bold and italic text ! and for awaiting a keypress. (The only reason to define these ! is to provide z-code/Glulx flexibility.) ! ! -- LongIntro. A description of IF, which prints both of ! -- BasicBrief. A quick description of IF in the abstract ! -- BasicIntro. A somewhat longer description of same, discuss ! ! -- ExplainPrompt. What a prompt is. ! -- StartingInstructions. Suggests that the player look, examine items, ! and check his inventory. ! -- StuckInstructions. A list of tips for a player in trouble ! ! -- AllCommunication. Prints all of the following: ! -- Communication. Basic instructions on how to form valid commands. ! -- OnMovement ! -- OnObjects ! -- OnNPCs, which will also call one of ! -- AskTellNPC (Define NPC_ASKTELL before including this file) ! -- MenuNPC (Define NPC_MENU before including this file) ! -- TalkToNPC (Define NPC_TALKTO before including this file) ! -- TopicMenuNPC (Define NPC_TOPIC before including this file) ! -- MetaCommands. Introduces SAVE, QUIT, RESTART, RESTORE, UNDO. ! ! -- StandardVerbs. A list of standard verbs. Self-adjusting to reflect ! the instructions about NPCS; it will list SCORE only if you have a MAX_SCORE ! greater than 0, and the OBJECTS/PLACES commands only if you have not defined ! NO_PLACES. ! -- Abbreviations. A list of abbreviations and their meanings. ! ! -- OnlineHelp. How to find online IF manuals. ! -- MoreGames. How to find more IF. ! ! SEE ALSO: ! -- SampleTranscript.h: contains several sample transcripts formatted ! for inclusion in IF games. ! ! CAVEAT ! ! Note that in some cases this material may be incorrect for your game. ! You are advised to read the resulting text and determine whether it suits ! your ends. ! !--------------------------------------------------------------------------- system_file; !--------------------------------------------------------------------------- ! Special effects -- bold-texting and pauses for display purposes !--------------------------------------------------------------------------- #ifdef TARGET_GLULX; [ ES_Pause i; i = KeyCharPrimitive(); if (i=='q') rtrue; rfalse; ]; #ifnot; [ ES_Pause i; @read_char 1 i; if (i == 'q') rtrue; rfalse; ]; #endif; #ifdef TARGET_GLULX; [ ESB str; ! print something in bold if (str==0) rfalse; glk_set_style(style_Input); print (string) str; glk_set_style(style_Normal); rtrue; ]; #ifnot; [ ESB str; ! print something in bold if (str==0) rfalse; style bold; print (string) str; style roman; rtrue; ]; #endif; #ifdef TARGET_GLULX; [ ESI str; ! print something in italics if (str==0) rfalse; glk_set_style(style_Emphasized); print (string) str; glk_set_style(style_Normal); rtrue; ]; #ifnot; [ ESI str; ! print something in italics if (str==0) rfalse; style underline; print (string) str; style roman; rtrue; ]; #endif; !--------------------------------------------------------------------------- ! Introductions !--------------------------------------------------------------------------- [ LongIntro; BasicBrief(); new_line; BasicIntro(); ]; [ BasicBrief; print (ESB) "INTERACTIVE FICTION^"; print "The game you are playing is a work of Interactive Fiction. In interactive fiction you play the main character of a story. You type commands which determine the actions of the character and the flow of the plot. Some IF games include graphics, but most do not: the imagery is provided courtesy of your imagination. On the other hand, there's a wide range of action available: whereas in other games you may be restricted to shooting, movement, or searching items you can click on with a mouse, IF allows you a wide range of verbs.^"; ]; [ BasicIntro; print "There are various kinds of IF in the world. Some of them put more emphasis on solving puzzles; some want to move you through a coherent plot of some kind; some want to offer you something to explore.^^"; print "In games with a lot of challenging puzzles, you can expect to spend a fair amount of time wandering around trying to figure out what you should do next; this is part of the fun. (If you like that sort of thing, anyway.) When you start a game, you can usually get a sense fairly early on of what kind of game it is and what the author expects you to do. Read the opening text carefully: it may tell you things about the character you are playing, your goals within the game, and so on.^^"; print "If the game tells you to type ABOUT or INFO the first time you play, you should always do so: this information may include special commands or other material without which you won't be able to finish. This is like the game manual in a commercial game, so don't ignore it.^^"; print (ESB) "HOW THE WORLD IS ASSEMBLED^^"; print "Space: Most IF games are set in a world made up of rooms without internal division. Movement between rooms is possible; movement within a room does not always amount to anything. >WALK OVER TO THE DESK is rarely a useful sort of command. On the other hand, if something is described as being high or out of reach, it is sometimes relevant to stand on an object to increase your height. This kind of activity tends to be important only if prompted by the game text.^^"; print "Containment: One thing that IF does tend to model thoroughly is containment. Is something in or on something else? The game keeps track of this, and many puzzles have to do with where things are -- in the player's possession, lying on the floor of the room, on a table, in a box, etc.^^"; print "Types of Action: Most of the actions you can perform in the world of IF are brief and specific. >WALK WEST or >OPEN DOOR are likely to be provided. >TAKE A JOURNEY or >BUILD A TABLE are not. Things like >GO TO THE HOTEL are on the borderline: some games allow them, but most do not. In general, abstract, multi-stage behavior usually has to be broken down in order for the game to understand it.^^"; print "Other Characters: Other characters in IF games are sometimes rather limited. On the other hand, there are also games in which character interaction is the main point of the game. You should be able to get a feel early on for the characters -- if they seem to respond to a lot of questions, remember what they're told, move around on their own, etc., then they may be fairly important. If they have a lot of stock responses and don't seem to have been the game designer's main concern, then they are most likely present either as local color or to provide the solution to a specific puzzle or set of puzzles. Characters in very puzzle-oriented games often have to be bribed, threatened, or cajoled into doing something that the player cannot do -- giving up a piece of information or an object, reaching something high, allowing the player into a restricted area, and so on.^"; rtrue; ]; !--------------------------------------------------------------------------- ! Explain Prompt, Starting Instructions, Stuck Instructions !--------------------------------------------------------------------------- [ ExplainPrompt; print (ESB) "PROMPT^"; print "The ", (ESB) ">", " sign is where the game says, ~Okay, what do you want to do now?~ You may respond by typing an instruction -- usually an imperative verb, possibly followed by prepositions and objects. So, for instance, LOOK, LOOK AT FISH, TAKE FISH.^"; ]; [ StartingInstructions; print (ESB) "GETTING STARTED^"; print "The first thing you want to do when starting a game is acquaint yourself with your surroundings and get a sense of your goal. To this end, you should:^^"; print (ESB) "Read the introductory text carefully. ", "Sometimes it contains clues.^"; print (ESB) "Look at the room description. ", "Get an idea of what sort of place you're in. Usually the description will tell you two important things: where the exits from the room are, and what the objects are that you can interact with. Type LOOK if you want to see the room description again.^"; print (ESB) "Look at the things in the room. ", "Individual descriptions of items can help you out.^"; print (ESB) "Examine yourself. ", "Information about your character may be important.^"; print (ESB) "TAKE INVENTORY. ", "Sometimes you'll be holding something important.^"; print (ESB) "Explore. ", "Move from room to room, and check out every location available.^"; ]; [ StuckInstructions; print (ESI) "[These suggestions are lengthy, so the game will periodically pause while printing them. To stop the instructions mid-flow, press Q at a pause; to continue, press any other key.]^^"; print (ESB) "Explore. ", "Examine every object mentioned in room descriptions, and everything in your inventory. Examine yourself, too. Look inside all closed containers. Open all doors and go through them. If anything is locked, that's probably a puzzle, and you should try to get it unlocked.^^"; print (ESB) "Try out all your senses. ", "If the game mentions an interesting texture, odor, or sound, try SMELLing, TOUCHing, LISTENing, etc.^^"; print (ESB) "Be thorough. ", "If you *still* can't figure out what to do, try opening windows, looking under beds, etc. Sometimes objects are well-hidden.^^"; print (ESB) "Reread. ", "Look back at things you've already seen; sometimes this will trigger an idea you hadn't thought of.^^"; if (ES_Pause()) jump end; print (ESB) "Take hints from the prose of the game. ", "Things that are described in great detail are probably more important than things that are given one-liners. Play with those objects. If a machine is described as having component parts, look at the parts, and try manipulating them. Likewise, notice the verbs that the game itself uses. Try using those yourself. Games often include special verbs -- the names of magic spells, or other special commands. There's no harm in attempting something if the game mentions it.^^"; print (ESB) "Rephrase. ", "If you have something in mind that you want to do, but can't get the game to respond, try alternative wordings. Often synonyms are provided. Game designers usually try to anticipate all the synonyms you are likely to come up with, but they may not have thought of yours.^^"; if (ES_Pause()) jump end; print (ESB) "Try variations. ", "Sometimes an action doesn't work, but *does* produce some kind of unusual result. These are often indications that you're on the right track, even if you haven't figured out quite the right approach yet. Pressing the red button alone may only cause a grinding noise from inside the wall, so perhaps pressing the blue and *then* the red will open the secret door.^^"; print (ESB) "Try to understand the game's internal logic. ", "Sometimes there is a system in play that does not operate in the normal world -- a kind of magic, for instance, or technology we don't have on modern-day earth. If you've been introduced to such a system in the game, ask yourself how you might apply it to the situations that are still causing you problems.^^"; if (ES_Pause()) jump end; print (ESB) "Check the *whole* screen. ", "Are there extra windows besides the main window? What's going on in those? Check out the status bar, if there is one -- it may contain the name of the room you're in, your score, the time of day, your character's state of health, or some other important information. If there's something up there, it's worth paying attention to that, too. When and where does it change? Why is it significant? If the bar is describing your character's health, you can bet there is probably a point at which that will be important.^^"; print (ESB) "Consider the genre of the game. ", "Mysteries, romances, and thrillers all have their own types of action and motivation. What are you trying to do, and how do conventional characters go about doing that? What's the right sort of behavior for a detective/romance heroine/spy?^^"; if (ES_Pause()) jump end; print (ESB) "Play with someone else. ", "Two heads are usually better than one.^^"; print (ESB) "Try typing HINT, HELP, INFO, ABOUT: ", "one or more of these are often implemented as a source of suggestions about how to get past difficult spots. If that doesn't work, try emailing the author or (better yet) posting a request for hints on the newsgroup rec.games.int-fiction. For best results, put the name of the game you want help with in the subject line; then leave a page or so of blank ~spoiler space~ (so that no one will read about where you got to in the game unless they've already played it), and describe your problem as clearly as possible. Someone will probably be able to tell you how to get around it.^^"; if (ES_Pause()) jump end; print (ESB) "Email the author: ", "If you're stuck somewhere that just makes no sense at all, it's possible that you're facing a bug. If you think you are, you should email the author (politely) to report the problem and ask for a way around it.^"; .end; "Good luck!"; ]; !--------------------------------------------------------------------------- ! Standard Verbs list !--------------------------------------------------------------------------- [ StandardVerbs flag; print "Here is a list of the standard instructions in games like this. You should be aware that the game you are playing may introduce some other verbs, as well. This list is just a place to start:^^"; #ifdef TARGET_GLULX; glk_set_style(style_Preformatted); #ifnot; font off; #endif; print "LOOK "; print "EXAMINE "; print "SEARCH "; print "LOOK UNDER "; print "^"; print "^"; print "INVENTORY "; print "TAKE "; print "DROP "; print "EMPTY "; print "^"; print "REMOVE "; print "PUT ON "; print "PUT IN "; print "TRANSFER "; print "^"; print "^"; print "NORTH [N] "; print "SOUTH [S] "; print "EAST [E] "; print "WEST [W] "; print "^"; print "[NE] "; print "[NW] "; print "[SE] "; print "[SW] "; print "^"; print "UP "; print "DOWN "; print "IN "; print "OUT "; print "^"; print "GO "; print "ENTER "; print "EXIT "; print "GET OFF "; print "^"; print "^"; print "CLIMB "; print "SWIM "; print "JUMP "; print "JUMP OVER "; print "^"; print "^"; print "LOCK "; print "UNLOCK "; print "OPEN "; print "CLOSE "; print "^"; print "SWITCH ON "; print "SWITCH OFF "; print "SET "; print "TURN "; print "^"; print "PULL "; print "PUSH "; print "PUSH NORTH "; print "THROW AT "; print "^"; print "SWING "; print "WAVE "; print "RUB "; print "SQUEEZE "; print "^"; print "EAT "; print "DRINK "; print "WEAR "; print "TAKE OFF "; print "^"; print "LISTEN "; print "TASTE "; print "TOUCH "; print "SMELL "; print "^"; print "BURN "; print "DIG "; print "CUT "; print "TIE "; print "^"; print "BLOW "; print "BREAK "; print "FILL "; print "CONSULT "; print "^"; print "WAIT "; print "SLEEP "; print "SING "; print "THINK "; print "^"; print "^"; print "GIVE "; print "SHOW "; print "WAKE "; print "KISS "; print "^"; print "ATTACK "; print "BUY "; #ifdef NPC_ASKTELL; print "TELL "; print "ANSWER "; print "^"; print "ASK "; print "ASK FOR "; #endif; #ifdef NPC_TOPIC; print "TELL "; print "ASK "; #endif; #ifdef NPC_TALKTO; print "TALK TO "; #endif; #ifdef NPC_MENU; print "TALK TO "; #endif; print "^"; #ifdef TARGET_GLULX; glk_set_style(style_Normal); #ifnot; font on; #endif; print "^^The following commands control the game itself:^^"; #ifdef TARGET_GLULX; glk_set_style(style_Preformatted); #ifnot; font off; #endif; print "RESTART RESTORE SAVE^"; print "QUIT UNDO PRONOUNS^"; print "SCRIPT ON SCRIPT OFF VERIFY^"; #ifndef NO_PLACES; print "OBJECTS PLACES ^"; #endif; #ifdef MAX_SCORE; if (max_score > 0) { print "SCORE "; print "NOTIFY ON "; print "NOTIFY OFF "; flag = 1; } #endif; #ifdef TASKS_PROVIDED; if (max_score > 0) { print "FULL SCORE"; flag = 1; } #endif; if (flag) print "^"; #ifdef TARGET_GLULX; glk_set_style(style_Normal); #ifnot; font on; #endif; ]; [ Abbreviations; print "Standard abbreviations are as follow:^^"; print "D -- DOWN."; print "E -- EAST."; print "G -- AGAIN. Repeat the previous command."; print "I -- INVENTORY. See what you're holding."; print "L -- LOOK. See what's around you."; print "N -- NORTH"; print "NE -- NORTHEAST"; print "NW -- NORTHWEST"; print "Q -- QUIT"; print "S -- SOUTH"; print "SE -- SOUTHEAST"; print "SW -- SOUTHWEST"; print "U -- UP"; print "W -- WEST"; print "X -- EXAMINE (something)"; print "Z -- WAIT"; new_line; ]; !--------------------------------------------------------------------------- ! Communication etc. !--------------------------------------------------------------------------- [ AllCommunication; Communication(); OnMovement(); OnObjects(); OnNPCs(); MetaCommands(); ]; [ Communication; print (ESB) "BASIC COMMUNICATION^"; print "In order to communicate with the game, you will need to enter instructions beginning with imperative verbs -- as though you were giving orders to someone else to carry out. For instance, >CLOSE THE DOOR or >LOOK UNDER THE RUG.^^"; print "You may use articles (~the~ and ~a~), but you do not need to. >CLOSE DOOR will also work for the game's purposes.^"; ]; [ OnMovement; print (ESB) "ROOMS AND TRAVEL^"; print "At any given time, you are in a specific location, or room. When you go into a room, the game will print a description of what you can see there. This description will contain two vital kinds of information: things in the room you can interact with or take, and a list of exits, or ways out. If you want to see the description again, you may just type LOOK.^^"; print "When you want to leave a location and go to another one, you may communicate this to the game using compass directions: eg, GO NORTH. For simplicity's sake, you are allowed to omit the word GO, and to abbreviate the compass directions. So you have NORTH, SOUTH, EAST, WEST, NORTHEAST, SOUTHEAST, NORTHWEST, SOUTHWEST, UP, and DOWN, or in short form N, S, E, W, NE, SE, NW, SW, U, and D.^^"; print "In some locations, IN and OUT will also be useful.^^"; ]; [ OnObjects; print (ESB) "OBJECTS^"; print "Throughout the game there will be assorted objects that you can do things with. Most importantly, you may TAKE or GET items, and (when you are tired of them) DROP them again. INVENTORY (abbreviated I) will list the items you are currently holding.^^"; print "There are usually assorted things you may do with these objects. OPEN, CLOSE, WEAR, EAT, LOCK, and UNLOCK are especially common.^^"; print "Occasionally, you will find that the game does not recognize the name of an object even though it has been described as being in the room with you. If this is the case, the object is just there for scenery, and you may assume that you do not need to interact with it.^"; ]; [ OnNPCs; print (ESB) "OTHER CHARACTERS^"; #ifdef NPC_MENU; MenuNPC(); rtrue; #endif; #ifdef NPC_TALKTO; TalkToNPC(); rtrue; #endif; #ifdef NPC_TOPIC; TopicMenuNPC(); rtrue; #endif; AskTellNPC(); rtrue; ]; [ AskTellNPC; print "From time to time you will meet other people and creatures. You will be unable to converse with the people in plain English; instead, a more constrained system of communication is used. There are four common ways to talk to characters:^^"; print "Ask them about an object.^"; print (ESB) ">ASK PAUL ABOUT HIS BROTHER^"; print (ESB) ">ASK GLENDA ABOUT RUBY SLIPPERS^^"; print "Show them an object.^"; print (ESB) ">SHOW WARRANT TO DRUG LORD^"; print (ESB) ">SHOW BUCKET OF WATER TO EVIL WITCH^^"; print "Tell them about an object.^"; print (ESB) ">TELL ICE CREAM VENDOR ABOUT HIS TRUCK^"; print (ESB) ">TELL DOROTHY ABOUT FLYING MONKEYS^^"; print "Command them.^"; print (ESB) ">FREDDY, HELLO^"; print (ESB) ">TINY TIM, PUT THE UKELELE ON THE TABLE^"; print (ESB) ">TIN MAN, GET UP. CARRY US^^"; print "FREDDY, HELLO is not an actual command, but is phrased like one. (Note that not all games implement this command, but it's worth knowing about just in case.) Notice that you can give characters more than one command on a line. Most characters will be less than responsive to commands.^"; ]; [ MenuNPC; print "From time to time you will meet other people and creatures. You may use the command >TALK TO CHARACTER, and the game will give you a menu of options, which you may use to converse with the character in plain English.^^"; print "You may also find it useful to show them things:^^"; print (ESB) ">SHOW WARRANT TO DRUG LORD^"; print (ESB) ">SHOW BUCKET OF WATER TO EVIL WITCH^^"; print "or give them instructions:^"; print (ESB) ">FREDDY, HELLO^"; print (ESB) ">TINY TIM, PUT THE UKELELE ON THE TABLE^^"; print "but if you're just not getting through, rely on the menus.^"; ]; [ TalkToNPC; print "From time to time you will meet other people and creatures. You will be unable to converse with the people in plain English; instead, you may use the command >TALK TO CHARACTER, and the game will decide what you should say at this juncture.^"; ]; [ TopicMenuNPC; print "From time to time you will meet other people and creatures. You will be unable to converse with the people in plain English; instead, a more constrained system of communication is used. There are three common ways to talk to characters:^^"; print "Bring up a topic for conversation.^"; print (ESB) ">TELL BOB ABOUT THE FLAG^"; print (ESB) ">ASK BETTY ABOUT CHOCOLATE CAKE^^"; print "These can also be abbreviated to A and T, and the game will also keep track of which person you're talking to, if you've already started a conversation. This allows briefer commands, like so:^^"; print (ESB) ">A CAKE^"; print (ESB) ">T FLAG^^"; print "If there is more than one thing you are allowed to ask Betty about the chocolate cake, the game will give you a menu of options in the window at the bottom of the screen. Otherwise, it will carry out your instruction immediately.^^"; print "Show them an object.^"; print (ESB) ">SHOW WARRANT TO DRUG LORD^"; print (ESB) ">SHOW BUCKET OF WATER TO EVIL WITCH^^"; print "Command them.^"; print (ESB) ">TINY TIM, PUT THE UKELELE ON THE TABLE^"; print (ESB) ">TIN MAN, GET UP. CARRY US^^"; print "Most characters will be less than responsive to commands.^"; ]; [ MetaCommands; print (ESB) "METACOMMANDS^"; print "There are a few simple commands for controlling the function of the game itself. These are:^^"; print (ESB) "SAVE", " -- Saves a snapshot of the game as it is now.^"; print (ESB) "RESTORE", " -- Puts the game back to a previously saved state. (Note: you may keep as many saved games as you like.)^"; print (ESB) "RESTART", " -- Puts the game back to its initial state.^"; print (ESB) "QUIT", " -- Ends the game.^"; ]; !--------------------------------------------------------------------------- ! About the IF community online !--------------------------------------------------------------------------- [ OnlineHelp; print (ESB) "IF HELP ONLINE^^"; print "Some online sources of introductory help for IF games include:^^"; print "Frederik Ramsberg's Beginner's Guide, http://www.octagate.com/Fredrik/IFGuide/^"; print "Interactive Fiction -- Getting Started, http://adamcadre.ac/content/if.txt^"; print "A Brief Introduction to Interactive Fiction, http://www.tads.org/if.htm^^"; print "For more information about IF, the IF community, and other games to play, you should also try:^^"; print "Brasslantern, http://www.brasslantern.org/^"; print "Baf's Guide to Interactive Fiction, http://www.wurb.com/if/index^^"; print "You may also ask for specific information and game hints on the newsgroup rec.games.int-fiction.^^"; print "If you are not familiar with newsgroups and don't have a newsreader already set up, you may access it by going to http://groups.google.com/ and selecting ~rec~, then ~rec.games~, then ~rec.games.int-fiction.~ You will be presented with a long list of things that people are already talking about. If you've never read this newsgroup before, you should look for a thread with the word ~FAQ~ in the title, and read it: this will give you instructions about how to post without breaking any of the community's rules of etiquette. (They're not especially fussy, but following them will get you prompter and more polite assistance.)^^"; print "Once you've done that, you may post a new message with a clearly labelled subject (like ~[HINT REQUEST] Jigsaw~ or ~Looking for Mystery Games~). Afterwards, wait a few hours and check the newsgroup again to see whether anyone has an answer for you.^^"; ]; [ MoreGames; print (ESB) "MORE SOURCES OF IF^^"; print "There are more games like this at the IF Archive, www.ifarchive.org.^^"; print "Instructions on setting them up are available from Frederik Ramsberg's Beginner's Guide, http://www.octagate.com/Fredrik/IFGuide/^"; print "Guides to good IF (for beginners or in general) are to be found at the Beginner's Guide just mentioned, or at Baf's Guide to Interactive Fiction, http://www.wurb.com/if/index."; ]; ! ============================================================================== ! PARSER: Front end to parser. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ! ------------------------------------------------------------------------------ ! Inclusion of "linklpa" (which defines properties and attributes) ! Global variables, constants and arrays ! 1: outside of the parser ! 2: used within the parser ! Inclusion of natural language definition file ! (which creates a compass and direction-objects) ! Darkness and player objects ! Definition of grammar token numbering system used by Inform ! ! The InformParser object ! keyboard reading ! level 0: outer shell, conversation, errors ! 1: grammar lines ! 2: tokens ! 3: object lists ! 4: scope and ambiguity resolving ! 5: object comparisons ! 6: word comparisons ! 7: reading words and moving tables about ! pronoun management ! ! The InformLibrary object ! main game loop ! action processing ! end of turn sequence ! scope looping, before/after sequence, sending messages out ! timers, daemons, time of day, score notification ! light and darkness ! changing player personality ! tracing code (only present if DEBUG is set) ! ! Status line printing, menu display ! Printing object names with articles ! Miscellaneous utility routines ! Game banner, "version" verb, run-time errors ! ============================================================================== System_file; #Ifndef LIBRARY_STAGE; ! This file is the first one to define LIBRARY_STAGE. ! "This file already included" <=> "LIBRARY_STAGE exists" ! ------------------------------------------------------------------------------ #Ifndef VN_1633; Message fatalerror "*** Library 6.12.2 needs Inform v6.33 or later to work ***"; #Endif; ! VN_ Constant LibSerial "180611"; Constant LibRelease "6.12.2"; Constant LIBRARY_VERSION 612; Constant Grammar__Version 2; Constant BEFORE_PARSER 10; Constant AFTER_PARSER 20; Constant AFTER_VERBLIB 30; Constant AFTER_GRAMMAR 40; Constant LIBRARY_STAGE = BEFORE_PARSER; Default COMMENT_CHARACTER '*'; #Ifdef INFIX; Default DEBUG 0; #Endif; ! INFIX #Ifndef WORDSIZE; ! compiling with Z-code only compiler Default TARGET_ZCODE 0; Constant WORDSIZE 2; #Endif; ! WORDSIZE #Ifdef TARGET_ZCODE; ! offsets into Z-machine header Constant HDR_ZCODEVERSION $00; ! byte Constant HDR_TERPFLAGS $01; ! byte Constant HDR_GAMERELEASE $02; ! word Constant HDR_HIGHMEMORY $04; ! word Constant HDR_INITIALPC $06; ! word Constant HDR_DICTIONARY $08; ! word Constant HDR_OBJECTS $0A; ! word Constant HDR_GLOBALS $0C; ! word Constant HDR_STATICMEMORY $0E; ! word Constant HDR_GAMEFLAGS $10; ! word Constant HDR_GAMESERIAL $12; ! six ASCII characters Constant HDR_ABBREVIATIONS $18; ! word Constant HDR_FILELENGTH $1A; ! word Constant HDR_CHECKSUM $1C; ! word Constant HDR_TERPNUMBER $1E; ! byte Constant HDR_TERPVERSION $1F; ! byte Constant HDR_SCREENHLINES $20; ! byte Constant HDR_SCREENWCHARS $21; ! byte Constant HDR_SCREENWUNITS $22; ! word Constant HDR_SCREENHUNITS $24; ! word Constant HDR_FONTWUNITS $26; ! byte Constant HDR_FONTHUNITS $27; ! byte Constant HDR_ROUTINEOFFSET $28; ! word Constant HDR_STRINGOFFSET $2A; ! word Constant HDR_BGCOLOUR $2C; ! byte Constant HDR_FGCOLOUR $2D; ! byte Constant HDR_TERMCHARS $2E; ! word Constant HDR_PIXELSTO3 $30; ! word Constant HDR_TERPSTANDARD $32; ! two bytes Constant HDR_ALPHABET $34; ! word Constant HDR_EXTENSION $36; ! word Constant HDR_UNUSED $38; ! two words Constant HDR_INFORMVERSION $3C; ! four ASCII characters #Ifnot; ! TARGET_GLULX ! offsets into Glulx header and start of ROM Constant HDR_MAGICNUMBER $00; ! long word Constant HDR_GLULXVERSION $04; ! long word Constant HDR_RAMSTART $08; ! long word Constant HDR_EXTSTART $0C; ! long word Constant HDR_ENDMEM $10; ! long word Constant HDR_STACKSIZE $14; ! long word Constant HDR_STARTFUNC $18; ! long word Constant HDR_DECODINGTBL $1C; ! long word Constant HDR_CHECKSUM $20; ! long word Constant ROM_INFO $24; ! four ASCII characters Constant ROM_MEMORYLAYOUT $28; ! long word Constant ROM_INFORMVERSION $2C; ! four ASCII characters Constant ROM_COMPVERSION $30; ! four ASCII characters Constant ROM_GAMERELEASE $34; ! short word Constant ROM_GAMESERIAL $36; ! six ASCII characters Include "infglk"; #Endif; ! TARGET_ Include "linklpa"; Fake_Action LetGo; Fake_Action Receive; Fake_Action ThrownAt; Fake_Action Order; Fake_Action TheSame; Fake_Action PluralFound; Fake_Action ListMiscellany; Fake_Action Miscellany; Fake_Action Prompt; Fake_Action NotUnderstood; Fake_Action Going; #Ifdef NO_PLACES; Fake_Action Places; Fake_Action Objects; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ [ Main; InformLibrary.play(); ]; ! ------------------------------------------------------------------------------ #Ifdef COLOR; Constant COLOUR; #Endif; #Ifdef COLOUR; Global clr_on = 1; #Ifnot; Global clr_on = 0; #Endif; ! ------------------------------------------------------------------------------ ! Global variables and their associated Constant and Array declarations ! ------------------------------------------------------------------------------ Global location = InformLibrary; ! Must be first global defined Global sline1; ! Must be second Global sline2; ! Must be third ! (for status line display) ! ------------------------------------------------------------------------------ ! Z-Machine and interpreter issues ! ------------------------------------------------------------------------------ #Ifdef TARGET_ZCODE; Global top_object; ! Largest valid number of any tree object ! ### these globals are not meaningful... well, maybe standard_interpreter, ! but I'll decide that later (AP). Constant INDIV_PROP_START 64; ! Equivalent of a Glulx constant #Endif; ! TARGET_ZCODE Global standard_interpreter; ! The version number of the Z-Machine Standard which the ! interpreter claims to support, in form (upper byte).(lower) Global undo_flag; ! Can the interpreter provide "undo"? Global just_undone; ! Can't have two successive UNDOs Global transcript_mode; ! true when game scripting is on #Ifdef TARGET_ZCODE; Global xcommsdir; ! true if command recording is on #Endif; ! TARGET_ZCODE #Ifdef TARGET_GLULX; Constant GG_MAINWIN_ROCK 201; Constant GG_STATUSWIN_ROCK 202; Constant GG_QUOTEWIN_ROCK 203; Constant GG_SAVESTR_ROCK 301; Constant GG_SCRIPTSTR_ROCK 302; Constant GG_COMMANDWSTR_ROCK 303; Constant GG_COMMANDRSTR_ROCK 304; Constant GG_SCRIPTFREF_ROCK 401; Array gg_event --> 4; #Ifdef VN_1630; Array gg_arguments buffer 28; #Ifnot; Array gg_arguments --> 8; #Endif; ! VN_ Global gg_mainwin = 0; Global gg_statuswin = 0; Global gg_quotewin = 0; Global gg_scriptfref = 0; Global gg_scriptstr = 0; Global gg_savestr = 0; Global gg_commandstr = 0; Global gg_command_reading = 0; ! true if gg_commandstr is being replayed #Endif; ! TARGET_GLULX Global gg_statuswin_cursize = 0; Global gg_statuswin_size = 1; ! ------------------------------------------------------------------------------ ! Time and score ! (for linkage reasons, the task_* arrays are created not here but in verblib.h) ! ------------------------------------------------------------------------------ #Ifndef sys_statusline_flag; Global sys_statusline_flag = 0; ! non-zero if status line displays time #Endif; #Ifndef START_MOVE; Constant START_MOVE 0; ! Traditionally 0 for Infocom, 1 for Inform #Endif; Global turns = START_MOVE; ! Number of turns of play so far Global the_time = NULL; ! Current time (in minutes since midnight) Global time_rate = 1; ! How often time is updated Global time_step; ! By how much #Ifndef MAX_TIMERS; Constant MAX_TIMERS 32; ! Max number timers/daemons active at once #Endif; ! MAX_TIMERS Array the_timers --> MAX_TIMERS; Global active_timers; ! Number of timers/daemons actives Global score; ! The current score Global last_score; ! Score last turn (for testing for changes) Global notify_mode = true; ! Score notification Global places_score; ! Contribution to score made by visiting Global things_score; ! Contribution made by acquisition ! ------------------------------------------------------------------------------ ! The player ! ------------------------------------------------------------------------------ Global player; ! Which object the human is playing through Global deadflag; ! Normally 0, or false; 1 for dead ! 2 for victorious, and higher numbers ! represent exotic forms of death ! ------------------------------------------------------------------------------ ! Light and room descriptions ! ------------------------------------------------------------------------------ Global lightflag = true; ! Is there currently light to see by? Global real_location; ! When in darkness, location = thedark ! and this holds the real location Global prev_location; ! The previous value of real_location Global visibility_ceiling; ! Highest object in tree visible from the ! player's point of view (usually the room, ! sometimes darkness, sometimes a closed ! non-transparent container). Global lookmode = 2; ! 1=brief, 2=verbose, 3=superbrief ! room descriptions Global print_player_flag; ! If set, print something like "(as Fred)" ! in room descriptions, to reveal whom the ! player is playing through Global lastdesc; ! Value of location at time of most recent ! room description printed out ! ------------------------------------------------------------------------------ ! List writing (style bits are defined as Constants in "verblibm.h") ! ------------------------------------------------------------------------------ Global c_style; ! Current list-writer style Global lt_value; ! Common value of list_together Global listing_together; ! Object number of one member of a group ! being listed together Global listing_size; ! Size of such a group Global wlf_indent; ! Current level of indentation printed by ! WriteListFrom() Global inventory_stage = 1; ! 1 or 2 according to the context in which ! "invent" routines of objects are called Global inventory_style; ! List-writer style currently used while ! printing inventories ! ------------------------------------------------------------------------------ ! Menus and printing ! ------------------------------------------------------------------------------ Global pretty_flag = true; ! Use character graphics, or plain text? Global menu_nesting; ! Level of nesting (0 = root menu) Global menu_item; ! These are used in communicating Global item_width = 8; ! with the menu-creating routines Global item_name = "---"; Global lm_n; ! Parameters used by LibraryMessages Global lm_o; ! mechanism Global lm_s; #Ifdef DEBUG; Constant DEBUG_MESSAGES $0001; Constant DEBUG_ACTIONS $0002; Constant DEBUG_TIMERS $0004; Constant DEBUG_CHANGES $0008; Constant DEBUG_VERBOSE $0080; Global debug_flag; ! Bitmap of flags for tracing actions, ! calls to object routines, etc. Global x_scope_count; ! Used in printing a list of everything #Endif; ! DEBUG ! in scope ! five for colour control ! see http://www.inform-fiction.org/patches/L61007.html ! To enable colour define a constant or Global: COLOR or COLOUR !Global clr_on; ! has colour been enabled by the player? #Ifdef COLOUR; Global clr_fg = 1; ! foreground colour Global clr_bg = 1; ! background colour Global clr_fgstatus = 1; ! foreground colour of statusline Global clr_bgstatus = 1; ! background colour of statusline #Endif; ! COLOUR Global statuswin_current; ! if writing to top window Constant CLR_CURRENT 0; Constant CLR_DEFAULT 1; Constant CLR_BLACK 2; Constant CLR_RED 3; Constant CLR_GREEN 4; Constant CLR_YELLOW 5; Constant CLR_BLUE 6; Constant CLR_MAGENTA 7; Constant CLR_CYAN 8; Constant CLR_WHITE 9; Constant CLR_PURPLE 7; Constant CLR_AZURE 8; Constant WIN_ALL 0; Constant WIN_STATUS 1; Constant WIN_MAIN 2; ! ------------------------------------------------------------------------------ ! Action processing ! ------------------------------------------------------------------------------ Global action; ! Action currently being asked to perform Global inp1; ! 0 (nothing), 1 (number) or first noun Global inp2; ! 0 (nothing), 1 (number) or second noun Global noun; ! First noun or numerical value Global second; ! Second noun or numerical value Global keep_silent; ! If true, attempt to perform the action ! silently (e.g. for implicit takes, ! implicit opening of unlocked doors) Global reason_code; ! Reason for calling a "life" rule ! (an action or fake such as ##Kiss) Global receive_action; ! Either ##PutOn or ##Insert, whichever is ! action being tried when an object's ! "before" rule is checking "Receive" Global no_implicit_actions; ! Don't implicitly do things. ! ============================================================================== ! Parser variables: first, for communication to the parser ! ------------------------------------------------------------------------------ Global parser_trace = 0; ! Set this to 1 to make the parser trace ! tokens and lines Global parser_action; ! For the use of the parser when calling Global parser_one; ! user-supplied routines Global parser_two; ! Array inputobjs --> 16; ! For parser to write its results in Global parser_inflection; ! A property (usually "name") to find ! object names in Global parser_inflection_func; ! Programmer sets this to true when ! parser_infection is a function ! ------------------------------------------------------------------------------ ! Parser output ! ------------------------------------------------------------------------------ Global actor; ! Person asked to do something Global actors_location; ! Like location, but for the actor Global meta; ! Verb is a meta-command (such as "save") #Ifdef INFIX; Global infix_verb; ! Verb is an Infix command #Endif; Array multiple_object --> 64; ! List of multiple parameters Global multiflag; ! Multiple-object flag passed to actions ! Also used to prevent misleading MULTI_PE Global toomany_flag; ! Flag for "multiple match too large" ! (e.g. if "take all" took over 100 things) Global special_word; ! Dictionary address for "special" token Global special_number; ! Number typed for "special" token Global parsed_number; ! For user-supplied parsing routines Global consult_from; ! Word that a "consult" topic starts on Global consult_words; ! ...and number of words in topic Global asking_player; ! True during disambiguation question ! ------------------------------------------------------------------------------ ! Implicit taking ! ------------------------------------------------------------------------------ Global notheld_mode; ! To do with implicit taking Global onotheld_mode; ! "old copy of notheld_mode", ditto Global not_holding; ! Object to be automatically taken as an ! implicit command Array kept_results --> 16; ! Delayed command (while the take happens) ! ------------------------------------------------------------------------------ ! Error numbers when parsing a grammar line ! ------------------------------------------------------------------------------ Global etype; ! Error number on current line Global best_etype; ! Preferred error number so far Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed Constant STUCK_PE = 1; Constant UPTO_PE = 2; Constant NUMBER_PE = 3; Constant CANTSEE_PE = 4; Constant TOOLIT_PE = 5; Constant NOTHELD_PE = 6; Constant MULTI_PE = 7; Constant MMULTI_PE = 8; Constant VAGUE_PE = 9; Constant EXCEPT_PE = 10; Constant ANIMA_PE = 11; Constant VERB_PE = 12; Constant SCENERY_PE = 13; Constant ITGONE_PE = 14; Constant JUNKAFTER_PE = 15; Constant TOOFEW_PE = 16; Constant NOTHING_PE = 17; Constant ASKSCOPE_PE = 18; ! ------------------------------------------------------------------------------ ! Pattern-matching against a single grammar line ! ------------------------------------------------------------------------------ Array pattern --> 32; ! For the current pattern match Global pcount; ! and a marker within it Array pattern2 --> 32; ! And another, which stores the best match Global pcount2; ! so far Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text Array line_ttype-->32; ! For storing an analysed grammar line Array line_tdata-->32; Array line_token-->32; Global parameters; ! Parameters (objects) entered so far Global nsns; ! Number of special_numbers entered so far Global special_number1; ! First number, if one was typed Global special_number2; ! Second number, if two were typed ! ------------------------------------------------------------------------------ ! Inferences and looking ahead ! ------------------------------------------------------------------------------ Global params_wanted; ! Number of parameters needed ! (which may change in parsing) Global inferfrom; ! The point from which the rest of the ! command must be inferred Global inferword; ! And the preposition inferred Global dont_infer; ! Another dull flag Global no_infer_message = false; ! Use in ChooseObjects to suppress ! an inference message. Global action_to_be; ! (If the current line were accepted.) Global action_reversed; ! (Parameters would be reversed in order.) Global advance_warning; ! What a later-named thing will be ! ------------------------------------------------------------------------------ ! At the level of individual tokens now ! ------------------------------------------------------------------------------ Global found_ttype; ! Used to break up tokens into type Global found_tdata; ! and data (by AnalyseToken) Global token_filter; ! For noun filtering by user routines Global length_of_noun; ! Set by NounDomain to no of words in noun #Ifdef TARGET_ZCODE; Constant REPARSE_CODE = 10000; ! Signals "reparse the text" as a reply ! from NounDomain #Ifnot; ! TARGET_GLULX Constant REPARSE_CODE = $40000000; ! The parser rather gunkily adds addresses ! to REPARSE_CODE for some purposes and ! expects the result to be greater than ! REPARSE_CODE (signed comparison). ! So Glulx Inform is limited to a single ! gigabyte of storage, for the moment. #Endif; ! TARGET_ Global lookahead; ! The token after the one now being matched Global multi_mode; ! Multiple mode Global multi_wanted; ! Number of things needed in multitude Global multi_had; ! Number of things actually found Global multi_context; ! What token the multi-obj was accepted for Global indef_mode; ! "Indefinite" mode - ie, "take a brick" ! is in this mode Global indef_type; ! Bit-map holding types of specification Global indef_wanted; ! Number of items wanted (100 for all) Global indef_guess_p; ! Plural-guessing flag Global indef_owner; ! Object which must hold these items Global indef_cases; ! Possible gender and numbers of them Global indef_possambig; ! Has a possibly dangerous assumption ! been made about meaning of a descriptor? Global indef_nspec_at; ! Word at which a number like "two" was ! parsed (for backtracking) Global allow_plurals; ! Whether plurals presently allowed or not Global take_all_rule; ! Slightly different rules apply to ! "take all" than other uses of multiple ! objects, to make adjudication produce ! more pragmatically useful results ! (Not a flag: possible values 0, 1, 2) Global dict_flags_of_noun; ! Of the noun currently being parsed ! (a bitmap in #dict_par1 format) Constant DICT_VERB $01; Constant DICT_META $02; Constant DICT_PLUR $04; Constant DICT_PREP $08; Constant DICT_X654 $70; Constant DICT_NOUN $80; Global pronoun_word; ! Records which pronoun ("it", "them", ...) ! caused an error Global pronoun_obj; ! And what obj it was thought to refer to Global pronoun__word; ! Saved value Global pronoun__obj; ! Saved value ! ------------------------------------------------------------------------------ ! Searching through scope and parsing "scope=Routine" grammar tokens ! ------------------------------------------------------------------------------ Constant PARSING_REASON = 0; ! Possible reasons for searching scope Constant TALKING_REASON = 1; Constant EACH_TURN_REASON = 2; Constant REACT_BEFORE_REASON = 3; Constant REACT_AFTER_REASON = 4; Constant LOOPOVERSCOPE_REASON = 5; Constant TESTSCOPE_REASON = 6; Global scope_reason = PARSING_REASON; ! Current reason for searching scope Global scope_token; ! For "scope=Routine" grammar tokens Global scope_error; Global scope_stage; ! 1, 2 then 3 Global ats_flag = 0; ! For AddToScope routines Global ats_hls; ! Global placed_in_flag; ! To do with PlaceInScope ! ------------------------------------------------------------------------------ ! The match list of candidate objects for a given token ! ------------------------------------------------------------------------------ Constant MATCH_LIST_SIZE = 64; Array match_list --> MATCH_LIST_SIZE; ! An array of matched objects so far Array match_classes --> MATCH_LIST_SIZE; ! An array of equivalence classes for them Array match_scores --> MATCH_LIST_SIZE; ! An array of match scores for them Global number_matched; ! How many items in it? (0 means none) Global number_of_classes; ! How many equivalence classes? Global match_length; ! How many words long are these matches? Global saved_ml; Global match_from; ! At what word of the input do they begin? Global bestguess_score; ! What did the best-guess object score? ! ------------------------------------------------------------------------------ ! Low level textual manipulation ! ------------------------------------------------------------------------------ #Ifdef TARGET_ZCODE; ! 'buffer' holds the input line as typed by the player ! ! buffer->0 INPUT_BUFFER_LEN - WORDSIZE ! buffer->1 Number of characters input by player ! buffer->2 ... buffer->121 The actual characters ! buffer->122 Spare byte to allow for 'terp bugs ! ! 'parse' holds the result of parsing that line into dictionary words ! ! parse->0 MAX_BUFFER_WORDS ! parse->1 Number of words input by player ! ! parse-->1 Dictionary addr of first input word ! parse->4 Number of characters in the word ! parse->5 Start position in 'buffer' of the word ! ! parse-->3 parse->8,9 Same data for second input word ! ... ! parse-->29 parse->60,61 Same data for MAX_BUFFER_WORDS input word ! parse->62,63,64 Spare bytes (not sure why) Constant INPUT_BUFFER_LEN = WORDSIZE + 120; ! 120 is limit on input chars Constant MAX_BUFFER_WORDS = 15; ! Limit on input words Array buffer -> INPUT_BUFFER_LEN + 1; ! For main line of input Array buffer2 -> INPUT_BUFFER_LEN + 1; ! For supplementary questions Array buffer3 -> INPUT_BUFFER_LEN + 1; ! Retaining input for "AGAIN" #Ifdef VN_1630; Array parse buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer' Array parse2 buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer2' #Ifnot; Array parse -> 2 + (MAX_BUFFER_WORDS * 4) + 3; Array parse2 -> 2 + (MAX_BUFFER_WORDS * 4) + 3; #Endif; ! VN_ #Ifnot; ! TARGET_GLULX ! 'buffer' holds the input line as typed by the player ! ! buffer-->0 Number of characters input by player ! buffer->4 ... buffer->259 The actual characters ! ! 'parse' holds the result of parsing that line into dictionary words ! ! parse-->0 Number of words input by player ! ! parse-->1 Dictionary addr of first input word ! parse-->2 Number of characters in the word ! parse-->3 Start position in 'buffer' of the word ! ! parse-->4,5,6 Same data for second input word ! ... ! parse-->58,59,60 Same data for MAX_BUFFER_WORDS input word Constant INPUT_BUFFER_LEN = WORDSIZE + 256; ! 256 is limit on input chars Constant MAX_BUFFER_WORDS = 20; ! Limit on input words #Ifdef VN_1630; Array buffer buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For main line of input Array buffer2 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For supplementary questions Array buffer3 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! Retaining input for "AGAIN" #Ifnot; Array buffer -> INPUT_BUFFER_LEN; Array buffer2 -> INPUT_BUFFER_LEN; Array buffer3 -> INPUT_BUFFER_LEN; #Endif; ! VN_ Array parse --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer' Array parse2 --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer2' #Endif; ! TARGET_ Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute ! for commas in parse buffers Global wn; ! Word number within "parse" (from 1) Global num_words; ! Number of words typed Global num_desc; ! Number of descriptors typed Global verb_word; ! Verb word (eg, take in "take all" or ! "dwarf, take all") - address in dict Global verb_wordnum; ! its number in typing order (eg, 1 or 3) Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from the ! above if user's routines match multi-word verbs) Global oops_from; ! The "first mistake" word number Global saved_oops; ! Used in working this out Constant OOPS_WORKSPACE_LEN 64; ! Used temporarily by "oops" routine Array oops_workspace -> OOPS_WORKSPACE_LEN; Global held_back_mode; ! Flag: is there some input from last time Global hb_wn; ! left over? (And a save value for wn.) ! (Used for full stops and "then".) Global caps_mode; ! Keep track of (The) with 'proper' caps Global print_anything_result; ! Return value from a PrintAny() routine Global initial_lookmode; ! Default, or set in Initialise() Global before_first_turn; ! True until after initial LOOK ! ---------------------------------------------------------------------------- Array PowersOfTwo_TB ! Used in converting case numbers to case --> $$100000000000 ! bitmaps $$010000000000 $$001000000000 $$000100000000 $$000010000000 $$000001000000 $$000000100000 $$000000010000 $$000000001000 $$000000000100 $$000000000010 $$000000000001; ! ============================================================================ ! Constants, and one variable, needed for the language definition file ! ---------------------------------------------------------------------------- Constant POSSESS_PK = $100; Constant DEFART_PK = $101; Constant INDEFART_PK = $102; Global short_name_case; Global dict_start; Global dict_entry_size; Global dict_end; ! ---------------------------------------------------------------------------- Include "language__"; ! The natural language definition, whose filename is taken from ! the ICL language_name variable ! ---------------------------------------------------------------------------- #Ifndef LanguageCases; Constant LanguageCases = 1; #Endif; ! LanguageCases ! ------------------------------------------------------------------------------ ! Pronouns support for the cruder (library 6/2 and earlier) version: ! only needed in English ! ------------------------------------------------------------------------------ #Ifdef EnglishNaturalLanguage; Global itobj = NULL; ! The object which is currently "it" Global himobj = NULL; ! The object which is currently "him" Global herobj = NULL; ! The object which is currently "her" Global old_itobj = NULL; ! The object which is currently "it" Global old_himobj = NULL; ! The object which is currently "him" Global old_herobj = NULL; ! The object which is currently "her" #Endif; ! EnglishNaturalLanguage ! ============================================================================ ! For present and past tenses ! ---------------------------------------------------------------------------- Constant PRESENT_TENSE 0; Constant PAST_TENSE 1; ! ============================================================================ ! For InformLibrary.actor_act() to control what happens when it aborts. ! ---------------------------------------------------------------------------- Constant ACTOR_ACT_OK 0; Constant ACTOR_ACT_ABORT_NOTUNDERSTOOD 1; Constant ACTOR_ACT_ABORT_ORDER 2; ! ============================================================================ ! "Darkness" is not really a place: but it has to be an object so that the ! location-name on the status line can be "Darkness". ! ---------------------------------------------------------------------------- Object thedark "(darkness object)" with initial 0, short_name DARKNESS__TX, description [; return L__M(##Miscellany, 17); ]; ! If you want to use the third-person of the narrative voice, you will ! need to replace this selfobj with your own. Class SelfClass with name ',a' ',b' ',c' ',d' ',e', short_name YOURSELF__TX, description [; return L__M(##Miscellany, 19); ], before NULL, after NULL, life NULL, each_turn NULL, time_out NULL, describe NULL, article "the", add_to_scope 0, capacity 100, parse_name 0, orders 0, number 0, narrative_voice 2, narrative_tense PRESENT_TENSE, nameless true, posture 0, before_implicit [;Take: return 2;], has concealed animate proper transparent; SelfClass selfobj "(self object)"; ! ============================================================================ ! The definition of the token-numbering system used by Inform. ! ---------------------------------------------------------------------------- Constant ILLEGAL_TT = 0; ! Types of grammar token: illegal Constant ELEMENTARY_TT = 1; ! (one of those below) Constant PREPOSITION_TT = 2; ! e.g. 'into' Constant ROUTINE_FILTER_TT = 3; ! e.g. noun=CagedCreature Constant ATTR_FILTER_TT = 4; ! e.g. edible Constant SCOPE_TT = 5; ! e.g. scope=Spells Constant GPR_TT = 6; ! a general parsing routine Constant NOUN_TOKEN = 0; ! The elementary grammar tokens, and Constant HELD_TOKEN = 1; ! the numbers compiled by Inform to Constant MULTI_TOKEN = 2; ! encode them Constant MULTIHELD_TOKEN = 3; Constant MULTIEXCEPT_TOKEN = 4; Constant MULTIINSIDE_TOKEN = 5; Constant CREATURE_TOKEN = 6; Constant SPECIAL_TOKEN = 7; Constant NUMBER_TOKEN = 8; Constant TOPIC_TOKEN = 9; Constant GPR_FAIL = -1; ! Return values from General Parsing Constant GPR_PREPOSITION = 0; ! Routines Constant GPR_NUMBER = 1; Constant GPR_MULTIPLE = 2; Constant GPR_REPARSE = REPARSE_CODE; Constant GPR_NOUN = $ff00; Constant GPR_HELD = $ff01; Constant GPR_MULTI = $ff02; Constant GPR_MULTIHELD = $ff03; Constant GPR_MULTIEXCEPT = $ff04; Constant GPR_MULTIINSIDE = $ff05; Constant GPR_CREATURE = $ff06; Constant ENDIT_TOKEN = 15; ! Value used to mean "end of grammar line" #Iftrue (Grammar__Version == 1); [ AnalyseToken token m; found_tdata = token; if (token < 0) { found_ttype = ILLEGAL_TT; return; } if (token <= 8) { found_ttype = ELEMENTARY_TT; return; } if (token < 15) { found_ttype = ILLEGAL_TT; return; } if (token == 15) { found_ttype = ELEMENTARY_TT; return; } if (token < 48) { found_ttype = ROUTINE_FILTER_TT; found_tdata = token - 16; return; } if (token < 80) { found_ttype = GPR_TT; found_tdata = #preactions_table-->(token-48); return; } if (token < 128) { found_ttype = SCOPE_TT; found_tdata = #preactions_table-->(token-80); return; } if (token < 180) { found_ttype = ATTR_FILTER_TT; found_tdata = token - 128; return; } found_ttype = PREPOSITION_TT; m = #adjectives_table; for (::) { if (token == m-->1) { found_tdata = m-->0; return; } m = m+4; } m = #adjectives_table; RunTimeError(1); found_tdata = m; ]; [ UnpackGrammarLine line_address i m; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } for (i=0 : i<=5 : i++) { line_token-->i = line_address->(i+1); AnalyseToken(line_token-->i); if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN) && (m == line_address->0)) { line_token-->i = ENDIT_TOKEN; break; } line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; if (found_ttype ~= PREPOSITION_TT) m++; } action_to_be = line_address->7; action_reversed = false; params_wanted = line_address->0; return line_address + 8; ]; #Ifnot; ! Grammar__Version == 2 [ AnalyseToken token; if (token == ENDIT_TOKEN) { found_ttype = ELEMENTARY_TT; found_tdata = ENDIT_TOKEN; return; } found_ttype = (token->0) & $$1111; found_tdata = (token+1)-->0; ]; #Ifdef TARGET_ZCODE; [ UnpackGrammarLine line_address i; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } action_to_be = 256*(line_address->0) + line_address->1; action_reversed = ((action_to_be & $400) ~= 0); action_to_be = action_to_be & $3ff; line_address--; params_wanted = 0; for (i=0 : : i++) { line_address = line_address + 3; if (line_address->0 == ENDIT_TOKEN) break; line_token-->i = line_address; AnalyseToken(line_address); if (found_ttype ~= PREPOSITION_TT) params_wanted++; line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; } return line_address + 1; ]; #Ifnot; ! TARGET_GLULX [ UnpackGrammarLine line_address i; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } @aloads line_address 0 action_to_be; action_reversed = (((line_address->2) & 1) ~= 0); line_address = line_address - 2; params_wanted = 0; for (i=0 : : i++) { line_address = line_address + 5; if (line_address->0 == ENDIT_TOKEN) break; line_token-->i = line_address; AnalyseToken(line_address); if (found_ttype ~= PREPOSITION_TT) params_wanted++; line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; } return line_address + 1; ]; #Endif; ! TARGET_ #Endif; ! Grammar__Version ! To protect against a bug in early versions of the "Zip" interpreter: ! Of course, in Glulx, this routine actually performs work. #Ifdef TARGET_ZCODE; [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ]; #Ifnot; ! TARGET_GLULX Array gg_tokenbuf -> DICT_WORD_SIZE; [ GGWordCompare str1 str2 ix jx; for (ix=0 : ixix) - (str2->ix); if (jx ~= 0) return jx; } return 0; ]; [ Tokenise__ buf tab cx numwords len bx ix wx wpos wlen val res dictlen entrylen; len = buf-->0; buf = buf+WORDSIZE; ! First, split the buffer up into words. We use the standard Infocom ! list of word separators (comma, period, double-quote). cx = 0; numwords = 0; while (cx < len) { while (cx < len && buf->cx == ' ') cx++; if (cx >= len) break; bx = cx; if (buf->cx == '.' or ',' or '"') cx++; else { while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++; } tab-->(numwords*3+2) = (cx-bx); tab-->(numwords*3+3) = WORDSIZE+bx; numwords++; if (numwords >= MAX_BUFFER_WORDS) break; } tab-->0 = numwords; ! Now we look each word up in the dictionary. dictlen = #dictionary_table-->0; entrylen = DICT_WORD_SIZE + 7; for (wx=0 : wx(wx*3+2); wpos = tab-->(wx*3+3); ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE ! characters and lower case. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE; cx = wpos - WORDSIZE; for (ix=0 : ixix = glk_char_to_lower(buf->(cx+ix)); for (: ixix = 0; val = #dictionary_table + WORDSIZE; @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res; tab-->(wx*3+1) = res; } ]; #Endif; ! TARGET_ ! ============================================================================ ! The InformParser object abstracts the front end of the parser. ! ! InformParser.parse_input(results) ! returns only when a sensible request has been made, and puts into the ! "results" buffer: ! ! --> 0 = The action number ! --> 1 = Number of parameters ! --> 2, 3, ... = The parameters (object numbers), but ! 0 means "put the multiple object list here" ! 1 means "put one of the special numbers here" ! ! ---------------------------------------------------------------------------- Object InformParser "(Inform Parser)" with parse_input [ results; Parser__parse(results); ], has proper; ! ---------------------------------------------------------------------------- ! The Keyboard routine actually receives the player's words, ! putting the words in "a_buffer" and their dictionary addresses in ! "a_table". It is assumed that the table is the same one on each ! (standard) call. ! ! It can also be used by miscellaneous routines in the game to ask ! yes-no questions and the like, without invoking the rest of the parser. ! ! Return the number of words typed ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ GetNthChar a_buffer n i; for (i = 0: a_buffer->(2+i) == ' ': i++) { if (i > a_buffer->(1)) return false; } return a_buffer->(2+i+n); ]; [ KeyboardPrimitive a_buffer a_table; read a_buffer a_table; #Iftrue (#version_number == 6); @output_stream -1; @loadb a_buffer 1 -> sp; @add a_buffer 2 -> sp; @print_table sp sp; new_line; @output_stream 1; #Endif; ]; [ KeyCharPrimitive win key; if (win) @set_window win; @read_char 1 -> key; return key; ]; [ KeyTimerInterrupt; rtrue; ]; [ KeyDelay tenths key; @read_char 1 tenths KeyTimerInterrupt -> key; return key; ]; #Ifnot; ! TARGET_GLULX [ GetNthChar a_buffer n i; for (i = 0: a_buffer->(4+i) == ' ': i++) { if (i > a_buffer->(1)) return false; } return a_buffer->(4+i+n); ]; [ KeyCharPrimitive win nostat done res ix jx ch; jx = ch; ! squash compiler warnings if (win == 0) win = gg_mainwin; if (gg_commandstr ~= 0 && gg_command_reading ~= false) { ! get_line_stream done = glk_get_line_stream(gg_commandstr, gg_arguments, 31); if (done == 0) { glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; ! fall through to normal user input. } else { ! Trim the trailing newline if (gg_arguments->(done-1) == 10) done = done-1; res = gg_arguments->0; if (res == '\') { res = 0; for (ix=1 : ixix; if (ch >= '0' && ch <= '9') { @shiftl res 4 res; res = res + (ch-'0'); } else if (ch >= 'a' && ch <= 'f') { @shiftl res 4 res; res = res + (ch+10-'a'); } else if (ch >= 'A' && ch <= 'F') { @shiftl res 4 res; res = res + (ch+10-'A'); } } } jump KCPContinue; } } done = false; glk_request_char_event(win); while (~~done) { glk_select(gg_event); switch (gg_event-->0) { 5: ! evtype_Arrange if (nostat) { glk_cancel_char_event(win); res = $80000000; done = true; break; } DrawStatusLine(); 2: ! evtype_CharInput if (gg_event-->1 == win) { res = gg_event-->2; done = true; } } ix = HandleGlkEvent(gg_event, 1, gg_arguments); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments); if (ix == 2) { res = gg_arguments-->0; done = true; } else if (ix == -1) { done = false; } } if (gg_commandstr ~= 0 && gg_command_reading == false) { if (res < 32 || res >= 256 || (res == '\' or ' ')) { glk_put_char_stream(gg_commandstr, '\'); done = 0; jx = res; for (ix=0 : ix<8 : ix++) { @ushiftr jx 28 ch; @shiftl jx 4 jx; ch = ch & $0F; if (ch ~= 0 || ix == 7) done = 1; if (done) { if (ch >= 0 && ch <= 9) ch = ch + '0'; else ch = (ch - 10) + 'A'; glk_put_char_stream(gg_commandstr, ch); } } } else { glk_put_char_stream(gg_commandstr, res); } glk_put_char_stream(gg_commandstr, 10); } .KCPContinue; return res; ]; [ KeyDelay tenths key done ix; glk_request_char_event(gg_mainwin); glk_request_timer_events(tenths*100); while (~~done) { glk_select(gg_event); ix = HandleGlkEvent(gg_event, 1, gg_arguments); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments); if (ix == 2) { key = gg_arguments-->0; done = true; } else if (ix >= 0 && gg_event-->0 == 1 or 2) { key = gg_event-->2; done = true; } } glk_cancel_char_event(gg_mainwin); glk_request_timer_events(0); return key; ]; [ KeyboardPrimitive a_buffer a_table done ix; if (gg_commandstr ~= 0 && gg_command_reading ~= false) { ! get_line_stream done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1); if (done == 0) { glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; ! L__M(##CommandsRead, 5); would come after prompt ! fall through to normal user input. } else { ! Trim the trailing newline if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1; a_buffer-->0 = done; glk_set_style(style_Input); glk_put_buffer(a_buffer+WORDSIZE, done); glk_set_style(style_Normal); print "^"; jump KPContinue; } } done = false; glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); while (~~done) { glk_select(gg_event); switch (gg_event-->0) { 5: ! evtype_Arrange DrawStatusLine(); 3: ! evtype_LineInput if (gg_event-->1 == gg_mainwin) { a_buffer-->0 = gg_event-->2; done = true; } } ix = HandleGlkEvent(gg_event, 0, a_buffer); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 0, a_buffer); if (ix == 2) done = true; else if (ix == -1) done = false; } if (gg_commandstr ~= 0 && gg_command_reading == false) { ! put_buffer_stream glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0); glk_put_char_stream(gg_commandstr, 10); } .KPContinue; Tokenise__(a_buffer,a_table); ! It's time to close any quote window we've got going. if (gg_quotewin) { glk_window_close(gg_quotewin, 0); gg_quotewin = 0; } ]; #Endif; ! TARGET_ [ Keyboard a_buffer a_table nw i w w2 x1 x2; DisplayStatus(); .FreshInput; ! Save the start of the buffer, in case "oops" needs to restore it ! to the previous time's buffer for (i=0 : ii = a_buffer->i; ! In case of an array entry corruption that shouldn't happen, but would be ! disastrous if it did: #Ifdef TARGET_ZCODE; a_buffer->0 = INPUT_BUFFER_LEN - WORDSIZE; a_table->0 = MAX_BUFFER_WORDS; ! Allow to split input into this many words #Endif; ! TARGET_ ! Print the prompt, and read in the words and dictionary addresses L__M(##Prompt); if (AfterPrompt() == 0) LibraryExtensions.RunAll(ext_afterprompt); #IfV5; DrawStatusLine(); #Endif; ! V5 KeyboardPrimitive(a_buffer, a_table); nw = NumberWords(a_table); ! If the line was blank, get a fresh line if (nw == 0) { L__M(##Miscellany, 10); jump FreshInput; } ! Unless the opening word was "oops", return ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX. w = a_table-->1; if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops; if (a_buffer->WORDSIZE == COMMENT_CHARACTER) { #Ifdef TARGET_ZCODE; if ((HDR_GAMEFLAGS-->0) & $0001 || xcommsdir) L__M(##Miscellany, 54); else L__M(##Miscellany, 55); #Ifnot; ! TARGET_GLULX if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54); else L__M(##Miscellany, 55); #Endif; ! TARGET_ jump FreshInput; } #IfV5; ! Undo handling if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) { i = PerformUndo(); if (i == 0) jump FreshInput; } #Ifdef TARGET_ZCODE; @save_undo i; #Ifnot; ! TARGET_GLULX @saveundo i; if (i == -1) { GGRecoverObjects(); i = 2; } else i = (~~i); #Endif; ! TARGET_ just_undone = 0; undo_flag = 2; if (i == -1) undo_flag = 0; if (i == 0) undo_flag = 1; if (i == 2) { RestoreColours(); #Ifdef TARGET_ZCODE; style bold; #Ifnot; ! TARGET_GLULX glk_set_style(style_Subheader); #Endif; ! TARGET_ print (name) location, "^"; #Ifdef TARGET_ZCODE; style roman; #Ifnot; ! TARGET_GLULX glk_set_style(style_Normal); #Endif; ! TARGET_ L__M(##Miscellany, 13); just_undone = 1; jump FreshInput; } #Endif; ! V5 return nw; .DoOops; if (oops_from == 0) { L__M(##Miscellany, 14); jump FreshInput; } if (nw == 1) { L__M(##Miscellany, 15); jump FreshInput; } if (nw > 2) { L__M(##Miscellany, 16); jump FreshInput; } ! So now we know: there was a previous mistake, and the player has ! attempted to correct a single word of it. for (i=0 : ii = a_buffer->i; #Ifdef TARGET_ZCODE; x1 = a_table->9; ! Start of word following "oops" x2 = a_table->8; ! Length of word following "oops" #Ifnot; ! TARGET_GLULX x1 = a_table-->6; ! Start of word following "oops" x2 = a_table-->5; ! Length of word following "oops" #Endif; ! TARGET_ ! Repair the buffer to the text that was in it before the "oops" ! was typed: for (i=0 : i < OOPS_WORKSPACE_LEN : i++) a_buffer->i = oops_workspace->i; Tokenise__(a_buffer, a_table); ! Work out the position in the buffer of the word to be corrected: #Ifdef TARGET_ZCODE; w = a_table->(4*oops_from + 1); ! Start of word to go w2 = a_table->(4*oops_from); ! Length of word to go #Ifnot; ! TARGET_GLULX w = a_table-->(3*oops_from); ! Start of word to go w2 = a_table-->(3*oops_from - 1); ! Length of word to go #Endif; ! TARGET_ #IfDef OOPS_CHECK; print "[~"; for (i=0 : i(i+w); print "~ --> ~"; #Endif; ! Write spaces over the word to be corrected: for (i=0 : i(i+w) = ' '; if (w2 < x2) { ! If the replacement is longer than the original, move up... for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--) a_buffer->i = a_buffer->(i-x2+w2); ! ...increasing buffer size accordingly. SetKeyBufLength(GetKeyBufLength(a_buffer) + (x2-w2), a_buffer); } ! Write the correction in: for (i=0 : i(i+w) = buffer2->(i+x1); #IfDef OOPS_CHECK; print (char) buffer2->(i+x1); #Endif; } #IfDef OOPS_CHECK; print "~]^^"; #Endif; Tokenise__(a_buffer, a_table); nw=NumberWords(a_table); ! saved_ml = 0; return nw; ]; ! end of Keyboard [ PerformUndo i; if (turns == START_MOVE) { L__M(##Miscellany, 11); return 0; } if (undo_flag == 0) { L__M(##Miscellany, 6); return 0; } if (undo_flag == 1) { L__M(##Miscellany, 7); return 0; } #Ifdef TARGET_ZCODE; @restore_undo i; #Ifnot; ! TARGET_GLULX @restoreundo i; i = (~~i); #Endif; ! TARGET_ if (i == 0) { L__M(##Miscellany, 7); return 0; } L__M(##Miscellany, 1); return 1; ]; ! ========================== ! Taken from I7's Parser.i6t ! ========================== [ DictionaryWordToVerbNum dword verbnum; #Ifdef TARGET_ZCODE; verbnum = $ff-(dword->#dict_par2); #Ifnot; ! GLULX dword = dword + #dict_par2 - 1; @aloads dword 0 verbnum; verbnum = $ffff-verbnum; #Endif; return verbnum; ]; ! ========================== ! ---------------------------------------------------------------------------- ! To simplify the picture a little, a rough map of the main routine: ! ! (A) Get the input, do "oops" and "again" ! (B) Is it a direction, and so an implicit "go"? If so go to (K) ! (C) Is anyone being addressed? ! (D) Get the verb: try all the syntax lines for that verb ! (E) Break down a syntax line into analysed tokens ! (F) Look ahead for advance warning for multiexcept/multiinside ! (G) Parse each token in turn (calling ParseToken to do most of the work) ! (H) Cheaply parse otherwise unrecognised conversation and return ! (I) Print best possible error message ! (J) Retry the whole lot ! (K) Last thing: check for "then" and further instructions(s), return. ! ! The strategic points (A) to (K) are marked in the commentary. ! ! Note that there are three different places where a return can happen. ! ---------------------------------------------------------------------------- [ Parser__parse results syntax line num_lines line_address i j k token l m line_etype vw; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! A: Get the input, do "oops" and "again" ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Firstly, in "not held" mode, we still have a command left over from last ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit" ! last time, with "eat biscuit" tucked away until now). So we return that. if (notheld_mode == 1) { for (i=0 : i<8 : i++) results-->i = kept_results-->i; notheld_mode = 0; rtrue; } if (held_back_mode ~= 0) { held_back_mode = 0; Tokenise__(buffer, parse); jump ReParse; } .ReType; Keyboard(buffer, parse); #Ifdef INFIX; ! An Infix verb is a special kind of meta verb. We mark them here. if (GetNthChar(buffer, 0) == ';') infix_verb = true; else infix_verb = false; #Endif; .ReParse; parser_inflection = name; parser_inflection_func = false; ! Initially assume the command is aimed at the player, and the verb ! is the first word num_words = NumberWords(); wn = 1; #Ifdef LanguageToInformese; LanguageToInformese(); #IfV5; ! Re-tokenise: Tokenise__(buffer,parse); #Endif; ! V5 #Endif; ! LanguageToInformese if (BeforeParsing() == false) { LibraryExtensions.ext_number_1 = wn; ! Set "between calls" functionality to restore wn each pass LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN; LibraryExtensions.RunWhile(ext_beforeparsing, false); LibraryExtensions.BetweenCalls = 0; ! Turn off "between calls" functionality } num_words = NumberWords(); k=0; #Ifdef DEBUG; if (parser_trace >= 2) { print "[ "; for (i=0 : im; print "~ "; if (j == 0) print "?"; else { #Ifdef TARGET_ZCODE; if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 && UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0) print (address) j; else print j; #Ifnot; ! TARGET_GLULX if (j->0 == $60) print (address) j; else print j; #Endif; ! TARGET_ } if (i ~= num_words-1) print " / "; } print " ]^"; } #Endif; ! DEBUG verb_wordnum = 1; actor = player; actors_location = ScopeCeiling(player); usual_grammar_after = 0; .AlmostReParse; scope_token = 0; action_to_be = NULL; ! Begin from what we currently think is the verb word .BeginCommand; wn = verb_wordnum; verb_word = NextWordStopped(); ! If there's no input here, we must have something like "person,". if (verb_word == -1) { best_etype = STUCK_PE; jump GiveError; } ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing ! has previously been typed; simply copy the previous text across if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD; if (verb_word == AGAIN1__WD) { if (actor ~= player) { L__M(##Miscellany, 20); jump ReType; } if (GetKeyBufLength(buffer3) == 0) { L__M(##Miscellany, 21); jump ReType; } if (WordAddress(verb_wordnum) == buffer + WORDSIZE) { ! not held back ! splice rest of buffer onto end of buffer3 i = GetKeyBufLength(buffer3); while (buffer3 -> (i + WORDSIZE - 1) == ' ' or '.') i--; j = i - WordLength(verb_wordnum); ! amount to move buffer up by if (j > 0) { for (m=INPUT_BUFFER_LEN-1 : m>=WORDSIZE+j : m--) buffer->m = buffer->(m-j); SetKeyBufLength(GetKeyBufLength()+j); } for (m=WORDSIZE : mm = buffer3->m; if (j < 0) for (:mm = ' '; } else for (i=0 : ii = buffer3->i; jump ReParse; } ! Save the present input in case of an "again" next time if (verb_word ~= AGAIN1__WD) for (i=0 : ii = buffer->i; if (usual_grammar_after == 0) { j = verb_wordnum; i = RunRoutines(actor, grammar); #Ifdef DEBUG; if (parser_trace >= 2 && actor.grammar ~= 0 or NULL) print " [Grammar property returned ", i, "]^"; #Endif; ! DEBUG #Ifdef TARGET_ZCODE; if ((i ~= 0 or 1) && (UnsignedCompare(i, dict_start) < 0 || UnsignedCompare(i, dict_end) >= 0 || (i - dict_start) % dict_entry_size ~= 0)) { usual_grammar_after = j; i=-i; } #Ifnot; ! TARGET_GLULX if (i < 0) { usual_grammar_after = j; i=-i; } #Endif; if (i == 1) { results-->0 = action; results-->1 = 0; ! Number of parameters results-->2 = noun; results-->3 = second; if (noun) results-->1 = 1; if (second) results-->1 = 2; rtrue; } if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; } else { wn = verb_wordnum; verb_word = NextWord(); } } else usual_grammar_after = 0; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! B: Is it a direction, and so an implicit "go"? If so go to (K) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifdef LanguageIsVerb; if (verb_word == 0) { i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum); wn = i; } #Endif; ! LanguageIsVerb ! If the first word is not listed as a verb, it must be a direction ! or the name of someone to talk to if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) { ! So is the first word an object contained in the special object "compass" ! (i.e., a direction)? This needs use of NounDomain, a routine which ! does the object matching, returning the object number, or 0 if none found, ! or REPARSE_CODE if it has restructured the parse table so the whole parse ! must be begun again... wn = verb_wordnum; indef_mode = false; token_filter = 0; l = NounDomain(compass, 0, NOUN_TOKEN); if (l == REPARSE_CODE) jump ReParse; ! If it is a direction, send back the results: ! action=GoSub, no of arguments=1, argument 1=the direction. if (l ~= 0) { results-->0 = ##Go; action_to_be = ##Go; results-->1 = 1; results-->2 = l; jump LookForMore; } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! C: Is anyone being addressed? ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Only check for a comma (a "someone, do something" command) if we are ! not already in the middle of one. (This simplification stops us from ! worrying about "robot, wizard, you are an idiot", telling the robot to ! tell the wizard that she is an idiot.) if (actor == player) { for (j=2 : j<=num_words : j++) { i=NextWord(); if (i == comma_word) jump Conversation; } } vw = verb_word; verb_word = UnknownVerb(vw); if (verb_word == false) verb_word = LibraryExtensions.RunWhile(ext_unknownverb, false, vw); if (verb_word) jump VerbAccepted; best_etype = VERB_PE; jump GiveError; ! NextWord nudges the word number wn on by one each time, so we've now ! advanced past a comma. (A comma is a word all on its own in the table.) .Conversation; j = wn - 1; if (j == 1) { L__M(##Miscellany, 22); jump ReType; } ! Use NounDomain (in the context of "animate creature") to see if the ! words make sense as the name of someone held or nearby wn = 1; lookahead = HELD_TOKEN; scope_reason = TALKING_REASON; l = NounDomain(player,actors_location,CREATURE_TOKEN); scope_reason = PARSING_REASON; if (l == REPARSE_CODE) jump ReParse; if (l == 0) { L__M(##Miscellany, 23); jump ReType; } .Conversation2; ! The object addressed must at least be "talkable" if not actually "animate" ! (the distinction allows, for instance, a microphone to be spoken to, ! without the parser thinking that the microphone is human). if (l hasnt animate && l hasnt talkable) { L__M(##Miscellany, 24, l); jump ReType; } ! Check that there aren't any mystery words between the end of the person's ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north"). if (wn ~= j) { L__M(##Miscellany, 25); jump ReType; } ! The player has now successfully named someone. Adjust "him", "her", "it": PronounNotice(l); ! Set the global variable "actor", adjust the number of the first word, ! and begin parsing again from there. verb_wordnum = j + 1; ! Stop things like "me, again": if (l == player) { wn = verb_wordnum; if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) { L__M(##Miscellany, 20); jump ReType; } } actor = l; actors_location = ScopeCeiling(l); #Ifdef DEBUG; if (parser_trace >= 1) print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^"; #Endif; ! DEBUG jump BeginCommand; } ! end of first-word-not-a-verb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! D: Get the verb: try all the syntax lines for that verb ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .VerbAccepted; ! We now definitely have a verb, not a direction, whether we got here by the ! "take ..." or "person, take ..." method. Get the meta flag for this verb: meta = (verb_word->#dict_par1) & DICT_META; ! You can't order other people to "full score" for you, and so on... if (meta && actor ~= player) { best_etype = VERB_PE; meta = false; jump GiveError; } ! Now let i be the corresponding verb number, stored in the dictionary entry ! (in a peculiar 255-n fashion for traditional Infocom reasons)... i = DictionaryWordToVerbNum(verb_word); ! ...then look up the i-th entry in the verb table, whose address is at word ! 7 in the Z-machine (in the header), so as to get the address of the syntax ! table for the given verb... #Ifdef TARGET_ZCODE; syntax = (HDR_STATICMEMORY-->0)-->i; #Ifnot; ! TARGET_GLULX syntax = (#grammar_table)-->(i+1); #Endif; ! TARGET_ ! ...and then see how many lines (ie, different patterns corresponding to the ! same verb) are stored in the parse table... num_lines = (syntax->0) - 1; ! ...and now go through them all, one by one. ! To prevent pronoun_word 0 being misunderstood, pronoun_word = NULL; pronoun_obj = NULL; #Ifdef DEBUG; if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^"; #Endif; ! DEBUG best_etype = STUCK_PE; nextbest_etype = STUCK_PE; multiflag = false; saved_oops = 0; ! "best_etype" is the current failure-to-match error - it is by default ! the least informative one, "don't understand that sentence". ! "nextbest_etype" remembers the best alternative to having to ask a ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE). ! multiflag is used here to prevent inappropriate MULTI_PE errors ! in addition to its unrelated duties passing information to action routines !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! E: Break down a syntax line into analysed tokens ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! line_address = syntax + 1; for (line=0 : line<=num_lines : line++) { for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } ! Unpack the syntax line from Inform format into three arrays; ensure that ! the sequence of tokens ends in an ENDIT_TOKEN. line_address = UnpackGrammarLine(line_address); #Ifdef DEBUG; if (parser_trace >= 1) { if (parser_trace >= 2) new_line; print "[line ", line; DebugGrammarLine(); print "]^"; } #Endif; ! DEBUG ! We aren't in "not holding" or inferring modes, and haven't entered ! any parameters on the line yet, or any special numbers; the multiple ! object is still empty. token_filter = 0; not_holding = 0; inferfrom = 0; parameters = 0; nsns = 0; special_word = 0; special_number = 0; multiple_object-->0 = 0; multi_context = 0; etype = STUCK_PE; line_etype = 100; ! Put the word marker back to just after the verb wn = verb_wordnum+1; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! F: Look ahead for advance warning for multiexcept/multiinside ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! There are two special cases where parsing a token now has to be ! affected by the result of parsing another token later, and these ! two cases (multiexcept and multiinside tokens) are helped by a quick ! look ahead, to work out the future token now. We can only carry this ! out in the simple (but by far the most common) case: ! ! multiexcept noun ! ! and similarly for multiinside. advance_warning = NULL; indef_mode = false; for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) { scope_token = 0; if (line_ttype-->pcount ~= PREPOSITION_TT) i++; if (line_ttype-->pcount == ELEMENTARY_TT) { if (line_tdata-->pcount == MULTI_TOKEN) m = true; if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && i == 1) { ! First non-preposition is "multiexcept" or ! "multiinside", so look ahead. #Ifdef DEBUG; if (parser_trace >= 2) print " [Trying look-ahead]^"; #Endif; ! DEBUG ! We need this to be followed by 1 or more prepositions. pcount++; if (line_ttype-->pcount == PREPOSITION_TT) { ! skip ahead to a preposition word in the input do { l = NextWord(); } until ((wn > num_words) || (l && (l->#dict_par1) & DICT_PREP ~= 0)); if (wn > num_words) { #Ifdef DEBUG; if (parser_trace >= 2) print " [Look-ahead aborted: prepositions missing]^"; #Endif; jump EmptyLine; } do { if (PrepositionChain(l, pcount) ~= -1) { ! advance past the chain if ((line_token-->pcount)->0 & $20 ~= 0) { pcount++; while ((line_token-->pcount ~= ENDIT_TOKEN) && ((line_token-->pcount)->0 & $10 ~= 0)) pcount++; } else { pcount++; } } else { ! try to find another preposition word do { l = NextWord(); } until ((wn >= num_words) || (l && (l->#dict_par1) & 8 ~= 0)); if (l && (l->#dict_par1) & 8) continue; ! lookahead failed #Ifdef DEBUG; if (parser_trace >= 2) print " [Look-ahead aborted: prepositions don't match]^"; #Endif; jump LineFailed; } l = NextWord(); } until (line_ttype-->pcount ~= PREPOSITION_TT); .EmptyLine; ! put back the non-preposition we just read wn--; if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) { l = Descriptors(); ! skip past THE etc if (l~=0) etype=l; ! don't allow multiple objects l = NounDomain(actors_location, actor, NOUN_TOKEN); #Ifdef DEBUG; if (parser_trace >= 2) { print " [Advanced to ~noun~ token: "; if (l == REPARSE_CODE) print "re-parse request]^"; if (l == 1) print "but multiple found]^"; if (l == 0) print "error ", etype, "]^"; if (l >= 2) print (the) l, "]^"; } #Endif; ! DEBUG if (l == REPARSE_CODE) jump ReParse; if (l >= 2) advance_warning = l; } } break; } } } ! Slightly different line-parsing rules will apply to "take multi", to ! prevent "take all" behaving correctly but misleadingly when there's ! nothing to take. take_all_rule = 0; if (m && params_wanted == 1 && action_to_be == ##Take) take_all_rule = 1; ! And now start again, properly, forearmed or not as the case may be. ! As a precaution, we clear all the variables again (they may have been ! disturbed by the call to NounDomain, which may have called outside ! code, which may have done anything!). not_holding = 0; inferfrom = 0; inferword = 0; parameters = 0; nsns = 0; special_word = 0; special_number = 0; multiple_object-->0 = 0; etype = STUCK_PE; line_etype = 100; wn = verb_wordnum+1; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! G: Parse each token in turn (calling ParseToken to do most of the work) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! "Pattern" gradually accumulates what has been recognised so far, ! so that it may be reprinted by the parser later on for (pcount=1 : : pcount++) { pattern-->pcount = PATTERN_NULL; scope_token = 0; token = line_token-->(pcount-1); lookahead = line_token-->pcount; #Ifdef DEBUG; if (parser_trace >= 2) print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token, "]^"; #Endif; ! DEBUG if (token ~= ENDIT_TOKEN) { scope_reason = PARSING_REASON; parser_inflection = name; parser_inflection_func = false; AnalyseToken(token); if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT && found_tdata == TOPIC_TOKEN && line_etype == 100) { if (actor ~= player) { best_etype = VERB_PE; jump GiveError; } l = inputobjs-->2; wn--; j = wn; jump Conversation2; } l = ParseToken__(found_ttype, found_tdata, pcount-1, token); while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256); scope_reason = PARSING_REASON; if (l == GPR_PREPOSITION) { if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT || found_tdata~=TOPIC_TOKEN)) params_wanted--; l = true; } else if (l < 0) l = false; else if (l ~= GPR_REPARSE) { if (l == GPR_NUMBER) { if (nsns == 0) special_number1 = parsed_number; else special_number2 = parsed_number; nsns++; l = 1; } if (l == GPR_MULTIPLE) l = 0; results-->(parameters+2) = l; parameters++; pattern-->pcount = l; l = true; } #Ifdef DEBUG; if (parser_trace >= 3) { print " [token resulted in "; if (l == REPARSE_CODE) print "re-parse request]^"; if (l == 0) print "failure with error type ", etype, "]^"; if (l == 1) print "success]^"; } #Endif; ! DEBUG if (l == REPARSE_CODE) jump ReParse; if (l == false) { if (etype < line_etype) line_etype = etype; if (etype == STUCK_PE || wn >= num_words) break; } } else { ! If the player has entered enough already but there's still ! text to wade through: store the pattern away so as to be able to produce ! a decent error message if this turns out to be the best we ever manage, ! and in the mean time give up on this line ! However, if the superfluous text begins with a comma or "then" then ! take that to be the start of another instruction if (line_etype < 100) break; if (wn <= num_words) { l = NextWord(); if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) { held_back_mode = 1; hb_wn = wn-1; } else { for (m=0 : m<32 : m++) pattern2-->m = pattern-->m; pcount2 = pcount; etype = UPTO_PE; break; } } ! Now, we may need to revise the multiple object because of the single one ! we now know (but didn't when the list was drawn up). if (parameters >= 1 && results-->2 == 0) { l = ReviseMulti(results-->3); if (l ~= 0) { etype = l; results-->0 = action_to_be; break; } } if (parameters >= 2 && results-->3 == 0) { l = ReviseMulti(results-->2); if (l ~= 0) { etype = l; break; } } ! To trap the case of "take all" inferring only "yourself" when absolutely ! nothing else is in the vicinity... if (take_all_rule == 2 && results-->2 == actor) { best_etype = NOTHING_PE; jump GiveError; } #Ifdef DEBUG; if (parser_trace >= 1) print "[Line successfully parsed]^"; #Endif; ! DEBUG ! The line has successfully matched the text. Declare the input error-free... oops_from = 0; ! ...explain any inferences made (using the pattern)... if (inferfrom ~= 0 && no_infer_message == false) { print "("; PrintCommand(inferfrom); print ")^"; } no_infer_message = false; ! ...copy the action number, and the number of parameters... results-->0 = action_to_be; results-->1 = parameters; ! ...reverse first and second parameters if need be... if (action_reversed && parameters == 2) { i = results-->2; results-->2 = results-->3; results-->3 = i; if (nsns == 2) { i = special_number1; special_number1 = special_number2; special_number2 = i; } } ! ...and to reset "it"-style objects to the first of these parameters, if ! there is one (and it really is an object)... if (parameters > 0 && results-->2 >= 2) PronounNotice(results-->2); ! ...and worry about the case where an object was allowed as a parameter ! even though the player wasn't holding it and should have been: in this ! event, keep the results for next time round, go into "not holding" mode, ! and for now tell the player what's happening and return a "take" request ! instead... if (not_holding ~= 0 && actor == player) { action = ##Take; i = RunRoutines(not_holding, before_implicit); ! i = 0: Take the object, tell the player (default) ! i = 1: Take the object, don't tell the player ! i = 2: don't Take the object, continue ! i = 3: don't Take the object, don't continue if (i > 2 || no_implicit_actions) { best_etype = NOTHELD_PE; jump GiveError; } ! perform the implicit Take if (i < 2) { if (i ~= 1) ! and tell the player L__M(##Miscellany, 26, not_holding); notheld_mode = 1; for (i=0 : i<8 : i++) kept_results-->i = results-->i; results-->0 = ##Take; results-->1 = 1; results-->2 = not_holding; } } ! (Notice that implicit takes are only generated for the player, and not ! for other actors. This avoids entirely logical, but misleading, text ! being printed.) ! ...and return from the parser altogether, having successfully matched ! a line. if (held_back_mode == 1) { wn=hb_wn; jump LookForMore; } rtrue; } ! end of if(token ~= ENDIT_TOKEN) else } ! end of for(pcount++) .LineFailed; ! The line has failed to match. ! We continue the outer "for" loop, trying the next line in the grammar. if (line_etype < 100) etype = line_etype; if (etype > best_etype) best_etype = etype; if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype; ! ...unless the line was something like "take all" which failed because ! nothing matched the "all", in which case we stop and give an error now. if (take_all_rule == 2 && etype==NOTHING_PE) break; } ! end of for(line++) ! The grammar is exhausted: every line has failed to match. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! H: Cheaply parse otherwise unrecognised conversation and return ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .GiveError; etype = best_etype; ! Errors are handled differently depending on who was talking. ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then ! it is taken as conversation which the parser has no business in disallowing. if (actor ~= player) { if (usual_grammar_after ~= 0) { verb_wordnum = usual_grammar_after; jump AlmostReParse; } wn = verb_wordnum; special_word = NextWord(); if (special_word == comma_word) { special_word = NextWord(); verb_wordnum++; } special_number = TryNumber(verb_wordnum); results-->0 = ##NotUnderstood; results-->1 = 2; results-->2 = 1; special_number1 = special_word; results-->3 = actor; consult_from = verb_wordnum; consult_words = num_words-consult_from+1; rtrue; } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! I: Print best possible error message ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! If the player was the actor (eg, in "take dfghh") the error must be ! printed, and fresh input called for. In four cases the oops word ! must be jiggled (where oops_from is set to something). if (ParserError(etype)) jump ReType; if (LibraryExtensions.RunWhile(ext_parsererror, false, etype)) jump ReType; pronoun_word = pronoun__word; pronoun_obj = pronoun__obj; if (etype == STUCK_PE) { L__M(##Miscellany, 27); oops_from = 1; } if (etype == UPTO_PE) { L__M(##Miscellany, 28); for (m=0 : m<32 : m++) pattern-->m = pattern2-->m; pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56); oops_from = wn-1; } if (etype == NUMBER_PE) L__M(##Miscellany, 29); if (etype == CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops;} if (etype == TOOLIT_PE) L__M(##Miscellany, 31); if (etype == NOTHELD_PE) { L__M(##Miscellany, 32, not_holding); oops_from=saved_oops; } if (etype == MULTI_PE) L__M(##Miscellany, 33); if (etype == MMULTI_PE) L__M(##Miscellany, 34); if (etype == VAGUE_PE) L__M(##Miscellany, 35, pronoun_word); if (etype == EXCEPT_PE) L__M(##Miscellany, 36); if (etype == ANIMA_PE) L__M(##Miscellany, 37); if (etype == VERB_PE) L__M(##Miscellany, 38); if (etype == SCENERY_PE) L__M(##Miscellany, 39); if (etype == ITGONE_PE) { if (pronoun_obj == NULL) L__M(##Miscellany, 35, pronoun_word); else L__M(##Miscellany, 40, pronoun_word, pronoun_obj); } if (etype == JUNKAFTER_PE) L__M(##Miscellany, 41); if (etype == TOOFEW_PE) L__M(##Miscellany, 42, multi_had); if (etype == NOTHING_PE) { if (results-->0 == ##Remove && results-->3 ofclass Object) { noun = results-->3; ! ensure valid for messages if (noun has animate) L__M(##Miscellany, 44, verb_word); else if (noun hasnt container or supporter) L__M(##Insert, 2, noun); else if (noun has container && noun hasnt open) L__M(##Take, 9, noun); else if (children(noun)==0) L__M(##Search, 6, noun); else results-->0 = 0; } if (results-->0 ~= ##Remove) { if (multi_wanted == 100) L__M(##Miscellany, 43); else { #Ifdef NO_TAKE_ALL; if (take_all_rule == 2) L__M(##Miscellany, 59); else L__M(##Miscellany, 44, verb_word); #Ifnot; L__M(##Miscellany, 44, verb_word); #Endif; ! NO_TAKE_ALL } } } if (etype == ASKSCOPE_PE) { scope_stage = 3; if (scope_error() == -1) { best_etype = nextbest_etype; jump GiveError; } } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! J: Retry the whole lot ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! And go (almost) right back to square one... jump ReType; ! ...being careful not to go all the way back, to avoid infinite repetition ! of a deferred command causing an error. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! K: Last thing: check for "then" and further instructions(s), return. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! At this point, the return value is all prepared, and we are only looking ! to see if there is a "then" followed by subsequent instruction(s). .LookForMore; if (wn > num_words) rtrue; i = NextWord(); if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) { if (wn > num_words) { held_back_mode = false; return; } i = WordAddress(verb_wordnum); j = WordAddress(wn); for (: i0 = ' '; i = NextWord(); if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) { ! Delete the words "then again" from the again buffer, ! in which we have just realised that it must occur: ! prevents an infinite loop on "i. again" i = WordAddress(wn-2)-buffer; if (wn > num_words) j = INPUT_BUFFER_LEN-1; else j = WordAddress(wn)-buffer; for (: ii = ' '; } Tokenise__(buffer,parse); held_back_mode = true; return; } best_etype = UPTO_PE; jump GiveError; ]; ! end of Parser__parse [ ScopeCeiling person act; act = parent(person); if (act == 0) return person; if (person == player && location == thedark) return thedark; while (parent(act)~=0 && (act has transparent || act has supporter || (act has container && act has open))) act = parent(act); return act; ]; ! ---------------------------------------------------------------------------- ! Descriptors() ! ! Handles descriptive words like "my", "his", "another" and so on. ! Skips "the", and leaves wn pointing to the first misunderstood word. ! ! Allowed to set up for a plural only if allow_p is set ! ! Returns error number, or 0 if no error occurred ! ---------------------------------------------------------------------------- Constant OTHER_BIT = 1; ! These will be used in Adjudicate() Constant MY_BIT = 2; ! to disambiguate choices Constant THAT_BIT = 4; Constant PLURAL_BIT = 8; Constant LIT_BIT = 16; Constant UNLIT_BIT = 32; [ ResetDescriptors; indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0; indef_possambig = false; indef_owner = nothing; indef_cases = $$111111111111; indef_nspec_at = 0; ]; [ Descriptors allows_multiple o x flag cto type m n; ResetDescriptors(); if (wn > num_words) return 0; m = wn; for (flag=true : flag :) { o = NextWordStopped(); flag = false; for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) if (o == LanguageDescriptors-->x) { flag = true; type = LanguageDescriptors-->(x+2); if (type ~= DEFART_PK) indef_mode = true; indef_possambig = true; indef_cases = indef_cases & (LanguageDescriptors-->(x+1)); if (type == POSSESS_PK) { cto = LanguageDescriptors-->(x+3); switch (cto) { 0: indef_type = indef_type | MY_BIT; 1: indef_type = indef_type | THAT_BIT; default: indef_owner = PronounValue(cto); if (indef_owner == NULL) indef_owner = InformParser; } } if (type == light) indef_type = indef_type | LIT_BIT; if (type == -light) indef_type = indef_type | UNLIT_BIT; } if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) { indef_mode = 1; flag = 1; indef_type = indef_type | OTHER_BIT; } if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { indef_mode = 1; flag = 1; indef_wanted = 100; if (take_all_rule == 1) take_all_rule = 2; indef_type = indef_type | PLURAL_BIT; } if (allow_plurals && allows_multiple) { n = TryNumber(wn-1); if (n == 1) { indef_mode = 1; flag = 1; indef_wanted = 1; } if (n > 1) { indef_guess_p = 1; indef_mode = 1; flag = 1; indef_wanted = n; indef_nspec_at = wn-1; indef_type = indef_type | PLURAL_BIT; } } if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) wn--; ! Skip 'of' after these } wn--; num_desc = wn - m; return 0; ]; ! ---------------------------------------------------------------------------- ! CreatureTest: Will this person do for a "creature" token? ! ---------------------------------------------------------------------------- [ CreatureTest obj; if (actor ~= player) rtrue; if (obj has animate) rtrue; if (obj hasnt talkable) rfalse; if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue; rfalse; ]; [ PrepositionChain wd index; if (line_tdata-->index == wd) return wd; if ((line_token-->index)->0 & $20 == 0) return -1; do { if (line_tdata-->index == wd) return wd; index++; } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0)); return -1; ]; ! ---------------------------------------------------------------------------- ! ParseToken(type, data): ! Parses the given token, from the current word number wn, with exactly ! the specification of a general parsing routine. ! (Except that for "topic" tokens and prepositions, you need to supply ! a position in a valid grammar line as third argument.) ! ! Returns: ! GPR_REPARSE for "reconstructed input, please re-parse from scratch" ! GPR_PREPOSITION for "token accepted with no result" ! $ff00 + x for "please parse ParseToken(ELEMENTARY_TT, x) instead" ! 0 for "token accepted, result is the multiple object list" ! 1 for "token accepted, result is the number in parsed_number" ! object num for "token accepted with this object as result" ! -1 for "token rejected" ! ! (A) Analyse the token; handle all tokens not involving ! object lists and break down others into elementary tokens ! (B) Begin parsing an object list ! (C) Parse descriptors (articles, pronouns, etc.) in the list ! (D) Parse an object name ! (E) Parse connectives ("and", "but", etc.) and go back to (C) ! (F) Return the conclusion of parsing an object list ! ---------------------------------------------------------------------------- [ ParseToken given_ttype given_tdata token_n x y; x = lookahead; lookahead = NOUN_TOKEN; y = ParseToken__(given_ttype,given_tdata,token_n); if (y == GPR_REPARSE) Tokenise__(buffer,parse); lookahead = x; return y; ]; [ ParseToken__ given_ttype given_tdata token_n token l o i j k and_parity single_object desc_wn many_flag token_allows_multiple prev_indef_wanted; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! A: Analyse token; handle all not involving object lists, break down others ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! token_filter = 0; switch (given_ttype) { ELEMENTARY_TT: switch (given_tdata) { SPECIAL_TOKEN: l = TryNumber(wn); special_word = NextWord(); #Ifdef DEBUG; if (l ~= -1000) if (parser_trace >= 3) print " [Read special as the number ", l, "]^"; #Endif; ! DEBUG if (l == -1000) { #Ifdef DEBUG; if (parser_trace >= 3) print " [Read special word at word number ", wn, "]^"; #Endif; ! DEBUG l = special_word; } parsed_number = l; return GPR_NUMBER; NUMBER_TOKEN: l=TryNumber(wn++); if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; } #Ifdef DEBUG; if (parser_trace>=3) print " [Read number as ", l, "]^"; #Endif; ! DEBUG parsed_number = l; return GPR_NUMBER; CREATURE_TOKEN: if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell) scope_reason = TALKING_REASON; TOPIC_TOKEN: consult_from = wn; if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) && (line_token-->(token_n+1) ~= ENDIT_TOKEN)) RunTimeError(13); do o = NextWordStopped(); until (o == -1 || PrepositionChain(o, token_n+1) ~= -1); wn--; consult_words = wn-consult_from; if (consult_words == 0) return GPR_FAIL; if (action_to_be == ##Ask or ##Answer or ##Tell) { o = wn; wn = consult_from; parsed_number = NextWord(); #Ifdef EnglishNaturalLanguage; if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord(); #Endif; ! EnglishNaturalLanguage wn = o; return 1; } if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT)) return GPR_FAIL; ! don't infer if required preposition is absent return GPR_PREPOSITION; } PREPOSITION_TT: #Iffalse (Grammar__Version == 1); ! Is it an unnecessary alternative preposition, when a previous choice ! has already been matched? if ((token->0) & $10) return GPR_PREPOSITION; #Endif; ! Grammar__Version ! If we've run out of the player's input, but still have parameters to ! specify, we go into "infer" mode, remembering where we are and the ! preposition we are inferring... if (wn > num_words) { if (inferfrom==0 && parameterspcount = REPARSE_CODE + Dword__No(given_tdata); } ! If we are not inferring, then the line is wrong... if (inferfrom == 0) return -1; ! If not, then the line is right but we mark in the preposition... pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata); return GPR_PREPOSITION; } o = NextWord(); pattern-->pcount = REPARSE_CODE + Dword__No(o); ! Whereas, if the player has typed something here, see if it is the ! required preposition... if it's wrong, the line must be wrong, ! but if it's right, the token is passed (jump to finish this token). if (o == given_tdata) return GPR_PREPOSITION; #Iffalse (Grammar__Version == 1); if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION; #Endif; ! Grammar__Version return -1; GPR_TT: l = given_tdata(); #Ifdef DEBUG; if (parser_trace >= 3) print " [Outside parsing routine returned ", l, "]^"; #Endif; ! DEBUG return l; SCOPE_TT: scope_token = given_tdata; scope_stage = 1; l = scope_token(); #Ifdef DEBUG; if (parser_trace >= 3) print " [Scope routine returned multiple-flag of ", l, "]^"; #Endif; ! DEBUG if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN; ATTR_FILTER_TT: token_filter = 1 + given_tdata; given_tdata = NOUN_TOKEN; ROUTINE_FILTER_TT: token_filter = given_tdata; given_tdata = NOUN_TOKEN; } ! end of switch(given_ttype) token = given_tdata; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! B: Begin parsing an object list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! There are now three possible ways we can be here: ! parsing an elementary token other than "special" or "number"; ! parsing a scope token; ! parsing a noun-filter token (either by routine or attribute). ! ! In each case, token holds the type of elementary parse to ! perform in matching one or more objects, and ! token_filter is 0 (default), an attribute + 1 for an attribute filter ! or a routine address for a routine filter. token_allows_multiple = false; if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) token_allows_multiple = true; many_flag = false; and_parity = true; dont_infer = false; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! C: Parse descriptors (articles, pronouns, etc.) in the list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! We expect to find a list of objects next in what the player's typed. .ObjectList; #Ifdef DEBUG; if (parser_trace >= 3) print " [Object list from word ", wn, "]^"; #Endif; ! DEBUG ! Take an advance look at the next word: if it's "it" or "them", and these ! are unset, set the appropriate error number and give up on the line ! (if not, these are still parsed in the usual way - it is not assumed ! that they still refer to something in scope) o = NextWord(); wn--; pronoun_word = NULL; pronoun_obj = NULL; l = PronounValue(o); if (l ~= 0) { pronoun_word = o; pronoun_obj = l; if (l == NULL) { ! Don't assume this is a use of an unset pronoun until the ! descriptors have been checked, because it might be an ! article (or some such) instead for (l=1 : l<=LanguageDescriptors-->0 : l=l+4) if (o == LanguageDescriptors-->l) jump AssumeDescriptor; pronoun__word = pronoun_word; pronoun__obj = pronoun_obj; etype = VAGUE_PE; return GPR_FAIL; } } .AssumeDescriptor; if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; } allow_plurals = true; desc_wn = wn; .TryAgain; ! First, we parse any descriptive words (like "the", "five" or "every"): l = Descriptors(token_allows_multiple); if (l ~= 0) { etype = l; return GPR_FAIL; } .TryAgain2; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! D: Parse an object name ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! This is an actual specified object, and is therefore where a typing error ! is most likely to occur, so we set: oops_from = wn; ! So, two cases. Case 1: token not equal to "held" ! but we may well be dealing with multiple objects ! In either case below we use NounDomain, giving it the token number as ! context, and two places to look: among the actor's possessions, and in the ! present location. (Note that the order depends on which is likeliest.) if (token ~= HELD_TOKEN) { i = multiple_object-->0; #Ifdef DEBUG; if (parser_trace >= 3) print " [Calling NounDomain on location and actor]^"; #Endif; ! DEBUG l = NounDomain(actors_location, actor, token); if (l == REPARSE_CODE) return l; ! Reparse after Q&A if (l ~= nothing && l ~= 1 && l notin actor && token == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) { if (ImplicitTake(l)) { etype = NOTHELD_PE; jump FailToken; } } if (indef_wanted == 100 && l == 0 && number_matched == 0) l = 1; ! ReviseMulti if TAKE ALL FROM empty container if (token_allows_multiple && ~~multiflag) { if (best_etype==MULTI_PE) best_etype=STUCK_PE; multiflag = true; } if (l == 0) { if (indef_possambig) { saved_ml = match_length; ResetDescriptors(); wn = desc_wn; jump TryAgain2; } if (etype ~=TOOFEW_PE && (multiflag || etype ~= MULTI_PE)) etype = CantSee(); jump FailToken; } ! Choose best error #Ifdef DEBUG; if (parser_trace >= 3) { if (l > 1) print " [NounDomain returned ", (the) l, "]^"; else { print " [NounDomain appended to the multiple object list:^"; k = multiple_object-->0; for (j=i+1 : j<=k : j++) print " Entry ", j, ": ", (The) multiple_object-->j, " (", multiple_object-->j, ")^"; print " List now has size ", k, "]^"; } } #Endif; ! DEBUG if (l == 1) { if (~~many_flag) many_flag = true; else { ! Merge with earlier ones k = multiple_object-->0; ! (with either parity) multiple_object-->0 = i; for (j=i+1 : j<=k : j++) { if (and_parity) MultiAdd(multiple_object-->j); else MultiSub(multiple_object-->j); } #Ifdef DEBUG; if (parser_trace >= 3) print " [Merging ", k-i, " new objects to the ", i, " old ones]^"; #Endif; ! DEBUG } } else { ! A single object was indeed found if (match_length == 0 && indef_possambig) { ! So the answer had to be inferred from no textual data, ! and we know that there was an ambiguity in the descriptor ! stage (such as a word which could be a pronoun being ! parsed as an article or possessive). It's worth having ! another go. ResetDescriptors(); wn = desc_wn; jump TryAgain2; } if (token == CREATURE_TOKEN && CreatureTest(l) == 0) { etype = ANIMA_PE; jump FailToken; } ! Animation is required if (~~many_flag) single_object = l; else { if (and_parity) MultiAdd(l); else MultiSub(l); #Ifdef DEBUG; if (parser_trace >= 3) print " [Combining ", (the) l, " with list]^"; #Endif; ! DEBUG } } } else { ! Case 2: token is "held" (which fortunately can't take multiple objects) ! and may generate an implicit take l = NounDomain(actor,actors_location,token); ! Same as above... if (l == REPARSE_CODE) return GPR_REPARSE; if (l == 0) { if (indef_possambig) { ResetDescriptors(); wn = desc_wn; jump TryAgain2; } etype = CantSee(); jump FailToken; ! Choose best error } ! ...until it produces something not held by the actor. Then an implicit ! take must be tried. If this is already happening anyway, things are too ! confused and we have to give up (but saving the oops marker so as to get ! it on the right word afterwards). ! The point of this last rule is that a sequence like ! ! > read newspaper ! (taking the newspaper first) ! The dwarf unexpectedly prevents you from taking the newspaper! ! ! should not be allowed to go into an infinite repeat - read becomes ! take then read, but take has no effect, so read becomes take then read... ! Anyway for now all we do is record the number of the object to take. o = parent(l); if (o ~= actor) { if (notheld_mode == 1) { saved_oops = oops_from; etype = NOTHELD_PE; jump FailToken; } not_holding = l; #Ifdef DEBUG; if (parser_trace >= 3) print " [Allowing object ", (the) l, " for now]^"; #Endif; ! DEBUG } single_object = l; } ! end of if (token ~= HELD_TOKEN) else ! The following moves the word marker to just past the named object... wn = oops_from + match_length; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! E: Parse connectives ("and", "but", etc.) and go back to (C) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Object(s) specified now: is that the end of the list, or have we reached ! "and", "but" and so on? If so, create a multiple-object list if we ! haven't already (and are allowed to). .NextInList; o = NextWord(); if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) { #Ifdef DEBUG; if (parser_trace >= 3) print " [Read connective '", (address) o, "']^"; #Endif; ! DEBUG k = NextWord(); if (k ~= AND1__WD) wn--; ! allow Serial commas in input if (k > 0 && (k->#dict_par1) & (DICT_NOUN+DICT_VERB) == DICT_VERB) { wn--; ! player meant 'THEN' jump PassToken; } if (~~token_allows_multiple) { if (multiflag) jump PassToken; ! give UPTO_PE error etype=MULTI_PE; jump FailToken; } if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity; if (~~many_flag) { multiple_object-->0 = 1; multiple_object-->1 = single_object; many_flag = true; #Ifdef DEBUG; if (parser_trace >= 3) print " [Making new list from ", (the) single_object, "]^"; #Endif; ! DEBUG } dont_infer = true; inferfrom=0; ! Don't print (inferences) jump ObjectList; ! And back around } wn--; ! Word marker back to first not-understood word !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! F: Return the conclusion of parsing an object list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Happy or unhappy endings: .PassToken; if (many_flag) { single_object = GPR_MULTIPLE; multi_context = token; } else { if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) { if (indef_wanted < 100 && indef_wanted > 1) { multi_had = 1; multi_wanted = indef_wanted; etype = TOOFEW_PE; jump FailToken; } } } return single_object; .FailToken; ! If we were only guessing about it being a plural, try again but only ! allowing singulars (so that words like "six" are not swallowed up as ! Descriptors) if (allow_plurals && indef_guess_p == 1) { #Ifdef DEBUG; if (parser_trace >= 4) print " [Retrying singulars after failure ", etype, "]^"; #Endif; prev_indef_wanted = indef_wanted; allow_plurals = false; wn = desc_wn; jump TryAgain; } if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE; return GPR_FAIL; ]; ! end of ParseToken__ ! ---------------------------------------------------------------------------- ! NounDomain does the most substantial part of parsing an object name. ! ! It is given two "domains" - usually a location and then the actor who is ! looking - and a context (i.e. token type), and returns: ! ! 0 if no match at all could be made, ! 1 if a multiple object was made, ! k if object k was the one decided upon, ! REPARSE_CODE if it asked a question of the player and consequently rewrote ! the player's input, so that the whole parser should start again ! on the rewritten input. ! ! In the case when it returns 1= 4) { print " [NounDomain called at word ", wn, "]^"; print " "; if (indef_mode) { print "seeking indefinite object: "; if (indef_type & OTHER_BIT) print "other "; if (indef_type & MY_BIT) print "my "; if (indef_type & THAT_BIT) print "that "; if (indef_type & PLURAL_BIT) print "plural "; if (indef_type & LIT_BIT) print "lit "; if (indef_type & UNLIT_BIT) print "unlit "; if (indef_owner ~= 0) print "owner:", (name) indef_owner; new_line; print " number wanted: "; if (indef_wanted == 100) print "all"; else print indef_wanted; new_line; print " most likely GNAs of names: ", indef_cases, "^"; } else print "seeking definite object^"; } #Endif; ! DEBUG match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0; SearchScope(domain1, domain2, context); #Ifdef DEBUG; if (parser_trace >= 4) print " [NounDomain made ", number_matched, " matches]^"; #Endif; ! DEBUG wn = match_from+match_length; ! If nothing worked at all, leave with the word marker skipped past the ! first unmatched word... if (number_matched == 0) { wn++; rfalse; } ! Suppose that there really were some words being parsed (i.e., we did ! not just infer). If so, and if there was only one match, it must be ! right and we return it... if (match_from <= num_words) { if (number_matched == 1) { i=match_list-->0; if (indef_mode) { if ((indef_type & LIT_BIT) && i hasnt light) rfalse; if ((indef_type & UNLIT_BIT) && i has light) rfalse; } return i; } ! ...now suppose that there was more typing to come, i.e. suppose that ! the user entered something beyond this noun. If nothing ought to follow, ! then there must be a mistake, (unless what does follow is just a full ! stop, and or comma) if (wn <= num_words) { i = NextWord(); wn--; if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word or THEN1__WD or THEN2__WD or THEN3__WD or BUT1__WD or BUT2__WD or BUT3__WD) { if (lookahead == ENDIT_TOKEN) rfalse; } } } ! Now look for a good choice, if there's more than one choice... number_of_classes = 0; if (match_length == 0 && indef_mode && indef_wanted ~= 100) number_matched = 0; ! ask question for 'take three' if (number_matched == 1) i = match_list-->0; if (number_matched > 1) { i = Adjudicate(context); if (i == -1) rfalse; if (i == 1) rtrue; ! Adjudicate has made a multiple ! object, and we pass it on } ! If i is non-zero here, one of two things is happening: either ! (a) an inference has been successfully made that object i is ! the intended one from the user's specification, or ! (b) the user finished typing some time ago, but we've decided ! on i because it's the only possible choice. ! In either case we have to keep the pattern up to date, ! note that an inference has been made and return. ! (Except, we don't note which of a pile of identical objects.) if (i ~= 0) { if (dont_infer) return i; if (inferfrom == 0) inferfrom=pcount; pattern-->pcount = i; return i; } ! If we get here, there was no obvious choice of object to make. If in ! fact we've already gone past the end of the player's typing (which ! means the match list must contain every object in scope, regardless ! of its name), then it's foolish to give an enormous list to choose ! from - instead we go and ask a more suitable question... if (match_from > num_words) jump Incomplete; return AskPlayer(context); ! Now we come to the question asked when the input has run out ! and can't easily be guessed (eg, the player typed "take" and there ! were plenty of things which might have been meant). .Incomplete; if (best_etype == NOTHING_PE && pattern-->1 == 0) rfalse; ! for DROP when empty-handed if (context == CREATURE_TOKEN) L__M(##Miscellany, 48, actor); else L__M(##Miscellany, 49, actor); #Ifdef TARGET_ZCODE; for (i=2 : ii = ' '; #Endif; ! TARGET_ZCODE answer_words = Keyboard(buffer2, parse2); first_word = WordValue(1, parse2); #Ifdef LanguageIsVerb; if (first_word == 0) { j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j; } #Endif; ! LanguageIsVerb ! Once again, if the reply looks like a command, give it to the ! parser to get on with and forget about the question... ! Once again, if the reply looks like a command ! (that is, VERB ... or XXX,VERB ...), give it to the parser to get ! on with and forget about the question... if (first_word) { if ((first_word->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } if (NumberWords(parse2) > 2) { j = WordValue(2, parse2); k = WordValue(3, parse2); if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } } ! ...but if we have a genuine answer, then: ! ! (1) we must glue in text suitable for anything that's been inferred. if (inferfrom ~= 0) { for (j=inferfrom : jj == PATTERN_NULL) continue; i = WORDSIZE + GetKeyBufLength(); SetKeyBufLength(i-WORDSIZE + 1); buffer->(i++) = ' '; #Ifdef DEBUG; if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^"; #Endif; ! DEBUG ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX. parse2-->1 = 0; ! An inferred object. Best we can do is glue in a pronoun. ! (This is imperfect, but it's very seldom needed anyway.) if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) { ! was the inference made from some noun words? ! In which case, we can infer again. if ((WordValue(NumberWords())->#dict_par1) & DICT_NOUN) continue; PronounNotice(pattern-->j); for (k=1 : k<=LanguagePronouns-->0 : k=k+3) if (pattern-->j == LanguagePronouns-->(k+2)) { parse2-->1 = LanguagePronouns-->k; #Ifdef DEBUG; if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^"; #Endif; ! DEBUG break; } } else { ! An inferred preposition. parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE); #Ifdef DEBUG; if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^"; #Endif; ! DEBUG } ! parse2-->1 now holds the dictionary address of the word to glue in. if (parse2-->1 ~= 0) { k = buffer + i; #Ifdef TARGET_ZCODE; @output_stream 3 k; print (address) parse2-->1; @output_stream -3; k = k-->0; for (l=i : ll = buffer->(l+2); #Ifnot; ! TARGET_GLULX k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1); l=l; ! suppress compiler warning #Endif; ! TARGET_ i = i + k; SetKeyBufLength(i-WORDSIZE); } } } ! (2) we must glue the newly-typed text onto the end. i = WORDSIZE + GetKeyBufLength(); buffer->(i++) = ' '; SetKeyBufLength(GetKeyBufLength()+1); for (j=0 : ji = buffer2->(j+WORDSIZE); SetKeyBufLength(GetKeyBufLength()+1); if (i-WORDSIZE == INPUT_BUFFER_LEN-1) break; } ! (3) we fill up the buffer with spaces, which is unnecessary, but may ! help incorrectly-written interpreters to cope. #Ifdef TARGET_ZCODE; for (: ii = ' '; #Endif; ! TARGET_ZCODE return REPARSE_CODE; ]; ! end of NounDomain [ AskPlayer context i j k l first_word answer_words marker; ! Now we print up the question, using the equivalence classes as worked ! out by Adjudicate() so as not to repeat ourselves on plural objects... asking_player = true; if (context == CREATURE_TOKEN) L__M(##Miscellany, 45); else L__M(##Miscellany, 46); j = number_of_classes; marker = 0; for (i=1 : i<=number_of_classes : i++) { while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++; k = match_list-->marker; if (match_classes-->marker > 0) print (the) k; else print (a) k; if (i < j-1) print (string) COMMA__TX; if (i == j-1) print (SerialComma) j, (string) OR__TX; } L__M(##Miscellany, 57); ! ...and get an answer: .WhichOne; #Ifdef TARGET_ZCODE; for (i=WORDSIZE : ii = ' '; #Endif; ! TARGET_ZCODE answer_words = Keyboard(buffer2, parse2); first_word = WordValue(1, parse2); asking_player = false; ! Take care of "all", because that does something too clever here to do ! later on: if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { l = multiple_object-->0; for (i=0 : ii; multiple_object-->(i+1+l) = k; } multiple_object-->0 = i+l; rtrue; } L__M(##Miscellany, 47); jump WhichOne; } ! If the first word of the reply can be interpreted as a verb, then ! assume that the player has ignored the question and given a new ! command altogether. ! (This is one time when it's convenient that the directions are ! not themselves verbs - thus, "north" as a reply to "Which, the north ! or south door" is not treated as a fresh command but as an answer.) #Ifdef LanguageIsVerb; if (first_word == 0) { j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j; } #Endif; ! LanguageIsVerb if (first_word) { if (((first_word->#dict_par1) & DICT_VERB) && ~~LanguageVerbMayBeName(first_word)) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } if (NumberWords(parse2) > 2) { j = WordValue(2, parse2); k = WordValue(3, parse2); if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } } ! Now we insert the answer into the original typed command, as ! words additionally describing the same object ! (eg, > take red button ! Which one, ... ! > music ! becomes "take music red button". The parser will thus have three ! words to work from next time, not two.) k = WordAddress(match_from) - buffer; l = GetKeyBufLength(buffer2) +1; for (j=buffer + INPUT_BUFFER_LEN - 1 : j>=buffer+k+l : j--) j->0 = j->(-l); for (i=0 : i(k+i) = buffer2->(WORDSIZE+i); buffer->(k+l-1) = ' '; SetKeyBufLength(GetKeyBufLength() + l); ! Having reconstructed the input, we warn the parser accordingly ! and get out. return REPARSE_CODE; ]; ! ---------------------------------------------------------------------------- ! The Adjudicate routine tries to see if there is an obvious choice, when ! faced with a list of objects (the match_list) each of which matches the ! player's specification equally well. ! ! To do this it makes use of the context (the token type being worked on). ! It counts up the number of obvious choices for the given context ! (all to do with where a candidate is, except for 6 (animate) which is to ! do with whether it is animate or not); ! ! if only one obvious choice is found, that is returned; ! ! if we are in indefinite mode (don't care which) one of the obvious choices ! is returned, or if there is no obvious choice then an unobvious one is ! made; ! ! at this stage, we work out whether the objects are distinguishable from ! each other or not: if they are all indistinguishable from each other, ! then choose one, it doesn't matter which; ! ! otherwise, 0 (meaning, unable to decide) is returned (but remember that ! the equivalence classes we've just worked out will be needed by other ! routines to clear up this mess, so we can't economise on working them ! out). ! ! Returns -1 if an error occurred ! ---------------------------------------------------------------------------- Constant SCORE__CHOOSEOBJ = 1000; Constant SCORE__IFGOOD = 500; Constant SCORE__UNCONCEALED = 100; Constant SCORE__BESTLOC = 60; Constant SCORE__NEXTBESTLOC = 40; Constant SCORE__NOTCOMPASS = 20; Constant SCORE__NOTSCENERY = 10; Constant SCORE__NOTACTOR = 5; Constant SCORE__GNA = 1; Constant SCORE__DIVISOR = 20; [ Adjudicate context i j k good_flag good_ones last n flag offset sovert; #Ifdef DEBUG; if (parser_trace >= 4) { print " [Adjudicating match list of size ", number_matched, " in context ", context, "]^"; print " "; if (indef_mode) { print "indefinite type: "; if (indef_type & OTHER_BIT) print "other "; if (indef_type & MY_BIT) print "my "; if (indef_type & THAT_BIT) print "that "; if (indef_type & PLURAL_BIT) print "plural "; if (indef_type & LIT_BIT) print "lit "; if (indef_type & UNLIT_BIT) print "unlit "; if (indef_owner ~= 0) print "owner:", (name) indef_owner; new_line; print " number wanted: "; if (indef_wanted == 100) print "all"; else print indef_wanted; new_line; print " most likely GNAs of names: ", indef_cases, "^"; } else print "definite object^"; } #Endif; ! DEBUG j = number_matched-1; good_ones = 0; last = match_list-->0; for (i=0 : i<=j : i++) { n = match_list-->i; match_scores-->i = 0; good_flag = false; switch (context) { HELD_TOKEN, MULTIHELD_TOKEN: if (parent(n) == actor) good_flag = true; MULTIEXCEPT_TOKEN: if (advance_warning == -1) { good_flag = true; } else { if (n ~= advance_warning) good_flag = true; } MULTIINSIDE_TOKEN: if (advance_warning == -1) { if (parent(n) ~= actor) good_flag = true; } else { if (n in advance_warning) good_flag = true; } CREATURE_TOKEN: if (CreatureTest(n) == 1) good_flag = true; default: good_flag = true; } if (good_flag) { match_scores-->i = SCORE__IFGOOD; good_ones++; last = n; } } if (good_ones == 1) return last; ! If there is ambiguity about what was typed, but it definitely wasn't ! animate as required, then return anything; higher up in the parser ! a suitable error will be given. (This prevents a question being asked.) if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0; if (indef_mode == 0) indef_type=0; ScoreMatchL(context); if (number_matched == 0) return -1; if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) { if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { etype = MULTI_PE; return -1; } i = 0; offset = multiple_object-->0; sovert = -1; for (j=BestGuess() : j~=-1 && i(i+offset) = j; #Ifdef DEBUG; if (parser_trace >= 4) print " Accepting it^"; #Endif; ! DEBUG } else { i = i; #Ifdef DEBUG; if (parser_trace >= 4) print " Rejecting it^"; #Endif; ! DEBUG } } if (i < indef_wanted && indef_wanted < 100) { etype = TOOFEW_PE; multi_wanted = indef_wanted; multi_had=i; return -1; } multiple_object-->0 = i+offset; multi_context = context; #Ifdef DEBUG; if (parser_trace >= 4) print " Made multiple object of size ", i, "]^"; #Endif; ! DEBUG return 1; } for (i=0 : ii = 0; n = 1; for (i=0 : ii == 0) { match_classes-->i = n++; flag = 0; for (j=i+1 : jj == 0 && Identical(match_list-->i, match_list-->j) == 1) { flag=1; match_classes-->j = match_classes-->i; } if (flag == 1) match_classes-->i = 1-n; } n--; number_of_classes = n; #Ifdef DEBUG; if (parser_trace >= 4) { print " Grouped into ", n, " possibilities by name:^"; for (i=0 : ii > 0) print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ", match_classes-->i, "^"; } #Endif; ! DEBUG if (n == 1) dont_infer = true; if (indef_mode == 0) { ! Is there now a single highest-scoring object? i = SingleBestGuess(); if (i >= 0) { #Ifdef DEBUG; if (parser_trace >= 4) print " Single best-scoring object returned.]^"; #Endif; ! DEBUG return i; } } if (indef_mode == 0) { if (n > 1) { k = -1; for (i=0 : ii > k) { k = match_scores-->i; j = match_classes-->i; j = j*j; flag = 0; } else if (match_scores-->i == k) { if ((match_classes-->i) * (match_classes-->i) ~= j) flag = 1; } } if (flag) { #Ifdef DEBUG; if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^"; #Endif; ! DEBUG return 0; } #Ifdef DEBUG; if (parser_trace >= 4) print " Best choices are all from the same group.^"; #Endif; ! DEBUG } } ! When the player is really vague, or there's a single collection of ! indistinguishable objects to choose from, choose the one the player ! most recently acquired, or if the player has none of them, then ! the one most recently put where it is. return BestGuess(); ]; ! Adjudicate ! ---------------------------------------------------------------------------- ! ReviseMulti revises the multiple object which already exists, in the ! light of information which has come along since then (i.e., the second ! parameter). It returns a parser error number, or else 0 if all is well. ! This only ever throws things out, never adds new ones. ! ---------------------------------------------------------------------------- [ ReviseMulti second_p i low; #Ifdef DEBUG; if (parser_trace >= 4) print " Revising multiple object list of size ", multiple_object-->0, " with 2nd ", (name) second_p, "^"; #Endif; ! DEBUG if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { for (i=1,low=0 : i<=multiple_object-->0 : i++) { if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) || (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) { low++; multiple_object-->low = multiple_object-->i; } } multiple_object-->0 = low; } if (multi_context == MULTI_TOKEN && action_to_be == ##Take) { for (i=1,low=0 : i<=multiple_object-->0 : i++) if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++; #Ifdef DEBUG; if (parser_trace >= 4) print " Token 2 plural case: number with actor ", low, "^"; #Endif; ! DEBUG if (take_all_rule == 2 || low > 0) { for (i=1,low=0 : i<=multiple_object-->0 : i++) { if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) { low++; multiple_object-->low = multiple_object-->i; } } multiple_object-->0 = low; } } i = multiple_object-->0; #Ifdef DEBUG; if (parser_trace >= 4) print " Done: new size ", i, "^"; #Endif; ! DEBUG if (i == 0) return NOTHING_PE; return 0; ]; ! ---------------------------------------------------------------------------- ! ScoreMatchL scores the match list for quality in terms of what the ! player has vaguely asked for. Points are awarded for conforming with ! requirements like "my", and so on. Remove from the match list any ! entries which fail the basic requirements of the descriptors. ! ---------------------------------------------------------------------------- [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s; ! if (indef_type & OTHER_BIT ~= 0) threshold++; if (indef_type & MY_BIT ~= 0) threshold++; if (indef_type & THAT_BIT ~= 0) threshold++; if (indef_type & LIT_BIT ~= 0) threshold++; if (indef_type & UNLIT_BIT ~= 0) threshold++; if (indef_owner ~= nothing) threshold++; #Ifdef DEBUG; if (parser_trace >= 4) print " Scoring match list: indef mode ", indef_mode, " type ", indef_type, ", satisfying ", threshold, " requirements:^"; #Endif; ! DEBUG if (action_to_be ~= ##Take) a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC; if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) { a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC; } for (i=0 : ii; its_owner = parent(obj); its_score=0; met=0; ! if (indef_type & OTHER_BIT ~= 0 ! && obj ~= itobj or himobj or herobj) met++; if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++; if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++; if (indef_type & LIT_BIT ~= 0 && obj has light) met++; if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++; if (indef_owner ~= 0 && its_owner == indef_owner) met++; if (met < threshold) { #Ifdef DEBUG; if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ", (the) its_owner, " is rejected (doesn't match descriptors)^"; #Endif; ! DEBUG match_list-->i = -1; } else { its_score = 0; if (obj hasnt concealed) its_score = SCORE__UNCONCEALED; if (its_owner == actor) its_score = its_score + a_s; else if (its_owner == actors_location) its_score = its_score + l_s; else { #Ifdef TRADITIONAL_TAKE_ALL; if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS; #Ifnot; if (its_owner ~= compass) if (take_all_rule && its_owner && its_owner has static or scenery && (its_owner has supporter || (its_owner has container && its_owner has open))) its_score = its_score + l_s; else its_score = its_score + SCORE__NOTCOMPASS; #Endif; ! TRADITIONAL_TAKE_ALL } j = ChooseObjects(obj, 2); if (j == 0) j = LibraryExtensions.RunAll(ext_chooseobjects, obj, 2); its_score = its_score + SCORE__CHOOSEOBJ * j; if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY; if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR; ! A small bonus for having the correct GNA, ! for sorting out ambiguous articles and the like. if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj)))) its_score = its_score + SCORE__GNA; match_scores-->i = match_scores-->i + its_score; #Ifdef DEBUG; if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ", (the) its_owner, " : ", match_scores-->i, " points^"; #Endif; ! DEBUG } } for (i=0 : ii == -1) { if (i == number_matched-1) { number_matched--; break; } for (j=i : jj = match_list-->(j+1); match_scores-->j = match_scores-->(j+1); } number_matched--; } } ]; ! ---------------------------------------------------------------------------- ! BestGuess makes the best guess it can out of the match list, assuming that ! everything in the match list is textually as good as everything else; ! however it ignores items marked as -1, and so marks anything it chooses. ! It returns -1 if there are no possible choices. ! ---------------------------------------------------------------------------- [ BestGuess earliest its_score best i; earliest = 0; best = -1; for (i=0 : ii >= 0) { its_score = match_scores-->i; if (its_score > best) { best = its_score; earliest = i; } } } #Ifdef DEBUG; if (parser_trace >= 4) if (best < 0) print " Best guess ran out of choices^"; else print " Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^"; #Endif; ! DEBUG if (best < 0) return -1; i = match_list-->earliest; match_list-->earliest = -1; bestguess_score = best; return i; ]; ! ---------------------------------------------------------------------------- ! SingleBestGuess returns the highest-scoring object in the match list ! if it is the clear winner, or returns -1 if there is no clear winner ! ---------------------------------------------------------------------------- [ SingleBestGuess earliest its_score best i; earliest = -1; best = -1000; for (i=0 : ii; if (its_score == best) earliest = -1; if (its_score > best) { best = its_score; earliest = match_list-->i; } } bestguess_score = best; return earliest; ]; ! ---------------------------------------------------------------------------- ! Identical decides whether or not two objects can be distinguished from ! each other by anything the player can type. If not, it returns true. ! ---------------------------------------------------------------------------- [ Identical o1 o2 p1 p2 n1 n2 i j flag; if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side if (o1 == 0 || o2 == 0) rfalse; ! Similarly if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time ! What complicates things is that o1 or o2 might have a parsing routine, ! so the parser can't know from here whether they are or aren't the same. ! If they have different parsing routines, we simply assume they're ! different. If they have the same routine (which they probably got from ! a class definition) then the decision process is as follows: ! ! the routine is called (with self being o1, not that it matters) ! with noun and second being set to o1 and o2, and action being set ! to the fake action TheSame. If it returns -1, they are found ! identical; if -2, different; and if >=0, then the usual method ! is used instead. if (o1.parse_name ~= 0 || o2.parse_name ~= 0) { if (o1.parse_name ~= o2.parse_name) rfalse; parser_action = ##TheSame; parser_one = o1; parser_two = o2; j = wn; i = RunRoutines(o1,parse_name); wn = j; if (i == -1) rtrue; if (i == -2) rfalse; } ! This is the default algorithm: do they have the same words in their ! "name" (i.e. property no. 1) properties. (Note that the following allows ! for repeated words and words in different orders.) p1 = o1.&1; n1 = (o1.#1)/WORDSIZE; p2 = o2.&1; n2 = (o2.#1)/WORDSIZE; ! for (i=0 : ii, " "; } new_line; ! for (i=0 : ii, " "; } new_line; for (i=0 : ii == p2-->j) flag = 1; if (flag == 0) rfalse; } for (j=0 : ji == p2-->j) flag = 1; if (flag == 0) rfalse; } ! print "Which are identical!^"; rtrue; ]; ! ---------------------------------------------------------------------------- ! PrintCommand reconstructs the command as it presently reads, from ! the pattern which has been built up ! ! If from is 0, it starts with the verb: then it goes through the pattern. ! The other parameter is "emptyf" - a flag: if 0, it goes up to pcount: ! if 1, it goes up to pcount-1. ! ! Note that verbs and prepositions are printed out of the dictionary: ! and that since the dictionary may only preserve the first six characters ! of a word (in a V3 game), we have to hand-code the longer words needed. ! ! (Recall that pattern entries are 0 for "multiple object", 1 for "special ! word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the ! preposition n) ! ---------------------------------------------------------------------------- [ PrintCommand from i k spacing_flag; #Ifdef LanguageCommand; LanguageCommand(from); i = k = spacing_flag = 0; ! suppress warning #Ifnot; if (from == 0) { i = verb_word; if (LanguageVerb(i) == 0 && PrintVerb(i) == false && LibraryExtensions.RunWhile(ext_printverb, false, i) == 0) print (address) i; from++; spacing_flag = true; } for (k=from : kk; if (i == PATTERN_NULL) continue; if (spacing_flag) print (char) ' '; if (i == 0) { print (string) THOSET__TX; jump TokenPrinted; } if (i == 1) { print (string) THAT__TX; jump TokenPrinted; } if (i >= REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE); else if (i in compass && LanguageVerbLikesAdverb(verb_word)) LanguageDirection (i.door_dir); ! the direction name as adverb else print (the) i; .TokenPrinted; spacing_flag = true; } #Endif; ! LanguageCommand ]; ! ---------------------------------------------------------------------------- ! The CantSee routine returns a good error number for the situation where ! the last word looked at didn't seem to refer to any object in context. ! ! The idea is that: if the actor is in a location (but not inside something ! like, for instance, a tank which is in that location) then an attempt to ! refer to one of the words listed as meaningful-but-irrelevant there ! will cause "you don't need to refer to that in this game" rather than ! "no such thing" or "what's 'it'?". ! (The advantage of not having looked at "irrelevant" local nouns until now ! is that it stops them from clogging up the ambiguity-resolving process. ! Thus game objects always triumph over scenery.) ! ---------------------------------------------------------------------------- [ CantSee i w e; if (scope_token ~= 0) { scope_error = scope_token; return ASKSCOPE_PE; } wn--; w = NextWord(); e = CANTSEE_PE; if (w == pronoun_word) { pronoun__word = pronoun_word; pronoun__obj = pronoun_obj; e = ITGONE_PE; } i = actor; while (parent(i) ~= 0) i = parent(i); wn--; if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE; else { Descriptors(); ! skip past THE etc if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE; } if (saved_ml) saved_oops = num_desc + match_from + saved_ml; else saved_oops = num_desc + match_from + match_length; wn++; return e; ]; ! ---------------------------------------------------------------------------- ! The MultiAdd routine adds object "o" to the multiple-object-list. ! ! This is only allowed to hold 63 objects at most, at which point it ignores ! any new entries (and sets a global flag so that a warning may later be ! printed if need be). ! ---------------------------------------------------------------------------- [ MultiAdd o i j; i = multiple_object-->0; if (i == 63) { toomany_flag = 1; rtrue; } for (j=1 : j<=i : j++) if (o == multiple_object-->j) rtrue; i++; multiple_object-->i = o; multiple_object-->0 = i; ]; ! ---------------------------------------------------------------------------- ! The MultiSub routine deletes object "o" from the multiple-object-list. ! ! It returns 0 if the object was there in the first place, and 9 (because ! this is the appropriate error number in Parser()) if it wasn't. ! ---------------------------------------------------------------------------- [ MultiSub o i j k et; i = multiple_object-->0; et = 0; for (j=1 : j<=i : j++) if (o == multiple_object-->j) { for (k=j : k<=i : k++) multiple_object-->k = multiple_object-->(k+1); multiple_object-->0 = --i; return et; } et = 9; return et; ]; ! ---------------------------------------------------------------------------- ! The MultiFilter routine goes through the multiple-object-list and throws ! out anything without the given attribute "attr" set. ! ---------------------------------------------------------------------------- [ MultiFilter attr i j o; .MFiltl; i = multiple_object-->0; for (j=1 : j<=i : j++) { o = multiple_object-->j; if (o hasnt attr) { MultiSub(o); jump Mfiltl; } } ]; ! ---------------------------------------------------------------------------- ! The UserFilter routine consults the user's filter (or checks on attribute) ! to see what already-accepted nouns are acceptable ! ---------------------------------------------------------------------------- [ UserFilter obj; if (token_filter > 0 && token_filter < 49) { if (obj has (token_filter-1)) rtrue; rfalse; } noun = obj; return token_filter(); ]; ! ---------------------------------------------------------------------------- ! MoveWord copies word at2 from parse buffer b2 to word at1 in "parse" ! (the main parse buffer) ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ MoveWord at1 b2 at2 x y; x = at1*2-1; y = at2*2-1; parse-->x++ = b2-->y++; parse-->x = b2-->y; ]; #Ifnot; ! TARGET_GLULX [ MoveWord at1 b2 at2 x y; x = at1*3-2; y = at2*3-2; parse-->x++ = b2-->y++; parse-->x++ = b2-->y++; parse-->x = b2-->y; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! SearchScope domain1 domain2 context ! ! Works out what objects are in scope (possibly asking an outside routine), ! but does not look at anything the player has typed. ! ---------------------------------------------------------------------------- [ SearchScope domain1 domain2 context i is; i = 0; ! Everything is in scope to the debugging commands #Ifdef DEBUG; if (scope_reason == PARSING_REASON && LanguageVerbIsDebugging(verb_word)) { #Ifdef TARGET_ZCODE; for (i=selfobj : i<=top_object : i++) if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object)) PlaceInScope(i); #Ifnot; ! TARGET_GLULX objectloop (i) if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object)) PlaceInScope(i); #Endif; ! TARGET_ rtrue; } #Endif; ! DEBUG ! First, a scope token gets priority here: if (scope_token ~= 0) { scope_stage = 2; if (scope_token()) rtrue; } ! Pick up everything in the location except the actor's possessions; ! then go through those. (This ensures the actor's possessions are in ! scope even in Darkness.) if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) { if (IsSeeThrough(advance_warning) == 1) ScopeWithin(advance_warning, 0, context); } else { ! Next, call any user-supplied routine adding things to the scope, ! which may circumvent the usual routines altogether ! if they return true: if (actor == domain1 or domain2) { is = InScope(actor); if (is == false) is = LibraryExtensions.RunWhile(ext_inscope, false, actor); if (is) rtrue; } if (domain1 ~= 0 && domain1 has supporter or container) ScopeWithin_O(domain1, domain1, context); ScopeWithin(domain1, domain2, context); if (domain2 ~= 0 && domain2 has supporter or container) ScopeWithin_O(domain2, domain2, context); ScopeWithin(domain2, 0, context); } ! A special rule applies: ! in Darkness as in light, the actor is always in scope to himself. if (thedark == domain1 or domain2) { ScopeWithin_O(actor, actor, context); if (parent(actor) has supporter or container) ScopeWithin_O(parent(actor), parent(actor), context); } ]; ! ---------------------------------------------------------------------------- ! IsSeeThrough is used at various places: roughly speaking, it determines ! whether o being in scope means that the contents of o are in scope. ! ---------------------------------------------------------------------------- [ IsSeeThrough o; if (o has supporter or transparent || (o has container && o has open)) rtrue; rfalse; ]; ! ---------------------------------------------------------------------------- ! PlaceInScope is provided for routines outside the library, and is not ! called within the parser (except for debugging purposes). ! ---------------------------------------------------------------------------- [ PlaceInScope thing; if (scope_reason~=PARSING_REASON or TALKING_REASON) { DoScopeAction(thing); rtrue; } wn = match_from; TryGivenObject(thing); placed_in_flag = 1; ]; ! ---------------------------------------------------------------------------- ! DoScopeAction ! ---------------------------------------------------------------------------- [ DoScopeAction thing s p1; s = scope_reason; p1 = parser_one; #Ifdef DEBUG; if (parser_trace >= 6) print "[DSA on ", (the) thing, " with reason = ", scope_reason, " p1 = ", parser_one, " p2 = ", parser_two, "]^"; #Endif; ! DEBUG switch (scope_reason) { REACT_BEFORE_REASON: if (thing.react_before == 0 or NULL) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering react_before for ", (the) thing, "]^"; #Endif; ! DEBUG if (parser_one == 0) parser_one = RunRoutines(thing, react_before); REACT_AFTER_REASON: if (thing.react_after == 0 or NULL) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering react_after for ", (the) thing, "]^"; #Endif; ! DEBUG if (parser_one == 0) parser_one = RunRoutines(thing, react_after); EACH_TURN_REASON: if (thing.each_turn == 0) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering each_turn for ", (the) thing, "]^"; #Endif; ! DEBUG PrintOrRun(thing, each_turn); TESTSCOPE_REASON: if (thing == parser_one) parser_two = 1; LOOPOVERSCOPE_REASON: parser_one(thing); parser_one=p1; } scope_reason = s; ]; ! ---------------------------------------------------------------------------- ! ScopeWithin looks for objects in the domain which make textual sense ! and puts them in the match list. (However, it does not recurse through ! the second argument.) ! ---------------------------------------------------------------------------- [ ScopeWithin domain nosearch context x y; if (domain == 0) rtrue; ! Special rule: the directions (interpreted as the 12 walls of a room) are ! always in context. (So, e.g., "examine north wall" is always legal.) ! (Unless we're parsing something like "all", because it would just slow ! things down then, or unless the context is "creature".) if (indef_mode==0 && domain==actors_location && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN) ScopeWithin(compass); ! Look through the objects in the domain, avoiding "objectloop" in case ! movements occur, e.g. when trying each_turn. x = child(domain); while (x ~= 0) { y = sibling(x); ScopeWithin_O(x, nosearch, context); x = y; } ]; [ ScopeWithin_O domain nosearch context i ad n; ! If the scope reason is unusual, don't parse. if (scope_reason ~= PARSING_REASON or TALKING_REASON) { DoScopeAction(domain); jump DontAccept; } ! "it" or "them" matches to the it-object only. (Note that (1) this means ! that "it" will only be understood if the object in question is still ! in context, and (2) only one match can ever be made in this case.) if (match_from <= num_words) { ! If there's any text to match, that is wn = match_from; i = NounWord(); if (i == 1 && player == domain) MakeMatch(domain, 1); if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1); } ! Construing the current word as the start of a noun, can it refer to the ! object? wn = match_from; if (TryGivenObject(domain) > 0) if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) { ! This case arises if the player has typed a number in ! which is hypothetically an indefinite descriptor: ! e.g. "take two clubs". We have just checked the object ! against the word "clubs", in the hope of eventually finding ! two such objects. But we also backtrack and check it ! against the words "two clubs", in case it turns out to ! be the 2 of Clubs from a pack of cards, say. If it does ! match against "two clubs", we tear up our original ! assumption about the meaning of "two" and lapse back into ! definite mode. wn = indef_nspec_at; if (TryGivenObject(domain) > 0) { match_from = indef_nspec_at; ResetDescriptors(); } wn = match_from; } .DontAccept; ! Shall we consider the possessions of the current object, as well? ! Only if it's a container (so, for instance, if a dwarf carries a ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword" ! will). ! Also, only if there are such possessions. ! ! Notice that the parser can see "into" anything flagged as ! transparent - such as a dwarf whose sword you can get at. if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1) ScopeWithin(domain,nosearch,context); ! Drag any extras into context ad = domain.&add_to_scope; if (ad ~= 0) { ! Test if the property value is not an object. #Ifdef TARGET_ZCODE; i = (UnsignedCompare(ad-->0, top_object) > 0); #Ifnot; ! TARGET_GLULX i = (((ad-->0)->0) ~= $70); #Endif; ! TARGET_ if (i) { ats_flag = 2+context; RunRoutines(domain, add_to_scope); ats_flag = 0; } else { n = domain.#add_to_scope; for (i=0 : (WORDSIZE*i)i) ScopeWithin_O(ad-->i, 0, context); } } ]; [ AddToScope obj; if (ats_flag >= 2) ScopeWithin_O(obj, 0, ats_flag-2); if (ats_flag == 1) { if (HasLightSource(obj)==1) ats_hls = 1; } ]; ! ---------------------------------------------------------------------------- ! MakeMatch looks at how good a match is. If it's the best so far, then ! wipe out all the previous matches and start a new list with this one. ! If it's only as good as the best so far, add it to the list. ! If it's worse, ignore it altogether. ! ! The idea is that "red panic button" is better than "red button" or "panic". ! ! number_matched (the number of words matched) is set to the current level ! of quality. ! ! We never match anything twice, and keep at most 64 equally good items. ! ---------------------------------------------------------------------------- [ MakeMatch obj quality i; #Ifdef DEBUG; if (parser_trace >= 6) print " Match with quality ",quality,"^"; #Endif; ! DEBUG if (token_filter ~= 0 && UserFilter(obj) == 0) { #Ifdef DEBUG; if (parser_trace >= 6) print " Match filtered out: token filter ", token_filter, "^"; #Endif; ! DEBUG rtrue; } if (quality < match_length) rtrue; if (quality > match_length) { match_length = quality; number_matched = 0; } else { if (number_matched >= MATCH_LIST_SIZE) rtrue; for (i=0 : ii == obj) rtrue; } match_list-->number_matched++ = obj; #Ifdef DEBUG; if (parser_trace >= 6) print " Match added to list^"; #Endif; ! DEBUG ]; ! ---------------------------------------------------------------------------- ! TryGivenObject tries to match as many words as possible in what has been ! typed to the given object, obj. If it manages any words matched at all, ! it calls MakeMatch to say so, then returns the number of words (or 1 ! if it was a match because of inadequate input). ! ---------------------------------------------------------------------------- [ TryGivenObject obj threshold k w j; #Ifdef DEBUG; if (parser_trace >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^"; #Endif; ! DEBUG dict_flags_of_noun = 0; ! If input has run out then always match, with only quality 0 (this saves ! time). if (wn > num_words) { if (indef_mode ~= 0) dict_flags_of_noun = DICT_X654; ! Reject "plural" bit MakeMatch(obj,0); #Ifdef DEBUG; if (parser_trace >= 5) print " Matched (0)^"; #Endif; ! DEBUG return 1; } ! Ask the object to parse itself if necessary, sitting up and taking notice ! if it says the plural was used: if (obj.parse_name~=0) { parser_action = NULL; j=wn; k = RunRoutines(obj,parse_name); if (k > 0) { wn=j+k; .MMbyPN; if (parser_action == ##PluralFound) dict_flags_of_noun = dict_flags_of_noun | DICT_PLUR; if (dict_flags_of_noun & DICT_PLUR) { if (~~allow_plurals) k = 0; else { if (indef_mode == 0) { indef_mode = 1; indef_type = 0; indef_wanted = 0; } indef_type = indef_type | PLURAL_BIT; if (indef_wanted == 0) indef_wanted = 100; } } #Ifdef DEBUG; if (parser_trace >= 5) print " Matched (", k, ")^"; #Endif; ! DEBUG MakeMatch(obj,k); return k; } if (k == 0) jump NoWordsMatch; wn = j; } ! The default algorithm is simply to count up how many words pass the ! Refers test: parser_action = NULL; w = NounWord(); if (w == 1 && player == obj) { k=1; jump MMbyPN; } if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; } j = --wn; threshold = ParseNoun(obj); if (threshold == -1) { LibraryExtensions.ext_number_1 = wn; ! Set the "between calls" functionality to LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN; threshold = LibraryExtensions.RunWhile(ext_parsenoun, -1, obj); LibraryExtensions.BetweenCalls = 0; ! Turn off the "between calls" functionality } #Ifdef DEBUG; if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^"; #Endif; ! DEBUG if (threshold < 0) wn++; if (threshold > 0) { k = threshold; jump MMbyPN; } if (threshold == 0 || Refers(obj,wn-1) == 0) { .NoWordsMatch; if (indef_mode ~= 0) { k = 0; parser_action = NULL; jump MMbyPN; } rfalse; } if (threshold < 0) { threshold = 1; dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100; w = NextWord(); while (Refers(obj, wn-1)) { threshold++; if (w) dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR)); w = NextWord(); } } k = threshold; jump MMbyPN; ]; ! ---------------------------------------------------------------------------- ! Refers works out whether the word at number wnum can refer to the object ! obj, returning true or false. The standard method is to see if the ! word is listed under "name" for the object, but this is more complex ! in languages other than English. ! ---------------------------------------------------------------------------- [ Refers obj wnum wd k l m; if (obj == 0 || wnum <= 0) rfalse; #Ifdef LanguageRefers; k = LanguageRefers(obj,wnum); if (k >= 0) return k; #Endif; ! LanguageRefers k = wn; wn = wnum; wd = NextWordStopped(); wn = k; if (parser_inflection_func) { k = parser_inflection(obj, wd); if (k >= 0) return k; m = -k; } else m = parser_inflection; k = obj.&m; l = (obj.#m)/WORDSIZE-1; for (m=0 : m<=l : m++) if (wd == k-->m) rtrue; rfalse; ]; [ WordInProperty wd obj prop k l m; k = obj.∝ l = (obj.#prop)/WORDSIZE-1; for (m=0 : m<=l : m++) if (wd == k-->m) rtrue; rfalse; ]; [ DictionaryLookup b l i; for (i=0 : i(WORDSIZE+i) = b->i; SetKeyBufLength(l, buffer2); Tokenise__(buffer2, parse2); return parse2-->1; ]; ! ---------------------------------------------------------------------------- ! NounWord (which takes no arguments) returns: ! ! 0 if the next word is unrecognised or does not carry the "noun" bit in ! its dictionary entry, ! 1 if a word meaning "me", ! the index in the pronoun table (plus 2) of the value field of a pronoun, ! if the word is a pronoun, ! the address in the dictionary if it is a recognised noun. ! ! The "current word" marker moves on one. ! ---------------------------------------------------------------------------- [ NounWord i j s; i = NextWord(); if (i == 0) rfalse; if (i == ME1__WD or ME2__WD or ME3__WD) return 1; s = LanguagePronouns-->0; for (j=1 : j<=s : j=j+3) if (i == LanguagePronouns-->j) return j+2; if ((i->#dict_par1) & DICT_NOUN == 0) rfalse; return i; ]; ! ---------------------------------------------------------------------------- ! NextWord (which takes no arguments) returns: ! ! 0 if the next word is unrecognised, ! comma_word if a comma ! THEN1__WD if a full stop ! or the dictionary address if it is recognised. ! The "current word" marker is moved on. ! ! NextWordStopped does the same, but returns -1 when input has run out ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ NextWord i j; if (wn <= 0 || wn > parse->1) { wn++; rfalse; } i = wn*2-1; wn++; j = parse-->i; if (j == ',//') j = comma_word; if (j == './/') j = THEN1__WD; return j; ]; [ NextWordStopped; if (wn > parse->1) { wn++; return -1; } return NextWord(); ]; [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer if (p==0) p=parse; if (b==0) b=buffer; return b + p->(wordnum*4+1); ]; [ WordLength wordnum p; ! Length of 'wordnum' string in buffer if (p==0) p=parse; return p->(wordnum*4); ]; [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*2-1); ]; [ NumberWords p; ! Number of parsed strings in buffer if (p==0) p=parse; return p->1; ]; [ GetKeyBufLength b; ! Number of typed chars in buffer if (b==0) b=buffer; return b->1; ]; [ SetKeyBufLength n b; ! Update number of typed chars in buffer if (b==0) b=buffer; if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE; b->1 = n; ]; #Ifnot; ! TARGET_GLULX [ NextWord i j; if (wn <= 0 || wn > parse-->0) { wn++; rfalse; } i = wn*3-2; wn++; j = parse-->i; if (j == ',//') j=comma_word; if (j == './/') j=THEN1__WD; return j; ]; [ NextWordStopped; if (wn > parse-->0) { wn++; return -1; } return NextWord(); ]; [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer if (p==0) p=parse; if (b==0) b=buffer; return b + p-->(wordnum*3); ]; [ WordLength wordnum p; ! Length of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*3-1); ]; [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*3-2); ]; [ NumberWords p; ! Number of parsed strings in buffer if (p==0) p=parse; return p-->0; ]; [ GetKeyBufLength b; ! Number of typed chars in buffer if (b==0) b=buffer; return b-->0; ]; [ SetKeyBufLength n b; ! Update number of typed chars in buffer if (b==0) b=buffer; if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE; b-->0 = n; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! TryNumber is the only routine which really does any character-level ! parsing, since that's normally left to the Z-machine. ! It takes word number "wordnum" and tries to parse it as an (unsigned) ! decimal number, returning ! ! -1000 if it is not a number ! the number if it has between 1 and 4 digits ! 10000 if it has 5 or more digits. ! ! (The danger of allowing 5 digits is that Z-machine integers are only ! 16 bits long, and anyway this isn't meant to be perfect.) ! ! Using NumberWord, it also catches "one" up to "twenty". ! ! Note that a game can provide a ParseNumber routine which takes priority, ! to enable parsing of odder numbers ("x45y12", say). ! ---------------------------------------------------------------------------- [ TryNumber wordnum i j c num len mul tot d digit; i = wn; wn = wordnum; j = NextWord(); wn = i; j = NumberWord(j); if (j >= 1) return j; num = WordAddress(wordnum); len = WordLength(wordnum); tot = ParseNumber(num, len); if (tot == 0) tot = LibraryExtensions.RunWhile(ext_parsenumber, 0, num, len); if (tot ~= 0) return tot; if (len >= 4) mul=1000; if (len == 3) mul=100; if (len == 2) mul=10; if (len == 1) mul=1; tot = 0; c = 0; for (c = 0 : c < len : c++) { digit=num->c; if (digit == '0') { d = 0; jump digok; } if (digit == '1') { d = 1; jump digok; } if (digit == '2') { d = 2; jump digok; } if (digit == '3') { d = 3; jump digok; } if (digit == '4') { d = 4; jump digok; } if (digit == '5') { d = 5; jump digok; } if (digit == '6') { d = 6; jump digok; } if (digit == '7') { d = 7; jump digok; } if (digit == '8') { d = 8; jump digok; } if (digit == '9') { d = 9; jump digok; } return -1000; .digok; tot = tot+mul*d; mul = mul/10; } if (len > 4) tot=10000; return tot; ]; ! ---------------------------------------------------------------------------- ! AnyNumber is a general parsing routine which accepts binary, hexadecimal ! and decimal numbers up to the full Zcode/Glulx ranges. ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; ! decimal range is -32768 to 32767 Constant MAX_DECIMAL_SIZE 5; Constant MAX_DECIMAL_BASE 3276; #Ifnot; ! TARGET_GLULX ! decimal range is -2147483648 to 2147483647 Constant MAX_DECIMAL_SIZE 10; Constant MAX_DECIMAL_BASE 214748364; #Endif; ! TARGET_ [ AnyNumber wa we sign base digit digit_count num; wa = WordAddress(wn); we = wa + WordLength(wn); sign = 1; base = 10; if (wa->0 == '-') { sign = -1; wa++; } else { if (wa->0 == '$') { base = 16; wa++; } if (wa->0 == '$') { base = 2; wa++; } } if (wa >= we) return GPR_FAIL; ! no digits after -/$ while (wa->0 == '0') wa++; ! skip leading zeros for (num=0,digit_count=1 : wa0) { '0' to '9': digit = wa->0 - '0'; 'A' to 'F': digit = wa->0 - 'A' + 10; 'a' to 'f': digit = wa->0 - 'a' + 10; default: return GPR_FAIL; } if (digit >= base) return GPR_FAIL; digit_count++; switch (base) { 16: if (digit_count > 2*WORDSIZE) return GPR_FAIL; 2: if (digit_count > 8*WORDSIZE) return GPR_FAIL; 10: if (digit_count > MAX_DECIMAL_SIZE) return GPR_FAIL; if (digit_count == MAX_DECIMAL_SIZE) { if (num > MAX_DECIMAL_BASE) return GPR_FAIL; if (num == MAX_DECIMAL_BASE) { if (sign == 1 && digit > 7) return GPR_FAIL; if (sign == -1 && digit > 8) return GPR_FAIL; } } } num = base*num + digit; } parsed_number = num * sign; wn++; return GPR_NUMBER; ]; ! ---------------------------------------------------------------------------- ! GetGender returns 0 if the given animate object is female, and 1 if male ! (not all games will want such a simple decision function!) ! ---------------------------------------------------------------------------- [ GetGender person; if (person hasnt female) rtrue; rfalse; ]; [ GetGNAOfObject obj case gender; if (obj hasnt animate) case = 6; if (obj has male) gender = male; if (obj has female) gender = female; if (obj has neuter) gender = neuter; if (gender == 0) { if (case == 0) gender = LanguageAnimateGender; else gender = LanguageInanimateGender; } if (gender == female) case = case + 1; if (gender == neuter) case = case + 2; if (obj has pluralname) case = case + 3; return case; ]; ! ---------------------------------------------------------------------------- ! Converting between dictionary addresses and entry numbers ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ]; [ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ]; #Ifnot; ! TARGET_GLULX ! In Glulx, dictionary entries *are* addresses. [ Dword__No w; return w; ]; [ No__Dword n; return n; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! For copying buffers ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ CopyBuffer bto bfrom i size; size = bto->0; for (i=1 : i<=size : i++) bto->i = bfrom->i; ]; #Ifnot; ! TARGET_GLULX [ CopyBuffer bto bfrom i; for (i=0 : ii = bfrom->i; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! Provided for use by language definition files ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ LTI_Insert i ch b y; ! Protect us from strict mode, as this isn't an array in quite the ! sense it expects b = buffer; ! Insert character ch into buffer at point i. ! Being careful not to let the buffer possibly overflow: y = b->1; if (y > b->0) y = b->0; ! Move the subsequent text along one character: for (y=y+2 : y>i : y--) b->y = b->(y-1); b->i = ch; ! And the text is now one character longer: if (b->1 < b->0) (b->1)++; ]; #Ifnot; ! TARGET_GLULX [ LTI_Insert i ch b y; ! Protect us from strict mode, as this isn't an array in quite the ! sense it expects b = buffer; ! Insert character ch into buffer at point i. ! Being careful not to let the buffer possibly overflow: y = b-->0; if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN; ! Move the subsequent text along one character: for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1); b->i = ch; ! And the text is now one character longer: if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++; ]; #Endif; ! TARGET_ ! ============================================================================ [ PronounsSub x y c d; L__M(##Pronouns, 1); c = (LanguagePronouns-->0)/3; if (player ~= selfobj) c++; if (c == 0) return L__M(##Pronouns, 4); for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) { print "~", (address) LanguagePronouns-->x, "~ "; y = LanguagePronouns-->(x+2); if (y == NULL) L__M(##Pronouns, 3); else { L__M(##Pronouns, 2); print (the) y; } d++; if (d < c-1) print (string) COMMA__TX; if (d == c-1) print (SerialComma) c, (string) AND__TX; } if (player ~= selfobj) { print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2); c = player; player = selfobj; print (the) c; player = c; } L__M(##Pronouns, 5); ]; [ SetPronoun dword value x; for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (LanguagePronouns-->x == dword) { LanguagePronouns-->(x+2) = value; return; } RunTimeError(12); ]; [ PronounValue dword x; for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (LanguagePronouns-->x == dword) return LanguagePronouns-->(x+2); return 0; ]; [ ResetVagueWords obj; PronounNotice(obj); ]; #Ifdef EnglishNaturalLanguage; [ PronounOldEnglish; if (itobj ~= old_itobj) SetPronoun('it', itobj); if (himobj ~= old_himobj) SetPronoun('him', himobj); if (herobj ~= old_herobj) SetPronoun('her', herobj); old_itobj = itobj; old_himobj = himobj; old_herobj = herobj; ]; #Endif; !EnglishNaturalLanguage [ PronounNotice obj x bm; if (obj == player) return; #Ifdef EnglishNaturalLanguage; PronounOldEnglish(); #Endif; ! EnglishNaturalLanguage bm = PowersOfTwo_TB-->(GetGNAOfObject(obj)); for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (bm & (LanguagePronouns-->(x+1)) ~= 0) LanguagePronouns-->(x+2) = obj; #Ifdef EnglishNaturalLanguage; itobj = PronounValue('it'); old_itobj = itobj; himobj = PronounValue('him'); old_himobj = himobj; herobj = PronounValue('her'); old_herobj = herobj; #Endif; ! EnglishNaturalLanguage ]; ! ============================================================================ ! End of the parser proper: the remaining routines are its front end. ! ---------------------------------------------------------------------------- Object InformLibrary "(Inform Library)" with play [ i j k l; #Ifdef TARGET_ZCODE; ZZInitialise(); #Ifnot; ! TARGET_GLULX GGInitialise(); #Endif; ! TARGET_ GamePrologue(); while (~~deadflag) { ! everything happens in this loop #Ifdef EnglishNaturalLanguage; PronounOldEnglish(); old_itobj = PronounValue('it'); old_himobj = PronounValue('him'); old_herobj = PronounValue('her'); #Endif; ! EnglishNaturalLanguage .very__late__error; if (score ~= last_score) { if (notify_mode == 1) NotifyTheScore(); last_score = score; } .late__error; inputobjs-->0 = 0; inputobjs-->1 = 0; inputobjs-->2 = 0; inputobjs-->3 = 0; meta = false; ! The Parser writes its results into inputobjs and meta, ! a flag indicating a "meta-verb". This can only be set for ! commands by the player, not for orders to others. InformParser.parse_input(inputobjs); action = inputobjs-->0; ! -------------------------------------------------------------- ! Reverse "give fred biscuit" into "give biscuit to fred" if (action == ##GiveR or ##ShowR) { i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i; if (action == ##GiveR) action = ##Give; else action = ##Show; } ! Convert "P, tell me about X" to "ask P about X" if (action == ##Tell && inputobjs-->2 == player && actor ~= player) { inputobjs-->2 = actor; actor = player; action = ##Ask; } ! Convert "ask P for X" to "P, give X to me" if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) { actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = player; action = ##Give; } ! For old, obsolete code: special_word contains the topic word ! in conversation if (action == ##Ask or ##Tell or ##Answer) special_word = special_number1; ! -------------------------------------------------------------- multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false; ! For implicit taking and multiple object detection .begin__action; inp1 = 0; inp2 = 0; i = inputobjs-->1; if (i >= 1) inp1 = inputobjs-->2; if (i >= 2) inp2 = inputobjs-->3; ! inp1 and inp2 hold: object numbers, or 0 for "multiple object", ! or 1 for "a number or dictionary address" if (inp1 == 1) noun = special_number1; else noun = inp1; if (inp2 == 1) { if (inp1 == 1) second = special_number2; else second = special_number1; } else second = inp2; ! ------------------------------------------------------------- !print "inp1: ", (name) inp1, "^"; !print "inp2: ", (name) inp2, "^"; !print "inputobjs-->1: ", (name) inputobjs-->1, "^"; !print "inputobjs-->2: ", (name) inputobjs-->2, "^"; !print "inputobjs-->3: ", (name) inputobjs-->3, "^"; !print "noun: ", (name) noun, "^"; !print "second: ", (name) second, "^"; !print "actor: ", (name) actor, "^"; !print "---^"; ! -------------------------------------------------------------- ! Generate the action... if ((i == 0) || (i == 1 && inp1 ~= 0) || (i == 2 && inp1 ~= 0 && inp2 ~= 0)) { if (actor ~= player) { switch (self.actor_act(actor, action, noun, second)) { ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action; default: jump turn__end; } } self.begin_action(action, noun, second, 0); jump turn__end; } ! ...unless a multiple object must be substituted. First: ! (a) check the multiple list isn't empty; ! (b) warn the player if it has been cut short because too long; ! (c) generate a sequence of actions from the list ! (stopping in the event of death or movement away). multiflag = true; j = multiple_object-->0; if (j == 0) { L__M(##Miscellany, 2); jump late__error; } if (toomany_flag) { toomany_flag = false; L__M(##Miscellany, 1); } i = location; for (k=1 : k<=j : k++) { if (deadflag) break; if (location ~= i) { L__M(##Miscellany, 51); break; } l = multiple_object-->k; PronounNotice(l); print (name) l, (string) COLON__TX; if (inp1 == 0) { inp1 = l; switch (self.actor_act(actor, action, l, second)) { ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action; ACTOR_ACT_ABORT_ORDER: jump turn__end; } inp1 = 0; } else { inp2 = l; if (self.actor_act(actor, action, noun, l) == ACTOR_ACT_ABORT_NOTUNDERSTOOD) jump begin__action; inp2 = 0; } } ! -------------------------------------------------------------- .turn__end; ! No time passes if either (i) the verb was meta, or ! (ii) we've only had the implicit take before the "real" ! action to follow. if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; } if (meta) continue; if (~~deadflag) self.end_turn_sequence(); else if (START_MOVE ~= 1) turns++; } ! end of while() if (deadflag ~= 2 && AfterLife() == false) LibraryExtensions.RunAll(ext_afterlife); if (deadflag == 0) jump very__late__error; GameEpilogue(); ], ! end of 'play' property end_turn_sequence [; AdvanceWorldClock(); if (deadflag) return; RunTimersAndDaemons(); if (deadflag) return; RunEachTurnProperties(); if (deadflag) return; if (TimePasses() == 0) LibraryExtensions.RunAll(ext_timepasses); if (deadflag) return; AdjustLight(); if (deadflag) return; NoteObjectAcquisitions(); ], ! The first time we call self.actor_act(), set the fifth ! parameter to true. Details can be seen in ! 1d95759b1bd6674509d6cf9161e6db3da3831f10 and in Issues #23 and ! #30 (Mantis 1813 and 1885) actor_act [ p a n s ft j sp sa sn ss; sp = actor; actor = p; if (p ~= player) { ! The player's "orders" property can refuse to allow ! conversation here, by returning true. If not, the order is ! sent to the other person's "orders" property. If that also ! returns false, then: if it was a misunderstood command ! anyway, it is converted to an Answer action (thus ! "floyd, grrr" ends up as "say grrr to floyd"). If it was a ! good command, it is finally offered to the Order: part of ! the other person's "life" property, the old-fashioned ! way of dealing with conversation. sa = action; sn = noun; ss = second; action = a; noun = n; second = s; j = RunRoutines(player, orders); if (j == 0) { j = RunRoutines(actor, orders); if (j == 0) { if (action == ##NotUnderstood) { inputobjs-->3 = actor; actor = player; action = ##Answer; ! abort, not resetting action globals return ACTOR_ACT_ABORT_NOTUNDERSTOOD; } if (RunLife(actor, ##Order) == 0) { L__M(##Order, 1, actor); if (~~ft) return ACTOR_ACT_ABORT_ORDER; } } } action = sa; noun = sn; second = ss; if (ft) return ACTOR_ACT_ABORT_ORDER; } else if (~~ft) self.begin_action(a, n, s, 0); actor = sp; return ACTOR_ACT_OK; ], begin_action [ a n s source sa sn ss; sa = action; sn = noun; ss = second; action = a; noun = n; second = s; #Ifdef DEBUG; if (debug_flag & DEBUG_ACTIONS) TraceAction(source); #Ifnot; source = 0; #Endif; ! DEBUG #Iftrue (Grammar__Version == 1); if ((meta || BeforeRoutines() == false) && action < 256) { #Ifdef INFIX; if (infix_verb) { if (BeforeRoutines() == false) ActionPrimitive(); } else ActionPrimitive(); #Ifnot; ActionPrimitive(); #Endif; } #Ifnot; if ((meta || BeforeRoutines() == false) && action < 4096) { #Ifdef INFIX; if (infix_verb) { if (BeforeRoutines() == false) ActionPrimitive(); } else ActionPrimitive(); #Ifnot; ActionPrimitive(); #Endif; } #Endif; ! Grammar__Version action = sa; noun = sn; second = ss; ], has proper; ! ---------------------------------------------------------------------------- ! Routines called before and after main 'play' loop [ GamePrologue i j; before_first_turn = true; for (i=1 : i<=100 : i++) j = random(i); ChangeDefault(cant_go, CANTGO__TX); real_location = thedark; player = selfobj; actor = player; selfobj.capacity = MAX_CARRIED; ! ### change? #Ifdef LanguageInitialise; LanguageInitialise(); #Endif; ! LanguageInitialise #Ifdef EnglishNaturalLanguage; old_itobj = itobj; old_himobj = himobj; old_herobj = herobj; #Endif;! EnglishNaturalLanguage new_line; LibraryExtensions.RunAll(ext_initialise); j = Initialise(); last_score = score; initial_lookmode = lookmode; objectloop (i in player) give i moved ~concealed; move player to location; while (parent(location)) location = parent(location); real_location = location; MoveFloatingObjects(); actor = player; ! resync, because player may have been changed in Initialise() actors_location = location; lightflag = OffersLight(parent(player)); if (lightflag == 0) location = thedark; if (j ~= 2) Banner(); #ifndef NOINITIAL_LOOK; ; #endif; before_first_turn = false; ]; [ GameEpilogue; print "^^ "; #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; ! V5 #Ifnot; ! TARGET_GLULX glk_set_style(style_Alert); #Endif; ! TARGET_ print "***"; switch (deadflag) { 1: L__M(##Miscellany, 3); 2: L__M(##Miscellany, 4); default: print " "; if (DeathMessage() == false) LibraryExtensions.RunAll(ext_deathmessage); print " "; } print "***"; #Ifdef TARGET_ZCODE; #IfV5; style roman; #Endif; ! V5 #Ifnot; ! TARGET_GLULX glk_set_style(style_Normal); #Endif; ! TARGET_ print "^^"; #Ifndef NO_SCORE; print "^"; #Endif; ! NO_SCORE Epilogue(); ScoreSub(); DisplayStatus(); AfterGameOver(); ]; ! ---------------------------------------------------------------------------- ! Routines called at end of each turn [ AdvanceWorldClock; turns++; if (the_time ~= NULL) { if (time_rate >= 0) the_time=the_time+time_rate; else { time_step--; if (time_step == 0) { the_time++; time_step = -time_rate; } } the_time = the_time % 1440; } ]; [ RunTimersAndDaemons i j; #Ifdef DEBUG; if (debug_flag & DEBUG_TIMERS) { for (i=0 : ii; if (j ~= 0) { print (name) (j&~WORD_HIGHBIT), ": "; if (j & WORD_HIGHBIT) print "daemon"; else print "timer with ", j.time_left, " turns to go"; new_line; } } } #Endif; ! DEBUG for (i=0 : ii; if (j ~= 0) { if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon); else { if (j.time_left == 0) { StopTimer(j); RunRoutines(j, time_out); } else j.time_left = j.time_left-1; } } } ]; [ RunEachTurnProperties; scope_reason = EACH_TURN_REASON; verb_word = 0; DoScopeAction(location); SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; ]; ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ ActionPrimitive; (#actions_table-->action)(); ]; #Ifnot; ! TARGET_GLULX [ ActionPrimitive; (#actions_table-->(action+1))(); ]; #Endif; ! TARGET_ [ AfterGameOver i amus_ret; .RRQPL; L__M(##Miscellany,5); .RRQL; L__M(##Prompt); #Ifdef TARGET_ZCODE; #IfV3; read buffer parse; #Endif; ! V3 temp_global=0; #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5 #Ifnot; ! TARGET_GLULX KeyboardPrimitive(buffer, parse); #Endif; ! TARGET_ i = parse-->1; if (i == QUIT1__WD or QUIT2__WD) { #Ifdef TARGET_ZCODE; quit; #Ifnot; ! TARGET_GLULX quit; #Endif; ! TARGET_ } if (i == RESTART__WD) { #Ifdef TARGET_ZCODE; @restart; #Ifnot; ! TARGET_GLULX @restart; #Endif; ! TARGET_ } if (i == RESTORE__WD) { RestoreSub(); jump RRQPL; } if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) { new_line; FullScoreSub(); jump RRQPL; } if (deadflag == 2 && i == AMUSING__WD) { amus_ret = false; if (AMUSING_PROVIDED == 0) { new_line; amus_ret = Amusing(); } if (amus_ret == false) LibraryExtensions.RunAll(ext_amusing); jump RRQPL; } #IfV5; if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) { i = PerformUndo(); if (i == 0) jump RRQPL; } #Endif; ! V5 L__M(##Miscellany, 8); jump RRQL; ]; [ NoteObjectAcquisitions i; objectloop (i in player) if (i hasnt moved) { give i moved; if (i has scored) { score = score + OBJECT_SCORE; things_score = things_score + OBJECT_SCORE; } } ]; ! ---------------------------------------------------------------------------- ! R_Process is invoked by the <...> and <<...>> statements, whose syntax is: ! ! traditional ! ! introduced at compiler 6.33 [ R_Process a n s p s1 s2 s3; s1 = inp1; s2 = inp2; s3 = actor; inp1 = n; inp2 = s; if (p) actor = p; else actor = player; InformLibrary.begin_action(a, n, s, 1); inp1 = s1; inp2 = s2; actor = s3; ]; ! ---------------------------------------------------------------------------- [ TestScope obj act a al sr x y; x = parser_one; y = parser_two; parser_one = obj; parser_two = 0; a = actor; al = actors_location; sr = scope_reason; scope_reason = TESTSCOPE_REASON; if (act == 0) actor = player; else actor = act; actors_location = ScopeCeiling(actor); SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a; actors_location = al; parser_one = x; x = parser_two; parser_two = y; return x; ]; [ LoopOverScope routine act x y a al; x = parser_one; y = scope_reason; a = actor; al = actors_location; parser_one = routine; if (act == 0) actor = player; else actor = act; actors_location = ScopeCeiling(actor); scope_reason = LOOPOVERSCOPE_REASON; SearchScope(actors_location, actor, 0); parser_one = x; scope_reason = y; actor = a; actors_location = al; ]; [ BeforeRoutines rv; if (GamePreRoutine()) rtrue; if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepreroutine, 0); if (rv) rtrue; if (RunRoutines(player, orders)) rtrue; scope_reason = REACT_BEFORE_REASON; parser_one = 0; SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; if (parser_one) rtrue; if (location && RunRoutines(location, before)) rtrue; if (inp1 > 1 && RunRoutines(inp1, before)) rtrue; rfalse; ]; [ AfterRoutines rv; scope_reason = REACT_AFTER_REASON; parser_one = 0; SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; if (parser_one) rtrue; if (location && RunRoutines(location, after)) rtrue; if (inp1 > 1 && RunRoutines(inp1, after)) rtrue; rv = GamePostRoutine(); if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepostroutine, false); return rv; ]; [ RunLife a j; #Ifdef DEBUG; if (debug_flag & DEBUG_ACTIONS) TraceAction(2, j); #Endif; ! DEBUG reason_code = j; return RunRoutines(a,life); ]; [ ZRegion addr; switch (metaclass(addr)) { ! Left over from Inform 5 nothing: return 0; Object, Class: return 1; Routine: return 2; String: return 3; } ]; [ PrintOrRun obj prop flag; if (obj.#prop > WORDSIZE) return RunRoutines(obj, prop); if (obj.prop == NULL) rfalse; switch (metaclass(obj.prop)) { Class, Object, nothing: return RunTimeError(2,obj,prop); String: print (string) obj.prop; if (flag == 0) new_line; rtrue; Routine: return RunRoutines(obj, prop); } ]; [ PrintOrRunVar var flag; switch (metaclass(var)) { Object: print (name) var; String: print (string) var; if (flag == 0) new_line; Routine: return var(); default: print (char) '(', var, (char) ')'; } rtrue; ]; [ ValueOrRun obj prop; !### this is entirely unlikely to work. Does anyone care? (AP) ! Well, it's certainly used three times in verblibm.h (RF) ! Update: now not used (RF) if (obj.prop < 256) return obj.prop; return RunRoutines(obj, prop); ]; [ RunRoutines obj prop; if (obj == thedark && prop ~= initial or short_name or description) obj = real_location; if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse; return obj.prop(); ]; #Ifdef TARGET_ZCODE; [ ChangeDefault prop val a b; ! Use assembly-language here because -S compilation won't allow this: @loadw 0 5 -> a; b = prop-1; @storew a b val; ]; #Ifnot; ! TARGET_GLULX [ ChangeDefault prop val; ! Use assembly-language here because -S compilation won't allow this: ! #cpv__start-->prop = val; @astore #cpv__start prop val; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- [ StartTimer obj timer i; for (i=0 : ii == obj) rfalse; for (i=0 : ii == 0) jump FoundTSlot; i = active_timers++; if (i >= MAX_TIMERS) { RunTimeError(4); return; } .FoundTSlot; if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; } the_timers-->i = obj; obj.time_left = timer; ]; [ StopTimer obj i; for (i=0 : ii == obj) jump FoundTSlot2; rfalse; .FoundTSlot2; if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; } the_timers-->i = 0; obj.time_left = 0; ]; [ StartDaemon obj i; for (i=0 : ii == WORD_HIGHBIT + obj) rfalse; for (i=0 : ii == 0) jump FoundTSlot3; i = active_timers++; if (i >= MAX_TIMERS) RunTimeError(4); .FoundTSlot3; the_timers-->i = WORD_HIGHBIT + obj; ]; [ StopDaemon obj i; for (i=0 : ii == WORD_HIGHBIT + obj) jump FoundTSlot4; rfalse; .FoundTSlot4; the_timers-->i = 0; ]; ! ---------------------------------------------------------------------------- [ DisplayStatus; if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; } else { sline1 = the_time/60; sline2 = the_time%60;} ]; [ SetTime t s; the_time = t; time_rate = s; time_step = 0; if (s < 0) time_step = 0-s; ]; [ NotifyTheScore; #Ifdef TARGET_GLULX; glk_set_style(style_Note); #Endif; ! TARGET_GLULX print "^["; L__M(##Miscellany, 50, score-last_score); print ".]^"; #Ifdef TARGET_GLULX; glk_set_style(style_Normal); #Endif; ! TARGET_GLULX ]; ! ---------------------------------------------------------------------------- [ AdjustLight flag i; i = lightflag; lightflag = OffersLight(parent(player)); if (i == 0 && lightflag == 1) { location = real_location; if (flag == 0) ; } if (i == 1 && lightflag == 0) { real_location = location; location = thedark; if (flag == 0) { NoteArrival(); return L__M(##Miscellany, 9); } } if (i == 0 && lightflag == 0) location = thedark; ]; [ OffersLight i j; if (i == 0) rfalse; if (i has light) rtrue; objectloop (j in i) if (HasLightSource(j) == 1) rtrue; if (i has container) { if (i has open || i has transparent) return OffersLight(parent(i)); } else { if (i has enterable || i has transparent || i has supporter) return OffersLight(parent(i)); } rfalse; ]; [ HidesLightSource obj; if (obj == player) rfalse; if (obj has transparent or supporter) rfalse; if (obj has container) return (obj hasnt open); return (obj hasnt enterable); ]; [ HasLightSource i j ad; if (i == 0) rfalse; if (i has light) rtrue; if (i has enterable || IsSeeThrough(i) == 1) if (~~(HidesLightSource(i))) objectloop (j in i) if (HasLightSource(j) == 1) rtrue; ad = i.&add_to_scope; if (parent(i) ~= 0 && ad ~= 0) { if (metaclass(ad-->0) == Routine) { ats_hls = 0; ats_flag = 1; RunRoutines(i, add_to_scope); ats_flag = 0; if (ats_hls == 1) rtrue; } else { for (j=0 : (WORDSIZE*j)j) == 1) rtrue; } } rfalse; ]; [ ChangePlayer obj flag i; ! if (obj.&number == 0) return RunTimeError(7, obj); if (obj == nothing) obj = selfobj; if (actor == player) actor=obj; give player ~transparent ~concealed; i = obj; while (parent(i) ~= 0) { if (i has animate) give i transparent; i = parent(i); } if (player == selfobj && player provides nameless && player.nameless == true) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { player.short_name = MYFORMER__TX; (player.&name)-->0 = 'my'; (player.&name)-->1 = 'former'; (player.&name)-->2 = 'self'; } else if (player.narrative_voice == 2) { player.short_name = FORMER__TX; (player.&name)-->0 = 'my'; (player.&name)-->1 = 'former'; (player.&name)-->2 = 'self'; } } } player = obj; give player transparent concealed animate; i = player; while (parent(i) ~= 0) i = parent(i); location = i; real_location = location; if (parent(player) == 0) return RunTimeError(10); MoveFloatingObjects(); lightflag = OffersLight(parent(player)); if (lightflag == 0) location = thedark; print_player_flag = flag; ]; ! ---------------------------------------------------------------------------- #Ifdef DEBUG; #Ifdef TARGET_ZCODE; [ DebugParameter w; print w; if (w >= 1 && w <= top_object) print " (", (name) w, ")"; if (UnsignedCompare(w, dict_start) >= 0 && UnsignedCompare(w, dict_end) < 0 && (w - dict_start) % dict_entry_size == 0) print " ('", (address) w, "')"; ]; [ DebugAction a anames; #Iftrue (Grammar__Version == 1); if (a >= 256) { print ""; return; } #Ifnot; if (a >= 4096) { print ""; return; } #Endif; ! Grammar__Version anames = #identifiers_table; anames = anames + 2*(anames-->0) + 2*48; print (string) anames-->a; ]; [ DebugAttribute a anames; if (a < 0 || a >= 48) print ""; else { anames = #identifiers_table; anames = anames + 2*(anames-->0); print (string) anames-->a; } ]; #Ifnot; ! TARGET_GLULX [ DebugParameter w endmem; print w; @getmemsize endmem; if (w >= 1 && w < endmem) { if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")"; if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')"; } ]; [ DebugAction a str; if (a >= 4096) { print ""; return; } if (a < 0 || a >= #identifiers_table-->7) print ""; else { str = #identifiers_table-->6; str = str-->a; if (str) print (string) str; else print ""; } ]; [ DebugAttribute a str; if (a < 0 || a >= NUM_ATTR_BYTES*8) print ""; else { str = #identifiers_table-->4; str = str-->a; if (str) print (string) str; else print ""; } ]; #Endif; ! TARGET_ [ TraceAction source ar; if (source < 2) print "[ Action ", (DebugAction) action; else { if (ar == ##Order) print "[ Order to ", (name) actor, ": ", (DebugAction) action; else print "[ Life rule ", (DebugAction) ar; } if (noun ~= 0) print " with noun ", (DebugParameter) noun; if (second ~= 0) print " and second ", (DebugParameter) second; if (source == 0) print " "; if (source == 1) print " (from < > statement) "; print "]^"; ]; [ DebugToken token; AnalyseToken(token); switch (found_ttype) { ILLEGAL_TT: print ""; ELEMENTARY_TT: switch (found_tdata) { NOUN_TOKEN: print "noun"; HELD_TOKEN: print "held"; MULTI_TOKEN: print "multi"; MULTIHELD_TOKEN: print "multiheld"; MULTIEXCEPT_TOKEN: print "multiexcept"; MULTIINSIDE_TOKEN: print "multiinside"; CREATURE_TOKEN: print "creature"; SPECIAL_TOKEN: print "special"; NUMBER_TOKEN: print "number"; TOPIC_TOKEN: print "topic"; ENDIT_TOKEN: print "END"; } PREPOSITION_TT: print "'", (address) found_tdata, "'"; ROUTINE_FILTER_TT: #Ifdef INFIX; print "noun=", (InfixPrintPA) found_tdata; #Ifnot; print "noun=Routine(", found_tdata, ")"; #Endif; ! INFIX ATTR_FILTER_TT: print (DebugAttribute) found_tdata; SCOPE_TT: #Ifdef INFIX; print "scope=", (InfixPrintPA) found_tdata; #Ifnot; print "scope=Routine(", found_tdata, ")"; #Endif; ! INFIX GPR_TT: #Ifdef INFIX; print (InfixPrintPA) found_tdata; #Ifnot; print "Routine(", found_tdata, ")"; #Endif; ! INFIX } ]; [ DebugGrammarLine pcount; print " * "; for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) { if ((line_token-->pcount)->0 & $10) print "/ "; print (DebugToken) line_token-->pcount, " "; } print "-> ", (DebugAction) action_to_be; if (action_reversed) print " reverse"; ]; #Ifdef TARGET_ZCODE; [ ShowVerbSub grammar lines j; if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0) "Try typing ~showverb~ and then the name of a verb."; print "Verb"; if ((noun->#dict_par1) & DICT_META) print " meta"; for (j=dict_start : j#dict_par2 == noun->#dict_par2) print " '", (address) j, "'"; new_line; grammar = (HDR_STATICMEMORY-->0)-->($ff-(noun->#dict_par2)); lines = grammar->0; grammar++; if (lines == 0) "has no grammar lines."; for (: lines>0 : lines--) { grammar = UnpackGrammarLine(grammar); print " "; DebugGrammarLine(); new_line; } ]; #Ifnot; ! TARGET_GLULX [ ShowVerbSub grammar lines i j wd dictlen entrylen; if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0) "Try typing ~showverb~ and then the name of a verb."; print "Verb"; if ((noun->#dict_par1) & DICT_META) print " meta"; dictlen = #dictionary_table-->0; entrylen = DICT_WORD_SIZE + 7; for (j=0 : j#dict_par2 == noun->#dict_par2) print " '", (address) wd, "'"; } new_line; i = DictionaryWordToVerbNum(noun); grammar = (#grammar_table)-->(i+1); lines = grammar->0; grammar++; if (lines == 0) "has no grammar lines."; for (: lines>0 : lines--) { grammar = UnpackGrammarLine(grammar); print " "; DebugGrammarLine(); new_line; } ]; #Endif; ! TARGET_ [ ShowObjSub c f l a n x numattr; if (noun == 0) noun = location; objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; } if (f == 1) print (name) l, " ~"; else print "Object ~"; print (name) noun, "~ (", noun, ")"; if (parent(noun)) print " in ~", (name) parent(noun), "~ (", parent(noun), ")"; new_line; if (f > 1) { print " class "; objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " "; new_line; } #Ifdef TARGET_ZCODE; numattr = 48; #Ifnot; ! TARGET_GLULX numattr = NUM_ATTR_BYTES * 8; #Endif; ! TARGET_ for (a=0,f=0 : a0; #Ifnot; ! TARGET_GLULX l = INDIV_PROP_START + #identifiers_table-->3; #Endif; ! TARGET_ for (a=1 : a<=l : a++) { if ((a ~= 2 or 3) && noun.&a) { if (f == 0) { print " with "; f=1; } print (property) a; n = noun.#a; for (c=0 : WORDSIZE*cc; if (a == name) print "'", (address) x, "'"; else { if (a == number or capacity or time_left) print x; else { switch (x) { NULL: print "NULL"; 0: print "0"; 1: print "1"; default: switch (metaclass(x)) { Class, Object: print (name) x; String: print "~", (string) x, "~"; Routine: print "[...]"; } print " (", x, ")"; } } } } print ",^ "; } } ! if (f==1) new_line; ]; [ ShowDictSub_helper x; print (address) x; ]; [ ShowDictSub dp el ne f x y z; #Ifdef TARGET_ZCODE; dp = HDR_DICTIONARY-->0; ! start of dictionary dp = dp + dp->0 + 1; ! skip over word-separators table el = dp->0; dp = dp + 1; ! entry length ne = dp-->0; dp = dp + WORDSIZE; ! number of entries #Ifnot; ! TARGET_GLULX; dp = #dictionary_table; ! start of dictionary el = DICT_WORD_SIZE + 7; ! entry length ne = dp-->0; dp = dp + WORDSIZE; ! number of entries #Endif; ! TARGET_ ! dp now at first entry wn = 2; x = NextWordStopped(); switch (x) { 0: "That word isn't in the dictionary."; -1: ; ! show all entries THEN1__WD: dp = './/'; ne = 1; ! show '.' COMMA_WORD: dp = ',//'; ne = 1; ! show ',' default: dp = x; ne = 1; f = true; ! show specified entry, plus associated objects } for ( : ne-- : dp=dp+el) { print (address) dp, " --> "; x = dp->#dict_par1; ! flag bits y = PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, ShowDictSub_helper, dp) + WORDSIZE; for (z=WORDSIZE : zz = UpperCase(StorageForShortName->z); if (y > WORDSIZE+1 && StorageForShortName->z == ' ' or '.' or ',') x = x | $8000; print (char) StorageForShortName->z; } print " --> "; if (x == 0) print " no flags"; else { !if (x & $0040) print " BIT_6"; !if (x & $0020) print " BIT_5"; !if (x & $0010) print " BIT_4"; if (x & $8000) print " UNTYPEABLE"; if (x & DICT_NOUN) print " noun"; if (x & DICT_PLUR) print "+plural"; if (x & DICT_VERB) print " verb"; if (x & DICT_META) print "+meta"; if (x & DICT_PREP) print " preposition"; if (f && (x & DICT_NOUN)) { print " --> refers to these objects:"; objectloop (x) if (WordInProperty(dp, x, name)) print "^ ", (name) x, " (", x, ")"; } } new_line; } ]; #Endif; ! DEBUG ! ---------------------------------------------------------------------------- ! Miscellaneous display routines used by DrawStatusLine and available for ! user. Most of these vary according to which machine is being compiled to ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ ClearScreen window; switch (window) { WIN_ALL: @erase_window -1; WIN_STATUS: @erase_window 1; WIN_MAIN: @erase_window 0; } ]; #Iftrue (#version_number == 6); [ MoveCursorV6 line column charw; ! 1-based postion on text grid @get_wind_prop 1 13 -> charw; ! font size charw = charw & $FF; line = 1 + charw*(line-1); column = 1 + charw*(column-1); @set_cursor line column; ]; #Endif; #Ifndef MoveCursor; [ MoveCursor line column; ! 1-based postion on text grid if (~~statuswin_current) { @set_window 1; #Ifdef COLOUR; if (clr_on && clr_bgstatus > 1) @set_colour clr_fgstatus clr_bgstatus; else #Endif; ! COLOUR style reverse; } if (line == 0) { line = 1; column = 1; } #Iftrue (#version_number == 6); MoveCursorV6(line, column); #Ifnot; @set_cursor line column; #Endif; statuswin_current = true; ]; #Endif; [ MainWindow; if (statuswin_current) { #Ifdef COLOUR; if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg; else #Endif; ! COLOUR style roman; @set_window 0; } statuswin_current = false; ]; #Iftrue (#version_number == 6); [ ScreenWidth width charw; @get_wind_prop 1 3 -> width; @get_wind_prop 1 13 -> charw; charw = charw & $FF; if (charw == 0) return width; return (width+charw-1) / charw; ]; #Ifnot; [ ScreenWidth; return (HDR_SCREENWCHARS->0); ]; #Endif; [ ScreenHeight; return (HDR_SCREENHLINES->0); ]; #Iftrue (#version_number == 6); [ StatusLineHeight height wx wy x y charh; ! Split the window. Standard 1.0 interpreters should keep the window 0 ! cursor in the same absolute position, but older interpreters, ! including Infocom's don't - they keep the window 0 cursor in the ! same position relative to its origin. We therefore compensate ! manually. @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx; @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh; @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x; height = height * charh; @split_window height; y = y - height + wy - 1; if (y < 1) y = 1; x = x + wx - 1; @set_cursor y x 0; gg_statuswin_cursize = height; ]; #Ifnot; [ StatusLineHeight height; if (gg_statuswin_cursize ~= height) @split_window height; gg_statuswin_cursize = height; ]; #Endif; #Ifdef COLOUR; [ SetColour f b window; if (window == 0) { ! if setting both together, set reverse clr_fgstatus = b; clr_bgstatus = f; } if (window == 1) { clr_fgstatus = f; clr_bgstatus = b; } if (window == 0 or 2) { clr_fg = f; clr_bg = b; } if (clr_on) { if (statuswin_current) @set_colour clr_fgstatus clr_bgstatus; else @set_colour clr_fg clr_bg; } ]; #Endif; ! COLOUR #Ifnot; ! TARGET_GLULX [ ClearScreen window; if (window == WIN_ALL or WIN_MAIN) { glk_window_clear(gg_mainwin); if (gg_quotewin) { glk_window_close(gg_quotewin, 0); gg_quotewin = 0; } } if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk_window_clear(gg_statuswin); ]; [ MoveCursor line column; ! 0-based postion on text grid if (gg_statuswin) { glk_set_window(gg_statuswin); } if (line == 0) { line = 1; column = 1; } glk_window_move_cursor(gg_statuswin, column-1, line-1); statuswin_current=1; ]; [ MainWindow; glk_set_window(gg_mainwin); statuswin_current=0; ]; [ MakeColourWord c; if (c > 9) return c; c = c-2; return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0); ]; [ ScreenWidth id; id=gg_mainwin; if (gg_statuswin && statuswin_current) id = gg_statuswin; glk_window_get_size(id, gg_arguments, 0); return gg_arguments-->0; ]; [ ScreenHeight; glk_window_get_size(gg_mainwin, 0, gg_arguments); return gg_arguments-->0; ]; #Ifdef COLOUR; [ SetColour f b window doclear i fwd bwd swin; if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer if (clr_on) { fwd = MakeColourWord(f); bwd = MakeColourWord(b); for (i=0 : i<=10: i++) { if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints glk_stylehint_clear(swin, i, 7); glk_stylehint_clear(swin, i, 8); } else { glk_stylehint_set(swin, i, 7, fwd); glk_stylehint_set(swin, i, 8, bwd); } } ! Now re-open the windows to apply the hints if (gg_statuswin) glk_window_close(gg_statuswin, 0); if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) { glk_window_close(gg_mainwin, 0); gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK); if (gg_scriptstr ~= 0) glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); } gg_statuswin = glk_window_open($12, gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK); if (statuswin_current && gg_statuswin) MoveCursor(); else MainWindow(); } if (window ~= 2) { clr_fgstatus = f; clr_bgstatus = b; } if (window ~= 1) { clr_fg = f; clr_bg = b; } ]; #Endif; ! COLOUR #Endif; ! TARGET_ #Ifndef COLOUR; [ SetColour f b window doclear; f = b = window = doclear = 0; ]; #Endif; [ SetClr f b w; SetColour (f, b, w); ]; [ RestoreColours; ! L61007, L61113 gg_statuswin_cursize = -1; ! Force window split in StatusLineHeight() #Ifdef COLOUR; if (clr_on) { ! check colour has been used SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored SetColour(clr_fgstatus, clr_bgstatus, 1, true); ClearScreen(); } #Endif; #Ifdef TARGET_ZCODE; #Iftrue (#version_number == 6); ! request screen update (0-->8) = (0-->8) | $$00000100; #Endif; #Endif; ! TARGET_ ]; ! ---------------------------------------------------------------------------- ! Except in Version 3, the DrawStatusLine routine does just that: this is ! provided explicitly so that it can be Replace'd to change the style, and ! as written it emulates the ordinary Standard game status line, which is ! drawn in hardware ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; #IfV5; #Iftrue (#version_number == 6); [ DrawStatusLine width x charw scw mvw; HDR_GAMEFLAGS-->0 = (HDR_GAMEFLAGS-->0) & ~$0004; StatusLineHeight(gg_statuswin_size); ! Now clear the window. This isn't totally trivial. Our approach is to ! select the fixed space font, measure its width, and print an appropriate ! number of spaces. We round up if the screen isn't a whole number of ! characters wide, and rely on window 1 being set to clip by default. MoveCursor(1, 1); @set_font 4 -> x; width = ScreenWidth(); spaces width; ! Back to standard font for the display. We use output_stream 3 to ! measure the space required, the aim being to get 50 characters ! worth of space for the location name. MoveCursor(1, 2); @set_font 1 -> x; if (location == thedark) print (name) location; else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } @get_wind_prop 1 3 -> width; @get_wind_prop 1 13 -> charw; charw = charw & $FF; @output_stream 3 StorageForShortName; print (string) SCORE__TX, "00000"; @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw; @output_stream 3 StorageForShortName; print (string) MOVES__TX, "00000"; @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw; if (width - scw - mvw >= 50*charw) { x = 1+width-scw-mvw; @set_cursor 1 x; print (string) SCORE__TX, sline1; x = x+scw; @set_cursor 1 x; print (string) MOVES__TX, sline2; } else { @output_stream 3 StorageForShortName; print "00000/00000"; @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw; if (width - scw >= 50*charw) { x = 1+width-scw; @set_cursor 1 x; print sline1, "/", sline2; } } ! Reselect roman, as Infocom's interpreters interpreters go funny ! if reverse is selected twice. MainWindow(); ]; #Endif; ! #version_number == 6 #Endif; ! V5 #Endif; ! TARGET_ZCODE #Ifndef DrawStatusLine; [ DrawStatusLine width posa posb; #Ifdef TARGET_GLULX; ! If we have no status window, we must not try to redraw it. if (gg_statuswin == 0) return; #Endif; ! If there is no player location, we shouldn't try to draw status window if (location == nothing || parent(player) == nothing) return; StatusLineHeight(gg_statuswin_size); MoveCursor(1, 1); width = ScreenWidth(); posa = width-26; posb = width-13; spaces width; MoveCursor(1, 2); if (location == thedark) { print (name) location; } else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } if (sys_statusline_flag && width > 53) { MoveCursor(1, posa); print (string) TIME__TX; LanguageTimeOfDay(sline1, sline2); } else { if (width > 66) { #Ifndef NO_SCORE; MoveCursor(1, posa); print (string) SCORE__TX, sline1; #Endif; MoveCursor(1, posb); print (string) MOVES__TX, sline2; } #Ifndef NO_SCORE; if (width > 53 && width <= 66) { MoveCursor(1, posb); print sline1, "/", sline2; } #Endif; } MainWindow(); ! set_window ]; #Endif; #Ifdef TARGET_GLULX; [ StatusLineHeight hgt parwin; if (gg_statuswin == 0) return; if (hgt == gg_statuswin_cursize) return; parwin = glk_window_get_parent(gg_statuswin); glk_window_set_arrangement(parwin, $12, hgt, 0); gg_statuswin_cursize = hgt; ]; [ Box__Routine maxwid arr ix lines lastnl parwin; maxwid = 0; ! squash compiler warning lines = arr-->0; if (gg_quotewin == 0) { gg_arguments-->0 = lines; ix = InitGlkWindow(GG_QUOTEWIN_ROCK); if (ix == false) ix = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_QUOTEWIN_ROCK); if (ix == false) gg_quotewin = glk_window_open(gg_mainwin, $12, lines, 3, GG_QUOTEWIN_ROCK); ! window_open } else { parwin = glk_window_get_parent(gg_quotewin); glk_window_set_arrangement(parwin, $12, lines, 0); } lastnl = true; if (gg_quotewin) { glk_window_clear(gg_quotewin); glk_set_window(gg_quotewin); lastnl = false; } ! If gg_quotewin is zero here, the quote just appears in the story window. glk_set_style(style_BlockQuote); for (ix=0 : ix(ix+1); if (ix < lines-1 || lastnl) new_line; } glk_set_style(style_Normal); if (gg_quotewin) { glk_set_window(gg_mainwin); } ]; #Endif; ! TARGET_GLULX #Ifdef TARGET_ZCODE; [ ZZInitialise; standard_interpreter = HDR_TERPSTANDARD-->0; transcript_mode = ((HDR_GAMEFLAGS-->0) & $0001); sys_statusline_flag = ( (HDR_TERPFLAGS->0) & $0002 ) / 2; top_object = #largest_object-255; dict_start = HDR_DICTIONARY-->0; dict_entry_size = dict_start->(dict_start->0 + 1); dict_start = dict_start + dict_start->0 + 4; dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size; #Ifdef DEBUG; if (dict_start > 0 && dict_end < 0 && ((-dict_start) - dict_end) % dict_entry_size == 0) print "** Warning: grammar properties might not work correctly **^"; #Endif; ! DEBUG buffer->0 = INPUT_BUFFER_LEN - WORDSIZE; buffer2->0 = INPUT_BUFFER_LEN - WORDSIZE; buffer3->0 = INPUT_BUFFER_LEN - WORDSIZE; parse->0 = MAX_BUFFER_WORDS; parse2->0 = MAX_BUFFER_WORDS; ]; #Ifnot; ! TARGET_GLULX; [ GGInitialise res; @gestalt 4 2 res; ! Test if this interpreter has Glk. if (res == 0) { ! Without Glk, we're entirely screwed. quit; } ! Set the VM's I/O system to be Glk. @setiosys 2 0; ! First, we must go through all the Glk objects that exist, and see ! if we created any of them. One might think this strange, since the ! program has just started running, but remember that the player might ! have just typed "restart". GGRecoverObjects(); res = InitGlkWindow(0); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 0); if (res) return; ! Now, gg_mainwin and gg_storywin might already be set. If not, set them. if (gg_mainwin == 0) { ! Open the story window. res = InitGlkWindow(GG_MAINWIN_ROCK); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_MAINWIN_ROCK); if (res == false) gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK); if (gg_mainwin == 0) { ! If we can't even open one window, there's no point in going on. quit; } } else { ! There was already a story window. We should erase it. glk_window_clear(gg_mainwin); } if (gg_statuswin == 0) { res = InitGlkWindow(GG_STATUSWIN_ROCK); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_STATUSWIN_ROCK); if (res == false) { gg_statuswin_cursize = gg_statuswin_size; gg_statuswin = glk_window_open(gg_mainwin, $12, gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK); } } ! It's possible that the status window couldn't be opened, in which case ! gg_statuswin is now zero. We must allow for that later on. glk_set_window(gg_mainwin); if (InitGlkWindow(1) == false) LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 1); ]; [ GGRecoverObjects id; ! If GGRecoverObjects() has been called, all these stored IDs are ! invalid, so we start by clearing them all out. ! (In fact, after a restoreundo, some of them may still be good. ! For simplicity, though, we assume the general case.) gg_mainwin = 0; gg_statuswin = 0; gg_quotewin = 0; gg_scriptfref = 0; gg_scriptstr = 0; gg_savestr = 0; gg_statuswin_cursize = 0; #Ifdef DEBUG; gg_commandstr = 0; gg_command_reading = false; #Endif; ! DEBUG ! Also tell the game to clear its object references. if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0); id = glk_stream_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_SAVESTR_ROCK: gg_savestr = id; GG_SCRIPTSTR_ROCK: gg_scriptstr = id; #Ifdef DEBUG; GG_COMMANDWSTR_ROCK: gg_commandstr = id; gg_command_reading = false; GG_COMMANDRSTR_ROCK: gg_commandstr = id; gg_command_reading = true; #Endif; ! DEBUG default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0); } id = glk_stream_iterate(id, gg_arguments); } id = glk_window_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_MAINWIN_ROCK: gg_mainwin = id; GG_STATUSWIN_ROCK: gg_statuswin = id; GG_QUOTEWIN_ROCK: gg_quotewin = id; default: if (IdentifyGlkObject(1, 0, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 0, id, gg_arguments-->0); } id = glk_window_iterate(id, gg_arguments); } id = glk_fileref_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_SCRIPTFREF_ROCK: gg_scriptfref = id; default: if (IdentifyGlkObject(1, 2, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 2, id, gg_arguments-->0); } id = glk_fileref_iterate(id, gg_arguments); } ! Tell the game to tie up any loose ends. if (IdentifyGlkObject(2) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 2); ]; ! This somewhat obfuscated function will print anything. ! It handles strings, functions (with optional arguments), objects, ! object properties (with optional arguments), and dictionary words. ! It does *not* handle plain integers, but you can use ! DecimalNumber or EnglishNumber to handle that case. ! ! Calling: Is equivalent to: ! ------- ---------------- ! PrintAnything() ! PrintAnything(0) ! PrintAnything("string"); print (string) "string"; ! PrintAnything('word') print (address) 'word'; ! PrintAnything(obj) print (name) obj; ! PrintAnything(obj, prop) obj.prop(); ! PrintAnything(obj, prop, args...) obj.prop(args...); ! PrintAnything(func) func(); ! PrintAnything(func, args...) func(args...); [ PrintAnything _vararg_count obj mclass; print_anything_result = 0; if (_vararg_count == 0) return; @copy sp obj; _vararg_count--; if (obj == 0) return; if (obj->0 == $60) { ! Dictionary word. Metaclass() can't catch this case, so we do ! it manually. print (address) obj; return; } mclass = metaclass(obj); switch (mclass) { nothing: return; String: print (string) obj; return; Routine: ! Call the function with all the arguments which are already ! on the stack. @call obj _vararg_count print_anything_result; return; Object: if (_vararg_count == 0) { print (name) obj; } else { ! Push the object back onto the stack, and call the ! veneer routine that handles obj.prop() calls. @copy obj sp; _vararg_count++; @call CA__Pr _vararg_count print_anything_result; } return; } ]; ! This does the same as PrintAnything, but the output is sent to a ! byte array in memory. The first two arguments must be the array ! address and length; the following arguments are interpreted as ! for PrintAnything. The return value is the number of characters ! output. ! If the output is longer than the array length given, the extra ! characters are discarded, so the array does not overflow. ! (However, the return value is the total length of the output, ! including discarded characters.) [ PrintAnyToArray _vararg_count arr arrlen str oldstr len; @copy sp arr; @copy sp arrlen; _vararg_count = _vararg_count - 2; oldstr = glk_stream_get_current(); ! stream_get_current str = glk_stream_open_memory(arr, arrlen, 1, 0); if (str == 0) return 0; glk_stream_set_current(str); @call PrintAnything _vararg_count 0; glk_stream_set_current(oldstr); @copy $ffffffff sp; @copy str sp; @glk $0044 2 0; @copy sp len; @copy sp 0; return len; ]; ! And this calls PrintAnyToArray on a particular array, jiggering ! the result to be a Glulx C-style ($E0) string. Constant GG_ANYTOSTRING_LEN 66; Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1; [ ChangeAnyToCString _vararg_count ix len; ix = GG_ANYTOSTRING_LEN-2; @copy ix sp; ix = AnyToStrArr+1; @copy ix sp; ix = _vararg_count+2; @call PrintAnyToArray ix len; AnyToStrArr->0 = $E0; if (len >= GG_ANYTOSTRING_LEN) len = GG_ANYTOSTRING_LEN-1; AnyToStrArr->(len+1) = 0; return AnyToStrArr; ]; #Endif; ! TARGET_ ! This is a trivial function which just prints a number, in decimal ! digits. It may be useful as a stub to pass to PrintAnything. [ DecimalNumber num; print num; ]; #Ifndef SHORTNAMEBUF_LEN; ! Can't use 'Default', unfortunately, Constant SHORTNAMEBUF_LEN 160; ! but this is functionally equivalent #Endif; #IfV5; #Ifdef VN_1630; Array StorageForShortName buffer SHORTNAMEBUF_LEN; #Ifnot; Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN; #Endif; ! VN_1630 #Endif; ! V5 #Ifdef TARGET_ZCODE; ! Platform-independent way of printing strings, routines and properties ! to a buffer (defined as length word followed by byte characters). [ PrintToBuffer buf len a b c d e; print_anything_result = 0; @output_stream 3 buf; switch (metaclass(a)) { String: print (string) a; Routine: print_anything_result = a(b, c, d, e); Object,Class: if (b) print_anything_result = PrintOrRun(a, b, true); else print (name) a; } @output_stream -3; if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()"); return buf-->0; ]; #Ifnot; ! TARGET_GLULX [ PrintToBuffer buf len a b c d e; if (b) { if (metaclass(a) == Object && a.#b == WORDSIZE && metaclass(a.b) == String) buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b); else buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b, c, d, e); } else buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a); if (buf-->0 > len) buf-->0 = len; return buf-->0; ]; #Endif; ! TARGET_ ! Print contents of buffer (defined as length word followed by byte characters). ! no_break == 1: omit trailing newline. ! set_case == 1: capitalise first letter; ! == 2: capitalise first letter, remainder lower case; ! == 3: all lower case; ! == 4: all upper case. ! centred == 1: add leading spaces. [ PrintFromBuffer buf no_break set_case centred i j k; j = (buf-->0) - 1; if (buf->(j+WORDSIZE) ~= 10 or 13) j++; ! trim any trailing newline if (centred) { k = (ScreenWidth() - j) / 2; if (k>0) spaces k; } for (i=0 : i(WORDSIZE+i); switch (set_case) { 0: break; 1: if (i) set_case = 0; else k = UpperCase(k); 2: if (i) k = LowerCase(k); else k = UpperCase(k); 3: k = LowerCase(k); 4: k = UpperCase(k); } print (char) k; } if (no_break == false) new_line; return j; ]; ! None of the following functions should be called for zcode if the ! output exceeds the size of the buffer. [ StringSize a b c d e; PrintToBuffer(StorageForShortName, 160, a, b, c, d, e); return StorageForShortName-->0; ]; [ PrintCapitalised a b no_break no_caps centred; if (metaclass(a) == Routine or String || b == 0 || metaclass(a.b) == Routine or String) PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, a, b); else if (a.b == NULL) rfalse; else return RunTimeError(2, a, b); if (no_caps == 0 or 1) no_caps = ~~no_caps; PrintFromBuffer(StorageForShortName, no_break, no_caps, centred); return print_anything_result; ]; [ Centre a b; PrintCapitalised(a, b, false, true, true); ]; [ CapitRule str no_caps; if (no_caps) print (string) str; else PrintCapitalised(str,0,true); ]; [ PrefaceByArticle o acode pluralise capitalise i artform findout artval; if (o provides articles) { artval=(o.&articles)-->(acode+short_name_case*LanguageCases); if (capitalise) print (CapitRule) artval; else print (string) artval; if (pluralise) return; print (PSN__) o; return; } i = GetGNAOfObject(o); if (pluralise) { if (i < 3 || (i >= 6 && i < 9)) i = i + 3; } i = LanguageGNAsToArticles-->i; artform = LanguageArticles + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases); #Iftrue (LanguageContractionForms == 2); if (artform-->acode ~= artform-->(acode+3)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms == 3); if (artform-->acode ~= artform-->(acode+3)) findout = true; if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms == 4); if (artform-->acode ~= artform-->(acode+3)) findout = true; if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true; if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms > 4); findout = true; #Endif; ! LanguageContractionForms #Ifdef TARGET_ZCODE; if (standard_interpreter && findout) { StorageForShortName-->0 = SHORTNAMEBUF_LEN; @output_stream 3 StorageForShortName; if (pluralise) print (number) pluralise; else print (PSN__) o; @output_stream -3; acode = acode + 3*LanguageContraction(StorageForShortName + 2); } #Ifnot; ! TARGET_GLULX if (findout) { if (pluralise) PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, EnglishNumber, pluralise); else PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); acode = acode + 3*LanguageContraction(StorageForShortName); } #Endif; ! TARGET_ CapitRule (artform-->acode, ~~capitalise); ! print article if (pluralise) return; print (PSN__) o; ]; [ PSN__ o; if (o == 0) { print (string) NOTHING__TX; rtrue; } switch (metaclass(o)) { Routine: print ""; rtrue; String: print ""; rtrue; nothing: print ""; rtrue; } #Ifdef LanguagePrintShortName; if (LanguagePrintShortName(o)) rtrue; #Endif; ! LanguagePrintShortName if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue; if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue; print (object) o; ]; [ Indefart o saveIndef; saveIndef = indef_mode; indef_mode = true; caps_mode = false; if (o has proper) { indef_mode = NULL; print (PSN__) o; indef_mode = saveIndef; return; } if (o provides article) { PrintOrRun(o, article, 1); print " ", (PSN__) o; indef_mode = saveIndef; return; } PrefaceByArticle(o, 2); indef_mode = saveIndef; ]; [ CInDefArt o saveIndef saveCaps; saveIndef = indef_mode; indef_mode = true; saveCaps = caps_mode; caps_mode = true; if (o has proper) { indef_mode = NULL; PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); PrintFromBuffer(StorageForShortName, true, caps_mode); caps_mode = saveCaps; return; } if (o provides article) { PrintCapitalised(o, article, true); print " ", (PSN__) o; indef_mode = saveIndef; caps_mode = saveCaps; return; } PrefaceByArticle(o, 2, 0, 1); caps_mode = saveCaps; indef_mode = saveIndef; ]; [ Defart o saveIndef; saveIndef = indef_mode; indef_mode = false; caps_mode = false; if ((~~o ofclass Object) || o has proper) { indef_mode = NULL; if (o == player) { if (player provides narrative_voice) { switch (player.narrative_voice) { 1: print (string) MYSELF__TX; 2: print (string) YOURSELF__TX; 3: print (PSN__) o; default: RunTimeError(16, player.narrative_voice); } } else ThatOrThose(player); } else { print (PSN__) o; } indef_mode = saveIndef; return; } PrefaceByArticle(o, 1); indef_mode = saveIndef; ]; [ CDefart o saveIndef saveCaps; saveIndef = indef_mode; indef_mode = false; saveCaps = caps_mode; caps_mode = true; if (~~o ofclass Object) { indef_mode = NULL; print (PSN__) o; } else if (o has proper) { indef_mode = NULL; PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); PrintFromBuffer(StorageForShortName, true, caps_mode); } else PrefaceByArticle(o, 0); indef_mode = saveIndef; caps_mode = saveCaps; ]; [ PrintShortName o saveIndef; saveIndef = indef_mode; indef_mode = NULL; PSN__(o); indef_mode = saveIndef; ]; [ EnglishNumber n; LanguageNumber(n); ]; [ SerialComma n; #Ifdef SERIAL_COMMAS; if (n>2) print ","; #Endif; n=0; ! quell unused n variable warning ]; [ NumberWord o i n; n = LanguageNumbers-->0; for (i=1 : i<=n : i=i+2) if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1); return 0; ]; [ RandomEntry tab; if (tab-->0 == 0) return RunTimeError(8); return tab-->(random(tab-->0)); ]; ! ---------------------------------------------------------------------------- ! Useful routine: unsigned comparison (for addresses in Z-machine) ! Returns 1 if x>y, 0 if x=y, -1 if x= 0) return 1; if (x >= 0 && y < 0) return -1; u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT; if (u > v) return 1; return -1; ]; ! ============================================================================== #Ifdef NITFOL_HOOKS; ! Code contributed by Evin Robertson #Ifdef TARGET_GLULX; ! Might be nice for Z-machine games too, ! but I'm not going to try to make this work ! given #Ifdef funniness. Array magic_array --> ! This is so nitfol can do typo correction / ! automapping / debugging on Glulx games $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc' 10 refers to length Magic_Global_Dispatch__ DI__check_word ! DI__check_word(buf, length) PrintShortName WV__Pr RV__Pr CA__Pr ! obj.prop = x; x = obj.prop; obj.prop(x) RA__Pr RL__Pr RA__Sc ! obj.∝ obj.#prop; class::prop OP__Pr OC__Cl ! obj provides prop; obj ofclass class OB__Move OB__Remove OB__Parent__ OB__Child__ OB__Sibling__ ! No explicit veneer for these ; [ OB__Parent__ obj; return parent(obj); ]; [ OB__Child__ obj; return child(obj); ]; [ OB__Sibling__ obj; return sibling(obj); ]; [ Magic_Global_Dispatch__ glbl; switch (glbl) { 0: if (location == TheDark) return real_location; return location; 1: return Player; -1: return CompassDirection::number; ! Silliness to make exist RA__Sc ! Should never be called. } return magic_array; ! Silences a warning. ]; [ DI__check_word buf wlen ix val res dictlen entrylen; ! Just like in Tokenise__. In fact, Tokenise__ could call this if ! it wanted, instead of doing this itself. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE; for (ix=0 : ixix = glk_char_to_lower(buf->ix); } for (: ixix = 0; } val = #dictionary_table + WORDSIZE; entrylen = DICT_WORD_SIZE + 7; @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res; return res; ]; #Endif; ! TARGET_ #Endif; ! NITFOL_HOOKS ! ============================================================================== Object LibraryExtensions "(Library Extensions)" with RunAll [ prop a1 a2 a3 obj rval max; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval > max) max = rval; if (self.BetweenCalls) self.BetweenCalls(); } return max; ], RunUntil [ prop exitval a1 a2 a3 obj rval; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval == exitval) return rval; if (self.BetweenCalls) self.BetweenCalls(); } return ~~exitval; ], RunWhile [ prop exitval a1 a2 a3 obj rval; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval ~= exitval) return rval; if (self.BetweenCalls) self.BetweenCalls(); } return exitval; ], ext_number_1 0, ! general temporary workspace ! can be set to a function (e.g. RestoreWN) meant for execution ! after non-terminating calls to extension objects ! (via RunUntil/While/All) BetweenCalls 0, RestoreWN [; wn = self.ext_number_1; ], ! Special interception points ext_messages 0, ! Called if LibraryMessages.before() ! returns false ! Extensions run while they return false ! Cross-platform entry points ! Called/Runs ext_afterlife 0, ! [C2/R1] ext_afterprompt 0, ! [C2/R1] ext_amusing 0, ! [C2/R1] ext_beforeparsing 0, ! [C2/R2] ext_chooseobjects 0, ! [C2/R2] ext_darktodark 0, ! [C2/R1] ext_deathmessage 0, ! [C2/R1] ext_gamepostroutine 0, ! [C2/R2] ext_gamepreroutine 0, ! [C2/R2] ext_initialise 0, ! [C1/R1] ext_inscope 0, ! [C2/R2] ext_lookroutine 0, ! [C2/R1] ext_newroom 0, ! [C2/R1] ext_objectdoesnotfit 0, ! [C2/R2] ext_parsenoun 0, ! [C3/R3] ext_parsenumber 0, ! [C2/R2] ext_parsererror 0, ! [C2/R2] ext_printrank 0, ! [C2/R1] ext_printtaskname 0, ! [C2/R1] ext_printverb 0, ! [C2/R2] ext_timepasses 0, ! [C2/R1] ext_unknownverb 0, ! [C2/R2] ! [C1] = Called in all cases ! [C2] = Called if EP is undefined, or returns false ! [C3] = called if EP is undefined, or returns -1 ! [R1] = All extensions run ! [R2] = Extensions run while they return false ! [R3] = Extensions run while they return -1 #Ifdef TARGET_GLULX; ! Glulx entry points ! Called: Runs: ext_handleglkevent 0, ! if EP undefined while extensions return false ext_identifyglkobject 0, ! if EP undefined while extensions return false ext_initglkwindow 0, ! if EP undefined while extensions return false #Endif; ! TARGET_GLULX; has proper; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_PARSER; #Ifnot; Message "Warning: 'parser' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; ! ============================================================================== ! ============================================================================== ! ENGLISH: Language Definition File ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! This file is automatically Included in your game file by "parserm". ! Strictly, "parserm" includes the file named in the "language__" variable, ! whose contents can be defined by+language_name=XXX compiler setting (with a ! default of "english"). ! ! Define the constant DIALECT_US before including "Parser" to obtain American ! English. ! ============================================================================== System_file; #Ifndef LIBRARY_ENGLISH; ! if this file is already included, ! don't try to include it again. ! ------------------------------------------------------------------------------ ! Part I. Preliminaries ! ------------------------------------------------------------------------------ Constant EnglishNaturalLanguage; ! Needed to keep old pronouns mechanism Class CompassDirection with number 0, article "the", description [; if (location provides compass_look && location.compass_look(self)) rtrue; if (self.compass_look()) rtrue; L__M(##Look, 7, self); ], compass_look false, parse_name [; return -1; ] has scenery; Object Compass "compass" has concealed; #Ifndef WITHOUT_DIRECTIONS; CompassDirection -> n_obj with short_name "north", door_dir n_to, name 'n//' 'north'; CompassDirection -> s_obj with short_name "south", door_dir s_to, name 's//' 'south'; CompassDirection -> e_obj with short_name "east", door_dir e_to, name 'e//' 'east'; CompassDirection -> w_obj with short_name "west", door_dir w_to, name 'w//' 'west'; CompassDirection -> ne_obj with short_name "northeast", door_dir ne_to, name 'ne' 'northeast'; CompassDirection -> nw_obj with short_name "northwest", door_dir nw_to, name 'nw' 'northwest'; CompassDirection -> se_obj with short_name "southeast", door_dir se_to, name 'se' 'southeast'; CompassDirection -> sw_obj with short_name "southwest", door_dir sw_to, name 'sw' 'southwest'; CompassDirection -> u_obj with short_name "up above", door_dir u_to, name 'u//' 'up' 'ceiling' 'above' 'sky'; CompassDirection -> d_obj with short_name "ground", door_dir d_to, name 'd//' 'down' 'floor' 'below' 'ground'; #endif; ! WITHOUT_DIRECTIONS CompassDirection -> in_obj with short_name "inside", door_dir in_to; CompassDirection -> out_obj with short_name "outside", door_dir out_to; ! ------------------------------------------------------------------------------ ! Part II. Vocabulary ! ------------------------------------------------------------------------------ Constant AGAIN1__WD = 'again'; Constant AGAIN2__WD = 'g//'; Constant AGAIN3__WD = 'again'; Constant OOPS1__WD = 'oops'; Constant OOPS2__WD = 'o//'; Constant OOPS3__WD = 'oops'; Constant UNDO1__WD = 'undo'; Constant UNDO2__WD = 'undo'; Constant UNDO3__WD = 'undo'; Constant ALL1__WD = 'all'; Constant ALL2__WD = 'each'; Constant ALL3__WD = 'every'; Constant ALL4__WD = 'everything'; Constant ALL5__WD = 'both'; Constant AND1__WD = 'and'; Constant AND2__WD = 'and'; Constant AND3__WD = 'and'; Constant BUT1__WD = 'but'; Constant BUT2__WD = 'except'; Constant BUT3__WD = 'but'; Constant ME1__WD = 'me'; Constant ME2__WD = 'myself'; Constant ME3__WD = 'self'; Constant OF1__WD = 'of'; Constant OF2__WD = 'of'; Constant OF3__WD = 'of'; Constant OF4__WD = 'of'; Constant OTHER1__WD = 'another'; Constant OTHER2__WD = 'other'; Constant OTHER3__WD = 'other'; Constant THEN1__WD = 'then'; Constant THEN2__WD = 'then'; Constant THEN3__WD = 'then'; Constant NO1__WD = 'n//'; Constant NO2__WD = 'no'; Constant NO3__WD = 'no'; Constant YES1__WD = 'y//'; Constant YES2__WD = 'yes'; Constant YES3__WD = 'yes'; Constant AMUSING__WD = 'amusing'; Constant FULLSCORE1__WD = 'fullscore'; Constant FULLSCORE2__WD = 'full'; Constant QUIT1__WD = 'q//'; Constant QUIT2__WD = 'quit'; Constant RESTART__WD = 'restart'; Constant RESTORE__WD = 'restore'; Array LanguagePronouns table ! word possible GNAs connected ! to follow: to: ! a i ! s p s p ! mfnmfnmfnmfn 'it' $$001000111000 NULL 'him' $$100000100000 NULL 'her' $$010000010000 NULL 'them' $$000111000111 NULL; Array LanguageDescriptors table ! word possible GNAs descriptor connected ! to follow: type: to: ! a i ! s p s p ! mfnmfnmfnmfn 'my' $$111111111111 POSSESS_PK 0 'this' $$111111111111 POSSESS_PK 0 'these' $$000111000111 POSSESS_PK 0 'that' $$111111111111 POSSESS_PK 1 'those' $$000111000111 POSSESS_PK 1 'his' $$111111111111 POSSESS_PK 'him' 'her' $$111111111111 POSSESS_PK 'her' 'their' $$111111111111 POSSESS_PK 'them' 'its' $$111111111111 POSSESS_PK 'it' 'the' $$111111111111 DEFART_PK NULL 'a//' $$111000111000 INDEFART_PK NULL 'an' $$111000111000 INDEFART_PK NULL 'some' $$000111000111 INDEFART_PK NULL 'lit' $$111111111111 light NULL 'lighted' $$111111111111 light NULL 'unlit' $$111111111111 (-light) NULL; Array LanguageNumbers table 'one' 1 'two' 2 'three' 3 'four' 4 'five' 5 'six' 6 'seven' 7 'eight' 8 'nine' 9 'ten' 10 'eleven' 11 'twelve' 12 'thirteen' 13 'fourteen' 14 'fifteen' 15 'sixteen' 16 'seventeen' 17 'eighteen' 18 'nineteen' 19 'twenty' 20; ! ------------------------------------------------------------------------------ ! Part III. Translation ! ------------------------------------------------------------------------------ [ LanguageToInformese; ]; ! ------------------------------------------------------------------------------ ! Part IV. Printing ! ------------------------------------------------------------------------------ Constant LanguageAnimateGender = male; Constant LanguageInanimateGender = neuter; Constant LanguageContractionForms = 2; ! English has two: ! 0 = starting with a consonant ! 1 = starting with a vowel [ LanguageContraction text; if (text->0 == 'a' or 'e' or 'i' or 'o' or 'u' or 'A' or 'E' or 'I' or 'O' or 'U') return 1; return 0; ]; Array LanguageArticles --> ! Contraction form 0: Contraction form 1: ! Cdef Def Indef Cdef Def Indef "The " "the " "a " "The " "the " "an " ! Articles 0 "The " "the " "some " "The " "the " "some "; ! Articles 1 ! a i ! s p s p ! m f n m f n m f n m f n Array LanguageGNAsToArticles --> 0 0 0 1 1 1 0 0 0 1 1 1; [ LanguageDirection d; switch (d) { n_to: print "north"; s_to: print "south"; e_to: print "east"; w_to: print "west"; ne_to: print "northeast"; nw_to: print "northwest"; se_to: print "southeast"; sw_to: print "southwest"; u_to: print "up"; d_to: print "down"; in_to: print "in"; out_to: print "out"; default: return RunTimeError(9,d); } ]; [ LanguageNumber n f; if (n == 0) { print "zero"; rfalse; } if (n < 0) { print "minus "; n = -n; } if (n >= 1000) { print (LanguageNumber) n/1000, " thousand"; n = n%1000; f = 1; } if (n >= 100) { if (f == 1) print ", "; print (LanguageNumber) n/100, " hundred"; n = n%100; f = 1; } if (n == 0) rfalse; #Ifdef DIALECT_US; if (f == 1) print " "; #Ifnot; if (f == 1) print " and "; #Endif; switch (n) { 1: print "one"; 2: print "two"; 3: print "three"; 4: print "four"; 5: print "five"; 6: print "six"; 7: print "seven"; 8: print "eight"; 9: print "nine"; 10: print "ten"; 11: print "eleven"; 12: print "twelve"; 13: print "thirteen"; 14: print "fourteen"; 15: print "fifteen"; 16: print "sixteen"; 17: print "seventeen"; 18: print "eighteen"; 19: print "nineteen"; 20 to 99: switch (n/10) { 2: print "twenty"; 3: print "thirty"; 4: print "forty"; 5: print "fifty"; 6: print "sixty"; 7: print "seventy"; 8: print "eighty"; 9: print "ninety"; } if (n%10 ~= 0) print "-", (LanguageNumber) n%10; } ]; [ LanguageTimeOfDay hours mins i; i = hours%12; if (i == 0) i = 12; if (i < 10) print " "; print i, ":", mins/10, mins%10; if ((hours/12) > 0) print " pm"; else print " am"; ]; [ LanguageVerb i; switch (i) { 'i//','inv','inventory': print "take inventory"; 'l//': print "look"; 'x//': print "examine"; 'z//': print "wait"; default: rfalse; } rtrue; ]; ! ---------------------------------------------------------------------------- ! LanguageVerbIsDebugging is called by SearchScope. It should return true ! if word w is a debugging verb which needs all objects to be in scope. ! ---------------------------------------------------------------------------- #Ifdef DEBUG; [ LanguageVerbIsDebugging w; if (w == 'purloin' or 'tree' or 'abstract' or 'gonear' or 'scope' or 'showobj') rtrue; rfalse; ]; #Endif; ! ---------------------------------------------------------------------------- ! LanguageVerbLikesAdverb is called by PrintCommand when printing an UPTO_PE ! error or an inference message. Words which are intransitive verbs, i.e., ! which require a direction name as an adverb ('walk west'), not a noun ! ('I only understood you as far as wanting to touch /the/ ground'), should ! cause the routine to return true. ! ---------------------------------------------------------------------------- [ LanguageVerbLikesAdverb w; if (w == 'look' or 'go' or 'push' or 'walk') rtrue; rfalse; ]; ! ---------------------------------------------------------------------------- ! LanguageVerbMayBeName is called by NounDomain when dealing with the ! player's reply to a "Which do you mean, the short stick or the long ! stick?" prompt from the parser. If the reply is another verb (for example, ! LOOK) then then previous ambiguous command is discarded /unless/ ! it is one of these words which could be both a verb /and/ an ! adjective in a 'name' property. ! ---------------------------------------------------------------------------- [ LanguageVerbMayBeName w; if (w == 'long' or 'short' or 'normal' or 'brief' or 'full' or 'verbose') rtrue; rfalse; ]; Constant NKEY__TX = "N = next subject"; Constant PKEY__TX = "P = previous"; Constant QKEY1__TX = " Q = resume game"; Constant QKEY2__TX = "Q = previous menu"; Constant RKEY__TX = "RETURN = read subject"; Constant NKEY1__KY = 'N'; Constant NKEY2__KY = 'n'; Constant PKEY1__KY = 'P'; Constant PKEY2__KY = 'p'; Constant QKEY1__KY = 'Q'; Constant QKEY2__KY = 'q'; Constant SCORE__TX = "Score: "; Constant MOVES__TX = "Moves: "; Constant TIME__TX = "Time: "; Constant CANTGO__TX = "You can't go that way."; Constant FORMER__TX = "your former self"; Constant MYFORMER__TX = "my former self"; Constant YOURSELF__TX = "yourself"; Constant MYSELF__TX = "myself"; Constant YOU__TX = "You"; Constant DARKNESS__TX = "Darkness"; Constant THOSET__TX = "those things"; Constant THAT__TX = "that"; Constant OR__TX = " or "; Constant NOTHING__TX = "nothing"; Constant IS__TX = " is"; Constant ARE__TX = " are"; Constant IS2__TX = "is "; Constant ARE2__TX = "are "; Constant WAS__TX = " was"; Constant WERE__TX = " were"; Constant WAS2__TX = "was "; Constant WERE2__TX = "were "; Constant AND__TX = " and "; Constant WHOM__TX = "whom "; Constant WHICH__TX = "which "; Constant COMMA__TX = ", "; Constant COLON__TX = ": "; ! ---------------------------------------------------------------------------- ! FYI on nominative pronouns versus accusative pronouns... ! Consider the sentence "She hit him.". ! "She" is in the nominative case. It appears at the beginning of a sentence. ! "him" is in the accusative case. It won't appear at the beginning. ! ---------------------------------------------------------------------------- ! Accusative [ ThatOrThose obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "me"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "you"; return; } if (obj has pluralname) { print "those"; return; } if (obj has female) { print "her"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "him"; return; } print "that"; ]; ! Accusative [ ItOrThem obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "myself"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "yourself"; return; } if (obj has pluralname) { print "them"; return; } if (obj has female) { print "her"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "him"; return; } print "it"; ]; ! Nominative [ CThatOrThose obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "I"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "You"; return; } if (obj has pluralname) { print "Those"; return; } if (obj has female) { print "She"; return; } if (obj has male or animate) { if (obj hasnt neuter) { print "He"; return; } } print "That"; ]; ! Nominative [ CTheyreorThats obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { Tense("I'm", "I was"); return; } if (player.narrative_voice == 3) { CDefart(player); Tense("'s", " was"); return; } } Tense("You're", "You were"); return; } if (obj has pluralname) { Tense("They're", "They were"); return; } if (obj has female) { Tense("She's", "She was"); return; } if (obj has male or animate) { if (obj hasnt neuter) { Tense("He's", "He was"); return; } } Tense("That's", "That was"); ]; [ IsOrAre obj; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { if (obj has pluralname || obj == player) print "were"; else print "was"; return; } if (obj has pluralname || obj == player) print "are"; else print "is"; return; ]; [ nop x; x = x; ]; ! print rule to absorb unwanted return value [ SubjectNotPlayer obj reportage v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v2 = past; v3 = past; } if (reportage && actor ~= player) { L__M(##Miscellany, 60, actor); if (obj == actor) { print (theActor) obj, " ", (string) v3; return; } else if (obj has pluralname) { print (the) obj, " ", (string) v2; return; } else {print (the) obj, " ", (string) v3; return;} } else if (obj has pluralname) { print (The) obj, " ", (string) v2; return;} else { print (The) obj, " ", (string) v3; return;} ]; [ CSubjectVoice obj v1 v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v1 = past; v2 = past; v3 = past; } else { if (v2 == 0) v2 = v1; if (v3 == 0) v3 = v1; } if (obj ~= player) { print (string) v3; return; } if (player provides narrative_voice) switch (player.narrative_voice) { 1: print (string) v1; return; 2: ! Do nothing. 3: print (string) v3; return; default: RunTimeError(16, player.narrative_voice); } print (string) v2; return; ]; [ CSubjectVerb obj reportage nocaps v1 v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v1 = past; v2 = past; v3 = past; } else { if (v2 == 0) v2 = v1; if (v3 == 0) v3 = v1; } if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: print "I ", (string) v1; return; 2: ! Do nothing. 3: CDefart(player); print " ", (string) v3; return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) { print "you ", (string) v2; return; } print "You ", (string) v2; return; } SubjectNotPlayer(obj, reportage, v2, v3); ]; [ CSubjectIs obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'm", "I was"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" is", " was"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you're", "you were"); else Tense("You're", "You were"); return; } SubjectNotPlayer(obj, reportage, "are", "is", "was"); ]; [ CSubjectIsnt obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'm not", "I wasn't"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" isn't", " wasn't"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you aren't", "you weren't"); else Tense("You aren't", "You weren't"); return; } SubjectNotPlayer(obj, reportage, "aren't", "isn't", "wasn't"); ]; [ CSubjectHas obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I've", "I had"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" has", " had"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you've", "you'd"); else Tense("You've", "You'd"); return; } SubjectNotPlayer(obj, reportage, "have", "has", "had"); ]; [ CSubjectWill obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'll", "I would've"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" will", " would've"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you'll", "you'd"); else Tense("You'll", "You'd"); return; } SubjectNotPlayer(obj, reportage, "will", "will", "would"); ]; [ CSubjectCan obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "can", 0, "can", "could"); ]; [ CSubjectCant obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "can't", 0, "can't", "couldn't"); ]; [ CSubjectDont obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "don't", 0, "doesn't", "didn't"); ]; [ OnesSelf obj; if (obj == player) { if (player provides narrative_voice) switch(player.narrative_voice) { 1: print (string) MYSELF__TX; return; 2: ! Do nothing. 3: if (obj has female) {print "herself"; return;} print "himself"; return; default: RunTimeError(16, player.narrative_voice); } print "yourself"; return; } if (obj has male) { print "himself"; return; } if (obj has female) {print "herself"; return; } print "itself"; return; ]; [ Possessive obj caps; if (obj == player) { if (player provides narrative_voice) switch(player.narrative_voice) { 1: if (caps) print "M"; else print "m"; print "y"; return; 2: ! Do nothing. 3: CDefart(player); print "'s"; return; default: RunTimeError(16, player.narrative_voice); } if (caps) print "Y"; else print "y"; print "our"; return; } if (caps) print "H"; else print "h"; if (obj has male) { print "is"; return; } if (obj has female) { print "er"; return; } if (caps) print "I"; else { print "i"; print "ts"; return; } ]; [ PossessiveCaps obj; Possessive(obj, true); ]; [ theActor obj; if (obj == player) { if (obj provides narrative_voice) { switch (obj.narrative_voice) { 1: print "I"; return; 2: ! Do nothing. 3: if (obj has neuter) { print "it"; return; } if (obj has female) { print "she"; return; } print "he"; return; default: RunTimeError(16, player.narrative_voice); } } print "you"; return; } if (obj has pluralname) { print "they"; return; } if (obj has female) { print "she"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "he"; return; } print "that"; ]; [ SupportObj obj s1 s2; if (obj has supporter) print (string) s1; else print (string) s2; ]; [ PluralObj obj s1 s2 past; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { print (string) past; return; } if (obj has pluralname) print (string) s1; else print (string) s2; ]; ! ---------------------------------------------------------------------------- ! Tense is a little helper function to present the correct tense of a ! verb. The first parameter is the verb in present tense. The second ! parameter is the verb in past tense. If the second parameter is ! omitted, then nothing will be printed if the appropriate tense is past. ! ---------------------------------------------------------------------------- [ Tense present past; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { if (past == false) return; print (string) past; } else print (string) present; ]; [ DecideAgainst; CSubjectVerb(actor, false, false, "decide",0,"decides","decided"); print " that"; Tense("'s not", " wasn't"); " such a good idea."; ]; #Ifdef TARGET_ZCODE; [ LowerCase c; ! for ZSCII matching ISO 8859-1 switch (c) { 'A' to 'Z': c = c + 32; 202, 204, 212, 214, 221: c--; 217, 218: c = c - 2; 158 to 160, 167, 168, 208 to 210: c = c - 3; 186 to 190, 196 to 200: c = c - 5 ; 175 to 180: c = c - 6; } return c; ]; [ UpperCase c; ! for ZSCII matching ISO 8859-1 switch (c) { 'a' to 'z': c = c - 32; 201, 203, 211, 213, 220: c++; 215, 216: c = c + 2; 155 to 157, 164, 165, 205 to 207: c = c + 3; 181 to 185, 191 to 195: c = c + 5 ; 169 to 174: c = c + 6; } return c; ]; #Ifnot; ! TARGET_GLULX [ LowerCase c; return glk_char_to_lower(c); ]; [ UpperCase c; return glk_char_to_upper(c); ]; #Endif; ! TARGET_ [ LanguageLM n x1 x2; Answer,Ask: print "There "; Tense("is", "was"); " no reply."; ! Ask: see Answer Attack: print "Violence "; Tense("isn't", "wasn't"); " the answer to this one."; Blow: CSubjectCant(actor,true); " usefully blow ", (thatorthose) x1, "."; Burn: switch (n) { 1: print "This dangerous act would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Buy: print "Nothing "; Tense("is", "was"); " on sale."; Climb: switch (n) { 1: print "Climbing ", (ThatOrThose) x1, " would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Close: switch (n) { 1: CSubjectIs(x1,true); print " not something ", (theActor) actor; Tense(" can close", " could have closed"); "."; 2: CSubjectIs(x1,true); " already closed."; 3: CSubjectVerb(actor,false,false,"close",0,"closes","closed"); " ", (the) x1, "."; 4: "(first closing ", (the) x1, ")"; } CommandsOff: switch (n) { 1: "[Command recording off.]"; #Ifdef TARGET_GLULX; 2: "[Command recording already off.]"; #Endif; ! TARGET_ } CommandsOn: switch (n) { 1: "[Command recording on.]"; #Ifdef TARGET_GLULX; 2: "[Commands are currently replaying.]"; 3: "[Command recording already on.]"; 4: "[Command recording failed.]"; #Endif; ! TARGET_ } CommandsRead: switch (n) { 1: "[Replaying commands.]"; #Ifdef TARGET_GLULX; 2: "[Commands are already replaying.]"; 3: "[Command replay failed. Command recording is on.]"; 4: "[Command replay failed.]"; 5: "[Command replay complete.]"; #Endif; ! TARGET_ } Consult: CSubjectVerb(actor,true,false,"discover",0,"discovers","discovered"); print " nothing of interest in "; if (x1 == player) { OnesSelf(x1); ".";} else print_ret (the) x1, "."; Cut: switch (n) { 1: print "Cutting ", (ThatOrThose) x1, " up would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Dig: print "Digging would "; Tense("achieve", "have achieved"); " nothing here."; Disrobe: switch (n) { 1: CSubjectIsnt(actor,true); " wearing ", (ThatOrThose) x1, "."; 2: CSubjectVerb(actor,false,false,"take off",0,"takes off", "took off"); " ", (the) x1, "."; } Drink: print "There"; Tense("'s", " was"); " nothing suitable to drink here."; Drop: switch (n) { 1: CSubjectIs(x1,true); " already here."; 2: CSubjectVerb(actor, false, false, "haven't got", 0, "hasn't got", "didn't have"); " ", (the) x1, "."; 3: "(first taking ", (the) x1, " off)"; 4: "Dropped."; } Eat: switch (n) { 1: CSubjectIs(x1,true); " plainly inedible."; 2: CSubjectVerb(actor,false,false,"eat",0,"eats", "ate"); print " ", (the) x1; if (actor == player) ". Not bad."; else "."; } EmptyT: switch (n) { 1: CSubjectCant(x1,true); " contain things."; 2: CSubjectIs(x1,true); " closed."; 3: CSubjectIs(x1,true); " empty already."; 4: print "That wouldn't "; Tense("empty", "have emptied"); " anything."; } Enter: switch (n) { 1: print "But "; CSubjectIs(actor,true,true); " already ", (nop) SupportObj(x1,"on ","in "), (the) x1, "."; 2: CSubjectIs(x1,true); print " not something ", (theActor) actor; Tense(" can ", " could "); switch (x2) { 'stand': "stand on."; 'sit': "sit down on."; 'lie': "lie down on."; default: "enter."; } 3: CSubjectCant(actor,true); " get into the closed ", (name) x1, "."; 4: CSubjectCan(actor,true); " only get into something free-standing."; 5: CSubjectVerb(actor,false,false,"get",0,"gets","got"); SupportObj(x1," onto"," into"); " ", (the) x1, "."; 6: "(getting ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, ")"; 7: if (x1 has supporter) "(getting onto ", (the) x1, ")"; if (x1 has container) "(getting into ", (the) x1, ")"; "(entering ", (the) x1, ")"; } Examine: switch (n) { 1: "Darkness, noun. An absence of light to see by."; 2: CSubjectVerb(actor,true,false,"see",0,"sees","saw"); " nothing special about ", (the) x1, "."; 3: CSubjectIs(x1,true); Tense(" currently"); print " switched "; if (x1 has on) "on."; else "off."; } Exit: switch (n) { 1: print "But "; CSubjectIsnt(actor,true,true); " in anything at the moment."; 2: CSubjectCant(actor,false); " get out of the closed ", (name) x1, "."; 3: CSubjectVerb(actor,false,false,"get",0,"gets", "got"); print " "; SupportObj(x1,"off","out of"); " ", (the) x1, "."; 4: CSubjectIsnt(actor,true); print " "; SupportObj(x1,"on","in"); " ", (the) x1, "."; 5: "(first getting ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, ")"; 6: CSubjectVerb(actor,false,false,"stand",0,"stands","stood"); " up."; } Fill: switch (n) { 1: print "There "; Tense("isn't", "wasn't"); " anything obvious with which to fill ", (the) x1, "."; 2: print "Filling ", (the) x1, " from ", (the) x2; Tense(" doesn't", " didn't"); " make sense."; } FullScore: switch (n) { 1: if (deadflag) print "The score was "; else print "The score is "; "made up as follows:^"; 2: "finding sundry items"; 3: "visiting various places"; 4: print "total (out of ", MAX_SCORE; ")"; } GetOff: print "But "; CSubjectIsnt(actor,true,true); " on ", (the) x1, " at the moment."; Give: switch (n) { 1: CSubjectIsnt(actor,true); " holding ", (the) x1, "."; 2: CSubjectVerb(actor,false,false,"juggle",0,"juggles","juggled"); print " ", (the) x1, " for a while, but "; CSubjectVoice(actor,"don't","don't","doesn't","didn't"); " achieve much."; 3: CSubjectDont(x1,true); " seem interested."; 4: CSubjectVerb(actor,false,false,"hand over",0,"hands over","handed over"); " ", (the) x1, "."; } Go: switch (n) { 1: CSubjectWill(actor,true); Tense(" have", " had"); " to get ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, " first."; 2: CSubjectCant(actor,true); " go that way."; 3: CSubjectIs (actor,true); " unable to climb ", (the) x1, "."; 4: CSubjectIs (actor,true); " unable to descend by ", (the) x1, "."; 5: CSubjectCant(actor,true); " since ", (the) x1, " ", (IsOrAre) x1, " in the way."; 6: CSubjectCant(actor,true); " since ", (the) x1, " ", (nop) PluralObj(x1,"lead","leads","led"), " nowhere."; 7: CSubjectVerb(actor,false,false,"depart",0,"departs","departed"); "."; } Insert: switch (n) { 1: CSubjectVerb(actor,true,false,"need",0,"needs","needed"); print " to be holding ", (the) x1, " before ", (theActor) actor; Tense(" can", " could"); " put ", (ItOrThem) x1, " into something else."; 2: CSubjectCant(x1,true); " contain things."; 3: CSubjectIs (x1,true); " closed."; 4: CSubjectWill(actor,true); Tense(" need", " needed"); " to take ", (ItOrThem) x1, " off first."; 5: CSubjectCant(actor,true); " put something inside itself."; 6: "(first taking ", (ItOrThem) x1, " off)"; 7: print "There "; Tense(" is", " was"); " no more room in ", (the) x1, "."; 8: "Done."; 9: CSubjectVerb(actor,false,false,"put",0,"puts","put"); " ", (the) x1, " into ", (the) x2, "."; } Inv: switch (n) { 1: CSubjectIs (actor,false); " carrying nothing."; 2: CSubjectIs (actor,false); print " carrying"; 3: ":"; 4: "."; } Jump: CSubjectVerb(actor,false,false,"jump",0,"jumps","jumped"); " on the spot, fruitlessly."; JumpIn: print "Jumping in ", (the) x1, " "; Tense("would achieve", "would have achieved"); " nothing here."; JumpOn: print "Jumping upon ", (the) x1, " "; Tense("would achieve", "would have achieved"); " nothing here."; JumpOver: switch (n) { 1: CSubjectVerb(actor,true,false,"achieve",0,"achieve","achieved"); " nothing by this."; 2: DecideAgainst(); } Kiss: "Keep your mind on the game."; Listen: CSubjectVerb(actor,true,false,"hear",0,"hears","heard"); " nothing unexpected."; ListMiscellany: switch (n) { 1: print " (providing light)"; 2: print " (which ", (IsOrAre) x1, " closed)"; 3: print " (closed and providing light)"; 4: print " (which ", (IsOrAre) x1, " empty)"; 5: print " (empty and providing light)"; 6: print " (which ", (IsOrAre) x1, " closed and empty)"; 7: print " (closed, empty and providing light)"; 8: print " (providing light and being worn"; 9: print " (providing light"; 10: print " (being worn"; 11: print " (which ", (IsOrAre) x1, " "; 12: print "open"; 13: print "open but empty"; 14: print "closed"; 15: print "closed and locked"; 16: print " and empty"; 17: print " (which ", (IsOrAre) x1, " empty)"; 18: print " containing "; 19: print " (on "; 20: print ", on top of "; 21: print " (in "; 22: print ", inside "; } LMode1: print (string) Story, " is now in its "; if (initial_lookmode == 1) print "normal "; "~brief~ printing mode, which gives long descriptions of places never before visited and short descriptions otherwise."; LMode2: print (string) Story, " is now in its "; if (initial_lookmode ~= 1 or 3) print "normal "; "~verbose~ mode, which always gives long descriptions of locations (even if you've been there before)."; LMode3: print (string) Story, " is now in its "; if (initial_lookmode == 3) print "normal "; "~superbrief~ mode, which always gives short descriptions of locations (even if you haven't been there before)."; Lock: switch (n) { 1: CSubjectDont(x1,true); print " seem to be something ", (theActor) actor; Tense(" can", " could"); " lock."; 2: CSubjectIs (x1,true); " locked at the moment."; 3: CSubjectWill(actor,true); " first have to close ", (the) x1, "."; 4: CSubjectDont(x1,true); " seem to fit the lock."; 5: CSubjectVerb(actor,false,false,"lock",0,"locks","locked"); " ", (the) x1, "."; } Look: switch (n) { 1: print " (on ", (the) x1, ")"; 2: print " (in ", (the) x1, ")"; 3: print " (as ", (object) x1, ")"; 4: print "^On ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; 5,6: if (x1 ~= location) { if (x1 has supporter) print "^On "; else print "^In "; print (the) x1, " ", (theActor) actor, " "; Tense("can", "could"); } else { new_line; CSubjectCan(actor,false); } if (n == 5) print " also"; print " see "; WriteListFrom(child(x1), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT); if (x1 ~= location) "."; else " here."; 7: CSubjectVerb(actor,true,false,"see",0,"sees", "saw"); " nothing unexpected in that direction."; } LookUnder: switch (n) { 1: print "But it"; Tense("'s", " was"); " dark."; 2: CSubjectVerb(actor,true,false,"find",0,"finds", "found"); " nothing of interest."; } Mild: "Quite."; Miscellany: switch (n) { 1: "(considering the first sixteen objects only)^"; 2: "Nothing to do!"; 3: print " "; CSubjectVerb(player, false, false, "died", "have died", "has died"); print " "; 4: print " "; CSubjectVerb(player, false, false, "won", "have won", "has won"); print " "; 5: print "^Would you like to RESTART, RESTORE a saved game"; #Ifdef DEATH_MENTION_UNDO; print ", UNDO your last move"; #Endif; if (TASKS_PROVIDED == 0) print ", give the FULL score for that game"; if (deadflag == 2 && AMUSING_PROVIDED == 0) print ", see some suggestions for AMUSING things to do"; SerialComma(3); print " or QUIT?"; 6: "[Your interpreter does not provide ~undo~. Sorry!]"; #Ifdef TARGET_ZCODE; 7: "~Undo~ failed. [Not all interpreters provide it.]"; #Ifnot; ! TARGET_GLULX 7: "[You cannot ~undo~ any further.]"; #Endif; ! TARGET_ 8: "Please give one of the answers above."; 9: print "^It "; Tense("is now", "was"); print " pitch dark in "; Tense("here", "there"); "!"; 10: "I beg your pardon?"; 11: "[You can't ~undo~ what hasn't been done!]"; 12: "[Can't ~undo~ twice in succession. Sorry!]"; 13: "[Previous turn undone.]"; 14: "Sorry, that can't be corrected."; 15: "Think nothing of it."; 16: "~Oops~ can only correct a single word."; 17: print "It "; Tense("is", "was"); print " pitch dark, and ", (theActor) actor; Tense(" can't", " couldn't"); " see a thing."; 18: print "yourself"; 19: "As good-looking as ever."; 20: "To repeat a command like ~frog, jump~, just say ~again~, not ~frog, again~."; 21: CSubjectCan(actor,true); " hardly repeat that."; 22: CSubjectCant(actor, true); " begin with a comma."; 23: CSubjectVerb(actor, true, false, "seem", "seem", "seems", "seemed"); print " to want to talk to someone, but I "; Tense("can't", "couldn't"); " see whom."; 24: CSubjectCant(actor, true); " talk to ", (the) x1, "."; 25: "To talk to someone, try ~someone, hello~ or some such."; 26: "(first taking ", (the) x1, ")"; 27: "I didn't understand that sentence."; 28: print "I only understood you as far as wanting to "; 29: "I didn't understand that number."; 30: CSubjectCant(actor,true); " see any such thing."; 31: CSubjectVerb(actor, true, false, "seem", "seem", "seems", "seemed"); " to have said too little!"; 32: CSubjectIsnt(actor); " holding that!"; 33: "You can't use multiple objects with that verb."; 34: "You can only use multiple objects once on a line."; 35: "I'm not sure what ~", (address) x1, "~ refers to."; 36: "You excepted something not included anyway!"; 37: CSubjectCan(actor,true); " only do that to something animate."; #Ifdef DIALECT_US; 38: "That's not a verb I recognize."; #Ifnot; 38: "That's not a verb I recognise."; #Endif; 39: "That's not something you need to refer to in the course of this game."; 40: CSubjectCant(actor,true); " see ~", (address) x1, "~ (", (the) x2, ") at the moment."; 41: "I didn't understand the way that finished."; 42: if (x1 == 0) print "None"; else print "Only ", (number) x1; print " of those "; if (x1 == 1) print "is"; else print "are"; " available."; 43: "Nothing to do!"; 44: print "There "; Tense("is", "was"); " nothing to ", (address) x1, "."; 45: print "Who do you mean, "; 46: print "Which do you mean, "; 47: "Sorry, you can only have one item here. Which exactly?"; 48: print "Whom "; CSubjectVoice(player, "do", "do", "does", "did"); print " "; CSubjectVerb(player, false, true, "want", "want", "want", "want"); if (x1 ~= player && x1 ~= nothing) print " ", (the) x1; print " to "; PrintCommand(); "?"; 49: print "What "; CSubjectVoice(player, "do", "do", "does", "did"); print " "; CSubjectVerb(player, false, true, "want", "want", "want", "want"); if (x1 ~= player && x1 ~= nothing) print " ", (the) x1; print " to "; PrintCommand(); "?"; 50: print "The score has just gone "; if (x1 > 0) print "up"; else { x1 = -x1; print "down"; } print " by ", (number) x1, " point"; if (x1 > 1) print "s"; 51: "(Since something dramatic has happened, your list of commands has been cut short.)"; 52: "^Type a number from 1 to ", x1, ", 0 to redisplay or press ENTER."; 53: "^[Please press SPACE.]"; 54: "[Comment recorded.]"; 55: "[Comment NOT recorded.]"; 56: "."; 57: "?"; 58: "(first taking ", (the) x1, " ", (nop) SupportObj(x2,"off","out of"), " ", (the) x2, ")"; 59: "You'll have to be more specific."; 60: print (The) x1, " observes that "; } No,Yes: "That was a rhetorical question."; NotifyOff: "Score notification off."; NotifyOn: "Score notification on."; Objects: switch (n) { 1: "Objects ", (nop) CSubjectVerb(actor, false, true, "have", "have", "has"), " handled:^"; 2: "None."; 3: print " (worn)"; 4: print " (held)"; 5: print " (given away)"; 6: print " (in ", (name) x1, ")"; 7: print " (in ", (the) x1, ")"; 8: print " (inside ", (the) x1, ")"; 9: print " (on ", (the) x1, ")"; 10: print " (lost)"; } Open: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor; Tense(" can open", " could have opened"); "."; 2: CSubjectVerb(x1,true,false,"seem",0,"seems","seemed"); " to be locked."; 3: CSubjectIs (x1,true); " already open."; 4: CSubjectVerb(actor,false,false,"open",0,"opens","opened"); print " ", (the) x1; Tense(", revealing ", " and revealed "); if (WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT) == 0) "nothing."; "."; 5: CSubjectVerb(actor,false,false,"open",0,"opens","opened"); " ", (the) x1, "."; 6: "(first opening ", (the) x1, ")"; } Order: CSubjectHas(x1,false); " better things to do."; Places: switch (n) { 1: print "You have visited: "; 2: "."; } Pray: print "Nothing practical "; Tense("results", "resulted"); " from ", (Possessive) actor, " prayer."; Prompt: print "^>"; Pronouns: switch (n) { 1: print "At the moment, "; 2: print "means "; 3: print "is unset"; 4: "no pronouns are known to the game."; 5: "."; } Pull,Push,Turn: switch (n) { 1: if (player provides narrative_voice && player.narrative_voice == 3) { print_ret (The) player, " ", (nop) Tense("isn't", "wasn't"), " likely to help matters by punishing ", (OnesSelf) player, " that way."; } else { "Punishing ", (OnesSelf) player, " that way ", (nop) Tense("isn't", "wasn't"), " likely to help matters."; } 2: CSubjectIs (x1,true); " fixed in place."; 3: CSubjectIs (actor,true); " unable to."; 4: print "Nothing obvious "; Tense("happens", "happened"); "."; 5: print "That would "; Tense("be", "have been"); " less than courteous."; 6: DecideAgainst(); } ! Push: see Pull PushDir: switch (n) { 1: print "That really "; Tense("wouldn't", "didn't"); " serve any purpose."; 2: print "That's "; Tense("not", "wasn't"); " a direction."; 3: print "Not that way ", (theActor) actor; Tense(" can't", "couldn't"); "."; } PutOn: switch (n) { 1: CSubjectVerb(actor,true,false,"need",0,"needs","needed"); print " to be holding ", (the) x1, " before ", (theActor) actor; Tense(" can", " could"); " put ", (ItOrThem) x1, " on top of something else."; 2: CSubjectCant(actor,true,true); " put something on top of itself."; 3: print "Putting things on ", (the) x1, " would"; Tense(" achieve", "'ve achieved"); " nothing."; 4: CSubjectVerb(actor,true,false,"lack",0,"lacks","lacked"); " the dexterity."; 5: "(first taking ", (ItOrThem) x1, " off)"; 6: print "There "; Tense("is", "was"); " no more room on ", (the) x1, "."; 7: "Done."; 8: CSubjectVerb(actor,false,false,"put",0,"puts","put"); " ", (the) x1, " on ", (the) x2, "."; } Quit: switch (n) { 1: print "Please answer yes or no."; 2: print "Are you sure you want to quit? "; } Remove: switch (n) { 1: CSubjectIs (x1,true); " unfortunately closed."; 2: print "But "; CSubjectIsnt(x1,true); " there now."; 3: "Removed."; } Restart: switch (n) { 1: print "Are you sure you want to restart? "; 2: "Failed."; } Restore: switch (n) { 1: "Restore failed."; 2: "Ok."; } Rub: switch (n) { 1: CSubjectVerb(actor,true,false,"achieve",0,"achieves","achieved"); " nothing by this."; 2: DecideAgainst(); } Save: switch (n) { 1: "Save failed."; 2: "Ok."; } Score: switch (n) { 1: if (deadflag) print "In that game you scored "; else print "You have so far scored "; print score, " out of a possible ", MAX_SCORE, ", in ", turns, " turn"; if (turns ~= 1) print "s"; return; 2: "There is no score in this story."; } ScriptOff: switch (n) { 1: "Transcripting is already off."; 2: "^End of transcript."; 3: "Attempt to end transcript failed."; } ScriptOn: switch (n) { 1: "Transcripting is already on."; 2: print "Start of a transcript of"; VersionSub(); 3: "Attempt to begin transcript failed."; } Search: switch (n) { 1: print "But it"; Tense("'s", " was"); " dark."; 2: print "There "; Tense("is", "was"); " nothing on ", (the) x1, "."; 3: print "On ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; 4: CSubjectVerb(actor,true,false,"find",0,"finds","found"); " nothing of interest."; 5: CSubjectCant(actor,true); " see inside, since ", (the) x1, " ", (IsOrAre) x1, " closed."; 6: "", (The) x1, " ", (IsOrAre) x1, " empty."; 7: print "In ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; } ! Preceding "No," unable to be used for Set and SetTo Set: CSubjectCant(actor,true); " set ", (ThatOrThose) x1, "."; SetTo: CSubjectCant(actor,true); " set ", (ThatOrThose) x1, " to anything."; Show: switch (n) { 1: CSubjectIsnt(actor,true); " holding ", (the) x1, "."; 2: CSubjectIs (x1,true); " unimpressed."; } Sing: print (PossessiveCaps) actor, " singing "; Tense("is", "was"); " abominable."; Sleep: CSubjectIsnt(actor,true); " feeling especially drowsy."; Smell: switch (n) { 1: CSubjectVerb(actor,true,false,"smell",0,"smells","smelled"); " nothing unexpected."; 2: DecideAgainst(); } #Ifdef DIALECT_US; Sorry: "Oh, don't apologize."; #Ifnot; Sorry: "Oh, don't apologise."; #Endif; Squeeze: switch (n) { 1: DecideAgainst(); 2: CSubjectVerb(actor,true,false,"achieve",0,"achieves","achieved"); " nothing by this."; } Strong: print "Real adventurers "; Tense ("do", "did"); " not use such language."; Swim: print "There"; Tense("'s not", " wasn't"); " enough water to swim in."; Swing: print "There"; Tense("'s", " was"); " nothing sensible to swing here."; SwitchOff: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor, " "; Tense("can", "could"); " switch."; 2: CSubjectIs (x1,true); " already off."; 3: CSubjectVerb(actor,false,false,"switch",0,"switches","switched"); " ", (the) x1, " off."; } SwitchOn: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor, " "; Tense("can", "could"); " switch."; 2: CSubjectIs (x1,true); " already on."; 3: CSubjectVerb(actor,false,false,"switch",0,"switches","switched"); " ", (the) x1, " on."; } Take: switch (n) { 1: "Taken."; 2: CSubjectIs (actor,false); " always self-possessed."; 3: print "I don't suppose ", (the) x1, " would "; Tense("care", "have cared"); " for that."; 4: CSubjectWill(actor,true); print " have "; Tense("", "had "); "to get ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, " first."; 5: CSubjectVerb(actor,true,false,"already have",0,"already has","already had"); " ", (ThatOrThose) x1, "."; 6: CSubjectVerb(x2,true,false,"seem",0,"seems","seemed"); " to belong to ", (the) x1, "."; 7: CSubjectVerb(x2,true,false,"seem",0,"seems","seemed"); " to be a part of ", (the) x1, "."; 8: CSubjectIs (x1,true); " not available."; 9: CSubjectIs (x1,true); " not open."; 10: CSubjectIs (x1,true); " hardly portable."; 11: CSubjectIs (x1,true); " fixed in place."; 12: CSubjectIs (actor,true); " carrying too many things already."; 13: "(putting ", (the) x1, " into ", (the) x2, " to make room)"; } Taste: switch (n) { 1: CSubjectVerb(actor,true,false,"taste",0,"tastes","tasted"); " nothing unexpected."; 2: DecideAgainst(); } Tell: switch (n) { 1: CSubjectVerb(actor,false,false,"talk",0,"talks","talked"); " to ", (OnesSelf) actor, " for a while."; 2: print "This provoke"; Tense("s", "d"); " no reaction."; } Think: "What a good idea."; ThrowAt: switch (n) { 1: "Futile."; 2: CSubjectVerb(actor,true,false,"lack",0,"lacks","lacked"); print " the nerve when it "; Tense("comes", "came"); " to the crucial moment."; } Tie: switch (n) { 1: CSubjectVerb(actor,true,false,"would",0,0); Tense(" achieve", " have achieved"); " nothing by this."; 2: DecideAgainst(); } Touch: switch (n) { 1: DecideAgainst(); 2: CSubjectVerb(actor,true,false,"feel",0,"feels","felt"); " nothing unexpected."; 3: print "That really "; Tense("wouldn't", "didn't"); " serve any purpose."; } ! Turn: see Pull. Unlock: switch (n) { 1: CSubjectDont(x1,true); print " seem to be something ", (theActor) actor; Tense(" can unlock", " could have unlocked"); "."; 2: CSubjectIs (x1,true); " unlocked at the moment."; 3: CSubjectDont(x1,true); " seem to fit the lock."; 4: CSubjectVerb(actor,false,false,"unlock",0,"unlocks","unlocked"); " ", (the) x1, "."; 5: "(first unlocking ", (the) x1, ")"; } VagueGo: CSubjectWill(actor); print " have "; Tense("", "had "); "to say which compass direction to go in."; Verify: switch (n) { 1: "The game file has verified as intact."; 2: "The game file did not verify as intact, and may be corrupt."; } Wait: print "Time passe"; Tense("s", "d"); "."; Wake: print "The dreadful truth is, this "; Tense("is", "was"); " not a dream."; WakeOther:print "That seem"; Tense("s", "ed"); " unnecessary."; Wave: switch (n) { 1: print "But "; CSubjectIsnt(actor,true,true); " holding ", (ThatOrThose) x1, "."; 2: CSubjectVerb(actor,false,false,"look",0,"looks","looked"); print " ridiculous waving ", (the) x1; if (x2) " at ", (the) x2, "."; "."; 3: DecideAgainst(); } WaveHands: CSubjectVerb(actor,false,false,"wave",0,"waves","waved"); switch (n) { 1: ! nothing 2: print " at ", (the) x1; } ", feeling foolish."; Wear: switch (n) { 1: CSubjectCant(actor,true); " wear ", (ThatOrThose) x1, "!"; 2: CSubjectIs (actor,true); " not holding ", (ThatOrThose) x1, "!"; 3: CSubjectIs (actor,true); " already wearing ", (ThatOrThose) x1, "!"; 4: CSubjectVerb(actor,false,false,"put on",0,"puts on","put on"); " ", (the) x1, "."; } ! Yes: see No. ]; ! ============================================================================== Constant LIBRARY_ENGLISH; ! for dependency checking. #Endif; ! ============================================================================== ! ============================================================================== ! GRAMMAR: Grammar table entries for the standard verbs library. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive or at ! https://gitlab.com/DavidGriffith/inform6lib/ ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ============================================================================== System_file; #Ifdef LIBRARY_STAGE; #Iffalse LIBRARY_STAGE >= AFTER_GRAMMAR; ! if not already included #Iftrue LIBRARY_STAGE == AFTER_VERBLIB; ! if okay to include it ! ------------------------------------------------------------------------------ ! The "meta-verbs", commands to the game rather than in the game, come first: ! ------------------------------------------------------------------------------ Verb meta 'brief' * -> LMode1; Verb meta 'verbose' 'long' * -> LMode2; Verb meta 'superbrief' 'short' * -> LMode3; Verb meta 'normal' * -> LModeNormal; Verb meta 'notify' * -> NotifyOn * 'on' -> NotifyOn * 'off' -> NotifyOff; Verb meta 'pronouns' 'nouns' * -> Pronouns; Verb meta 'quit' 'q//' 'die' * -> Quit; Verb meta 'recording' * -> CommandsOn * 'on' -> CommandsOn * 'off' -> CommandsOff; Verb meta 'replay' * -> CommandsRead; Verb meta 'restart' * -> Restart; Verb meta 'restore' * -> Restore; Verb meta 'save' * -> Save; Verb meta 'score' * -> Score; Verb meta 'fullscore' 'full' * -> FullScore * 'score' -> FullScore; Verb meta 'script' 'transcript' * -> ScriptOn * 'on' -> ScriptOn * 'off' -> ScriptOff; Verb meta 'noscript' 'unscript' * -> ScriptOff; Verb meta 'verify' * -> Verify; Verb meta 'version' * -> Version; #Ifndef NO_PLACES; Verb meta 'objects' * -> Objects; Verb meta 'places' * -> Places; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ ! Debugging grammar ! ------------------------------------------------------------------------------ #Ifdef DEBUG; Verb meta 'actions' * -> ActionsOn * 'on' -> ActionsOn * 'off' -> ActionsOff; Verb meta 'changes' * -> ChangesOn * 'on' -> ChangesOn * 'off' -> ChangesOff; Verb meta 'gonear' * anynumber -> GoNear * noun -> Gonear; Verb meta 'goto' * anynumber -> Goto; Verb meta 'random' * -> Predictable; Verb meta 'routines' 'messages' * -> RoutinesOn * 'on' -> RoutinesOn * 'verbose' -> RoutinesVerbose * 'off' -> RoutinesOff; Verb meta 'scope' * -> Scope * anynumber -> Scope * noun -> Scope; Verb meta 'showdict' 'dict' * -> ShowDict * topic -> ShowDict; Verb meta 'showobj' * -> Showobj * anynumber -> Showobj * multi -> Showobj; Verb meta 'showverb' * special -> Showverb; Verb meta 'timers' 'daemons' * -> TimersOn * 'on' -> TimersOn * 'off' -> TimersOff; Verb meta 'trace' * -> TraceOn * number -> TraceLevel * 'on' -> TraceOn * 'off' -> TraceOff; Verb meta 'abstract' * anynumber 'to' anynumber -> XAbstract * noun 'to' noun -> XAbstract; Verb meta 'purloin' * anynumber -> XPurloin * multi -> XPurloin; Verb meta 'tree' * -> XTree * anynumber -> XTree * noun -> XTree; #Ifdef TARGET_GLULX; Verb meta 'glklist' * -> Glklist; #Endif; ! TARGET_ #Endif; ! DEBUG ! ------------------------------------------------------------------------------ ! And now the game verbs. ! ------------------------------------------------------------------------------ [ ADirection; if (noun in compass) rtrue; rfalse; ]; Verb 'answer' 'say' 'shout' 'speak' * topic 'to' creature -> Answer; Verb 'ask' * creature 'about' topic -> Ask * creature 'for' noun -> AskFor * creature 'to' topic -> AskTo * 'that' creature topic -> AskTo; Verb 'attack' 'break' 'crack' 'destroy' 'fight' 'hit' 'kill' 'murder' 'punch' 'smash' 'thump' 'torture' 'wreck' * noun -> Attack; Verb 'blow' * held -> Blow; Verb 'bother' 'curses' 'darn' 'drat' * -> Mild * topic -> Mild; Verb 'burn' 'light' * noun -> Burn * noun 'with' held -> Burn; Verb 'buy' 'purchase' * noun -> Buy; Verb 'climb' 'scale' * noun -> Climb * 'up'/'over' noun -> Climb; Verb 'close' 'cover' 'shut' * noun -> Close * 'up' noun -> Close * 'off' noun -> SwitchOff; Verb 'consult' * noun 'about' topic -> Consult * noun 'on' topic -> Consult; Verb 'cut' 'chop' 'prune' 'slice' * noun -> Cut; Verb 'dig' * noun -> Dig * noun 'with' held -> Dig * 'in' noun -> Dig * 'in' noun 'with' held -> Dig; Verb 'disrobe' 'doff' 'shed' * held -> Disrobe; Verb 'drink' 'sip' 'swallow' * noun -> Drink; Verb 'drop' 'discard' * multiheld -> Drop * multiexcept 'in'/'into'/'down' noun -> Insert * multiexcept 'on'/'onto' noun -> PutOn; Verb 'throw' * held 'at'/'against'/'on'/'onto' noun -> ThrowAt; Verb 'eat' * held -> Eat; Verb 'empty' * noun -> Empty * 'out' noun -> Empty * noun 'out' -> Empty * noun 'to'/'into'/'on'/'onto' noun -> EmptyT; Verb 'enter' 'cross' * -> GoIn * noun -> Enter; Verb 'examine' 'x//' 'check' 'describe' 'watch' * noun -> Examine; Verb 'exit' 'out' 'outside' * -> Exit * noun -> Exit; Verb 'fill' * noun -> Fill * noun 'from' noun -> Fill; Verb 'get' * 'out'/'off'/'up' 'of'/'from' noun -> Exit * multi -> Take * 'in'/'into'/'on'/'onto' noun -> Enter * 'off' noun -> GetOff * multiinside 'from'/'off' noun -> Remove; Verb 'give' 'feed' 'offer' 'pay' * creature held -> Give reverse * held 'to' creature -> Give * 'over' held 'to' creature -> Give; Verb 'go' 'run' 'walk' * -> VagueGo * noun=ADirection -> Go * noun -> Enter * 'out'/'outside' -> Exit * 'in'/'inside' -> GoIn * 'into'/'in'/'inside'/'through' noun -> Enter; Verb 'in' 'inside' * -> GoIn; Verb 'insert' * multiexcept 'in'/'into' noun -> Insert; Verb 'inventory' 'inv' 'i//' * -> Inv * 'tall' -> InvTall * 'wide' -> InvWide; Verb 'jump' 'hop' 'skip' * -> Jump * 'in' noun -> JumpIn * 'into' noun -> JumpIn * 'on' noun -> JumpOn * 'upon' noun -> JumpOn * 'over' noun -> JumpOver; Verb 'kiss' 'embrace' 'hug' * creature -> Kiss; Verb 'leave' * -> VagueGo * noun=ADirection -> Go * noun -> Exit * 'into'/'in'/'inside'/'through' noun -> Enter; Verb 'listen' 'hear' * -> Listen * noun -> Listen * 'to' noun -> Listen; Verb 'lock' * noun 'with' held -> Lock; Verb 'look' 'l//' * -> Look * 'at' noun -> Examine * 'inside'/'in'/'into'/'through'/'on' noun -> Search * 'under' noun -> LookUnder * 'up' topic 'in' noun -> Consult * noun=ADirection -> Examine * 'to' noun=ADirection -> Examine; Verb 'no' * -> No; Verb 'open' 'uncover' 'undo' 'unwrap' * noun -> Open * noun 'with' held -> Unlock; Verb 'peel' * noun -> Take * 'off' noun -> Take; Verb 'pick' * 'up' multi -> Take * multi 'up' -> Take; Verb 'pray' * -> Pray; Verb 'pry' 'prise' 'prize' 'lever' 'jemmy' 'force' * noun 'with' held -> Unlock * 'apart'/'open' noun 'with' held -> Unlock * noun 'apart'/'open' 'with' held -> Unlock; Verb 'pull' 'drag' * noun -> Pull; Verb 'push' 'clear' 'move' 'press' 'shift' * noun -> Push * noun noun -> PushDir * noun 'to' noun -> Transfer; Verb 'put' * multiexcept 'in'/'inside'/'into' noun -> Insert * multiexcept 'on'/'onto' noun -> PutOn * 'on' held -> Wear * 'down' multiheld -> Drop * multiheld 'down' -> Drop; Verb 'read' * noun -> Examine * 'about' topic 'in' noun -> Consult * topic 'in' noun -> Consult; Verb 'remove' * held -> Disrobe * multi -> Take * multiinside 'from' noun -> Remove; Verb 'rub' 'clean' 'dust' 'polish' 'scrub' 'shine' 'sweep' 'wipe' * noun -> Rub; Verb 'search' * noun -> Search; Verb 'set' 'adjust' * noun -> Set * noun 'to' special -> SetTo; Verb 'show' 'display' 'present' * creature held -> Show reverse * held 'to' creature -> Show; Verb 'shit' 'damn' 'fuck' 'sod' * -> Strong * topic -> Strong; Verb 'sing' * -> Sing; Verb 'sit' 'lie' * 'on' 'top' 'of' noun -> Enter * 'on'/'in'/'inside' noun -> Enter; Verb 'sleep' 'nap' * -> Sleep; Verb 'smell' 'sniff' * -> Smell * noun -> Smell; Verb 'sorry' * -> Sorry; Verb 'squeeze' 'squash' * noun -> Squeeze; Verb 'stand' * -> Exit * 'up' -> Exit * 'on' noun -> Enter; Verb 'swim' 'dive' * -> Swim; Verb 'swing' * noun -> Swing * 'on' noun -> Swing; Verb 'switch' * noun -> Switchon * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Verb 'take' 'carry' 'hold' * multi -> Take * 'off' held -> Disrobe * multiinside 'from'/'off' noun -> Remove * 'inventory' -> Inv; Verb 'taste' * noun -> Taste; Verb 'tell' * creature 'about' topic -> Tell * creature 'to' topic -> AskTo; Verb 'think' * -> Think; Verb 'tie' 'attach' 'connect' 'fasten' 'fix' * noun -> Tie * noun 'to' noun -> Tie; Verb 'touch' 'feel' 'fondle' 'grope' * noun -> Touch; Verb 'transfer' * noun 'to' noun -> Transfer; Verb 'turn' 'rotate' 'screw' 'twist' 'unscrew' * noun -> Turn * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Verb 'unlock' * noun 'with' held -> Unlock; Verb 'wait' 'z//' * -> Wait; Verb 'wake' 'awake' 'awaken' * -> Wake * 'up' -> Wake * creature -> WakeOther * creature 'up' -> WakeOther * 'up' creature -> WakeOther; Verb 'wave' * -> WaveHands * noun -> Wave * noun 'at' noun -> Wave * 'at' noun -> WaveHands; Verb 'wear' 'don' * held -> Wear; Verb 'yes' 'y//' * -> Yes; ! ------------------------------------------------------------------------------ ! This routine is no longer used here, but provided to help existing games ! which use it as a general parsing routine: [ ConTopic w; consult_from = wn; do w = NextWordStopped(); until (w == -1 || (w == 'to' && action_to_be == ##Answer)); wn--; consult_words = wn - consult_from; if (consult_words == 0) return -1; if (action_to_be == ##Answer or ##Ask or ##Tell) { w = wn; wn = consult_from; parsed_number = NextWord(); if (parsed_number == 'the' && consult_words > 1) parsed_number = NextWord(); wn = w; return 1; } return 0; ]; ! ------------------------------------------------------------------------------ ! Final task: provide trivial routines if the user hasn't already: ! ------------------------------------------------------------------------------ Default Story 0; Default Headline 0; Default d_obj NULL; Default u_obj NULL; Stub AfterLife 0; Stub AfterPrompt 0; Stub Amusing 0; Stub BeforeParsing 0; Stub ChooseObjects 2; Stub DarkToDark 0; Stub DeathMessage 0; Stub Epilogue 0; Stub GamePostRoutine 0; Stub GamePreRoutine 0; Stub InScope 1; Stub LookRoutine 0; Stub NewRoom 0; Stub ObjectDoesNotFit 2; Stub ParseNumber 2; Stub ParserError 1; Stub PrintTaskName 1; Stub PrintVerb 1; Stub TimePasses 0; Stub UnknownVerb 1; #Ifdef TARGET_GLULX; Stub HandleGlkEvent 2; Stub IdentifyGlkObject 4; Stub InitGlkWindow 1; #Endif; ! TARGET_GLULX #Ifndef PrintRank; [ PrintRank; "."; ]; #Endif; #Ifndef ParseNoun; [ ParseNoun obj; obj = obj; return -1; ]; #Endif; #Ifdef INFIX; Include "infix"; #Endif; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_GRAMMAR; #Ifnot; ! LIBRARY_STAGE < AFTER_GRAMMAR but ~= AFTER_VERBLIB Message "Error: 'verblib' needs to be correctly included before including 'grammar'. This will cause a big number of errors!"; #Endif; #Ifnot; ! LIBRARY_STAGE >= AFTER_GRAMMAR : already included Message "Warning: 'grammar' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; #Ifnot; ! LIBRARY_STAGE is not defined Message "Error: 'parser', then 'verblib' need to be correctly included before including 'grammar'. This will cause a big number of errors!"; #Endif; ! ============================================================================== The Inform Library version 6.12 introduces two fun new ways of storytelling. First, you can use first-person and third person narrative voices. Second, you can use past tense. The typical way to write interactive fiction has been for the game to talk about the player in the second-person narrative voice in the present tense. For example: > PUT COOKIE ON TABLE You put the cookie on the table. That's how Infocom usually did things and is Inform's default. Now suppose you, the author, want the story to read more like an autobiography. Consider this: > PUT COOKIE ON TABLE I put the cookie on the table. This is the first-person narrative voice. Third-person narrative voice looks like this: > PUT COOKIE ON TABLE George puts the cookie on the table. To cause these alternative narrative voices, the player object needs to have a narrative_voice property with a value of 1 or 3. Setting it to 2 results in the old regular behavior. It's most convenient to do this in the Initialise() routine like this: [ Initialise; location = TheRoom; player.narrative_voice = 1; ]; For third-person mode, some additional properties are necessary: [ Initialise; location = TheRoom; player.narrative_voice = 3; player.short_name = "George Jones"; (player.&name)-->0 = 'George'; (player.&name)-->1 = 'Jones'; ]; The name property must be specified like this. There are five slots to which you may add dictionary words. Using these modes may require a bit more discipline when it comes to writing your prose and dealing with the default responses from new verbs you introduce. If the player always controls the same character and/or the narrative voice never changes, you can write your verb subroutines to always talk about George. If the player changes characters or the narrative voice changes, things get a bit more complicated. Problems like that can be solved by careful use of the CSubject functions which are detailed at the end of this document. Previously if you changed the player-character to something other than selfobj, the former player-character would be described as "Your former self" when typing "LOOK". If you want to change bodies like this, you must set player.short_name even if you use the old regular second-person voice. Make sure the narrative_voice, short_name, and name properties are all sensibly set for all possible bodies the player might control. Changing bodies can be a handy technique for implement flashbacks. Create self2, self3, and so on for each time period you visit. Just change the player global to the self you want to be in control. The SelfClass class is provided with all the various properties a player-character needs. Use this class whenever you create a new self. An interesting way of using the third-person voice is to give the PC a generic name like "detective" (put that in the short_name and name properties). Then take away the PC's proper attribute. This will cause the library to address the PC as "the detective" and correctly capitalize the word "the". For instance: [ Initialise; location = theroom; player.narrative_voice = 3; player.short_name = "detective"; (player.&name)-->0 = 'detective'; give player ~proper; ]; This results in a room description like this: >PUT FOLDER ON TABLE The detective puts the folder on the table. >LOOK Squad Room This is the 13th Precinct's squad room. The detective can see a table (upon which is a folder). >INVENTORY The detective is carrying: a badge a revolver The default article used to describe a non-proper PC is "the". If you want to talk about "a detective", just set player.article to "a". There is another property for the player object: nameless. This has meaning only in the first-person or second-person narrative voices. Normally if you're using either of these voices and the PC switches bodies, the PC's former body is referred to as "My former self" or "Your former self". If you want the former body to be referred to some other way, set player.nameless to false and set the name and short_name properties to something appropriate. If third-person narrative voice is used, the nameless property is ignored. "The detective" isn't namelessness. It's just not a proper name. The Library uses the CSubject subroutines to figure out when to say "I", "You", or "George". You can use them too. The most important of these is CSubjectVerb(). It handles the conjugation of verbs used by the actor. These are its parameters: obj The actor who is doing something. reportage Boolean: causes the actor to "observe"[1] nocaps Boolean: Don't capitalise if "you" is used[2]. v1 1st person "I" present verb. v2 2nd person "You" present verb[3]. v3 3rd person "He", "she", or "it" present verb. past The past tense form of the verb[4]. [1] In the Library itself, "reportage" is used in SubjectNotPlayer() which determines the correct conjugation for certain actions performed by NPCs. If this is "false", then this will happen: >SALLY, RUB LAMP Sally achieves nothing by this. If this is "true" then this happens: >SALLY, RUB LAMP Sally observes that she achieves nothing by this. This sort of thing can be important if you want to imply that the NPC is aware of the result of an action. [2] When the second-person voice is used, responses very often begin with "You". Suppose you want to put a conjunction like "but" in front of "you". For instance: >EAT PUDDING But you can't have any pudding if you don't eat your meat! See that "you" is not capitalised. If you omit this parameter, it will be false and therefore the game will capitalise "you". If you want to use a conjunction as in the above example, pass "true". [3] The v2 parameter is usually not necessary because the first and second person present forms of verbs are usually the same. Unless you have something special planned, pass 0 for v2. [4] Past tense can be useful for implementing a flashback. For instance: >EXAMINE VASE The vase was very expensive-looking. Here's an example of using CSubjectVerb(): CSubjectVerb(actor,false,false,"close",0,"closes","closed"); " ", (the) x1, "."; The default is to use the present tense. If you want to change to the past tense, set player.narrative_tense to PAST_TENSE. To change back, change it to PRESENT_TENSE. If you're doing something more complicated, like going back and forth between the present and the past, it can be handy to create a "past PC" with its own posessions and properties. That one always has its narrative_tense set to PAST_TENSE. Then when you want to flash back, simply call ChangePlayer() appropriately. There is a helper function called Tense() that doesn't quite fit in with the CSubject functions. This one takes two parameters: present and past. Its purpose is to keep the tense of various verbs straight. It's not meant for sorting out narrative voices, but instead is applied after that has been sorted out. In fact, Tense() is used extensively in the CSubject helper functions to do just that. Here's how to use it: print "After the storm, there still "; Tense("isn't", "wasn't"); " enough rainfall."; Most of the rest of the CSubject functions are wrappers for commonly used verbs: "is", "isn't", "has", "will", "can", "can't", and "don't". These have only three parameters: obj, reportage, and nocaps. You can call these with two or one parameters if you like. The functions will receive 0 (also false) for missing trailing parameters. CSubjectVoice() is similar to CSubjectVerb() except that the name of the subject is not printed. Here they are along with sample calls: CSubjectIs() CSubjectIs(x1,true); " already closed."; CSubjectIsnt() print "But "; CSubjectIsnt(actor,true,false); " in anything at the moment."; CSubjectHas() CSubjectHas(actor,false); " better things to do."; CSubjectWill() CSubjectWill(actor,true); " first have to close ", (the) x1, "."; CSubjectCan() CSubjectCan(actor,true); " only get into something free-standing."; CSubjectCant() CSubjectCant(actor,true); " usefully blow ", (thatorthose) x1, "."; CSubjectDont() CSubjectDont(x1,true); " seem to fit the lock."; CSubjectVoice() print "What "; CSubjectVoice(player, "do", "do", "does", "did"); print " "; CSubjectVerb(player, false, true, "want", "want", "want", "want"); " to do?"; ! ============================================================================== ! VERBLIB: Front end to standard verbs library. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ============================================================================== System_file; #Ifdef LIBRARY_STAGE; #Iffalse LIBRARY_STAGE >= AFTER_VERBLIB; ! if not already included #Iftrue LIBRARY_STAGE == AFTER_PARSER; ! if okay to include it ! ------------------------------------------------------------------------------ Default AMUSING_PROVIDED 1; Default MAX_CARRIED 100; Default MAX_SCORE 0; Default NUMBER_TASKS 1; Default OBJECT_SCORE 4; Default ROOM_SCORE 5; Default SACK_OBJECT 0; Default TASKS_PROVIDED 1; #Ifndef task_scores; Array task_scores -> 0 0 0 0; #Endif; Array task_done -> NUMBER_TASKS; #Ifndef LibraryMessages; Object LibraryMessages; #Endif; #Ifndef NO_PLACES; [ ObjectsSub; Objects1Sub(); ]; [ PlacesSub; Places1Sub(); ]; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ ! Banner(), VersionSub(), and RunTimeError() are preempted by LanguageBanner(), ! LanguageVersionSub(), and LanguageError() respectfully. When converting ! this library to support a different natural language, these three latter ! functions should be created rather than editing this file. ! ------------------------------------------------------------------------------ [ Banner i; #Ifdef LanguageBanner; LanguageBanner(); i = 0; ! suppress warning #Ifnot; if (Story) { #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; print "^", (string) Story; #IfV5; style roman; #Endif; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Header); print "^", (string) Story; glk_set_style(style_Normal); #Endif; ! TARGET_ } if (Headline) print (string) Headline; #Ifdef TARGET_ZCODE; print "Release ", (HDR_GAMERELEASE-->0) & $03ff, " / Serial number "; for (i=0 : i<6 : i++) print (char) HDR_GAMESERIAL->i; #Ifnot; ! TARGET_GLULX; print "Release "; @aloads ROM_GAMERELEASE 0 i; print i; print " / Serial number "; for (i=0 : i<6 : i++) print (char) ROM_GAMESERIAL->i; #Endif; ! TARGET_ print " / Inform v"; inversion; print " Library v", (string) LibRelease, " "; #Ifdef STRICT_MODE; print "S"; #Endif; ! STRICT_MODE #Ifdef INFIX; print "X"; #Ifnot; #Ifdef DEBUG; print "D"; #Endif; ! DEBUG #Endif; ! INFIX new_line; #Endif; ! LanguageBanner ]; [ VersionSub ix; #Ifdef LanguageVersionSub; LanguageVersionSub(); ix = 0; ! suppress warning #Ifnot; Banner(); #Ifdef TARGET_ZCODE; ix = 0; ! shut up compiler warning if (standard_interpreter > 0) { print "Standard interpreter ", standard_interpreter/256, ".", standard_interpreter%256, " (", HDR_TERPNUMBER->0; #Iftrue (#version_number == 6); print (char) '.', HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print ") / "; } else { print "Interpreter ", HDR_TERPNUMBER->0, " Version "; #Iftrue (#version_number == 6); print HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print " / "; } #Ifnot; ! TARGET_GLULX; @gestalt 1 0 ix; print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; @gestalt 0 0 ix; print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; #Endif; ! TARGET_; print "Library serial number ", (string) LibSerial, "^"; #Ifdef LanguageVersion; print (string) LanguageVersion, "^"; #Endif; ! LanguageVersion #Endif; ! LanguageVersionSub ]; [ RunTimeError n p1 p2; #Ifdef LanguageError; LanguageError(n, p1, p2); #Ifnot; #Ifdef DEBUG; print "** Library error ", n, " (", p1, ", ", p2, ") **^** "; switch (n) { 1: print "preposition not found (this should not occur)"; 2: print "Property value not routine or string: ~", (property) p2, "~ of ~", (name) p1, "~ (", p1, ")"; 3: print "Entry in property list not routine or string: ~", (property) p2, "~ list of ~", (name) p1, "~ (", p1, ")"; 4: print "Too many timers/daemons are active simultaneously. The limit is the library constant MAX_TIMERS (currently ", MAX_TIMERS, ") and should be increased"; 5: print "Object ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 7: print "The object ~", (name) p1, "~ can only be used as a player object if it has the ~number~ property"; 8: print "Attempt to take random entry from an empty table array"; 9: print p1, " is not a valid direction property number"; 10: print "The player-object is outside the object tree"; 11: print "The room ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 12: print "Tried to set a non-existent pronoun using SetPronoun"; 13: print "A 'topic' token can only be followed by a preposition"; 14: print "Overflowed buffer limit of ", p1, " using '@@64output_stream 3' ", (string) p2; 15: print "LoopWithinObject broken because the object ", (name) p1, " was moved while the loop passed through it."; 16: print "Attempt to use illegal narrative_voice of ", p1, "."; default: print "(unexplained)"; } " **"; #Ifnot; "** Library error ", n, " (", p1, ", ", p2, ") **"; #Endif; ! DEBUG #Endif; ! LanguageError ]; ! ---------------------------------------------------------------------------- ! The WriteListFrom routine, a flexible object-lister taking care of ! plurals, inventory information, various formats and so on. This is used ! by everything in the library which ever wants to list anything. ! ! If there were no objects to list, it prints nothing and returns false; ! otherwise it returns true. ! ! o is the object, and style is a bitmap, whose bits are given by: ! ---------------------------------------------------------------------------- Constant NEWLINE_BIT $0001; ! New-line after each entry Constant INDENT_BIT $0002; ! Indent each entry by depth Constant FULLINV_BIT $0004; ! Full inventory information after entry Constant ENGLISH_BIT $0008; ! English sentence style, with commas and and Constant RECURSE_BIT $0010; ! Recurse downwards with usual rules Constant ALWAYS_BIT $0020; ! Always recurse downwards Constant TERSE_BIT $0040; ! More terse English style Constant PARTINV_BIT $0080; ! Only brief inventory information after entry Constant DEFART_BIT $0100; ! Use the definite article in list Constant WORKFLAG_BIT $0200; ! At top level (only), only list objects ! which have the "workflag" attribute Constant ISARE_BIT $0400; ! Print " is" or " are" before list Constant CONCEAL_BIT $0800; ! Omit objects with "concealed" or "scenery": ! if WORKFLAG_BIT also set, then does _not_ ! apply at top level, but does lower down Constant NOARTICLE_BIT $1000; ! Print no articles, definite or not Constant ID_BIT $2000; ! Print object id after each entry [ NextEntry o odepth; for (::) { o = sibling(o); if (o == 0) return 0; if (lt_value && o.list_together ~= lt_value) continue; if (c_style & WORKFLAG_BIT && odepth==0 && o hasnt workflag) continue; if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) continue; return o; } ]; [ WillRecurs o; if (c_style & ALWAYS_BIT) rtrue; if (c_style & RECURSE_BIT == 0) rfalse; if ((o has transparent or supporter) || (o has container && o has open)) rtrue; rfalse; ]; [ ListEqual o1 o2; if (child(o1) && WillRecurs(o1)) rfalse; if (child(o2) && WillRecurs(o2)) rfalse; if (c_style & (FULLINV_BIT + PARTINV_BIT)) { if ((o1 hasnt worn && o2 has worn) || (o2 hasnt worn && o1 has worn)) rfalse; if ((o1 hasnt light && o2 has light) || (o2 hasnt light && o1 has light)) rfalse; if (o1 has container) { if (o2 hasnt container) rfalse; if ((o1 has open && o2 hasnt open) || (o2 has open && o1 hasnt open)) rfalse; } else if (o2 has container) rfalse; } return Identical(o1, o2); ]; [ SortTogether obj value; ! print "Sorting together possessions of ", (object) obj, " by value ", value, "^"; ! for (x=child(obj) : x : x=sibling(x)) ! print (the) x, " no: ", x, " lt: ", x.list_together, "^"; while (child(obj)) { if (child(obj).list_together ~= value) move child(obj) to out_obj; else move child(obj) to in_obj; } while (child(in_obj)) move child(in_obj) to obj; while (child(out_obj)) move child(out_obj) to obj; ]; [ SortOutList obj i k l; ! print "^^Sorting out list from ", (name) obj, "^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; .AP_SOL; for (i=obj : i : i=sibling(i)) { k = i.list_together; if (k ~= 0) { ! print "Scanning ", (name) i, " with lt=", k, "^"; for (i=sibling(i) : i && i.list_together == k :) i = sibling(i); if (i == 0) rfalse; ! print "First not in block is ", (name) i, " with lt=", i.list_together, "^"; for (l=sibling(i) : l : l=sibling(l)) if (l.list_together == k) { SortTogether(parent(obj), k); ! print "^^After ST:^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; obj = child(parent(obj)); jump AP_SOL; } } } ]; #Ifdef TARGET_ZCODE; [ Print__Spaces n; ! To avoid a bug occurring in Inform 6.01 to 6.10 if (n == 0) return; spaces n; ]; #Ifnot; ! TARGET_GLULX; [ Print__Spaces n; while (n > 0) { @streamchar ' '; n = n - 1; } ]; #Endif; ! TARGET_ [ WriteListFrom o style depth s1 s2 s3 s4 s5 s6; if (o == nothing) return 0; s1 = c_style; s2 = lt_value; s3 = listing_together; s4 = listing_size; s5 = wlf_indent; s6 = inventory_stage; if (o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } c_style = style; wlf_indent = 0; if (WriteListR(o, depth) == 0) return 0; c_style = s1; lt_value = s2; listing_together = s3; listing_size = s4; wlf_indent = s5; inventory_stage = s6; rtrue; ]; [ WriteListR o depth stack_pointer classes_p sizes_p i j k k2 l m n q senc mr; if (depth > 0 && o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } for (::) { if (o == 0) rfalse; if (c_style & WORKFLAG_BIT && depth==0 && o hasnt workflag) { o = sibling(o); continue; } if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) { o = sibling(o); continue; } break; } classes_p = match_classes + stack_pointer; sizes_p = match_list + stack_pointer; for (i=o,j=0 : i && (j+stack_pointer)<128 : i=NextEntry(i,depth),j++) { classes_p->j = 0; if (i.plural) k++; } if (c_style & ISARE_BIT) { if (j == 1 && o hasnt pluralname) Tense(IS__TX, WAS__TX); else Tense(ARE__TX, WERE__TX); if (c_style & NEWLINE_BIT) print ":^"; else print (char) ' '; c_style = c_style - ISARE_BIT; } stack_pointer = stack_pointer+j+1; if (k < 2) jump EconomyVersion; ! It takes two to plural n = 1; for (i=o,k=0 : kk == 0) { classes_p->k = n; sizes_p->n = 1; for (l=NextEntry(i,depth),m=k+1 : l && mm == 0 && i.plural && l.plural ~= 0) { if (ListEqual(i, l) == 1) { sizes_p->n = sizes_p->n + 1; classes_p->m = n; } } n++; } n--; for (i=1,j=o,k=0 : i<=n : i++,senc++) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } m = sizes_p->i; if (j == 0) mr = 0; else { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together == mr) senc--; mr = j.list_together; } } senc--; for (i=1,j=o,k=0,mr=0 : senc>=0 : i++,senc--) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { senc++; jump Omit_FL2; } k2 = NextEntry(j, depth); if (k2 == 0 || k2.list_together ~= j.list_together) jump Omit_WL2; k2 = metaclass(j.list_together); if (k2 == Routine or String) { q = j; listing_size = 1; l = k; m = i; while (m < n && q.list_together == j.list_together) { m++; while (((classes_p->l) ~= m) && ((classes_p->l) ~= -m)) { l++; q = NextEntry(q, depth); } if (q.list_together == j.list_together) listing_size++; } ! print " [", listing_size, "] "; if (listing_size == 1) jump Omit_WL2; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k2 == String) { q = 0; for (l=0 : l(l+i); EnglishNumber(q); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k2 ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist2; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k2 == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist2; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL2; } } .Omit_WL2; if (WriteBeforeEntry(j, depth, 0, senc) == 1) jump Omit_FL2; if (sizes_p->i == 1) { if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; } else { if (c_style & DEFART_BIT) PrefaceByArticle(j, 1, sizes_p->i); print (number) sizes_p->i, " "; PrintOrRun(j, plural, 1); } if (sizes_p->i > 1 && j hasnt pluralname) { give j pluralname; WriteAfterEntry(j, depth, stack_pointer); give j ~pluralname; } else { WriteAfterEntry(j,depth,stack_pointer); } .Omit_EL2; if (c_style & ENGLISH_BIT) { if (senc == 1) print (SerialComma) i+senc, (string) AND__TX; if (senc > 1) print (string) COMMA__TX; } .Omit_FL2; } rtrue; .EconomyVersion; n = j; for (i=1,j=o : i<=n : j=NextEntry(j,depth),i++,senc++) { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together==mr) senc--; mr = j.list_together; } for (i=1,j=o,mr=0 : i<=senc : j=NextEntry(j,depth),i++) { if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { i--; jump Omit_FL; } k = NextEntry(j, depth); if (k == 0 || k.list_together ~= j.list_together) jump Omit_WL; k = metaclass(j.list_together); if (k == Routine or String) { if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k == String) { q = j; l = 0; do { q = NextEntry(q, depth); l++; } until (q == 0 || q.list_together ~= j.list_together); EnglishNumber(l); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL; } } .Omit_WL; if (WriteBeforeEntry(j, depth, i, senc) == 1) jump Omit_FL; if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; WriteAfterEntry(j, depth, stack_pointer); .Omit_EL; if (c_style & ENGLISH_BIT) { if (i == senc-1) print (SerialComma) senc, (string) AND__TX; if (i < senc-1) print (string) COMMA__TX; } .Omit_FL; } ]; ! end of WriteListR [ WriteBeforeEntry o depth ipos sentencepos flag; inventory_stage = 1; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (o.invent && (c_style & (PARTINV_BIT|FULLINV_BIT))) { flag = PrintOrRun(o, invent, 1); if (flag) { if (c_style & ENGLISH_BIT) { if (ipos == sentencepos-1) print (SerialComma) sentencepos, (string) AND__TX; if (ipos < sentencepos-1) print (string) COMMA__TX; } if (c_style & NEWLINE_BIT) new_line; } } return flag; ]; [ WriteAfterEntry o depth stack_p p recurse_flag parenth_flag eldest_child child_count combo i j; inventory_stage = 2; if (c_style & PARTINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; combo = 0; if (o has light && location hasnt light) combo=combo+1; if (o has container && o hasnt open) combo=combo+2; if ((o has container && (o has open || o has transparent))) { objectloop(i in o) { if (i hasnt concealed && i hasnt scenery) { j = true; break; } } if (~~j) combo=combo+4; } if (combo) L__M(##ListMiscellany, combo, o); } ! end of PARTINV_BIT processing if (c_style & FULLINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; if (o has light && o has worn) { L__M(##ListMiscellany, 8, o); parenth_flag = true; } else { if (o has light) { L__M(##ListMiscellany, 9, o); parenth_flag = true; } if (o has worn) { L__M(##ListMiscellany, 10, o); parenth_flag = true; } } if (o has container) if (o has openable) { if (parenth_flag) print (string) AND__TX; else L__M(##ListMiscellany, 11, o); if (o has open) if (child(o)) L__M(##ListMiscellany, 12, o); else L__M(##ListMiscellany, 13, o); else if (o has lockable && o has locked) L__M(##ListMiscellany, 15, o); else L__M(##ListMiscellany, 14, o); parenth_flag = true; } else if (child(o)==0 && o has transparent) if (parenth_flag) L__M(##ListMiscellany, 16, o); else L__M(##ListMiscellany, 17, o); if (parenth_flag) print ")"; } ! end of FULLINV_BIT processing if (c_style & CONCEAL_BIT) { child_count = 0; objectloop (p in o) if (p hasnt concealed && p hasnt scenery) { child_count++; eldest_child = p; } } else { child_count = children(o); eldest_child = child(o); } if (child_count && (c_style & ALWAYS_BIT)) { if (c_style & ENGLISH_BIT) L__M(##ListMiscellany, 18, o); recurse_flag = true; } if (child_count && (c_style & RECURSE_BIT)) { if (o has supporter) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 19, o); else L__M(##ListMiscellany, 20, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } if (o has container && (o has open || o has transparent)) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 21, o); else L__M(##ListMiscellany, 22, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } } if (recurse_flag && (c_style & ENGLISH_BIT)) if (child_count > 1 || eldest_child has pluralname) Tense(ARE2__TX, WERE2__TX); else Tense(IS2__TX, WAS2__TX); if (c_style & NEWLINE_BIT) new_line; if (recurse_flag) { o = child(o); #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; lt_value = 0; listing_together = 0; listing_size = 0; WriteListR(o, depth+1, stack_p); #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; if (c_style & TERSE_BIT) print ")"; } ]; ! ---------------------------------------------------------------------------- ! LoopWithinObject(rtn,obj,arg) ! ! rtn is the address of a user-supplied routine. ! obj is an optional parent object whose dependents are to be processed; the ! default is the current actor (normally the player). ! arg is an optional argument passed to the rtn; this can be a single variable ! or constant, or the address of an array (which enables multiple values to be ! passed and returned). ! ! For each object o which is a child, grandchild, great-grandchild, etc, of the ! original obj, LoopWithinObject() calls rtn(o,arg). ! ! The rtn should perform any appropriate testing or processing on each object o, ! using the optional arg value if necessary. If the rtn returns true (or any ! positive value), the children of o, if any, are also tested; those children ! are skipped if rtn returns false. To terminate the loop before all objects ! have been processed, rtn should return a large negative number (eg -99). ! ! To deal with supporters and open containers, so that objects are processed ! only if they are accessible to the player, rtn might end with these ! statements: ! if ((o has transparent or supporter) || (o has container && o has open)) rtrue; ! rfalse; ! or alternatively with: ! c_style = RECURSE_BIT; return WillRecurs(o); ! ! LoopWithinObject() returns the number of objects which have been processed. ! ---------------------------------------------------------------------------- [ LoopWithinObject rtn obj arg n o x y; if (obj == 0) obj = actor; o = child(obj); while (o) { y = parent(o); n++; x = rtn(o, arg); ! user-supplied routine returning x. ! if x < 0: skip up to next parent ! if x = 0: jump across to next sibling ! if x > 0: continue down to child objects if (y ~= parent(o)) { RunTimeError(15, o); rfalse; } if (x > 0 && child(o)) o = child(o); else while (o) { if (++x > 0 && sibling(o)) { o = sibling(o); break; } o = parent(o); if (o == obj) return n; } } ]; ! ---------------------------------------------------------------------------- ! Much better menus can be created using one of the optional library ! extensions. These are provided for compatibility with previous practice: ! ---------------------------------------------------------------------------- [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j; menu_nesting++; .LKRD; menu_item = 0; lines = EntryR(); main_title = item_name; print "--- "; print (string) main_title; print " ---^^"; if (menu_choices ofclass Routine) menu_choices(); else print (string) menu_choices; for (::) { L__M(##Miscellany, 52, lines); print "> "; #Ifdef TARGET_ZCODE; #IfV3; read buffer parse; #Ifnot; read buffer parse DrawStatusLine; #Endif; ! V3 j = parse->1; ! number of words #Ifnot; ! TARGET_GLULX; KeyboardPrimitive(buffer, parse); j = parse-->0; ! number of words #Endif; ! TARGET_ i = parse-->1; if (j == 0 || (i == QUIT1__WD or QUIT2__WD)) { menu_nesting--; if (menu_nesting > 0) rfalse; if (deadflag == 0) <>; rfalse; } i = TryNumber(1); if (i == 0) jump LKRD; if (i < 1 || i > lines) continue; menu_item = i; j = ChoiceR(); if (j == 2) jump LKRD; if (j == 3) rfalse; } ]; #Ifdef TARGET_ZCODE; #IfV3; [ DoMenu menu_choices EntryR ChoiceR; LowKey_Menu(menu_choices, EntryR, ChoiceR); ]; #Endif; ! V3 #IfV5; [ DoMenu menu_choices EntryR ChoiceR lines main_title main_wid cl i j oldcl pkey ch cw y x; if (pretty_flag == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 7; .ReDisplay; oldcl = 0; @erase_window $ffff; #Iftrue (#version_number == 6); @set_cursor -1; ch = HDR_FONTWUNITS->0; #Ifnot; ch = 1; #Endif; i = ch * (lines+7); @split_window i; i = HDR_SCREENWCHARS->0; if (i == 0) i = 80; @set_window 1; @set_cursor 1 1; #Iftrue (#version_number == 6); @set_font 4 -> cw; cw = HDR_FONTHUNITS->0; #Ifnot; cw = 1; #Endif; style reverse; spaces(i); j=1+(i/2-main_wid)*cw; @set_cursor 1 j; print (string) main_title; y=1+ch; @set_cursor y 1; spaces(i); x=1+cw; @set_cursor y x; print (string) NKEY__TX; j=1+(i-13)*cw; @set_cursor y j; print (string) PKEY__TX; y=y+ch; @set_cursor y 1; spaces(i); @set_cursor y x; print (string) RKEY__TX; j=1+(i-18)*cw; @set_cursor y j; if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; style roman; y = y+2*ch; @set_cursor y x; font off; if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); x = 1+3*cw; for (::) { if (cl ~= oldcl) { if (oldcl>0) { y=1+(oldcl-1)*ch; @set_cursor y x; print " "; } y=1+(cl-1)*ch; @set_cursor y x; print ">"; } oldcl = cl; @read_char 1 -> pkey; if (pkey == NKEY1__KY or NKEY2__KY or 130) { cl++; if (cl == 7+lines) cl = 7; continue; } if (pkey == PKEY1__KY or PKEY2__KY or 129) { cl--; if (cl == 6) cl = 6+lines; continue; } if (pkey == QKEY1__KY or QKEY2__KY or 27 or 131) break; if (pkey == 10 or 13 or 132) { @set_window 0; font on; new_line; new_line; new_line; menu_item = cl-6; EntryR(); @erase_window $ffff; @split_window ch; i = HDR_SCREENWCHARS->0; if ( i== 0) i = 80; @set_window 1; @set_cursor 1 1; style reverse; spaces(i); j=1+(i/2-item_width)*cw; @set_cursor 1 j; print (string) item_name; style roman; @set_window 0; new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); @read_char 1 -> pkey; jump ReDisplay; } } menu_nesting--; if (menu_nesting > 0) rfalse; font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; #Iftrue (#version_number == 6); @set_cursor -2; #Endif; new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! V5 #Ifnot; ! TARGET_GLULX [ DoMenu menu_choices EntryR ChoiceR winwid winhgt lines main_title main_wid cl i oldcl pkey; if (pretty_flag == 0 || gg_statuswin == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 0; ! If we printed "hit arrow keys" here, it would be appropriate to ! check for the availability of Glk input keys. But we actually ! print "hit N/P/Q". So it's reasonable to silently accept Glk ! arrow key codes as secondary options. .ReDisplay; glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(lines+7); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-main_wid, 0); print (string) main_title; glk_window_move_cursor(gg_statuswin, 1, 1); print (string) NKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-13, 1); print (string) PKEY__TX; glk_window_move_cursor(gg_statuswin, 1, 2); print (string) RKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-18, 2); if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; glk_set_style(style_Normal); glk_window_move_cursor(gg_statuswin, 1, 4); if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); oldcl = -1; for (::) { if (cl ~= oldcl) { if (cl < 0 || cl >= lines) cl = 0; if (oldcl >= 0) { glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) ' '; } oldcl = cl; glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) '>'; } pkey = KeyCharPrimitive(gg_statuswin, true); if (pkey == $80000000) jump ReDisplay; if (pkey == NKEY1__KY or NKEY2__KY or $fffffffb) { cl++; if (cl >= lines) cl = 0; continue; } if (pkey == PKEY1__KY or PKEY2__KY or $fffffffc) { cl--; if (cl < 0) cl = lines-1; continue; } if (pkey == QKEY1__KY or QKEY2__KY or $fffffff8 or $fffffffe) break; if (pkey == $fffffffa or $fffffffd) { glk_set_window(gg_mainwin); new_line; new_line; new_line; menu_item = cl+1; EntryR(); glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(1); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-item_width, 0); print (string) item_name; glk_set_style(style_Normal); glk_set_window(gg_mainwin); new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); pkey = KeyCharPrimitive(gg_mainwin, 1); jump ReDisplay; } } ! done with this menu... menu_nesting--; if (menu_nesting > 0) rfalse; glk_set_window(gg_mainwin); glk_window_clear(gg_mainwin); new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! A cunning routine (which could have been a daemon, but isn't, for the ! sake of efficiency) to move objects which could be in many rooms about ! so that the player never catches one not in place ! ---------------------------------------------------------------------------- [ MoveFloatingObjects i k l m address flag; if (location == player or nothing) return; objectloop (i) { address = i.&found_in; if (address && i hasnt non_floating && ~~IndirectlyContains(player, i)) { if (metaclass(address-->0) == Routine) flag = i.found_in(); else { flag = false; k = i.#found_in/WORDSIZE; for (l=0 : ll; if ((m in Class && location ofclass m) || m == location || m in location) { flag = true; break; } } } if (flag) { if (i notin location) move i to location; } else { if (parent(i)) remove i; } } } ]; ! ---------------------------------------------------------------------------- ! Two little routines for moving the player safely. ! ---------------------------------------------------------------------------- [ PlayerTo newplace flag; NoteDeparture(); move player to newplace; while (parent(newplace)) newplace = parent(newplace); location = real_location = newplace; MoveFloatingObjects(); AdjustLight(1); switch (flag) { 0: ; 1: NoteArrival(); ScoreArrival(); 2: LookSub(1); } ]; [ MovePlayer direc; ; ; ]; ! ---------------------------------------------------------------------------- ! The handy YesOrNo routine, and some "meta" verbs ! ---------------------------------------------------------------------------- [ YesOrNo noStatusRedraw i j; for (::) { #Ifdef TARGET_ZCODE; if (location == nothing || parent(player) == nothing || noStatusRedraw) read buffer parse; else read buffer parse DrawStatusLine; j = parse->1; #Ifnot; ! TARGET_GLULX; noStatusRedraw = 0; ! suppress warning KeyboardPrimitive(buffer, parse); j = parse-->0; #Endif; ! TARGET_ if (j) { ! at least one word entered i = parse-->1; if (i == YES1__WD or YES2__WD or YES3__WD) rtrue; if (i == NO1__WD or NO2__WD or NO3__WD) rfalse; } L__M(##Quit, 1); print "> "; } ]; #Ifdef TARGET_ZCODE; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart, 1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub; restore Rmaybe; return L__M(##Restore, 1); .RMaybe; L__M(##Restore, 2); ]; [ SaveSub flag; #IfV5; @save -> flag; switch (flag) { 0: L__M(##Save, 1); 1: L__M(##Save, 2); 2: RestoreColours(); L__M(##Restore, 2); } #Ifnot; save Smaybe; return L__M(##Save, 1); .SMaybe; L__M(##Save, 2); #Endif; ! V5 ]; [ VerifySub; @verify ?Vmaybe; jump Vwrong; .Vmaybe; return L__M(##Verify, 1); .Vwrong; L__M(##Verify, 2); ]; [ ScriptOnSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode) return L__M(##ScriptOn, 1); @output_stream 2; if (((HDR_GAMEFLAGS-->0) & 1) == 0) return L__M(##ScriptOn, 3); L__M(##ScriptOn, 2); transcript_mode = true; ]; [ ScriptOffSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode == false) return L__M(##ScriptOff, 1); L__M(##ScriptOff, 2); @output_stream -2; if ((HDR_GAMEFLAGS-->0) & 1) return L__M(##ScriptOff, 3); transcript_mode = false; ]; [ CommandsOnSub; @output_stream 4; xcommsdir = 1; L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (xcommsdir == 1) @output_stream -4; xcommsdir = 0; L__M(##CommandsOff, 1); ]; [ CommandsReadSub; @input_stream 1; xcommsdir = 2; L__M(##CommandsRead, 1); ]; #Ifnot; ! TARGET_GLULX; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart,1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub res fref; fref = glk_fileref_create_by_prompt($01, $02, 0); if (fref == 0) jump RFailed; gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump RFailed; @restore gg_savestr res; glk_stream_close(gg_savestr, 0); gg_savestr = 0; .RFailed; L__M(##Restore, 1); ]; [ SaveSub res fref; fref = glk_fileref_create_by_prompt($01, $01, 0); if (fref == 0) jump SFailed; gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump SFailed; @save gg_savestr res; if (res == -1) { ! The player actually just typed "restore". We're going to print ! L__M(##Restore,2); the Z-Code Inform library does this correctly ! now. But first, we have to recover all the Glk objects; the values ! in our global variables are all wrong. GGRecoverObjects(); glk_stream_close(gg_savestr, 0); gg_savestr = 0; return L__M(##Restore, 2); } glk_stream_close(gg_savestr, 0); gg_savestr = 0; if (res == 0) return L__M(##Save, 2); .SFailed; L__M(##Save, 1); ]; [ VerifySub res; @verify res; if (res == 0) return L__M(##Verify, 1); L__M(##Verify, 2); ]; [ ScriptOnSub; if (gg_scriptstr) return L__M(##ScriptOn, 1); if (gg_scriptfref == 0) { gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK); if (gg_scriptfref == 0) jump S1Failed; } gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK); if (gg_scriptstr == 0) jump S1Failed; glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); L__M(##ScriptOn, 2); VersionSub(); return; .S1Failed; L__M(##ScriptOn, 3); ]; [ ScriptOffSub; if (gg_scriptstr == 0) return L__M(##ScriptOff,1); L__M(##ScriptOff, 2); glk_stream_close(gg_scriptstr, 0); gg_scriptstr = 0; ]; [ CommandsOnSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsOn, 2); else return L__M(##CommandsOn, 3); } fref = glk_fileref_create_by_prompt($103, $01, 0); if (fref == 0) return L__M(##CommandsOn, 4); gg_command_reading = false; gg_commandstr = glk_stream_open_file(fref, $01, GG_COMMANDWSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsOn, 4); L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (gg_commandstr == 0) return L__M(##CommandsOff, 2); if (gg_command_reading) return L__M(##CommandsRead, 5); glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; L__M(##CommandsOff, 1); ]; [ CommandsReadSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsRead, 2); else return L__M(##CommandsRead, 3); } fref = glk_fileref_create_by_prompt($103, $02, 0); if (fref == 0) return L__M(##CommandsRead, 4); gg_command_reading = true; gg_commandstr = glk_stream_open_file(fref, $02, GG_COMMANDRSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsRead, 4); L__M(##CommandsRead, 1); ]; #Endif; ! TARGET_; [ NotifyOnSub; notify_mode = true; L__M(##NotifyOn); ]; [ NotifyOffSub; notify_mode = false; L__M(##NotifyOff); ]; [ Places1Sub i j k; L__M(##Places, 1); objectloop (i has visited) j++; objectloop (i has visited) { print (name) i; k++; if (k == j) return L__M(##Places, 2); if (k == j-1) print (SerialComma) j, (string) AND__TX; else print (string) COMMA__TX; } ]; [ Objects1Sub i j f; L__M(##Objects, 1); objectloop (i has moved) { f = 1; print (the) i; j = parent(i); if (j) { if (j == player) { if (i has worn) L__M(##Objects, 3, j, i); else L__M(##Objects, 4, j, i); jump Obj__Ptd; } if (j has animate) { L__M(##Objects, 5, j, i); jump Obj__Ptd; } if (j has visited) { L__M(##Objects, 6, j, i); jump Obj__Ptd; } if (j has container) { L__M(##Objects, 8, j, i); jump Obj__Ptd; } if (j has supporter) { L__M(##Objects, 9, j, i); jump Obj__Ptd; } if (j has enterable) { L__M(##Objects, 7, j, i); jump Obj__Ptd; } } L__M(##Objects, 10, j, i); .Obj__Ptd; new_line; } if (f == 0) L__M(##Objects, 2); ]; ! ---------------------------------------------------------------------------- ! The scoring system ! ---------------------------------------------------------------------------- [ ScoreSub; #Ifdef NO_SCORE; if (deadflag == 0) L__M(##Score, 2); #Ifnot; if (deadflag) new_line; L__M(##Score, 1); if(PrintRank() == false) LibraryExtensions.RunAll(ext_printrank); #Endif; ! NO_SCORE ]; #Ifndef TaskScore; [ TaskScore i; return task_scores->i; ]; #Endif; [ Achieved num; if (task_done->num == 0) { task_done->num = 1; score = score + TaskScore(num); } ]; [ PANum m n; print " "; n = m; if (n < 0) { n = -m; n = n*10; } if (n < 10) { print " "; jump Panuml; } if (n < 100) { print " "; jump Panuml; } if (n < 1000) { print " "; } .Panuml; print m, " "; ]; [ FullScoreSub i; ScoreSub(); if (score == 0 || TASKS_PROVIDED == 1) rfalse; new_line; L__M(##FullScore, 1); for (i=0 : ii == 1) { PANum(TaskScore(i)); if(PrintTaskName(i) == false) LibraryExtensions.RunAll(ext_printtaskname,i); } if (things_score) { PANum(things_score); L__M(##FullScore, 2); } if (places_score) { PANum(places_score); L__M(##FullScore, 3); } new_line; PANum(score); L__M(##FullScore, 4); ]; ! ---------------------------------------------------------------------------- ! Real verbs start here: Inventory ! ---------------------------------------------------------------------------- [ InvWideSub; if (actor == player) inventory_style = ENGLISH_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = ENGLISH_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvTallSub; if (actor == player) inventory_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = NEWLINE_BIT+INDENT_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvSub x; if (child(actor) == 0) return L__M(##Inv, 1); if (inventory_style == 0) if (actor == player) return InvTallSub(); else return InvWideSub(); L__M(##Inv, 2); if (inventory_style & NEWLINE_BIT) L__M(##Inv, 3); else print " "; WriteListFrom(child(actor), inventory_style, 1); if (inventory_style & ENGLISH_BIT) L__M(##Inv, 4); #Ifndef MANUAL_PRONOUNS; objectloop (x in player) PronounNotice(x); #Endif; x = 0; ! To prevent a "not used" error AfterRoutines(); ]; ! ---------------------------------------------------------------------------- ! The object tree and determining the possibility of moves ! ---------------------------------------------------------------------------- [ CommonAncestor o1 o2 i j; ! Find the nearest object indirectly containing o1 and o2, ! or return 0 if there is no common ancestor. i = o1; while (i) { j = o2; while (j) { if (j == i) return i; j = parent(j); } i = parent(i); } return 0; ]; [ IndirectlyContains o1 o2; ! Does o1 indirectly contain o2? (Same as testing if their common ancestor is o1.) while (o2) { if (o1 == o2) rtrue; if (o2 ofclass Class) rfalse; o2 = parent(o2); } rfalse; ]; [ ObjectScopedBySomething item i j k l m; i = item; objectloop (j .& add_to_scope) { l = j.&add_to_scope; k = (j.#add_to_scope)/WORDSIZE; if (l-->0 ofclass Routine) continue; for (m=0 : mm == i) return j; } rfalse; ]; [ ObjectIsUntouchable item flag1 flag2 ancestor i; ! Determine if there's any barrier preventing the actor from moving ! things to "item". Return false if no barrier; otherwise print a ! suitable message and return true. ! If flag1 is set, do not print any message. ! If flag2 is set, also apply Take/Remove restrictions. ! If the item has been added to scope by something, it's first necessary ! for that something to be touchable. ancestor = CommonAncestor(actor, item); if (ancestor == 0) { ancestor = item; while (ancestor && (i = ObjectScopedBySomething(ancestor)) == 0) ancestor = parent(ancestor); if (i) { if (ObjectIsUntouchable(i, flag1, flag2)) return; ! An item immediately added to scope } } else ! First, a barrier between the actor and the ancestor. The actor ! can only be in a sequence of enterable objects, and only closed ! containers form a barrier. if (actor ~= ancestor) { i = parent(actor); while (i ~= ancestor) { if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } ! Second, a barrier between the item and the ancestor. The item can ! be carried by someone, part of a piece of machinery, in or on top ! of something and so on. i = parent(item); if (item ~= ancestor && i ~= player) { while (i ~= ancestor) { if (flag2 && i hasnt container && i hasnt supporter) { if (i has animate) { if (flag1) rtrue; return L__M(##Take, 6, i, noun); } if (i has transparent) { if (flag1) rtrue; return L__M(##Take, 7, i, noun); } if (flag1) rtrue; return L__M(##Take, 8, item, noun); } if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } rfalse; ]; [ AttemptToTakeObject item ancestor after_recipient i k; ! Try to transfer the given item to the actor: return false ! if successful, true if unsuccessful, printing a suitable message ! in the latter case. ! People cannot ordinarily be taken. if (item == actor) return L__M(##Take, 2, noun); if (item has animate) return L__M(##Take, 3, item); ancestor = CommonAncestor(actor, item); if (ancestor == 0) { i = ObjectScopedBySomething(item); if (i) ancestor = CommonAncestor(actor, i); } ! Is the actor indirectly inside the item? if (ancestor == item) return L__M(##Take, 4, item); ! Does the actor already directly contain the item? if (item in actor) return L__M(##Take, 5, item); ! Can the actor touch the item, or is there (e.g.) a closed container ! in the way? if (ObjectIsUntouchable(item, false, true)) rtrue; ! The item is now known to be accessible. ! Consult the immediate possessor of the item, if it's in a container ! which the actor is not in. i = parent(item); if (i && i ~= ancestor && (i has container or supporter)) { after_recipient = i; k = action; action = ##LetGo; if (RunRoutines(i, before)) { action = k; rtrue; } action = k; } if (item has scenery) return L__M(##Take, 10, item); if (item has static) return L__M(##Take, 11, item); ! The item is now known to be available for taking. Is the player ! carrying too much? If so, possibly juggle items into the rucksack ! to make room. if (ObjectDoesNotFit(item, actor) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, item, actor)) return; if (AtFullCapacity(item, actor)) return L__M(##Take, 12, item); ! Transfer the item. move item to actor; give item ~worn; ! Send "after" message to the object letting go of the item, if any. if (after_recipient) { k = action; action = ##LetGo; if (RunRoutines(after_recipient, after)) { action = k; rtrue; } action = k; } rfalse; ]; [ AtFullCapacity n s obj k; n = n; ! suppress compiler warning if (s == actor) { objectloop (obj in s) if (obj hasnt worn) k++; } else k = children(s); if (k < RunRoutines(s, capacity) || (s == player && RoomInSack())) rfalse; ]; [ RoomInSack obj ks; if (SACK_OBJECT && SACK_OBJECT in player) { ks = keep_silent; keep_silent = 2; for (obj=youngest(player) : obj : obj=elder(obj)) if (obj ~= SACK_OBJECT && obj hasnt worn or light) { ; if (obj in SACK_OBJECT) { keep_silent = ks; return L__M(##Take, 13, obj, SACK_OBJECT); } } keep_silent = ks; } rfalse; ]; ! ---------------------------------------------------------------------------- ! Support for implicit actions ! ---------------------------------------------------------------------------- [ CheckImplicitAction act o1 o2 sav_act sav_noun sav_sec res; if (o1 provides before_implicit) { sav_act = action; action = act; sav_noun = noun; noun = o1; if (o2) { sav_sec = second; second = o2; } res = RunRoutines(o1, before_implicit); action = sav_act; noun = sav_noun; if (sav_sec) second = sav_sec; } else { if (no_implicit_actions) res = 2; else res = 0; } return res; ]; [ ImplicitTake obj res ks supcon; switch (metaclass(obj)) { Class, String, Routine, nothing: rfalse; } if (obj in actor) rfalse; if (action_to_be == ##Drop && ~~IndirectlyContains(actor, obj)) rfalse; res = CheckImplicitAction(##Take, obj); ! 0 = Take object, Tell the user (normal default) ! 1 = Take object, don't Tell ! 2 = don't Take object continue (default with no_implicit_actions) ! 3 = don't Take object, don't continue if (res >= 2) rtrue; if (parent(obj) && parent(obj) has container or supporter) supcon = parent(obj); ks = keep_silent; keep_silent = 2; AttemptToTakeObject(obj); keep_silent = ks; if (obj notin actor) rtrue; if (res == 0 && ~~keep_silent) if (supcon) L__M(##Miscellany, 58, obj, supcon); else L__M(##Miscellany, 26, obj); rfalse; ]; [ ImplicitExit obj res ks; if (parent(obj) == nothing) rfalse; res = CheckImplicitAction(##Exit, obj); ! 0 = Exit object, Tell the user (normal default) ! 1 = Exit object, don't Tell ! 2 = don't Exit object continue (default with no_implicit_actions) ! 3 = don't Exit object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (parent(actor) == obj) rtrue; if (res == 0 && ~~keep_silent) L__M(##Exit, 5, obj); rfalse; ]; [ ImplicitClose obj res ks; if (obj hasnt open) rfalse; res = CheckImplicitAction(##Close, obj); ! 0 = Close object, Tell the user (normal default) ! 1 = Close object, don't Tell ! 2 = don't Close object continue (default with no_implicit_actions) ! 3 = don't Close object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (obj has open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Close, 4, obj); rfalse; ]; [ ImplicitOpen obj res temp inp1temp; if (obj has open) rfalse; res = CheckImplicitAction(##Open, obj); ! 0 = Open object, Tell the user (normal default) ! 1 = Open object, don't Tell ! 2 = don't Open object continue (default with no_implicit_actions) ! 3 = don't Open object, don't continue if (res >= 2) rtrue; if (obj has locked) rtrue; temp = keep_silent; keep_silent = 2; ; keep_silent = temp; if (obj hasnt open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Open, 6, obj); temp = action; action = ##Open; inp1temp = inp1; inp1 = obj; AfterRoutines(); inp1 = inp1temp; action = temp; rfalse; ]; [ ImplicitUnlock obj; if (obj has locked) rtrue; rfalse; ]; [ ImplicitDisrobe obj res ks; if (obj hasnt worn) rfalse; res = CheckImplicitAction(##Disrobe, obj); ! 0 = Take off object, Tell the user (normal default) ! 1 = Take off object, don't Tell ! 2 = don't Take off object continue (default with no_implicit_actions) ! 3 = don't Take off object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (obj has worn && obj in actor) rtrue; if (res == 0 && ~~keep_silent) L__M(##Drop, 3, obj); rfalse; ]; ! ---------------------------------------------------------------------------- ! Object movement verbs ! ---------------------------------------------------------------------------- [ TakeSub; if (onotheld_mode == 0 || noun notin actor) if (AttemptToTakeObject(noun)) return; if (AfterRoutines()) return; notheld_mode = onotheld_mode; if (notheld_mode == 1 || keep_silent) return; L__M(##Take, 1, noun); ]; [ RemoveSub i; i = parent(noun); if (i && i has container && i hasnt open && ImplicitOpen(i)) return L__M(##Remove, 1, i); if (i ~= second) return L__M(##Remove, 2, noun); if (i has animate) return L__M(##Take, 6, i, noun); if (AttemptToTakeObject(noun)) rtrue; action = ##Remove; if (AfterRoutines()) return; action = ##Take; if (AfterRoutines()) return; if (keep_silent) return; L__M(##Remove, 3, noun); ]; [ DropSub; if (noun == actor) return L__M(##PutOn, 4, noun); if (noun in parent(actor)) return L__M(##Drop, 1, noun); if (noun notin actor && ~~ImplicitTake(noun)) return L__M(##Drop, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; move noun to parent(actor); if (AfterRoutines() || keep_silent) return; L__M(##Drop, 4, noun); ]; [ PutOnSub ancestor; receive_action = ##PutOn; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##PutOn, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##PutOn, 2, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, before)) { action = ##PutOn; return; } action = ##PutOn; } if (second hasnt supporter) return L__M(##PutOn, 3, second); if (ancestor == actor) return L__M(##PutOn, 4, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##PutOn, 6, second); move noun to second; if (AfterRoutines()) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##PutOn; return; } action = ##PutOn; } if (keep_silent) return; if (multiflag) return L__M(##PutOn, 7); L__M(##PutOn, 8, noun, second); ]; [ InsertSub ancestor; receive_action = ##Insert; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Insert, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##Insert, 5, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second,before)) { action = ##Insert; rtrue; } action = ##Insert; if (second has container && second hasnt open && ImplicitOpen(second)) return L__M(##Insert, 3, second); } if (second hasnt container) return L__M(##Insert, 2, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##Insert, 7, second); move noun to second; if (AfterRoutines()) rtrue; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##Insert; rtrue; } action = ##Insert; } if (keep_silent) rtrue; if (multiflag) return L__M(##Insert, 8, noun); L__M(##Insert, 9, noun, second); ]; ! ---------------------------------------------------------------------------- ! Empties and transfers are routed through the actions above ! ---------------------------------------------------------------------------- [ TransferSub; if (noun notin actor && AttemptToTakeObject(noun)) return; if (second has supporter) <>; if (second == d_obj) <>; <>; ]; [ EmptySub; second = d_obj; EmptyTSub(); ]; [ EmptyTSub i j k flag; if (noun == second) return L__M(##EmptyT, 4, noun); if (ObjectIsUntouchable(noun)) return; if (noun hasnt container) return L__M(##EmptyT, 1, noun); if (noun hasnt open && ImplicitOpen(noun)) return L__M(##EmptyT, 2, noun); if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) return L__M(##EmptyT, 1, second); if (second hasnt open && ImplicitOpen(second)) return L__M(##EmptyT, 2, second); } } i = child(noun); k = children(noun); if (i == 0) return L__M(##EmptyT, 3, noun); while (i) { j = sibling(i); flag = false; if (ObjectIsUntouchable(noun)) flag = true; if (noun hasnt container) flag = true; if (noun hasnt open) flag = true; if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) flag = true; if (second hasnt open) flag = true; } } if (k-- == 0) flag = 1; if (flag) break; if (keep_silent == 0) print (name) i, (string) COLON__TX; ; i = j; } ]; ! ---------------------------------------------------------------------------- ! Gifts ! ---------------------------------------------------------------------------- [ GiveSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Give, 1, noun); if (second == actor) return L__M(##Give, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; if (second == player) { move noun to player; return L__M(##Give, 4, noun); } if (RunLife(second, ##Give)) return; L__M(##Give, 3, second); ]; [ GiveRSub; ; ]; [ ShowSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Show, 1, noun); if (second == player) <>; if (RunLife(second, ##Show)) return; L__M(##Show, 2, second); ]; [ ShowRSub; ; ]; ! ---------------------------------------------------------------------------- ! Travelling around verbs ! ---------------------------------------------------------------------------- [ EnterSub ancestor j ks; if (noun has door || noun in compass) <>; if (actor in noun) return L__M(##Enter, 1, noun); if (noun hasnt enterable) return L__M(##Enter, 2, noun, verb_word); if (parent(actor) ~= parent(noun)) { ancestor = CommonAncestor(actor, noun); if (ancestor == actor or 0) return L__M(##Enter, 4, noun); while (actor notin ancestor) { j = parent(actor); ks = keep_silent; if (parent(j) ~= ancestor || noun ~= ancestor) { L__M(##Enter, 6, j); keep_silent = 1; } ; keep_silent = ks; if (actor in j) return; } if (actor in noun) return; if (noun notin ancestor) { j = parent(noun); while (parent(j) ~= ancestor) j = parent(j); L__M(##Enter, 7, j); ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (actor notin j) return; <>; } } if (noun has container && noun hasnt open && ImplicitOpen(noun)) return L__M(##Enter, 3, noun); move actor to noun; if (AfterRoutines() || keep_silent) return; L__M(##Enter, 5, noun); if (actor == player) Locale(noun); ]; [ GetOffSub; if (parent(actor) == noun) <>; L__M(##GetOff, 1, noun); ]; [ ExitSub p; p = parent(actor); if (noun ~= nothing && noun ~= p) return L__M(##Exit, 4 ,noun); if (p == location || (location == thedark && p == real_location)) { if (actor provides posture && actor.posture) { actor.posture = 0; return L__M(##Exit, 6); } if ((location.out_to) || (location == thedark && real_location.out_to)) <>; return L__M(##Exit, 1); } if (p has container && p hasnt open && ImplicitOpen(p)) return L__M(##Exit, 2, p); if (noun == nothing) { inp1 = p; if (RunRoutines(p, before)) return; } move actor to parent(p); if (player provides posture) player.posture = 0; if (AfterRoutines() || keep_silent) return; L__M(##Exit, 3, p); if (actor == player && p has container) LookSub(1); ]; [ VagueGoSub; L__M(##VagueGo); ]; [ GoInSub; <>; ]; [ GoSub i j k movewith thedir next_loc; ! first, check if any PushDir object is touchable if (second && second notin Compass && ObjectIsUntouchable(second)) return; movewith = 0; i = parent(actor); if ((location ~= thedark && i ~= location) || (location == thedark && i ~= real_location)) { j = location; if (location == thedark) location = real_location; k = RunRoutines(i, before); if (k ~= 3) location = j; if (k == 1) { movewith = i; i = parent(i); } else { if (k) rtrue; if (ImplicitExit(i)) return L__M(##Go, 1, i); i = parent(actor); } } thedir = noun.door_dir; if (metaclass(thedir) == Routine) thedir = RunRoutines(noun, door_dir); next_loc = i.thedir; k = metaclass(next_loc); if (k == String) { print (string) next_loc; new_line; rfalse; } if (k == Routine) { next_loc = RunRoutines(i, thedir); if (next_loc == 1) rtrue; } if (k == nothing || next_loc == 0) { if (i.cant_go ~= 0 or CANTGO__TX) PrintOrRun(i, cant_go); else L__M(##Go, 2); rfalse; } if (next_loc has door) { if (next_loc has concealed) return L__M(##Go, 2); if (next_loc hasnt open && ImplicitOpen(next_loc)) { if (noun == u_obj) return L__M(##Go, 3, next_loc); if (noun == d_obj) return L__M(##Go, 4, next_loc); return L__M(##Go, 5, next_loc); } k = RunRoutines(next_loc, door_to); if (k == 0) return L__M(##Go, 6, next_loc); if (k == 1) rtrue; next_loc = k; } action = ##Going; if (RunRoutines(next_loc, before)) { action = ##Go; return; } action = ##Go; if (movewith == 0) move actor to next_loc; else move movewith to next_loc; if (actor ~= player) return L__M(##Go, 7); k = location; location = next_loc; MoveFloatingObjects(); if (OffersLight(location)) lightflag = true; else { lightflag = false; if (k == thedark) { if(DarkToDark() == false) ! From real_location To location LibraryExtensions.RunAll(ext_darktodark); if (deadflag) rtrue; } location = thedark; } NoteDeparture(); real_location = next_loc; action = ##Going; if (RunRoutines(prev_location, after)) { action = ##Go; return; } action = ##Go; if (AfterRoutines() || keep_silent) return; LookSub(1); ]; ! ---------------------------------------------------------------------------- ! Describing the world. SayWhatsOn(object) does just that (producing ! no text if nothing except possibly "scenery" and "concealed" items are). ! Locale(object) runs through the "tail end" of a Look-style room ! description for the contents of the object, printing up suitable ! descriptions as it goes. ! ---------------------------------------------------------------------------- [ SayWhatsOn descon j f; if (descon == parent(player)) rfalse; objectloop (j in descon) if (j hasnt concealed && j hasnt scenery) f = 1; if (f == 0) rfalse; L__M(##Look, 4, descon); ]; [ NotSupportingThePlayer o i; i = parent(player); while (i && i ~= visibility_ceiling) { if (i == o) rfalse; i = parent(i); if (i && i hasnt supporter) rtrue; } rtrue; ]; ! modified with the fix for L61122 [ Locale descin text_without_ALSO text_with_ALSO o p num_objs must_print_ALSO; objectloop (o in descin) give o ~workflag; num_objs = 0; objectloop (o in descin) if (o hasnt concealed && NotSupportingThePlayer(o)) { #Ifndef MANUAL_PRONOUNS; PronounNotice(o); #Endif; if (o has scenery) { if (o has supporter && child(o)) SayWhatsOn(o); } else { give o workflag; num_objs++; p = initial; if ((o has door or container) && o has open && o provides when_open) { p = when_open; jump Prop_Chosen; } if ((o has door or container) && o hasnt open && o provides when_closed) { p = when_closed; jump Prop_Chosen; } if (o has switchable && o has on && o provides when_on) { p = when_on; jump Prop_Chosen; } if (o has switchable && o hasnt on && o provides when_off) { p = when_off; } .Prop_Chosen; if (o.&describe && RunRoutines(o, describe)) { must_print_ALSO = true; give o ~workflag; num_objs--; continue; } if (o.p && (o hasnt moved || p ~= initial)) { new_line; PrintOrRun(o, p); must_print_ALSO = true; give o ~workflag; num_objs--; if (o has supporter && child(o)) SayWhatsOn(o); } } } if (num_objs == 0) return 0; if (actor ~= player) give actor concealed; if (text_without_ALSO) { new_line; if (must_print_ALSO) print (string) text_with_ALSO, " "; else print (string) text_without_ALSO, " "; WriteListFrom(child(descin), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT); } else { if (must_print_ALSO) L__M(##Look, 5, descin); else L__M(##Look, 6, descin); } if (actor ~= player) give actor ~concealed; return num_objs; ]; ! ---------------------------------------------------------------------------- ! Looking. LookSub(1) is allowed to abbreviate long descriptions, but ! LookSub(0) (which is what happens when the Look action is generated) ! isn't. (Except that these are over-ridden by the player-set lookmode.) ! ---------------------------------------------------------------------------- [ LMode1Sub; lookmode=1; L__M(##LMode1); ]; ! Brief [ LMode2Sub; lookmode=2; L__M(##LMode2); ]; ! Verbose [ LMode3Sub; lookmode=3; L__M(##LMode3); ]; ! Superbrief [ LModeNormalSub; ! 'normal' value: the default, or as set in Initialise() switch (initial_lookmode) { 1: <>; 3: <>; default: <>; } ]; [ NoteArrival descin; if (location ~= lastdesc) { if (location.initial) PrintOrRun(location, initial); if (location == thedark) { lastdesc = thedark; return; } descin = location; if(NewRoom() == false) LibraryExtensions.RunAll(ext_newroom); lastdesc = descin; } ]; [ NoteDeparture; prev_location = real_location; ]; [ ScoreArrival; if (location hasnt visited) { give location visited; if (location has scored) { score = score + ROOM_SCORE; places_score = places_score + ROOM_SCORE; } } ]; [ FindVisibilityLevels visibility_levels; visibility_levels = 1; visibility_ceiling = parent(player); while ((parent(visibility_ceiling)) && (visibility_ceiling hasnt container || visibility_ceiling has open or transparent)) { visibility_ceiling = parent(visibility_ceiling); visibility_levels++; } return visibility_levels; ]; [ LookSub allow_abbrev visibility_levels i j k nl_flag; if (parent(player) == 0) return RunTimeError(10); .MovedByInitial; if (location == thedark) { visibility_ceiling = thedark; NoteArrival(); } else { visibility_levels = FindVisibilityLevels(); if (visibility_ceiling == location) { NoteArrival(); if (visibility_ceiling ~= location) jump MovedByInitial; } } ! Printing the top line: e.g. ! Octagonal Room (on the table) (as Frodo) new_line; #Ifdef TARGET_ZCODE; style bold; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Subheader); #Endif; ! TARGET_ if (visibility_levels == 0) print (name) thedark; else { if (visibility_ceiling ~= location) print (The) visibility_ceiling; else print (name) visibility_ceiling; } #Ifdef TARGET_ZCODE; style roman; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Normal); #Endif; ! TARGET_ for (j=1,i=parent(player) : j0 : j--,i=parent(i)) give i workflag; for (j=visibility_levels : j>0 : j--) { for (i=player,k=0 : k>; else return L__M(##Search, 5, noun); if (noun has switchable) { L__M(##Examine, 3, noun); rfalse; } return L__M(##Examine, 2, noun); } i = PrintOrRun(noun, description); if (i < 2 && noun has switchable) L__M(##Examine, 3, noun); AfterRoutines(); ]; [ LookUnderSub; if (location == thedark) return L__M(##LookUnder, 1, noun); L__M(##LookUnder, 2); ]; [ VisibleContents o i f; objectloop (i in o) if (i hasnt concealed or scenery) f++; return f; ]; [ SearchSub f; if (location == thedark) return L__M(##Search, 1, noun); if (ObjectIsUntouchable(noun)) return; f = VisibleContents(noun); if (noun has supporter) { if (f == 0) return L__M(##Search, 2, noun); return L__M(##Search, 3, noun); } if (noun hasnt container) return L__M(##Search, 4, noun); if (noun hasnt transparent or open && ImplicitOpen(noun)) return L__M(##Search, 5, noun); if (AfterRoutines()) return; if (f == 0) return L__M(##Search, 6, noun); L__M(##Search, 7, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which change the state of objects without moving them ! ---------------------------------------------------------------------------- [ UnlockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Unlock, 1, noun); if (noun hasnt locked) return L__M(##Unlock, 2, noun); if (noun.with_key ~= second) return L__M(##Unlock, 3, second); give noun ~locked; if (AfterRoutines() || keep_silent) return; L__M(##Unlock, 4, noun); ]; [ LockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Lock, 1, noun); if (noun has locked) return L__M(##Lock, 2 ,noun); if (noun has open && ImplicitClose(noun)) return L__M(##Lock, 3, noun); if (noun.with_key ~= second) return L__M(##Lock, 4, second); give noun locked; if (AfterRoutines() || keep_silent) return; L__M(##Lock, 5, noun); ]; [ SwitchonSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOn, 1, noun); if (noun has on) return L__M(##SwitchOn, 2, noun); give noun on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOn, 3, noun); ]; [ SwitchoffSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOff, 1, noun); if (noun hasnt on) return L__M(##SwitchOff, 2, noun); give noun ~on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOff, 3, noun); ]; [ OpenSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Open, 1, noun); if (noun has locked && ImplicitUnlock(noun)) return L__M(##Open, 2, noun); if (noun has open) return L__M(##Open, 3, noun); give noun open; if (keep_silent || AfterRoutines()) return; if (noun hasnt container) return L__M(##Open, 5, noun); if ((noun has container && location ~= thedark && VisibleContents(noun) && IndirectlyContains(noun, player)) == 0) { if (noun hasnt transparent && noun hasnt door) return L__M(##Open, 4, noun); } L__M(##Open, 5, noun); ]; [ CloseSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Close, 1, noun); if (noun hasnt open) return L__M(##Close, 2, noun); give noun ~open; if (AfterRoutines() || keep_silent) return; L__M(##Close, 3, noun); ]; [ DisrobeSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt worn) return L__M(##Disrobe, 1, noun); give noun ~worn; if (AfterRoutines() || keep_silent) return; L__M(##Disrobe, 2, noun); ]; [ WearSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt clothing) return L__M(##Wear, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wear, 2, noun); if (noun has worn) return L__M(##Wear, 3, noun); give noun worn; if (AfterRoutines() || keep_silent) return; L__M(##Wear, 4, noun); ]; [ EatSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt edible) return L__M(##Eat, 1, noun); if (noun has worn && ImplicitDisrobe(noun)) return; remove noun; if (AfterRoutines() || keep_silent) return; L__M(##Eat, 2, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which are really just stubs (anything which happens for these ! actions must happen in before rules) ! ---------------------------------------------------------------------------- [ AllowPushDir i; if (parent(second) ~= compass) return L__M(##PushDir, 2, noun); if (second == u_obj or d_obj) return L__M(##PushDir, 3, noun); AfterRoutines(); i = noun; move i to actor; ; if (location == thedark) move i to real_location; else move i to location; ]; [ AnswerSub; if (second && RunLife(second,##Answer)) rfalse; L__M(##Answer, 1, noun); ]; [ AskSub; if (RunLife(noun,##Ask)) rfalse; L__M(##Ask, 1, noun); ]; [ AskForSub; if (noun == player) <>; L__M(##Order, 1, noun); ]; [ AskToSub; L__M(##Order, 1, noun); ]; [ AttackSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && RunLife(noun, ##Attack)) rfalse; L__M(##Attack, 1, noun); ]; [ BlowSub; L__M(##Blow, 1, noun); ]; [ BurnSub; if (noun has animate) return L__M(##Burn, 2, noun); L__M(##Burn, 1, noun); ]; [ BuySub; L__M(##Buy, 1, noun); ]; [ ClimbSub; if (noun has animate) return L__M(##Climb, 2, noun); L__M(##Climb, 1, noun); ]; [ ConsultSub; L__M(##Consult, 1, noun); ]; [ CutSub; if (noun has animate) return L__M(##Cut, 2, noun); L__M(##Cut, 1, noun); ]; [ DigSub; L__M(##Dig, 1, noun); ]; [ DrinkSub; L__M(##Drink, 1, noun); ]; [ FillSub; if (second == nothing) return L__M(##Fill, 1, noun); L__M(##Fill, 2, noun, second); ]; [ JumpSub; L__M(##Jump, 1, noun); ]; [ JumpInSub; if (noun has animate) return L__M(##JumpIn, 2, noun); if (noun has enterable) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOnSub; if (noun has animate) return L__M(##JumpOn, 2, noun); if (noun has enterable && noun has supporter) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOverSub; if (noun has animate) return L__M(##JumpOver, 2, noun); L__M(##JumpOver, 1, noun); ]; [ KissSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##Kiss)) return; if (noun == actor) return L__M(##Touch, 3, noun); L__M(##Kiss, 1, noun); ]; [ ListenSub; L__M(##Listen, 1, noun); ]; [ MildSub; L__M(##Mild, 1, noun); ]; [ NoSub; L__M(##No); ]; [ PraySub; L__M(##Pray, 1, noun); ]; [ PullSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Pull, 1, noun); if (noun == actor) return L__M(##Pull, 6, noun); if (noun has static) return L__M(##Pull, 2, noun); if (noun has scenery) return L__M(##Pull, 3, noun); if (noun has animate) return L__M(##Pull, 5, noun); L__M(##Pull, 4, noun); ]; [ PushSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Push, 1, noun); if (noun == actor) return L__M(##Push, 5, noun); if (noun has static) return L__M(##Push, 2, noun); if (noun has scenery) return L__M(##Push, 3, noun); if (noun has animate) return L__M(##Push, 5, noun); L__M(##Push, 4, noun); ]; [ PushDirSub; L__M(##PushDir, 1, noun); ]; [ RubSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Rub, 2, noun); L__M(##Rub, 1, noun); ]; [ SetSub; L__M(##Set, 1, noun); ]; [ SetToSub; L__M(##SetTo, 1, noun); ]; [ SingSub; L__M(##Sing, 1, noun); ]; [ SleepSub; L__M(##Sleep, 1, noun); ]; [ SmellSub; if (noun ~= nothing && noun has animate) return L__M(##Smell, 2, noun); L__M(##Smell, 1, noun); ]; [ SorrySub; L__M(##Sorry, 1, noun); ]; [ SqueezeSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && noun ~= player) return L__M(##Squeeze, 1, noun); L__M(##Squeeze, 2, noun); ]; [ StrongSub; L__M(##Strong, 1, noun); ]; [ SwimSub; L__M(##Swim, 1, noun); ]; [ SwingSub; L__M(##Swing, 1, noun); ]; [ TasteSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Taste, 2, noun); L__M(##Taste, 1, noun); ]; [ TellSub; if (noun == actor) return L__M(##Tell, 1, noun); if (RunLife(noun, ##Tell)) return; L__M(##Tell, 2, noun); ]; [ ThinkSub; L__M(##Think, 1, noun); ]; [ ThrowAtSub; if (ObjectIsUntouchable(noun)) return; if (second > 1) { action = ##ThrownAt; if (RunRoutines(second, before)) { action = ##ThrowAt; rtrue; } action = ##ThrowAt; } if (noun has worn && ImplicitDisrobe(noun)) return; if (second hasnt animate) return L__M(##ThrowAt, 1, noun); if (RunLife(second, ##ThrowAt)) return; L__M(##ThrowAt, 2, noun); ]; [ TieSub; if (noun has animate) return L__M(##Tie, 2, noun); L__M(##Tie, 1, noun); ]; [ TouchSub; if (noun == actor) return L__M(##Touch, 3, noun); if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Touch, 1, noun); L__M(##Touch, 2,noun); ]; [ TurnSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Turn, 1, noun); if (noun == actor) return L__M(##Turn, 5, noun); if (noun has static) return L__M(##Turn, 2, noun); if (noun has scenery) return L__M(##Turn, 3, noun); if (noun has animate) return L__M(##Turn, 5, noun); L__M(##Turn, 4, noun); ]; [ WaitSub; if (AfterRoutines()) rtrue; L__M(##Wait, 1, noun); ]; [ WakeSub; L__M(##Wake, 1, noun); ]; [ WakeOtherSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##WakeOther)) return; L__M(##WakeOther, 1, noun); ]; [ WaveSub; if (noun == player) return L__M(##Wave, 2 ,noun, second); if (noun == actor) return L__M(##Wave, 3, noun, second); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wave, 1, noun); L__M(##Wave, 2, noun, second); ]; [ WaveHandsSub; if (noun) return L__M(##WaveHands, 2, noun); L__M(##WaveHands, 1, noun); ]; [ YesSub; L__M(##Yes); ]; ! ---------------------------------------------------------------------------- ! Debugging verbs ! ---------------------------------------------------------------------------- #Ifdef DEBUG; [ TraceOnSub; parser_trace = 1; "[Trace on.]"; ]; [ TraceLevelSub; parser_trace = noun; print "[Parser tracing set to level ", parser_trace, ".]^"; ]; [ TraceOffSub; parser_trace = 0; "Trace off."; ]; [ RoutinesOnSub; debug_flag = debug_flag | DEBUG_MESSAGES; "[Message listing on.]"; ]; [ RoutinesOffSub; debug_flag = debug_flag & ~DEBUG_MESSAGES; "[Message listing off.]"; ]; [ RoutinesVerboseSub; debug_flag = debug_flag | (DEBUG_VERBOSE|DEBUG_MESSAGES); "[Verbose message listing on.]"; ]; [ ActionsOnSub; debug_flag = debug_flag | DEBUG_ACTIONS; "[Action listing on.]"; ]; [ ActionsOffSub; debug_flag = debug_flag & ~DEBUG_ACTIONS; "[Action listing off.]"; ]; [ TimersOnSub; debug_flag = debug_flag | DEBUG_TIMERS; "[Timers listing on.]"; ]; [ TimersOffSub; debug_flag = debug_flag & ~DEBUG_TIMERS; "[Timers listing off.]"; ]; #Ifdef VN_1610; [ ChangesOnSub; debug_flag = debug_flag | DEBUG_CHANGES; "[Changes listing on.]"; ]; [ ChangesOffSub; debug_flag = debug_flag & ~DEBUG_CHANGES; "[Changes listing off.]"; ]; #Ifnot; [ ChangesOnSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; [ ChangesOffSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; #Endif; ! VN_1610 #Ifdef TARGET_ZCODE; [ PredictableSub i; i = random(-100); "[Random number generator now predictable.]"; ]; #Ifnot; ! TARGET_GLULX; [ PredictableSub; @setrandom 100; "[Random number generator now predictable.]"; ]; #Endif; ! TARGET_; [ XTestMove obj dest; if (~~obj ofclass Object) "[Not an object.]"; if (~~dest ofclass Object) "[Destination not an object.]"; if ((obj <= InformLibrary) || (obj == LibraryMessages) || (obj in 1)) "[Can't move ", (name) obj, ": it's a system object.]"; while (dest) { if (dest == obj) "[Can't move ", (name) obj, ": it would contain itself.]"; dest = parent(dest); } rfalse; ]; [ XPurloinSub; if (XTestMove(noun, player)) return; move noun to player; give noun moved ~concealed; "[Purloined.]"; ]; [ XAbstractSub; if (XTestMove(noun, second)) return; move noun to second; "[Abstracted.]"; ]; [ XObj obj f; if (parent(obj) == 0) print (name) obj; else print (a) obj; print " (", obj, ") "; if (f && parent(obj)) print "in ~", (name) parent(obj), "~ (", parent(obj), ")"; new_line; if (child(obj) == 0) rtrue; if (obj == Class) ! ??? WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+NOARTICLE_BIT, 1); else WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+FULLINV_BIT, 1); ]; [ XTreeSub i; if (noun && ~~noun ofclass Object) "[Not an object.]"; if (noun == 0) { objectloop (i) if (i ofclass Object && parent(i) == 0) XObj(i); } else XObj(noun, true); ]; [ GotoSub; if ((~~noun ofclass Object) || parent(noun)) "[Not a safe place.]"; PlayerTo(noun); ]; [ GoNearSub x; if (~~noun ofclass Object) "[Not a safe place.]"; x = noun; while (parent(x)) x = parent(x); PlayerTo(x); ]; [ Print_ScL obj; print_ret ++x_scope_count, ": ", (a) obj, " (", obj, ")"; ]; [ ScopeSub; if (noun && ~~noun ofclass Object) "[Not an object.]"; x_scope_count = 0; LoopOverScope(Print_ScL, noun); if (x_scope_count == 0) "Nothing is in scope."; ]; #Ifdef TARGET_GLULX; [ GlkListSub id val; id = glk_window_iterate(0, gg_arguments); while (id) { print "Window ", id, " (", gg_arguments-->0, "): "; val = glk_window_get_type(id); switch (val) { 1: print "pair"; 2: print "blank"; 3: print "textbuffer"; 4: print "textgrid"; 5: print "graphics"; default: print "unknown"; } val = glk_window_get_parent(id); if (val) print ", parent is window ", val; else print ", no parent (root)"; val = glk_window_get_stream(id); print ", stream ", val; val = glk_window_get_echo_stream(id); if (val) print ", echo stream ", val; print "^"; id = glk_window_iterate(id, gg_arguments); } id = glk_stream_iterate(0, gg_arguments); while (id) { print "Stream ", id, " (", gg_arguments-->0, ")^"; id = glk_stream_iterate(id, gg_arguments); } id = glk_fileref_iterate(0, gg_arguments); while (id) { print "Fileref ", id, " (", gg_arguments-->0, ")^"; id = glk_fileref_iterate(id, gg_arguments); } val = glk_gestalt(gestalt_Sound, 0); if (val) { id = glk_schannel_iterate(0, gg_arguments); while (id) { print "Soundchannel ", id, " (", gg_arguments-->0, ")^"; id = glk_schannel_iterate(id, gg_arguments); } } ]; #Endif; ! TARGET_; #Endif; ! DEBUG ! ---------------------------------------------------------------------------- ! Finally: the mechanism for library text (the text is in the language defn) ! ---------------------------------------------------------------------------- [ L__M act n x1 x2 s; if (keep_silent == 2) return; s = sw__var; sw__var = act; if (n == 0) n = 1; L___M(n, x1, x2); sw__var = s; ]; [ L___M n x1 x2 s; s = action; lm_n = n; lm_o = x1; lm_s = x2; action = sw__var; if (RunRoutines(LibraryMessages, before)) { action = s; rfalse; } if (LibraryExtensions.RunWhile(ext_messages, false )) { action = s; rfalse; } action = s; LanguageLM(n, x1, x2); ]; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_VERBLIB; #Ifnot; ! LIBRARY_STAGE < AFTER_VERBLIB but ~= AFTER_PARSER ! (this shouldn't happen because if 'parser' isn't there, LIBRARY_STAGE isn't defined) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; #Ifnot; ! LIBRARY_STAGE >= AFTER_VERBLIB: already included Message "Warning: 'verblib' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; #Ifnot; ! LIBRARY_STAGE is not defined (likely, 'parser' hasn't been included) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; ! ============================================================================== The Inform 6 Library is licensed under either 1) The traditional Inform license as described by the DM4, or 2) The Artistic License 2.0 (see ARTISTIC). The user is free to choose which license to accept, i.e., free to choose either set of terms and conditions. Here is the relevant bit from the Inform Designer's Manual, 4th edition: Copyright on Inform, the program and its source code, its example games and documentation (including this book) is retained by Graham Nelson, who asserts the moral right to be identified as the author under the Copyrights, Designs and Patents Act 1988. Having said this, I am happy for it to be freely distributed to anybody who wants a copy, provided that: (a) distributed copies are not substantially different from those archived by the author, (b) this and other copyright messages are always retained in full, and (c) no profit is involved. (Exceptions to these rules must be negotiated directly with the author.) However, a story file produced with the Inform compiler (and libraries) then belongs to its author, and may be sold for profit if desired, provided that its game banner contains the information that it was compiled by Inform, and the Inform version number. ! ============================================================================== ! LINKLPA: Link declarations of common properties and attributes. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! This file is automatically Included in your game file by "Parser". ! ============================================================================== System_file; ! ------------------------------------------------------------------------------ ! Some VM-specific constants. ! (WORDSIZE and TARGET_XXX are defined by the compiler.) ! ------------------------------------------------------------------------------ #Ifdef TARGET_ZCODE; Constant NULL = $ffff; Constant WORD_HIGHBIT = $8000; #Ifnot; ! TARGET_GLULX Constant NULL = $ffffffff; Constant WORD_HIGHBIT = $80000000; #Endif; ! TARGET_ ! ------------------------------------------------------------------------------ ! The common attributes and properties. ! ------------------------------------------------------------------------------ Attribute animate; #Ifdef USE_MODULES; #Iffalse (animate == 0); Message error "Please move your Attribute declarations after the Include ~Parser~ line: otherwise it will be impossible to USE_MODULES"; #Endif; #Endif; Attribute absent; Attribute non_floating alias absent; Attribute clothing; Attribute concealed; Attribute container; Attribute door; Attribute edible; Attribute enterable; Attribute general; Attribute light; Attribute lockable; Attribute locked; Attribute moved; Attribute on; Attribute open; Attribute openable; Attribute proper; Attribute scenery; Attribute scored; Attribute static; Attribute supporter; Attribute switchable; Attribute talkable; Attribute transparent; Attribute visited; Attribute workflag; Attribute worn; Attribute male; Attribute female; Attribute neuter; Attribute pluralname; ! ------------------------------------------------------------------------------ Property additive before NULL; Property additive after NULL; Property additive life NULL; Property n_to; Property s_to; Property e_to; Property w_to; Property ne_to; Property nw_to; Property se_to; Property sw_to; Property u_to; Property d_to; Property in_to; Property out_to; #Ifdef USE_MODULES; #Iffalse (7 >= n_to); Message error "Please move your Property declarations after the Include ~Parser~ line: otherwise it will be impossible to USE_MODULES"; #Endif; #Endif; Property door_to; Property with_key; Property door_dir; Property invent; Property plural; Property add_to_scope; Property list_together; Property react_before; Property react_after; Property grammar; Property additive orders; Property initial; Property when_open; Property when_closed; Property when_on; Property when_off; Property description; Property additive describe NULL; Property article "a"; Property cant_go; Property found_in; ! For fiddly reasons this can't alias Property time_left; Property number; Property additive time_out NULL; Property daemon; Property additive each_turn NULL; Property capacity 100; Property short_name 0; Property short_name_indef 0; Property parse_name 0; Property articles; Property inside_description; ! ============================================================================== ! ============================================================================== ! PARSER: Front end to parser. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ! ------------------------------------------------------------------------------ ! Inclusion of "linklpa" (which defines properties and attributes) ! Global variables, constants and arrays ! 1: outside of the parser ! 2: used within the parser ! Inclusion of natural language definition file ! (which creates a compass and direction-objects) ! Darkness and player objects ! Definition of grammar token numbering system used by Inform ! ! The InformParser object ! keyboard reading ! level 0: outer shell, conversation, errors ! 1: grammar lines ! 2: tokens ! 3: object lists ! 4: scope and ambiguity resolving ! 5: object comparisons ! 6: word comparisons ! 7: reading words and moving tables about ! pronoun management ! ! The InformLibrary object ! main game loop ! action processing ! end of turn sequence ! scope looping, before/after sequence, sending messages out ! timers, daemons, time of day, score notification ! light and darkness ! changing player personality ! tracing code (only present if DEBUG is set) ! ! Status line printing, menu display ! Printing object names with articles ! Miscellaneous utility routines ! Game banner, "version" verb, run-time errors ! ============================================================================== System_file; #Ifndef LIBRARY_STAGE; ! This file is the first one to define LIBRARY_STAGE. ! "This file already included" <=> "LIBRARY_STAGE exists" ! ------------------------------------------------------------------------------ #Ifndef VN_1633; Message fatalerror "*** Library 6.12.2 needs Inform v6.33 or later to work ***"; #Endif; ! VN_ Constant LibSerial "180611"; Constant LibRelease "6.12.2"; Constant LIBRARY_VERSION 612; Constant Grammar__Version 2; Constant BEFORE_PARSER 10; Constant AFTER_PARSER 20; Constant AFTER_VERBLIB 30; Constant AFTER_GRAMMAR 40; Constant LIBRARY_STAGE = BEFORE_PARSER; Default COMMENT_CHARACTER '*'; #Ifdef INFIX; Default DEBUG 0; #Endif; ! INFIX #Ifndef WORDSIZE; ! compiling with Z-code only compiler Default TARGET_ZCODE 0; Constant WORDSIZE 2; #Endif; ! WORDSIZE #Ifdef TARGET_ZCODE; ! offsets into Z-machine header Constant HDR_ZCODEVERSION $00; ! byte Constant HDR_TERPFLAGS $01; ! byte Constant HDR_GAMERELEASE $02; ! word Constant HDR_HIGHMEMORY $04; ! word Constant HDR_INITIALPC $06; ! word Constant HDR_DICTIONARY $08; ! word Constant HDR_OBJECTS $0A; ! word Constant HDR_GLOBALS $0C; ! word Constant HDR_STATICMEMORY $0E; ! word Constant HDR_GAMEFLAGS $10; ! word Constant HDR_GAMESERIAL $12; ! six ASCII characters Constant HDR_ABBREVIATIONS $18; ! word Constant HDR_FILELENGTH $1A; ! word Constant HDR_CHECKSUM $1C; ! word Constant HDR_TERPNUMBER $1E; ! byte Constant HDR_TERPVERSION $1F; ! byte Constant HDR_SCREENHLINES $20; ! byte Constant HDR_SCREENWCHARS $21; ! byte Constant HDR_SCREENWUNITS $22; ! word Constant HDR_SCREENHUNITS $24; ! word Constant HDR_FONTWUNITS $26; ! byte Constant HDR_FONTHUNITS $27; ! byte Constant HDR_ROUTINEOFFSET $28; ! word Constant HDR_STRINGOFFSET $2A; ! word Constant HDR_BGCOLOUR $2C; ! byte Constant HDR_FGCOLOUR $2D; ! byte Constant HDR_TERMCHARS $2E; ! word Constant HDR_PIXELSTO3 $30; ! word Constant HDR_TERPSTANDARD $32; ! two bytes Constant HDR_ALPHABET $34; ! word Constant HDR_EXTENSION $36; ! word Constant HDR_UNUSED $38; ! two words Constant HDR_INFORMVERSION $3C; ! four ASCII characters #Ifnot; ! TARGET_GLULX ! offsets into Glulx header and start of ROM Constant HDR_MAGICNUMBER $00; ! long word Constant HDR_GLULXVERSION $04; ! long word Constant HDR_RAMSTART $08; ! long word Constant HDR_EXTSTART $0C; ! long word Constant HDR_ENDMEM $10; ! long word Constant HDR_STACKSIZE $14; ! long word Constant HDR_STARTFUNC $18; ! long word Constant HDR_DECODINGTBL $1C; ! long word Constant HDR_CHECKSUM $20; ! long word Constant ROM_INFO $24; ! four ASCII characters Constant ROM_MEMORYLAYOUT $28; ! long word Constant ROM_INFORMVERSION $2C; ! four ASCII characters Constant ROM_COMPVERSION $30; ! four ASCII characters Constant ROM_GAMERELEASE $34; ! short word Constant ROM_GAMESERIAL $36; ! six ASCII characters Include "infglk"; #Endif; ! TARGET_ Include "linklpa"; Fake_Action LetGo; Fake_Action Receive; Fake_Action ThrownAt; Fake_Action Order; Fake_Action TheSame; Fake_Action PluralFound; Fake_Action ListMiscellany; Fake_Action Miscellany; Fake_Action Prompt; Fake_Action NotUnderstood; Fake_Action Going; #Ifdef NO_PLACES; Fake_Action Places; Fake_Action Objects; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ [ Main; InformLibrary.play(); ]; ! ------------------------------------------------------------------------------ #Ifdef COLOR; Constant COLOUR; #Endif; #Ifdef COLOUR; Global clr_on = 1; #Ifnot; Global clr_on = 0; #Endif; ! ------------------------------------------------------------------------------ ! Global variables and their associated Constant and Array declarations ! ------------------------------------------------------------------------------ Global location = InformLibrary; ! Must be first global defined Global sline1; ! Must be second Global sline2; ! Must be third ! (for status line display) ! ------------------------------------------------------------------------------ ! Z-Machine and interpreter issues ! ------------------------------------------------------------------------------ #Ifdef TARGET_ZCODE; Global top_object; ! Largest valid number of any tree object ! ### these globals are not meaningful... well, maybe standard_interpreter, ! but I'll decide that later (AP). Constant INDIV_PROP_START 64; ! Equivalent of a Glulx constant #Endif; ! TARGET_ZCODE Global standard_interpreter; ! The version number of the Z-Machine Standard which the ! interpreter claims to support, in form (upper byte).(lower) Global undo_flag; ! Can the interpreter provide "undo"? Global just_undone; ! Can't have two successive UNDOs Global transcript_mode; ! true when game scripting is on #Ifdef TARGET_ZCODE; Global xcommsdir; ! true if command recording is on #Endif; ! TARGET_ZCODE #Ifdef TARGET_GLULX; Constant GG_MAINWIN_ROCK 201; Constant GG_STATUSWIN_ROCK 202; Constant GG_QUOTEWIN_ROCK 203; Constant GG_SAVESTR_ROCK 301; Constant GG_SCRIPTSTR_ROCK 302; Constant GG_COMMANDWSTR_ROCK 303; Constant GG_COMMANDRSTR_ROCK 304; Constant GG_SCRIPTFREF_ROCK 401; Array gg_event --> 4; #Ifdef VN_1630; Array gg_arguments buffer 28; #Ifnot; Array gg_arguments --> 8; #Endif; ! VN_ Global gg_mainwin = 0; Global gg_statuswin = 0; Global gg_quotewin = 0; Global gg_scriptfref = 0; Global gg_scriptstr = 0; Global gg_savestr = 0; Global gg_commandstr = 0; Global gg_command_reading = 0; ! true if gg_commandstr is being replayed #Endif; ! TARGET_GLULX Global gg_statuswin_cursize = 0; Global gg_statuswin_size = 1; ! ------------------------------------------------------------------------------ ! Time and score ! (for linkage reasons, the task_* arrays are created not here but in verblib.h) ! ------------------------------------------------------------------------------ #Ifndef sys_statusline_flag; Global sys_statusline_flag = 0; ! non-zero if status line displays time #Endif; #Ifndef START_MOVE; Constant START_MOVE 0; ! Traditionally 0 for Infocom, 1 for Inform #Endif; Global turns = START_MOVE; ! Number of turns of play so far Global the_time = NULL; ! Current time (in minutes since midnight) Global time_rate = 1; ! How often time is updated Global time_step; ! By how much #Ifndef MAX_TIMERS; Constant MAX_TIMERS 32; ! Max number timers/daemons active at once #Endif; ! MAX_TIMERS Array the_timers --> MAX_TIMERS; Global active_timers; ! Number of timers/daemons actives Global score; ! The current score Global last_score; ! Score last turn (for testing for changes) Global notify_mode = true; ! Score notification Global places_score; ! Contribution to score made by visiting Global things_score; ! Contribution made by acquisition ! ------------------------------------------------------------------------------ ! The player ! ------------------------------------------------------------------------------ Global player; ! Which object the human is playing through Global deadflag; ! Normally 0, or false; 1 for dead ! 2 for victorious, and higher numbers ! represent exotic forms of death ! ------------------------------------------------------------------------------ ! Light and room descriptions ! ------------------------------------------------------------------------------ Global lightflag = true; ! Is there currently light to see by? Global real_location; ! When in darkness, location = thedark ! and this holds the real location Global prev_location; ! The previous value of real_location Global visibility_ceiling; ! Highest object in tree visible from the ! player's point of view (usually the room, ! sometimes darkness, sometimes a closed ! non-transparent container). Global lookmode = 2; ! 1=brief, 2=verbose, 3=superbrief ! room descriptions Global print_player_flag; ! If set, print something like "(as Fred)" ! in room descriptions, to reveal whom the ! player is playing through Global lastdesc; ! Value of location at time of most recent ! room description printed out ! ------------------------------------------------------------------------------ ! List writing (style bits are defined as Constants in "verblibm.h") ! ------------------------------------------------------------------------------ Global c_style; ! Current list-writer style Global lt_value; ! Common value of list_together Global listing_together; ! Object number of one member of a group ! being listed together Global listing_size; ! Size of such a group Global wlf_indent; ! Current level of indentation printed by ! WriteListFrom() Global inventory_stage = 1; ! 1 or 2 according to the context in which ! "invent" routines of objects are called Global inventory_style; ! List-writer style currently used while ! printing inventories ! ------------------------------------------------------------------------------ ! Menus and printing ! ------------------------------------------------------------------------------ Global pretty_flag = true; ! Use character graphics, or plain text? Global menu_nesting; ! Level of nesting (0 = root menu) Global menu_item; ! These are used in communicating Global item_width = 8; ! with the menu-creating routines Global item_name = "---"; Global lm_n; ! Parameters used by LibraryMessages Global lm_o; ! mechanism Global lm_s; #Ifdef DEBUG; Constant DEBUG_MESSAGES $0001; Constant DEBUG_ACTIONS $0002; Constant DEBUG_TIMERS $0004; Constant DEBUG_CHANGES $0008; Constant DEBUG_VERBOSE $0080; Global debug_flag; ! Bitmap of flags for tracing actions, ! calls to object routines, etc. Global x_scope_count; ! Used in printing a list of everything #Endif; ! DEBUG ! in scope ! five for colour control ! see http://www.inform-fiction.org/patches/L61007.html ! To enable colour define a constant or Global: COLOR or COLOUR !Global clr_on; ! has colour been enabled by the player? #Ifdef COLOUR; Global clr_fg = 1; ! foreground colour Global clr_bg = 1; ! background colour Global clr_fgstatus = 1; ! foreground colour of statusline Global clr_bgstatus = 1; ! background colour of statusline #Endif; ! COLOUR Global statuswin_current; ! if writing to top window Constant CLR_CURRENT 0; Constant CLR_DEFAULT 1; Constant CLR_BLACK 2; Constant CLR_RED 3; Constant CLR_GREEN 4; Constant CLR_YELLOW 5; Constant CLR_BLUE 6; Constant CLR_MAGENTA 7; Constant CLR_CYAN 8; Constant CLR_WHITE 9; Constant CLR_PURPLE 7; Constant CLR_AZURE 8; Constant WIN_ALL 0; Constant WIN_STATUS 1; Constant WIN_MAIN 2; ! ------------------------------------------------------------------------------ ! Action processing ! ------------------------------------------------------------------------------ Global action; ! Action currently being asked to perform Global inp1; ! 0 (nothing), 1 (number) or first noun Global inp2; ! 0 (nothing), 1 (number) or second noun Global noun; ! First noun or numerical value Global second; ! Second noun or numerical value Global keep_silent; ! If true, attempt to perform the action ! silently (e.g. for implicit takes, ! implicit opening of unlocked doors) Global reason_code; ! Reason for calling a "life" rule ! (an action or fake such as ##Kiss) Global receive_action; ! Either ##PutOn or ##Insert, whichever is ! action being tried when an object's ! "before" rule is checking "Receive" Global no_implicit_actions; ! Don't implicitly do things. ! ============================================================================== ! Parser variables: first, for communication to the parser ! ------------------------------------------------------------------------------ Global parser_trace = 0; ! Set this to 1 to make the parser trace ! tokens and lines Global parser_action; ! For the use of the parser when calling Global parser_one; ! user-supplied routines Global parser_two; ! Array inputobjs --> 16; ! For parser to write its results in Global parser_inflection; ! A property (usually "name") to find ! object names in Global parser_inflection_func; ! Programmer sets this to true when ! parser_infection is a function ! ------------------------------------------------------------------------------ ! Parser output ! ------------------------------------------------------------------------------ Global actor; ! Person asked to do something Global actors_location; ! Like location, but for the actor Global meta; ! Verb is a meta-command (such as "save") #Ifdef INFIX; Global infix_verb; ! Verb is an Infix command #Endif; Array multiple_object --> 64; ! List of multiple parameters Global multiflag; ! Multiple-object flag passed to actions ! Also used to prevent misleading MULTI_PE Global toomany_flag; ! Flag for "multiple match too large" ! (e.g. if "take all" took over 100 things) Global special_word; ! Dictionary address for "special" token Global special_number; ! Number typed for "special" token Global parsed_number; ! For user-supplied parsing routines Global consult_from; ! Word that a "consult" topic starts on Global consult_words; ! ...and number of words in topic Global asking_player; ! True during disambiguation question ! ------------------------------------------------------------------------------ ! Implicit taking ! ------------------------------------------------------------------------------ Global notheld_mode; ! To do with implicit taking Global onotheld_mode; ! "old copy of notheld_mode", ditto Global not_holding; ! Object to be automatically taken as an ! implicit command Array kept_results --> 16; ! Delayed command (while the take happens) ! ------------------------------------------------------------------------------ ! Error numbers when parsing a grammar line ! ------------------------------------------------------------------------------ Global etype; ! Error number on current line Global best_etype; ! Preferred error number so far Global nextbest_etype; ! Preferred one, if ASKSCOPE_PE disallowed Constant STUCK_PE = 1; Constant UPTO_PE = 2; Constant NUMBER_PE = 3; Constant CANTSEE_PE = 4; Constant TOOLIT_PE = 5; Constant NOTHELD_PE = 6; Constant MULTI_PE = 7; Constant MMULTI_PE = 8; Constant VAGUE_PE = 9; Constant EXCEPT_PE = 10; Constant ANIMA_PE = 11; Constant VERB_PE = 12; Constant SCENERY_PE = 13; Constant ITGONE_PE = 14; Constant JUNKAFTER_PE = 15; Constant TOOFEW_PE = 16; Constant NOTHING_PE = 17; Constant ASKSCOPE_PE = 18; ! ------------------------------------------------------------------------------ ! Pattern-matching against a single grammar line ! ------------------------------------------------------------------------------ Array pattern --> 32; ! For the current pattern match Global pcount; ! and a marker within it Array pattern2 --> 32; ! And another, which stores the best match Global pcount2; ! so far Constant PATTERN_NULL = $ffff; ! Entry for a token producing no text Array line_ttype-->32; ! For storing an analysed grammar line Array line_tdata-->32; Array line_token-->32; Global parameters; ! Parameters (objects) entered so far Global nsns; ! Number of special_numbers entered so far Global special_number1; ! First number, if one was typed Global special_number2; ! Second number, if two were typed ! ------------------------------------------------------------------------------ ! Inferences and looking ahead ! ------------------------------------------------------------------------------ Global params_wanted; ! Number of parameters needed ! (which may change in parsing) Global inferfrom; ! The point from which the rest of the ! command must be inferred Global inferword; ! And the preposition inferred Global dont_infer; ! Another dull flag Global no_infer_message = false; ! Use in ChooseObjects to suppress ! an inference message. Global action_to_be; ! (If the current line were accepted.) Global action_reversed; ! (Parameters would be reversed in order.) Global advance_warning; ! What a later-named thing will be ! ------------------------------------------------------------------------------ ! At the level of individual tokens now ! ------------------------------------------------------------------------------ Global found_ttype; ! Used to break up tokens into type Global found_tdata; ! and data (by AnalyseToken) Global token_filter; ! For noun filtering by user routines Global length_of_noun; ! Set by NounDomain to no of words in noun #Ifdef TARGET_ZCODE; Constant REPARSE_CODE = 10000; ! Signals "reparse the text" as a reply ! from NounDomain #Ifnot; ! TARGET_GLULX Constant REPARSE_CODE = $40000000; ! The parser rather gunkily adds addresses ! to REPARSE_CODE for some purposes and ! expects the result to be greater than ! REPARSE_CODE (signed comparison). ! So Glulx Inform is limited to a single ! gigabyte of storage, for the moment. #Endif; ! TARGET_ Global lookahead; ! The token after the one now being matched Global multi_mode; ! Multiple mode Global multi_wanted; ! Number of things needed in multitude Global multi_had; ! Number of things actually found Global multi_context; ! What token the multi-obj was accepted for Global indef_mode; ! "Indefinite" mode - ie, "take a brick" ! is in this mode Global indef_type; ! Bit-map holding types of specification Global indef_wanted; ! Number of items wanted (100 for all) Global indef_guess_p; ! Plural-guessing flag Global indef_owner; ! Object which must hold these items Global indef_cases; ! Possible gender and numbers of them Global indef_possambig; ! Has a possibly dangerous assumption ! been made about meaning of a descriptor? Global indef_nspec_at; ! Word at which a number like "two" was ! parsed (for backtracking) Global allow_plurals; ! Whether plurals presently allowed or not Global take_all_rule; ! Slightly different rules apply to ! "take all" than other uses of multiple ! objects, to make adjudication produce ! more pragmatically useful results ! (Not a flag: possible values 0, 1, 2) Global dict_flags_of_noun; ! Of the noun currently being parsed ! (a bitmap in #dict_par1 format) Constant DICT_VERB $01; Constant DICT_META $02; Constant DICT_PLUR $04; Constant DICT_PREP $08; Constant DICT_X654 $70; Constant DICT_NOUN $80; Global pronoun_word; ! Records which pronoun ("it", "them", ...) ! caused an error Global pronoun_obj; ! And what obj it was thought to refer to Global pronoun__word; ! Saved value Global pronoun__obj; ! Saved value ! ------------------------------------------------------------------------------ ! Searching through scope and parsing "scope=Routine" grammar tokens ! ------------------------------------------------------------------------------ Constant PARSING_REASON = 0; ! Possible reasons for searching scope Constant TALKING_REASON = 1; Constant EACH_TURN_REASON = 2; Constant REACT_BEFORE_REASON = 3; Constant REACT_AFTER_REASON = 4; Constant LOOPOVERSCOPE_REASON = 5; Constant TESTSCOPE_REASON = 6; Global scope_reason = PARSING_REASON; ! Current reason for searching scope Global scope_token; ! For "scope=Routine" grammar tokens Global scope_error; Global scope_stage; ! 1, 2 then 3 Global ats_flag = 0; ! For AddToScope routines Global ats_hls; ! Global placed_in_flag; ! To do with PlaceInScope ! ------------------------------------------------------------------------------ ! The match list of candidate objects for a given token ! ------------------------------------------------------------------------------ Constant MATCH_LIST_SIZE = 64; Array match_list --> MATCH_LIST_SIZE; ! An array of matched objects so far Array match_classes --> MATCH_LIST_SIZE; ! An array of equivalence classes for them Array match_scores --> MATCH_LIST_SIZE; ! An array of match scores for them Global number_matched; ! How many items in it? (0 means none) Global number_of_classes; ! How many equivalence classes? Global match_length; ! How many words long are these matches? Global saved_ml; Global match_from; ! At what word of the input do they begin? Global bestguess_score; ! What did the best-guess object score? ! ------------------------------------------------------------------------------ ! Low level textual manipulation ! ------------------------------------------------------------------------------ #Ifdef TARGET_ZCODE; ! 'buffer' holds the input line as typed by the player ! ! buffer->0 INPUT_BUFFER_LEN - WORDSIZE ! buffer->1 Number of characters input by player ! buffer->2 ... buffer->121 The actual characters ! buffer->122 Spare byte to allow for 'terp bugs ! ! 'parse' holds the result of parsing that line into dictionary words ! ! parse->0 MAX_BUFFER_WORDS ! parse->1 Number of words input by player ! ! parse-->1 Dictionary addr of first input word ! parse->4 Number of characters in the word ! parse->5 Start position in 'buffer' of the word ! ! parse-->3 parse->8,9 Same data for second input word ! ... ! parse-->29 parse->60,61 Same data for MAX_BUFFER_WORDS input word ! parse->62,63,64 Spare bytes (not sure why) Constant INPUT_BUFFER_LEN = WORDSIZE + 120; ! 120 is limit on input chars Constant MAX_BUFFER_WORDS = 15; ! Limit on input words Array buffer -> INPUT_BUFFER_LEN + 1; ! For main line of input Array buffer2 -> INPUT_BUFFER_LEN + 1; ! For supplementary questions Array buffer3 -> INPUT_BUFFER_LEN + 1; ! Retaining input for "AGAIN" #Ifdef VN_1630; Array parse buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer' Array parse2 buffer (MAX_BUFFER_WORDS * 4) + 3; ! Parsed data from 'buffer2' #Ifnot; Array parse -> 2 + (MAX_BUFFER_WORDS * 4) + 3; Array parse2 -> 2 + (MAX_BUFFER_WORDS * 4) + 3; #Endif; ! VN_ #Ifnot; ! TARGET_GLULX ! 'buffer' holds the input line as typed by the player ! ! buffer-->0 Number of characters input by player ! buffer->4 ... buffer->259 The actual characters ! ! 'parse' holds the result of parsing that line into dictionary words ! ! parse-->0 Number of words input by player ! ! parse-->1 Dictionary addr of first input word ! parse-->2 Number of characters in the word ! parse-->3 Start position in 'buffer' of the word ! ! parse-->4,5,6 Same data for second input word ! ... ! parse-->58,59,60 Same data for MAX_BUFFER_WORDS input word Constant INPUT_BUFFER_LEN = WORDSIZE + 256; ! 256 is limit on input chars Constant MAX_BUFFER_WORDS = 20; ! Limit on input words #Ifdef VN_1630; Array buffer buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For main line of input Array buffer2 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! For supplementary questions Array buffer3 buffer (INPUT_BUFFER_LEN-WORDSIZE); ! Retaining input for "AGAIN" #Ifnot; Array buffer -> INPUT_BUFFER_LEN; Array buffer2 -> INPUT_BUFFER_LEN; Array buffer3 -> INPUT_BUFFER_LEN; #Endif; ! VN_ Array parse --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer' Array parse2 --> 1 + (MAX_BUFFER_WORDS * 3); ! Parsed data from 'buffer2' #Endif; ! TARGET_ Constant comma_word = 'comma,'; ! An "untypeable word" used to substitute ! for commas in parse buffers Global wn; ! Word number within "parse" (from 1) Global num_words; ! Number of words typed Global num_desc; ! Number of descriptors typed Global verb_word; ! Verb word (eg, take in "take all" or ! "dwarf, take all") - address in dict Global verb_wordnum; ! its number in typing order (eg, 1 or 3) Global usual_grammar_after; ! Point from which usual grammar is parsed (it may vary from the ! above if user's routines match multi-word verbs) Global oops_from; ! The "first mistake" word number Global saved_oops; ! Used in working this out Constant OOPS_WORKSPACE_LEN 64; ! Used temporarily by "oops" routine Array oops_workspace -> OOPS_WORKSPACE_LEN; Global held_back_mode; ! Flag: is there some input from last time Global hb_wn; ! left over? (And a save value for wn.) ! (Used for full stops and "then".) Global caps_mode; ! Keep track of (The) with 'proper' caps Global print_anything_result; ! Return value from a PrintAny() routine Global initial_lookmode; ! Default, or set in Initialise() Global before_first_turn; ! True until after initial LOOK ! ---------------------------------------------------------------------------- Array PowersOfTwo_TB ! Used in converting case numbers to case --> $$100000000000 ! bitmaps $$010000000000 $$001000000000 $$000100000000 $$000010000000 $$000001000000 $$000000100000 $$000000010000 $$000000001000 $$000000000100 $$000000000010 $$000000000001; ! ============================================================================ ! Constants, and one variable, needed for the language definition file ! ---------------------------------------------------------------------------- Constant POSSESS_PK = $100; Constant DEFART_PK = $101; Constant INDEFART_PK = $102; Global short_name_case; Global dict_start; Global dict_entry_size; Global dict_end; ! ---------------------------------------------------------------------------- Include "language__"; ! The natural language definition, whose filename is taken from ! the ICL language_name variable ! ---------------------------------------------------------------------------- #Ifndef LanguageCases; Constant LanguageCases = 1; #Endif; ! LanguageCases ! ------------------------------------------------------------------------------ ! Pronouns support for the cruder (library 6/2 and earlier) version: ! only needed in English ! ------------------------------------------------------------------------------ #Ifdef EnglishNaturalLanguage; Global itobj = NULL; ! The object which is currently "it" Global himobj = NULL; ! The object which is currently "him" Global herobj = NULL; ! The object which is currently "her" Global old_itobj = NULL; ! The object which is currently "it" Global old_himobj = NULL; ! The object which is currently "him" Global old_herobj = NULL; ! The object which is currently "her" #Endif; ! EnglishNaturalLanguage ! ============================================================================ ! For present and past tenses ! ---------------------------------------------------------------------------- Constant PRESENT_TENSE 0; Constant PAST_TENSE 1; ! ============================================================================ ! For InformLibrary.actor_act() to control what happens when it aborts. ! ---------------------------------------------------------------------------- Constant ACTOR_ACT_OK 0; Constant ACTOR_ACT_ABORT_NOTUNDERSTOOD 1; Constant ACTOR_ACT_ABORT_ORDER 2; ! ============================================================================ ! "Darkness" is not really a place: but it has to be an object so that the ! location-name on the status line can be "Darkness". ! ---------------------------------------------------------------------------- Object thedark "(darkness object)" with initial 0, short_name DARKNESS__TX, description [; return L__M(##Miscellany, 17); ]; ! If you want to use the third-person of the narrative voice, you will ! need to replace this selfobj with your own. Class SelfClass with name ',a' ',b' ',c' ',d' ',e', short_name YOURSELF__TX, description [; return L__M(##Miscellany, 19); ], before NULL, after NULL, life NULL, each_turn NULL, time_out NULL, describe NULL, article "the", add_to_scope 0, capacity 100, parse_name 0, orders 0, number 0, narrative_voice 2, narrative_tense PRESENT_TENSE, nameless true, posture 0, before_implicit [;Take: return 2;], has concealed animate proper transparent; SelfClass selfobj "(self object)"; ! ============================================================================ ! The definition of the token-numbering system used by Inform. ! ---------------------------------------------------------------------------- Constant ILLEGAL_TT = 0; ! Types of grammar token: illegal Constant ELEMENTARY_TT = 1; ! (one of those below) Constant PREPOSITION_TT = 2; ! e.g. 'into' Constant ROUTINE_FILTER_TT = 3; ! e.g. noun=CagedCreature Constant ATTR_FILTER_TT = 4; ! e.g. edible Constant SCOPE_TT = 5; ! e.g. scope=Spells Constant GPR_TT = 6; ! a general parsing routine Constant NOUN_TOKEN = 0; ! The elementary grammar tokens, and Constant HELD_TOKEN = 1; ! the numbers compiled by Inform to Constant MULTI_TOKEN = 2; ! encode them Constant MULTIHELD_TOKEN = 3; Constant MULTIEXCEPT_TOKEN = 4; Constant MULTIINSIDE_TOKEN = 5; Constant CREATURE_TOKEN = 6; Constant SPECIAL_TOKEN = 7; Constant NUMBER_TOKEN = 8; Constant TOPIC_TOKEN = 9; Constant GPR_FAIL = -1; ! Return values from General Parsing Constant GPR_PREPOSITION = 0; ! Routines Constant GPR_NUMBER = 1; Constant GPR_MULTIPLE = 2; Constant GPR_REPARSE = REPARSE_CODE; Constant GPR_NOUN = $ff00; Constant GPR_HELD = $ff01; Constant GPR_MULTI = $ff02; Constant GPR_MULTIHELD = $ff03; Constant GPR_MULTIEXCEPT = $ff04; Constant GPR_MULTIINSIDE = $ff05; Constant GPR_CREATURE = $ff06; Constant ENDIT_TOKEN = 15; ! Value used to mean "end of grammar line" #Iftrue (Grammar__Version == 1); [ AnalyseToken token m; found_tdata = token; if (token < 0) { found_ttype = ILLEGAL_TT; return; } if (token <= 8) { found_ttype = ELEMENTARY_TT; return; } if (token < 15) { found_ttype = ILLEGAL_TT; return; } if (token == 15) { found_ttype = ELEMENTARY_TT; return; } if (token < 48) { found_ttype = ROUTINE_FILTER_TT; found_tdata = token - 16; return; } if (token < 80) { found_ttype = GPR_TT; found_tdata = #preactions_table-->(token-48); return; } if (token < 128) { found_ttype = SCOPE_TT; found_tdata = #preactions_table-->(token-80); return; } if (token < 180) { found_ttype = ATTR_FILTER_TT; found_tdata = token - 128; return; } found_ttype = PREPOSITION_TT; m = #adjectives_table; for (::) { if (token == m-->1) { found_tdata = m-->0; return; } m = m+4; } m = #adjectives_table; RunTimeError(1); found_tdata = m; ]; [ UnpackGrammarLine line_address i m; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } for (i=0 : i<=5 : i++) { line_token-->i = line_address->(i+1); AnalyseToken(line_token-->i); if ((found_ttype == ELEMENTARY_TT) && (found_tdata == NOUN_TOKEN) && (m == line_address->0)) { line_token-->i = ENDIT_TOKEN; break; } line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; if (found_ttype ~= PREPOSITION_TT) m++; } action_to_be = line_address->7; action_reversed = false; params_wanted = line_address->0; return line_address + 8; ]; #Ifnot; ! Grammar__Version == 2 [ AnalyseToken token; if (token == ENDIT_TOKEN) { found_ttype = ELEMENTARY_TT; found_tdata = ENDIT_TOKEN; return; } found_ttype = (token->0) & $$1111; found_tdata = (token+1)-->0; ]; #Ifdef TARGET_ZCODE; [ UnpackGrammarLine line_address i; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } action_to_be = 256*(line_address->0) + line_address->1; action_reversed = ((action_to_be & $400) ~= 0); action_to_be = action_to_be & $3ff; line_address--; params_wanted = 0; for (i=0 : : i++) { line_address = line_address + 3; if (line_address->0 == ENDIT_TOKEN) break; line_token-->i = line_address; AnalyseToken(line_address); if (found_ttype ~= PREPOSITION_TT) params_wanted++; line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; } return line_address + 1; ]; #Ifnot; ! TARGET_GLULX [ UnpackGrammarLine line_address i; for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } @aloads line_address 0 action_to_be; action_reversed = (((line_address->2) & 1) ~= 0); line_address = line_address - 2; params_wanted = 0; for (i=0 : : i++) { line_address = line_address + 5; if (line_address->0 == ENDIT_TOKEN) break; line_token-->i = line_address; AnalyseToken(line_address); if (found_ttype ~= PREPOSITION_TT) params_wanted++; line_ttype-->i = found_ttype; line_tdata-->i = found_tdata; } return line_address + 1; ]; #Endif; ! TARGET_ #Endif; ! Grammar__Version ! To protect against a bug in early versions of the "Zip" interpreter: ! Of course, in Glulx, this routine actually performs work. #Ifdef TARGET_ZCODE; [ Tokenise__ b p; b->(2 + b->1) = 0; @tokenise b p; ]; #Ifnot; ! TARGET_GLULX Array gg_tokenbuf -> DICT_WORD_SIZE; [ GGWordCompare str1 str2 ix jx; for (ix=0 : ixix) - (str2->ix); if (jx ~= 0) return jx; } return 0; ]; [ Tokenise__ buf tab cx numwords len bx ix wx wpos wlen val res dictlen entrylen; len = buf-->0; buf = buf+WORDSIZE; ! First, split the buffer up into words. We use the standard Infocom ! list of word separators (comma, period, double-quote). cx = 0; numwords = 0; while (cx < len) { while (cx < len && buf->cx == ' ') cx++; if (cx >= len) break; bx = cx; if (buf->cx == '.' or ',' or '"') cx++; else { while (cx < len && buf->cx ~= ' ' or '.' or ',' or '"') cx++; } tab-->(numwords*3+2) = (cx-bx); tab-->(numwords*3+3) = WORDSIZE+bx; numwords++; if (numwords >= MAX_BUFFER_WORDS) break; } tab-->0 = numwords; ! Now we look each word up in the dictionary. dictlen = #dictionary_table-->0; entrylen = DICT_WORD_SIZE + 7; for (wx=0 : wx(wx*3+2); wpos = tab-->(wx*3+3); ! Copy the word into the gg_tokenbuf array, clipping to DICT_WORD_SIZE ! characters and lower case. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE; cx = wpos - WORDSIZE; for (ix=0 : ixix = glk_char_to_lower(buf->(cx+ix)); for (: ixix = 0; val = #dictionary_table + WORDSIZE; @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res; tab-->(wx*3+1) = res; } ]; #Endif; ! TARGET_ ! ============================================================================ ! The InformParser object abstracts the front end of the parser. ! ! InformParser.parse_input(results) ! returns only when a sensible request has been made, and puts into the ! "results" buffer: ! ! --> 0 = The action number ! --> 1 = Number of parameters ! --> 2, 3, ... = The parameters (object numbers), but ! 0 means "put the multiple object list here" ! 1 means "put one of the special numbers here" ! ! ---------------------------------------------------------------------------- Object InformParser "(Inform Parser)" with parse_input [ results; Parser__parse(results); ], has proper; ! ---------------------------------------------------------------------------- ! The Keyboard routine actually receives the player's words, ! putting the words in "a_buffer" and their dictionary addresses in ! "a_table". It is assumed that the table is the same one on each ! (standard) call. ! ! It can also be used by miscellaneous routines in the game to ask ! yes-no questions and the like, without invoking the rest of the parser. ! ! Return the number of words typed ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ GetNthChar a_buffer n i; for (i = 0: a_buffer->(2+i) == ' ': i++) { if (i > a_buffer->(1)) return false; } return a_buffer->(2+i+n); ]; [ KeyboardPrimitive a_buffer a_table; read a_buffer a_table; #Iftrue (#version_number == 6); @output_stream -1; @loadb a_buffer 1 -> sp; @add a_buffer 2 -> sp; @print_table sp sp; new_line; @output_stream 1; #Endif; ]; [ KeyCharPrimitive win key; if (win) @set_window win; @read_char 1 -> key; return key; ]; [ KeyTimerInterrupt; rtrue; ]; [ KeyDelay tenths key; @read_char 1 tenths KeyTimerInterrupt -> key; return key; ]; #Ifnot; ! TARGET_GLULX [ GetNthChar a_buffer n i; for (i = 0: a_buffer->(4+i) == ' ': i++) { if (i > a_buffer->(1)) return false; } return a_buffer->(4+i+n); ]; [ KeyCharPrimitive win nostat done res ix jx ch; jx = ch; ! squash compiler warnings if (win == 0) win = gg_mainwin; if (gg_commandstr ~= 0 && gg_command_reading ~= false) { ! get_line_stream done = glk_get_line_stream(gg_commandstr, gg_arguments, 31); if (done == 0) { glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; ! fall through to normal user input. } else { ! Trim the trailing newline if (gg_arguments->(done-1) == 10) done = done-1; res = gg_arguments->0; if (res == '\') { res = 0; for (ix=1 : ixix; if (ch >= '0' && ch <= '9') { @shiftl res 4 res; res = res + (ch-'0'); } else if (ch >= 'a' && ch <= 'f') { @shiftl res 4 res; res = res + (ch+10-'a'); } else if (ch >= 'A' && ch <= 'F') { @shiftl res 4 res; res = res + (ch+10-'A'); } } } jump KCPContinue; } } done = false; glk_request_char_event(win); while (~~done) { glk_select(gg_event); switch (gg_event-->0) { 5: ! evtype_Arrange if (nostat) { glk_cancel_char_event(win); res = $80000000; done = true; break; } DrawStatusLine(); 2: ! evtype_CharInput if (gg_event-->1 == win) { res = gg_event-->2; done = true; } } ix = HandleGlkEvent(gg_event, 1, gg_arguments); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments); if (ix == 2) { res = gg_arguments-->0; done = true; } else if (ix == -1) { done = false; } } if (gg_commandstr ~= 0 && gg_command_reading == false) { if (res < 32 || res >= 256 || (res == '\' or ' ')) { glk_put_char_stream(gg_commandstr, '\'); done = 0; jx = res; for (ix=0 : ix<8 : ix++) { @ushiftr jx 28 ch; @shiftl jx 4 jx; ch = ch & $0F; if (ch ~= 0 || ix == 7) done = 1; if (done) { if (ch >= 0 && ch <= 9) ch = ch + '0'; else ch = (ch - 10) + 'A'; glk_put_char_stream(gg_commandstr, ch); } } } else { glk_put_char_stream(gg_commandstr, res); } glk_put_char_stream(gg_commandstr, 10); } .KCPContinue; return res; ]; [ KeyDelay tenths key done ix; glk_request_char_event(gg_mainwin); glk_request_timer_events(tenths*100); while (~~done) { glk_select(gg_event); ix = HandleGlkEvent(gg_event, 1, gg_arguments); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments); if (ix == 2) { key = gg_arguments-->0; done = true; } else if (ix >= 0 && gg_event-->0 == 1 or 2) { key = gg_event-->2; done = true; } } glk_cancel_char_event(gg_mainwin); glk_request_timer_events(0); return key; ]; [ KeyboardPrimitive a_buffer a_table done ix; if (gg_commandstr ~= 0 && gg_command_reading ~= false) { ! get_line_stream done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE, (INPUT_BUFFER_LEN-WORDSIZE)-1); if (done == 0) { glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; ! L__M(##CommandsRead, 5); would come after prompt ! fall through to normal user input. } else { ! Trim the trailing newline if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1; a_buffer-->0 = done; glk_set_style(style_Input); glk_put_buffer(a_buffer+WORDSIZE, done); glk_set_style(style_Normal); print "^"; jump KPContinue; } } done = false; glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0); while (~~done) { glk_select(gg_event); switch (gg_event-->0) { 5: ! evtype_Arrange DrawStatusLine(); 3: ! evtype_LineInput if (gg_event-->1 == gg_mainwin) { a_buffer-->0 = gg_event-->2; done = true; } } ix = HandleGlkEvent(gg_event, 0, a_buffer); if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 0, a_buffer); if (ix == 2) done = true; else if (ix == -1) done = false; } if (gg_commandstr ~= 0 && gg_command_reading == false) { ! put_buffer_stream glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0); glk_put_char_stream(gg_commandstr, 10); } .KPContinue; Tokenise__(a_buffer,a_table); ! It's time to close any quote window we've got going. if (gg_quotewin) { glk_window_close(gg_quotewin, 0); gg_quotewin = 0; } ]; #Endif; ! TARGET_ [ Keyboard a_buffer a_table nw i w w2 x1 x2; DisplayStatus(); .FreshInput; ! Save the start of the buffer, in case "oops" needs to restore it ! to the previous time's buffer for (i=0 : ii = a_buffer->i; ! In case of an array entry corruption that shouldn't happen, but would be ! disastrous if it did: #Ifdef TARGET_ZCODE; a_buffer->0 = INPUT_BUFFER_LEN - WORDSIZE; a_table->0 = MAX_BUFFER_WORDS; ! Allow to split input into this many words #Endif; ! TARGET_ ! Print the prompt, and read in the words and dictionary addresses L__M(##Prompt); if (AfterPrompt() == 0) LibraryExtensions.RunAll(ext_afterprompt); #IfV5; DrawStatusLine(); #Endif; ! V5 KeyboardPrimitive(a_buffer, a_table); nw = NumberWords(a_table); ! If the line was blank, get a fresh line if (nw == 0) { L__M(##Miscellany, 10); jump FreshInput; } ! Unless the opening word was "oops", return ! Conveniently, a_table-->1 is the first word in both ZCODE and GLULX. w = a_table-->1; if (w == OOPS1__WD or OOPS2__WD or OOPS3__WD) jump DoOops; if (a_buffer->WORDSIZE == COMMENT_CHARACTER) { #Ifdef TARGET_ZCODE; if ((HDR_GAMEFLAGS-->0) & $0001 || xcommsdir) L__M(##Miscellany, 54); else L__M(##Miscellany, 55); #Ifnot; ! TARGET_GLULX if (gg_scriptstr || gg_commandstr) L__M(##Miscellany, 54); else L__M(##Miscellany, 55); #Endif; ! TARGET_ jump FreshInput; } #IfV5; ! Undo handling if ((w == UNDO1__WD or UNDO2__WD or UNDO3__WD) && (nw==1)) { i = PerformUndo(); if (i == 0) jump FreshInput; } #Ifdef TARGET_ZCODE; @save_undo i; #Ifnot; ! TARGET_GLULX @saveundo i; if (i == -1) { GGRecoverObjects(); i = 2; } else i = (~~i); #Endif; ! TARGET_ just_undone = 0; undo_flag = 2; if (i == -1) undo_flag = 0; if (i == 0) undo_flag = 1; if (i == 2) { RestoreColours(); #Ifdef TARGET_ZCODE; style bold; #Ifnot; ! TARGET_GLULX glk_set_style(style_Subheader); #Endif; ! TARGET_ print (name) location, "^"; #Ifdef TARGET_ZCODE; style roman; #Ifnot; ! TARGET_GLULX glk_set_style(style_Normal); #Endif; ! TARGET_ L__M(##Miscellany, 13); just_undone = 1; jump FreshInput; } #Endif; ! V5 return nw; .DoOops; if (oops_from == 0) { L__M(##Miscellany, 14); jump FreshInput; } if (nw == 1) { L__M(##Miscellany, 15); jump FreshInput; } if (nw > 2) { L__M(##Miscellany, 16); jump FreshInput; } ! So now we know: there was a previous mistake, and the player has ! attempted to correct a single word of it. for (i=0 : ii = a_buffer->i; #Ifdef TARGET_ZCODE; x1 = a_table->9; ! Start of word following "oops" x2 = a_table->8; ! Length of word following "oops" #Ifnot; ! TARGET_GLULX x1 = a_table-->6; ! Start of word following "oops" x2 = a_table-->5; ! Length of word following "oops" #Endif; ! TARGET_ ! Repair the buffer to the text that was in it before the "oops" ! was typed: for (i=0 : i < OOPS_WORKSPACE_LEN : i++) a_buffer->i = oops_workspace->i; Tokenise__(a_buffer, a_table); ! Work out the position in the buffer of the word to be corrected: #Ifdef TARGET_ZCODE; w = a_table->(4*oops_from + 1); ! Start of word to go w2 = a_table->(4*oops_from); ! Length of word to go #Ifnot; ! TARGET_GLULX w = a_table-->(3*oops_from); ! Start of word to go w2 = a_table-->(3*oops_from - 1); ! Length of word to go #Endif; ! TARGET_ #IfDef OOPS_CHECK; print "[~"; for (i=0 : i(i+w); print "~ --> ~"; #Endif; ! Write spaces over the word to be corrected: for (i=0 : i(i+w) = ' '; if (w2 < x2) { ! If the replacement is longer than the original, move up... for (i=INPUT_BUFFER_LEN-1 : i>=w+x2 : i--) a_buffer->i = a_buffer->(i-x2+w2); ! ...increasing buffer size accordingly. SetKeyBufLength(GetKeyBufLength(a_buffer) + (x2-w2), a_buffer); } ! Write the correction in: for (i=0 : i(i+w) = buffer2->(i+x1); #IfDef OOPS_CHECK; print (char) buffer2->(i+x1); #Endif; } #IfDef OOPS_CHECK; print "~]^^"; #Endif; Tokenise__(a_buffer, a_table); nw=NumberWords(a_table); ! saved_ml = 0; return nw; ]; ! end of Keyboard [ PerformUndo i; if (turns == START_MOVE) { L__M(##Miscellany, 11); return 0; } if (undo_flag == 0) { L__M(##Miscellany, 6); return 0; } if (undo_flag == 1) { L__M(##Miscellany, 7); return 0; } #Ifdef TARGET_ZCODE; @restore_undo i; #Ifnot; ! TARGET_GLULX @restoreundo i; i = (~~i); #Endif; ! TARGET_ if (i == 0) { L__M(##Miscellany, 7); return 0; } L__M(##Miscellany, 1); return 1; ]; ! ========================== ! Taken from I7's Parser.i6t ! ========================== [ DictionaryWordToVerbNum dword verbnum; #Ifdef TARGET_ZCODE; verbnum = $ff-(dword->#dict_par2); #Ifnot; ! GLULX dword = dword + #dict_par2 - 1; @aloads dword 0 verbnum; verbnum = $ffff-verbnum; #Endif; return verbnum; ]; ! ========================== ! ---------------------------------------------------------------------------- ! To simplify the picture a little, a rough map of the main routine: ! ! (A) Get the input, do "oops" and "again" ! (B) Is it a direction, and so an implicit "go"? If so go to (K) ! (C) Is anyone being addressed? ! (D) Get the verb: try all the syntax lines for that verb ! (E) Break down a syntax line into analysed tokens ! (F) Look ahead for advance warning for multiexcept/multiinside ! (G) Parse each token in turn (calling ParseToken to do most of the work) ! (H) Cheaply parse otherwise unrecognised conversation and return ! (I) Print best possible error message ! (J) Retry the whole lot ! (K) Last thing: check for "then" and further instructions(s), return. ! ! The strategic points (A) to (K) are marked in the commentary. ! ! Note that there are three different places where a return can happen. ! ---------------------------------------------------------------------------- [ Parser__parse results syntax line num_lines line_address i j k token l m line_etype vw; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! A: Get the input, do "oops" and "again" ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Firstly, in "not held" mode, we still have a command left over from last ! time (eg, the user typed "eat biscuit", which was parsed as "take biscuit" ! last time, with "eat biscuit" tucked away until now). So we return that. if (notheld_mode == 1) { for (i=0 : i<8 : i++) results-->i = kept_results-->i; notheld_mode = 0; rtrue; } if (held_back_mode ~= 0) { held_back_mode = 0; Tokenise__(buffer, parse); jump ReParse; } .ReType; Keyboard(buffer, parse); #Ifdef INFIX; ! An Infix verb is a special kind of meta verb. We mark them here. if (GetNthChar(buffer, 0) == ';') infix_verb = true; else infix_verb = false; #Endif; .ReParse; parser_inflection = name; parser_inflection_func = false; ! Initially assume the command is aimed at the player, and the verb ! is the first word num_words = NumberWords(); wn = 1; #Ifdef LanguageToInformese; LanguageToInformese(); #IfV5; ! Re-tokenise: Tokenise__(buffer,parse); #Endif; ! V5 #Endif; ! LanguageToInformese if (BeforeParsing() == false) { LibraryExtensions.ext_number_1 = wn; ! Set "between calls" functionality to restore wn each pass LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN; LibraryExtensions.RunWhile(ext_beforeparsing, false); LibraryExtensions.BetweenCalls = 0; ! Turn off "between calls" functionality } num_words = NumberWords(); k=0; #Ifdef DEBUG; if (parser_trace >= 2) { print "[ "; for (i=0 : im; print "~ "; if (j == 0) print "?"; else { #Ifdef TARGET_ZCODE; if (UnsignedCompare(j, HDR_DICTIONARY-->0) >= 0 && UnsignedCompare(j, HDR_HIGHMEMORY-->0) < 0) print (address) j; else print j; #Ifnot; ! TARGET_GLULX if (j->0 == $60) print (address) j; else print j; #Endif; ! TARGET_ } if (i ~= num_words-1) print " / "; } print " ]^"; } #Endif; ! DEBUG verb_wordnum = 1; actor = player; actors_location = ScopeCeiling(player); usual_grammar_after = 0; .AlmostReParse; scope_token = 0; action_to_be = NULL; ! Begin from what we currently think is the verb word .BeginCommand; wn = verb_wordnum; verb_word = NextWordStopped(); ! If there's no input here, we must have something like "person,". if (verb_word == -1) { best_etype = STUCK_PE; jump GiveError; } ! Now try for "again" or "g", which are special cases: don't allow "again" if nothing ! has previously been typed; simply copy the previous text across if (verb_word == AGAIN2__WD or AGAIN3__WD) verb_word = AGAIN1__WD; if (verb_word == AGAIN1__WD) { if (actor ~= player) { L__M(##Miscellany, 20); jump ReType; } if (GetKeyBufLength(buffer3) == 0) { L__M(##Miscellany, 21); jump ReType; } if (WordAddress(verb_wordnum) == buffer + WORDSIZE) { ! not held back ! splice rest of buffer onto end of buffer3 i = GetKeyBufLength(buffer3); while (buffer3 -> (i + WORDSIZE - 1) == ' ' or '.') i--; j = i - WordLength(verb_wordnum); ! amount to move buffer up by if (j > 0) { for (m=INPUT_BUFFER_LEN-1 : m>=WORDSIZE+j : m--) buffer->m = buffer->(m-j); SetKeyBufLength(GetKeyBufLength()+j); } for (m=WORDSIZE : mm = buffer3->m; if (j < 0) for (:mm = ' '; } else for (i=0 : ii = buffer3->i; jump ReParse; } ! Save the present input in case of an "again" next time if (verb_word ~= AGAIN1__WD) for (i=0 : ii = buffer->i; if (usual_grammar_after == 0) { j = verb_wordnum; i = RunRoutines(actor, grammar); #Ifdef DEBUG; if (parser_trace >= 2 && actor.grammar ~= 0 or NULL) print " [Grammar property returned ", i, "]^"; #Endif; ! DEBUG #Ifdef TARGET_ZCODE; if ((i ~= 0 or 1) && (UnsignedCompare(i, dict_start) < 0 || UnsignedCompare(i, dict_end) >= 0 || (i - dict_start) % dict_entry_size ~= 0)) { usual_grammar_after = j; i=-i; } #Ifnot; ! TARGET_GLULX if (i < 0) { usual_grammar_after = j; i=-i; } #Endif; if (i == 1) { results-->0 = action; results-->1 = 0; ! Number of parameters results-->2 = noun; results-->3 = second; if (noun) results-->1 = 1; if (second) results-->1 = 2; rtrue; } if (i ~= 0) { verb_word = i; wn--; verb_wordnum--; } else { wn = verb_wordnum; verb_word = NextWord(); } } else usual_grammar_after = 0; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! B: Is it a direction, and so an implicit "go"? If so go to (K) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #Ifdef LanguageIsVerb; if (verb_word == 0) { i = wn; verb_word = LanguageIsVerb(buffer, parse, verb_wordnum); wn = i; } #Endif; ! LanguageIsVerb ! If the first word is not listed as a verb, it must be a direction ! or the name of someone to talk to if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) { ! So is the first word an object contained in the special object "compass" ! (i.e., a direction)? This needs use of NounDomain, a routine which ! does the object matching, returning the object number, or 0 if none found, ! or REPARSE_CODE if it has restructured the parse table so the whole parse ! must be begun again... wn = verb_wordnum; indef_mode = false; token_filter = 0; l = NounDomain(compass, 0, NOUN_TOKEN); if (l == REPARSE_CODE) jump ReParse; ! If it is a direction, send back the results: ! action=GoSub, no of arguments=1, argument 1=the direction. if (l ~= 0) { results-->0 = ##Go; action_to_be = ##Go; results-->1 = 1; results-->2 = l; jump LookForMore; } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! C: Is anyone being addressed? ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Only check for a comma (a "someone, do something" command) if we are ! not already in the middle of one. (This simplification stops us from ! worrying about "robot, wizard, you are an idiot", telling the robot to ! tell the wizard that she is an idiot.) if (actor == player) { for (j=2 : j<=num_words : j++) { i=NextWord(); if (i == comma_word) jump Conversation; } } vw = verb_word; verb_word = UnknownVerb(vw); if (verb_word == false) verb_word = LibraryExtensions.RunWhile(ext_unknownverb, false, vw); if (verb_word) jump VerbAccepted; best_etype = VERB_PE; jump GiveError; ! NextWord nudges the word number wn on by one each time, so we've now ! advanced past a comma. (A comma is a word all on its own in the table.) .Conversation; j = wn - 1; if (j == 1) { L__M(##Miscellany, 22); jump ReType; } ! Use NounDomain (in the context of "animate creature") to see if the ! words make sense as the name of someone held or nearby wn = 1; lookahead = HELD_TOKEN; scope_reason = TALKING_REASON; l = NounDomain(player,actors_location,CREATURE_TOKEN); scope_reason = PARSING_REASON; if (l == REPARSE_CODE) jump ReParse; if (l == 0) { L__M(##Miscellany, 23); jump ReType; } .Conversation2; ! The object addressed must at least be "talkable" if not actually "animate" ! (the distinction allows, for instance, a microphone to be spoken to, ! without the parser thinking that the microphone is human). if (l hasnt animate && l hasnt talkable) { L__M(##Miscellany, 24, l); jump ReType; } ! Check that there aren't any mystery words between the end of the person's ! name and the comma (eg, throw out "dwarf sdfgsdgs, go north"). if (wn ~= j) { L__M(##Miscellany, 25); jump ReType; } ! The player has now successfully named someone. Adjust "him", "her", "it": PronounNotice(l); ! Set the global variable "actor", adjust the number of the first word, ! and begin parsing again from there. verb_wordnum = j + 1; ! Stop things like "me, again": if (l == player) { wn = verb_wordnum; if (NextWordStopped() == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) { L__M(##Miscellany, 20); jump ReType; } } actor = l; actors_location = ScopeCeiling(l); #Ifdef DEBUG; if (parser_trace >= 1) print "[Actor is ", (the) actor, " in ", (name) actors_location, "]^"; #Endif; ! DEBUG jump BeginCommand; } ! end of first-word-not-a-verb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! D: Get the verb: try all the syntax lines for that verb ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .VerbAccepted; ! We now definitely have a verb, not a direction, whether we got here by the ! "take ..." or "person, take ..." method. Get the meta flag for this verb: meta = (verb_word->#dict_par1) & DICT_META; ! You can't order other people to "full score" for you, and so on... if (meta && actor ~= player) { best_etype = VERB_PE; meta = false; jump GiveError; } ! Now let i be the corresponding verb number, stored in the dictionary entry ! (in a peculiar 255-n fashion for traditional Infocom reasons)... i = DictionaryWordToVerbNum(verb_word); ! ...then look up the i-th entry in the verb table, whose address is at word ! 7 in the Z-machine (in the header), so as to get the address of the syntax ! table for the given verb... #Ifdef TARGET_ZCODE; syntax = (HDR_STATICMEMORY-->0)-->i; #Ifnot; ! TARGET_GLULX syntax = (#grammar_table)-->(i+1); #Endif; ! TARGET_ ! ...and then see how many lines (ie, different patterns corresponding to the ! same verb) are stored in the parse table... num_lines = (syntax->0) - 1; ! ...and now go through them all, one by one. ! To prevent pronoun_word 0 being misunderstood, pronoun_word = NULL; pronoun_obj = NULL; #Ifdef DEBUG; if (parser_trace >= 1) print "[Parsing for the verb '", (address) verb_word, "' (", num_lines+1, " lines)]^"; #Endif; ! DEBUG best_etype = STUCK_PE; nextbest_etype = STUCK_PE; multiflag = false; saved_oops = 0; ! "best_etype" is the current failure-to-match error - it is by default ! the least informative one, "don't understand that sentence". ! "nextbest_etype" remembers the best alternative to having to ask a ! scope token for an error message (i.e., the best not counting ASKSCOPE_PE). ! multiflag is used here to prevent inappropriate MULTI_PE errors ! in addition to its unrelated duties passing information to action routines !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! E: Break down a syntax line into analysed tokens ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! line_address = syntax + 1; for (line=0 : line<=num_lines : line++) { for (i=0 : i<32 : i++) { line_token-->i = ENDIT_TOKEN; line_ttype-->i = ELEMENTARY_TT; line_tdata-->i = ENDIT_TOKEN; } ! Unpack the syntax line from Inform format into three arrays; ensure that ! the sequence of tokens ends in an ENDIT_TOKEN. line_address = UnpackGrammarLine(line_address); #Ifdef DEBUG; if (parser_trace >= 1) { if (parser_trace >= 2) new_line; print "[line ", line; DebugGrammarLine(); print "]^"; } #Endif; ! DEBUG ! We aren't in "not holding" or inferring modes, and haven't entered ! any parameters on the line yet, or any special numbers; the multiple ! object is still empty. token_filter = 0; not_holding = 0; inferfrom = 0; parameters = 0; nsns = 0; special_word = 0; special_number = 0; multiple_object-->0 = 0; multi_context = 0; etype = STUCK_PE; line_etype = 100; ! Put the word marker back to just after the verb wn = verb_wordnum+1; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! F: Look ahead for advance warning for multiexcept/multiinside ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! There are two special cases where parsing a token now has to be ! affected by the result of parsing another token later, and these ! two cases (multiexcept and multiinside tokens) are helped by a quick ! look ahead, to work out the future token now. We can only carry this ! out in the simple (but by far the most common) case: ! ! multiexcept noun ! ! and similarly for multiinside. advance_warning = NULL; indef_mode = false; for (i=0,m=false,pcount=0 : line_token-->pcount ~= ENDIT_TOKEN : pcount++) { scope_token = 0; if (line_ttype-->pcount ~= PREPOSITION_TT) i++; if (line_ttype-->pcount == ELEMENTARY_TT) { if (line_tdata-->pcount == MULTI_TOKEN) m = true; if (line_tdata-->pcount == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN && i == 1) { ! First non-preposition is "multiexcept" or ! "multiinside", so look ahead. #Ifdef DEBUG; if (parser_trace >= 2) print " [Trying look-ahead]^"; #Endif; ! DEBUG ! We need this to be followed by 1 or more prepositions. pcount++; if (line_ttype-->pcount == PREPOSITION_TT) { ! skip ahead to a preposition word in the input do { l = NextWord(); } until ((wn > num_words) || (l && (l->#dict_par1) & DICT_PREP ~= 0)); if (wn > num_words) { #Ifdef DEBUG; if (parser_trace >= 2) print " [Look-ahead aborted: prepositions missing]^"; #Endif; jump EmptyLine; } do { if (PrepositionChain(l, pcount) ~= -1) { ! advance past the chain if ((line_token-->pcount)->0 & $20 ~= 0) { pcount++; while ((line_token-->pcount ~= ENDIT_TOKEN) && ((line_token-->pcount)->0 & $10 ~= 0)) pcount++; } else { pcount++; } } else { ! try to find another preposition word do { l = NextWord(); } until ((wn >= num_words) || (l && (l->#dict_par1) & 8 ~= 0)); if (l && (l->#dict_par1) & 8) continue; ! lookahead failed #Ifdef DEBUG; if (parser_trace >= 2) print " [Look-ahead aborted: prepositions don't match]^"; #Endif; jump LineFailed; } l = NextWord(); } until (line_ttype-->pcount ~= PREPOSITION_TT); .EmptyLine; ! put back the non-preposition we just read wn--; if ((line_ttype-->pcount == ELEMENTARY_TT) && (line_tdata-->pcount == NOUN_TOKEN)) { l = Descriptors(); ! skip past THE etc if (l~=0) etype=l; ! don't allow multiple objects l = NounDomain(actors_location, actor, NOUN_TOKEN); #Ifdef DEBUG; if (parser_trace >= 2) { print " [Advanced to ~noun~ token: "; if (l == REPARSE_CODE) print "re-parse request]^"; if (l == 1) print "but multiple found]^"; if (l == 0) print "error ", etype, "]^"; if (l >= 2) print (the) l, "]^"; } #Endif; ! DEBUG if (l == REPARSE_CODE) jump ReParse; if (l >= 2) advance_warning = l; } } break; } } } ! Slightly different line-parsing rules will apply to "take multi", to ! prevent "take all" behaving correctly but misleadingly when there's ! nothing to take. take_all_rule = 0; if (m && params_wanted == 1 && action_to_be == ##Take) take_all_rule = 1; ! And now start again, properly, forearmed or not as the case may be. ! As a precaution, we clear all the variables again (they may have been ! disturbed by the call to NounDomain, which may have called outside ! code, which may have done anything!). not_holding = 0; inferfrom = 0; inferword = 0; parameters = 0; nsns = 0; special_word = 0; special_number = 0; multiple_object-->0 = 0; etype = STUCK_PE; line_etype = 100; wn = verb_wordnum+1; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! G: Parse each token in turn (calling ParseToken to do most of the work) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! "Pattern" gradually accumulates what has been recognised so far, ! so that it may be reprinted by the parser later on for (pcount=1 : : pcount++) { pattern-->pcount = PATTERN_NULL; scope_token = 0; token = line_token-->(pcount-1); lookahead = line_token-->pcount; #Ifdef DEBUG; if (parser_trace >= 2) print " [line ", line, " token ", pcount, " word ", wn, " : ", (DebugToken) token, "]^"; #Endif; ! DEBUG if (token ~= ENDIT_TOKEN) { scope_reason = PARSING_REASON; parser_inflection = name; parser_inflection_func = false; AnalyseToken(token); if (action_to_be == ##AskTo && found_ttype == ELEMENTARY_TT && found_tdata == TOPIC_TOKEN && line_etype == 100) { if (actor ~= player) { best_etype = VERB_PE; jump GiveError; } l = inputobjs-->2; wn--; j = wn; jump Conversation2; } l = ParseToken__(found_ttype, found_tdata, pcount-1, token); while (l<-200) l = ParseToken__(ELEMENTARY_TT, l + 256); scope_reason = PARSING_REASON; if (l == GPR_PREPOSITION) { if (found_ttype~=PREPOSITION_TT && (found_ttype~=ELEMENTARY_TT || found_tdata~=TOPIC_TOKEN)) params_wanted--; l = true; } else if (l < 0) l = false; else if (l ~= GPR_REPARSE) { if (l == GPR_NUMBER) { if (nsns == 0) special_number1 = parsed_number; else special_number2 = parsed_number; nsns++; l = 1; } if (l == GPR_MULTIPLE) l = 0; results-->(parameters+2) = l; parameters++; pattern-->pcount = l; l = true; } #Ifdef DEBUG; if (parser_trace >= 3) { print " [token resulted in "; if (l == REPARSE_CODE) print "re-parse request]^"; if (l == 0) print "failure with error type ", etype, "]^"; if (l == 1) print "success]^"; } #Endif; ! DEBUG if (l == REPARSE_CODE) jump ReParse; if (l == false) { if (etype < line_etype) line_etype = etype; if (etype == STUCK_PE || wn >= num_words) break; } } else { ! If the player has entered enough already but there's still ! text to wade through: store the pattern away so as to be able to produce ! a decent error message if this turns out to be the best we ever manage, ! and in the mean time give up on this line ! However, if the superfluous text begins with a comma or "then" then ! take that to be the start of another instruction if (line_etype < 100) break; if (wn <= num_words) { l = NextWord(); if (l == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) { held_back_mode = 1; hb_wn = wn-1; } else { for (m=0 : m<32 : m++) pattern2-->m = pattern-->m; pcount2 = pcount; etype = UPTO_PE; break; } } ! Now, we may need to revise the multiple object because of the single one ! we now know (but didn't when the list was drawn up). if (parameters >= 1 && results-->2 == 0) { l = ReviseMulti(results-->3); if (l ~= 0) { etype = l; results-->0 = action_to_be; break; } } if (parameters >= 2 && results-->3 == 0) { l = ReviseMulti(results-->2); if (l ~= 0) { etype = l; break; } } ! To trap the case of "take all" inferring only "yourself" when absolutely ! nothing else is in the vicinity... if (take_all_rule == 2 && results-->2 == actor) { best_etype = NOTHING_PE; jump GiveError; } #Ifdef DEBUG; if (parser_trace >= 1) print "[Line successfully parsed]^"; #Endif; ! DEBUG ! The line has successfully matched the text. Declare the input error-free... oops_from = 0; ! ...explain any inferences made (using the pattern)... if (inferfrom ~= 0 && no_infer_message == false) { print "("; PrintCommand(inferfrom); print ")^"; } no_infer_message = false; ! ...copy the action number, and the number of parameters... results-->0 = action_to_be; results-->1 = parameters; ! ...reverse first and second parameters if need be... if (action_reversed && parameters == 2) { i = results-->2; results-->2 = results-->3; results-->3 = i; if (nsns == 2) { i = special_number1; special_number1 = special_number2; special_number2 = i; } } ! ...and to reset "it"-style objects to the first of these parameters, if ! there is one (and it really is an object)... if (parameters > 0 && results-->2 >= 2) PronounNotice(results-->2); ! ...and worry about the case where an object was allowed as a parameter ! even though the player wasn't holding it and should have been: in this ! event, keep the results for next time round, go into "not holding" mode, ! and for now tell the player what's happening and return a "take" request ! instead... if (not_holding ~= 0 && actor == player) { action = ##Take; i = RunRoutines(not_holding, before_implicit); ! i = 0: Take the object, tell the player (default) ! i = 1: Take the object, don't tell the player ! i = 2: don't Take the object, continue ! i = 3: don't Take the object, don't continue if (i > 2 || no_implicit_actions) { best_etype = NOTHELD_PE; jump GiveError; } ! perform the implicit Take if (i < 2) { if (i ~= 1) ! and tell the player L__M(##Miscellany, 26, not_holding); notheld_mode = 1; for (i=0 : i<8 : i++) kept_results-->i = results-->i; results-->0 = ##Take; results-->1 = 1; results-->2 = not_holding; } } ! (Notice that implicit takes are only generated for the player, and not ! for other actors. This avoids entirely logical, but misleading, text ! being printed.) ! ...and return from the parser altogether, having successfully matched ! a line. if (held_back_mode == 1) { wn=hb_wn; jump LookForMore; } rtrue; } ! end of if(token ~= ENDIT_TOKEN) else } ! end of for(pcount++) .LineFailed; ! The line has failed to match. ! We continue the outer "for" loop, trying the next line in the grammar. if (line_etype < 100) etype = line_etype; if (etype > best_etype) best_etype = etype; if (etype ~= ASKSCOPE_PE && etype > nextbest_etype) nextbest_etype = etype; ! ...unless the line was something like "take all" which failed because ! nothing matched the "all", in which case we stop and give an error now. if (take_all_rule == 2 && etype==NOTHING_PE) break; } ! end of for(line++) ! The grammar is exhausted: every line has failed to match. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! H: Cheaply parse otherwise unrecognised conversation and return ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .GiveError; etype = best_etype; ! Errors are handled differently depending on who was talking. ! If the command was addressed to somebody else (eg, "dwarf, sfgh") then ! it is taken as conversation which the parser has no business in disallowing. if (actor ~= player) { if (usual_grammar_after ~= 0) { verb_wordnum = usual_grammar_after; jump AlmostReParse; } wn = verb_wordnum; special_word = NextWord(); if (special_word == comma_word) { special_word = NextWord(); verb_wordnum++; } special_number = TryNumber(verb_wordnum); results-->0 = ##NotUnderstood; results-->1 = 2; results-->2 = 1; special_number1 = special_word; results-->3 = actor; consult_from = verb_wordnum; consult_words = num_words-consult_from+1; rtrue; } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! I: Print best possible error message ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! If the player was the actor (eg, in "take dfghh") the error must be ! printed, and fresh input called for. In four cases the oops word ! must be jiggled (where oops_from is set to something). if (ParserError(etype)) jump ReType; if (LibraryExtensions.RunWhile(ext_parsererror, false, etype)) jump ReType; pronoun_word = pronoun__word; pronoun_obj = pronoun__obj; if (etype == STUCK_PE) { L__M(##Miscellany, 27); oops_from = 1; } if (etype == UPTO_PE) { L__M(##Miscellany, 28); for (m=0 : m<32 : m++) pattern-->m = pattern2-->m; pcount = pcount2; PrintCommand(0); L__M(##Miscellany, 56); oops_from = wn-1; } if (etype == NUMBER_PE) L__M(##Miscellany, 29); if (etype == CANTSEE_PE) { L__M(##Miscellany, 30); oops_from=saved_oops;} if (etype == TOOLIT_PE) L__M(##Miscellany, 31); if (etype == NOTHELD_PE) { L__M(##Miscellany, 32, not_holding); oops_from=saved_oops; } if (etype == MULTI_PE) L__M(##Miscellany, 33); if (etype == MMULTI_PE) L__M(##Miscellany, 34); if (etype == VAGUE_PE) L__M(##Miscellany, 35, pronoun_word); if (etype == EXCEPT_PE) L__M(##Miscellany, 36); if (etype == ANIMA_PE) L__M(##Miscellany, 37); if (etype == VERB_PE) L__M(##Miscellany, 38); if (etype == SCENERY_PE) L__M(##Miscellany, 39); if (etype == ITGONE_PE) { if (pronoun_obj == NULL) L__M(##Miscellany, 35, pronoun_word); else L__M(##Miscellany, 40, pronoun_word, pronoun_obj); } if (etype == JUNKAFTER_PE) L__M(##Miscellany, 41); if (etype == TOOFEW_PE) L__M(##Miscellany, 42, multi_had); if (etype == NOTHING_PE) { if (results-->0 == ##Remove && results-->3 ofclass Object) { noun = results-->3; ! ensure valid for messages if (noun has animate) L__M(##Miscellany, 44, verb_word); else if (noun hasnt container or supporter) L__M(##Insert, 2, noun); else if (noun has container && noun hasnt open) L__M(##Take, 9, noun); else if (children(noun)==0) L__M(##Search, 6, noun); else results-->0 = 0; } if (results-->0 ~= ##Remove) { if (multi_wanted == 100) L__M(##Miscellany, 43); else { #Ifdef NO_TAKE_ALL; if (take_all_rule == 2) L__M(##Miscellany, 59); else L__M(##Miscellany, 44, verb_word); #Ifnot; L__M(##Miscellany, 44, verb_word); #Endif; ! NO_TAKE_ALL } } } if (etype == ASKSCOPE_PE) { scope_stage = 3; if (scope_error() == -1) { best_etype = nextbest_etype; jump GiveError; } } !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! J: Retry the whole lot ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! And go (almost) right back to square one... jump ReType; ! ...being careful not to go all the way back, to avoid infinite repetition ! of a deferred command causing an error. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! K: Last thing: check for "then" and further instructions(s), return. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! At this point, the return value is all prepared, and we are only looking ! to see if there is a "then" followed by subsequent instruction(s). .LookForMore; if (wn > num_words) rtrue; i = NextWord(); if (i == THEN1__WD or THEN2__WD or THEN3__WD or comma_word or AND1__WD) { if (wn > num_words) { held_back_mode = false; return; } i = WordAddress(verb_wordnum); j = WordAddress(wn); for (: i0 = ' '; i = NextWord(); if (i == AGAIN1__WD or AGAIN2__WD or AGAIN3__WD) { ! Delete the words "then again" from the again buffer, ! in which we have just realised that it must occur: ! prevents an infinite loop on "i. again" i = WordAddress(wn-2)-buffer; if (wn > num_words) j = INPUT_BUFFER_LEN-1; else j = WordAddress(wn)-buffer; for (: ii = ' '; } Tokenise__(buffer,parse); held_back_mode = true; return; } best_etype = UPTO_PE; jump GiveError; ]; ! end of Parser__parse [ ScopeCeiling person act; act = parent(person); if (act == 0) return person; if (person == player && location == thedark) return thedark; while (parent(act)~=0 && (act has transparent || act has supporter || (act has container && act has open))) act = parent(act); return act; ]; ! ---------------------------------------------------------------------------- ! Descriptors() ! ! Handles descriptive words like "my", "his", "another" and so on. ! Skips "the", and leaves wn pointing to the first misunderstood word. ! ! Allowed to set up for a plural only if allow_p is set ! ! Returns error number, or 0 if no error occurred ! ---------------------------------------------------------------------------- Constant OTHER_BIT = 1; ! These will be used in Adjudicate() Constant MY_BIT = 2; ! to disambiguate choices Constant THAT_BIT = 4; Constant PLURAL_BIT = 8; Constant LIT_BIT = 16; Constant UNLIT_BIT = 32; [ ResetDescriptors; indef_mode = 0; indef_type = 0; indef_wanted = 0; indef_guess_p = 0; indef_possambig = false; indef_owner = nothing; indef_cases = $$111111111111; indef_nspec_at = 0; ]; [ Descriptors allows_multiple o x flag cto type m n; ResetDescriptors(); if (wn > num_words) return 0; m = wn; for (flag=true : flag :) { o = NextWordStopped(); flag = false; for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) if (o == LanguageDescriptors-->x) { flag = true; type = LanguageDescriptors-->(x+2); if (type ~= DEFART_PK) indef_mode = true; indef_possambig = true; indef_cases = indef_cases & (LanguageDescriptors-->(x+1)); if (type == POSSESS_PK) { cto = LanguageDescriptors-->(x+3); switch (cto) { 0: indef_type = indef_type | MY_BIT; 1: indef_type = indef_type | THAT_BIT; default: indef_owner = PronounValue(cto); if (indef_owner == NULL) indef_owner = InformParser; } } if (type == light) indef_type = indef_type | LIT_BIT; if (type == -light) indef_type = indef_type | UNLIT_BIT; } if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) { indef_mode = 1; flag = 1; indef_type = indef_type | OTHER_BIT; } if (o == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { indef_mode = 1; flag = 1; indef_wanted = 100; if (take_all_rule == 1) take_all_rule = 2; indef_type = indef_type | PLURAL_BIT; } if (allow_plurals && allows_multiple) { n = TryNumber(wn-1); if (n == 1) { indef_mode = 1; flag = 1; indef_wanted = 1; } if (n > 1) { indef_guess_p = 1; indef_mode = 1; flag = 1; indef_wanted = n; indef_nspec_at = wn-1; indef_type = indef_type | PLURAL_BIT; } } if (flag == 1 && NextWordStopped() ~= OF1__WD or OF2__WD or OF3__WD or OF4__WD) wn--; ! Skip 'of' after these } wn--; num_desc = wn - m; return 0; ]; ! ---------------------------------------------------------------------------- ! CreatureTest: Will this person do for a "creature" token? ! ---------------------------------------------------------------------------- [ CreatureTest obj; if (actor ~= player) rtrue; if (obj has animate) rtrue; if (obj hasnt talkable) rfalse; if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue; rfalse; ]; [ PrepositionChain wd index; if (line_tdata-->index == wd) return wd; if ((line_token-->index)->0 & $20 == 0) return -1; do { if (line_tdata-->index == wd) return wd; index++; } until ((line_token-->index == ENDIT_TOKEN) || (((line_token-->index)->0 & $10) == 0)); return -1; ]; ! ---------------------------------------------------------------------------- ! ParseToken(type, data): ! Parses the given token, from the current word number wn, with exactly ! the specification of a general parsing routine. ! (Except that for "topic" tokens and prepositions, you need to supply ! a position in a valid grammar line as third argument.) ! ! Returns: ! GPR_REPARSE for "reconstructed input, please re-parse from scratch" ! GPR_PREPOSITION for "token accepted with no result" ! $ff00 + x for "please parse ParseToken(ELEMENTARY_TT, x) instead" ! 0 for "token accepted, result is the multiple object list" ! 1 for "token accepted, result is the number in parsed_number" ! object num for "token accepted with this object as result" ! -1 for "token rejected" ! ! (A) Analyse the token; handle all tokens not involving ! object lists and break down others into elementary tokens ! (B) Begin parsing an object list ! (C) Parse descriptors (articles, pronouns, etc.) in the list ! (D) Parse an object name ! (E) Parse connectives ("and", "but", etc.) and go back to (C) ! (F) Return the conclusion of parsing an object list ! ---------------------------------------------------------------------------- [ ParseToken given_ttype given_tdata token_n x y; x = lookahead; lookahead = NOUN_TOKEN; y = ParseToken__(given_ttype,given_tdata,token_n); if (y == GPR_REPARSE) Tokenise__(buffer,parse); lookahead = x; return y; ]; [ ParseToken__ given_ttype given_tdata token_n token l o i j k and_parity single_object desc_wn many_flag token_allows_multiple prev_indef_wanted; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! A: Analyse token; handle all not involving object lists, break down others ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! token_filter = 0; switch (given_ttype) { ELEMENTARY_TT: switch (given_tdata) { SPECIAL_TOKEN: l = TryNumber(wn); special_word = NextWord(); #Ifdef DEBUG; if (l ~= -1000) if (parser_trace >= 3) print " [Read special as the number ", l, "]^"; #Endif; ! DEBUG if (l == -1000) { #Ifdef DEBUG; if (parser_trace >= 3) print " [Read special word at word number ", wn, "]^"; #Endif; ! DEBUG l = special_word; } parsed_number = l; return GPR_NUMBER; NUMBER_TOKEN: l=TryNumber(wn++); if (l == -1000) { etype = NUMBER_PE; return GPR_FAIL; } #Ifdef DEBUG; if (parser_trace>=3) print " [Read number as ", l, "]^"; #Endif; ! DEBUG parsed_number = l; return GPR_NUMBER; CREATURE_TOKEN: if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell) scope_reason = TALKING_REASON; TOPIC_TOKEN: consult_from = wn; if ((line_ttype-->(token_n+1) ~= PREPOSITION_TT) && (line_token-->(token_n+1) ~= ENDIT_TOKEN)) RunTimeError(13); do o = NextWordStopped(); until (o == -1 || PrepositionChain(o, token_n+1) ~= -1); wn--; consult_words = wn-consult_from; if (consult_words == 0) return GPR_FAIL; if (action_to_be == ##Ask or ##Answer or ##Tell) { o = wn; wn = consult_from; parsed_number = NextWord(); #Ifdef EnglishNaturalLanguage; if (parsed_number == 'the' && consult_words > 1) parsed_number=NextWord(); #Endif; ! EnglishNaturalLanguage wn = o; return 1; } if (o==-1 && (line_ttype-->(token_n+1) == PREPOSITION_TT)) return GPR_FAIL; ! don't infer if required preposition is absent return GPR_PREPOSITION; } PREPOSITION_TT: #Iffalse (Grammar__Version == 1); ! Is it an unnecessary alternative preposition, when a previous choice ! has already been matched? if ((token->0) & $10) return GPR_PREPOSITION; #Endif; ! Grammar__Version ! If we've run out of the player's input, but still have parameters to ! specify, we go into "infer" mode, remembering where we are and the ! preposition we are inferring... if (wn > num_words) { if (inferfrom==0 && parameterspcount = REPARSE_CODE + Dword__No(given_tdata); } ! If we are not inferring, then the line is wrong... if (inferfrom == 0) return -1; ! If not, then the line is right but we mark in the preposition... pattern-->pcount = REPARSE_CODE + Dword__No(given_tdata); return GPR_PREPOSITION; } o = NextWord(); pattern-->pcount = REPARSE_CODE + Dword__No(o); ! Whereas, if the player has typed something here, see if it is the ! required preposition... if it's wrong, the line must be wrong, ! but if it's right, the token is passed (jump to finish this token). if (o == given_tdata) return GPR_PREPOSITION; #Iffalse (Grammar__Version == 1); if (PrepositionChain(o, token_n) ~= -1) return GPR_PREPOSITION; #Endif; ! Grammar__Version return -1; GPR_TT: l = given_tdata(); #Ifdef DEBUG; if (parser_trace >= 3) print " [Outside parsing routine returned ", l, "]^"; #Endif; ! DEBUG return l; SCOPE_TT: scope_token = given_tdata; scope_stage = 1; l = scope_token(); #Ifdef DEBUG; if (parser_trace >= 3) print " [Scope routine returned multiple-flag of ", l, "]^"; #Endif; ! DEBUG if (l == 1) given_tdata = MULTI_TOKEN; else given_tdata = NOUN_TOKEN; ATTR_FILTER_TT: token_filter = 1 + given_tdata; given_tdata = NOUN_TOKEN; ROUTINE_FILTER_TT: token_filter = given_tdata; given_tdata = NOUN_TOKEN; } ! end of switch(given_ttype) token = given_tdata; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! B: Begin parsing an object list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! There are now three possible ways we can be here: ! parsing an elementary token other than "special" or "number"; ! parsing a scope token; ! parsing a noun-filter token (either by routine or attribute). ! ! In each case, token holds the type of elementary parse to ! perform in matching one or more objects, and ! token_filter is 0 (default), an attribute + 1 for an attribute filter ! or a routine address for a routine filter. token_allows_multiple = false; if (token == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) token_allows_multiple = true; many_flag = false; and_parity = true; dont_infer = false; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! C: Parse descriptors (articles, pronouns, etc.) in the list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! We expect to find a list of objects next in what the player's typed. .ObjectList; #Ifdef DEBUG; if (parser_trace >= 3) print " [Object list from word ", wn, "]^"; #Endif; ! DEBUG ! Take an advance look at the next word: if it's "it" or "them", and these ! are unset, set the appropriate error number and give up on the line ! (if not, these are still parsed in the usual way - it is not assumed ! that they still refer to something in scope) o = NextWord(); wn--; pronoun_word = NULL; pronoun_obj = NULL; l = PronounValue(o); if (l ~= 0) { pronoun_word = o; pronoun_obj = l; if (l == NULL) { ! Don't assume this is a use of an unset pronoun until the ! descriptors have been checked, because it might be an ! article (or some such) instead for (l=1 : l<=LanguageDescriptors-->0 : l=l+4) if (o == LanguageDescriptors-->l) jump AssumeDescriptor; pronoun__word = pronoun_word; pronoun__obj = pronoun_obj; etype = VAGUE_PE; return GPR_FAIL; } } .AssumeDescriptor; if (o == ME1__WD or ME2__WD or ME3__WD) { pronoun_word = o; pronoun_obj = player; } allow_plurals = true; desc_wn = wn; .TryAgain; ! First, we parse any descriptive words (like "the", "five" or "every"): l = Descriptors(token_allows_multiple); if (l ~= 0) { etype = l; return GPR_FAIL; } .TryAgain2; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! D: Parse an object name ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! This is an actual specified object, and is therefore where a typing error ! is most likely to occur, so we set: oops_from = wn; ! So, two cases. Case 1: token not equal to "held" ! but we may well be dealing with multiple objects ! In either case below we use NounDomain, giving it the token number as ! context, and two places to look: among the actor's possessions, and in the ! present location. (Note that the order depends on which is likeliest.) if (token ~= HELD_TOKEN) { i = multiple_object-->0; #Ifdef DEBUG; if (parser_trace >= 3) print " [Calling NounDomain on location and actor]^"; #Endif; ! DEBUG l = NounDomain(actors_location, actor, token); if (l == REPARSE_CODE) return l; ! Reparse after Q&A if (l ~= nothing && l ~= 1 && l notin actor && token == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) { if (ImplicitTake(l)) { etype = NOTHELD_PE; jump FailToken; } } if (indef_wanted == 100 && l == 0 && number_matched == 0) l = 1; ! ReviseMulti if TAKE ALL FROM empty container if (token_allows_multiple && ~~multiflag) { if (best_etype==MULTI_PE) best_etype=STUCK_PE; multiflag = true; } if (l == 0) { if (indef_possambig) { saved_ml = match_length; ResetDescriptors(); wn = desc_wn; jump TryAgain2; } if (etype ~=TOOFEW_PE && (multiflag || etype ~= MULTI_PE)) etype = CantSee(); jump FailToken; } ! Choose best error #Ifdef DEBUG; if (parser_trace >= 3) { if (l > 1) print " [NounDomain returned ", (the) l, "]^"; else { print " [NounDomain appended to the multiple object list:^"; k = multiple_object-->0; for (j=i+1 : j<=k : j++) print " Entry ", j, ": ", (The) multiple_object-->j, " (", multiple_object-->j, ")^"; print " List now has size ", k, "]^"; } } #Endif; ! DEBUG if (l == 1) { if (~~many_flag) many_flag = true; else { ! Merge with earlier ones k = multiple_object-->0; ! (with either parity) multiple_object-->0 = i; for (j=i+1 : j<=k : j++) { if (and_parity) MultiAdd(multiple_object-->j); else MultiSub(multiple_object-->j); } #Ifdef DEBUG; if (parser_trace >= 3) print " [Merging ", k-i, " new objects to the ", i, " old ones]^"; #Endif; ! DEBUG } } else { ! A single object was indeed found if (match_length == 0 && indef_possambig) { ! So the answer had to be inferred from no textual data, ! and we know that there was an ambiguity in the descriptor ! stage (such as a word which could be a pronoun being ! parsed as an article or possessive). It's worth having ! another go. ResetDescriptors(); wn = desc_wn; jump TryAgain2; } if (token == CREATURE_TOKEN && CreatureTest(l) == 0) { etype = ANIMA_PE; jump FailToken; } ! Animation is required if (~~many_flag) single_object = l; else { if (and_parity) MultiAdd(l); else MultiSub(l); #Ifdef DEBUG; if (parser_trace >= 3) print " [Combining ", (the) l, " with list]^"; #Endif; ! DEBUG } } } else { ! Case 2: token is "held" (which fortunately can't take multiple objects) ! and may generate an implicit take l = NounDomain(actor,actors_location,token); ! Same as above... if (l == REPARSE_CODE) return GPR_REPARSE; if (l == 0) { if (indef_possambig) { ResetDescriptors(); wn = desc_wn; jump TryAgain2; } etype = CantSee(); jump FailToken; ! Choose best error } ! ...until it produces something not held by the actor. Then an implicit ! take must be tried. If this is already happening anyway, things are too ! confused and we have to give up (but saving the oops marker so as to get ! it on the right word afterwards). ! The point of this last rule is that a sequence like ! ! > read newspaper ! (taking the newspaper first) ! The dwarf unexpectedly prevents you from taking the newspaper! ! ! should not be allowed to go into an infinite repeat - read becomes ! take then read, but take has no effect, so read becomes take then read... ! Anyway for now all we do is record the number of the object to take. o = parent(l); if (o ~= actor) { if (notheld_mode == 1) { saved_oops = oops_from; etype = NOTHELD_PE; jump FailToken; } not_holding = l; #Ifdef DEBUG; if (parser_trace >= 3) print " [Allowing object ", (the) l, " for now]^"; #Endif; ! DEBUG } single_object = l; } ! end of if (token ~= HELD_TOKEN) else ! The following moves the word marker to just past the named object... wn = oops_from + match_length; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! E: Parse connectives ("and", "but", etc.) and go back to (C) ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Object(s) specified now: is that the end of the list, or have we reached ! "and", "but" and so on? If so, create a multiple-object list if we ! haven't already (and are allowed to). .NextInList; o = NextWord(); if (o == AND1__WD or AND2__WD or AND3__WD or BUT1__WD or BUT2__WD or BUT3__WD or comma_word) { #Ifdef DEBUG; if (parser_trace >= 3) print " [Read connective '", (address) o, "']^"; #Endif; ! DEBUG k = NextWord(); if (k ~= AND1__WD) wn--; ! allow Serial commas in input if (k > 0 && (k->#dict_par1) & (DICT_NOUN+DICT_VERB) == DICT_VERB) { wn--; ! player meant 'THEN' jump PassToken; } if (~~token_allows_multiple) { if (multiflag) jump PassToken; ! give UPTO_PE error etype=MULTI_PE; jump FailToken; } if (o == BUT1__WD or BUT2__WD or BUT3__WD) and_parity = 1-and_parity; if (~~many_flag) { multiple_object-->0 = 1; multiple_object-->1 = single_object; many_flag = true; #Ifdef DEBUG; if (parser_trace >= 3) print " [Making new list from ", (the) single_object, "]^"; #Endif; ! DEBUG } dont_infer = true; inferfrom=0; ! Don't print (inferences) jump ObjectList; ! And back around } wn--; ! Word marker back to first not-understood word !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! F: Return the conclusion of parsing an object list ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! Happy or unhappy endings: .PassToken; if (many_flag) { single_object = GPR_MULTIPLE; multi_context = token; } else { if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) { if (indef_wanted < 100 && indef_wanted > 1) { multi_had = 1; multi_wanted = indef_wanted; etype = TOOFEW_PE; jump FailToken; } } } return single_object; .FailToken; ! If we were only guessing about it being a plural, try again but only ! allowing singulars (so that words like "six" are not swallowed up as ! Descriptors) if (allow_plurals && indef_guess_p == 1) { #Ifdef DEBUG; if (parser_trace >= 4) print " [Retrying singulars after failure ", etype, "]^"; #Endif; prev_indef_wanted = indef_wanted; allow_plurals = false; wn = desc_wn; jump TryAgain; } if ((indef_wanted > 0 || prev_indef_wanted > 0) && (~~multiflag)) etype = MULTI_PE; return GPR_FAIL; ]; ! end of ParseToken__ ! ---------------------------------------------------------------------------- ! NounDomain does the most substantial part of parsing an object name. ! ! It is given two "domains" - usually a location and then the actor who is ! looking - and a context (i.e. token type), and returns: ! ! 0 if no match at all could be made, ! 1 if a multiple object was made, ! k if object k was the one decided upon, ! REPARSE_CODE if it asked a question of the player and consequently rewrote ! the player's input, so that the whole parser should start again ! on the rewritten input. ! ! In the case when it returns 1= 4) { print " [NounDomain called at word ", wn, "]^"; print " "; if (indef_mode) { print "seeking indefinite object: "; if (indef_type & OTHER_BIT) print "other "; if (indef_type & MY_BIT) print "my "; if (indef_type & THAT_BIT) print "that "; if (indef_type & PLURAL_BIT) print "plural "; if (indef_type & LIT_BIT) print "lit "; if (indef_type & UNLIT_BIT) print "unlit "; if (indef_owner ~= 0) print "owner:", (name) indef_owner; new_line; print " number wanted: "; if (indef_wanted == 100) print "all"; else print indef_wanted; new_line; print " most likely GNAs of names: ", indef_cases, "^"; } else print "seeking definite object^"; } #Endif; ! DEBUG match_length = 0; number_matched = 0; match_from = wn; placed_in_flag = 0; SearchScope(domain1, domain2, context); #Ifdef DEBUG; if (parser_trace >= 4) print " [NounDomain made ", number_matched, " matches]^"; #Endif; ! DEBUG wn = match_from+match_length; ! If nothing worked at all, leave with the word marker skipped past the ! first unmatched word... if (number_matched == 0) { wn++; rfalse; } ! Suppose that there really were some words being parsed (i.e., we did ! not just infer). If so, and if there was only one match, it must be ! right and we return it... if (match_from <= num_words) { if (number_matched == 1) { i=match_list-->0; if (indef_mode) { if ((indef_type & LIT_BIT) && i hasnt light) rfalse; if ((indef_type & UNLIT_BIT) && i has light) rfalse; } return i; } ! ...now suppose that there was more typing to come, i.e. suppose that ! the user entered something beyond this noun. If nothing ought to follow, ! then there must be a mistake, (unless what does follow is just a full ! stop, and or comma) if (wn <= num_words) { i = NextWord(); wn--; if (i ~= AND1__WD or AND2__WD or AND3__WD or comma_word or THEN1__WD or THEN2__WD or THEN3__WD or BUT1__WD or BUT2__WD or BUT3__WD) { if (lookahead == ENDIT_TOKEN) rfalse; } } } ! Now look for a good choice, if there's more than one choice... number_of_classes = 0; if (match_length == 0 && indef_mode && indef_wanted ~= 100) number_matched = 0; ! ask question for 'take three' if (number_matched == 1) i = match_list-->0; if (number_matched > 1) { i = Adjudicate(context); if (i == -1) rfalse; if (i == 1) rtrue; ! Adjudicate has made a multiple ! object, and we pass it on } ! If i is non-zero here, one of two things is happening: either ! (a) an inference has been successfully made that object i is ! the intended one from the user's specification, or ! (b) the user finished typing some time ago, but we've decided ! on i because it's the only possible choice. ! In either case we have to keep the pattern up to date, ! note that an inference has been made and return. ! (Except, we don't note which of a pile of identical objects.) if (i ~= 0) { if (dont_infer) return i; if (inferfrom == 0) inferfrom=pcount; pattern-->pcount = i; return i; } ! If we get here, there was no obvious choice of object to make. If in ! fact we've already gone past the end of the player's typing (which ! means the match list must contain every object in scope, regardless ! of its name), then it's foolish to give an enormous list to choose ! from - instead we go and ask a more suitable question... if (match_from > num_words) jump Incomplete; return AskPlayer(context); ! Now we come to the question asked when the input has run out ! and can't easily be guessed (eg, the player typed "take" and there ! were plenty of things which might have been meant). .Incomplete; if (best_etype == NOTHING_PE && pattern-->1 == 0) rfalse; ! for DROP when empty-handed if (context == CREATURE_TOKEN) L__M(##Miscellany, 48, actor); else L__M(##Miscellany, 49, actor); #Ifdef TARGET_ZCODE; for (i=2 : ii = ' '; #Endif; ! TARGET_ZCODE answer_words = Keyboard(buffer2, parse2); first_word = WordValue(1, parse2); #Ifdef LanguageIsVerb; if (first_word == 0) { j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j; } #Endif; ! LanguageIsVerb ! Once again, if the reply looks like a command, give it to the ! parser to get on with and forget about the question... ! Once again, if the reply looks like a command ! (that is, VERB ... or XXX,VERB ...), give it to the parser to get ! on with and forget about the question... if (first_word) { if ((first_word->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } if (NumberWords(parse2) > 2) { j = WordValue(2, parse2); k = WordValue(3, parse2); if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } } ! ...but if we have a genuine answer, then: ! ! (1) we must glue in text suitable for anything that's been inferred. if (inferfrom ~= 0) { for (j=inferfrom : jj == PATTERN_NULL) continue; i = WORDSIZE + GetKeyBufLength(); SetKeyBufLength(i-WORDSIZE + 1); buffer->(i++) = ' '; #Ifdef DEBUG; if (parser_trace >= 5) print "[Gluing in inference with pattern code ", pattern-->j, "]^"; #Endif; ! DEBUG ! Conveniently, parse2-->1 is the first word in both ZCODE and GLULX. parse2-->1 = 0; ! An inferred object. Best we can do is glue in a pronoun. ! (This is imperfect, but it's very seldom needed anyway.) if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE) { ! was the inference made from some noun words? ! In which case, we can infer again. if ((WordValue(NumberWords())->#dict_par1) & DICT_NOUN) continue; PronounNotice(pattern-->j); for (k=1 : k<=LanguagePronouns-->0 : k=k+3) if (pattern-->j == LanguagePronouns-->(k+2)) { parse2-->1 = LanguagePronouns-->k; #Ifdef DEBUG; if (parser_trace >= 5) print "[Using pronoun '", (address) parse2-->1, "']^"; #Endif; ! DEBUG break; } } else { ! An inferred preposition. parse2-->1 = No__Dword(pattern-->j - REPARSE_CODE); #Ifdef DEBUG; if (parser_trace >= 5) print "[Using preposition '", (address) parse2-->1, "']^"; #Endif; ! DEBUG } ! parse2-->1 now holds the dictionary address of the word to glue in. if (parse2-->1 ~= 0) { k = buffer + i; #Ifdef TARGET_ZCODE; @output_stream 3 k; print (address) parse2-->1; @output_stream -3; k = k-->0; for (l=i : ll = buffer->(l+2); #Ifnot; ! TARGET_GLULX k = PrintAnyToArray(buffer+i, INPUT_BUFFER_LEN-i, parse2-->1); l=l; ! suppress compiler warning #Endif; ! TARGET_ i = i + k; SetKeyBufLength(i-WORDSIZE); } } } ! (2) we must glue the newly-typed text onto the end. i = WORDSIZE + GetKeyBufLength(); buffer->(i++) = ' '; SetKeyBufLength(GetKeyBufLength()+1); for (j=0 : ji = buffer2->(j+WORDSIZE); SetKeyBufLength(GetKeyBufLength()+1); if (i-WORDSIZE == INPUT_BUFFER_LEN-1) break; } ! (3) we fill up the buffer with spaces, which is unnecessary, but may ! help incorrectly-written interpreters to cope. #Ifdef TARGET_ZCODE; for (: ii = ' '; #Endif; ! TARGET_ZCODE return REPARSE_CODE; ]; ! end of NounDomain [ AskPlayer context i j k l first_word answer_words marker; ! Now we print up the question, using the equivalence classes as worked ! out by Adjudicate() so as not to repeat ourselves on plural objects... asking_player = true; if (context == CREATURE_TOKEN) L__M(##Miscellany, 45); else L__M(##Miscellany, 46); j = number_of_classes; marker = 0; for (i=1 : i<=number_of_classes : i++) { while (((match_classes-->marker) ~= i) && ((match_classes-->marker) ~= -i)) marker++; k = match_list-->marker; if (match_classes-->marker > 0) print (the) k; else print (a) k; if (i < j-1) print (string) COMMA__TX; if (i == j-1) print (SerialComma) j, (string) OR__TX; } L__M(##Miscellany, 57); ! ...and get an answer: .WhichOne; #Ifdef TARGET_ZCODE; for (i=WORDSIZE : ii = ' '; #Endif; ! TARGET_ZCODE answer_words = Keyboard(buffer2, parse2); first_word = WordValue(1, parse2); asking_player = false; ! Take care of "all", because that does something too clever here to do ! later on: if (first_word == ALL1__WD or ALL2__WD or ALL3__WD or ALL4__WD or ALL5__WD) { if (context == MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { l = multiple_object-->0; for (i=0 : ii; multiple_object-->(i+1+l) = k; } multiple_object-->0 = i+l; rtrue; } L__M(##Miscellany, 47); jump WhichOne; } ! If the first word of the reply can be interpreted as a verb, then ! assume that the player has ignored the question and given a new ! command altogether. ! (This is one time when it's convenient that the directions are ! not themselves verbs - thus, "north" as a reply to "Which, the north ! or south door" is not treated as a fresh command but as an answer.) #Ifdef LanguageIsVerb; if (first_word == 0) { j = wn; first_word = LanguageIsVerb(buffer2, parse2, 1); wn = j; } #Endif; ! LanguageIsVerb if (first_word) { if (((first_word->#dict_par1) & DICT_VERB) && ~~LanguageVerbMayBeName(first_word)) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } if (NumberWords(parse2) > 2) { j = WordValue(2, parse2); k = WordValue(3, parse2); if (j == ',//' && k && (k->#dict_par1) & DICT_VERB) { CopyBuffer(buffer, buffer2); return REPARSE_CODE; } } } ! Now we insert the answer into the original typed command, as ! words additionally describing the same object ! (eg, > take red button ! Which one, ... ! > music ! becomes "take music red button". The parser will thus have three ! words to work from next time, not two.) k = WordAddress(match_from) - buffer; l = GetKeyBufLength(buffer2) +1; for (j=buffer + INPUT_BUFFER_LEN - 1 : j>=buffer+k+l : j--) j->0 = j->(-l); for (i=0 : i(k+i) = buffer2->(WORDSIZE+i); buffer->(k+l-1) = ' '; SetKeyBufLength(GetKeyBufLength() + l); ! Having reconstructed the input, we warn the parser accordingly ! and get out. return REPARSE_CODE; ]; ! ---------------------------------------------------------------------------- ! The Adjudicate routine tries to see if there is an obvious choice, when ! faced with a list of objects (the match_list) each of which matches the ! player's specification equally well. ! ! To do this it makes use of the context (the token type being worked on). ! It counts up the number of obvious choices for the given context ! (all to do with where a candidate is, except for 6 (animate) which is to ! do with whether it is animate or not); ! ! if only one obvious choice is found, that is returned; ! ! if we are in indefinite mode (don't care which) one of the obvious choices ! is returned, or if there is no obvious choice then an unobvious one is ! made; ! ! at this stage, we work out whether the objects are distinguishable from ! each other or not: if they are all indistinguishable from each other, ! then choose one, it doesn't matter which; ! ! otherwise, 0 (meaning, unable to decide) is returned (but remember that ! the equivalence classes we've just worked out will be needed by other ! routines to clear up this mess, so we can't economise on working them ! out). ! ! Returns -1 if an error occurred ! ---------------------------------------------------------------------------- Constant SCORE__CHOOSEOBJ = 1000; Constant SCORE__IFGOOD = 500; Constant SCORE__UNCONCEALED = 100; Constant SCORE__BESTLOC = 60; Constant SCORE__NEXTBESTLOC = 40; Constant SCORE__NOTCOMPASS = 20; Constant SCORE__NOTSCENERY = 10; Constant SCORE__NOTACTOR = 5; Constant SCORE__GNA = 1; Constant SCORE__DIVISOR = 20; [ Adjudicate context i j k good_flag good_ones last n flag offset sovert; #Ifdef DEBUG; if (parser_trace >= 4) { print " [Adjudicating match list of size ", number_matched, " in context ", context, "]^"; print " "; if (indef_mode) { print "indefinite type: "; if (indef_type & OTHER_BIT) print "other "; if (indef_type & MY_BIT) print "my "; if (indef_type & THAT_BIT) print "that "; if (indef_type & PLURAL_BIT) print "plural "; if (indef_type & LIT_BIT) print "lit "; if (indef_type & UNLIT_BIT) print "unlit "; if (indef_owner ~= 0) print "owner:", (name) indef_owner; new_line; print " number wanted: "; if (indef_wanted == 100) print "all"; else print indef_wanted; new_line; print " most likely GNAs of names: ", indef_cases, "^"; } else print "definite object^"; } #Endif; ! DEBUG j = number_matched-1; good_ones = 0; last = match_list-->0; for (i=0 : i<=j : i++) { n = match_list-->i; match_scores-->i = 0; good_flag = false; switch (context) { HELD_TOKEN, MULTIHELD_TOKEN: if (parent(n) == actor) good_flag = true; MULTIEXCEPT_TOKEN: if (advance_warning == -1) { good_flag = true; } else { if (n ~= advance_warning) good_flag = true; } MULTIINSIDE_TOKEN: if (advance_warning == -1) { if (parent(n) ~= actor) good_flag = true; } else { if (n in advance_warning) good_flag = true; } CREATURE_TOKEN: if (CreatureTest(n) == 1) good_flag = true; default: good_flag = true; } if (good_flag) { match_scores-->i = SCORE__IFGOOD; good_ones++; last = n; } } if (good_ones == 1) return last; ! If there is ambiguity about what was typed, but it definitely wasn't ! animate as required, then return anything; higher up in the parser ! a suitable error will be given. (This prevents a question being asked.) if (context == CREATURE_TOKEN && good_ones == 0) return match_list-->0; if (indef_mode == 0) indef_type=0; ScoreMatchL(context); if (number_matched == 0) return -1; if (indef_mode == 1 && indef_type & PLURAL_BIT ~= 0) { if (context ~= MULTI_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { etype = MULTI_PE; return -1; } i = 0; offset = multiple_object-->0; sovert = -1; for (j=BestGuess() : j~=-1 && i(i+offset) = j; #Ifdef DEBUG; if (parser_trace >= 4) print " Accepting it^"; #Endif; ! DEBUG } else { i = i; #Ifdef DEBUG; if (parser_trace >= 4) print " Rejecting it^"; #Endif; ! DEBUG } } if (i < indef_wanted && indef_wanted < 100) { etype = TOOFEW_PE; multi_wanted = indef_wanted; multi_had=i; return -1; } multiple_object-->0 = i+offset; multi_context = context; #Ifdef DEBUG; if (parser_trace >= 4) print " Made multiple object of size ", i, "]^"; #Endif; ! DEBUG return 1; } for (i=0 : ii = 0; n = 1; for (i=0 : ii == 0) { match_classes-->i = n++; flag = 0; for (j=i+1 : jj == 0 && Identical(match_list-->i, match_list-->j) == 1) { flag=1; match_classes-->j = match_classes-->i; } if (flag == 1) match_classes-->i = 1-n; } n--; number_of_classes = n; #Ifdef DEBUG; if (parser_trace >= 4) { print " Grouped into ", n, " possibilities by name:^"; for (i=0 : ii > 0) print " ", (The) match_list-->i, " (", match_list-->i, ") --- group ", match_classes-->i, "^"; } #Endif; ! DEBUG if (n == 1) dont_infer = true; if (indef_mode == 0) { ! Is there now a single highest-scoring object? i = SingleBestGuess(); if (i >= 0) { #Ifdef DEBUG; if (parser_trace >= 4) print " Single best-scoring object returned.]^"; #Endif; ! DEBUG return i; } } if (indef_mode == 0) { if (n > 1) { k = -1; for (i=0 : ii > k) { k = match_scores-->i; j = match_classes-->i; j = j*j; flag = 0; } else if (match_scores-->i == k) { if ((match_classes-->i) * (match_classes-->i) ~= j) flag = 1; } } if (flag) { #Ifdef DEBUG; if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^"; #Endif; ! DEBUG return 0; } #Ifdef DEBUG; if (parser_trace >= 4) print " Best choices are all from the same group.^"; #Endif; ! DEBUG } } ! When the player is really vague, or there's a single collection of ! indistinguishable objects to choose from, choose the one the player ! most recently acquired, or if the player has none of them, then ! the one most recently put where it is. return BestGuess(); ]; ! Adjudicate ! ---------------------------------------------------------------------------- ! ReviseMulti revises the multiple object which already exists, in the ! light of information which has come along since then (i.e., the second ! parameter). It returns a parser error number, or else 0 if all is well. ! This only ever throws things out, never adds new ones. ! ---------------------------------------------------------------------------- [ ReviseMulti second_p i low; #Ifdef DEBUG; if (parser_trace >= 4) print " Revising multiple object list of size ", multiple_object-->0, " with 2nd ", (name) second_p, "^"; #Endif; ! DEBUG if (multi_context == MULTIEXCEPT_TOKEN or MULTIINSIDE_TOKEN) { for (i=1,low=0 : i<=multiple_object-->0 : i++) { if ( (multi_context==MULTIEXCEPT_TOKEN && multiple_object-->i ~= second_p) || (multi_context==MULTIINSIDE_TOKEN && multiple_object-->i in second_p)) { low++; multiple_object-->low = multiple_object-->i; } } multiple_object-->0 = low; } if (multi_context == MULTI_TOKEN && action_to_be == ##Take) { for (i=1,low=0 : i<=multiple_object-->0 : i++) if (ScopeCeiling(multiple_object-->i)==ScopeCeiling(actor)) low++; #Ifdef DEBUG; if (parser_trace >= 4) print " Token 2 plural case: number with actor ", low, "^"; #Endif; ! DEBUG if (take_all_rule == 2 || low > 0) { for (i=1,low=0 : i<=multiple_object-->0 : i++) { if (ScopeCeiling(multiple_object-->i) == ScopeCeiling(actor)) { low++; multiple_object-->low = multiple_object-->i; } } multiple_object-->0 = low; } } i = multiple_object-->0; #Ifdef DEBUG; if (parser_trace >= 4) print " Done: new size ", i, "^"; #Endif; ! DEBUG if (i == 0) return NOTHING_PE; return 0; ]; ! ---------------------------------------------------------------------------- ! ScoreMatchL scores the match list for quality in terms of what the ! player has vaguely asked for. Points are awarded for conforming with ! requirements like "my", and so on. Remove from the match list any ! entries which fail the basic requirements of the descriptors. ! ---------------------------------------------------------------------------- [ ScoreMatchL context its_owner its_score obj i j threshold met a_s l_s; ! if (indef_type & OTHER_BIT ~= 0) threshold++; if (indef_type & MY_BIT ~= 0) threshold++; if (indef_type & THAT_BIT ~= 0) threshold++; if (indef_type & LIT_BIT ~= 0) threshold++; if (indef_type & UNLIT_BIT ~= 0) threshold++; if (indef_owner ~= nothing) threshold++; #Ifdef DEBUG; if (parser_trace >= 4) print " Scoring match list: indef mode ", indef_mode, " type ", indef_type, ", satisfying ", threshold, " requirements:^"; #Endif; ! DEBUG if (action_to_be ~= ##Take) a_s = SCORE__NEXTBESTLOC; l_s = SCORE__BESTLOC; if (context == HELD_TOKEN or MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) { a_s = SCORE__BESTLOC; l_s = SCORE__NEXTBESTLOC; } for (i=0 : ii; its_owner = parent(obj); its_score=0; met=0; ! if (indef_type & OTHER_BIT ~= 0 ! && obj ~= itobj or himobj or herobj) met++; if (indef_type & MY_BIT ~= 0 && its_owner == actor) met++; if (indef_type & THAT_BIT ~= 0 && its_owner == actors_location) met++; if (indef_type & LIT_BIT ~= 0 && obj has light) met++; if (indef_type & UNLIT_BIT ~= 0 && obj hasnt light) met++; if (indef_owner ~= 0 && its_owner == indef_owner) met++; if (met < threshold) { #Ifdef DEBUG; if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ", (the) its_owner, " is rejected (doesn't match descriptors)^"; #Endif; ! DEBUG match_list-->i = -1; } else { its_score = 0; if (obj hasnt concealed) its_score = SCORE__UNCONCEALED; if (its_owner == actor) its_score = its_score + a_s; else if (its_owner == actors_location) its_score = its_score + l_s; else { #Ifdef TRADITIONAL_TAKE_ALL; if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS; #Ifnot; if (its_owner ~= compass) if (take_all_rule && its_owner && its_owner has static or scenery && (its_owner has supporter || (its_owner has container && its_owner has open))) its_score = its_score + l_s; else its_score = its_score + SCORE__NOTCOMPASS; #Endif; ! TRADITIONAL_TAKE_ALL } j = ChooseObjects(obj, 2); if (j == 0) j = LibraryExtensions.RunAll(ext_chooseobjects, obj, 2); its_score = its_score + SCORE__CHOOSEOBJ * j; if (obj hasnt scenery) its_score = its_score + SCORE__NOTSCENERY; if (obj ~= actor) its_score = its_score + SCORE__NOTACTOR; ! A small bonus for having the correct GNA, ! for sorting out ambiguous articles and the like. if (indef_cases & (PowersOfTwo_TB-->(GetGNAOfObject(obj)))) its_score = its_score + SCORE__GNA; match_scores-->i = match_scores-->i + its_score; #Ifdef DEBUG; if (parser_trace >= 4) print " ", (The) match_list-->i, " (", match_list-->i, ") in ", (the) its_owner, " : ", match_scores-->i, " points^"; #Endif; ! DEBUG } } for (i=0 : ii == -1) { if (i == number_matched-1) { number_matched--; break; } for (j=i : jj = match_list-->(j+1); match_scores-->j = match_scores-->(j+1); } number_matched--; } } ]; ! ---------------------------------------------------------------------------- ! BestGuess makes the best guess it can out of the match list, assuming that ! everything in the match list is textually as good as everything else; ! however it ignores items marked as -1, and so marks anything it chooses. ! It returns -1 if there are no possible choices. ! ---------------------------------------------------------------------------- [ BestGuess earliest its_score best i; earliest = 0; best = -1; for (i=0 : ii >= 0) { its_score = match_scores-->i; if (its_score > best) { best = its_score; earliest = i; } } } #Ifdef DEBUG; if (parser_trace >= 4) if (best < 0) print " Best guess ran out of choices^"; else print " Best guess ", (the) match_list-->earliest, " (", match_list-->earliest, ")^"; #Endif; ! DEBUG if (best < 0) return -1; i = match_list-->earliest; match_list-->earliest = -1; bestguess_score = best; return i; ]; ! ---------------------------------------------------------------------------- ! SingleBestGuess returns the highest-scoring object in the match list ! if it is the clear winner, or returns -1 if there is no clear winner ! ---------------------------------------------------------------------------- [ SingleBestGuess earliest its_score best i; earliest = -1; best = -1000; for (i=0 : ii; if (its_score == best) earliest = -1; if (its_score > best) { best = its_score; earliest = match_list-->i; } } bestguess_score = best; return earliest; ]; ! ---------------------------------------------------------------------------- ! Identical decides whether or not two objects can be distinguished from ! each other by anything the player can type. If not, it returns true. ! ---------------------------------------------------------------------------- [ Identical o1 o2 p1 p2 n1 n2 i j flag; if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side if (o1 == 0 || o2 == 0) rfalse; ! Similarly if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time ! What complicates things is that o1 or o2 might have a parsing routine, ! so the parser can't know from here whether they are or aren't the same. ! If they have different parsing routines, we simply assume they're ! different. If they have the same routine (which they probably got from ! a class definition) then the decision process is as follows: ! ! the routine is called (with self being o1, not that it matters) ! with noun and second being set to o1 and o2, and action being set ! to the fake action TheSame. If it returns -1, they are found ! identical; if -2, different; and if >=0, then the usual method ! is used instead. if (o1.parse_name ~= 0 || o2.parse_name ~= 0) { if (o1.parse_name ~= o2.parse_name) rfalse; parser_action = ##TheSame; parser_one = o1; parser_two = o2; j = wn; i = RunRoutines(o1,parse_name); wn = j; if (i == -1) rtrue; if (i == -2) rfalse; } ! This is the default algorithm: do they have the same words in their ! "name" (i.e. property no. 1) properties. (Note that the following allows ! for repeated words and words in different orders.) p1 = o1.&1; n1 = (o1.#1)/WORDSIZE; p2 = o2.&1; n2 = (o2.#1)/WORDSIZE; ! for (i=0 : ii, " "; } new_line; ! for (i=0 : ii, " "; } new_line; for (i=0 : ii == p2-->j) flag = 1; if (flag == 0) rfalse; } for (j=0 : ji == p2-->j) flag = 1; if (flag == 0) rfalse; } ! print "Which are identical!^"; rtrue; ]; ! ---------------------------------------------------------------------------- ! PrintCommand reconstructs the command as it presently reads, from ! the pattern which has been built up ! ! If from is 0, it starts with the verb: then it goes through the pattern. ! The other parameter is "emptyf" - a flag: if 0, it goes up to pcount: ! if 1, it goes up to pcount-1. ! ! Note that verbs and prepositions are printed out of the dictionary: ! and that since the dictionary may only preserve the first six characters ! of a word (in a V3 game), we have to hand-code the longer words needed. ! ! (Recall that pattern entries are 0 for "multiple object", 1 for "special ! word", 2 to REPARSE_CODE-1 are object numbers and REPARSE_CODE+n means the ! preposition n) ! ---------------------------------------------------------------------------- [ PrintCommand from i k spacing_flag; #Ifdef LanguageCommand; LanguageCommand(from); i = k = spacing_flag = 0; ! suppress warning #Ifnot; if (from == 0) { i = verb_word; if (LanguageVerb(i) == 0 && PrintVerb(i) == false && LibraryExtensions.RunWhile(ext_printverb, false, i) == 0) print (address) i; from++; spacing_flag = true; } for (k=from : kk; if (i == PATTERN_NULL) continue; if (spacing_flag) print (char) ' '; if (i == 0) { print (string) THOSET__TX; jump TokenPrinted; } if (i == 1) { print (string) THAT__TX; jump TokenPrinted; } if (i >= REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE); else if (i in compass && LanguageVerbLikesAdverb(verb_word)) LanguageDirection (i.door_dir); ! the direction name as adverb else print (the) i; .TokenPrinted; spacing_flag = true; } #Endif; ! LanguageCommand ]; ! ---------------------------------------------------------------------------- ! The CantSee routine returns a good error number for the situation where ! the last word looked at didn't seem to refer to any object in context. ! ! The idea is that: if the actor is in a location (but not inside something ! like, for instance, a tank which is in that location) then an attempt to ! refer to one of the words listed as meaningful-but-irrelevant there ! will cause "you don't need to refer to that in this game" rather than ! "no such thing" or "what's 'it'?". ! (The advantage of not having looked at "irrelevant" local nouns until now ! is that it stops them from clogging up the ambiguity-resolving process. ! Thus game objects always triumph over scenery.) ! ---------------------------------------------------------------------------- [ CantSee i w e; if (scope_token ~= 0) { scope_error = scope_token; return ASKSCOPE_PE; } wn--; w = NextWord(); e = CANTSEE_PE; if (w == pronoun_word) { pronoun__word = pronoun_word; pronoun__obj = pronoun_obj; e = ITGONE_PE; } i = actor; while (parent(i) ~= 0) i = parent(i); wn--; if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE; else { Descriptors(); ! skip past THE etc if (i has visited && Refers(i,wn) == 1) e = SCENERY_PE; } if (saved_ml) saved_oops = num_desc + match_from + saved_ml; else saved_oops = num_desc + match_from + match_length; wn++; return e; ]; ! ---------------------------------------------------------------------------- ! The MultiAdd routine adds object "o" to the multiple-object-list. ! ! This is only allowed to hold 63 objects at most, at which point it ignores ! any new entries (and sets a global flag so that a warning may later be ! printed if need be). ! ---------------------------------------------------------------------------- [ MultiAdd o i j; i = multiple_object-->0; if (i == 63) { toomany_flag = 1; rtrue; } for (j=1 : j<=i : j++) if (o == multiple_object-->j) rtrue; i++; multiple_object-->i = o; multiple_object-->0 = i; ]; ! ---------------------------------------------------------------------------- ! The MultiSub routine deletes object "o" from the multiple-object-list. ! ! It returns 0 if the object was there in the first place, and 9 (because ! this is the appropriate error number in Parser()) if it wasn't. ! ---------------------------------------------------------------------------- [ MultiSub o i j k et; i = multiple_object-->0; et = 0; for (j=1 : j<=i : j++) if (o == multiple_object-->j) { for (k=j : k<=i : k++) multiple_object-->k = multiple_object-->(k+1); multiple_object-->0 = --i; return et; } et = 9; return et; ]; ! ---------------------------------------------------------------------------- ! The MultiFilter routine goes through the multiple-object-list and throws ! out anything without the given attribute "attr" set. ! ---------------------------------------------------------------------------- [ MultiFilter attr i j o; .MFiltl; i = multiple_object-->0; for (j=1 : j<=i : j++) { o = multiple_object-->j; if (o hasnt attr) { MultiSub(o); jump Mfiltl; } } ]; ! ---------------------------------------------------------------------------- ! The UserFilter routine consults the user's filter (or checks on attribute) ! to see what already-accepted nouns are acceptable ! ---------------------------------------------------------------------------- [ UserFilter obj; if (token_filter > 0 && token_filter < 49) { if (obj has (token_filter-1)) rtrue; rfalse; } noun = obj; return token_filter(); ]; ! ---------------------------------------------------------------------------- ! MoveWord copies word at2 from parse buffer b2 to word at1 in "parse" ! (the main parse buffer) ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ MoveWord at1 b2 at2 x y; x = at1*2-1; y = at2*2-1; parse-->x++ = b2-->y++; parse-->x = b2-->y; ]; #Ifnot; ! TARGET_GLULX [ MoveWord at1 b2 at2 x y; x = at1*3-2; y = at2*3-2; parse-->x++ = b2-->y++; parse-->x++ = b2-->y++; parse-->x = b2-->y; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! SearchScope domain1 domain2 context ! ! Works out what objects are in scope (possibly asking an outside routine), ! but does not look at anything the player has typed. ! ---------------------------------------------------------------------------- [ SearchScope domain1 domain2 context i is; i = 0; ! Everything is in scope to the debugging commands #Ifdef DEBUG; if (scope_reason == PARSING_REASON && LanguageVerbIsDebugging(verb_word)) { #Ifdef TARGET_ZCODE; for (i=selfobj : i<=top_object : i++) if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object)) PlaceInScope(i); #Ifnot; ! TARGET_GLULX objectloop (i) if (i ofclass Object && (parent(i) == 0 || parent(i) ofclass Object)) PlaceInScope(i); #Endif; ! TARGET_ rtrue; } #Endif; ! DEBUG ! First, a scope token gets priority here: if (scope_token ~= 0) { scope_stage = 2; if (scope_token()) rtrue; } ! Pick up everything in the location except the actor's possessions; ! then go through those. (This ensures the actor's possessions are in ! scope even in Darkness.) if (context == MULTIINSIDE_TOKEN && advance_warning ~= -1) { if (IsSeeThrough(advance_warning) == 1) ScopeWithin(advance_warning, 0, context); } else { ! Next, call any user-supplied routine adding things to the scope, ! which may circumvent the usual routines altogether ! if they return true: if (actor == domain1 or domain2) { is = InScope(actor); if (is == false) is = LibraryExtensions.RunWhile(ext_inscope, false, actor); if (is) rtrue; } if (domain1 ~= 0 && domain1 has supporter or container) ScopeWithin_O(domain1, domain1, context); ScopeWithin(domain1, domain2, context); if (domain2 ~= 0 && domain2 has supporter or container) ScopeWithin_O(domain2, domain2, context); ScopeWithin(domain2, 0, context); } ! A special rule applies: ! in Darkness as in light, the actor is always in scope to himself. if (thedark == domain1 or domain2) { ScopeWithin_O(actor, actor, context); if (parent(actor) has supporter or container) ScopeWithin_O(parent(actor), parent(actor), context); } ]; ! ---------------------------------------------------------------------------- ! IsSeeThrough is used at various places: roughly speaking, it determines ! whether o being in scope means that the contents of o are in scope. ! ---------------------------------------------------------------------------- [ IsSeeThrough o; if (o has supporter or transparent || (o has container && o has open)) rtrue; rfalse; ]; ! ---------------------------------------------------------------------------- ! PlaceInScope is provided for routines outside the library, and is not ! called within the parser (except for debugging purposes). ! ---------------------------------------------------------------------------- [ PlaceInScope thing; if (scope_reason~=PARSING_REASON or TALKING_REASON) { DoScopeAction(thing); rtrue; } wn = match_from; TryGivenObject(thing); placed_in_flag = 1; ]; ! ---------------------------------------------------------------------------- ! DoScopeAction ! ---------------------------------------------------------------------------- [ DoScopeAction thing s p1; s = scope_reason; p1 = parser_one; #Ifdef DEBUG; if (parser_trace >= 6) print "[DSA on ", (the) thing, " with reason = ", scope_reason, " p1 = ", parser_one, " p2 = ", parser_two, "]^"; #Endif; ! DEBUG switch (scope_reason) { REACT_BEFORE_REASON: if (thing.react_before == 0 or NULL) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering react_before for ", (the) thing, "]^"; #Endif; ! DEBUG if (parser_one == 0) parser_one = RunRoutines(thing, react_before); REACT_AFTER_REASON: if (thing.react_after == 0 or NULL) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering react_after for ", (the) thing, "]^"; #Endif; ! DEBUG if (parser_one == 0) parser_one = RunRoutines(thing, react_after); EACH_TURN_REASON: if (thing.each_turn == 0) return; #Ifdef DEBUG; if (parser_trace >= 2) print "[Considering each_turn for ", (the) thing, "]^"; #Endif; ! DEBUG PrintOrRun(thing, each_turn); TESTSCOPE_REASON: if (thing == parser_one) parser_two = 1; LOOPOVERSCOPE_REASON: parser_one(thing); parser_one=p1; } scope_reason = s; ]; ! ---------------------------------------------------------------------------- ! ScopeWithin looks for objects in the domain which make textual sense ! and puts them in the match list. (However, it does not recurse through ! the second argument.) ! ---------------------------------------------------------------------------- [ ScopeWithin domain nosearch context x y; if (domain == 0) rtrue; ! Special rule: the directions (interpreted as the 12 walls of a room) are ! always in context. (So, e.g., "examine north wall" is always legal.) ! (Unless we're parsing something like "all", because it would just slow ! things down then, or unless the context is "creature".) if (indef_mode==0 && domain==actors_location && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN) ScopeWithin(compass); ! Look through the objects in the domain, avoiding "objectloop" in case ! movements occur, e.g. when trying each_turn. x = child(domain); while (x ~= 0) { y = sibling(x); ScopeWithin_O(x, nosearch, context); x = y; } ]; [ ScopeWithin_O domain nosearch context i ad n; ! If the scope reason is unusual, don't parse. if (scope_reason ~= PARSING_REASON or TALKING_REASON) { DoScopeAction(domain); jump DontAccept; } ! "it" or "them" matches to the it-object only. (Note that (1) this means ! that "it" will only be understood if the object in question is still ! in context, and (2) only one match can ever be made in this case.) if (match_from <= num_words) { ! If there's any text to match, that is wn = match_from; i = NounWord(); if (i == 1 && player == domain) MakeMatch(domain, 1); if (i >= 2 && i < 128 && (LanguagePronouns-->i == domain)) MakeMatch(domain, 1); } ! Construing the current word as the start of a noun, can it refer to the ! object? wn = match_from; if (TryGivenObject(domain) > 0) if (indef_nspec_at > 0 && match_from ~= indef_nspec_at) { ! This case arises if the player has typed a number in ! which is hypothetically an indefinite descriptor: ! e.g. "take two clubs". We have just checked the object ! against the word "clubs", in the hope of eventually finding ! two such objects. But we also backtrack and check it ! against the words "two clubs", in case it turns out to ! be the 2 of Clubs from a pack of cards, say. If it does ! match against "two clubs", we tear up our original ! assumption about the meaning of "two" and lapse back into ! definite mode. wn = indef_nspec_at; if (TryGivenObject(domain) > 0) { match_from = indef_nspec_at; ResetDescriptors(); } wn = match_from; } .DontAccept; ! Shall we consider the possessions of the current object, as well? ! Only if it's a container (so, for instance, if a dwarf carries a ! sword, then "drop sword" will not be accepted, but "dwarf, drop sword" ! will). ! Also, only if there are such possessions. ! ! Notice that the parser can see "into" anything flagged as ! transparent - such as a dwarf whose sword you can get at. if (child(domain) ~= 0 && domain ~= nosearch && IsSeeThrough(domain) == 1) ScopeWithin(domain,nosearch,context); ! Drag any extras into context ad = domain.&add_to_scope; if (ad ~= 0) { ! Test if the property value is not an object. #Ifdef TARGET_ZCODE; i = (UnsignedCompare(ad-->0, top_object) > 0); #Ifnot; ! TARGET_GLULX i = (((ad-->0)->0) ~= $70); #Endif; ! TARGET_ if (i) { ats_flag = 2+context; RunRoutines(domain, add_to_scope); ats_flag = 0; } else { n = domain.#add_to_scope; for (i=0 : (WORDSIZE*i)i) ScopeWithin_O(ad-->i, 0, context); } } ]; [ AddToScope obj; if (ats_flag >= 2) ScopeWithin_O(obj, 0, ats_flag-2); if (ats_flag == 1) { if (HasLightSource(obj)==1) ats_hls = 1; } ]; ! ---------------------------------------------------------------------------- ! MakeMatch looks at how good a match is. If it's the best so far, then ! wipe out all the previous matches and start a new list with this one. ! If it's only as good as the best so far, add it to the list. ! If it's worse, ignore it altogether. ! ! The idea is that "red panic button" is better than "red button" or "panic". ! ! number_matched (the number of words matched) is set to the current level ! of quality. ! ! We never match anything twice, and keep at most 64 equally good items. ! ---------------------------------------------------------------------------- [ MakeMatch obj quality i; #Ifdef DEBUG; if (parser_trace >= 6) print " Match with quality ",quality,"^"; #Endif; ! DEBUG if (token_filter ~= 0 && UserFilter(obj) == 0) { #Ifdef DEBUG; if (parser_trace >= 6) print " Match filtered out: token filter ", token_filter, "^"; #Endif; ! DEBUG rtrue; } if (quality < match_length) rtrue; if (quality > match_length) { match_length = quality; number_matched = 0; } else { if (number_matched >= MATCH_LIST_SIZE) rtrue; for (i=0 : ii == obj) rtrue; } match_list-->number_matched++ = obj; #Ifdef DEBUG; if (parser_trace >= 6) print " Match added to list^"; #Endif; ! DEBUG ]; ! ---------------------------------------------------------------------------- ! TryGivenObject tries to match as many words as possible in what has been ! typed to the given object, obj. If it manages any words matched at all, ! it calls MakeMatch to say so, then returns the number of words (or 1 ! if it was a match because of inadequate input). ! ---------------------------------------------------------------------------- [ TryGivenObject obj threshold k w j; #Ifdef DEBUG; if (parser_trace >= 5) print " Trying ", (the) obj, " (", obj, ") at word ", wn, "^"; #Endif; ! DEBUG dict_flags_of_noun = 0; ! If input has run out then always match, with only quality 0 (this saves ! time). if (wn > num_words) { if (indef_mode ~= 0) dict_flags_of_noun = DICT_X654; ! Reject "plural" bit MakeMatch(obj,0); #Ifdef DEBUG; if (parser_trace >= 5) print " Matched (0)^"; #Endif; ! DEBUG return 1; } ! Ask the object to parse itself if necessary, sitting up and taking notice ! if it says the plural was used: if (obj.parse_name~=0) { parser_action = NULL; j=wn; k = RunRoutines(obj,parse_name); if (k > 0) { wn=j+k; .MMbyPN; if (parser_action == ##PluralFound) dict_flags_of_noun = dict_flags_of_noun | DICT_PLUR; if (dict_flags_of_noun & DICT_PLUR) { if (~~allow_plurals) k = 0; else { if (indef_mode == 0) { indef_mode = 1; indef_type = 0; indef_wanted = 0; } indef_type = indef_type | PLURAL_BIT; if (indef_wanted == 0) indef_wanted = 100; } } #Ifdef DEBUG; if (parser_trace >= 5) print " Matched (", k, ")^"; #Endif; ! DEBUG MakeMatch(obj,k); return k; } if (k == 0) jump NoWordsMatch; wn = j; } ! The default algorithm is simply to count up how many words pass the ! Refers test: parser_action = NULL; w = NounWord(); if (w == 1 && player == obj) { k=1; jump MMbyPN; } if (w >= 2 && w < 128 && (LanguagePronouns-->w == obj)) { k = 1; jump MMbyPN; } j = --wn; threshold = ParseNoun(obj); if (threshold == -1) { LibraryExtensions.ext_number_1 = wn; ! Set the "between calls" functionality to LibraryExtensions.BetweenCalls = LibraryExtensions.RestoreWN; threshold = LibraryExtensions.RunWhile(ext_parsenoun, -1, obj); LibraryExtensions.BetweenCalls = 0; ! Turn off the "between calls" functionality } #Ifdef DEBUG; if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^"; #Endif; ! DEBUG if (threshold < 0) wn++; if (threshold > 0) { k = threshold; jump MMbyPN; } if (threshold == 0 || Refers(obj,wn-1) == 0) { .NoWordsMatch; if (indef_mode ~= 0) { k = 0; parser_action = NULL; jump MMbyPN; } rfalse; } if (threshold < 0) { threshold = 1; dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100; w = NextWord(); while (Refers(obj, wn-1)) { threshold++; if (w) dict_flags_of_noun = dict_flags_of_noun | ((w->#dict_par1) & (DICT_X654+DICT_PLUR)); w = NextWord(); } } k = threshold; jump MMbyPN; ]; ! ---------------------------------------------------------------------------- ! Refers works out whether the word at number wnum can refer to the object ! obj, returning true or false. The standard method is to see if the ! word is listed under "name" for the object, but this is more complex ! in languages other than English. ! ---------------------------------------------------------------------------- [ Refers obj wnum wd k l m; if (obj == 0 || wnum <= 0) rfalse; #Ifdef LanguageRefers; k = LanguageRefers(obj,wnum); if (k >= 0) return k; #Endif; ! LanguageRefers k = wn; wn = wnum; wd = NextWordStopped(); wn = k; if (parser_inflection_func) { k = parser_inflection(obj, wd); if (k >= 0) return k; m = -k; } else m = parser_inflection; k = obj.&m; l = (obj.#m)/WORDSIZE-1; for (m=0 : m<=l : m++) if (wd == k-->m) rtrue; rfalse; ]; [ WordInProperty wd obj prop k l m; k = obj.∝ l = (obj.#prop)/WORDSIZE-1; for (m=0 : m<=l : m++) if (wd == k-->m) rtrue; rfalse; ]; [ DictionaryLookup b l i; for (i=0 : i(WORDSIZE+i) = b->i; SetKeyBufLength(l, buffer2); Tokenise__(buffer2, parse2); return parse2-->1; ]; ! ---------------------------------------------------------------------------- ! NounWord (which takes no arguments) returns: ! ! 0 if the next word is unrecognised or does not carry the "noun" bit in ! its dictionary entry, ! 1 if a word meaning "me", ! the index in the pronoun table (plus 2) of the value field of a pronoun, ! if the word is a pronoun, ! the address in the dictionary if it is a recognised noun. ! ! The "current word" marker moves on one. ! ---------------------------------------------------------------------------- [ NounWord i j s; i = NextWord(); if (i == 0) rfalse; if (i == ME1__WD or ME2__WD or ME3__WD) return 1; s = LanguagePronouns-->0; for (j=1 : j<=s : j=j+3) if (i == LanguagePronouns-->j) return j+2; if ((i->#dict_par1) & DICT_NOUN == 0) rfalse; return i; ]; ! ---------------------------------------------------------------------------- ! NextWord (which takes no arguments) returns: ! ! 0 if the next word is unrecognised, ! comma_word if a comma ! THEN1__WD if a full stop ! or the dictionary address if it is recognised. ! The "current word" marker is moved on. ! ! NextWordStopped does the same, but returns -1 when input has run out ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ NextWord i j; if (wn <= 0 || wn > parse->1) { wn++; rfalse; } i = wn*2-1; wn++; j = parse-->i; if (j == ',//') j = comma_word; if (j == './/') j = THEN1__WD; return j; ]; [ NextWordStopped; if (wn > parse->1) { wn++; return -1; } return NextWord(); ]; [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer if (p==0) p=parse; if (b==0) b=buffer; return b + p->(wordnum*4+1); ]; [ WordLength wordnum p; ! Length of 'wordnum' string in buffer if (p==0) p=parse; return p->(wordnum*4); ]; [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*2-1); ]; [ NumberWords p; ! Number of parsed strings in buffer if (p==0) p=parse; return p->1; ]; [ GetKeyBufLength b; ! Number of typed chars in buffer if (b==0) b=buffer; return b->1; ]; [ SetKeyBufLength n b; ! Update number of typed chars in buffer if (b==0) b=buffer; if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE; b->1 = n; ]; #Ifnot; ! TARGET_GLULX [ NextWord i j; if (wn <= 0 || wn > parse-->0) { wn++; rfalse; } i = wn*3-2; wn++; j = parse-->i; if (j == ',//') j=comma_word; if (j == './/') j=THEN1__WD; return j; ]; [ NextWordStopped; if (wn > parse-->0) { wn++; return -1; } return NextWord(); ]; [ WordAddress wordnum p b; ! Absolute addr of 'wordnum' string in buffer if (p==0) p=parse; if (b==0) b=buffer; return b + p-->(wordnum*3); ]; [ WordLength wordnum p; ! Length of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*3-1); ]; [ WordValue wordnum p; ! Dictionary value of 'wordnum' string in buffer if (p==0) p=parse; return p-->(wordnum*3-2); ]; [ NumberWords p; ! Number of parsed strings in buffer if (p==0) p=parse; return p-->0; ]; [ GetKeyBufLength b; ! Number of typed chars in buffer if (b==0) b=buffer; return b-->0; ]; [ SetKeyBufLength n b; ! Update number of typed chars in buffer if (b==0) b=buffer; if (n > INPUT_BUFFER_LEN-WORDSIZE) n=INPUT_BUFFER_LEN-WORDSIZE; b-->0 = n; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! TryNumber is the only routine which really does any character-level ! parsing, since that's normally left to the Z-machine. ! It takes word number "wordnum" and tries to parse it as an (unsigned) ! decimal number, returning ! ! -1000 if it is not a number ! the number if it has between 1 and 4 digits ! 10000 if it has 5 or more digits. ! ! (The danger of allowing 5 digits is that Z-machine integers are only ! 16 bits long, and anyway this isn't meant to be perfect.) ! ! Using NumberWord, it also catches "one" up to "twenty". ! ! Note that a game can provide a ParseNumber routine which takes priority, ! to enable parsing of odder numbers ("x45y12", say). ! ---------------------------------------------------------------------------- [ TryNumber wordnum i j c num len mul tot d digit; i = wn; wn = wordnum; j = NextWord(); wn = i; j = NumberWord(j); if (j >= 1) return j; num = WordAddress(wordnum); len = WordLength(wordnum); tot = ParseNumber(num, len); if (tot == 0) tot = LibraryExtensions.RunWhile(ext_parsenumber, 0, num, len); if (tot ~= 0) return tot; if (len >= 4) mul=1000; if (len == 3) mul=100; if (len == 2) mul=10; if (len == 1) mul=1; tot = 0; c = 0; for (c = 0 : c < len : c++) { digit=num->c; if (digit == '0') { d = 0; jump digok; } if (digit == '1') { d = 1; jump digok; } if (digit == '2') { d = 2; jump digok; } if (digit == '3') { d = 3; jump digok; } if (digit == '4') { d = 4; jump digok; } if (digit == '5') { d = 5; jump digok; } if (digit == '6') { d = 6; jump digok; } if (digit == '7') { d = 7; jump digok; } if (digit == '8') { d = 8; jump digok; } if (digit == '9') { d = 9; jump digok; } return -1000; .digok; tot = tot+mul*d; mul = mul/10; } if (len > 4) tot=10000; return tot; ]; ! ---------------------------------------------------------------------------- ! AnyNumber is a general parsing routine which accepts binary, hexadecimal ! and decimal numbers up to the full Zcode/Glulx ranges. ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; ! decimal range is -32768 to 32767 Constant MAX_DECIMAL_SIZE 5; Constant MAX_DECIMAL_BASE 3276; #Ifnot; ! TARGET_GLULX ! decimal range is -2147483648 to 2147483647 Constant MAX_DECIMAL_SIZE 10; Constant MAX_DECIMAL_BASE 214748364; #Endif; ! TARGET_ [ AnyNumber wa we sign base digit digit_count num; wa = WordAddress(wn); we = wa + WordLength(wn); sign = 1; base = 10; if (wa->0 == '-') { sign = -1; wa++; } else { if (wa->0 == '$') { base = 16; wa++; } if (wa->0 == '$') { base = 2; wa++; } } if (wa >= we) return GPR_FAIL; ! no digits after -/$ while (wa->0 == '0') wa++; ! skip leading zeros for (num=0,digit_count=1 : wa0) { '0' to '9': digit = wa->0 - '0'; 'A' to 'F': digit = wa->0 - 'A' + 10; 'a' to 'f': digit = wa->0 - 'a' + 10; default: return GPR_FAIL; } if (digit >= base) return GPR_FAIL; digit_count++; switch (base) { 16: if (digit_count > 2*WORDSIZE) return GPR_FAIL; 2: if (digit_count > 8*WORDSIZE) return GPR_FAIL; 10: if (digit_count > MAX_DECIMAL_SIZE) return GPR_FAIL; if (digit_count == MAX_DECIMAL_SIZE) { if (num > MAX_DECIMAL_BASE) return GPR_FAIL; if (num == MAX_DECIMAL_BASE) { if (sign == 1 && digit > 7) return GPR_FAIL; if (sign == -1 && digit > 8) return GPR_FAIL; } } } num = base*num + digit; } parsed_number = num * sign; wn++; return GPR_NUMBER; ]; ! ---------------------------------------------------------------------------- ! GetGender returns 0 if the given animate object is female, and 1 if male ! (not all games will want such a simple decision function!) ! ---------------------------------------------------------------------------- [ GetGender person; if (person hasnt female) rtrue; rfalse; ]; [ GetGNAOfObject obj case gender; if (obj hasnt animate) case = 6; if (obj has male) gender = male; if (obj has female) gender = female; if (obj has neuter) gender = neuter; if (gender == 0) { if (case == 0) gender = LanguageAnimateGender; else gender = LanguageInanimateGender; } if (gender == female) case = case + 1; if (gender == neuter) case = case + 2; if (obj has pluralname) case = case + 3; return case; ]; ! ---------------------------------------------------------------------------- ! Converting between dictionary addresses and entry numbers ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ]; [ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ]; #Ifnot; ! TARGET_GLULX ! In Glulx, dictionary entries *are* addresses. [ Dword__No w; return w; ]; [ No__Dword n; return n; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! For copying buffers ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ CopyBuffer bto bfrom i size; size = bto->0; for (i=1 : i<=size : i++) bto->i = bfrom->i; ]; #Ifnot; ! TARGET_GLULX [ CopyBuffer bto bfrom i; for (i=0 : ii = bfrom->i; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! Provided for use by language definition files ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ LTI_Insert i ch b y; ! Protect us from strict mode, as this isn't an array in quite the ! sense it expects b = buffer; ! Insert character ch into buffer at point i. ! Being careful not to let the buffer possibly overflow: y = b->1; if (y > b->0) y = b->0; ! Move the subsequent text along one character: for (y=y+2 : y>i : y--) b->y = b->(y-1); b->i = ch; ! And the text is now one character longer: if (b->1 < b->0) (b->1)++; ]; #Ifnot; ! TARGET_GLULX [ LTI_Insert i ch b y; ! Protect us from strict mode, as this isn't an array in quite the ! sense it expects b = buffer; ! Insert character ch into buffer at point i. ! Being careful not to let the buffer possibly overflow: y = b-->0; if (y > INPUT_BUFFER_LEN) y = INPUT_BUFFER_LEN; ! Move the subsequent text along one character: for (y=y+WORDSIZE : y>i : y--) b->y = b->(y-1); b->i = ch; ! And the text is now one character longer: if (b-->0 < INPUT_BUFFER_LEN) (b-->0)++; ]; #Endif; ! TARGET_ ! ============================================================================ [ PronounsSub x y c d; L__M(##Pronouns, 1); c = (LanguagePronouns-->0)/3; if (player ~= selfobj) c++; if (c == 0) return L__M(##Pronouns, 4); for (x=1,d=0 : x<=LanguagePronouns-->0 : x=x+3) { print "~", (address) LanguagePronouns-->x, "~ "; y = LanguagePronouns-->(x+2); if (y == NULL) L__M(##Pronouns, 3); else { L__M(##Pronouns, 2); print (the) y; } d++; if (d < c-1) print (string) COMMA__TX; if (d == c-1) print (SerialComma) c, (string) AND__TX; } if (player ~= selfobj) { print "~", (address) ME1__WD, "~ "; L__M(##Pronouns, 2); c = player; player = selfobj; print (the) c; player = c; } L__M(##Pronouns, 5); ]; [ SetPronoun dword value x; for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (LanguagePronouns-->x == dword) { LanguagePronouns-->(x+2) = value; return; } RunTimeError(12); ]; [ PronounValue dword x; for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (LanguagePronouns-->x == dword) return LanguagePronouns-->(x+2); return 0; ]; [ ResetVagueWords obj; PronounNotice(obj); ]; #Ifdef EnglishNaturalLanguage; [ PronounOldEnglish; if (itobj ~= old_itobj) SetPronoun('it', itobj); if (himobj ~= old_himobj) SetPronoun('him', himobj); if (herobj ~= old_herobj) SetPronoun('her', herobj); old_itobj = itobj; old_himobj = himobj; old_herobj = herobj; ]; #Endif; !EnglishNaturalLanguage [ PronounNotice obj x bm; if (obj == player) return; #Ifdef EnglishNaturalLanguage; PronounOldEnglish(); #Endif; ! EnglishNaturalLanguage bm = PowersOfTwo_TB-->(GetGNAOfObject(obj)); for (x=1 : x<=LanguagePronouns-->0 : x=x+3) if (bm & (LanguagePronouns-->(x+1)) ~= 0) LanguagePronouns-->(x+2) = obj; #Ifdef EnglishNaturalLanguage; itobj = PronounValue('it'); old_itobj = itobj; himobj = PronounValue('him'); old_himobj = himobj; herobj = PronounValue('her'); old_herobj = herobj; #Endif; ! EnglishNaturalLanguage ]; ! ============================================================================ ! End of the parser proper: the remaining routines are its front end. ! ---------------------------------------------------------------------------- Object InformLibrary "(Inform Library)" with play [ i j k l; #Ifdef TARGET_ZCODE; ZZInitialise(); #Ifnot; ! TARGET_GLULX GGInitialise(); #Endif; ! TARGET_ GamePrologue(); while (~~deadflag) { ! everything happens in this loop #Ifdef EnglishNaturalLanguage; PronounOldEnglish(); old_itobj = PronounValue('it'); old_himobj = PronounValue('him'); old_herobj = PronounValue('her'); #Endif; ! EnglishNaturalLanguage .very__late__error; if (score ~= last_score) { if (notify_mode == 1) NotifyTheScore(); last_score = score; } .late__error; inputobjs-->0 = 0; inputobjs-->1 = 0; inputobjs-->2 = 0; inputobjs-->3 = 0; meta = false; ! The Parser writes its results into inputobjs and meta, ! a flag indicating a "meta-verb". This can only be set for ! commands by the player, not for orders to others. InformParser.parse_input(inputobjs); action = inputobjs-->0; ! -------------------------------------------------------------- ! Reverse "give fred biscuit" into "give biscuit to fred" if (action == ##GiveR or ##ShowR) { i = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = i; if (action == ##GiveR) action = ##Give; else action = ##Show; } ! Convert "P, tell me about X" to "ask P about X" if (action == ##Tell && inputobjs-->2 == player && actor ~= player) { inputobjs-->2 = actor; actor = player; action = ##Ask; } ! Convert "ask P for X" to "P, give X to me" if (action == ##AskFor && inputobjs-->2 ~= player && actor == player) { actor = inputobjs-->2; inputobjs-->2 = inputobjs-->3; inputobjs-->3 = player; action = ##Give; } ! For old, obsolete code: special_word contains the topic word ! in conversation if (action == ##Ask or ##Tell or ##Answer) special_word = special_number1; ! -------------------------------------------------------------- multiflag = false; onotheld_mode = notheld_mode; notheld_mode = false; ! For implicit taking and multiple object detection .begin__action; inp1 = 0; inp2 = 0; i = inputobjs-->1; if (i >= 1) inp1 = inputobjs-->2; if (i >= 2) inp2 = inputobjs-->3; ! inp1 and inp2 hold: object numbers, or 0 for "multiple object", ! or 1 for "a number or dictionary address" if (inp1 == 1) noun = special_number1; else noun = inp1; if (inp2 == 1) { if (inp1 == 1) second = special_number2; else second = special_number1; } else second = inp2; ! ------------------------------------------------------------- !print "inp1: ", (name) inp1, "^"; !print "inp2: ", (name) inp2, "^"; !print "inputobjs-->1: ", (name) inputobjs-->1, "^"; !print "inputobjs-->2: ", (name) inputobjs-->2, "^"; !print "inputobjs-->3: ", (name) inputobjs-->3, "^"; !print "noun: ", (name) noun, "^"; !print "second: ", (name) second, "^"; !print "actor: ", (name) actor, "^"; !print "---^"; ! -------------------------------------------------------------- ! Generate the action... if ((i == 0) || (i == 1 && inp1 ~= 0) || (i == 2 && inp1 ~= 0 && inp2 ~= 0)) { if (actor ~= player) { switch (self.actor_act(actor, action, noun, second)) { ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action; default: jump turn__end; } } self.begin_action(action, noun, second, 0); jump turn__end; } ! ...unless a multiple object must be substituted. First: ! (a) check the multiple list isn't empty; ! (b) warn the player if it has been cut short because too long; ! (c) generate a sequence of actions from the list ! (stopping in the event of death or movement away). multiflag = true; j = multiple_object-->0; if (j == 0) { L__M(##Miscellany, 2); jump late__error; } if (toomany_flag) { toomany_flag = false; L__M(##Miscellany, 1); } i = location; for (k=1 : k<=j : k++) { if (deadflag) break; if (location ~= i) { L__M(##Miscellany, 51); break; } l = multiple_object-->k; PronounNotice(l); print (name) l, (string) COLON__TX; if (inp1 == 0) { inp1 = l; switch (self.actor_act(actor, action, l, second)) { ACTOR_ACT_ABORT_NOTUNDERSTOOD: jump begin__action; ACTOR_ACT_ABORT_ORDER: jump turn__end; } inp1 = 0; } else { inp2 = l; if (self.actor_act(actor, action, noun, l) == ACTOR_ACT_ABORT_NOTUNDERSTOOD) jump begin__action; inp2 = 0; } } ! -------------------------------------------------------------- .turn__end; ! No time passes if either (i) the verb was meta, or ! (ii) we've only had the implicit take before the "real" ! action to follow. if (notheld_mode == 1) { NoteObjectAcquisitions(); continue; } if (meta) continue; if (~~deadflag) self.end_turn_sequence(); else if (START_MOVE ~= 1) turns++; } ! end of while() if (deadflag ~= 2 && AfterLife() == false) LibraryExtensions.RunAll(ext_afterlife); if (deadflag == 0) jump very__late__error; GameEpilogue(); ], ! end of 'play' property end_turn_sequence [; AdvanceWorldClock(); if (deadflag) return; RunTimersAndDaemons(); if (deadflag) return; RunEachTurnProperties(); if (deadflag) return; if (TimePasses() == 0) LibraryExtensions.RunAll(ext_timepasses); if (deadflag) return; AdjustLight(); if (deadflag) return; NoteObjectAcquisitions(); ], ! The first time we call self.actor_act(), set the fifth ! parameter to true. Details can be seen in ! 1d95759b1bd6674509d6cf9161e6db3da3831f10 and in Issues #23 and ! #30 (Mantis 1813 and 1885) actor_act [ p a n s ft j sp sa sn ss; sp = actor; actor = p; if (p ~= player) { ! The player's "orders" property can refuse to allow ! conversation here, by returning true. If not, the order is ! sent to the other person's "orders" property. If that also ! returns false, then: if it was a misunderstood command ! anyway, it is converted to an Answer action (thus ! "floyd, grrr" ends up as "say grrr to floyd"). If it was a ! good command, it is finally offered to the Order: part of ! the other person's "life" property, the old-fashioned ! way of dealing with conversation. sa = action; sn = noun; ss = second; action = a; noun = n; second = s; j = RunRoutines(player, orders); if (j == 0) { j = RunRoutines(actor, orders); if (j == 0) { if (action == ##NotUnderstood) { inputobjs-->3 = actor; actor = player; action = ##Answer; ! abort, not resetting action globals return ACTOR_ACT_ABORT_NOTUNDERSTOOD; } if (RunLife(actor, ##Order) == 0) { L__M(##Order, 1, actor); if (~~ft) return ACTOR_ACT_ABORT_ORDER; } } } action = sa; noun = sn; second = ss; if (ft) return ACTOR_ACT_ABORT_ORDER; } else if (~~ft) self.begin_action(a, n, s, 0); actor = sp; return ACTOR_ACT_OK; ], begin_action [ a n s source sa sn ss; sa = action; sn = noun; ss = second; action = a; noun = n; second = s; #Ifdef DEBUG; if (debug_flag & DEBUG_ACTIONS) TraceAction(source); #Ifnot; source = 0; #Endif; ! DEBUG #Iftrue (Grammar__Version == 1); if ((meta || BeforeRoutines() == false) && action < 256) { #Ifdef INFIX; if (infix_verb) { if (BeforeRoutines() == false) ActionPrimitive(); } else ActionPrimitive(); #Ifnot; ActionPrimitive(); #Endif; } #Ifnot; if ((meta || BeforeRoutines() == false) && action < 4096) { #Ifdef INFIX; if (infix_verb) { if (BeforeRoutines() == false) ActionPrimitive(); } else ActionPrimitive(); #Ifnot; ActionPrimitive(); #Endif; } #Endif; ! Grammar__Version action = sa; noun = sn; second = ss; ], has proper; ! ---------------------------------------------------------------------------- ! Routines called before and after main 'play' loop [ GamePrologue i j; before_first_turn = true; for (i=1 : i<=100 : i++) j = random(i); ChangeDefault(cant_go, CANTGO__TX); real_location = thedark; player = selfobj; actor = player; selfobj.capacity = MAX_CARRIED; ! ### change? #Ifdef LanguageInitialise; LanguageInitialise(); #Endif; ! LanguageInitialise #Ifdef EnglishNaturalLanguage; old_itobj = itobj; old_himobj = himobj; old_herobj = herobj; #Endif;! EnglishNaturalLanguage new_line; LibraryExtensions.RunAll(ext_initialise); j = Initialise(); last_score = score; initial_lookmode = lookmode; objectloop (i in player) give i moved ~concealed; move player to location; while (parent(location)) location = parent(location); real_location = location; MoveFloatingObjects(); actor = player; ! resync, because player may have been changed in Initialise() actors_location = location; lightflag = OffersLight(parent(player)); if (lightflag == 0) location = thedark; if (j ~= 2) Banner(); #ifndef NOINITIAL_LOOK; ; #endif; before_first_turn = false; ]; [ GameEpilogue; print "^^ "; #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; ! V5 #Ifnot; ! TARGET_GLULX glk_set_style(style_Alert); #Endif; ! TARGET_ print "***"; switch (deadflag) { 1: L__M(##Miscellany, 3); 2: L__M(##Miscellany, 4); default: print " "; if (DeathMessage() == false) LibraryExtensions.RunAll(ext_deathmessage); print " "; } print "***"; #Ifdef TARGET_ZCODE; #IfV5; style roman; #Endif; ! V5 #Ifnot; ! TARGET_GLULX glk_set_style(style_Normal); #Endif; ! TARGET_ print "^^"; #Ifndef NO_SCORE; print "^"; #Endif; ! NO_SCORE Epilogue(); ScoreSub(); DisplayStatus(); AfterGameOver(); ]; ! ---------------------------------------------------------------------------- ! Routines called at end of each turn [ AdvanceWorldClock; turns++; if (the_time ~= NULL) { if (time_rate >= 0) the_time=the_time+time_rate; else { time_step--; if (time_step == 0) { the_time++; time_step = -time_rate; } } the_time = the_time % 1440; } ]; [ RunTimersAndDaemons i j; #Ifdef DEBUG; if (debug_flag & DEBUG_TIMERS) { for (i=0 : ii; if (j ~= 0) { print (name) (j&~WORD_HIGHBIT), ": "; if (j & WORD_HIGHBIT) print "daemon"; else print "timer with ", j.time_left, " turns to go"; new_line; } } } #Endif; ! DEBUG for (i=0 : ii; if (j ~= 0) { if (j & WORD_HIGHBIT) RunRoutines(j&~WORD_HIGHBIT, daemon); else { if (j.time_left == 0) { StopTimer(j); RunRoutines(j, time_out); } else j.time_left = j.time_left-1; } } } ]; [ RunEachTurnProperties; scope_reason = EACH_TURN_REASON; verb_word = 0; DoScopeAction(location); SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; ]; ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ ActionPrimitive; (#actions_table-->action)(); ]; #Ifnot; ! TARGET_GLULX [ ActionPrimitive; (#actions_table-->(action+1))(); ]; #Endif; ! TARGET_ [ AfterGameOver i amus_ret; .RRQPL; L__M(##Miscellany,5); .RRQL; L__M(##Prompt); #Ifdef TARGET_ZCODE; #IfV3; read buffer parse; #Endif; ! V3 temp_global=0; #IfV5; read buffer parse DrawStatusLine; #Endif; ! V5 #Ifnot; ! TARGET_GLULX KeyboardPrimitive(buffer, parse); #Endif; ! TARGET_ i = parse-->1; if (i == QUIT1__WD or QUIT2__WD) { #Ifdef TARGET_ZCODE; quit; #Ifnot; ! TARGET_GLULX quit; #Endif; ! TARGET_ } if (i == RESTART__WD) { #Ifdef TARGET_ZCODE; @restart; #Ifnot; ! TARGET_GLULX @restart; #Endif; ! TARGET_ } if (i == RESTORE__WD) { RestoreSub(); jump RRQPL; } if (i == FULLSCORE1__WD or FULLSCORE2__WD && TASKS_PROVIDED==0) { new_line; FullScoreSub(); jump RRQPL; } if (deadflag == 2 && i == AMUSING__WD) { amus_ret = false; if (AMUSING_PROVIDED == 0) { new_line; amus_ret = Amusing(); } if (amus_ret == false) LibraryExtensions.RunAll(ext_amusing); jump RRQPL; } #IfV5; if (i == UNDO1__WD or UNDO2__WD or UNDO3__WD) { i = PerformUndo(); if (i == 0) jump RRQPL; } #Endif; ! V5 L__M(##Miscellany, 8); jump RRQL; ]; [ NoteObjectAcquisitions i; objectloop (i in player) if (i hasnt moved) { give i moved; if (i has scored) { score = score + OBJECT_SCORE; things_score = things_score + OBJECT_SCORE; } } ]; ! ---------------------------------------------------------------------------- ! R_Process is invoked by the <...> and <<...>> statements, whose syntax is: ! ! traditional ! ! introduced at compiler 6.33 [ R_Process a n s p s1 s2 s3; s1 = inp1; s2 = inp2; s3 = actor; inp1 = n; inp2 = s; if (p) actor = p; else actor = player; InformLibrary.begin_action(a, n, s, 1); inp1 = s1; inp2 = s2; actor = s3; ]; ! ---------------------------------------------------------------------------- [ TestScope obj act a al sr x y; x = parser_one; y = parser_two; parser_one = obj; parser_two = 0; a = actor; al = actors_location; sr = scope_reason; scope_reason = TESTSCOPE_REASON; if (act == 0) actor = player; else actor = act; actors_location = ScopeCeiling(actor); SearchScope(actors_location, actor, 0); scope_reason = sr; actor = a; actors_location = al; parser_one = x; x = parser_two; parser_two = y; return x; ]; [ LoopOverScope routine act x y a al; x = parser_one; y = scope_reason; a = actor; al = actors_location; parser_one = routine; if (act == 0) actor = player; else actor = act; actors_location = ScopeCeiling(actor); scope_reason = LOOPOVERSCOPE_REASON; SearchScope(actors_location, actor, 0); parser_one = x; scope_reason = y; actor = a; actors_location = al; ]; [ BeforeRoutines rv; if (GamePreRoutine()) rtrue; if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepreroutine, 0); if (rv) rtrue; if (RunRoutines(player, orders)) rtrue; scope_reason = REACT_BEFORE_REASON; parser_one = 0; SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; if (parser_one) rtrue; if (location && RunRoutines(location, before)) rtrue; if (inp1 > 1 && RunRoutines(inp1, before)) rtrue; rfalse; ]; [ AfterRoutines rv; scope_reason = REACT_AFTER_REASON; parser_one = 0; SearchScope(ScopeCeiling(player), player, 0); scope_reason = PARSING_REASON; if (parser_one) rtrue; if (location && RunRoutines(location, after)) rtrue; if (inp1 > 1 && RunRoutines(inp1, after)) rtrue; rv = GamePostRoutine(); if (rv == false) rv=LibraryExtensions.RunWhile(ext_gamepostroutine, false); return rv; ]; [ RunLife a j; #Ifdef DEBUG; if (debug_flag & DEBUG_ACTIONS) TraceAction(2, j); #Endif; ! DEBUG reason_code = j; return RunRoutines(a,life); ]; [ ZRegion addr; switch (metaclass(addr)) { ! Left over from Inform 5 nothing: return 0; Object, Class: return 1; Routine: return 2; String: return 3; } ]; [ PrintOrRun obj prop flag; if (obj.#prop > WORDSIZE) return RunRoutines(obj, prop); if (obj.prop == NULL) rfalse; switch (metaclass(obj.prop)) { Class, Object, nothing: return RunTimeError(2,obj,prop); String: print (string) obj.prop; if (flag == 0) new_line; rtrue; Routine: return RunRoutines(obj, prop); } ]; [ PrintOrRunVar var flag; switch (metaclass(var)) { Object: print (name) var; String: print (string) var; if (flag == 0) new_line; Routine: return var(); default: print (char) '(', var, (char) ')'; } rtrue; ]; [ ValueOrRun obj prop; !### this is entirely unlikely to work. Does anyone care? (AP) ! Well, it's certainly used three times in verblibm.h (RF) ! Update: now not used (RF) if (obj.prop < 256) return obj.prop; return RunRoutines(obj, prop); ]; [ RunRoutines obj prop; if (obj == thedark && prop ~= initial or short_name or description) obj = real_location; if (obj.&prop == 0 && prop >= INDIV_PROP_START) rfalse; return obj.prop(); ]; #Ifdef TARGET_ZCODE; [ ChangeDefault prop val a b; ! Use assembly-language here because -S compilation won't allow this: @loadw 0 5 -> a; b = prop-1; @storew a b val; ]; #Ifnot; ! TARGET_GLULX [ ChangeDefault prop val; ! Use assembly-language here because -S compilation won't allow this: ! #cpv__start-->prop = val; @astore #cpv__start prop val; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- [ StartTimer obj timer i; for (i=0 : ii == obj) rfalse; for (i=0 : ii == 0) jump FoundTSlot; i = active_timers++; if (i >= MAX_TIMERS) { RunTimeError(4); return; } .FoundTSlot; if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; } the_timers-->i = obj; obj.time_left = timer; ]; [ StopTimer obj i; for (i=0 : ii == obj) jump FoundTSlot2; rfalse; .FoundTSlot2; if (obj.&time_left == 0) { RunTimeError(5, obj, time_left); return; } the_timers-->i = 0; obj.time_left = 0; ]; [ StartDaemon obj i; for (i=0 : ii == WORD_HIGHBIT + obj) rfalse; for (i=0 : ii == 0) jump FoundTSlot3; i = active_timers++; if (i >= MAX_TIMERS) RunTimeError(4); .FoundTSlot3; the_timers-->i = WORD_HIGHBIT + obj; ]; [ StopDaemon obj i; for (i=0 : ii == WORD_HIGHBIT + obj) jump FoundTSlot4; rfalse; .FoundTSlot4; the_timers-->i = 0; ]; ! ---------------------------------------------------------------------------- [ DisplayStatus; if (sys_statusline_flag == 0) { sline1 = score; sline2 = turns; } else { sline1 = the_time/60; sline2 = the_time%60;} ]; [ SetTime t s; the_time = t; time_rate = s; time_step = 0; if (s < 0) time_step = 0-s; ]; [ NotifyTheScore; #Ifdef TARGET_GLULX; glk_set_style(style_Note); #Endif; ! TARGET_GLULX print "^["; L__M(##Miscellany, 50, score-last_score); print ".]^"; #Ifdef TARGET_GLULX; glk_set_style(style_Normal); #Endif; ! TARGET_GLULX ]; ! ---------------------------------------------------------------------------- [ AdjustLight flag i; i = lightflag; lightflag = OffersLight(parent(player)); if (i == 0 && lightflag == 1) { location = real_location; if (flag == 0) ; } if (i == 1 && lightflag == 0) { real_location = location; location = thedark; if (flag == 0) { NoteArrival(); return L__M(##Miscellany, 9); } } if (i == 0 && lightflag == 0) location = thedark; ]; [ OffersLight i j; if (i == 0) rfalse; if (i has light) rtrue; objectloop (j in i) if (HasLightSource(j) == 1) rtrue; if (i has container) { if (i has open || i has transparent) return OffersLight(parent(i)); } else { if (i has enterable || i has transparent || i has supporter) return OffersLight(parent(i)); } rfalse; ]; [ HidesLightSource obj; if (obj == player) rfalse; if (obj has transparent or supporter) rfalse; if (obj has container) return (obj hasnt open); return (obj hasnt enterable); ]; [ HasLightSource i j ad; if (i == 0) rfalse; if (i has light) rtrue; if (i has enterable || IsSeeThrough(i) == 1) if (~~(HidesLightSource(i))) objectloop (j in i) if (HasLightSource(j) == 1) rtrue; ad = i.&add_to_scope; if (parent(i) ~= 0 && ad ~= 0) { if (metaclass(ad-->0) == Routine) { ats_hls = 0; ats_flag = 1; RunRoutines(i, add_to_scope); ats_flag = 0; if (ats_hls == 1) rtrue; } else { for (j=0 : (WORDSIZE*j)j) == 1) rtrue; } } rfalse; ]; [ ChangePlayer obj flag i; ! if (obj.&number == 0) return RunTimeError(7, obj); if (obj == nothing) obj = selfobj; if (actor == player) actor=obj; give player ~transparent ~concealed; i = obj; while (parent(i) ~= 0) { if (i has animate) give i transparent; i = parent(i); } if (player == selfobj && player provides nameless && player.nameless == true) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { player.short_name = MYFORMER__TX; (player.&name)-->0 = 'my'; (player.&name)-->1 = 'former'; (player.&name)-->2 = 'self'; } else if (player.narrative_voice == 2) { player.short_name = FORMER__TX; (player.&name)-->0 = 'my'; (player.&name)-->1 = 'former'; (player.&name)-->2 = 'self'; } } } player = obj; give player transparent concealed animate; i = player; while (parent(i) ~= 0) i = parent(i); location = i; real_location = location; if (parent(player) == 0) return RunTimeError(10); MoveFloatingObjects(); lightflag = OffersLight(parent(player)); if (lightflag == 0) location = thedark; print_player_flag = flag; ]; ! ---------------------------------------------------------------------------- #Ifdef DEBUG; #Ifdef TARGET_ZCODE; [ DebugParameter w; print w; if (w >= 1 && w <= top_object) print " (", (name) w, ")"; if (UnsignedCompare(w, dict_start) >= 0 && UnsignedCompare(w, dict_end) < 0 && (w - dict_start) % dict_entry_size == 0) print " ('", (address) w, "')"; ]; [ DebugAction a anames; #Iftrue (Grammar__Version == 1); if (a >= 256) { print ""; return; } #Ifnot; if (a >= 4096) { print ""; return; } #Endif; ! Grammar__Version anames = #identifiers_table; anames = anames + 2*(anames-->0) + 2*48; print (string) anames-->a; ]; [ DebugAttribute a anames; if (a < 0 || a >= 48) print ""; else { anames = #identifiers_table; anames = anames + 2*(anames-->0); print (string) anames-->a; } ]; #Ifnot; ! TARGET_GLULX [ DebugParameter w endmem; print w; @getmemsize endmem; if (w >= 1 && w < endmem) { if (w->0 >= $70 && w->0 < $7F) print " (", (name) w, ")"; if (w->0 >= $60 && w->0 < $6F) print " ('", (address) w, "')"; } ]; [ DebugAction a str; if (a >= 4096) { print ""; return; } if (a < 0 || a >= #identifiers_table-->7) print ""; else { str = #identifiers_table-->6; str = str-->a; if (str) print (string) str; else print ""; } ]; [ DebugAttribute a str; if (a < 0 || a >= NUM_ATTR_BYTES*8) print ""; else { str = #identifiers_table-->4; str = str-->a; if (str) print (string) str; else print ""; } ]; #Endif; ! TARGET_ [ TraceAction source ar; if (source < 2) print "[ Action ", (DebugAction) action; else { if (ar == ##Order) print "[ Order to ", (name) actor, ": ", (DebugAction) action; else print "[ Life rule ", (DebugAction) ar; } if (noun ~= 0) print " with noun ", (DebugParameter) noun; if (second ~= 0) print " and second ", (DebugParameter) second; if (source == 0) print " "; if (source == 1) print " (from < > statement) "; print "]^"; ]; [ DebugToken token; AnalyseToken(token); switch (found_ttype) { ILLEGAL_TT: print ""; ELEMENTARY_TT: switch (found_tdata) { NOUN_TOKEN: print "noun"; HELD_TOKEN: print "held"; MULTI_TOKEN: print "multi"; MULTIHELD_TOKEN: print "multiheld"; MULTIEXCEPT_TOKEN: print "multiexcept"; MULTIINSIDE_TOKEN: print "multiinside"; CREATURE_TOKEN: print "creature"; SPECIAL_TOKEN: print "special"; NUMBER_TOKEN: print "number"; TOPIC_TOKEN: print "topic"; ENDIT_TOKEN: print "END"; } PREPOSITION_TT: print "'", (address) found_tdata, "'"; ROUTINE_FILTER_TT: #Ifdef INFIX; print "noun=", (InfixPrintPA) found_tdata; #Ifnot; print "noun=Routine(", found_tdata, ")"; #Endif; ! INFIX ATTR_FILTER_TT: print (DebugAttribute) found_tdata; SCOPE_TT: #Ifdef INFIX; print "scope=", (InfixPrintPA) found_tdata; #Ifnot; print "scope=Routine(", found_tdata, ")"; #Endif; ! INFIX GPR_TT: #Ifdef INFIX; print (InfixPrintPA) found_tdata; #Ifnot; print "Routine(", found_tdata, ")"; #Endif; ! INFIX } ]; [ DebugGrammarLine pcount; print " * "; for (: line_token-->pcount ~= ENDIT_TOKEN : pcount++) { if ((line_token-->pcount)->0 & $10) print "/ "; print (DebugToken) line_token-->pcount, " "; } print "-> ", (DebugAction) action_to_be; if (action_reversed) print " reverse"; ]; #Ifdef TARGET_ZCODE; [ ShowVerbSub grammar lines j; if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0) "Try typing ~showverb~ and then the name of a verb."; print "Verb"; if ((noun->#dict_par1) & DICT_META) print " meta"; for (j=dict_start : j#dict_par2 == noun->#dict_par2) print " '", (address) j, "'"; new_line; grammar = (HDR_STATICMEMORY-->0)-->($ff-(noun->#dict_par2)); lines = grammar->0; grammar++; if (lines == 0) "has no grammar lines."; for (: lines>0 : lines--) { grammar = UnpackGrammarLine(grammar); print " "; DebugGrammarLine(); new_line; } ]; #Ifnot; ! TARGET_GLULX [ ShowVerbSub grammar lines i j wd dictlen entrylen; if (noun == 0 || ((noun->#dict_par1) & DICT_VERB) == 0) "Try typing ~showverb~ and then the name of a verb."; print "Verb"; if ((noun->#dict_par1) & DICT_META) print " meta"; dictlen = #dictionary_table-->0; entrylen = DICT_WORD_SIZE + 7; for (j=0 : j#dict_par2 == noun->#dict_par2) print " '", (address) wd, "'"; } new_line; i = DictionaryWordToVerbNum(noun); grammar = (#grammar_table)-->(i+1); lines = grammar->0; grammar++; if (lines == 0) "has no grammar lines."; for (: lines>0 : lines--) { grammar = UnpackGrammarLine(grammar); print " "; DebugGrammarLine(); new_line; } ]; #Endif; ! TARGET_ [ ShowObjSub c f l a n x numattr; if (noun == 0) noun = location; objectloop (c ofclass Class) if (noun ofclass c) { f++; l=c; } if (f == 1) print (name) l, " ~"; else print "Object ~"; print (name) noun, "~ (", noun, ")"; if (parent(noun)) print " in ~", (name) parent(noun), "~ (", parent(noun), ")"; new_line; if (f > 1) { print " class "; objectloop (c ofclass Class) if (noun ofclass c) print (name) c, " "; new_line; } #Ifdef TARGET_ZCODE; numattr = 48; #Ifnot; ! TARGET_GLULX numattr = NUM_ATTR_BYTES * 8; #Endif; ! TARGET_ for (a=0,f=0 : a0; #Ifnot; ! TARGET_GLULX l = INDIV_PROP_START + #identifiers_table-->3; #Endif; ! TARGET_ for (a=1 : a<=l : a++) { if ((a ~= 2 or 3) && noun.&a) { if (f == 0) { print " with "; f=1; } print (property) a; n = noun.#a; for (c=0 : WORDSIZE*cc; if (a == name) print "'", (address) x, "'"; else { if (a == number or capacity or time_left) print x; else { switch (x) { NULL: print "NULL"; 0: print "0"; 1: print "1"; default: switch (metaclass(x)) { Class, Object: print (name) x; String: print "~", (string) x, "~"; Routine: print "[...]"; } print " (", x, ")"; } } } } print ",^ "; } } ! if (f==1) new_line; ]; [ ShowDictSub_helper x; print (address) x; ]; [ ShowDictSub dp el ne f x y z; #Ifdef TARGET_ZCODE; dp = HDR_DICTIONARY-->0; ! start of dictionary dp = dp + dp->0 + 1; ! skip over word-separators table el = dp->0; dp = dp + 1; ! entry length ne = dp-->0; dp = dp + WORDSIZE; ! number of entries #Ifnot; ! TARGET_GLULX; dp = #dictionary_table; ! start of dictionary el = DICT_WORD_SIZE + 7; ! entry length ne = dp-->0; dp = dp + WORDSIZE; ! number of entries #Endif; ! TARGET_ ! dp now at first entry wn = 2; x = NextWordStopped(); switch (x) { 0: "That word isn't in the dictionary."; -1: ; ! show all entries THEN1__WD: dp = './/'; ne = 1; ! show '.' COMMA_WORD: dp = ',//'; ne = 1; ! show ',' default: dp = x; ne = 1; f = true; ! show specified entry, plus associated objects } for ( : ne-- : dp=dp+el) { print (address) dp, " --> "; x = dp->#dict_par1; ! flag bits y = PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, ShowDictSub_helper, dp) + WORDSIZE; for (z=WORDSIZE : zz = UpperCase(StorageForShortName->z); if (y > WORDSIZE+1 && StorageForShortName->z == ' ' or '.' or ',') x = x | $8000; print (char) StorageForShortName->z; } print " --> "; if (x == 0) print " no flags"; else { !if (x & $0040) print " BIT_6"; !if (x & $0020) print " BIT_5"; !if (x & $0010) print " BIT_4"; if (x & $8000) print " UNTYPEABLE"; if (x & DICT_NOUN) print " noun"; if (x & DICT_PLUR) print "+plural"; if (x & DICT_VERB) print " verb"; if (x & DICT_META) print "+meta"; if (x & DICT_PREP) print " preposition"; if (f && (x & DICT_NOUN)) { print " --> refers to these objects:"; objectloop (x) if (WordInProperty(dp, x, name)) print "^ ", (name) x, " (", x, ")"; } } new_line; } ]; #Endif; ! DEBUG ! ---------------------------------------------------------------------------- ! Miscellaneous display routines used by DrawStatusLine and available for ! user. Most of these vary according to which machine is being compiled to ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; [ ClearScreen window; switch (window) { WIN_ALL: @erase_window -1; WIN_STATUS: @erase_window 1; WIN_MAIN: @erase_window 0; } ]; #Iftrue (#version_number == 6); [ MoveCursorV6 line column charw; ! 1-based postion on text grid @get_wind_prop 1 13 -> charw; ! font size charw = charw & $FF; line = 1 + charw*(line-1); column = 1 + charw*(column-1); @set_cursor line column; ]; #Endif; #Ifndef MoveCursor; [ MoveCursor line column; ! 1-based postion on text grid if (~~statuswin_current) { @set_window 1; #Ifdef COLOUR; if (clr_on && clr_bgstatus > 1) @set_colour clr_fgstatus clr_bgstatus; else #Endif; ! COLOUR style reverse; } if (line == 0) { line = 1; column = 1; } #Iftrue (#version_number == 6); MoveCursorV6(line, column); #Ifnot; @set_cursor line column; #Endif; statuswin_current = true; ]; #Endif; [ MainWindow; if (statuswin_current) { #Ifdef COLOUR; if (clr_on && clr_bgstatus > 1) @set_colour clr_fg clr_bg; else #Endif; ! COLOUR style roman; @set_window 0; } statuswin_current = false; ]; #Iftrue (#version_number == 6); [ ScreenWidth width charw; @get_wind_prop 1 3 -> width; @get_wind_prop 1 13 -> charw; charw = charw & $FF; if (charw == 0) return width; return (width+charw-1) / charw; ]; #Ifnot; [ ScreenWidth; return (HDR_SCREENWCHARS->0); ]; #Endif; [ ScreenHeight; return (HDR_SCREENHLINES->0); ]; #Iftrue (#version_number == 6); [ StatusLineHeight height wx wy x y charh; ! Split the window. Standard 1.0 interpreters should keep the window 0 ! cursor in the same absolute position, but older interpreters, ! including Infocom's don't - they keep the window 0 cursor in the ! same position relative to its origin. We therefore compensate ! manually. @get_wind_prop 0 0 -> wy; @get_wind_prop 0 1 -> wx; @get_wind_prop 0 13 -> charh; @log_shift charh $FFF8 -> charh; @get_wind_prop 0 4 -> y; @get_wind_prop 0 5 -> x; height = height * charh; @split_window height; y = y - height + wy - 1; if (y < 1) y = 1; x = x + wx - 1; @set_cursor y x 0; gg_statuswin_cursize = height; ]; #Ifnot; [ StatusLineHeight height; if (gg_statuswin_cursize ~= height) @split_window height; gg_statuswin_cursize = height; ]; #Endif; #Ifdef COLOUR; [ SetColour f b window; if (window == 0) { ! if setting both together, set reverse clr_fgstatus = b; clr_bgstatus = f; } if (window == 1) { clr_fgstatus = f; clr_bgstatus = b; } if (window == 0 or 2) { clr_fg = f; clr_bg = b; } if (clr_on) { if (statuswin_current) @set_colour clr_fgstatus clr_bgstatus; else @set_colour clr_fg clr_bg; } ]; #Endif; ! COLOUR #Ifnot; ! TARGET_GLULX [ ClearScreen window; if (window == WIN_ALL or WIN_MAIN) { glk_window_clear(gg_mainwin); if (gg_quotewin) { glk_window_close(gg_quotewin, 0); gg_quotewin = 0; } } if (gg_statuswin && window == WIN_ALL or WIN_STATUS) glk_window_clear(gg_statuswin); ]; [ MoveCursor line column; ! 0-based postion on text grid if (gg_statuswin) { glk_set_window(gg_statuswin); } if (line == 0) { line = 1; column = 1; } glk_window_move_cursor(gg_statuswin, column-1, line-1); statuswin_current=1; ]; [ MainWindow; glk_set_window(gg_mainwin); statuswin_current=0; ]; [ MakeColourWord c; if (c > 9) return c; c = c-2; return $ff0000*(c&1) + $ff00*(c&2 ~= 0) + $ff*(c&4 ~= 0); ]; [ ScreenWidth id; id=gg_mainwin; if (gg_statuswin && statuswin_current) id = gg_statuswin; glk_window_get_size(id, gg_arguments, 0); return gg_arguments-->0; ]; [ ScreenHeight; glk_window_get_size(gg_mainwin, 0, gg_arguments); return gg_arguments-->0; ]; #Ifdef COLOUR; [ SetColour f b window doclear i fwd bwd swin; if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer if (clr_on) { fwd = MakeColourWord(f); bwd = MakeColourWord(b); for (i=0 : i<=10: i++) { if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints glk_stylehint_clear(swin, i, 7); glk_stylehint_clear(swin, i, 8); } else { glk_stylehint_set(swin, i, 7, fwd); glk_stylehint_set(swin, i, 8, bwd); } } ! Now re-open the windows to apply the hints if (gg_statuswin) glk_window_close(gg_statuswin, 0); if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) { glk_window_close(gg_mainwin, 0); gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK); if (gg_scriptstr ~= 0) glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); } gg_statuswin = glk_window_open($12, gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK); if (statuswin_current && gg_statuswin) MoveCursor(); else MainWindow(); } if (window ~= 2) { clr_fgstatus = f; clr_bgstatus = b; } if (window ~= 1) { clr_fg = f; clr_bg = b; } ]; #Endif; ! COLOUR #Endif; ! TARGET_ #Ifndef COLOUR; [ SetColour f b window doclear; f = b = window = doclear = 0; ]; #Endif; [ SetClr f b w; SetColour (f, b, w); ]; [ RestoreColours; ! L61007, L61113 gg_statuswin_cursize = -1; ! Force window split in StatusLineHeight() #Ifdef COLOUR; if (clr_on) { ! check colour has been used SetColour(clr_fg, clr_bg, 2); ! make sure both sets of variables are restored SetColour(clr_fgstatus, clr_bgstatus, 1, true); ClearScreen(); } #Endif; #Ifdef TARGET_ZCODE; #Iftrue (#version_number == 6); ! request screen update (0-->8) = (0-->8) | $$00000100; #Endif; #Endif; ! TARGET_ ]; ! ---------------------------------------------------------------------------- ! Except in Version 3, the DrawStatusLine routine does just that: this is ! provided explicitly so that it can be Replace'd to change the style, and ! as written it emulates the ordinary Standard game status line, which is ! drawn in hardware ! ---------------------------------------------------------------------------- #Ifdef TARGET_ZCODE; #IfV5; #Iftrue (#version_number == 6); [ DrawStatusLine width x charw scw mvw; HDR_GAMEFLAGS-->0 = (HDR_GAMEFLAGS-->0) & ~$0004; StatusLineHeight(gg_statuswin_size); ! Now clear the window. This isn't totally trivial. Our approach is to ! select the fixed space font, measure its width, and print an appropriate ! number of spaces. We round up if the screen isn't a whole number of ! characters wide, and rely on window 1 being set to clip by default. MoveCursor(1, 1); @set_font 4 -> x; width = ScreenWidth(); spaces width; ! Back to standard font for the display. We use output_stream 3 to ! measure the space required, the aim being to get 50 characters ! worth of space for the location name. MoveCursor(1, 2); @set_font 1 -> x; if (location == thedark) print (name) location; else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } @get_wind_prop 1 3 -> width; @get_wind_prop 1 13 -> charw; charw = charw & $FF; @output_stream 3 StorageForShortName; print (string) SCORE__TX, "00000"; @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw; @output_stream 3 StorageForShortName; print (string) MOVES__TX, "00000"; @output_stream -3; mvw = HDR_PIXELSTO3-->0 + charw; if (width - scw - mvw >= 50*charw) { x = 1+width-scw-mvw; @set_cursor 1 x; print (string) SCORE__TX, sline1; x = x+scw; @set_cursor 1 x; print (string) MOVES__TX, sline2; } else { @output_stream 3 StorageForShortName; print "00000/00000"; @output_stream -3; scw = HDR_PIXELSTO3-->0 + charw; if (width - scw >= 50*charw) { x = 1+width-scw; @set_cursor 1 x; print sline1, "/", sline2; } } ! Reselect roman, as Infocom's interpreters interpreters go funny ! if reverse is selected twice. MainWindow(); ]; #Endif; ! #version_number == 6 #Endif; ! V5 #Endif; ! TARGET_ZCODE #Ifndef DrawStatusLine; [ DrawStatusLine width posa posb; #Ifdef TARGET_GLULX; ! If we have no status window, we must not try to redraw it. if (gg_statuswin == 0) return; #Endif; ! If there is no player location, we shouldn't try to draw status window if (location == nothing || parent(player) == nothing) return; StatusLineHeight(gg_statuswin_size); MoveCursor(1, 1); width = ScreenWidth(); posa = width-26; posb = width-13; spaces width; MoveCursor(1, 2); if (location == thedark) { print (name) location; } else { FindVisibilityLevels(); if (visibility_ceiling == location) print (name) location; else print (The) visibility_ceiling; } if (sys_statusline_flag && width > 53) { MoveCursor(1, posa); print (string) TIME__TX; LanguageTimeOfDay(sline1, sline2); } else { if (width > 66) { #Ifndef NO_SCORE; MoveCursor(1, posa); print (string) SCORE__TX, sline1; #Endif; MoveCursor(1, posb); print (string) MOVES__TX, sline2; } #Ifndef NO_SCORE; if (width > 53 && width <= 66) { MoveCursor(1, posb); print sline1, "/", sline2; } #Endif; } MainWindow(); ! set_window ]; #Endif; #Ifdef TARGET_GLULX; [ StatusLineHeight hgt parwin; if (gg_statuswin == 0) return; if (hgt == gg_statuswin_cursize) return; parwin = glk_window_get_parent(gg_statuswin); glk_window_set_arrangement(parwin, $12, hgt, 0); gg_statuswin_cursize = hgt; ]; [ Box__Routine maxwid arr ix lines lastnl parwin; maxwid = 0; ! squash compiler warning lines = arr-->0; if (gg_quotewin == 0) { gg_arguments-->0 = lines; ix = InitGlkWindow(GG_QUOTEWIN_ROCK); if (ix == false) ix = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_QUOTEWIN_ROCK); if (ix == false) gg_quotewin = glk_window_open(gg_mainwin, $12, lines, 3, GG_QUOTEWIN_ROCK); ! window_open } else { parwin = glk_window_get_parent(gg_quotewin); glk_window_set_arrangement(parwin, $12, lines, 0); } lastnl = true; if (gg_quotewin) { glk_window_clear(gg_quotewin); glk_set_window(gg_quotewin); lastnl = false; } ! If gg_quotewin is zero here, the quote just appears in the story window. glk_set_style(style_BlockQuote); for (ix=0 : ix(ix+1); if (ix < lines-1 || lastnl) new_line; } glk_set_style(style_Normal); if (gg_quotewin) { glk_set_window(gg_mainwin); } ]; #Endif; ! TARGET_GLULX #Ifdef TARGET_ZCODE; [ ZZInitialise; standard_interpreter = HDR_TERPSTANDARD-->0; transcript_mode = ((HDR_GAMEFLAGS-->0) & $0001); sys_statusline_flag = ( (HDR_TERPFLAGS->0) & $0002 ) / 2; top_object = #largest_object-255; dict_start = HDR_DICTIONARY-->0; dict_entry_size = dict_start->(dict_start->0 + 1); dict_start = dict_start + dict_start->0 + 4; dict_end = dict_start + (dict_start - 2)-->0 * dict_entry_size; #Ifdef DEBUG; if (dict_start > 0 && dict_end < 0 && ((-dict_start) - dict_end) % dict_entry_size == 0) print "** Warning: grammar properties might not work correctly **^"; #Endif; ! DEBUG buffer->0 = INPUT_BUFFER_LEN - WORDSIZE; buffer2->0 = INPUT_BUFFER_LEN - WORDSIZE; buffer3->0 = INPUT_BUFFER_LEN - WORDSIZE; parse->0 = MAX_BUFFER_WORDS; parse2->0 = MAX_BUFFER_WORDS; ]; #Ifnot; ! TARGET_GLULX; [ GGInitialise res; @gestalt 4 2 res; ! Test if this interpreter has Glk. if (res == 0) { ! Without Glk, we're entirely screwed. quit; } ! Set the VM's I/O system to be Glk. @setiosys 2 0; ! First, we must go through all the Glk objects that exist, and see ! if we created any of them. One might think this strange, since the ! program has just started running, but remember that the player might ! have just typed "restart". GGRecoverObjects(); res = InitGlkWindow(0); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 0); if (res) return; ! Now, gg_mainwin and gg_storywin might already be set. If not, set them. if (gg_mainwin == 0) { ! Open the story window. res = InitGlkWindow(GG_MAINWIN_ROCK); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_MAINWIN_ROCK); if (res == false) gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK); if (gg_mainwin == 0) { ! If we can't even open one window, there's no point in going on. quit; } } else { ! There was already a story window. We should erase it. glk_window_clear(gg_mainwin); } if (gg_statuswin == 0) { res = InitGlkWindow(GG_STATUSWIN_ROCK); if (res == false) res = LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, GG_STATUSWIN_ROCK); if (res == false) { gg_statuswin_cursize = gg_statuswin_size; gg_statuswin = glk_window_open(gg_mainwin, $12, gg_statuswin_cursize, 4, GG_STATUSWIN_ROCK); } } ! It's possible that the status window couldn't be opened, in which case ! gg_statuswin is now zero. We must allow for that later on. glk_set_window(gg_mainwin); if (InitGlkWindow(1) == false) LibraryExtensions.RunWhile(ext_InitGlkWindow, 0, 1); ]; [ GGRecoverObjects id; ! If GGRecoverObjects() has been called, all these stored IDs are ! invalid, so we start by clearing them all out. ! (In fact, after a restoreundo, some of them may still be good. ! For simplicity, though, we assume the general case.) gg_mainwin = 0; gg_statuswin = 0; gg_quotewin = 0; gg_scriptfref = 0; gg_scriptstr = 0; gg_savestr = 0; gg_statuswin_cursize = 0; #Ifdef DEBUG; gg_commandstr = 0; gg_command_reading = false; #Endif; ! DEBUG ! Also tell the game to clear its object references. if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0); id = glk_stream_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_SAVESTR_ROCK: gg_savestr = id; GG_SCRIPTSTR_ROCK: gg_scriptstr = id; #Ifdef DEBUG; GG_COMMANDWSTR_ROCK: gg_commandstr = id; gg_command_reading = false; GG_COMMANDRSTR_ROCK: gg_commandstr = id; gg_command_reading = true; #Endif; ! DEBUG default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0); } id = glk_stream_iterate(id, gg_arguments); } id = glk_window_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_MAINWIN_ROCK: gg_mainwin = id; GG_STATUSWIN_ROCK: gg_statuswin = id; GG_QUOTEWIN_ROCK: gg_quotewin = id; default: if (IdentifyGlkObject(1, 0, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 0, id, gg_arguments-->0); } id = glk_window_iterate(id, gg_arguments); } id = glk_fileref_iterate(0, gg_arguments); while (id) { switch (gg_arguments-->0) { GG_SCRIPTFREF_ROCK: gg_scriptfref = id; default: if (IdentifyGlkObject(1, 2, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 2, id, gg_arguments-->0); } id = glk_fileref_iterate(id, gg_arguments); } ! Tell the game to tie up any loose ends. if (IdentifyGlkObject(2) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 2); ]; ! This somewhat obfuscated function will print anything. ! It handles strings, functions (with optional arguments), objects, ! object properties (with optional arguments), and dictionary words. ! It does *not* handle plain integers, but you can use ! DecimalNumber or EnglishNumber to handle that case. ! ! Calling: Is equivalent to: ! ------- ---------------- ! PrintAnything() ! PrintAnything(0) ! PrintAnything("string"); print (string) "string"; ! PrintAnything('word') print (address) 'word'; ! PrintAnything(obj) print (name) obj; ! PrintAnything(obj, prop) obj.prop(); ! PrintAnything(obj, prop, args...) obj.prop(args...); ! PrintAnything(func) func(); ! PrintAnything(func, args...) func(args...); [ PrintAnything _vararg_count obj mclass; print_anything_result = 0; if (_vararg_count == 0) return; @copy sp obj; _vararg_count--; if (obj == 0) return; if (obj->0 == $60) { ! Dictionary word. Metaclass() can't catch this case, so we do ! it manually. print (address) obj; return; } mclass = metaclass(obj); switch (mclass) { nothing: return; String: print (string) obj; return; Routine: ! Call the function with all the arguments which are already ! on the stack. @call obj _vararg_count print_anything_result; return; Object: if (_vararg_count == 0) { print (name) obj; } else { ! Push the object back onto the stack, and call the ! veneer routine that handles obj.prop() calls. @copy obj sp; _vararg_count++; @call CA__Pr _vararg_count print_anything_result; } return; } ]; ! This does the same as PrintAnything, but the output is sent to a ! byte array in memory. The first two arguments must be the array ! address and length; the following arguments are interpreted as ! for PrintAnything. The return value is the number of characters ! output. ! If the output is longer than the array length given, the extra ! characters are discarded, so the array does not overflow. ! (However, the return value is the total length of the output, ! including discarded characters.) [ PrintAnyToArray _vararg_count arr arrlen str oldstr len; @copy sp arr; @copy sp arrlen; _vararg_count = _vararg_count - 2; oldstr = glk_stream_get_current(); ! stream_get_current str = glk_stream_open_memory(arr, arrlen, 1, 0); if (str == 0) return 0; glk_stream_set_current(str); @call PrintAnything _vararg_count 0; glk_stream_set_current(oldstr); @copy $ffffffff sp; @copy str sp; @glk $0044 2 0; @copy sp len; @copy sp 0; return len; ]; ! And this calls PrintAnyToArray on a particular array, jiggering ! the result to be a Glulx C-style ($E0) string. Constant GG_ANYTOSTRING_LEN 66; Array AnyToStrArr -> GG_ANYTOSTRING_LEN+1; [ ChangeAnyToCString _vararg_count ix len; ix = GG_ANYTOSTRING_LEN-2; @copy ix sp; ix = AnyToStrArr+1; @copy ix sp; ix = _vararg_count+2; @call PrintAnyToArray ix len; AnyToStrArr->0 = $E0; if (len >= GG_ANYTOSTRING_LEN) len = GG_ANYTOSTRING_LEN-1; AnyToStrArr->(len+1) = 0; return AnyToStrArr; ]; #Endif; ! TARGET_ ! This is a trivial function which just prints a number, in decimal ! digits. It may be useful as a stub to pass to PrintAnything. [ DecimalNumber num; print num; ]; #Ifndef SHORTNAMEBUF_LEN; ! Can't use 'Default', unfortunately, Constant SHORTNAMEBUF_LEN 160; ! but this is functionally equivalent #Endif; #IfV5; #Ifdef VN_1630; Array StorageForShortName buffer SHORTNAMEBUF_LEN; #Ifnot; Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN; #Endif; ! VN_1630 #Endif; ! V5 #Ifdef TARGET_ZCODE; ! Platform-independent way of printing strings, routines and properties ! to a buffer (defined as length word followed by byte characters). [ PrintToBuffer buf len a b c d e; print_anything_result = 0; @output_stream 3 buf; switch (metaclass(a)) { String: print (string) a; Routine: print_anything_result = a(b, c, d, e); Object,Class: if (b) print_anything_result = PrintOrRun(a, b, true); else print (name) a; } @output_stream -3; if (buf-->0 > len) RunTimeError(14, len, "in PrintToBuffer()"); return buf-->0; ]; #Ifnot; ! TARGET_GLULX [ PrintToBuffer buf len a b c d e; if (b) { if (metaclass(a) == Object && a.#b == WORDSIZE && metaclass(a.b) == String) buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a.b); else buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a, b, c, d, e); } else buf-->0 = PrintAnyToArray(buf+WORDSIZE, len, a); if (buf-->0 > len) buf-->0 = len; return buf-->0; ]; #Endif; ! TARGET_ ! Print contents of buffer (defined as length word followed by byte characters). ! no_break == 1: omit trailing newline. ! set_case == 1: capitalise first letter; ! == 2: capitalise first letter, remainder lower case; ! == 3: all lower case; ! == 4: all upper case. ! centred == 1: add leading spaces. [ PrintFromBuffer buf no_break set_case centred i j k; j = (buf-->0) - 1; if (buf->(j+WORDSIZE) ~= 10 or 13) j++; ! trim any trailing newline if (centred) { k = (ScreenWidth() - j) / 2; if (k>0) spaces k; } for (i=0 : i(WORDSIZE+i); switch (set_case) { 0: break; 1: if (i) set_case = 0; else k = UpperCase(k); 2: if (i) k = LowerCase(k); else k = UpperCase(k); 3: k = LowerCase(k); 4: k = UpperCase(k); } print (char) k; } if (no_break == false) new_line; return j; ]; ! None of the following functions should be called for zcode if the ! output exceeds the size of the buffer. [ StringSize a b c d e; PrintToBuffer(StorageForShortName, 160, a, b, c, d, e); return StorageForShortName-->0; ]; [ PrintCapitalised a b no_break no_caps centred; if (metaclass(a) == Routine or String || b == 0 || metaclass(a.b) == Routine or String) PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, a, b); else if (a.b == NULL) rfalse; else return RunTimeError(2, a, b); if (no_caps == 0 or 1) no_caps = ~~no_caps; PrintFromBuffer(StorageForShortName, no_break, no_caps, centred); return print_anything_result; ]; [ Centre a b; PrintCapitalised(a, b, false, true, true); ]; [ CapitRule str no_caps; if (no_caps) print (string) str; else PrintCapitalised(str,0,true); ]; [ PrefaceByArticle o acode pluralise capitalise i artform findout artval; if (o provides articles) { artval=(o.&articles)-->(acode+short_name_case*LanguageCases); if (capitalise) print (CapitRule) artval; else print (string) artval; if (pluralise) return; print (PSN__) o; return; } i = GetGNAOfObject(o); if (pluralise) { if (i < 3 || (i >= 6 && i < 9)) i = i + 3; } i = LanguageGNAsToArticles-->i; artform = LanguageArticles + 3*WORDSIZE*LanguageContractionForms*(short_name_case + i*LanguageCases); #Iftrue (LanguageContractionForms == 2); if (artform-->acode ~= artform-->(acode+3)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms == 3); if (artform-->acode ~= artform-->(acode+3)) findout = true; if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms == 4); if (artform-->acode ~= artform-->(acode+3)) findout = true; if (artform-->(acode+3) ~= artform-->(acode+6)) findout = true; if (artform-->(acode+6) ~= artform-->(acode+9)) findout = true; #Endif; ! LanguageContractionForms #Iftrue (LanguageContractionForms > 4); findout = true; #Endif; ! LanguageContractionForms #Ifdef TARGET_ZCODE; if (standard_interpreter && findout) { StorageForShortName-->0 = SHORTNAMEBUF_LEN; @output_stream 3 StorageForShortName; if (pluralise) print (number) pluralise; else print (PSN__) o; @output_stream -3; acode = acode + 3*LanguageContraction(StorageForShortName + 2); } #Ifnot; ! TARGET_GLULX if (findout) { if (pluralise) PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, EnglishNumber, pluralise); else PrintAnyToArray(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); acode = acode + 3*LanguageContraction(StorageForShortName); } #Endif; ! TARGET_ CapitRule (artform-->acode, ~~capitalise); ! print article if (pluralise) return; print (PSN__) o; ]; [ PSN__ o; if (o == 0) { print (string) NOTHING__TX; rtrue; } switch (metaclass(o)) { Routine: print ""; rtrue; String: print ""; rtrue; nothing: print ""; rtrue; } #Ifdef LanguagePrintShortName; if (LanguagePrintShortName(o)) rtrue; #Endif; ! LanguagePrintShortName if (indef_mode && o.&short_name_indef ~= 0 && PrintOrRun(o, short_name_indef, 1) ~= 0) rtrue; if (o.&short_name ~= 0 && PrintOrRun(o, short_name, 1) ~= 0) rtrue; print (object) o; ]; [ Indefart o saveIndef; saveIndef = indef_mode; indef_mode = true; caps_mode = false; if (o has proper) { indef_mode = NULL; print (PSN__) o; indef_mode = saveIndef; return; } if (o provides article) { PrintOrRun(o, article, 1); print " ", (PSN__) o; indef_mode = saveIndef; return; } PrefaceByArticle(o, 2); indef_mode = saveIndef; ]; [ CInDefArt o saveIndef saveCaps; saveIndef = indef_mode; indef_mode = true; saveCaps = caps_mode; caps_mode = true; if (o has proper) { indef_mode = NULL; PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); PrintFromBuffer(StorageForShortName, true, caps_mode); caps_mode = saveCaps; return; } if (o provides article) { PrintCapitalised(o, article, true); print " ", (PSN__) o; indef_mode = saveIndef; caps_mode = saveCaps; return; } PrefaceByArticle(o, 2, 0, 1); caps_mode = saveCaps; indef_mode = saveIndef; ]; [ Defart o saveIndef; saveIndef = indef_mode; indef_mode = false; caps_mode = false; if ((~~o ofclass Object) || o has proper) { indef_mode = NULL; if (o == player) { if (player provides narrative_voice) { switch (player.narrative_voice) { 1: print (string) MYSELF__TX; 2: print (string) YOURSELF__TX; 3: print (PSN__) o; default: RunTimeError(16, player.narrative_voice); } } else ThatOrThose(player); } else { print (PSN__) o; } indef_mode = saveIndef; return; } PrefaceByArticle(o, 1); indef_mode = saveIndef; ]; [ CDefart o saveIndef saveCaps; saveIndef = indef_mode; indef_mode = false; saveCaps = caps_mode; caps_mode = true; if (~~o ofclass Object) { indef_mode = NULL; print (PSN__) o; } else if (o has proper) { indef_mode = NULL; PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, PSN__, o); PrintFromBuffer(StorageForShortName, true, caps_mode); } else PrefaceByArticle(o, 0); indef_mode = saveIndef; caps_mode = saveCaps; ]; [ PrintShortName o saveIndef; saveIndef = indef_mode; indef_mode = NULL; PSN__(o); indef_mode = saveIndef; ]; [ EnglishNumber n; LanguageNumber(n); ]; [ SerialComma n; #Ifdef SERIAL_COMMAS; if (n>2) print ","; #Endif; n=0; ! quell unused n variable warning ]; [ NumberWord o i n; n = LanguageNumbers-->0; for (i=1 : i<=n : i=i+2) if (o == LanguageNumbers-->i) return LanguageNumbers-->(i+1); return 0; ]; [ RandomEntry tab; if (tab-->0 == 0) return RunTimeError(8); return tab-->(random(tab-->0)); ]; ! ---------------------------------------------------------------------------- ! Useful routine: unsigned comparison (for addresses in Z-machine) ! Returns 1 if x>y, 0 if x=y, -1 if x= 0) return 1; if (x >= 0 && y < 0) return -1; u = x&~WORD_HIGHBIT; v= y&~WORD_HIGHBIT; if (u > v) return 1; return -1; ]; ! ============================================================================== #Ifdef NITFOL_HOOKS; ! Code contributed by Evin Robertson #Ifdef TARGET_GLULX; ! Might be nice for Z-machine games too, ! but I'm not going to try to make this work ! given #Ifdef funniness. Array magic_array --> ! This is so nitfol can do typo correction / ! automapping / debugging on Glulx games $6e66726d $4d616763 $ff0010 ! Goes to 'NfrmMagc' 10 refers to length Magic_Global_Dispatch__ DI__check_word ! DI__check_word(buf, length) PrintShortName WV__Pr RV__Pr CA__Pr ! obj.prop = x; x = obj.prop; obj.prop(x) RA__Pr RL__Pr RA__Sc ! obj.∝ obj.#prop; class::prop OP__Pr OC__Cl ! obj provides prop; obj ofclass class OB__Move OB__Remove OB__Parent__ OB__Child__ OB__Sibling__ ! No explicit veneer for these ; [ OB__Parent__ obj; return parent(obj); ]; [ OB__Child__ obj; return child(obj); ]; [ OB__Sibling__ obj; return sibling(obj); ]; [ Magic_Global_Dispatch__ glbl; switch (glbl) { 0: if (location == TheDark) return real_location; return location; 1: return Player; -1: return CompassDirection::number; ! Silliness to make exist RA__Sc ! Should never be called. } return magic_array; ! Silences a warning. ]; [ DI__check_word buf wlen ix val res dictlen entrylen; ! Just like in Tokenise__. In fact, Tokenise__ could call this if ! it wanted, instead of doing this itself. if (wlen > DICT_WORD_SIZE) wlen = DICT_WORD_SIZE; for (ix=0 : ixix = glk_char_to_lower(buf->ix); } for (: ixix = 0; } val = #dictionary_table + WORDSIZE; entrylen = DICT_WORD_SIZE + 7; @binarysearch gg_tokenbuf DICT_WORD_SIZE val entrylen dictlen 1 1 res; return res; ]; #Endif; ! TARGET_ #Endif; ! NITFOL_HOOKS ! ============================================================================== Object LibraryExtensions "(Library Extensions)" with RunAll [ prop a1 a2 a3 obj rval max; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval > max) max = rval; if (self.BetweenCalls) self.BetweenCalls(); } return max; ], RunUntil [ prop exitval a1 a2 a3 obj rval; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval == exitval) return rval; if (self.BetweenCalls) self.BetweenCalls(); } return ~~exitval; ], RunWhile [ prop exitval a1 a2 a3 obj rval; objectloop (obj in self) if (obj provides prop && obj.prop ofclass Routine) { rval = obj.prop(a1, a2, a3); if (rval ~= exitval) return rval; if (self.BetweenCalls) self.BetweenCalls(); } return exitval; ], ext_number_1 0, ! general temporary workspace ! can be set to a function (e.g. RestoreWN) meant for execution ! after non-terminating calls to extension objects ! (via RunUntil/While/All) BetweenCalls 0, RestoreWN [; wn = self.ext_number_1; ], ! Special interception points ext_messages 0, ! Called if LibraryMessages.before() ! returns false ! Extensions run while they return false ! Cross-platform entry points ! Called/Runs ext_afterlife 0, ! [C2/R1] ext_afterprompt 0, ! [C2/R1] ext_amusing 0, ! [C2/R1] ext_beforeparsing 0, ! [C2/R2] ext_chooseobjects 0, ! [C2/R2] ext_darktodark 0, ! [C2/R1] ext_deathmessage 0, ! [C2/R1] ext_gamepostroutine 0, ! [C2/R2] ext_gamepreroutine 0, ! [C2/R2] ext_initialise 0, ! [C1/R1] ext_inscope 0, ! [C2/R2] ext_lookroutine 0, ! [C2/R1] ext_newroom 0, ! [C2/R1] ext_objectdoesnotfit 0, ! [C2/R2] ext_parsenoun 0, ! [C3/R3] ext_parsenumber 0, ! [C2/R2] ext_parsererror 0, ! [C2/R2] ext_printrank 0, ! [C2/R1] ext_printtaskname 0, ! [C2/R1] ext_printverb 0, ! [C2/R2] ext_timepasses 0, ! [C2/R1] ext_unknownverb 0, ! [C2/R2] ! [C1] = Called in all cases ! [C2] = Called if EP is undefined, or returns false ! [C3] = called if EP is undefined, or returns -1 ! [R1] = All extensions run ! [R2] = Extensions run while they return false ! [R3] = Extensions run while they return -1 #Ifdef TARGET_GLULX; ! Glulx entry points ! Called: Runs: ext_handleglkevent 0, ! if EP undefined while extensions return false ext_identifyglkobject 0, ! if EP undefined while extensions return false ext_initglkwindow 0, ! if EP undefined while extensions return false #Endif; ! TARGET_GLULX; has proper; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_PARSER; #Ifnot; Message "Warning: 'parser' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; ! ============================================================================== The Inform Standard Library =========================== The standard header files are stored in lower-case here. Symbolic links to other semi-standard names get created upon installation (e.g. verblib.h gets symlinked to Verblib.h and VerbLib.h) -- except on Cygwin, where case doesn't seem to matter. Version 6.12.2 (11 June 2018) ============================= This is a maintenance release focusing almost entirely on bug fixes. A minor backport from I7's PArser.i6t has been added. Bugs fixed ---------- * Removed last vestiges of modules. * Fixed problem with invalid wordnum in Refers and NextWord. * Moved some responses to verbs into the language file. * Added notes about language-customized code for banner, version, and error messages. * Fixed tenses problems in CTheyreorThats(). * Fixed problem with ImplicitOpen() calling after routines on wrong object. * Backported Glulx 16-bit verb numbers from I7's Parser.i6t * Patched up infix.h with 16-bit Glulx verbnums, even though Glulx doesn't support Infix. Maybe someday Glulx will support it. Version 6.12.1 (7 June 2016) ============================ This is a maintenance release focusing entirely on bug fixes. No new features have been added. Bugs fixed ---------- * TAKE ROCKS, TAKE ALL, TAKE ROCKS mistakenly tried to take things from an NPC if one was in the room. * DM4 Exercise 32 failure corrected. Also fixed a problem that caused Glulx to get stuck in a loop. * L__M(##Give, 2) wasn't correctly parameterized for voices and tenses. * Improved some nonsensical responses to DROP. * Corrected ordering NPC to take and drop multiple objects. * Corrected problems with DropSub and ImplicitTake. * Faulty OOPS correction partially fixed. Minor changes ------------- * Removed all references to Github. This project will no longer be chained to any particular Git page provider. Version 6.12.0 (19 December 2015) ================================= This release of the Inform Library marks the first release after custody was taken over by David Griffith. It is now housed at https://github.com/DavidGriffith/inform6lib. This release focuses on bugs reported at http://www.inform-fiction.org/patches/library.html. A few significant enhancements have been added as well, mainly having to do with an abortive effort to produce Inform 6.40. An old repository of that can be found at https://github.com/DavidGriffith/inform-2006. New features ------------ First-person and third-person narrative voices are natively supported. Default mode is now VERBOSE. Added infglk.h for more convenient programming for the Glulx virtual machine. There is now a Global "no_infer_message" which can be used in the ChooseObjects() routine to suppress an inference message for particular match. This global is reset to false after the turn is complete. There is now a Global "no_implicit_actions" which can be used to tell the library not attempt to do things that implicitly must be done. For example, the PC is holding a sack containing an apple and the command "DROP APPLE" is typed. With no_implicit_actions set to true, the Library will complain instead of taking the apple out of the sack before dropping it. Once this global is set, it stays set. If you add "Constant NO_INITIAL_LOOK;" to the beginning of your code, the library will not do an initial LOOK at the beginning of your program. There is now an optional Epilogue() function. This will execute when the game ends. TAKE ALL has been modified such that scenery or animate objects will not be taken. To revert back to traditional behavior, add "Constant TRADITIONAL_TAKE_ALL;" at the beginning of your program. If you want to use color in your game, you must add "Constant COLOUR;" or "Constant COLOR;" to the beginnning of your code. This was required to fix L61105. To deal with L61126, a new global has been introduced parser_inflection_func. Whenever parser_inflection is set as a function, parser_inflection_func must be set to true. When the parser is done with it, parser_inflection_func is set back to false. Under the Z-machine, it is possible to tell if a global is a common property or a function. This is not so with the Glulx VM. This change goes for both Z-machine and Glulx. For background on how one might use parser_inflection, see Section 35 of the DM4. Bugs fixed ---------- Items of the form "Issue L61036" quote the bug’s reference number in the 'Library' section of the Inform Patch List at http://inform-fiction.org/patches/library.html * WAVE AT has been improved. * Handling of ambiguous orders given to NPCs has been improved. * Fixed a problem with misparsing caused by incomplete orders. Issue L61101 each_turn property causes runtime error. Problem: An each_turn property with both a local routine and a routine inherited from a Class causes a runtime error in Strict mode. Status: Fixed Issue L61102 GET IN now matches Compass object. Adding "in_obj.&name-->0 = '.ignore';" to Initialise() reverts back to the previous behavior. Status: Fixed Issue L61103 "statusline time;" statement isn't recognized. Problem: When I compile Greystone with 6.30 and 6/11 my statusline time; statement is seemingly ignored; the game runs by moves and not a clock. If I revert back to 6.21 and 6/10 the statusline is indeed a clock again and not a move counter. Status: Unable to reprodu Issue L61104 ListMaker doesn't support 'serial' commas. Problem: The WriteListFrom() listmaker doesn't support 'serial' commas (aka Oxford or Harvard commas): Tom, Dick, and Harry. Status: Fixed Issue L61105 'Game uses colour' bit is always set. Problem: Every game compiled with the 6/11 library has the 'game uses colour' bit set in the Flags2 header word. Fixed: From now on, if you want a game to use color, add "Constant COLOUR;" or "Constant COLOR;" to the beginning of your code. Status: Fixed Issue L61106 Improvement to LibraryExtensions.RunUntil. Problem: The LibraryExtensions.RunUntil property (new at 6/11 and not currently used by the library) should return simply true or false if it does nothing. Status: Fixed Issue L61107 (The) with 'proper' should capitalise object name. Problem: In the case of an object with the 'proper' attribute and a lower-case name (such as "your nose", "your corduroy trousers", "your mother's purse"), the (The) print rule should capitalise the first letter of the object name, so that library messages such as (The) x1, " ", (isorare) x1, " empty." correctly produce "Your mother's purse is empty." Status: Fixed Issue L61108 indef_mode not restored. Problem: When printing an object with the proper attribute, the functions IndefArt() and CIndefArt() temporarily modify -- but do not restore -- the value of the global variable indef_mode. Status: Fixed Issue L61109 Problem with 'Give reverse' grammar. Status: Fixed Issue L61110 Inference message inconsistency. Problem: In a pile of several indistinguishable objects, taking them from the floor does not generate an (inference) message, but it does when the final one is taken. (See also Suggestion 48) Status: Fixed Issue L61111 Multiple AGAINs treated as one. Status: Fixed Issue L61112 WITHOUT_DIRECTIONS causes compilation error. Problem: Version 6/11 of the Inform Library fails to compile if the constant WITHOUT_DIRECTIONS is set and the objects 'u_obj' and 'd_obj' aren't defined, because a few library routines expect those objects to exist. Status: Fixed Issue L61113 Size of upper window not restored properly on UNDO. Problem: Compile and run a trivial game with Nitfol. When the game begins, type WAIT and then UNDO. Nitfol displays the message [ERROR: output]: illegal line for set_cursor (1) 46968 (1,1) This happens in DrawStatusLine() and the reason is that the upper window has height 0, but the Library tries to position the cursor at (1,1). Comment: I couldn't get Nitfol to complain like this, but applied the fix anyhow. Issue L61114 Numbers in the name property. Problem: Code such as this would cause "GET 1" to not match the box Object -> box1 "box marked 1" with name 'box' 'marked' '1//', description "It's a wooden box marked with the number 1."; Object -> box2 "box marked 2" with name 'box' 'marked' '2//', description "It's a wooden box marked with the number 2."; Status: Fixed Issue L61115 'multiheld' can match unholdable objects. Problem: Contrary to the DM4, multiheld sometimes matches objects that are not held. This would be OK if the objects were then implicitly taken, like they are for held, but they are not. Status: Fixed Issue L61116 Poor response from WAVE SELF. Problem: The message produced by WAVE SELF -- "But you aren't holding you" -- makes little sense. Status: Fixed Issue L61117 Problem with statements in Infix. Status: Fixed Issue L61118 'thedark.initial' is never called. Problem: The library thoughtfully provides thedark.initial, but it is never called unless you are diabolical enough to make thedark contained by some location, which I'm sure is not what it was meant for. The DM is a bit contradictory about the purpose of thedark.initial, but the functionality that makes the most sense is that it is called at the transition from lighted to darkened. This makes up a gap in functionality: NewRoom() is called on light-to-light and dark-to-light; DarkToDark() is called on dark-to-dark, but absolutely nothing is called on light-to-dark. Status: Fixed Issue L61119 TRACE should distinguish matched and inferred token. Problem: When the parser partially matches a phrase, the TRACE command should not say "token resulted in success" for terms that it did not match but sucessfully inferred; instead it should state that those terms were inferred. This would avoid the phrase "token resulted in success" phrase meaning two different things -- actually matching and inferring. Status: Won't fix. Maybe will fix in 6/13. Issue L61120 Preposition parsing is too simplistic. Status: Fixed (by way of L61127) Issue L61121 add_to_scope of parentless object causes error. Problem: Consider an object which has no parent, and is brought into scope by an add_to_scope property. An attempt to take that object causes error messag [** Programming error: tried to test "has" or "hasnt" of nothing **] [** Programming error: tried to test "has" or "hasnt" of nothing **] That's hardly portable. Status: Fixed Issue L61122 Conflict between 'describe' and 'initial' properties. Problem: This object displays its 'initial' message even though it has 'moved' attribute; this is because of the presence of the 'describe' property, even though it returns false. Status: Fixed Issue L61123 Minor problem with parse_name. Problem: A (rather minor) error with the parse_name routine. On page 209, the DM4 states: ... Status: Fixed Issue L61124 Spurious space with 'articles' property. Problem: The rarely-used articles property defines an array of strings. (The property is provided for non-English languages where irregular nouns may have unusual vowel-contraction rules with articles.) The DM4 gives an example appropriate for a French game, with three strings in the array Object "haricot" with name 'haricot' 'legume', articles "Le " "le " "un ", ... ; Note that each string includes its individual trailing space, if appropriate. This is important, because a definite article like l' must be followed immediately by the object's name, without any intervening space. However, in fact a space does appear. Status: Fixed Issue L61125 match_list and match_scores over-run. Problem: The problem is that match_list-->number_matched is being accessed, when match_list has length only number_matched (that is, entries 0..number_matched-1). In particular this causes errors when th match_list is of full length (64 entries). Similarly for match_scores. Status: Fixed Issue L61126 parser_inflection requires common properties in Glulx. Problem: Glulx cannot distinguish between a global that is a function or a common property. They must be addressed differently. Code has been introduced to require the author to explicity declare if parser_inflection is a function or a common property. Status: Fixed Issue L61127 Improve multiexcept look-ahead. Problem: When the parser processes a grammar line that uses multiexcept or multiinside, it jumps ahead to match the second noun in order to provide context for the first one. However, in doing so, it skips over all the prepositions in the input, without caring whether they match the prepositions in the grammar line. If the second noun is ambiguous, this means the player may be asked a disambiguation question for a grammar line that has no chance of succeeding, whereas the grammar line that eventually succeeds might not even need disambiguation (thanks to different token type or ChooseObjects). This also fixes L61120 Status: Fixed I have also applied a fix submitted by Nathan Schwartzman at http://inform7.com/mantis/view.php?id=636. Issue L61128 OOPS sometimes changes wrong word. Problem: The OOPS command doesn't necessarily change the faulty word. In the examples below, 'ZZZ' should be corrected to 'RUBY'. This happens in the first example, but not the second. Status: Fixed Issue L61129 Results from 'grammar' property are misplaced. Problem: An animate or talkable object's grammar property can return 1 to mean (quoting from DM4) "you can stop parsing now because I have done it all, and put the resulting order into the variables action, noun and second". However, the library code to handle this return value does not work correctly. Status: Fixed Version 6/11 (27 February 2004) =============================== New features ------------ * The library automatically defines four constants: LIBRARY_PARSER at the end of Parser.h, LIBRARY_VERBLIB at the end of VerbLib.h, LIBRARY_GRAMMAR at the end of Grammar.h, and LIBRARY_ENGLISH at the end of English.h. Contributed library extensions can use these constants to check that they have been Included in the correct location. A fifth constant LIBRARY_VERSION, currently defined as the number 611, can be checked by extensions which require this particular version of the library. * The word "wall" has been removed from the CompassDirection objects defined in English.h, whose names are now simply "north", "south", etc. * The verbs LOOK [TO THE] NORTH, LOOK DOWN, LOOK OUT[SIDE] etc -- but not LOOK IN[SIDE], which is already available -- have been added. By default, the response is of the form "You see nothing unexpected...", but you can change this for individual directions in individual rooms by providing a compass_look property Room study "Your study" with description "There is a doorway to the east of this austere room.", compass_look [ obj; if (obj == e_obj) "You see a doorway."; if (obj == n_obj or s_obj or w_obj) "You see the wall."; ], e_to hallway; This enhancement uses the mechanism described in this topic in the Inform 6 FAQ (http://www.firthworks.com/roger/informfaq/ww.html#1 How can I get rid of those damn walls?) (except that the compass_look property was previously named compasslook), and means that you no longer need to make the library changes described ther * The verbs "ASK npc TO command" and "TELL npc TO command" -- both synomymous with "npc,command" -- are provided. The new grammar is: Verb 'ask' ... * creature 'to' topic -> AskTo ... in which the creature token matches the npc and the topic token represents the command. AskTo isn’t an action in the usual sense: it's trapped by the parser and converted to the original npc,command format. The npc can intercept the command by providing an orders property in the usual way -- see Section 18 of the Inform Designer’s Manual. This enhancement means that you may no longer require Irene Callaci's AskTellOrder.h library extension. * The verbs RECORDING [ON|OFF] and REPLAY are now always available, irrespective of the DEBUG state. This may cause compilation errors if you have already defined these verbs yourself. * The verbs PRY, PRISE, PRIZE and LEVER have been added. This may cause compilation errors if you have already defined these verbs yourself. * The parser treats input lines beginning with “*” as a comment, without attempting any further parsing. The character used to introduce comments can be changed by defining COMMENT_CHARACTER before you "Include Parser;". For example: Constant COMMENT_CHARACTER '!'; Since comments are used primarily when capturing a transcript -- either of a complete game (SCRIPT ON) or of input commands only (RECORDING ON) -- the parser responds "[Comment recorded]" or "[Comment NOT recorded]" as appropriate. * The selfobj object now includes an empty add_to_scope property, which you can over-ride with your own routine, typically to equip the player with body parts. For a single object: selfobj.add_to_scope = nose; or for multiple object [ IncludeBodyParts; PlaceInScope(nose); PlaceInScope(hands); ]; selfobj.add_to_scope = IncludeBodyParts; * The task-based scoring system (§22 of the Inform Designer’s Manual) uses a byte array, which precludes the awarding of large or negative scores. To get round this, you can Replace the TaskScore() library routine as follows, and then define task_scores as a word array: Replace TaskScore; Array task_scores --> 100 200 300 400 (-50) 600; [ TaskScore i; return task_scores-->i; ]; * The scoring system is completely disabled if you define a constant NO_SCORE near the start of your game. Constant NO_SCORE; * A new before_implicit property is available; at the moment this is used only by the parser, when it is about to perform an implicit TAKE (for example, EAT APPLE when you're not holding the apple). You can give this property to an object if you wish to control the parser's behaviour. The property's value should be a constant or a routine which returns: 0 to report "(first taking the...)" and then attempt to do so (this is what currently happens); 1 to attempt the TAKE without first issuing the message, 2 to proceed with the requested action without attempting the TAKE, or 3 to object that "You aren’t holding that!". The object can test action_to_be to determine which action has triggered the TAKE before_implicit [; Take: if (action_to_be == ##Eat) return 2; ], * A new system variable sys_statusline_flag is set to 1 initially if you have used the statusline time; directive in your program to show a clock, and to 0 otherwise. It can be changed by the program. * An object's invent property -- if it has one -- is now invoked both when displaying the player’s inventory and when including the object in a room description. invent is invoked in the usual way (with inventory_stage first set to 1, and then set to 2) both when mentioning the object in a room description, and when listing it in the player's inventory. By default you’ll get the same output each time. If you need to distinguish between the two occasions, you can test (c_style&PARTINV_BIT) -- true during a room description -- or (c_style&FULLINV_BIT) — true during an inventory. Here’s an example: Object -> "sack" with name 'sack', invent [; ! When listing objects in the player's inventory if (c_style&FULLINV_BIT) rfalse; ! When listing objects at the end of a room description if (inventory_stage == 1) switch (children(self)) { 0: print "an empty sack"; 1: print "a sack containing ", (a) child(self); default: print "an assortment of objects in a sack"; } rtrue; ], has container open; This enhancement uses the mechanism described in http://www.firthworks.com/roger/informfaq/ww.html#4 in the Inform 6 FAQ (Can I avoid printing "(which is empty)" after a container?) and means that you no longer need to Include WriteList. * The turns counter is now initialised to 0, not 1. You can change this if you define a constant START_MOVE near the start of your game. Constant START_MOVE 1; * A new LibraryExtensions object is defined, whose function is to act as a parent to initialisation objects created by library extensions. These objects may provide ext_initialise and/or ext_messages property routines, whose role is to help integrate the extension into a game. This is best explained by example. Consider the SmartCantGo.h extension, which replaces "You can't go that way" messages by the more informative "You can go only north, south and east", and can be integrated into a game by adding a ChangeDefault(cant_go, SmartCantGo) statement to your Initialise() routine. Instead of requiring the author to make this addition, the extension could now cause it to happen automatically by defining an initialisation object as a child of LibraryExtensions, like this: Object "(SmartCantGo)" LibraryExtensions with ext_initialise [; ChangeDefault(cant_go, SmartCantGo); ]; Just before calling the game's Initialise() routine, the library loops through the children -- if any -- of LibraryExtensions, and executes those ext_initialise properties that it finds there. The property routines can perform any appropriate setup processing that would otherwise have to be inserted into the Initialise() routine itself; for example, starting a daemon running. A similar process takes place when displaying library messages. The library first checks whether the author has provided a LibraryMessages object to intercept the message which it is about to display. If not, it now loops through the children of LibraryExtensions, and executes ext_messages properties that it finds there. If none of those routines returns true to signal that the message has been dealt with, the standard library text is then printed in the usual way. For example, here’s how an extension might automatically intercept Inventory messages (unless the game has already handled them via LibraryMessages): Object "(someInventoryExtension)" LibraryExtensions with ext_messages [; Inv: switch(lm_n) { 1: "You are empty-handed."; 2: "Your possessions include"; } ]; Note that this is an experimental feature, and may be modified or extended in the light of experience. Bugs fixed ---------- Items of the form [L61036] quote the bug's reference number in the 'Library' section of the Inform Patch List at http://inform-fiction.org/patches/library.html * A command like EMPTY ME no longer replies "yourself can't contain things". [L61036] * The commands TAKE ALL FROM X and REMOVE ALL FROM X, where X is a closed or empty container, now produce sensible messages rather than "You can’t see any such thing" and "You can’t use multiple objects with that verb" respectively. [L61035] * A problem with the misbehaviour of name properties on rooms, in conjunction with THE, has been corrected. [L61034] * The command PUT X INTO X now correctly produces "You can’t put something inside itself", rather than "You can’t see any such thing". [L61033] * Run-time errors resulting from IndirectlyContains() attempting to find the parent of a Class which supports dynamic creation of objects have been resolved. [L61032] * Code in Parser__parse() which deals with looking ahead to the indirect object in cases like PUT ALL INTO BAG (a MULTIEXCEPT token) and TAKE ALL FROM BAG (a MULTIINSIDE token) now correctly sets the advance_warning global (to BAG). [L61031, L61023] * The Inform Designer's Manual (p. 98) states that SHOWOBJ should accept an object number; now it does. [L61030] * The YesOrNo() routine now re-prompts correctly after garbage input. [L61029] * The parse buffer is no longer declared and initialised incorrectly (albeit harmlessly). [L61028, L60708] * The Inform Designer's Manual (p. 93) defines the calling order of routines and properties for the 'Before' stage as follow 1. GamePreRoutine() 2. orders of the player 3. react_before of every object in scope 4. before of the current room 5. before of the first noun, if specified In the library, however, steps 3 and 4 are executed in reverse order. They are now as documented. [L61027] * A found_in floating object which the player is able to take (probably due to a coding error) is no longer silently dropped when the player returns to one of the listed rooms. [L61026] * A small problem with inherited describe properties has been corrected. [L61025] * Standard screen-handling is now implemented in v6 games. [L61022] * The handling of "You can't go that way" messages is made consistent. Also, the statement ChangeDefault(cant_go,myRoutine); now works. [L61020] * Attempting to place an object in/on an object where it is already now results in "It’s already there", rather than "You need to be holding it before you can put it into something else". [L61019] * A problem with misleading inventory listing has been clarified. [L61018] * The command LEAVE X now correctly produces "But you aren't in/on the X", if appropriate. [L61017] * The response to READ was inappropriate when an object is misspelled or out of scope. [L61016] * A small bug in the choice of library messages for PUSH and TURN, which wasn’t noticeable unless you overrode the messages to be different from PULL, has been corrected. [L61015] * If you are in a dark room, you cannot examine what you are holding. Yet if you open a container you brought in from a lit room, the standard message "You open the box, revealing a..." was not being suppressed. [L61014] * The ScoreMatchL() routine in Parserm.h incorrectly decided which objects meet descriptors. As a result, some objects that didn't meet descriptors were not properly removed from the match list when the library is deciding which objects best match a player’s input. [L61013] * The Infix problem parsing commands containing commas and periods has been fixed. [L61010] * A problem when describing what's visible after opening a container has been corrected. [L61009] * An inappropriate message after GO NORTH CIRCULAR has been corrected. [L61008] * Modified foreground and background colours are now correct after RESTORE and UNDO. [L61007] * The grammar property now works with a large game whose dictionary lies above $8000. [L61006] * A buffer conflict with disambiguation and UNDO has been resolved. [L61004] * If a player is inside a closed, non-transparent container, the library prints an extra blank line between the header "The container" and the first inside_description line it prints. No more. [L61002] * The list writing routines do not handle plural containers correctly. If you have two empty boxes, it might list "two boxes (which is closed)". Not only should it say "are closed", but it will lump empty containers together even if some are open and others aren't. Now resolved. [L61001] * A conflict between DrawStatusLine() and DisplayStatus() on how to determine whether to display turns or time is settled in favour of checking a header flag. [L60709] ! ============================================================================== ! VERBLIB: Front end to standard verbs library. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ============================================================================== System_file; #Ifdef LIBRARY_STAGE; #Iffalse LIBRARY_STAGE >= AFTER_VERBLIB; ! if not already included #Iftrue LIBRARY_STAGE == AFTER_PARSER; ! if okay to include it ! ------------------------------------------------------------------------------ Default AMUSING_PROVIDED 1; Default MAX_CARRIED 100; Default MAX_SCORE 0; Default NUMBER_TASKS 1; Default OBJECT_SCORE 4; Default ROOM_SCORE 5; Default SACK_OBJECT 0; Default TASKS_PROVIDED 1; #Ifndef task_scores; Array task_scores -> 0 0 0 0; #Endif; Array task_done -> NUMBER_TASKS; #Ifndef LibraryMessages; Object LibraryMessages; #Endif; #Ifndef NO_PLACES; [ ObjectsSub; Objects1Sub(); ]; [ PlacesSub; Places1Sub(); ]; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ ! Banner(), VersionSub(), and RunTimeError() are preempted by LanguageBanner(), ! LanguageVersionSub(), and LanguageError() respectfully. When converting ! this library to support a different natural language, these three latter ! functions should be created rather than editing this file. ! ------------------------------------------------------------------------------ [ Banner i; #Ifdef LanguageBanner; LanguageBanner(); i = 0; ! suppress warning #Ifnot; if (Story) { #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; print "^", (string) Story; #IfV5; style roman; #Endif; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Header); print "^", (string) Story; glk_set_style(style_Normal); #Endif; ! TARGET_ } if (Headline) print (string) Headline; #Ifdef TARGET_ZCODE; print "Release ", (HDR_GAMERELEASE-->0) & $03ff, " / Serial number "; for (i=0 : i<6 : i++) print (char) HDR_GAMESERIAL->i; #Ifnot; ! TARGET_GLULX; print "Release "; @aloads ROM_GAMERELEASE 0 i; print i; print " / Serial number "; for (i=0 : i<6 : i++) print (char) ROM_GAMESERIAL->i; #Endif; ! TARGET_ print " / Inform v"; inversion; print " Library v", (string) LibRelease, " "; #Ifdef STRICT_MODE; print "S"; #Endif; ! STRICT_MODE #Ifdef INFIX; print "X"; #Ifnot; #Ifdef DEBUG; print "D"; #Endif; ! DEBUG #Endif; ! INFIX new_line; #Endif; ! LanguageBanner ]; [ VersionSub ix; #Ifdef LanguageVersionSub; LanguageVersionSub(); ix = 0; ! suppress warning #Ifnot; Banner(); #Ifdef TARGET_ZCODE; ix = 0; ! shut up compiler warning if (standard_interpreter > 0) { print "Standard interpreter ", standard_interpreter/256, ".", standard_interpreter%256, " (", HDR_TERPNUMBER->0; #Iftrue (#version_number == 6); print (char) '.', HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print ") / "; } else { print "Interpreter ", HDR_TERPNUMBER->0, " Version "; #Iftrue (#version_number == 6); print HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print " / "; } #Ifnot; ! TARGET_GLULX; @gestalt 1 0 ix; print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; @gestalt 0 0 ix; print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; #Endif; ! TARGET_; print "Library serial number ", (string) LibSerial, "^"; #Ifdef LanguageVersion; print (string) LanguageVersion, "^"; #Endif; ! LanguageVersion #Endif; ! LanguageVersionSub ]; [ RunTimeError n p1 p2; #Ifdef LanguageError; LanguageError(n, p1, p2); #Ifnot; #Ifdef DEBUG; print "** Library error ", n, " (", p1, ", ", p2, ") **^** "; switch (n) { 1: print "preposition not found (this should not occur)"; 2: print "Property value not routine or string: ~", (property) p2, "~ of ~", (name) p1, "~ (", p1, ")"; 3: print "Entry in property list not routine or string: ~", (property) p2, "~ list of ~", (name) p1, "~ (", p1, ")"; 4: print "Too many timers/daemons are active simultaneously. The limit is the library constant MAX_TIMERS (currently ", MAX_TIMERS, ") and should be increased"; 5: print "Object ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 7: print "The object ~", (name) p1, "~ can only be used as a player object if it has the ~number~ property"; 8: print "Attempt to take random entry from an empty table array"; 9: print p1, " is not a valid direction property number"; 10: print "The player-object is outside the object tree"; 11: print "The room ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 12: print "Tried to set a non-existent pronoun using SetPronoun"; 13: print "A 'topic' token can only be followed by a preposition"; 14: print "Overflowed buffer limit of ", p1, " using '@@64output_stream 3' ", (string) p2; 15: print "LoopWithinObject broken because the object ", (name) p1, " was moved while the loop passed through it."; 16: print "Attempt to use illegal narrative_voice of ", p1, "."; default: print "(unexplained)"; } " **"; #Ifnot; "** Library error ", n, " (", p1, ", ", p2, ") **"; #Endif; ! DEBUG #Endif; ! LanguageError ]; ! ---------------------------------------------------------------------------- ! The WriteListFrom routine, a flexible object-lister taking care of ! plurals, inventory information, various formats and so on. This is used ! by everything in the library which ever wants to list anything. ! ! If there were no objects to list, it prints nothing and returns false; ! otherwise it returns true. ! ! o is the object, and style is a bitmap, whose bits are given by: ! ---------------------------------------------------------------------------- Constant NEWLINE_BIT $0001; ! New-line after each entry Constant INDENT_BIT $0002; ! Indent each entry by depth Constant FULLINV_BIT $0004; ! Full inventory information after entry Constant ENGLISH_BIT $0008; ! English sentence style, with commas and and Constant RECURSE_BIT $0010; ! Recurse downwards with usual rules Constant ALWAYS_BIT $0020; ! Always recurse downwards Constant TERSE_BIT $0040; ! More terse English style Constant PARTINV_BIT $0080; ! Only brief inventory information after entry Constant DEFART_BIT $0100; ! Use the definite article in list Constant WORKFLAG_BIT $0200; ! At top level (only), only list objects ! which have the "workflag" attribute Constant ISARE_BIT $0400; ! Print " is" or " are" before list Constant CONCEAL_BIT $0800; ! Omit objects with "concealed" or "scenery": ! if WORKFLAG_BIT also set, then does _not_ ! apply at top level, but does lower down Constant NOARTICLE_BIT $1000; ! Print no articles, definite or not Constant ID_BIT $2000; ! Print object id after each entry [ NextEntry o odepth; for (::) { o = sibling(o); if (o == 0) return 0; if (lt_value && o.list_together ~= lt_value) continue; if (c_style & WORKFLAG_BIT && odepth==0 && o hasnt workflag) continue; if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) continue; return o; } ]; [ WillRecurs o; if (c_style & ALWAYS_BIT) rtrue; if (c_style & RECURSE_BIT == 0) rfalse; if ((o has transparent or supporter) || (o has container && o has open)) rtrue; rfalse; ]; [ ListEqual o1 o2; if (child(o1) && WillRecurs(o1)) rfalse; if (child(o2) && WillRecurs(o2)) rfalse; if (c_style & (FULLINV_BIT + PARTINV_BIT)) { if ((o1 hasnt worn && o2 has worn) || (o2 hasnt worn && o1 has worn)) rfalse; if ((o1 hasnt light && o2 has light) || (o2 hasnt light && o1 has light)) rfalse; if (o1 has container) { if (o2 hasnt container) rfalse; if ((o1 has open && o2 hasnt open) || (o2 has open && o1 hasnt open)) rfalse; } else if (o2 has container) rfalse; } return Identical(o1, o2); ]; [ SortTogether obj value; ! print "Sorting together possessions of ", (object) obj, " by value ", value, "^"; ! for (x=child(obj) : x : x=sibling(x)) ! print (the) x, " no: ", x, " lt: ", x.list_together, "^"; while (child(obj)) { if (child(obj).list_together ~= value) move child(obj) to out_obj; else move child(obj) to in_obj; } while (child(in_obj)) move child(in_obj) to obj; while (child(out_obj)) move child(out_obj) to obj; ]; [ SortOutList obj i k l; ! print "^^Sorting out list from ", (name) obj, "^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; .AP_SOL; for (i=obj : i : i=sibling(i)) { k = i.list_together; if (k ~= 0) { ! print "Scanning ", (name) i, " with lt=", k, "^"; for (i=sibling(i) : i && i.list_together == k :) i = sibling(i); if (i == 0) rfalse; ! print "First not in block is ", (name) i, " with lt=", i.list_together, "^"; for (l=sibling(i) : l : l=sibling(l)) if (l.list_together == k) { SortTogether(parent(obj), k); ! print "^^After ST:^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; obj = child(parent(obj)); jump AP_SOL; } } } ]; #Ifdef TARGET_ZCODE; [ Print__Spaces n; ! To avoid a bug occurring in Inform 6.01 to 6.10 if (n == 0) return; spaces n; ]; #Ifnot; ! TARGET_GLULX; [ Print__Spaces n; while (n > 0) { @streamchar ' '; n = n - 1; } ]; #Endif; ! TARGET_ [ WriteListFrom o style depth s1 s2 s3 s4 s5 s6; if (o == nothing) return 0; s1 = c_style; s2 = lt_value; s3 = listing_together; s4 = listing_size; s5 = wlf_indent; s6 = inventory_stage; if (o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } c_style = style; wlf_indent = 0; if (WriteListR(o, depth) == 0) return 0; c_style = s1; lt_value = s2; listing_together = s3; listing_size = s4; wlf_indent = s5; inventory_stage = s6; rtrue; ]; [ WriteListR o depth stack_pointer classes_p sizes_p i j k k2 l m n q senc mr; if (depth > 0 && o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } for (::) { if (o == 0) rfalse; if (c_style & WORKFLAG_BIT && depth==0 && o hasnt workflag) { o = sibling(o); continue; } if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) { o = sibling(o); continue; } break; } classes_p = match_classes + stack_pointer; sizes_p = match_list + stack_pointer; for (i=o,j=0 : i && (j+stack_pointer)<128 : i=NextEntry(i,depth),j++) { classes_p->j = 0; if (i.plural) k++; } if (c_style & ISARE_BIT) { if (j == 1 && o hasnt pluralname) Tense(IS__TX, WAS__TX); else Tense(ARE__TX, WERE__TX); if (c_style & NEWLINE_BIT) print ":^"; else print (char) ' '; c_style = c_style - ISARE_BIT; } stack_pointer = stack_pointer+j+1; if (k < 2) jump EconomyVersion; ! It takes two to plural n = 1; for (i=o,k=0 : kk == 0) { classes_p->k = n; sizes_p->n = 1; for (l=NextEntry(i,depth),m=k+1 : l && mm == 0 && i.plural && l.plural ~= 0) { if (ListEqual(i, l) == 1) { sizes_p->n = sizes_p->n + 1; classes_p->m = n; } } n++; } n--; for (i=1,j=o,k=0 : i<=n : i++,senc++) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } m = sizes_p->i; if (j == 0) mr = 0; else { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together == mr) senc--; mr = j.list_together; } } senc--; for (i=1,j=o,k=0,mr=0 : senc>=0 : i++,senc--) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { senc++; jump Omit_FL2; } k2 = NextEntry(j, depth); if (k2 == 0 || k2.list_together ~= j.list_together) jump Omit_WL2; k2 = metaclass(j.list_together); if (k2 == Routine or String) { q = j; listing_size = 1; l = k; m = i; while (m < n && q.list_together == j.list_together) { m++; while (((classes_p->l) ~= m) && ((classes_p->l) ~= -m)) { l++; q = NextEntry(q, depth); } if (q.list_together == j.list_together) listing_size++; } ! print " [", listing_size, "] "; if (listing_size == 1) jump Omit_WL2; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k2 == String) { q = 0; for (l=0 : l(l+i); EnglishNumber(q); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k2 ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist2; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k2 == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist2; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL2; } } .Omit_WL2; if (WriteBeforeEntry(j, depth, 0, senc) == 1) jump Omit_FL2; if (sizes_p->i == 1) { if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; } else { if (c_style & DEFART_BIT) PrefaceByArticle(j, 1, sizes_p->i); print (number) sizes_p->i, " "; PrintOrRun(j, plural, 1); } if (sizes_p->i > 1 && j hasnt pluralname) { give j pluralname; WriteAfterEntry(j, depth, stack_pointer); give j ~pluralname; } else { WriteAfterEntry(j,depth,stack_pointer); } .Omit_EL2; if (c_style & ENGLISH_BIT) { if (senc == 1) print (SerialComma) i+senc, (string) AND__TX; if (senc > 1) print (string) COMMA__TX; } .Omit_FL2; } rtrue; .EconomyVersion; n = j; for (i=1,j=o : i<=n : j=NextEntry(j,depth),i++,senc++) { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together==mr) senc--; mr = j.list_together; } for (i=1,j=o,mr=0 : i<=senc : j=NextEntry(j,depth),i++) { if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { i--; jump Omit_FL; } k = NextEntry(j, depth); if (k == 0 || k.list_together ~= j.list_together) jump Omit_WL; k = metaclass(j.list_together); if (k == Routine or String) { if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k == String) { q = j; l = 0; do { q = NextEntry(q, depth); l++; } until (q == 0 || q.list_together ~= j.list_together); EnglishNumber(l); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL; } } .Omit_WL; if (WriteBeforeEntry(j, depth, i, senc) == 1) jump Omit_FL; if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; WriteAfterEntry(j, depth, stack_pointer); .Omit_EL; if (c_style & ENGLISH_BIT) { if (i == senc-1) print (SerialComma) senc, (string) AND__TX; if (i < senc-1) print (string) COMMA__TX; } .Omit_FL; } ]; ! end of WriteListR [ WriteBeforeEntry o depth ipos sentencepos flag; inventory_stage = 1; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (o.invent && (c_style & (PARTINV_BIT|FULLINV_BIT))) { flag = PrintOrRun(o, invent, 1); if (flag) { if (c_style & ENGLISH_BIT) { if (ipos == sentencepos-1) print (SerialComma) sentencepos, (string) AND__TX; if (ipos < sentencepos-1) print (string) COMMA__TX; } if (c_style & NEWLINE_BIT) new_line; } } return flag; ]; [ WriteAfterEntry o depth stack_p p recurse_flag parenth_flag eldest_child child_count combo i j; inventory_stage = 2; if (c_style & PARTINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; combo = 0; if (o has light && location hasnt light) combo=combo+1; if (o has container && o hasnt open) combo=combo+2; if ((o has container && (o has open || o has transparent))) { objectloop(i in o) { if (i hasnt concealed && i hasnt scenery) { j = true; break; } } if (~~j) combo=combo+4; } if (combo) L__M(##ListMiscellany, combo, o); } ! end of PARTINV_BIT processing if (c_style & FULLINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; if (o has light && o has worn) { L__M(##ListMiscellany, 8, o); parenth_flag = true; } else { if (o has light) { L__M(##ListMiscellany, 9, o); parenth_flag = true; } if (o has worn) { L__M(##ListMiscellany, 10, o); parenth_flag = true; } } if (o has container) if (o has openable) { if (parenth_flag) print (string) AND__TX; else L__M(##ListMiscellany, 11, o); if (o has open) if (child(o)) L__M(##ListMiscellany, 12, o); else L__M(##ListMiscellany, 13, o); else if (o has lockable && o has locked) L__M(##ListMiscellany, 15, o); else L__M(##ListMiscellany, 14, o); parenth_flag = true; } else if (child(o)==0 && o has transparent) if (parenth_flag) L__M(##ListMiscellany, 16, o); else L__M(##ListMiscellany, 17, o); if (parenth_flag) print ")"; } ! end of FULLINV_BIT processing if (c_style & CONCEAL_BIT) { child_count = 0; objectloop (p in o) if (p hasnt concealed && p hasnt scenery) { child_count++; eldest_child = p; } } else { child_count = children(o); eldest_child = child(o); } if (child_count && (c_style & ALWAYS_BIT)) { if (c_style & ENGLISH_BIT) L__M(##ListMiscellany, 18, o); recurse_flag = true; } if (child_count && (c_style & RECURSE_BIT)) { if (o has supporter) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 19, o); else L__M(##ListMiscellany, 20, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } if (o has container && (o has open || o has transparent)) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 21, o); else L__M(##ListMiscellany, 22, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } } if (recurse_flag && (c_style & ENGLISH_BIT)) if (child_count > 1 || eldest_child has pluralname) Tense(ARE2__TX, WERE2__TX); else Tense(IS2__TX, WAS2__TX); if (c_style & NEWLINE_BIT) new_line; if (recurse_flag) { o = child(o); #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; lt_value = 0; listing_together = 0; listing_size = 0; WriteListR(o, depth+1, stack_p); #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; if (c_style & TERSE_BIT) print ")"; } ]; ! ---------------------------------------------------------------------------- ! LoopWithinObject(rtn,obj,arg) ! ! rtn is the address of a user-supplied routine. ! obj is an optional parent object whose dependents are to be processed; the ! default is the current actor (normally the player). ! arg is an optional argument passed to the rtn; this can be a single variable ! or constant, or the address of an array (which enables multiple values to be ! passed and returned). ! ! For each object o which is a child, grandchild, great-grandchild, etc, of the ! original obj, LoopWithinObject() calls rtn(o,arg). ! ! The rtn should perform any appropriate testing or processing on each object o, ! using the optional arg value if necessary. If the rtn returns true (or any ! positive value), the children of o, if any, are also tested; those children ! are skipped if rtn returns false. To terminate the loop before all objects ! have been processed, rtn should return a large negative number (eg -99). ! ! To deal with supporters and open containers, so that objects are processed ! only if they are accessible to the player, rtn might end with these ! statements: ! if ((o has transparent or supporter) || (o has container && o has open)) rtrue; ! rfalse; ! or alternatively with: ! c_style = RECURSE_BIT; return WillRecurs(o); ! ! LoopWithinObject() returns the number of objects which have been processed. ! ---------------------------------------------------------------------------- [ LoopWithinObject rtn obj arg n o x y; if (obj == 0) obj = actor; o = child(obj); while (o) { y = parent(o); n++; x = rtn(o, arg); ! user-supplied routine returning x. ! if x < 0: skip up to next parent ! if x = 0: jump across to next sibling ! if x > 0: continue down to child objects if (y ~= parent(o)) { RunTimeError(15, o); rfalse; } if (x > 0 && child(o)) o = child(o); else while (o) { if (++x > 0 && sibling(o)) { o = sibling(o); break; } o = parent(o); if (o == obj) return n; } } ]; ! ---------------------------------------------------------------------------- ! Much better menus can be created using one of the optional library ! extensions. These are provided for compatibility with previous practice: ! ---------------------------------------------------------------------------- [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j; menu_nesting++; .LKRD; menu_item = 0; lines = EntryR(); main_title = item_name; print "--- "; print (string) main_title; print " ---^^"; if (menu_choices ofclass Routine) menu_choices(); else print (string) menu_choices; for (::) { L__M(##Miscellany, 52, lines); print "> "; #Ifdef TARGET_ZCODE; #IfV3; read buffer parse; #Ifnot; read buffer parse DrawStatusLine; #Endif; ! V3 j = parse->1; ! number of words #Ifnot; ! TARGET_GLULX; KeyboardPrimitive(buffer, parse); j = parse-->0; ! number of words #Endif; ! TARGET_ i = parse-->1; if (j == 0 || (i == QUIT1__WD or QUIT2__WD)) { menu_nesting--; if (menu_nesting > 0) rfalse; if (deadflag == 0) <>; rfalse; } i = TryNumber(1); if (i == 0) jump LKRD; if (i < 1 || i > lines) continue; menu_item = i; j = ChoiceR(); if (j == 2) jump LKRD; if (j == 3) rfalse; } ]; #Ifdef TARGET_ZCODE; #IfV3; [ DoMenu menu_choices EntryR ChoiceR; LowKey_Menu(menu_choices, EntryR, ChoiceR); ]; #Endif; ! V3 #IfV5; [ DoMenu menu_choices EntryR ChoiceR lines main_title main_wid cl i j oldcl pkey ch cw y x; if (pretty_flag == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 7; .ReDisplay; oldcl = 0; @erase_window $ffff; #Iftrue (#version_number == 6); @set_cursor -1; ch = HDR_FONTWUNITS->0; #Ifnot; ch = 1; #Endif; i = ch * (lines+7); @split_window i; i = HDR_SCREENWCHARS->0; if (i == 0) i = 80; @set_window 1; @set_cursor 1 1; #Iftrue (#version_number == 6); @set_font 4 -> cw; cw = HDR_FONTHUNITS->0; #Ifnot; cw = 1; #Endif; style reverse; spaces(i); j=1+(i/2-main_wid)*cw; @set_cursor 1 j; print (string) main_title; y=1+ch; @set_cursor y 1; spaces(i); x=1+cw; @set_cursor y x; print (string) NKEY__TX; j=1+(i-13)*cw; @set_cursor y j; print (string) PKEY__TX; y=y+ch; @set_cursor y 1; spaces(i); @set_cursor y x; print (string) RKEY__TX; j=1+(i-18)*cw; @set_cursor y j; if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; style roman; y = y+2*ch; @set_cursor y x; font off; if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); x = 1+3*cw; for (::) { if (cl ~= oldcl) { if (oldcl>0) { y=1+(oldcl-1)*ch; @set_cursor y x; print " "; } y=1+(cl-1)*ch; @set_cursor y x; print ">"; } oldcl = cl; @read_char 1 -> pkey; if (pkey == NKEY1__KY or NKEY2__KY or 130) { cl++; if (cl == 7+lines) cl = 7; continue; } if (pkey == PKEY1__KY or PKEY2__KY or 129) { cl--; if (cl == 6) cl = 6+lines; continue; } if (pkey == QKEY1__KY or QKEY2__KY or 27 or 131) break; if (pkey == 10 or 13 or 132) { @set_window 0; font on; new_line; new_line; new_line; menu_item = cl-6; EntryR(); @erase_window $ffff; @split_window ch; i = HDR_SCREENWCHARS->0; if ( i== 0) i = 80; @set_window 1; @set_cursor 1 1; style reverse; spaces(i); j=1+(i/2-item_width)*cw; @set_cursor 1 j; print (string) item_name; style roman; @set_window 0; new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); @read_char 1 -> pkey; jump ReDisplay; } } menu_nesting--; if (menu_nesting > 0) rfalse; font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; #Iftrue (#version_number == 6); @set_cursor -2; #Endif; new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! V5 #Ifnot; ! TARGET_GLULX [ DoMenu menu_choices EntryR ChoiceR winwid winhgt lines main_title main_wid cl i oldcl pkey; if (pretty_flag == 0 || gg_statuswin == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 0; ! If we printed "hit arrow keys" here, it would be appropriate to ! check for the availability of Glk input keys. But we actually ! print "hit N/P/Q". So it's reasonable to silently accept Glk ! arrow key codes as secondary options. .ReDisplay; glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(lines+7); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-main_wid, 0); print (string) main_title; glk_window_move_cursor(gg_statuswin, 1, 1); print (string) NKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-13, 1); print (string) PKEY__TX; glk_window_move_cursor(gg_statuswin, 1, 2); print (string) RKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-18, 2); if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; glk_set_style(style_Normal); glk_window_move_cursor(gg_statuswin, 1, 4); if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); oldcl = -1; for (::) { if (cl ~= oldcl) { if (cl < 0 || cl >= lines) cl = 0; if (oldcl >= 0) { glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) ' '; } oldcl = cl; glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) '>'; } pkey = KeyCharPrimitive(gg_statuswin, true); if (pkey == $80000000) jump ReDisplay; if (pkey == NKEY1__KY or NKEY2__KY or $fffffffb) { cl++; if (cl >= lines) cl = 0; continue; } if (pkey == PKEY1__KY or PKEY2__KY or $fffffffc) { cl--; if (cl < 0) cl = lines-1; continue; } if (pkey == QKEY1__KY or QKEY2__KY or $fffffff8 or $fffffffe) break; if (pkey == $fffffffa or $fffffffd) { glk_set_window(gg_mainwin); new_line; new_line; new_line; menu_item = cl+1; EntryR(); glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(1); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-item_width, 0); print (string) item_name; glk_set_style(style_Normal); glk_set_window(gg_mainwin); new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); pkey = KeyCharPrimitive(gg_mainwin, 1); jump ReDisplay; } } ! done with this menu... menu_nesting--; if (menu_nesting > 0) rfalse; glk_set_window(gg_mainwin); glk_window_clear(gg_mainwin); new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! A cunning routine (which could have been a daemon, but isn't, for the ! sake of efficiency) to move objects which could be in many rooms about ! so that the player never catches one not in place ! ---------------------------------------------------------------------------- [ MoveFloatingObjects i k l m address flag; if (location == player or nothing) return; objectloop (i) { address = i.&found_in; if (address && i hasnt non_floating && ~~IndirectlyContains(player, i)) { if (metaclass(address-->0) == Routine) flag = i.found_in(); else { flag = false; k = i.#found_in/WORDSIZE; for (l=0 : ll; if ((m in Class && location ofclass m) || m == location || m in location) { flag = true; break; } } } if (flag) { if (i notin location) move i to location; } else { if (parent(i)) remove i; } } } ]; ! ---------------------------------------------------------------------------- ! Two little routines for moving the player safely. ! ---------------------------------------------------------------------------- [ PlayerTo newplace flag; NoteDeparture(); move player to newplace; while (parent(newplace)) newplace = parent(newplace); location = real_location = newplace; MoveFloatingObjects(); AdjustLight(1); switch (flag) { 0: ; 1: NoteArrival(); ScoreArrival(); 2: LookSub(1); } ]; [ MovePlayer direc; ; ; ]; ! ---------------------------------------------------------------------------- ! The handy YesOrNo routine, and some "meta" verbs ! ---------------------------------------------------------------------------- [ YesOrNo noStatusRedraw i j; for (::) { #Ifdef TARGET_ZCODE; if (location == nothing || parent(player) == nothing || noStatusRedraw) read buffer parse; else read buffer parse DrawStatusLine; j = parse->1; #Ifnot; ! TARGET_GLULX; noStatusRedraw = 0; ! suppress warning KeyboardPrimitive(buffer, parse); j = parse-->0; #Endif; ! TARGET_ if (j) { ! at least one word entered i = parse-->1; if (i == YES1__WD or YES2__WD or YES3__WD) rtrue; if (i == NO1__WD or NO2__WD or NO3__WD) rfalse; } L__M(##Quit, 1); print "> "; } ]; #Ifdef TARGET_ZCODE; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart, 1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub; restore Rmaybe; return L__M(##Restore, 1); .RMaybe; L__M(##Restore, 2); ]; [ SaveSub flag; #IfV5; @save -> flag; switch (flag) { 0: L__M(##Save, 1); 1: L__M(##Save, 2); 2: RestoreColours(); L__M(##Restore, 2); } #Ifnot; save Smaybe; return L__M(##Save, 1); .SMaybe; L__M(##Save, 2); #Endif; ! V5 ]; [ VerifySub; @verify ?Vmaybe; jump Vwrong; .Vmaybe; return L__M(##Verify, 1); .Vwrong; L__M(##Verify, 2); ]; [ ScriptOnSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode) return L__M(##ScriptOn, 1); @output_stream 2; if (((HDR_GAMEFLAGS-->0) & 1) == 0) return L__M(##ScriptOn, 3); L__M(##ScriptOn, 2); transcript_mode = true; ]; [ ScriptOffSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode == false) return L__M(##ScriptOff, 1); L__M(##ScriptOff, 2); @output_stream -2; if ((HDR_GAMEFLAGS-->0) & 1) return L__M(##ScriptOff, 3); transcript_mode = false; ]; [ CommandsOnSub; @output_stream 4; xcommsdir = 1; L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (xcommsdir == 1) @output_stream -4; xcommsdir = 0; L__M(##CommandsOff, 1); ]; [ CommandsReadSub; @input_stream 1; xcommsdir = 2; L__M(##CommandsRead, 1); ]; #Ifnot; ! TARGET_GLULX; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart,1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub res fref; fref = glk_fileref_create_by_prompt($01, $02, 0); if (fref == 0) jump RFailed; gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump RFailed; @restore gg_savestr res; glk_stream_close(gg_savestr, 0); gg_savestr = 0; .RFailed; L__M(##Restore, 1); ]; [ SaveSub res fref; fref = glk_fileref_create_by_prompt($01, $01, 0); if (fref == 0) jump SFailed; gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump SFailed; @save gg_savestr res; if (res == -1) { ! The player actually just typed "restore". We're going to print ! L__M(##Restore,2); the Z-Code Inform library does this correctly ! now. But first, we have to recover all the Glk objects; the values ! in our global variables are all wrong. GGRecoverObjects(); glk_stream_close(gg_savestr, 0); gg_savestr = 0; return L__M(##Restore, 2); } glk_stream_close(gg_savestr, 0); gg_savestr = 0; if (res == 0) return L__M(##Save, 2); .SFailed; L__M(##Save, 1); ]; [ VerifySub res; @verify res; if (res == 0) return L__M(##Verify, 1); L__M(##Verify, 2); ]; [ ScriptOnSub; if (gg_scriptstr) return L__M(##ScriptOn, 1); if (gg_scriptfref == 0) { gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK); if (gg_scriptfref == 0) jump S1Failed; } gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK); if (gg_scriptstr == 0) jump S1Failed; glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); L__M(##ScriptOn, 2); VersionSub(); return; .S1Failed; L__M(##ScriptOn, 3); ]; [ ScriptOffSub; if (gg_scriptstr == 0) return L__M(##ScriptOff,1); L__M(##ScriptOff, 2); glk_stream_close(gg_scriptstr, 0); gg_scriptstr = 0; ]; [ CommandsOnSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsOn, 2); else return L__M(##CommandsOn, 3); } fref = glk_fileref_create_by_prompt($103, $01, 0); if (fref == 0) return L__M(##CommandsOn, 4); gg_command_reading = false; gg_commandstr = glk_stream_open_file(fref, $01, GG_COMMANDWSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsOn, 4); L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (gg_commandstr == 0) return L__M(##CommandsOff, 2); if (gg_command_reading) return L__M(##CommandsRead, 5); glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; L__M(##CommandsOff, 1); ]; [ CommandsReadSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsRead, 2); else return L__M(##CommandsRead, 3); } fref = glk_fileref_create_by_prompt($103, $02, 0); if (fref == 0) return L__M(##CommandsRead, 4); gg_command_reading = true; gg_commandstr = glk_stream_open_file(fref, $02, GG_COMMANDRSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsRead, 4); L__M(##CommandsRead, 1); ]; #Endif; ! TARGET_; [ NotifyOnSub; notify_mode = true; L__M(##NotifyOn); ]; [ NotifyOffSub; notify_mode = false; L__M(##NotifyOff); ]; [ Places1Sub i j k; L__M(##Places, 1); objectloop (i has visited) j++; objectloop (i has visited) { print (name) i; k++; if (k == j) return L__M(##Places, 2); if (k == j-1) print (SerialComma) j, (string) AND__TX; else print (string) COMMA__TX; } ]; [ Objects1Sub i j f; L__M(##Objects, 1); objectloop (i has moved) { f = 1; print (the) i; j = parent(i); if (j) { if (j == player) { if (i has worn) L__M(##Objects, 3, j, i); else L__M(##Objects, 4, j, i); jump Obj__Ptd; } if (j has animate) { L__M(##Objects, 5, j, i); jump Obj__Ptd; } if (j has visited) { L__M(##Objects, 6, j, i); jump Obj__Ptd; } if (j has container) { L__M(##Objects, 8, j, i); jump Obj__Ptd; } if (j has supporter) { L__M(##Objects, 9, j, i); jump Obj__Ptd; } if (j has enterable) { L__M(##Objects, 7, j, i); jump Obj__Ptd; } } L__M(##Objects, 10, j, i); .Obj__Ptd; new_line; } if (f == 0) L__M(##Objects, 2); ]; ! ---------------------------------------------------------------------------- ! The scoring system ! ---------------------------------------------------------------------------- [ ScoreSub; #Ifdef NO_SCORE; if (deadflag == 0) L__M(##Score, 2); #Ifnot; if (deadflag) new_line; L__M(##Score, 1); if(PrintRank() == false) LibraryExtensions.RunAll(ext_printrank); #Endif; ! NO_SCORE ]; #Ifndef TaskScore; [ TaskScore i; return task_scores->i; ]; #Endif; [ Achieved num; if (task_done->num == 0) { task_done->num = 1; score = score + TaskScore(num); } ]; [ PANum m n; print " "; n = m; if (n < 0) { n = -m; n = n*10; } if (n < 10) { print " "; jump Panuml; } if (n < 100) { print " "; jump Panuml; } if (n < 1000) { print " "; } .Panuml; print m, " "; ]; [ FullScoreSub i; ScoreSub(); if (score == 0 || TASKS_PROVIDED == 1) rfalse; new_line; L__M(##FullScore, 1); for (i=0 : ii == 1) { PANum(TaskScore(i)); if(PrintTaskName(i) == false) LibraryExtensions.RunAll(ext_printtaskname,i); } if (things_score) { PANum(things_score); L__M(##FullScore, 2); } if (places_score) { PANum(places_score); L__M(##FullScore, 3); } new_line; PANum(score); L__M(##FullScore, 4); ]; ! ---------------------------------------------------------------------------- ! Real verbs start here: Inventory ! ---------------------------------------------------------------------------- [ InvWideSub; if (actor == player) inventory_style = ENGLISH_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = ENGLISH_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvTallSub; if (actor == player) inventory_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = NEWLINE_BIT+INDENT_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvSub x; if (child(actor) == 0) return L__M(##Inv, 1); if (inventory_style == 0) if (actor == player) return InvTallSub(); else return InvWideSub(); L__M(##Inv, 2); if (inventory_style & NEWLINE_BIT) L__M(##Inv, 3); else print " "; WriteListFrom(child(actor), inventory_style, 1); if (inventory_style & ENGLISH_BIT) L__M(##Inv, 4); #Ifndef MANUAL_PRONOUNS; objectloop (x in player) PronounNotice(x); #Endif; x = 0; ! To prevent a "not used" error AfterRoutines(); ]; ! ---------------------------------------------------------------------------- ! The object tree and determining the possibility of moves ! ---------------------------------------------------------------------------- [ CommonAncestor o1 o2 i j; ! Find the nearest object indirectly containing o1 and o2, ! or return 0 if there is no common ancestor. i = o1; while (i) { j = o2; while (j) { if (j == i) return i; j = parent(j); } i = parent(i); } return 0; ]; [ IndirectlyContains o1 o2; ! Does o1 indirectly contain o2? (Same as testing if their common ancestor is o1.) while (o2) { if (o1 == o2) rtrue; if (o2 ofclass Class) rfalse; o2 = parent(o2); } rfalse; ]; [ ObjectScopedBySomething item i j k l m; i = item; objectloop (j .& add_to_scope) { l = j.&add_to_scope; k = (j.#add_to_scope)/WORDSIZE; if (l-->0 ofclass Routine) continue; for (m=0 : mm == i) return j; } rfalse; ]; [ ObjectIsUntouchable item flag1 flag2 ancestor i; ! Determine if there's any barrier preventing the actor from moving ! things to "item". Return false if no barrier; otherwise print a ! suitable message and return true. ! If flag1 is set, do not print any message. ! If flag2 is set, also apply Take/Remove restrictions. ! If the item has been added to scope by something, it's first necessary ! for that something to be touchable. ancestor = CommonAncestor(actor, item); if (ancestor == 0) { ancestor = item; while (ancestor && (i = ObjectScopedBySomething(ancestor)) == 0) ancestor = parent(ancestor); if (i) { if (ObjectIsUntouchable(i, flag1, flag2)) return; ! An item immediately added to scope } } else ! First, a barrier between the actor and the ancestor. The actor ! can only be in a sequence of enterable objects, and only closed ! containers form a barrier. if (actor ~= ancestor) { i = parent(actor); while (i ~= ancestor) { if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } ! Second, a barrier between the item and the ancestor. The item can ! be carried by someone, part of a piece of machinery, in or on top ! of something and so on. i = parent(item); if (item ~= ancestor && i ~= player) { while (i ~= ancestor) { if (flag2 && i hasnt container && i hasnt supporter) { if (i has animate) { if (flag1) rtrue; return L__M(##Take, 6, i, noun); } if (i has transparent) { if (flag1) rtrue; return L__M(##Take, 7, i, noun); } if (flag1) rtrue; return L__M(##Take, 8, item, noun); } if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } rfalse; ]; [ AttemptToTakeObject item ancestor after_recipient i k; ! Try to transfer the given item to the actor: return false ! if successful, true if unsuccessful, printing a suitable message ! in the latter case. ! People cannot ordinarily be taken. if (item == actor) return L__M(##Take, 2, noun); if (item has animate) return L__M(##Take, 3, item); ancestor = CommonAncestor(actor, item); if (ancestor == 0) { i = ObjectScopedBySomething(item); if (i) ancestor = CommonAncestor(actor, i); } ! Is the actor indirectly inside the item? if (ancestor == item) return L__M(##Take, 4, item); ! Does the actor already directly contain the item? if (item in actor) return L__M(##Take, 5, item); ! Can the actor touch the item, or is there (e.g.) a closed container ! in the way? if (ObjectIsUntouchable(item, false, true)) rtrue; ! The item is now known to be accessible. ! Consult the immediate possessor of the item, if it's in a container ! which the actor is not in. i = parent(item); if (i && i ~= ancestor && (i has container or supporter)) { after_recipient = i; k = action; action = ##LetGo; if (RunRoutines(i, before)) { action = k; rtrue; } action = k; } if (item has scenery) return L__M(##Take, 10, item); if (item has static) return L__M(##Take, 11, item); ! The item is now known to be available for taking. Is the player ! carrying too much? If so, possibly juggle items into the rucksack ! to make room. if (ObjectDoesNotFit(item, actor) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, item, actor)) return; if (AtFullCapacity(item, actor)) return L__M(##Take, 12, item); ! Transfer the item. move item to actor; give item ~worn; ! Send "after" message to the object letting go of the item, if any. if (after_recipient) { k = action; action = ##LetGo; if (RunRoutines(after_recipient, after)) { action = k; rtrue; } action = k; } rfalse; ]; [ AtFullCapacity n s obj k; n = n; ! suppress compiler warning if (s == actor) { objectloop (obj in s) if (obj hasnt worn) k++; } else k = children(s); if (k < RunRoutines(s, capacity) || (s == player && RoomInSack())) rfalse; ]; [ RoomInSack obj ks; if (SACK_OBJECT && SACK_OBJECT in player) { ks = keep_silent; keep_silent = 2; for (obj=youngest(player) : obj : obj=elder(obj)) if (obj ~= SACK_OBJECT && obj hasnt worn or light) { ; if (obj in SACK_OBJECT) { keep_silent = ks; return L__M(##Take, 13, obj, SACK_OBJECT); } } keep_silent = ks; } rfalse; ]; ! ---------------------------------------------------------------------------- ! Support for implicit actions ! ---------------------------------------------------------------------------- [ CheckImplicitAction act o1 o2 sav_act sav_noun sav_sec res; if (o1 provides before_implicit) { sav_act = action; action = act; sav_noun = noun; noun = o1; if (o2) { sav_sec = second; second = o2; } res = RunRoutines(o1, before_implicit); action = sav_act; noun = sav_noun; if (sav_sec) second = sav_sec; } else { if (no_implicit_actions) res = 2; else res = 0; } return res; ]; [ ImplicitTake obj res ks supcon; switch (metaclass(obj)) { Class, String, Routine, nothing: rfalse; } if (obj in actor) rfalse; if (action_to_be == ##Drop && ~~IndirectlyContains(actor, obj)) rfalse; res = CheckImplicitAction(##Take, obj); ! 0 = Take object, Tell the user (normal default) ! 1 = Take object, don't Tell ! 2 = don't Take object continue (default with no_implicit_actions) ! 3 = don't Take object, don't continue if (res >= 2) rtrue; if (parent(obj) && parent(obj) has container or supporter) supcon = parent(obj); ks = keep_silent; keep_silent = 2; AttemptToTakeObject(obj); keep_silent = ks; if (obj notin actor) rtrue; if (res == 0 && ~~keep_silent) if (supcon) L__M(##Miscellany, 58, obj, supcon); else L__M(##Miscellany, 26, obj); rfalse; ]; [ ImplicitExit obj res ks; if (parent(obj) == nothing) rfalse; res = CheckImplicitAction(##Exit, obj); ! 0 = Exit object, Tell the user (normal default) ! 1 = Exit object, don't Tell ! 2 = don't Exit object continue (default with no_implicit_actions) ! 3 = don't Exit object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (parent(actor) == obj) rtrue; if (res == 0 && ~~keep_silent) L__M(##Exit, 5, obj); rfalse; ]; [ ImplicitClose obj res ks; if (obj hasnt open) rfalse; res = CheckImplicitAction(##Close, obj); ! 0 = Close object, Tell the user (normal default) ! 1 = Close object, don't Tell ! 2 = don't Close object continue (default with no_implicit_actions) ! 3 = don't Close object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (obj has open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Close, 4, obj); rfalse; ]; [ ImplicitOpen obj res temp inp1temp; if (obj has open) rfalse; res = CheckImplicitAction(##Open, obj); ! 0 = Open object, Tell the user (normal default) ! 1 = Open object, don't Tell ! 2 = don't Open object continue (default with no_implicit_actions) ! 3 = don't Open object, don't continue if (res >= 2) rtrue; if (obj has locked) rtrue; temp = keep_silent; keep_silent = 2; ; keep_silent = temp; if (obj hasnt open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Open, 6, obj); temp = action; action = ##Open; inp1temp = inp1; inp1 = obj; AfterRoutines(); inp1 = inp1temp; action = temp; rfalse; ]; [ ImplicitUnlock obj; if (obj has locked) rtrue; rfalse; ]; [ ImplicitDisrobe obj res ks; if (obj hasnt worn) rfalse; res = CheckImplicitAction(##Disrobe, obj); ! 0 = Take off object, Tell the user (normal default) ! 1 = Take off object, don't Tell ! 2 = don't Take off object continue (default with no_implicit_actions) ! 3 = don't Take off object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (obj has worn && obj in actor) rtrue; if (res == 0 && ~~keep_silent) L__M(##Drop, 3, obj); rfalse; ]; ! ---------------------------------------------------------------------------- ! Object movement verbs ! ---------------------------------------------------------------------------- [ TakeSub; if (onotheld_mode == 0 || noun notin actor) if (AttemptToTakeObject(noun)) return; if (AfterRoutines()) return; notheld_mode = onotheld_mode; if (notheld_mode == 1 || keep_silent) return; L__M(##Take, 1, noun); ]; [ RemoveSub i; i = parent(noun); if (i && i has container && i hasnt open && ImplicitOpen(i)) return L__M(##Remove, 1, i); if (i ~= second) return L__M(##Remove, 2, noun); if (i has animate) return L__M(##Take, 6, i, noun); if (AttemptToTakeObject(noun)) rtrue; action = ##Remove; if (AfterRoutines()) return; action = ##Take; if (AfterRoutines()) return; if (keep_silent) return; L__M(##Remove, 3, noun); ]; [ DropSub; if (noun == actor) return L__M(##PutOn, 4, noun); if (noun in parent(actor)) return L__M(##Drop, 1, noun); if (noun notin actor && ~~ImplicitTake(noun)) return L__M(##Drop, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; move noun to parent(actor); if (AfterRoutines() || keep_silent) return; L__M(##Drop, 4, noun); ]; [ PutOnSub ancestor; receive_action = ##PutOn; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##PutOn, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##PutOn, 2, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, before)) { action = ##PutOn; return; } action = ##PutOn; } if (second hasnt supporter) return L__M(##PutOn, 3, second); if (ancestor == actor) return L__M(##PutOn, 4, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##PutOn, 6, second); move noun to second; if (AfterRoutines()) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##PutOn; return; } action = ##PutOn; } if (keep_silent) return; if (multiflag) return L__M(##PutOn, 7); L__M(##PutOn, 8, noun, second); ]; [ InsertSub ancestor; receive_action = ##Insert; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Insert, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##Insert, 5, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second,before)) { action = ##Insert; rtrue; } action = ##Insert; if (second has container && second hasnt open && ImplicitOpen(second)) return L__M(##Insert, 3, second); } if (second hasnt container) return L__M(##Insert, 2, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##Insert, 7, second); move noun to second; if (AfterRoutines()) rtrue; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##Insert; rtrue; } action = ##Insert; } if (keep_silent) rtrue; if (multiflag) return L__M(##Insert, 8, noun); L__M(##Insert, 9, noun, second); ]; ! ---------------------------------------------------------------------------- ! Empties and transfers are routed through the actions above ! ---------------------------------------------------------------------------- [ TransferSub; if (noun notin actor && AttemptToTakeObject(noun)) return; if (second has supporter) <>; if (second == d_obj) <>; <>; ]; [ EmptySub; second = d_obj; EmptyTSub(); ]; [ EmptyTSub i j k flag; if (noun == second) return L__M(##EmptyT, 4, noun); if (ObjectIsUntouchable(noun)) return; if (noun hasnt container) return L__M(##EmptyT, 1, noun); if (noun hasnt open && ImplicitOpen(noun)) return L__M(##EmptyT, 2, noun); if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) return L__M(##EmptyT, 1, second); if (second hasnt open && ImplicitOpen(second)) return L__M(##EmptyT, 2, second); } } i = child(noun); k = children(noun); if (i == 0) return L__M(##EmptyT, 3, noun); while (i) { j = sibling(i); flag = false; if (ObjectIsUntouchable(noun)) flag = true; if (noun hasnt container) flag = true; if (noun hasnt open) flag = true; if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) flag = true; if (second hasnt open) flag = true; } } if (k-- == 0) flag = 1; if (flag) break; if (keep_silent == 0) print (name) i, (string) COLON__TX; ; i = j; } ]; ! ---------------------------------------------------------------------------- ! Gifts ! ---------------------------------------------------------------------------- [ GiveSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Give, 1, noun); if (second == actor) return L__M(##Give, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; if (second == player) { move noun to player; return L__M(##Give, 4, noun); } if (RunLife(second, ##Give)) return; L__M(##Give, 3, second); ]; [ GiveRSub; ; ]; [ ShowSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Show, 1, noun); if (second == player) <>; if (RunLife(second, ##Show)) return; L__M(##Show, 2, second); ]; [ ShowRSub; ; ]; ! ---------------------------------------------------------------------------- ! Travelling around verbs ! ---------------------------------------------------------------------------- [ EnterSub ancestor j ks; if (noun has door || noun in compass) <>; if (actor in noun) return L__M(##Enter, 1, noun); if (noun hasnt enterable) return L__M(##Enter, 2, noun, verb_word); if (parent(actor) ~= parent(noun)) { ancestor = CommonAncestor(actor, noun); if (ancestor == actor or 0) return L__M(##Enter, 4, noun); while (actor notin ancestor) { j = parent(actor); ks = keep_silent; if (parent(j) ~= ancestor || noun ~= ancestor) { L__M(##Enter, 6, j); keep_silent = 1; } ; keep_silent = ks; if (actor in j) return; } if (actor in noun) return; if (noun notin ancestor) { j = parent(noun); while (parent(j) ~= ancestor) j = parent(j); L__M(##Enter, 7, j); ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (actor notin j) return; <>; } } if (noun has container && noun hasnt open && ImplicitOpen(noun)) return L__M(##Enter, 3, noun); move actor to noun; if (AfterRoutines() || keep_silent) return; L__M(##Enter, 5, noun); if (actor == player) Locale(noun); ]; [ GetOffSub; if (parent(actor) == noun) <>; L__M(##GetOff, 1, noun); ]; [ ExitSub p; p = parent(actor); if (noun ~= nothing && noun ~= p) return L__M(##Exit, 4 ,noun); if (p == location || (location == thedark && p == real_location)) { if (actor provides posture && actor.posture) { actor.posture = 0; return L__M(##Exit, 6); } if ((location.out_to) || (location == thedark && real_location.out_to)) <>; return L__M(##Exit, 1); } if (p has container && p hasnt open && ImplicitOpen(p)) return L__M(##Exit, 2, p); if (noun == nothing) { inp1 = p; if (RunRoutines(p, before)) return; } move actor to parent(p); if (player provides posture) player.posture = 0; if (AfterRoutines() || keep_silent) return; L__M(##Exit, 3, p); if (actor == player && p has container) LookSub(1); ]; [ VagueGoSub; L__M(##VagueGo); ]; [ GoInSub; <>; ]; [ GoSub i j k movewith thedir next_loc; ! first, check if any PushDir object is touchable if (second && second notin Compass && ObjectIsUntouchable(second)) return; movewith = 0; i = parent(actor); if ((location ~= thedark && i ~= location) || (location == thedark && i ~= real_location)) { j = location; if (location == thedark) location = real_location; k = RunRoutines(i, before); if (k ~= 3) location = j; if (k == 1) { movewith = i; i = parent(i); } else { if (k) rtrue; if (ImplicitExit(i)) return L__M(##Go, 1, i); i = parent(actor); } } thedir = noun.door_dir; if (metaclass(thedir) == Routine) thedir = RunRoutines(noun, door_dir); next_loc = i.thedir; k = metaclass(next_loc); if (k == String) { print (string) next_loc; new_line; rfalse; } if (k == Routine) { next_loc = RunRoutines(i, thedir); if (next_loc == 1) rtrue; } if (k == nothing || next_loc == 0) { if (i.cant_go ~= 0 or CANTGO__TX) PrintOrRun(i, cant_go); else L__M(##Go, 2); rfalse; } if (next_loc has door) { if (next_loc has concealed) return L__M(##Go, 2); if (next_loc hasnt open && ImplicitOpen(next_loc)) { if (noun == u_obj) return L__M(##Go, 3, next_loc); if (noun == d_obj) return L__M(##Go, 4, next_loc); return L__M(##Go, 5, next_loc); } k = RunRoutines(next_loc, door_to); if (k == 0) return L__M(##Go, 6, next_loc); if (k == 1) rtrue; next_loc = k; } action = ##Going; if (RunRoutines(next_loc, before)) { action = ##Go; return; } action = ##Go; if (movewith == 0) move actor to next_loc; else move movewith to next_loc; if (actor ~= player) return L__M(##Go, 7); k = location; location = next_loc; MoveFloatingObjects(); if (OffersLight(location)) lightflag = true; else { lightflag = false; if (k == thedark) { if(DarkToDark() == false) ! From real_location To location LibraryExtensions.RunAll(ext_darktodark); if (deadflag) rtrue; } location = thedark; } NoteDeparture(); real_location = next_loc; action = ##Going; if (RunRoutines(prev_location, after)) { action = ##Go; return; } action = ##Go; if (AfterRoutines() || keep_silent) return; LookSub(1); ]; ! ---------------------------------------------------------------------------- ! Describing the world. SayWhatsOn(object) does just that (producing ! no text if nothing except possibly "scenery" and "concealed" items are). ! Locale(object) runs through the "tail end" of a Look-style room ! description for the contents of the object, printing up suitable ! descriptions as it goes. ! ---------------------------------------------------------------------------- [ SayWhatsOn descon j f; if (descon == parent(player)) rfalse; objectloop (j in descon) if (j hasnt concealed && j hasnt scenery) f = 1; if (f == 0) rfalse; L__M(##Look, 4, descon); ]; [ NotSupportingThePlayer o i; i = parent(player); while (i && i ~= visibility_ceiling) { if (i == o) rfalse; i = parent(i); if (i && i hasnt supporter) rtrue; } rtrue; ]; ! modified with the fix for L61122 [ Locale descin text_without_ALSO text_with_ALSO o p num_objs must_print_ALSO; objectloop (o in descin) give o ~workflag; num_objs = 0; objectloop (o in descin) if (o hasnt concealed && NotSupportingThePlayer(o)) { #Ifndef MANUAL_PRONOUNS; PronounNotice(o); #Endif; if (o has scenery) { if (o has supporter && child(o)) SayWhatsOn(o); } else { give o workflag; num_objs++; p = initial; if ((o has door or container) && o has open && o provides when_open) { p = when_open; jump Prop_Chosen; } if ((o has door or container) && o hasnt open && o provides when_closed) { p = when_closed; jump Prop_Chosen; } if (o has switchable && o has on && o provides when_on) { p = when_on; jump Prop_Chosen; } if (o has switchable && o hasnt on && o provides when_off) { p = when_off; } .Prop_Chosen; if (o.&describe && RunRoutines(o, describe)) { must_print_ALSO = true; give o ~workflag; num_objs--; continue; } if (o.p && (o hasnt moved || p ~= initial)) { new_line; PrintOrRun(o, p); must_print_ALSO = true; give o ~workflag; num_objs--; if (o has supporter && child(o)) SayWhatsOn(o); } } } if (num_objs == 0) return 0; if (actor ~= player) give actor concealed; if (text_without_ALSO) { new_line; if (must_print_ALSO) print (string) text_with_ALSO, " "; else print (string) text_without_ALSO, " "; WriteListFrom(child(descin), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT); } else { if (must_print_ALSO) L__M(##Look, 5, descin); else L__M(##Look, 6, descin); } if (actor ~= player) give actor ~concealed; return num_objs; ]; ! ---------------------------------------------------------------------------- ! Looking. LookSub(1) is allowed to abbreviate long descriptions, but ! LookSub(0) (which is what happens when the Look action is generated) ! isn't. (Except that these are over-ridden by the player-set lookmode.) ! ---------------------------------------------------------------------------- [ LMode1Sub; lookmode=1; L__M(##LMode1); ]; ! Brief [ LMode2Sub; lookmode=2; L__M(##LMode2); ]; ! Verbose [ LMode3Sub; lookmode=3; L__M(##LMode3); ]; ! Superbrief [ LModeNormalSub; ! 'normal' value: the default, or as set in Initialise() switch (initial_lookmode) { 1: <>; 3: <>; default: <>; } ]; [ NoteArrival descin; if (location ~= lastdesc) { if (location.initial) PrintOrRun(location, initial); if (location == thedark) { lastdesc = thedark; return; } descin = location; if(NewRoom() == false) LibraryExtensions.RunAll(ext_newroom); lastdesc = descin; } ]; [ NoteDeparture; prev_location = real_location; ]; [ ScoreArrival; if (location hasnt visited) { give location visited; if (location has scored) { score = score + ROOM_SCORE; places_score = places_score + ROOM_SCORE; } } ]; [ FindVisibilityLevels visibility_levels; visibility_levels = 1; visibility_ceiling = parent(player); while ((parent(visibility_ceiling)) && (visibility_ceiling hasnt container || visibility_ceiling has open or transparent)) { visibility_ceiling = parent(visibility_ceiling); visibility_levels++; } return visibility_levels; ]; [ LookSub allow_abbrev visibility_levels i j k nl_flag; if (parent(player) == 0) return RunTimeError(10); .MovedByInitial; if (location == thedark) { visibility_ceiling = thedark; NoteArrival(); } else { visibility_levels = FindVisibilityLevels(); if (visibility_ceiling == location) { NoteArrival(); if (visibility_ceiling ~= location) jump MovedByInitial; } } ! Printing the top line: e.g. ! Octagonal Room (on the table) (as Frodo) new_line; #Ifdef TARGET_ZCODE; style bold; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Subheader); #Endif; ! TARGET_ if (visibility_levels == 0) print (name) thedark; else { if (visibility_ceiling ~= location) print (The) visibility_ceiling; else print (name) visibility_ceiling; } #Ifdef TARGET_ZCODE; style roman; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Normal); #Endif; ! TARGET_ for (j=1,i=parent(player) : j0 : j--,i=parent(i)) give i workflag; for (j=visibility_levels : j>0 : j--) { for (i=player,k=0 : k>; else return L__M(##Search, 5, noun); if (noun has switchable) { L__M(##Examine, 3, noun); rfalse; } return L__M(##Examine, 2, noun); } i = PrintOrRun(noun, description); if (i < 2 && noun has switchable) L__M(##Examine, 3, noun); AfterRoutines(); ]; [ LookUnderSub; if (location == thedark) return L__M(##LookUnder, 1, noun); L__M(##LookUnder, 2); ]; [ VisibleContents o i f; objectloop (i in o) if (i hasnt concealed or scenery) f++; return f; ]; [ SearchSub f; if (location == thedark) return L__M(##Search, 1, noun); if (ObjectIsUntouchable(noun)) return; f = VisibleContents(noun); if (noun has supporter) { if (f == 0) return L__M(##Search, 2, noun); return L__M(##Search, 3, noun); } if (noun hasnt container) return L__M(##Search, 4, noun); if (noun hasnt transparent or open && ImplicitOpen(noun)) return L__M(##Search, 5, noun); if (AfterRoutines()) return; if (f == 0) return L__M(##Search, 6, noun); L__M(##Search, 7, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which change the state of objects without moving them ! ---------------------------------------------------------------------------- [ UnlockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Unlock, 1, noun); if (noun hasnt locked) return L__M(##Unlock, 2, noun); if (noun.with_key ~= second) return L__M(##Unlock, 3, second); give noun ~locked; if (AfterRoutines() || keep_silent) return; L__M(##Unlock, 4, noun); ]; [ LockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Lock, 1, noun); if (noun has locked) return L__M(##Lock, 2 ,noun); if (noun has open && ImplicitClose(noun)) return L__M(##Lock, 3, noun); if (noun.with_key ~= second) return L__M(##Lock, 4, second); give noun locked; if (AfterRoutines() || keep_silent) return; L__M(##Lock, 5, noun); ]; [ SwitchonSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOn, 1, noun); if (noun has on) return L__M(##SwitchOn, 2, noun); give noun on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOn, 3, noun); ]; [ SwitchoffSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOff, 1, noun); if (noun hasnt on) return L__M(##SwitchOff, 2, noun); give noun ~on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOff, 3, noun); ]; [ OpenSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Open, 1, noun); if (noun has locked && ImplicitUnlock(noun)) return L__M(##Open, 2, noun); if (noun has open) return L__M(##Open, 3, noun); give noun open; if (keep_silent || AfterRoutines()) return; if (noun hasnt container) return L__M(##Open, 5, noun); if ((noun has container && location ~= thedark && VisibleContents(noun) && IndirectlyContains(noun, player)) == 0) { if (noun hasnt transparent && noun hasnt door) return L__M(##Open, 4, noun); } L__M(##Open, 5, noun); ]; [ CloseSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Close, 1, noun); if (noun hasnt open) return L__M(##Close, 2, noun); give noun ~open; if (AfterRoutines() || keep_silent) return; L__M(##Close, 3, noun); ]; [ DisrobeSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt worn) return L__M(##Disrobe, 1, noun); give noun ~worn; if (AfterRoutines() || keep_silent) return; L__M(##Disrobe, 2, noun); ]; [ WearSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt clothing) return L__M(##Wear, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wear, 2, noun); if (noun has worn) return L__M(##Wear, 3, noun); give noun worn; if (AfterRoutines() || keep_silent) return; L__M(##Wear, 4, noun); ]; [ EatSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt edible) return L__M(##Eat, 1, noun); if (noun has worn && ImplicitDisrobe(noun)) return; remove noun; if (AfterRoutines() || keep_silent) return; L__M(##Eat, 2, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which are really just stubs (anything which happens for these ! actions must happen in before rules) ! ---------------------------------------------------------------------------- [ AllowPushDir i; if (parent(second) ~= compass) return L__M(##PushDir, 2, noun); if (second == u_obj or d_obj) return L__M(##PushDir, 3, noun); AfterRoutines(); i = noun; move i to actor; ; if (location == thedark) move i to real_location; else move i to location; ]; [ AnswerSub; if (second && RunLife(second,##Answer)) rfalse; L__M(##Answer, 1, noun); ]; [ AskSub; if (RunLife(noun,##Ask)) rfalse; L__M(##Ask, 1, noun); ]; [ AskForSub; if (noun == player) <>; L__M(##Order, 1, noun); ]; [ AskToSub; L__M(##Order, 1, noun); ]; [ AttackSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && RunLife(noun, ##Attack)) rfalse; L__M(##Attack, 1, noun); ]; [ BlowSub; L__M(##Blow, 1, noun); ]; [ BurnSub; if (noun has animate) return L__M(##Burn, 2, noun); L__M(##Burn, 1, noun); ]; [ BuySub; L__M(##Buy, 1, noun); ]; [ ClimbSub; if (noun has animate) return L__M(##Climb, 2, noun); L__M(##Climb, 1, noun); ]; [ ConsultSub; L__M(##Consult, 1, noun); ]; [ CutSub; if (noun has animate) return L__M(##Cut, 2, noun); L__M(##Cut, 1, noun); ]; [ DigSub; L__M(##Dig, 1, noun); ]; [ DrinkSub; L__M(##Drink, 1, noun); ]; [ FillSub; if (second == nothing) return L__M(##Fill, 1, noun); L__M(##Fill, 2, noun, second); ]; [ JumpSub; L__M(##Jump, 1, noun); ]; [ JumpInSub; if (noun has animate) return L__M(##JumpIn, 2, noun); if (noun has enterable) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOnSub; if (noun has animate) return L__M(##JumpOn, 2, noun); if (noun has enterable && noun has supporter) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOverSub; if (noun has animate) return L__M(##JumpOver, 2, noun); L__M(##JumpOver, 1, noun); ]; [ KissSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##Kiss)) return; if (noun == actor) return L__M(##Touch, 3, noun); L__M(##Kiss, 1, noun); ]; [ ListenSub; L__M(##Listen, 1, noun); ]; [ MildSub; L__M(##Mild, 1, noun); ]; [ NoSub; L__M(##No); ]; [ PraySub; L__M(##Pray, 1, noun); ]; [ PullSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Pull, 1, noun); if (noun == actor) return L__M(##Pull, 6, noun); if (noun has static) return L__M(##Pull, 2, noun); if (noun has scenery) return L__M(##Pull, 3, noun); if (noun has animate) return L__M(##Pull, 5, noun); L__M(##Pull, 4, noun); ]; [ PushSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Push, 1, noun); if (noun == actor) return L__M(##Push, 5, noun); if (noun has static) return L__M(##Push, 2, noun); if (noun has scenery) return L__M(##Push, 3, noun); if (noun has animate) return L__M(##Push, 5, noun); L__M(##Push, 4, noun); ]; [ PushDirSub; L__M(##PushDir, 1, noun); ]; [ RubSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Rub, 2, noun); L__M(##Rub, 1, noun); ]; [ SetSub; L__M(##Set, 1, noun); ]; [ SetToSub; L__M(##SetTo, 1, noun); ]; [ SingSub; L__M(##Sing, 1, noun); ]; [ SleepSub; L__M(##Sleep, 1, noun); ]; [ SmellSub; if (noun ~= nothing && noun has animate) return L__M(##Smell, 2, noun); L__M(##Smell, 1, noun); ]; [ SorrySub; L__M(##Sorry, 1, noun); ]; [ SqueezeSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && noun ~= player) return L__M(##Squeeze, 1, noun); L__M(##Squeeze, 2, noun); ]; [ StrongSub; L__M(##Strong, 1, noun); ]; [ SwimSub; L__M(##Swim, 1, noun); ]; [ SwingSub; L__M(##Swing, 1, noun); ]; [ TasteSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Taste, 2, noun); L__M(##Taste, 1, noun); ]; [ TellSub; if (noun == actor) return L__M(##Tell, 1, noun); if (RunLife(noun, ##Tell)) return; L__M(##Tell, 2, noun); ]; [ ThinkSub; L__M(##Think, 1, noun); ]; [ ThrowAtSub; if (ObjectIsUntouchable(noun)) return; if (second > 1) { action = ##ThrownAt; if (RunRoutines(second, before)) { action = ##ThrowAt; rtrue; } action = ##ThrowAt; } if (noun has worn && ImplicitDisrobe(noun)) return; if (second hasnt animate) return L__M(##ThrowAt, 1, noun); if (RunLife(second, ##ThrowAt)) return; L__M(##ThrowAt, 2, noun); ]; [ TieSub; if (noun has animate) return L__M(##Tie, 2, noun); L__M(##Tie, 1, noun); ]; [ TouchSub; if (noun == actor) return L__M(##Touch, 3, noun); if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Touch, 1, noun); L__M(##Touch, 2,noun); ]; [ TurnSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Turn, 1, noun); if (noun == actor) return L__M(##Turn, 5, noun); if (noun has static) return L__M(##Turn, 2, noun); if (noun has scenery) return L__M(##Turn, 3, noun); if (noun has animate) return L__M(##Turn, 5, noun); L__M(##Turn, 4, noun); ]; [ WaitSub; if (AfterRoutines()) rtrue; L__M(##Wait, 1, noun); ]; [ WakeSub; L__M(##Wake, 1, noun); ]; [ WakeOtherSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##WakeOther)) return; L__M(##WakeOther, 1, noun); ]; [ WaveSub; if (noun == player) return L__M(##Wave, 2 ,noun, second); if (noun == actor) return L__M(##Wave, 3, noun, second); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wave, 1, noun); L__M(##Wave, 2, noun, second); ]; [ WaveHandsSub; if (noun) return L__M(##WaveHands, 2, noun); L__M(##WaveHands, 1, noun); ]; [ YesSub; L__M(##Yes); ]; ! ---------------------------------------------------------------------------- ! Debugging verbs ! ---------------------------------------------------------------------------- #Ifdef DEBUG; [ TraceOnSub; parser_trace = 1; "[Trace on.]"; ]; [ TraceLevelSub; parser_trace = noun; print "[Parser tracing set to level ", parser_trace, ".]^"; ]; [ TraceOffSub; parser_trace = 0; "Trace off."; ]; [ RoutinesOnSub; debug_flag = debug_flag | DEBUG_MESSAGES; "[Message listing on.]"; ]; [ RoutinesOffSub; debug_flag = debug_flag & ~DEBUG_MESSAGES; "[Message listing off.]"; ]; [ RoutinesVerboseSub; debug_flag = debug_flag | (DEBUG_VERBOSE|DEBUG_MESSAGES); "[Verbose message listing on.]"; ]; [ ActionsOnSub; debug_flag = debug_flag | DEBUG_ACTIONS; "[Action listing on.]"; ]; [ ActionsOffSub; debug_flag = debug_flag & ~DEBUG_ACTIONS; "[Action listing off.]"; ]; [ TimersOnSub; debug_flag = debug_flag | DEBUG_TIMERS; "[Timers listing on.]"; ]; [ TimersOffSub; debug_flag = debug_flag & ~DEBUG_TIMERS; "[Timers listing off.]"; ]; #Ifdef VN_1610; [ ChangesOnSub; debug_flag = debug_flag | DEBUG_CHANGES; "[Changes listing on.]"; ]; [ ChangesOffSub; debug_flag = debug_flag & ~DEBUG_CHANGES; "[Changes listing off.]"; ]; #Ifnot; [ ChangesOnSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; [ ChangesOffSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; #Endif; ! VN_1610 #Ifdef TARGET_ZCODE; [ PredictableSub i; i = random(-100); "[Random number generator now predictable.]"; ]; #Ifnot; ! TARGET_GLULX; [ PredictableSub; @setrandom 100; "[Random number generator now predictable.]"; ]; #Endif; ! TARGET_; [ XTestMove obj dest; if (~~obj ofclass Object) "[Not an object.]"; if (~~dest ofclass Object) "[Destination not an object.]"; if ((obj <= InformLibrary) || (obj == LibraryMessages) || (obj in 1)) "[Can't move ", (name) obj, ": it's a system object.]"; while (dest) { if (dest == obj) "[Can't move ", (name) obj, ": it would contain itself.]"; dest = parent(dest); } rfalse; ]; [ XPurloinSub; if (XTestMove(noun, player)) return; move noun to player; give noun moved ~concealed; "[Purloined.]"; ]; [ XAbstractSub; if (XTestMove(noun, second)) return; move noun to second; "[Abstracted.]"; ]; [ XObj obj f; if (parent(obj) == 0) print (name) obj; else print (a) obj; print " (", obj, ") "; if (f && parent(obj)) print "in ~", (name) parent(obj), "~ (", parent(obj), ")"; new_line; if (child(obj) == 0) rtrue; if (obj == Class) ! ??? WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+NOARTICLE_BIT, 1); else WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+FULLINV_BIT, 1); ]; [ XTreeSub i; if (noun && ~~noun ofclass Object) "[Not an object.]"; if (noun == 0) { objectloop (i) if (i ofclass Object && parent(i) == 0) XObj(i); } else XObj(noun, true); ]; [ GotoSub; if ((~~noun ofclass Object) || parent(noun)) "[Not a safe place.]"; PlayerTo(noun); ]; [ GoNearSub x; if (~~noun ofclass Object) "[Not a safe place.]"; x = noun; while (parent(x)) x = parent(x); PlayerTo(x); ]; [ Print_ScL obj; print_ret ++x_scope_count, ": ", (a) obj, " (", obj, ")"; ]; [ ScopeSub; if (noun && ~~noun ofclass Object) "[Not an object.]"; x_scope_count = 0; LoopOverScope(Print_ScL, noun); if (x_scope_count == 0) "Nothing is in scope."; ]; #Ifdef TARGET_GLULX; [ GlkListSub id val; id = glk_window_iterate(0, gg_arguments); while (id) { print "Window ", id, " (", gg_arguments-->0, "): "; val = glk_window_get_type(id); switch (val) { 1: print "pair"; 2: print "blank"; 3: print "textbuffer"; 4: print "textgrid"; 5: print "graphics"; default: print "unknown"; } val = glk_window_get_parent(id); if (val) print ", parent is window ", val; else print ", no parent (root)"; val = glk_window_get_stream(id); print ", stream ", val; val = glk_window_get_echo_stream(id); if (val) print ", echo stream ", val; print "^"; id = glk_window_iterate(id, gg_arguments); } id = glk_stream_iterate(0, gg_arguments); while (id) { print "Stream ", id, " (", gg_arguments-->0, ")^"; id = glk_stream_iterate(id, gg_arguments); } id = glk_fileref_iterate(0, gg_arguments); while (id) { print "Fileref ", id, " (", gg_arguments-->0, ")^"; id = glk_fileref_iterate(id, gg_arguments); } val = glk_gestalt(gestalt_Sound, 0); if (val) { id = glk_schannel_iterate(0, gg_arguments); while (id) { print "Soundchannel ", id, " (", gg_arguments-->0, ")^"; id = glk_schannel_iterate(id, gg_arguments); } } ]; #Endif; ! TARGET_; #Endif; ! DEBUG ! ---------------------------------------------------------------------------- ! Finally: the mechanism for library text (the text is in the language defn) ! ---------------------------------------------------------------------------- [ L__M act n x1 x2 s; if (keep_silent == 2) return; s = sw__var; sw__var = act; if (n == 0) n = 1; L___M(n, x1, x2); sw__var = s; ]; [ L___M n x1 x2 s; s = action; lm_n = n; lm_o = x1; lm_s = x2; action = sw__var; if (RunRoutines(LibraryMessages, before)) { action = s; rfalse; } if (LibraryExtensions.RunWhile(ext_messages, false )) { action = s; rfalse; } action = s; LanguageLM(n, x1, x2); ]; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_VERBLIB; #Ifnot; ! LIBRARY_STAGE < AFTER_VERBLIB but ~= AFTER_PARSER ! (this shouldn't happen because if 'parser' isn't there, LIBRARY_STAGE isn't defined) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; #Ifnot; ! LIBRARY_STAGE >= AFTER_VERBLIB: already included Message "Warning: 'verblib' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; #Ifnot; ! LIBRARY_STAGE is not defined (likely, 'parser' hasn't been included) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; ! ============================================================================== ! infglk.h -- auto-generated by parse_dispatch.py. ! Generated for Glk API version 0.7.4 ! Declare as a system header unless we are in the I7 environment. ! (I7 does not use the System_file directive.) #ifndef NI_BUILD_COUNT; System_file; #endif; ! NI_BUILD_COUNT #ifndef INFGLK_H; Constant INFGLK_H = 1; #ifdef TARGET_GLULX; Constant evtype_Arrange = 5; Constant evtype_CharInput = 2; Constant evtype_Hyperlink = 8; Constant evtype_LineInput = 3; Constant evtype_MouseInput = 4; Constant evtype_None = 0; Constant evtype_Redraw = 6; Constant evtype_SoundNotify = 7; Constant evtype_Timer = 1; Constant evtype_VolumeNotify = 9; Constant filemode_Read = 2; Constant filemode_ReadWrite = 3; Constant filemode_Write = 1; Constant filemode_WriteAppend = 5; Constant fileusage_BinaryMode = 0; Constant fileusage_Data = 0; Constant fileusage_InputRecord = 3; Constant fileusage_SavedGame = 1; Constant fileusage_TextMode = 256; Constant fileusage_Transcript = 2; Constant fileusage_TypeMask = 15; Constant gestalt_CharInput = 1; Constant gestalt_CharOutput = 3; Constant gestalt_CharOutput_ApproxPrint = 1; Constant gestalt_CharOutput_CannotPrint = 0; Constant gestalt_CharOutput_ExactPrint = 2; Constant gestalt_DateTime = 20; Constant gestalt_DrawImage = 7; Constant gestalt_Graphics = 6; Constant gestalt_GraphicsCharInput = 23; Constant gestalt_GraphicsTransparency = 14; Constant gestalt_HyperlinkInput = 12; Constant gestalt_Hyperlinks = 11; Constant gestalt_LineInput = 2; Constant gestalt_LineInputEcho = 17; Constant gestalt_LineTerminatorKey = 19; Constant gestalt_LineTerminators = 18; Constant gestalt_MouseInput = 4; Constant gestalt_ResourceStream = 22; Constant gestalt_Sound = 8; Constant gestalt_Sound2 = 21; Constant gestalt_SoundMusic = 13; Constant gestalt_SoundNotify = 10; Constant gestalt_SoundVolume = 9; Constant gestalt_Timer = 5; Constant gestalt_Unicode = 15; Constant gestalt_UnicodeNorm = 16; Constant gestalt_Version = 0; Constant imagealign_InlineCenter = 3; Constant imagealign_InlineDown = 2; Constant imagealign_MarginLeft = 4; Constant imagealign_MarginRight = 5; Constant imagealign_InlineUp = 1; Constant keycode_Delete = 4294967289; Constant keycode_Down = 4294967291; Constant keycode_End = 4294967283; Constant keycode_Escape = 4294967288; Constant keycode_Func1 = 4294967279; Constant keycode_Func10 = 4294967270; Constant keycode_Func11 = 4294967269; Constant keycode_Func12 = 4294967268; Constant keycode_Func2 = 4294967278; Constant keycode_Func3 = 4294967277; Constant keycode_Func4 = 4294967276; Constant keycode_Func5 = 4294967275; Constant keycode_Func6 = 4294967274; Constant keycode_Func7 = 4294967273; Constant keycode_Func8 = 4294967272; Constant keycode_Func9 = 4294967271; Constant keycode_Home = 4294967284; Constant keycode_Left = 4294967294; Constant keycode_MAXVAL = 28; Constant keycode_PageDown = 4294967285; Constant keycode_PageUp = 4294967286; Constant keycode_Return = 4294967290; Constant keycode_Right = 4294967293; Constant keycode_Tab = 4294967287; Constant keycode_Unknown = 4294967295; Constant keycode_Up = 4294967292; Constant seekmode_Current = 1; Constant seekmode_End = 2; Constant seekmode_Start = 0; Constant style_Alert = 5; Constant style_BlockQuote = 7; Constant style_Emphasized = 1; Constant style_Header = 3; Constant style_Input = 8; Constant style_NUMSTYLES = 11; Constant style_Normal = 0; Constant style_Note = 6; Constant style_Preformatted = 2; Constant style_Subheader = 4; Constant style_User1 = 9; Constant style_User2 = 10; Constant stylehint_BackColor = 8; Constant stylehint_Indentation = 0; Constant stylehint_Justification = 2; Constant stylehint_NUMHINTS = 10; Constant stylehint_Oblique = 5; Constant stylehint_ParaIndentation = 1; Constant stylehint_Proportional = 6; Constant stylehint_ReverseColor = 9; Constant stylehint_Size = 3; Constant stylehint_TextColor = 7; Constant stylehint_Weight = 4; Constant stylehint_just_Centered = 2; Constant stylehint_just_LeftFlush = 0; Constant stylehint_just_LeftRight = 1; Constant stylehint_just_RightFlush = 3; Constant winmethod_Above = 2; Constant winmethod_Below = 3; Constant winmethod_Border = 0; Constant winmethod_BorderMask = 256; Constant winmethod_DirMask = 15; Constant winmethod_DivisionMask = 240; Constant winmethod_Fixed = 16; Constant winmethod_Left = 0; Constant winmethod_NoBorder = 256; Constant winmethod_Proportional = 32; Constant winmethod_Right = 1; Constant wintype_AllTypes = 0; Constant wintype_Blank = 2; Constant wintype_Graphics = 5; Constant wintype_Pair = 1; Constant wintype_TextBuffer = 3; Constant wintype_TextGrid = 4; [ glk_exit _vararg_count; ! glk_exit() @glk 1 _vararg_count 0; return 0; ]; [ glk_tick _vararg_count; ! glk_tick() @glk 3 _vararg_count 0; return 0; ]; [ glk_gestalt _vararg_count ret; ! glk_gestalt(uint, uint) => uint @glk 4 _vararg_count ret; return ret; ]; [ glk_gestalt_ext _vararg_count ret; ! glk_gestalt_ext(uint, uint, uintarray, arraylen) => uint @glk 5 _vararg_count ret; return ret; ]; [ glk_window_iterate _vararg_count ret; ! glk_window_iterate(window, &uint) => window @glk 32 _vararg_count ret; return ret; ]; [ glk_window_get_rock _vararg_count ret; ! glk_window_get_rock(window) => uint @glk 33 _vararg_count ret; return ret; ]; [ glk_window_get_root _vararg_count ret; ! glk_window_get_root() => window @glk 34 _vararg_count ret; return ret; ]; [ glk_window_open _vararg_count ret; ! glk_window_open(window, uint, uint, uint, uint) => window @glk 35 _vararg_count ret; return ret; ]; [ glk_window_close _vararg_count; ! glk_window_close(window, &{uint, uint}) @glk 36 _vararg_count 0; return 0; ]; [ glk_window_get_size _vararg_count; ! glk_window_get_size(window, &uint, &uint) @glk 37 _vararg_count 0; return 0; ]; [ glk_window_set_arrangement _vararg_count; ! glk_window_set_arrangement(window, uint, uint, window) @glk 38 _vararg_count 0; return 0; ]; [ glk_window_get_arrangement _vararg_count; ! glk_window_get_arrangement(window, &uint, &uint, &window) @glk 39 _vararg_count 0; return 0; ]; [ glk_window_get_type _vararg_count ret; ! glk_window_get_type(window) => uint @glk 40 _vararg_count ret; return ret; ]; [ glk_window_get_parent _vararg_count ret; ! glk_window_get_parent(window) => window @glk 41 _vararg_count ret; return ret; ]; [ glk_window_clear _vararg_count; ! glk_window_clear(window) @glk 42 _vararg_count 0; return 0; ]; [ glk_window_move_cursor _vararg_count; ! glk_window_move_cursor(window, uint, uint) @glk 43 _vararg_count 0; return 0; ]; [ glk_window_get_stream _vararg_count ret; ! glk_window_get_stream(window) => stream @glk 44 _vararg_count ret; return ret; ]; [ glk_window_set_echo_stream _vararg_count; ! glk_window_set_echo_stream(window, stream) @glk 45 _vararg_count 0; return 0; ]; [ glk_window_get_echo_stream _vararg_count ret; ! glk_window_get_echo_stream(window) => stream @glk 46 _vararg_count ret; return ret; ]; [ glk_set_window _vararg_count; ! glk_set_window(window) @glk 47 _vararg_count 0; return 0; ]; [ glk_window_get_sibling _vararg_count ret; ! glk_window_get_sibling(window) => window @glk 48 _vararg_count ret; return ret; ]; [ glk_stream_iterate _vararg_count ret; ! glk_stream_iterate(stream, &uint) => stream @glk 64 _vararg_count ret; return ret; ]; [ glk_stream_get_rock _vararg_count ret; ! glk_stream_get_rock(stream) => uint @glk 65 _vararg_count ret; return ret; ]; [ glk_stream_open_file _vararg_count ret; ! glk_stream_open_file(fileref, uint, uint) => stream @glk 66 _vararg_count ret; return ret; ]; [ glk_stream_open_memory _vararg_count ret; ! glk_stream_open_memory(nativechararray, arraylen, uint, uint) => stream @glk 67 _vararg_count ret; return ret; ]; [ glk_stream_close _vararg_count; ! glk_stream_close(stream, &{uint, uint}) @glk 68 _vararg_count 0; return 0; ]; [ glk_stream_set_position _vararg_count; ! glk_stream_set_position(stream, int, uint) @glk 69 _vararg_count 0; return 0; ]; [ glk_stream_get_position _vararg_count ret; ! glk_stream_get_position(stream) => uint @glk 70 _vararg_count ret; return ret; ]; [ glk_stream_set_current _vararg_count; ! glk_stream_set_current(stream) @glk 71 _vararg_count 0; return 0; ]; [ glk_stream_get_current _vararg_count ret; ! glk_stream_get_current() => stream @glk 72 _vararg_count ret; return ret; ]; [ glk_stream_open_resource _vararg_count ret; ! glk_stream_open_resource(uint, uint) => stream @glk 73 _vararg_count ret; return ret; ]; [ glk_fileref_create_temp _vararg_count ret; ! glk_fileref_create_temp(uint, uint) => fileref @glk 96 _vararg_count ret; return ret; ]; [ glk_fileref_create_by_name _vararg_count ret; ! glk_fileref_create_by_name(uint, string, uint) => fileref @glk 97 _vararg_count ret; return ret; ]; [ glk_fileref_create_by_prompt _vararg_count ret; ! glk_fileref_create_by_prompt(uint, uint, uint) => fileref @glk 98 _vararg_count ret; return ret; ]; [ glk_fileref_destroy _vararg_count; ! glk_fileref_destroy(fileref) @glk 99 _vararg_count 0; return 0; ]; [ glk_fileref_iterate _vararg_count ret; ! glk_fileref_iterate(fileref, &uint) => fileref @glk 100 _vararg_count ret; return ret; ]; [ glk_fileref_get_rock _vararg_count ret; ! glk_fileref_get_rock(fileref) => uint @glk 101 _vararg_count ret; return ret; ]; [ glk_fileref_delete_file _vararg_count; ! glk_fileref_delete_file(fileref) @glk 102 _vararg_count 0; return 0; ]; [ glk_fileref_does_file_exist _vararg_count ret; ! glk_fileref_does_file_exist(fileref) => uint @glk 103 _vararg_count ret; return ret; ]; [ glk_fileref_create_from_fileref _vararg_count ret; ! glk_fileref_create_from_fileref(uint, fileref, uint) => fileref @glk 104 _vararg_count ret; return ret; ]; [ glk_put_char _vararg_count; ! glk_put_char(uchar) @glk 128 _vararg_count 0; return 0; ]; [ glk_put_char_stream _vararg_count; ! glk_put_char_stream(stream, uchar) @glk 129 _vararg_count 0; return 0; ]; [ glk_put_string _vararg_count; ! glk_put_string(string) @glk 130 _vararg_count 0; return 0; ]; [ glk_put_string_stream _vararg_count; ! glk_put_string_stream(stream, string) @glk 131 _vararg_count 0; return 0; ]; [ glk_put_buffer _vararg_count; ! glk_put_buffer(nativechararray, arraylen) @glk 132 _vararg_count 0; return 0; ]; [ glk_put_buffer_stream _vararg_count; ! glk_put_buffer_stream(stream, nativechararray, arraylen) @glk 133 _vararg_count 0; return 0; ]; [ glk_set_style _vararg_count; ! glk_set_style(uint) @glk 134 _vararg_count 0; return 0; ]; [ glk_set_style_stream _vararg_count; ! glk_set_style_stream(stream, uint) @glk 135 _vararg_count 0; return 0; ]; [ glk_get_char_stream _vararg_count ret; ! glk_get_char_stream(stream) => int @glk 144 _vararg_count ret; return ret; ]; [ glk_get_line_stream _vararg_count ret; ! glk_get_line_stream(stream, nativechararray, arraylen) => uint @glk 145 _vararg_count ret; return ret; ]; [ glk_get_buffer_stream _vararg_count ret; ! glk_get_buffer_stream(stream, nativechararray, arraylen) => uint @glk 146 _vararg_count ret; return ret; ]; [ glk_char_to_lower _vararg_count ret; ! glk_char_to_lower(uchar) => uchar @glk 160 _vararg_count ret; return ret; ]; [ glk_char_to_upper _vararg_count ret; ! glk_char_to_upper(uchar) => uchar @glk 161 _vararg_count ret; return ret; ]; [ glk_stylehint_set _vararg_count; ! glk_stylehint_set(uint, uint, uint, int) @glk 176 _vararg_count 0; return 0; ]; [ glk_stylehint_clear _vararg_count; ! glk_stylehint_clear(uint, uint, uint) @glk 177 _vararg_count 0; return 0; ]; [ glk_style_distinguish _vararg_count ret; ! glk_style_distinguish(window, uint, uint) => uint @glk 178 _vararg_count ret; return ret; ]; [ glk_style_measure _vararg_count ret; ! glk_style_measure(window, uint, uint, &uint) => uint @glk 179 _vararg_count ret; return ret; ]; [ glk_select _vararg_count; ! glk_select(&{uint, window, uint, uint}) @glk 192 _vararg_count 0; return 0; ]; [ glk_select_poll _vararg_count; ! glk_select_poll(&{uint, window, uint, uint}) @glk 193 _vararg_count 0; return 0; ]; [ glk_request_line_event _vararg_count; ! glk_request_line_event(window, nativechararray, arraylen, uint) @glk 208 _vararg_count 0; return 0; ]; [ glk_cancel_line_event _vararg_count; ! glk_cancel_line_event(window, &{uint, window, uint, uint}) @glk 209 _vararg_count 0; return 0; ]; [ glk_request_char_event _vararg_count; ! glk_request_char_event(window) @glk 210 _vararg_count 0; return 0; ]; [ glk_cancel_char_event _vararg_count; ! glk_cancel_char_event(window) @glk 211 _vararg_count 0; return 0; ]; [ glk_request_mouse_event _vararg_count; ! glk_request_mouse_event(window) @glk 212 _vararg_count 0; return 0; ]; [ glk_cancel_mouse_event _vararg_count; ! glk_cancel_mouse_event(window) @glk 213 _vararg_count 0; return 0; ]; [ glk_request_timer_events _vararg_count; ! glk_request_timer_events(uint) @glk 214 _vararg_count 0; return 0; ]; [ glk_image_get_info _vararg_count ret; ! glk_image_get_info(uint, &uint, &uint) => uint @glk 224 _vararg_count ret; return ret; ]; [ glk_image_draw _vararg_count ret; ! glk_image_draw(window, uint, int, int) => uint @glk 225 _vararg_count ret; return ret; ]; [ glk_image_draw_scaled _vararg_count ret; ! glk_image_draw_scaled(window, uint, int, int, uint, uint) => uint @glk 226 _vararg_count ret; return ret; ]; [ glk_window_flow_break _vararg_count; ! glk_window_flow_break(window) @glk 232 _vararg_count 0; return 0; ]; [ glk_window_erase_rect _vararg_count; ! glk_window_erase_rect(window, int, int, uint, uint) @glk 233 _vararg_count 0; return 0; ]; [ glk_window_fill_rect _vararg_count; ! glk_window_fill_rect(window, uint, int, int, uint, uint) @glk 234 _vararg_count 0; return 0; ]; [ glk_window_set_background_color _vararg_count; ! glk_window_set_background_color(window, uint) @glk 235 _vararg_count 0; return 0; ]; [ glk_schannel_iterate _vararg_count ret; ! glk_schannel_iterate(schannel, &uint) => schannel @glk 240 _vararg_count ret; return ret; ]; [ glk_schannel_get_rock _vararg_count ret; ! glk_schannel_get_rock(schannel) => uint @glk 241 _vararg_count ret; return ret; ]; [ glk_schannel_create _vararg_count ret; ! glk_schannel_create(uint) => schannel @glk 242 _vararg_count ret; return ret; ]; [ glk_schannel_destroy _vararg_count; ! glk_schannel_destroy(schannel) @glk 243 _vararg_count 0; return 0; ]; [ glk_schannel_create_ext _vararg_count ret; ! glk_schannel_create_ext(uint, uint) => schannel @glk 244 _vararg_count ret; return ret; ]; [ glk_schannel_play_multi _vararg_count ret; ! glk_schannel_play_multi(schannelarray, arraylen, uintarray, arraylen, uint) => uint @glk 247 _vararg_count ret; return ret; ]; [ glk_schannel_play _vararg_count ret; ! glk_schannel_play(schannel, uint) => uint @glk 248 _vararg_count ret; return ret; ]; [ glk_schannel_play_ext _vararg_count ret; ! glk_schannel_play_ext(schannel, uint, uint, uint) => uint @glk 249 _vararg_count ret; return ret; ]; [ glk_schannel_stop _vararg_count; ! glk_schannel_stop(schannel) @glk 250 _vararg_count 0; return 0; ]; [ glk_schannel_set_volume _vararg_count; ! glk_schannel_set_volume(schannel, uint) @glk 251 _vararg_count 0; return 0; ]; [ glk_sound_load_hint _vararg_count; ! glk_sound_load_hint(uint, uint) @glk 252 _vararg_count 0; return 0; ]; [ glk_schannel_set_volume_ext _vararg_count; ! glk_schannel_set_volume_ext(schannel, uint, uint, uint) @glk 253 _vararg_count 0; return 0; ]; [ glk_schannel_pause _vararg_count; ! glk_schannel_pause(schannel) @glk 254 _vararg_count 0; return 0; ]; [ glk_schannel_unpause _vararg_count; ! glk_schannel_unpause(schannel) @glk 255 _vararg_count 0; return 0; ]; [ glk_set_hyperlink _vararg_count; ! glk_set_hyperlink(uint) @glk 256 _vararg_count 0; return 0; ]; [ glk_set_hyperlink_stream _vararg_count; ! glk_set_hyperlink_stream(stream, uint) @glk 257 _vararg_count 0; return 0; ]; [ glk_request_hyperlink_event _vararg_count; ! glk_request_hyperlink_event(window) @glk 258 _vararg_count 0; return 0; ]; [ glk_cancel_hyperlink_event _vararg_count; ! glk_cancel_hyperlink_event(window) @glk 259 _vararg_count 0; return 0; ]; [ glk_buffer_to_lower_case_uni _vararg_count ret; ! glk_buffer_to_lower_case_uni(uintarray, arraylen, uint) => uint @glk 288 _vararg_count ret; return ret; ]; [ glk_buffer_to_upper_case_uni _vararg_count ret; ! glk_buffer_to_upper_case_uni(uintarray, arraylen, uint) => uint @glk 289 _vararg_count ret; return ret; ]; [ glk_buffer_to_title_case_uni _vararg_count ret; ! glk_buffer_to_title_case_uni(uintarray, arraylen, uint, uint) => uint @glk 290 _vararg_count ret; return ret; ]; [ glk_buffer_canon_decompose_uni _vararg_count ret; ! glk_buffer_canon_decompose_uni(uintarray, arraylen, uint) => uint @glk 291 _vararg_count ret; return ret; ]; [ glk_buffer_canon_normalize_uni _vararg_count ret; ! glk_buffer_canon_normalize_uni(uintarray, arraylen, uint) => uint @glk 292 _vararg_count ret; return ret; ]; [ glk_put_char_uni _vararg_count; ! glk_put_char_uni(uint) @glk 296 _vararg_count 0; return 0; ]; [ glk_put_string_uni _vararg_count; ! glk_put_string_uni(unicode) @glk 297 _vararg_count 0; return 0; ]; [ glk_put_buffer_uni _vararg_count; ! glk_put_buffer_uni(uintarray, arraylen) @glk 298 _vararg_count 0; return 0; ]; [ glk_put_char_stream_uni _vararg_count; ! glk_put_char_stream_uni(stream, uint) @glk 299 _vararg_count 0; return 0; ]; [ glk_put_string_stream_uni _vararg_count; ! glk_put_string_stream_uni(stream, unicode) @glk 300 _vararg_count 0; return 0; ]; [ glk_put_buffer_stream_uni _vararg_count; ! glk_put_buffer_stream_uni(stream, uintarray, arraylen) @glk 301 _vararg_count 0; return 0; ]; [ glk_get_char_stream_uni _vararg_count ret; ! glk_get_char_stream_uni(stream) => int @glk 304 _vararg_count ret; return ret; ]; [ glk_get_buffer_stream_uni _vararg_count ret; ! glk_get_buffer_stream_uni(stream, uintarray, arraylen) => uint @glk 305 _vararg_count ret; return ret; ]; [ glk_get_line_stream_uni _vararg_count ret; ! glk_get_line_stream_uni(stream, uintarray, arraylen) => uint @glk 306 _vararg_count ret; return ret; ]; [ glk_stream_open_file_uni _vararg_count ret; ! glk_stream_open_file_uni(fileref, uint, uint) => stream @glk 312 _vararg_count ret; return ret; ]; [ glk_stream_open_memory_uni _vararg_count ret; ! glk_stream_open_memory_uni(uintarray, arraylen, uint, uint) => stream @glk 313 _vararg_count ret; return ret; ]; [ glk_stream_open_resource_uni _vararg_count ret; ! glk_stream_open_resource_uni(uint, uint) => stream @glk 314 _vararg_count ret; return ret; ]; [ glk_request_char_event_uni _vararg_count; ! glk_request_char_event_uni(window) @glk 320 _vararg_count 0; return 0; ]; [ glk_request_line_event_uni _vararg_count; ! glk_request_line_event_uni(window, uintarray, arraylen, uint) @glk 321 _vararg_count 0; return 0; ]; [ glk_set_echo_line_event _vararg_count; ! glk_set_echo_line_event(window, uint) @glk 336 _vararg_count 0; return 0; ]; [ glk_set_terminators_line_event _vararg_count; ! glk_set_terminators_line_event(window, uintarray, arraylen) @glk 337 _vararg_count 0; return 0; ]; [ glk_current_time _vararg_count; ! glk_current_time(&{int, uint, int}) @glk 352 _vararg_count 0; return 0; ]; [ glk_current_simple_time _vararg_count ret; ! glk_current_simple_time(uint) => int @glk 353 _vararg_count ret; return ret; ]; [ glk_time_to_date_utc _vararg_count; ! glk_time_to_date_utc(&{int, uint, int}, &{int, int, int, int, int, int, int, int}) @glk 360 _vararg_count 0; return 0; ]; [ glk_time_to_date_local _vararg_count; ! glk_time_to_date_local(&{int, uint, int}, &{int, int, int, int, int, int, int, int}) @glk 361 _vararg_count 0; return 0; ]; [ glk_simple_time_to_date_utc _vararg_count; ! glk_simple_time_to_date_utc(int, uint, &{int, int, int, int, int, int, int, int}) @glk 362 _vararg_count 0; return 0; ]; [ glk_simple_time_to_date_local _vararg_count; ! glk_simple_time_to_date_local(int, uint, &{int, int, int, int, int, int, int, int}) @glk 363 _vararg_count 0; return 0; ]; [ glk_date_to_time_utc _vararg_count; ! glk_date_to_time_utc(&{int, int, int, int, int, int, int, int}, &{int, uint, int}) @glk 364 _vararg_count 0; return 0; ]; [ glk_date_to_time_local _vararg_count; ! glk_date_to_time_local(&{int, int, int, int, int, int, int, int}, &{int, uint, int}) @glk 365 _vararg_count 0; return 0; ]; [ glk_date_to_simple_time_utc _vararg_count ret; ! glk_date_to_simple_time_utc(&{int, int, int, int, int, int, int, int}, uint) => int @glk 366 _vararg_count ret; return ret; ]; [ glk_date_to_simple_time_local _vararg_count ret; ! glk_date_to_simple_time_local(&{int, int, int, int, int, int, int, int}, uint) => int @glk 367 _vararg_count ret; return ret; ]; #endif; ! TARGET_GLULX #endif; ! INFGLK_H ! ============================================================================== ! GRAMMAR: Grammar table entries for the standard verbs library. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive or at ! https://gitlab.com/DavidGriffith/inform6lib/ ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ============================================================================== System_file; #Ifdef LIBRARY_STAGE; #Iffalse LIBRARY_STAGE >= AFTER_GRAMMAR; ! if not already included #Iftrue LIBRARY_STAGE == AFTER_VERBLIB; ! if okay to include it ! ------------------------------------------------------------------------------ ! The "meta-verbs", commands to the game rather than in the game, come first: ! ------------------------------------------------------------------------------ Verb meta 'brief' * -> LMode1; Verb meta 'verbose' 'long' * -> LMode2; Verb meta 'superbrief' 'short' * -> LMode3; Verb meta 'normal' * -> LModeNormal; Verb meta 'notify' * -> NotifyOn * 'on' -> NotifyOn * 'off' -> NotifyOff; Verb meta 'pronouns' 'nouns' * -> Pronouns; Verb meta 'quit' 'q//' 'die' * -> Quit; Verb meta 'recording' * -> CommandsOn * 'on' -> CommandsOn * 'off' -> CommandsOff; Verb meta 'replay' * -> CommandsRead; Verb meta 'restart' * -> Restart; Verb meta 'restore' * -> Restore; Verb meta 'save' * -> Save; Verb meta 'score' * -> Score; Verb meta 'fullscore' 'full' * -> FullScore * 'score' -> FullScore; Verb meta 'script' 'transcript' * -> ScriptOn * 'on' -> ScriptOn * 'off' -> ScriptOff; Verb meta 'noscript' 'unscript' * -> ScriptOff; Verb meta 'verify' * -> Verify; Verb meta 'version' * -> Version; #Ifndef NO_PLACES; Verb meta 'objects' * -> Objects; Verb meta 'places' * -> Places; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ ! Debugging grammar ! ------------------------------------------------------------------------------ #Ifdef DEBUG; Verb meta 'actions' * -> ActionsOn * 'on' -> ActionsOn * 'off' -> ActionsOff; Verb meta 'changes' * -> ChangesOn * 'on' -> ChangesOn * 'off' -> ChangesOff; Verb meta 'gonear' * anynumber -> GoNear * noun -> Gonear; Verb meta 'goto' * anynumber -> Goto; Verb meta 'random' * -> Predictable; Verb meta 'routines' 'messages' * -> RoutinesOn * 'on' -> RoutinesOn * 'verbose' -> RoutinesVerbose * 'off' -> RoutinesOff; Verb meta 'scope' * -> Scope * anynumber -> Scope * noun -> Scope; Verb meta 'showdict' 'dict' * -> ShowDict * topic -> ShowDict; Verb meta 'showobj' * -> Showobj * anynumber -> Showobj * multi -> Showobj; Verb meta 'showverb' * special -> Showverb; Verb meta 'timers' 'daemons' * -> TimersOn * 'on' -> TimersOn * 'off' -> TimersOff; Verb meta 'trace' * -> TraceOn * number -> TraceLevel * 'on' -> TraceOn * 'off' -> TraceOff; Verb meta 'abstract' * anynumber 'to' anynumber -> XAbstract * noun 'to' noun -> XAbstract; Verb meta 'purloin' * anynumber -> XPurloin * multi -> XPurloin; Verb meta 'tree' * -> XTree * anynumber -> XTree * noun -> XTree; #Ifdef TARGET_GLULX; Verb meta 'glklist' * -> Glklist; #Endif; ! TARGET_ #Endif; ! DEBUG ! ------------------------------------------------------------------------------ ! And now the game verbs. ! ------------------------------------------------------------------------------ [ ADirection; if (noun in compass) rtrue; rfalse; ]; Verb 'answer' 'say' 'shout' 'speak' * topic 'to' creature -> Answer; Verb 'ask' * creature 'about' topic -> Ask * creature 'for' noun -> AskFor * creature 'to' topic -> AskTo * 'that' creature topic -> AskTo; Verb 'attack' 'break' 'crack' 'destroy' 'fight' 'hit' 'kill' 'murder' 'punch' 'smash' 'thump' 'torture' 'wreck' * noun -> Attack; Verb 'blow' * held -> Blow; Verb 'bother' 'curses' 'darn' 'drat' * -> Mild * topic -> Mild; Verb 'burn' 'light' * noun -> Burn * noun 'with' held -> Burn; Verb 'buy' 'purchase' * noun -> Buy; Verb 'climb' 'scale' * noun -> Climb * 'up'/'over' noun -> Climb; Verb 'close' 'cover' 'shut' * noun -> Close * 'up' noun -> Close * 'off' noun -> SwitchOff; Verb 'consult' * noun 'about' topic -> Consult * noun 'on' topic -> Consult; Verb 'cut' 'chop' 'prune' 'slice' * noun -> Cut; Verb 'dig' * noun -> Dig * noun 'with' held -> Dig * 'in' noun -> Dig * 'in' noun 'with' held -> Dig; Verb 'disrobe' 'doff' 'shed' * held -> Disrobe; Verb 'drink' 'sip' 'swallow' * noun -> Drink; Verb 'drop' 'discard' * multiheld -> Drop * multiexcept 'in'/'into'/'down' noun -> Insert * multiexcept 'on'/'onto' noun -> PutOn; Verb 'throw' * held 'at'/'against'/'on'/'onto' noun -> ThrowAt; Verb 'eat' * held -> Eat; Verb 'empty' * noun -> Empty * 'out' noun -> Empty * noun 'out' -> Empty * noun 'to'/'into'/'on'/'onto' noun -> EmptyT; Verb 'enter' 'cross' * -> GoIn * noun -> Enter; Verb 'examine' 'x//' 'check' 'describe' 'watch' * noun -> Examine; Verb 'exit' 'out' 'outside' * -> Exit * noun -> Exit; Verb 'fill' * noun -> Fill * noun 'from' noun -> Fill; Verb 'get' * 'out'/'off'/'up' 'of'/'from' noun -> Exit * multi -> Take * 'in'/'into'/'on'/'onto' noun -> Enter * 'off' noun -> GetOff * multiinside 'from'/'off' noun -> Remove; Verb 'give' 'feed' 'offer' 'pay' * creature held -> Give reverse * held 'to' creature -> Give * 'over' held 'to' creature -> Give; Verb 'go' 'run' 'walk' * -> VagueGo * noun=ADirection -> Go * noun -> Enter * 'out'/'outside' -> Exit * 'in'/'inside' -> GoIn * 'into'/'in'/'inside'/'through' noun -> Enter; Verb 'in' 'inside' * -> GoIn; Verb 'insert' * multiexcept 'in'/'into' noun -> Insert; Verb 'inventory' 'inv' 'i//' * -> Inv * 'tall' -> InvTall * 'wide' -> InvWide; Verb 'jump' 'hop' 'skip' * -> Jump * 'in' noun -> JumpIn * 'into' noun -> JumpIn * 'on' noun -> JumpOn * 'upon' noun -> JumpOn * 'over' noun -> JumpOver; Verb 'kiss' 'embrace' 'hug' * creature -> Kiss; Verb 'leave' * -> VagueGo * noun=ADirection -> Go * noun -> Exit * 'into'/'in'/'inside'/'through' noun -> Enter; Verb 'listen' 'hear' * -> Listen * noun -> Listen * 'to' noun -> Listen; Verb 'lock' * noun 'with' held -> Lock; Verb 'look' 'l//' * -> Look * 'at' noun -> Examine * 'inside'/'in'/'into'/'through'/'on' noun -> Search * 'under' noun -> LookUnder * 'up' topic 'in' noun -> Consult * noun=ADirection -> Examine * 'to' noun=ADirection -> Examine; Verb 'no' * -> No; Verb 'open' 'uncover' 'undo' 'unwrap' * noun -> Open * noun 'with' held -> Unlock; Verb 'peel' * noun -> Take * 'off' noun -> Take; Verb 'pick' * 'up' multi -> Take * multi 'up' -> Take; Verb 'pray' * -> Pray; Verb 'pry' 'prise' 'prize' 'lever' 'jemmy' 'force' * noun 'with' held -> Unlock * 'apart'/'open' noun 'with' held -> Unlock * noun 'apart'/'open' 'with' held -> Unlock; Verb 'pull' 'drag' * noun -> Pull; Verb 'push' 'clear' 'move' 'press' 'shift' * noun -> Push * noun noun -> PushDir * noun 'to' noun -> Transfer; Verb 'put' * multiexcept 'in'/'inside'/'into' noun -> Insert * multiexcept 'on'/'onto' noun -> PutOn * 'on' held -> Wear * 'down' multiheld -> Drop * multiheld 'down' -> Drop; Verb 'read' * noun -> Examine * 'about' topic 'in' noun -> Consult * topic 'in' noun -> Consult; Verb 'remove' * held -> Disrobe * multi -> Take * multiinside 'from' noun -> Remove; Verb 'rub' 'clean' 'dust' 'polish' 'scrub' 'shine' 'sweep' 'wipe' * noun -> Rub; Verb 'search' * noun -> Search; Verb 'set' 'adjust' * noun -> Set * noun 'to' special -> SetTo; Verb 'show' 'display' 'present' * creature held -> Show reverse * held 'to' creature -> Show; Verb 'shit' 'damn' 'fuck' 'sod' * -> Strong * topic -> Strong; Verb 'sing' * -> Sing; Verb 'sit' 'lie' * 'on' 'top' 'of' noun -> Enter * 'on'/'in'/'inside' noun -> Enter; Verb 'sleep' 'nap' * -> Sleep; Verb 'smell' 'sniff' * -> Smell * noun -> Smell; Verb 'sorry' * -> Sorry; Verb 'squeeze' 'squash' * noun -> Squeeze; Verb 'stand' * -> Exit * 'up' -> Exit * 'on' noun -> Enter; Verb 'swim' 'dive' * -> Swim; Verb 'swing' * noun -> Swing * 'on' noun -> Swing; Verb 'switch' * noun -> Switchon * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Verb 'take' 'carry' 'hold' * multi -> Take * 'off' held -> Disrobe * multiinside 'from'/'off' noun -> Remove * 'inventory' -> Inv; Verb 'taste' * noun -> Taste; Verb 'tell' * creature 'about' topic -> Tell * creature 'to' topic -> AskTo; Verb 'think' * -> Think; Verb 'tie' 'attach' 'connect' 'fasten' 'fix' * noun -> Tie * noun 'to' noun -> Tie; Verb 'touch' 'feel' 'fondle' 'grope' * noun -> Touch; Verb 'transfer' * noun 'to' noun -> Transfer; Verb 'turn' 'rotate' 'screw' 'twist' 'unscrew' * noun -> Turn * noun 'on' -> Switchon * noun 'off' -> Switchoff * 'on' noun -> Switchon * 'off' noun -> Switchoff; Verb 'unlock' * noun 'with' held -> Unlock; Verb 'wait' 'z//' * -> Wait; Verb 'wake' 'awake' 'awaken' * -> Wake * 'up' -> Wake * creature -> WakeOther * creature 'up' -> WakeOther * 'up' creature -> WakeOther; Verb 'wave' * -> WaveHands * noun -> Wave * noun 'at' noun -> Wave * 'at' noun -> WaveHands; Verb 'wear' 'don' * held -> Wear; Verb 'yes' 'y//' * -> Yes; ! ------------------------------------------------------------------------------ ! This routine is no longer used here, but provided to help existing games ! which use it as a general parsing routine: [ ConTopic w; consult_from = wn; do w = NextWordStopped(); until (w == -1 || (w == 'to' && action_to_be == ##Answer)); wn--; consult_words = wn - consult_from; if (consult_words == 0) return -1; if (action_to_be == ##Answer or ##Ask or ##Tell) { w = wn; wn = consult_from; parsed_number = NextWord(); if (parsed_number == 'the' && consult_words > 1) parsed_number = NextWord(); wn = w; return 1; } return 0; ]; ! ------------------------------------------------------------------------------ ! Final task: provide trivial routines if the user hasn't already: ! ------------------------------------------------------------------------------ Default Story 0; Default Headline 0; Default d_obj NULL; Default u_obj NULL; Stub AfterLife 0; Stub AfterPrompt 0; Stub Amusing 0; Stub BeforeParsing 0; Stub ChooseObjects 2; Stub DarkToDark 0; Stub DeathMessage 0; Stub Epilogue 0; Stub GamePostRoutine 0; Stub GamePreRoutine 0; Stub InScope 1; Stub LookRoutine 0; Stub NewRoom 0; Stub ObjectDoesNotFit 2; Stub ParseNumber 2; Stub ParserError 1; Stub PrintTaskName 1; Stub PrintVerb 1; Stub TimePasses 0; Stub UnknownVerb 1; #Ifdef TARGET_GLULX; Stub HandleGlkEvent 2; Stub IdentifyGlkObject 4; Stub InitGlkWindow 1; #Endif; ! TARGET_GLULX #Ifndef PrintRank; [ PrintRank; "."; ]; #Endif; #Ifndef ParseNoun; [ ParseNoun obj; obj = obj; return -1; ]; #Endif; #Ifdef INFIX; Include "infix"; #Endif; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_GRAMMAR; #Ifnot; ! LIBRARY_STAGE < AFTER_GRAMMAR but ~= AFTER_VERBLIB Message "Error: 'verblib' needs to be correctly included before including 'grammar'. This will cause a big number of errors!"; #Endif; #Ifnot; ! LIBRARY_STAGE >= AFTER_GRAMMAR : already included Message "Warning: 'grammar' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; #Ifnot; ! LIBRARY_STAGE is not defined Message "Error: 'parser', then 'verblib' need to be correctly included before including 'grammar'. This will cause a big number of errors!"; #Endif; ! ============================================================================== The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ! ============================================================================== ! VERBLIB: Front end to standard verbs library. ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! In your game file, Include three library files in this order: ! Include "Parser"; ! Include "VerbLib"; ! Include "Grammar"; ! ============================================================================== System_file; #Ifdef LIBRARY_STAGE; #Iffalse LIBRARY_STAGE >= AFTER_VERBLIB; ! if not already included #Iftrue LIBRARY_STAGE == AFTER_PARSER; ! if okay to include it ! ------------------------------------------------------------------------------ Default AMUSING_PROVIDED 1; Default MAX_CARRIED 100; Default MAX_SCORE 0; Default NUMBER_TASKS 1; Default OBJECT_SCORE 4; Default ROOM_SCORE 5; Default SACK_OBJECT 0; Default TASKS_PROVIDED 1; #Ifndef task_scores; Array task_scores -> 0 0 0 0; #Endif; Array task_done -> NUMBER_TASKS; #Ifndef LibraryMessages; Object LibraryMessages; #Endif; #Ifndef NO_PLACES; [ ObjectsSub; Objects1Sub(); ]; [ PlacesSub; Places1Sub(); ]; #Endif; ! NO_PLACES ! ------------------------------------------------------------------------------ ! Banner(), VersionSub(), and RunTimeError() are preempted by LanguageBanner(), ! LanguageVersionSub(), and LanguageError() respectfully. When converting ! this library to support a different natural language, these three latter ! functions should be created rather than editing this file. ! ------------------------------------------------------------------------------ [ Banner i; #Ifdef LanguageBanner; LanguageBanner(); i = 0; ! suppress warning #Ifnot; if (Story) { #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; print "^", (string) Story; #IfV5; style roman; #Endif; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Header); print "^", (string) Story; glk_set_style(style_Normal); #Endif; ! TARGET_ } if (Headline) print (string) Headline; #Ifdef TARGET_ZCODE; print "Release ", (HDR_GAMERELEASE-->0) & $03ff, " / Serial number "; for (i=0 : i<6 : i++) print (char) HDR_GAMESERIAL->i; #Ifnot; ! TARGET_GLULX; print "Release "; @aloads ROM_GAMERELEASE 0 i; print i; print " / Serial number "; for (i=0 : i<6 : i++) print (char) ROM_GAMESERIAL->i; #Endif; ! TARGET_ print " / Inform v"; inversion; print " Library v", (string) LibRelease, " "; #Ifdef STRICT_MODE; print "S"; #Endif; ! STRICT_MODE #Ifdef INFIX; print "X"; #Ifnot; #Ifdef DEBUG; print "D"; #Endif; ! DEBUG #Endif; ! INFIX new_line; #Endif; ! LanguageBanner ]; [ VersionSub ix; #Ifdef LanguageVersionSub; LanguageVersionSub(); ix = 0; ! suppress warning #Ifnot; Banner(); #Ifdef TARGET_ZCODE; ix = 0; ! shut up compiler warning if (standard_interpreter > 0) { print "Standard interpreter ", standard_interpreter/256, ".", standard_interpreter%256, " (", HDR_TERPNUMBER->0; #Iftrue (#version_number == 6); print (char) '.', HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print ") / "; } else { print "Interpreter ", HDR_TERPNUMBER->0, " Version "; #Iftrue (#version_number == 6); print HDR_TERPVERSION->0; #Ifnot; print (char) HDR_TERPVERSION->0; #Endif; print " / "; } #Ifnot; ! TARGET_GLULX; @gestalt 1 0 ix; print "Interpreter version ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; @gestalt 0 0 ix; print "VM ", ix / $10000, ".", (ix & $FF00) / $100, ".", ix & $FF, " / "; #Endif; ! TARGET_; print "Library serial number ", (string) LibSerial, "^"; #Ifdef LanguageVersion; print (string) LanguageVersion, "^"; #Endif; ! LanguageVersion #Endif; ! LanguageVersionSub ]; [ RunTimeError n p1 p2; #Ifdef LanguageError; LanguageError(n, p1, p2); #Ifnot; #Ifdef DEBUG; print "** Library error ", n, " (", p1, ", ", p2, ") **^** "; switch (n) { 1: print "preposition not found (this should not occur)"; 2: print "Property value not routine or string: ~", (property) p2, "~ of ~", (name) p1, "~ (", p1, ")"; 3: print "Entry in property list not routine or string: ~", (property) p2, "~ list of ~", (name) p1, "~ (", p1, ")"; 4: print "Too many timers/daemons are active simultaneously. The limit is the library constant MAX_TIMERS (currently ", MAX_TIMERS, ") and should be increased"; 5: print "Object ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 7: print "The object ~", (name) p1, "~ can only be used as a player object if it has the ~number~ property"; 8: print "Attempt to take random entry from an empty table array"; 9: print p1, " is not a valid direction property number"; 10: print "The player-object is outside the object tree"; 11: print "The room ~", (name) p1, "~ has no ~", (property) p2, "~ property"; 12: print "Tried to set a non-existent pronoun using SetPronoun"; 13: print "A 'topic' token can only be followed by a preposition"; 14: print "Overflowed buffer limit of ", p1, " using '@@64output_stream 3' ", (string) p2; 15: print "LoopWithinObject broken because the object ", (name) p1, " was moved while the loop passed through it."; 16: print "Attempt to use illegal narrative_voice of ", p1, "."; default: print "(unexplained)"; } " **"; #Ifnot; "** Library error ", n, " (", p1, ", ", p2, ") **"; #Endif; ! DEBUG #Endif; ! LanguageError ]; ! ---------------------------------------------------------------------------- ! The WriteListFrom routine, a flexible object-lister taking care of ! plurals, inventory information, various formats and so on. This is used ! by everything in the library which ever wants to list anything. ! ! If there were no objects to list, it prints nothing and returns false; ! otherwise it returns true. ! ! o is the object, and style is a bitmap, whose bits are given by: ! ---------------------------------------------------------------------------- Constant NEWLINE_BIT $0001; ! New-line after each entry Constant INDENT_BIT $0002; ! Indent each entry by depth Constant FULLINV_BIT $0004; ! Full inventory information after entry Constant ENGLISH_BIT $0008; ! English sentence style, with commas and and Constant RECURSE_BIT $0010; ! Recurse downwards with usual rules Constant ALWAYS_BIT $0020; ! Always recurse downwards Constant TERSE_BIT $0040; ! More terse English style Constant PARTINV_BIT $0080; ! Only brief inventory information after entry Constant DEFART_BIT $0100; ! Use the definite article in list Constant WORKFLAG_BIT $0200; ! At top level (only), only list objects ! which have the "workflag" attribute Constant ISARE_BIT $0400; ! Print " is" or " are" before list Constant CONCEAL_BIT $0800; ! Omit objects with "concealed" or "scenery": ! if WORKFLAG_BIT also set, then does _not_ ! apply at top level, but does lower down Constant NOARTICLE_BIT $1000; ! Print no articles, definite or not Constant ID_BIT $2000; ! Print object id after each entry [ NextEntry o odepth; for (::) { o = sibling(o); if (o == 0) return 0; if (lt_value && o.list_together ~= lt_value) continue; if (c_style & WORKFLAG_BIT && odepth==0 && o hasnt workflag) continue; if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) continue; return o; } ]; [ WillRecurs o; if (c_style & ALWAYS_BIT) rtrue; if (c_style & RECURSE_BIT == 0) rfalse; if ((o has transparent or supporter) || (o has container && o has open)) rtrue; rfalse; ]; [ ListEqual o1 o2; if (child(o1) && WillRecurs(o1)) rfalse; if (child(o2) && WillRecurs(o2)) rfalse; if (c_style & (FULLINV_BIT + PARTINV_BIT)) { if ((o1 hasnt worn && o2 has worn) || (o2 hasnt worn && o1 has worn)) rfalse; if ((o1 hasnt light && o2 has light) || (o2 hasnt light && o1 has light)) rfalse; if (o1 has container) { if (o2 hasnt container) rfalse; if ((o1 has open && o2 hasnt open) || (o2 has open && o1 hasnt open)) rfalse; } else if (o2 has container) rfalse; } return Identical(o1, o2); ]; [ SortTogether obj value; ! print "Sorting together possessions of ", (object) obj, " by value ", value, "^"; ! for (x=child(obj) : x : x=sibling(x)) ! print (the) x, " no: ", x, " lt: ", x.list_together, "^"; while (child(obj)) { if (child(obj).list_together ~= value) move child(obj) to out_obj; else move child(obj) to in_obj; } while (child(in_obj)) move child(in_obj) to obj; while (child(out_obj)) move child(out_obj) to obj; ]; [ SortOutList obj i k l; ! print "^^Sorting out list from ", (name) obj, "^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; .AP_SOL; for (i=obj : i : i=sibling(i)) { k = i.list_together; if (k ~= 0) { ! print "Scanning ", (name) i, " with lt=", k, "^"; for (i=sibling(i) : i && i.list_together == k :) i = sibling(i); if (i == 0) rfalse; ! print "First not in block is ", (name) i, " with lt=", i.list_together, "^"; for (l=sibling(i) : l : l=sibling(l)) if (l.list_together == k) { SortTogether(parent(obj), k); ! print "^^After ST:^ "; ! for (i=child(location) : i : i=sibling(i)) ! print (name) i, " --> "; ! new_line; obj = child(parent(obj)); jump AP_SOL; } } } ]; #Ifdef TARGET_ZCODE; [ Print__Spaces n; ! To avoid a bug occurring in Inform 6.01 to 6.10 if (n == 0) return; spaces n; ]; #Ifnot; ! TARGET_GLULX; [ Print__Spaces n; while (n > 0) { @streamchar ' '; n = n - 1; } ]; #Endif; ! TARGET_ [ WriteListFrom o style depth s1 s2 s3 s4 s5 s6; if (o == nothing) return 0; s1 = c_style; s2 = lt_value; s3 = listing_together; s4 = listing_size; s5 = wlf_indent; s6 = inventory_stage; if (o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } c_style = style; wlf_indent = 0; if (WriteListR(o, depth) == 0) return 0; c_style = s1; lt_value = s2; listing_together = s3; listing_size = s4; wlf_indent = s5; inventory_stage = s6; rtrue; ]; [ WriteListR o depth stack_pointer classes_p sizes_p i j k k2 l m n q senc mr; if (depth > 0 && o == child(parent(o))) { SortOutList(o); o = child(parent(o)); } for (::) { if (o == 0) rfalse; if (c_style & WORKFLAG_BIT && depth==0 && o hasnt workflag) { o = sibling(o); continue; } if (c_style & CONCEAL_BIT && (o has concealed || o has scenery)) { o = sibling(o); continue; } break; } classes_p = match_classes + stack_pointer; sizes_p = match_list + stack_pointer; for (i=o,j=0 : i && (j+stack_pointer)<128 : i=NextEntry(i,depth),j++) { classes_p->j = 0; if (i.plural) k++; } if (c_style & ISARE_BIT) { if (j == 1 && o hasnt pluralname) Tense(IS__TX, WAS__TX); else Tense(ARE__TX, WERE__TX); if (c_style & NEWLINE_BIT) print ":^"; else print (char) ' '; c_style = c_style - ISARE_BIT; } stack_pointer = stack_pointer+j+1; if (k < 2) jump EconomyVersion; ! It takes two to plural n = 1; for (i=o,k=0 : kk == 0) { classes_p->k = n; sizes_p->n = 1; for (l=NextEntry(i,depth),m=k+1 : l && mm == 0 && i.plural && l.plural ~= 0) { if (ListEqual(i, l) == 1) { sizes_p->n = sizes_p->n + 1; classes_p->m = n; } } n++; } n--; for (i=1,j=o,k=0 : i<=n : i++,senc++) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } m = sizes_p->i; if (j == 0) mr = 0; else { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together == mr) senc--; mr = j.list_together; } } senc--; for (i=1,j=o,k=0,mr=0 : senc>=0 : i++,senc--) { while (((classes_p->k) ~= i) && ((classes_p->k) ~= -i)) { k++; j=NextEntry(j, depth); } if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { senc++; jump Omit_FL2; } k2 = NextEntry(j, depth); if (k2 == 0 || k2.list_together ~= j.list_together) jump Omit_WL2; k2 = metaclass(j.list_together); if (k2 == Routine or String) { q = j; listing_size = 1; l = k; m = i; while (m < n && q.list_together == j.list_together) { m++; while (((classes_p->l) ~= m) && ((classes_p->l) ~= -m)) { l++; q = NextEntry(q, depth); } if (q.list_together == j.list_together) listing_size++; } ! print " [", listing_size, "] "; if (listing_size == 1) jump Omit_WL2; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k2 == String) { q = 0; for (l=0 : l(l+i); EnglishNumber(q); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k2 ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist2; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k2 == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist2; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL2; } } .Omit_WL2; if (WriteBeforeEntry(j, depth, 0, senc) == 1) jump Omit_FL2; if (sizes_p->i == 1) { if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; } else { if (c_style & DEFART_BIT) PrefaceByArticle(j, 1, sizes_p->i); print (number) sizes_p->i, " "; PrintOrRun(j, plural, 1); } if (sizes_p->i > 1 && j hasnt pluralname) { give j pluralname; WriteAfterEntry(j, depth, stack_pointer); give j ~pluralname; } else { WriteAfterEntry(j,depth,stack_pointer); } .Omit_EL2; if (c_style & ENGLISH_BIT) { if (senc == 1) print (SerialComma) i+senc, (string) AND__TX; if (senc > 1) print (string) COMMA__TX; } .Omit_FL2; } rtrue; .EconomyVersion; n = j; for (i=1,j=o : i<=n : j=NextEntry(j,depth),i++,senc++) { if (j.list_together ~= 0 or lt_value && metaclass(j.list_together) == Routine or String && j.list_together==mr) senc--; mr = j.list_together; } for (i=1,j=o,mr=0 : i<=senc : j=NextEntry(j,depth),i++) { if (j.list_together ~= 0 or lt_value) { if (j.list_together == mr) { i--; jump Omit_FL; } k = NextEntry(j, depth); if (k == 0 || k.list_together ~= j.list_together) jump Omit_WL; k = metaclass(j.list_together); if (k == Routine or String) { if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (k == String) { q = j; l = 0; do { q = NextEntry(q, depth); l++; } until (q == 0 || q.list_together ~= j.list_together); EnglishNumber(l); print " "; print (string) j.list_together; if (c_style & ENGLISH_BIT) print " ("; if (c_style & INDENT_BIT) print ":^"; } q = c_style; if (k ~= String) { inventory_stage = 1; parser_one = j; parser_two = depth+wlf_indent; if (RunRoutines(j, list_together) == 1) jump Omit__Sublist; } #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; ! TARGET_; lt_value = j.list_together; listing_together = j; wlf_indent++; WriteListR(j, depth, stack_pointer); wlf_indent--; #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; ! TARGET_; if (k == String) { if (q & ENGLISH_BIT) print ")"; } else { inventory_stage = 2; parser_one = j; parser_two = depth+wlf_indent; RunRoutines(j, list_together); } .Omit__Sublist; if (q & NEWLINE_BIT && c_style & NEWLINE_BIT == 0) new_line; c_style = q; mr = j.list_together; jump Omit_EL; } } .Omit_WL; if (WriteBeforeEntry(j, depth, i, senc) == 1) jump Omit_FL; if (c_style & NOARTICLE_BIT) print (name) j; else { if (c_style & DEFART_BIT) print (the) j; else print (a) j; } if (c_style & ID_BIT) print " (", j, ")"; WriteAfterEntry(j, depth, stack_pointer); .Omit_EL; if (c_style & ENGLISH_BIT) { if (i == senc-1) print (SerialComma) senc, (string) AND__TX; if (i < senc-1) print (string) COMMA__TX; } .Omit_FL; } ]; ! end of WriteListR [ WriteBeforeEntry o depth ipos sentencepos flag; inventory_stage = 1; if (c_style & INDENT_BIT) Print__Spaces(2*(depth+wlf_indent)); if (o.invent && (c_style & (PARTINV_BIT|FULLINV_BIT))) { flag = PrintOrRun(o, invent, 1); if (flag) { if (c_style & ENGLISH_BIT) { if (ipos == sentencepos-1) print (SerialComma) sentencepos, (string) AND__TX; if (ipos < sentencepos-1) print (string) COMMA__TX; } if (c_style & NEWLINE_BIT) new_line; } } return flag; ]; [ WriteAfterEntry o depth stack_p p recurse_flag parenth_flag eldest_child child_count combo i j; inventory_stage = 2; if (c_style & PARTINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; combo = 0; if (o has light && location hasnt light) combo=combo+1; if (o has container && o hasnt open) combo=combo+2; if ((o has container && (o has open || o has transparent))) { objectloop(i in o) { if (i hasnt concealed && i hasnt scenery) { j = true; break; } } if (~~j) combo=combo+4; } if (combo) L__M(##ListMiscellany, combo, o); } ! end of PARTINV_BIT processing if (c_style & FULLINV_BIT) { if (o.invent && RunRoutines(o, invent)) if (c_style & NEWLINE_BIT) ""; else rtrue; if (o has light && o has worn) { L__M(##ListMiscellany, 8, o); parenth_flag = true; } else { if (o has light) { L__M(##ListMiscellany, 9, o); parenth_flag = true; } if (o has worn) { L__M(##ListMiscellany, 10, o); parenth_flag = true; } } if (o has container) if (o has openable) { if (parenth_flag) print (string) AND__TX; else L__M(##ListMiscellany, 11, o); if (o has open) if (child(o)) L__M(##ListMiscellany, 12, o); else L__M(##ListMiscellany, 13, o); else if (o has lockable && o has locked) L__M(##ListMiscellany, 15, o); else L__M(##ListMiscellany, 14, o); parenth_flag = true; } else if (child(o)==0 && o has transparent) if (parenth_flag) L__M(##ListMiscellany, 16, o); else L__M(##ListMiscellany, 17, o); if (parenth_flag) print ")"; } ! end of FULLINV_BIT processing if (c_style & CONCEAL_BIT) { child_count = 0; objectloop (p in o) if (p hasnt concealed && p hasnt scenery) { child_count++; eldest_child = p; } } else { child_count = children(o); eldest_child = child(o); } if (child_count && (c_style & ALWAYS_BIT)) { if (c_style & ENGLISH_BIT) L__M(##ListMiscellany, 18, o); recurse_flag = true; } if (child_count && (c_style & RECURSE_BIT)) { if (o has supporter) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 19, o); else L__M(##ListMiscellany, 20, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } if (o has container && (o has open || o has transparent)) { if (c_style & ENGLISH_BIT) { if (c_style & TERSE_BIT) L__M(##ListMiscellany, 21, o); else L__M(##ListMiscellany, 22, o); if (o has animate) print (string) WHOM__TX; else print (string) WHICH__TX; } recurse_flag = true; } } if (recurse_flag && (c_style & ENGLISH_BIT)) if (child_count > 1 || eldest_child has pluralname) Tense(ARE2__TX, WERE2__TX); else Tense(IS2__TX, WAS2__TX); if (c_style & NEWLINE_BIT) new_line; if (recurse_flag) { o = child(o); #Ifdef TARGET_ZCODE; @push lt_value; @push listing_together; @push listing_size; #Ifnot; ! TARGET_GLULX; @copy lt_value sp; @copy listing_together sp; @copy listing_size sp; #Endif; lt_value = 0; listing_together = 0; listing_size = 0; WriteListR(o, depth+1, stack_p); #Ifdef TARGET_ZCODE; @pull listing_size; @pull listing_together; @pull lt_value; #Ifnot; ! TARGET_GLULX; @copy sp listing_size; @copy sp listing_together; @copy sp lt_value; #Endif; if (c_style & TERSE_BIT) print ")"; } ]; ! ---------------------------------------------------------------------------- ! LoopWithinObject(rtn,obj,arg) ! ! rtn is the address of a user-supplied routine. ! obj is an optional parent object whose dependents are to be processed; the ! default is the current actor (normally the player). ! arg is an optional argument passed to the rtn; this can be a single variable ! or constant, or the address of an array (which enables multiple values to be ! passed and returned). ! ! For each object o which is a child, grandchild, great-grandchild, etc, of the ! original obj, LoopWithinObject() calls rtn(o,arg). ! ! The rtn should perform any appropriate testing or processing on each object o, ! using the optional arg value if necessary. If the rtn returns true (or any ! positive value), the children of o, if any, are also tested; those children ! are skipped if rtn returns false. To terminate the loop before all objects ! have been processed, rtn should return a large negative number (eg -99). ! ! To deal with supporters and open containers, so that objects are processed ! only if they are accessible to the player, rtn might end with these ! statements: ! if ((o has transparent or supporter) || (o has container && o has open)) rtrue; ! rfalse; ! or alternatively with: ! c_style = RECURSE_BIT; return WillRecurs(o); ! ! LoopWithinObject() returns the number of objects which have been processed. ! ---------------------------------------------------------------------------- [ LoopWithinObject rtn obj arg n o x y; if (obj == 0) obj = actor; o = child(obj); while (o) { y = parent(o); n++; x = rtn(o, arg); ! user-supplied routine returning x. ! if x < 0: skip up to next parent ! if x = 0: jump across to next sibling ! if x > 0: continue down to child objects if (y ~= parent(o)) { RunTimeError(15, o); rfalse; } if (x > 0 && child(o)) o = child(o); else while (o) { if (++x > 0 && sibling(o)) { o = sibling(o); break; } o = parent(o); if (o == obj) return n; } } ]; ! ---------------------------------------------------------------------------- ! Much better menus can be created using one of the optional library ! extensions. These are provided for compatibility with previous practice: ! ---------------------------------------------------------------------------- [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j; menu_nesting++; .LKRD; menu_item = 0; lines = EntryR(); main_title = item_name; print "--- "; print (string) main_title; print " ---^^"; if (menu_choices ofclass Routine) menu_choices(); else print (string) menu_choices; for (::) { L__M(##Miscellany, 52, lines); print "> "; #Ifdef TARGET_ZCODE; #IfV3; read buffer parse; #Ifnot; read buffer parse DrawStatusLine; #Endif; ! V3 j = parse->1; ! number of words #Ifnot; ! TARGET_GLULX; KeyboardPrimitive(buffer, parse); j = parse-->0; ! number of words #Endif; ! TARGET_ i = parse-->1; if (j == 0 || (i == QUIT1__WD or QUIT2__WD)) { menu_nesting--; if (menu_nesting > 0) rfalse; if (deadflag == 0) <>; rfalse; } i = TryNumber(1); if (i == 0) jump LKRD; if (i < 1 || i > lines) continue; menu_item = i; j = ChoiceR(); if (j == 2) jump LKRD; if (j == 3) rfalse; } ]; #Ifdef TARGET_ZCODE; #IfV3; [ DoMenu menu_choices EntryR ChoiceR; LowKey_Menu(menu_choices, EntryR, ChoiceR); ]; #Endif; ! V3 #IfV5; [ DoMenu menu_choices EntryR ChoiceR lines main_title main_wid cl i j oldcl pkey ch cw y x; if (pretty_flag == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 7; .ReDisplay; oldcl = 0; @erase_window $ffff; #Iftrue (#version_number == 6); @set_cursor -1; ch = HDR_FONTWUNITS->0; #Ifnot; ch = 1; #Endif; i = ch * (lines+7); @split_window i; i = HDR_SCREENWCHARS->0; if (i == 0) i = 80; @set_window 1; @set_cursor 1 1; #Iftrue (#version_number == 6); @set_font 4 -> cw; cw = HDR_FONTHUNITS->0; #Ifnot; cw = 1; #Endif; style reverse; spaces(i); j=1+(i/2-main_wid)*cw; @set_cursor 1 j; print (string) main_title; y=1+ch; @set_cursor y 1; spaces(i); x=1+cw; @set_cursor y x; print (string) NKEY__TX; j=1+(i-13)*cw; @set_cursor y j; print (string) PKEY__TX; y=y+ch; @set_cursor y 1; spaces(i); @set_cursor y x; print (string) RKEY__TX; j=1+(i-18)*cw; @set_cursor y j; if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; style roman; y = y+2*ch; @set_cursor y x; font off; if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); x = 1+3*cw; for (::) { if (cl ~= oldcl) { if (oldcl>0) { y=1+(oldcl-1)*ch; @set_cursor y x; print " "; } y=1+(cl-1)*ch; @set_cursor y x; print ">"; } oldcl = cl; @read_char 1 -> pkey; if (pkey == NKEY1__KY or NKEY2__KY or 130) { cl++; if (cl == 7+lines) cl = 7; continue; } if (pkey == PKEY1__KY or PKEY2__KY or 129) { cl--; if (cl == 6) cl = 6+lines; continue; } if (pkey == QKEY1__KY or QKEY2__KY or 27 or 131) break; if (pkey == 10 or 13 or 132) { @set_window 0; font on; new_line; new_line; new_line; menu_item = cl-6; EntryR(); @erase_window $ffff; @split_window ch; i = HDR_SCREENWCHARS->0; if ( i== 0) i = 80; @set_window 1; @set_cursor 1 1; style reverse; spaces(i); j=1+(i/2-item_width)*cw; @set_cursor 1 j; print (string) item_name; style roman; @set_window 0; new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); @read_char 1 -> pkey; jump ReDisplay; } } menu_nesting--; if (menu_nesting > 0) rfalse; font on; @set_cursor 1 1; @erase_window $ffff; @set_window 0; #Iftrue (#version_number == 6); @set_cursor -2; #Endif; new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! V5 #Ifnot; ! TARGET_GLULX [ DoMenu menu_choices EntryR ChoiceR winwid winhgt lines main_title main_wid cl i oldcl pkey; if (pretty_flag == 0 || gg_statuswin == 0) return LowKey_Menu(menu_choices, EntryR, ChoiceR); menu_nesting++; menu_item = 0; lines = EntryR(); main_title = item_name; main_wid = item_width; cl = 0; ! If we printed "hit arrow keys" here, it would be appropriate to ! check for the availability of Glk input keys. But we actually ! print "hit N/P/Q". So it's reasonable to silently accept Glk ! arrow key codes as secondary options. .ReDisplay; glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(lines+7); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-main_wid, 0); print (string) main_title; glk_window_move_cursor(gg_statuswin, 1, 1); print (string) NKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-13, 1); print (string) PKEY__TX; glk_window_move_cursor(gg_statuswin, 1, 2); print (string) RKEY__TX; glk_window_move_cursor(gg_statuswin, winwid-18, 2); if (menu_nesting == 1) print (string) QKEY1__TX; else print (string) QKEY2__TX; glk_set_style(style_Normal); glk_window_move_cursor(gg_statuswin, 1, 4); if (menu_choices ofclass String) print (string) menu_choices; else menu_choices(); oldcl = -1; for (::) { if (cl ~= oldcl) { if (cl < 0 || cl >= lines) cl = 0; if (oldcl >= 0) { glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) ' '; } oldcl = cl; glk_window_move_cursor(gg_statuswin, 3, oldcl+6); print (char) '>'; } pkey = KeyCharPrimitive(gg_statuswin, true); if (pkey == $80000000) jump ReDisplay; if (pkey == NKEY1__KY or NKEY2__KY or $fffffffb) { cl++; if (cl >= lines) cl = 0; continue; } if (pkey == PKEY1__KY or PKEY2__KY or $fffffffc) { cl--; if (cl < 0) cl = lines-1; continue; } if (pkey == QKEY1__KY or QKEY2__KY or $fffffff8 or $fffffffe) break; if (pkey == $fffffffa or $fffffffd) { glk_set_window(gg_mainwin); new_line; new_line; new_line; menu_item = cl+1; EntryR(); glk_window_clear(gg_statuswin); glk_window_clear(gg_mainwin); glk_set_window(gg_statuswin); StatusLineHeight(1); glk_window_get_size(gg_statuswin, gg_arguments, gg_arguments+4); winwid = gg_arguments-->0; winhgt = gg_arguments-->1; glk_set_style(style_Subheader); glk_window_move_cursor(gg_statuswin, winwid/2-item_width, 0); print (string) item_name; glk_set_style(style_Normal); glk_set_window(gg_mainwin); new_line; i = ChoiceR(); if (i == 2) jump ReDisplay; if (i == 3) break; L__M(##Miscellany, 53); pkey = KeyCharPrimitive(gg_mainwin, 1); jump ReDisplay; } } ! done with this menu... menu_nesting--; if (menu_nesting > 0) rfalse; glk_set_window(gg_mainwin); glk_window_clear(gg_mainwin); new_line; new_line; new_line; if (deadflag == 0) <>; ]; #Endif; ! TARGET_ ! ---------------------------------------------------------------------------- ! A cunning routine (which could have been a daemon, but isn't, for the ! sake of efficiency) to move objects which could be in many rooms about ! so that the player never catches one not in place ! ---------------------------------------------------------------------------- [ MoveFloatingObjects i k l m address flag; if (location == player or nothing) return; objectloop (i) { address = i.&found_in; if (address && i hasnt non_floating && ~~IndirectlyContains(player, i)) { if (metaclass(address-->0) == Routine) flag = i.found_in(); else { flag = false; k = i.#found_in/WORDSIZE; for (l=0 : ll; if ((m in Class && location ofclass m) || m == location || m in location) { flag = true; break; } } } if (flag) { if (i notin location) move i to location; } else { if (parent(i)) remove i; } } } ]; ! ---------------------------------------------------------------------------- ! Two little routines for moving the player safely. ! ---------------------------------------------------------------------------- [ PlayerTo newplace flag; NoteDeparture(); move player to newplace; while (parent(newplace)) newplace = parent(newplace); location = real_location = newplace; MoveFloatingObjects(); AdjustLight(1); switch (flag) { 0: ; 1: NoteArrival(); ScoreArrival(); 2: LookSub(1); } ]; [ MovePlayer direc; ; ; ]; ! ---------------------------------------------------------------------------- ! The handy YesOrNo routine, and some "meta" verbs ! ---------------------------------------------------------------------------- [ YesOrNo noStatusRedraw i j; for (::) { #Ifdef TARGET_ZCODE; if (location == nothing || parent(player) == nothing || noStatusRedraw) read buffer parse; else read buffer parse DrawStatusLine; j = parse->1; #Ifnot; ! TARGET_GLULX; noStatusRedraw = 0; ! suppress warning KeyboardPrimitive(buffer, parse); j = parse-->0; #Endif; ! TARGET_ if (j) { ! at least one word entered i = parse-->1; if (i == YES1__WD or YES2__WD or YES3__WD) rtrue; if (i == NO1__WD or NO2__WD or NO3__WD) rfalse; } L__M(##Quit, 1); print "> "; } ]; #Ifdef TARGET_ZCODE; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart, 1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub; restore Rmaybe; return L__M(##Restore, 1); .RMaybe; L__M(##Restore, 2); ]; [ SaveSub flag; #IfV5; @save -> flag; switch (flag) { 0: L__M(##Save, 1); 1: L__M(##Save, 2); 2: RestoreColours(); L__M(##Restore, 2); } #Ifnot; save Smaybe; return L__M(##Save, 1); .SMaybe; L__M(##Save, 2); #Endif; ! V5 ]; [ VerifySub; @verify ?Vmaybe; jump Vwrong; .Vmaybe; return L__M(##Verify, 1); .Vwrong; L__M(##Verify, 2); ]; [ ScriptOnSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode) return L__M(##ScriptOn, 1); @output_stream 2; if (((HDR_GAMEFLAGS-->0) & 1) == 0) return L__M(##ScriptOn, 3); L__M(##ScriptOn, 2); transcript_mode = true; ]; [ ScriptOffSub; transcript_mode = ((HDR_GAMEFLAGS-->0) & 1); if (transcript_mode == false) return L__M(##ScriptOff, 1); L__M(##ScriptOff, 2); @output_stream -2; if ((HDR_GAMEFLAGS-->0) & 1) return L__M(##ScriptOff, 3); transcript_mode = false; ]; [ CommandsOnSub; @output_stream 4; xcommsdir = 1; L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (xcommsdir == 1) @output_stream -4; xcommsdir = 0; L__M(##CommandsOff, 1); ]; [ CommandsReadSub; @input_stream 1; xcommsdir = 2; L__M(##CommandsRead, 1); ]; #Ifnot; ! TARGET_GLULX; [ QuitSub; L__M(##Quit, 2); if (YesOrNo()) quit; ]; [ RestartSub; L__M(##Restart,1); if (YesOrNo()) { @restart; L__M(##Restart, 2); } ]; [ RestoreSub res fref; fref = glk_fileref_create_by_prompt($01, $02, 0); if (fref == 0) jump RFailed; gg_savestr = glk_stream_open_file(fref, $02, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump RFailed; @restore gg_savestr res; glk_stream_close(gg_savestr, 0); gg_savestr = 0; .RFailed; L__M(##Restore, 1); ]; [ SaveSub res fref; fref = glk_fileref_create_by_prompt($01, $01, 0); if (fref == 0) jump SFailed; gg_savestr = glk_stream_open_file(fref, $01, GG_SAVESTR_ROCK); glk_fileref_destroy(fref); if (gg_savestr == 0) jump SFailed; @save gg_savestr res; if (res == -1) { ! The player actually just typed "restore". We're going to print ! L__M(##Restore,2); the Z-Code Inform library does this correctly ! now. But first, we have to recover all the Glk objects; the values ! in our global variables are all wrong. GGRecoverObjects(); glk_stream_close(gg_savestr, 0); gg_savestr = 0; return L__M(##Restore, 2); } glk_stream_close(gg_savestr, 0); gg_savestr = 0; if (res == 0) return L__M(##Save, 2); .SFailed; L__M(##Save, 1); ]; [ VerifySub res; @verify res; if (res == 0) return L__M(##Verify, 1); L__M(##Verify, 2); ]; [ ScriptOnSub; if (gg_scriptstr) return L__M(##ScriptOn, 1); if (gg_scriptfref == 0) { gg_scriptfref = glk_fileref_create_by_prompt($102, $05, GG_SCRIPTFREF_ROCK); if (gg_scriptfref == 0) jump S1Failed; } gg_scriptstr = glk_stream_open_file(gg_scriptfref, $05, GG_SCRIPTSTR_ROCK); if (gg_scriptstr == 0) jump S1Failed; glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); L__M(##ScriptOn, 2); VersionSub(); return; .S1Failed; L__M(##ScriptOn, 3); ]; [ ScriptOffSub; if (gg_scriptstr == 0) return L__M(##ScriptOff,1); L__M(##ScriptOff, 2); glk_stream_close(gg_scriptstr, 0); gg_scriptstr = 0; ]; [ CommandsOnSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsOn, 2); else return L__M(##CommandsOn, 3); } fref = glk_fileref_create_by_prompt($103, $01, 0); if (fref == 0) return L__M(##CommandsOn, 4); gg_command_reading = false; gg_commandstr = glk_stream_open_file(fref, $01, GG_COMMANDWSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsOn, 4); L__M(##CommandsOn, 1); ]; [ CommandsOffSub; if (gg_commandstr == 0) return L__M(##CommandsOff, 2); if (gg_command_reading) return L__M(##CommandsRead, 5); glk_stream_close(gg_commandstr, 0); gg_commandstr = 0; gg_command_reading = false; L__M(##CommandsOff, 1); ]; [ CommandsReadSub fref; if (gg_commandstr) { if (gg_command_reading) return L__M(##CommandsRead, 2); else return L__M(##CommandsRead, 3); } fref = glk_fileref_create_by_prompt($103, $02, 0); if (fref == 0) return L__M(##CommandsRead, 4); gg_command_reading = true; gg_commandstr = glk_stream_open_file(fref, $02, GG_COMMANDRSTR_ROCK); glk_fileref_destroy(fref); if (gg_commandstr == 0) return L__M(##CommandsRead, 4); L__M(##CommandsRead, 1); ]; #Endif; ! TARGET_; [ NotifyOnSub; notify_mode = true; L__M(##NotifyOn); ]; [ NotifyOffSub; notify_mode = false; L__M(##NotifyOff); ]; [ Places1Sub i j k; L__M(##Places, 1); objectloop (i has visited) j++; objectloop (i has visited) { print (name) i; k++; if (k == j) return L__M(##Places, 2); if (k == j-1) print (SerialComma) j, (string) AND__TX; else print (string) COMMA__TX; } ]; [ Objects1Sub i j f; L__M(##Objects, 1); objectloop (i has moved) { f = 1; print (the) i; j = parent(i); if (j) { if (j == player) { if (i has worn) L__M(##Objects, 3, j, i); else L__M(##Objects, 4, j, i); jump Obj__Ptd; } if (j has animate) { L__M(##Objects, 5, j, i); jump Obj__Ptd; } if (j has visited) { L__M(##Objects, 6, j, i); jump Obj__Ptd; } if (j has container) { L__M(##Objects, 8, j, i); jump Obj__Ptd; } if (j has supporter) { L__M(##Objects, 9, j, i); jump Obj__Ptd; } if (j has enterable) { L__M(##Objects, 7, j, i); jump Obj__Ptd; } } L__M(##Objects, 10, j, i); .Obj__Ptd; new_line; } if (f == 0) L__M(##Objects, 2); ]; ! ---------------------------------------------------------------------------- ! The scoring system ! ---------------------------------------------------------------------------- [ ScoreSub; #Ifdef NO_SCORE; if (deadflag == 0) L__M(##Score, 2); #Ifnot; if (deadflag) new_line; L__M(##Score, 1); if(PrintRank() == false) LibraryExtensions.RunAll(ext_printrank); #Endif; ! NO_SCORE ]; #Ifndef TaskScore; [ TaskScore i; return task_scores->i; ]; #Endif; [ Achieved num; if (task_done->num == 0) { task_done->num = 1; score = score + TaskScore(num); } ]; [ PANum m n; print " "; n = m; if (n < 0) { n = -m; n = n*10; } if (n < 10) { print " "; jump Panuml; } if (n < 100) { print " "; jump Panuml; } if (n < 1000) { print " "; } .Panuml; print m, " "; ]; [ FullScoreSub i; ScoreSub(); if (score == 0 || TASKS_PROVIDED == 1) rfalse; new_line; L__M(##FullScore, 1); for (i=0 : ii == 1) { PANum(TaskScore(i)); if(PrintTaskName(i) == false) LibraryExtensions.RunAll(ext_printtaskname,i); } if (things_score) { PANum(things_score); L__M(##FullScore, 2); } if (places_score) { PANum(places_score); L__M(##FullScore, 3); } new_line; PANum(score); L__M(##FullScore, 4); ]; ! ---------------------------------------------------------------------------- ! Real verbs start here: Inventory ! ---------------------------------------------------------------------------- [ InvWideSub; if (actor == player) inventory_style = ENGLISH_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = ENGLISH_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvTallSub; if (actor == player) inventory_style = NEWLINE_BIT+INDENT_BIT+FULLINV_BIT+RECURSE_BIT; else inventory_style = NEWLINE_BIT+INDENT_BIT+PARTINV_BIT; ; inventory_style = 0; ]; [ InvSub x; if (child(actor) == 0) return L__M(##Inv, 1); if (inventory_style == 0) if (actor == player) return InvTallSub(); else return InvWideSub(); L__M(##Inv, 2); if (inventory_style & NEWLINE_BIT) L__M(##Inv, 3); else print " "; WriteListFrom(child(actor), inventory_style, 1); if (inventory_style & ENGLISH_BIT) L__M(##Inv, 4); #Ifndef MANUAL_PRONOUNS; objectloop (x in player) PronounNotice(x); #Endif; x = 0; ! To prevent a "not used" error AfterRoutines(); ]; ! ---------------------------------------------------------------------------- ! The object tree and determining the possibility of moves ! ---------------------------------------------------------------------------- [ CommonAncestor o1 o2 i j; ! Find the nearest object indirectly containing o1 and o2, ! or return 0 if there is no common ancestor. i = o1; while (i) { j = o2; while (j) { if (j == i) return i; j = parent(j); } i = parent(i); } return 0; ]; [ IndirectlyContains o1 o2; ! Does o1 indirectly contain o2? (Same as testing if their common ancestor is o1.) while (o2) { if (o1 == o2) rtrue; if (o2 ofclass Class) rfalse; o2 = parent(o2); } rfalse; ]; [ ObjectScopedBySomething item i j k l m; i = item; objectloop (j .& add_to_scope) { l = j.&add_to_scope; k = (j.#add_to_scope)/WORDSIZE; if (l-->0 ofclass Routine) continue; for (m=0 : mm == i) return j; } rfalse; ]; [ ObjectIsUntouchable item flag1 flag2 ancestor i; ! Determine if there's any barrier preventing the actor from moving ! things to "item". Return false if no barrier; otherwise print a ! suitable message and return true. ! If flag1 is set, do not print any message. ! If flag2 is set, also apply Take/Remove restrictions. ! If the item has been added to scope by something, it's first necessary ! for that something to be touchable. ancestor = CommonAncestor(actor, item); if (ancestor == 0) { ancestor = item; while (ancestor && (i = ObjectScopedBySomething(ancestor)) == 0) ancestor = parent(ancestor); if (i) { if (ObjectIsUntouchable(i, flag1, flag2)) return; ! An item immediately added to scope } } else ! First, a barrier between the actor and the ancestor. The actor ! can only be in a sequence of enterable objects, and only closed ! containers form a barrier. if (actor ~= ancestor) { i = parent(actor); while (i ~= ancestor) { if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } ! Second, a barrier between the item and the ancestor. The item can ! be carried by someone, part of a piece of machinery, in or on top ! of something and so on. i = parent(item); if (item ~= ancestor && i ~= player) { while (i ~= ancestor) { if (flag2 && i hasnt container && i hasnt supporter) { if (i has animate) { if (flag1) rtrue; return L__M(##Take, 6, i, noun); } if (i has transparent) { if (flag1) rtrue; return L__M(##Take, 7, i, noun); } if (flag1) rtrue; return L__M(##Take, 8, item, noun); } if (i has container && i hasnt open) { if (flag1) rtrue; return L__M(##Take, 9, i, noun); } i = parent(i); } } rfalse; ]; [ AttemptToTakeObject item ancestor after_recipient i k; ! Try to transfer the given item to the actor: return false ! if successful, true if unsuccessful, printing a suitable message ! in the latter case. ! People cannot ordinarily be taken. if (item == actor) return L__M(##Take, 2, noun); if (item has animate) return L__M(##Take, 3, item); ancestor = CommonAncestor(actor, item); if (ancestor == 0) { i = ObjectScopedBySomething(item); if (i) ancestor = CommonAncestor(actor, i); } ! Is the actor indirectly inside the item? if (ancestor == item) return L__M(##Take, 4, item); ! Does the actor already directly contain the item? if (item in actor) return L__M(##Take, 5, item); ! Can the actor touch the item, or is there (e.g.) a closed container ! in the way? if (ObjectIsUntouchable(item, false, true)) rtrue; ! The item is now known to be accessible. ! Consult the immediate possessor of the item, if it's in a container ! which the actor is not in. i = parent(item); if (i && i ~= ancestor && (i has container or supporter)) { after_recipient = i; k = action; action = ##LetGo; if (RunRoutines(i, before)) { action = k; rtrue; } action = k; } if (item has scenery) return L__M(##Take, 10, item); if (item has static) return L__M(##Take, 11, item); ! The item is now known to be available for taking. Is the player ! carrying too much? If so, possibly juggle items into the rucksack ! to make room. if (ObjectDoesNotFit(item, actor) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, item, actor)) return; if (AtFullCapacity(item, actor)) return L__M(##Take, 12, item); ! Transfer the item. move item to actor; give item ~worn; ! Send "after" message to the object letting go of the item, if any. if (after_recipient) { k = action; action = ##LetGo; if (RunRoutines(after_recipient, after)) { action = k; rtrue; } action = k; } rfalse; ]; [ AtFullCapacity n s obj k; n = n; ! suppress compiler warning if (s == actor) { objectloop (obj in s) if (obj hasnt worn) k++; } else k = children(s); if (k < RunRoutines(s, capacity) || (s == player && RoomInSack())) rfalse; ]; [ RoomInSack obj ks; if (SACK_OBJECT && SACK_OBJECT in player) { ks = keep_silent; keep_silent = 2; for (obj=youngest(player) : obj : obj=elder(obj)) if (obj ~= SACK_OBJECT && obj hasnt worn or light) { ; if (obj in SACK_OBJECT) { keep_silent = ks; return L__M(##Take, 13, obj, SACK_OBJECT); } } keep_silent = ks; } rfalse; ]; ! ---------------------------------------------------------------------------- ! Support for implicit actions ! ---------------------------------------------------------------------------- [ CheckImplicitAction act o1 o2 sav_act sav_noun sav_sec res; if (o1 provides before_implicit) { sav_act = action; action = act; sav_noun = noun; noun = o1; if (o2) { sav_sec = second; second = o2; } res = RunRoutines(o1, before_implicit); action = sav_act; noun = sav_noun; if (sav_sec) second = sav_sec; } else { if (no_implicit_actions) res = 2; else res = 0; } return res; ]; [ ImplicitTake obj res ks supcon; switch (metaclass(obj)) { Class, String, Routine, nothing: rfalse; } if (obj in actor) rfalse; if (action_to_be == ##Drop && ~~IndirectlyContains(actor, obj)) rfalse; res = CheckImplicitAction(##Take, obj); ! 0 = Take object, Tell the user (normal default) ! 1 = Take object, don't Tell ! 2 = don't Take object continue (default with no_implicit_actions) ! 3 = don't Take object, don't continue if (res >= 2) rtrue; if (parent(obj) && parent(obj) has container or supporter) supcon = parent(obj); ks = keep_silent; keep_silent = 2; AttemptToTakeObject(obj); keep_silent = ks; if (obj notin actor) rtrue; if (res == 0 && ~~keep_silent) if (supcon) L__M(##Miscellany, 58, obj, supcon); else L__M(##Miscellany, 26, obj); rfalse; ]; [ ImplicitExit obj res ks; if (parent(obj) == nothing) rfalse; res = CheckImplicitAction(##Exit, obj); ! 0 = Exit object, Tell the user (normal default) ! 1 = Exit object, don't Tell ! 2 = don't Exit object continue (default with no_implicit_actions) ! 3 = don't Exit object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (parent(actor) == obj) rtrue; if (res == 0 && ~~keep_silent) L__M(##Exit, 5, obj); rfalse; ]; [ ImplicitClose obj res ks; if (obj hasnt open) rfalse; res = CheckImplicitAction(##Close, obj); ! 0 = Close object, Tell the user (normal default) ! 1 = Close object, don't Tell ! 2 = don't Close object continue (default with no_implicit_actions) ! 3 = don't Close object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 2; ; keep_silent = ks; if (obj has open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Close, 4, obj); rfalse; ]; [ ImplicitOpen obj res temp inp1temp; if (obj has open) rfalse; res = CheckImplicitAction(##Open, obj); ! 0 = Open object, Tell the user (normal default) ! 1 = Open object, don't Tell ! 2 = don't Open object continue (default with no_implicit_actions) ! 3 = don't Open object, don't continue if (res >= 2) rtrue; if (obj has locked) rtrue; temp = keep_silent; keep_silent = 2; ; keep_silent = temp; if (obj hasnt open) rtrue; if (res == 0 && ~~keep_silent) L__M(##Open, 6, obj); temp = action; action = ##Open; inp1temp = inp1; inp1 = obj; AfterRoutines(); inp1 = inp1temp; action = temp; rfalse; ]; [ ImplicitUnlock obj; if (obj has locked) rtrue; rfalse; ]; [ ImplicitDisrobe obj res ks; if (obj hasnt worn) rfalse; res = CheckImplicitAction(##Disrobe, obj); ! 0 = Take off object, Tell the user (normal default) ! 1 = Take off object, don't Tell ! 2 = don't Take off object continue (default with no_implicit_actions) ! 3 = don't Take off object, don't continue if (res >= 2) rtrue; ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (obj has worn && obj in actor) rtrue; if (res == 0 && ~~keep_silent) L__M(##Drop, 3, obj); rfalse; ]; ! ---------------------------------------------------------------------------- ! Object movement verbs ! ---------------------------------------------------------------------------- [ TakeSub; if (onotheld_mode == 0 || noun notin actor) if (AttemptToTakeObject(noun)) return; if (AfterRoutines()) return; notheld_mode = onotheld_mode; if (notheld_mode == 1 || keep_silent) return; L__M(##Take, 1, noun); ]; [ RemoveSub i; i = parent(noun); if (i && i has container && i hasnt open && ImplicitOpen(i)) return L__M(##Remove, 1, i); if (i ~= second) return L__M(##Remove, 2, noun); if (i has animate) return L__M(##Take, 6, i, noun); if (AttemptToTakeObject(noun)) rtrue; action = ##Remove; if (AfterRoutines()) return; action = ##Take; if (AfterRoutines()) return; if (keep_silent) return; L__M(##Remove, 3, noun); ]; [ DropSub; if (noun == actor) return L__M(##PutOn, 4, noun); if (noun in parent(actor)) return L__M(##Drop, 1, noun); if (noun notin actor && ~~ImplicitTake(noun)) return L__M(##Drop, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; move noun to parent(actor); if (AfterRoutines() || keep_silent) return; L__M(##Drop, 4, noun); ]; [ PutOnSub ancestor; receive_action = ##PutOn; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##PutOn, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##PutOn, 2, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, before)) { action = ##PutOn; return; } action = ##PutOn; } if (second hasnt supporter) return L__M(##PutOn, 3, second); if (ancestor == actor) return L__M(##PutOn, 4, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##PutOn, 6, second); move noun to second; if (AfterRoutines()) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##PutOn; return; } action = ##PutOn; } if (keep_silent) return; if (multiflag) return L__M(##PutOn, 7); L__M(##PutOn, 8, noun, second); ]; [ InsertSub ancestor; receive_action = ##Insert; if (second == d_obj || actor in second) <>; if (parent(noun) == second) return L__M(##Drop, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Insert, 1, noun); ancestor = CommonAncestor(noun, second); if (ancestor == noun) return L__M(##Insert, 5, noun); if (ObjectIsUntouchable(second)) return; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second,before)) { action = ##Insert; rtrue; } action = ##Insert; if (second has container && second hasnt open && ImplicitOpen(second)) return L__M(##Insert, 3, second); } if (second hasnt container) return L__M(##Insert, 2, second); if (noun has worn && ImplicitDisrobe(noun)) return; if (ObjectDoesNotFit(noun, second) || LibraryExtensions.RunWhile(ext_objectdoesnotfit, false, noun, second)) return; if (AtFullCapacity(noun, second)) return L__M(##Insert, 7, second); move noun to second; if (AfterRoutines()) rtrue; if (second ~= ancestor) { action = ##Receive; if (RunRoutines(second, after)) { action = ##Insert; rtrue; } action = ##Insert; } if (keep_silent) rtrue; if (multiflag) return L__M(##Insert, 8, noun); L__M(##Insert, 9, noun, second); ]; ! ---------------------------------------------------------------------------- ! Empties and transfers are routed through the actions above ! ---------------------------------------------------------------------------- [ TransferSub; if (noun notin actor && AttemptToTakeObject(noun)) return; if (second has supporter) <>; if (second == d_obj) <>; <>; ]; [ EmptySub; second = d_obj; EmptyTSub(); ]; [ EmptyTSub i j k flag; if (noun == second) return L__M(##EmptyT, 4, noun); if (ObjectIsUntouchable(noun)) return; if (noun hasnt container) return L__M(##EmptyT, 1, noun); if (noun hasnt open && ImplicitOpen(noun)) return L__M(##EmptyT, 2, noun); if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) return L__M(##EmptyT, 1, second); if (second hasnt open && ImplicitOpen(second)) return L__M(##EmptyT, 2, second); } } i = child(noun); k = children(noun); if (i == 0) return L__M(##EmptyT, 3, noun); while (i) { j = sibling(i); flag = false; if (ObjectIsUntouchable(noun)) flag = true; if (noun hasnt container) flag = true; if (noun hasnt open) flag = true; if (second ~= d_obj) { if (second hasnt supporter) { if (second hasnt container) flag = true; if (second hasnt open) flag = true; } } if (k-- == 0) flag = 1; if (flag) break; if (keep_silent == 0) print (name) i, (string) COLON__TX; ; i = j; } ]; ! ---------------------------------------------------------------------------- ! Gifts ! ---------------------------------------------------------------------------- [ GiveSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Give, 1, noun); if (second == actor) return L__M(##Give, 2, noun); if (noun has worn && ImplicitDisrobe(noun)) return; if (second == player) { move noun to player; return L__M(##Give, 4, noun); } if (RunLife(second, ##Give)) return; L__M(##Give, 3, second); ]; [ GiveRSub; ; ]; [ ShowSub; if (noun notin actor && ImplicitTake(noun)) return L__M(##Show, 1, noun); if (second == player) <>; if (RunLife(second, ##Show)) return; L__M(##Show, 2, second); ]; [ ShowRSub; ; ]; ! ---------------------------------------------------------------------------- ! Travelling around verbs ! ---------------------------------------------------------------------------- [ EnterSub ancestor j ks; if (noun has door || noun in compass) <>; if (actor in noun) return L__M(##Enter, 1, noun); if (noun hasnt enterable) return L__M(##Enter, 2, noun, verb_word); if (parent(actor) ~= parent(noun)) { ancestor = CommonAncestor(actor, noun); if (ancestor == actor or 0) return L__M(##Enter, 4, noun); while (actor notin ancestor) { j = parent(actor); ks = keep_silent; if (parent(j) ~= ancestor || noun ~= ancestor) { L__M(##Enter, 6, j); keep_silent = 1; } ; keep_silent = ks; if (actor in j) return; } if (actor in noun) return; if (noun notin ancestor) { j = parent(noun); while (parent(j) ~= ancestor) j = parent(j); L__M(##Enter, 7, j); ks = keep_silent; keep_silent = 1; ; keep_silent = ks; if (actor notin j) return; <>; } } if (noun has container && noun hasnt open && ImplicitOpen(noun)) return L__M(##Enter, 3, noun); move actor to noun; if (AfterRoutines() || keep_silent) return; L__M(##Enter, 5, noun); if (actor == player) Locale(noun); ]; [ GetOffSub; if (parent(actor) == noun) <>; L__M(##GetOff, 1, noun); ]; [ ExitSub p; p = parent(actor); if (noun ~= nothing && noun ~= p) return L__M(##Exit, 4 ,noun); if (p == location || (location == thedark && p == real_location)) { if (actor provides posture && actor.posture) { actor.posture = 0; return L__M(##Exit, 6); } if ((location.out_to) || (location == thedark && real_location.out_to)) <>; return L__M(##Exit, 1); } if (p has container && p hasnt open && ImplicitOpen(p)) return L__M(##Exit, 2, p); if (noun == nothing) { inp1 = p; if (RunRoutines(p, before)) return; } move actor to parent(p); if (player provides posture) player.posture = 0; if (AfterRoutines() || keep_silent) return; L__M(##Exit, 3, p); if (actor == player && p has container) LookSub(1); ]; [ VagueGoSub; L__M(##VagueGo); ]; [ GoInSub; <>; ]; [ GoSub i j k movewith thedir next_loc; ! first, check if any PushDir object is touchable if (second && second notin Compass && ObjectIsUntouchable(second)) return; movewith = 0; i = parent(actor); if ((location ~= thedark && i ~= location) || (location == thedark && i ~= real_location)) { j = location; if (location == thedark) location = real_location; k = RunRoutines(i, before); if (k ~= 3) location = j; if (k == 1) { movewith = i; i = parent(i); } else { if (k) rtrue; if (ImplicitExit(i)) return L__M(##Go, 1, i); i = parent(actor); } } thedir = noun.door_dir; if (metaclass(thedir) == Routine) thedir = RunRoutines(noun, door_dir); next_loc = i.thedir; k = metaclass(next_loc); if (k == String) { print (string) next_loc; new_line; rfalse; } if (k == Routine) { next_loc = RunRoutines(i, thedir); if (next_loc == 1) rtrue; } if (k == nothing || next_loc == 0) { if (i.cant_go ~= 0 or CANTGO__TX) PrintOrRun(i, cant_go); else L__M(##Go, 2); rfalse; } if (next_loc has door) { if (next_loc has concealed) return L__M(##Go, 2); if (next_loc hasnt open && ImplicitOpen(next_loc)) { if (noun == u_obj) return L__M(##Go, 3, next_loc); if (noun == d_obj) return L__M(##Go, 4, next_loc); return L__M(##Go, 5, next_loc); } k = RunRoutines(next_loc, door_to); if (k == 0) return L__M(##Go, 6, next_loc); if (k == 1) rtrue; next_loc = k; } action = ##Going; if (RunRoutines(next_loc, before)) { action = ##Go; return; } action = ##Go; if (movewith == 0) move actor to next_loc; else move movewith to next_loc; if (actor ~= player) return L__M(##Go, 7); k = location; location = next_loc; MoveFloatingObjects(); if (OffersLight(location)) lightflag = true; else { lightflag = false; if (k == thedark) { if(DarkToDark() == false) ! From real_location To location LibraryExtensions.RunAll(ext_darktodark); if (deadflag) rtrue; } location = thedark; } NoteDeparture(); real_location = next_loc; action = ##Going; if (RunRoutines(prev_location, after)) { action = ##Go; return; } action = ##Go; if (AfterRoutines() || keep_silent) return; LookSub(1); ]; ! ---------------------------------------------------------------------------- ! Describing the world. SayWhatsOn(object) does just that (producing ! no text if nothing except possibly "scenery" and "concealed" items are). ! Locale(object) runs through the "tail end" of a Look-style room ! description for the contents of the object, printing up suitable ! descriptions as it goes. ! ---------------------------------------------------------------------------- [ SayWhatsOn descon j f; if (descon == parent(player)) rfalse; objectloop (j in descon) if (j hasnt concealed && j hasnt scenery) f = 1; if (f == 0) rfalse; L__M(##Look, 4, descon); ]; [ NotSupportingThePlayer o i; i = parent(player); while (i && i ~= visibility_ceiling) { if (i == o) rfalse; i = parent(i); if (i && i hasnt supporter) rtrue; } rtrue; ]; ! modified with the fix for L61122 [ Locale descin text_without_ALSO text_with_ALSO o p num_objs must_print_ALSO; objectloop (o in descin) give o ~workflag; num_objs = 0; objectloop (o in descin) if (o hasnt concealed && NotSupportingThePlayer(o)) { #Ifndef MANUAL_PRONOUNS; PronounNotice(o); #Endif; if (o has scenery) { if (o has supporter && child(o)) SayWhatsOn(o); } else { give o workflag; num_objs++; p = initial; if ((o has door or container) && o has open && o provides when_open) { p = when_open; jump Prop_Chosen; } if ((o has door or container) && o hasnt open && o provides when_closed) { p = when_closed; jump Prop_Chosen; } if (o has switchable && o has on && o provides when_on) { p = when_on; jump Prop_Chosen; } if (o has switchable && o hasnt on && o provides when_off) { p = when_off; } .Prop_Chosen; if (o.&describe && RunRoutines(o, describe)) { must_print_ALSO = true; give o ~workflag; num_objs--; continue; } if (o.p && (o hasnt moved || p ~= initial)) { new_line; PrintOrRun(o, p); must_print_ALSO = true; give o ~workflag; num_objs--; if (o has supporter && child(o)) SayWhatsOn(o); } } } if (num_objs == 0) return 0; if (actor ~= player) give actor concealed; if (text_without_ALSO) { new_line; if (must_print_ALSO) print (string) text_with_ALSO, " "; else print (string) text_without_ALSO, " "; WriteListFrom(child(descin), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT); } else { if (must_print_ALSO) L__M(##Look, 5, descin); else L__M(##Look, 6, descin); } if (actor ~= player) give actor ~concealed; return num_objs; ]; ! ---------------------------------------------------------------------------- ! Looking. LookSub(1) is allowed to abbreviate long descriptions, but ! LookSub(0) (which is what happens when the Look action is generated) ! isn't. (Except that these are over-ridden by the player-set lookmode.) ! ---------------------------------------------------------------------------- [ LMode1Sub; lookmode=1; L__M(##LMode1); ]; ! Brief [ LMode2Sub; lookmode=2; L__M(##LMode2); ]; ! Verbose [ LMode3Sub; lookmode=3; L__M(##LMode3); ]; ! Superbrief [ LModeNormalSub; ! 'normal' value: the default, or as set in Initialise() switch (initial_lookmode) { 1: <>; 3: <>; default: <>; } ]; [ NoteArrival descin; if (location ~= lastdesc) { if (location.initial) PrintOrRun(location, initial); if (location == thedark) { lastdesc = thedark; return; } descin = location; if(NewRoom() == false) LibraryExtensions.RunAll(ext_newroom); lastdesc = descin; } ]; [ NoteDeparture; prev_location = real_location; ]; [ ScoreArrival; if (location hasnt visited) { give location visited; if (location has scored) { score = score + ROOM_SCORE; places_score = places_score + ROOM_SCORE; } } ]; [ FindVisibilityLevels visibility_levels; visibility_levels = 1; visibility_ceiling = parent(player); while ((parent(visibility_ceiling)) && (visibility_ceiling hasnt container || visibility_ceiling has open or transparent)) { visibility_ceiling = parent(visibility_ceiling); visibility_levels++; } return visibility_levels; ]; [ LookSub allow_abbrev visibility_levels i j k nl_flag; if (parent(player) == 0) return RunTimeError(10); .MovedByInitial; if (location == thedark) { visibility_ceiling = thedark; NoteArrival(); } else { visibility_levels = FindVisibilityLevels(); if (visibility_ceiling == location) { NoteArrival(); if (visibility_ceiling ~= location) jump MovedByInitial; } } ! Printing the top line: e.g. ! Octagonal Room (on the table) (as Frodo) new_line; #Ifdef TARGET_ZCODE; style bold; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Subheader); #Endif; ! TARGET_ if (visibility_levels == 0) print (name) thedark; else { if (visibility_ceiling ~= location) print (The) visibility_ceiling; else print (name) visibility_ceiling; } #Ifdef TARGET_ZCODE; style roman; #Ifnot; ! TARGET_GLULX; glk_set_style(style_Normal); #Endif; ! TARGET_ for (j=1,i=parent(player) : j0 : j--,i=parent(i)) give i workflag; for (j=visibility_levels : j>0 : j--) { for (i=player,k=0 : k>; else return L__M(##Search, 5, noun); if (noun has switchable) { L__M(##Examine, 3, noun); rfalse; } return L__M(##Examine, 2, noun); } i = PrintOrRun(noun, description); if (i < 2 && noun has switchable) L__M(##Examine, 3, noun); AfterRoutines(); ]; [ LookUnderSub; if (location == thedark) return L__M(##LookUnder, 1, noun); L__M(##LookUnder, 2); ]; [ VisibleContents o i f; objectloop (i in o) if (i hasnt concealed or scenery) f++; return f; ]; [ SearchSub f; if (location == thedark) return L__M(##Search, 1, noun); if (ObjectIsUntouchable(noun)) return; f = VisibleContents(noun); if (noun has supporter) { if (f == 0) return L__M(##Search, 2, noun); return L__M(##Search, 3, noun); } if (noun hasnt container) return L__M(##Search, 4, noun); if (noun hasnt transparent or open && ImplicitOpen(noun)) return L__M(##Search, 5, noun); if (AfterRoutines()) return; if (f == 0) return L__M(##Search, 6, noun); L__M(##Search, 7, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which change the state of objects without moving them ! ---------------------------------------------------------------------------- [ UnlockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Unlock, 1, noun); if (noun hasnt locked) return L__M(##Unlock, 2, noun); if (noun.with_key ~= second) return L__M(##Unlock, 3, second); give noun ~locked; if (AfterRoutines() || keep_silent) return; L__M(##Unlock, 4, noun); ]; [ LockSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt lockable) return L__M(##Lock, 1, noun); if (noun has locked) return L__M(##Lock, 2 ,noun); if (noun has open && ImplicitClose(noun)) return L__M(##Lock, 3, noun); if (noun.with_key ~= second) return L__M(##Lock, 4, second); give noun locked; if (AfterRoutines() || keep_silent) return; L__M(##Lock, 5, noun); ]; [ SwitchonSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOn, 1, noun); if (noun has on) return L__M(##SwitchOn, 2, noun); give noun on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOn, 3, noun); ]; [ SwitchoffSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt switchable) return L__M(##SwitchOff, 1, noun); if (noun hasnt on) return L__M(##SwitchOff, 2, noun); give noun ~on; if (AfterRoutines() || keep_silent) return; L__M(##SwitchOff, 3, noun); ]; [ OpenSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Open, 1, noun); if (noun has locked && ImplicitUnlock(noun)) return L__M(##Open, 2, noun); if (noun has open) return L__M(##Open, 3, noun); give noun open; if (keep_silent || AfterRoutines()) return; if (noun hasnt container) return L__M(##Open, 5, noun); if ((noun has container && location ~= thedark && VisibleContents(noun) && IndirectlyContains(noun, player)) == 0) { if (noun hasnt transparent && noun hasnt door) return L__M(##Open, 4, noun); } L__M(##Open, 5, noun); ]; [ CloseSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt openable) return L__M(##Close, 1, noun); if (noun hasnt open) return L__M(##Close, 2, noun); give noun ~open; if (AfterRoutines() || keep_silent) return; L__M(##Close, 3, noun); ]; [ DisrobeSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt worn) return L__M(##Disrobe, 1, noun); give noun ~worn; if (AfterRoutines() || keep_silent) return; L__M(##Disrobe, 2, noun); ]; [ WearSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt clothing) return L__M(##Wear, 1, noun); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wear, 2, noun); if (noun has worn) return L__M(##Wear, 3, noun); give noun worn; if (AfterRoutines() || keep_silent) return; L__M(##Wear, 4, noun); ]; [ EatSub; if (ObjectIsUntouchable(noun)) return; if (noun hasnt edible) return L__M(##Eat, 1, noun); if (noun has worn && ImplicitDisrobe(noun)) return; remove noun; if (AfterRoutines() || keep_silent) return; L__M(##Eat, 2, noun); ]; ! ---------------------------------------------------------------------------- ! Verbs which are really just stubs (anything which happens for these ! actions must happen in before rules) ! ---------------------------------------------------------------------------- [ AllowPushDir i; if (parent(second) ~= compass) return L__M(##PushDir, 2, noun); if (second == u_obj or d_obj) return L__M(##PushDir, 3, noun); AfterRoutines(); i = noun; move i to actor; ; if (location == thedark) move i to real_location; else move i to location; ]; [ AnswerSub; if (second && RunLife(second,##Answer)) rfalse; L__M(##Answer, 1, noun); ]; [ AskSub; if (RunLife(noun,##Ask)) rfalse; L__M(##Ask, 1, noun); ]; [ AskForSub; if (noun == player) <>; L__M(##Order, 1, noun); ]; [ AskToSub; L__M(##Order, 1, noun); ]; [ AttackSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && RunLife(noun, ##Attack)) rfalse; L__M(##Attack, 1, noun); ]; [ BlowSub; L__M(##Blow, 1, noun); ]; [ BurnSub; if (noun has animate) return L__M(##Burn, 2, noun); L__M(##Burn, 1, noun); ]; [ BuySub; L__M(##Buy, 1, noun); ]; [ ClimbSub; if (noun has animate) return L__M(##Climb, 2, noun); L__M(##Climb, 1, noun); ]; [ ConsultSub; L__M(##Consult, 1, noun); ]; [ CutSub; if (noun has animate) return L__M(##Cut, 2, noun); L__M(##Cut, 1, noun); ]; [ DigSub; L__M(##Dig, 1, noun); ]; [ DrinkSub; L__M(##Drink, 1, noun); ]; [ FillSub; if (second == nothing) return L__M(##Fill, 1, noun); L__M(##Fill, 2, noun, second); ]; [ JumpSub; L__M(##Jump, 1, noun); ]; [ JumpInSub; if (noun has animate) return L__M(##JumpIn, 2, noun); if (noun has enterable) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOnSub; if (noun has animate) return L__M(##JumpOn, 2, noun); if (noun has enterable && noun has supporter) <>; L__M(##JumpOn, 1, noun); ]; [ JumpOverSub; if (noun has animate) return L__M(##JumpOver, 2, noun); L__M(##JumpOver, 1, noun); ]; [ KissSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##Kiss)) return; if (noun == actor) return L__M(##Touch, 3, noun); L__M(##Kiss, 1, noun); ]; [ ListenSub; L__M(##Listen, 1, noun); ]; [ MildSub; L__M(##Mild, 1, noun); ]; [ NoSub; L__M(##No); ]; [ PraySub; L__M(##Pray, 1, noun); ]; [ PullSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Pull, 1, noun); if (noun == actor) return L__M(##Pull, 6, noun); if (noun has static) return L__M(##Pull, 2, noun); if (noun has scenery) return L__M(##Pull, 3, noun); if (noun has animate) return L__M(##Pull, 5, noun); L__M(##Pull, 4, noun); ]; [ PushSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Push, 1, noun); if (noun == actor) return L__M(##Push, 5, noun); if (noun has static) return L__M(##Push, 2, noun); if (noun has scenery) return L__M(##Push, 3, noun); if (noun has animate) return L__M(##Push, 5, noun); L__M(##Push, 4, noun); ]; [ PushDirSub; L__M(##PushDir, 1, noun); ]; [ RubSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Rub, 2, noun); L__M(##Rub, 1, noun); ]; [ SetSub; L__M(##Set, 1, noun); ]; [ SetToSub; L__M(##SetTo, 1, noun); ]; [ SingSub; L__M(##Sing, 1, noun); ]; [ SleepSub; L__M(##Sleep, 1, noun); ]; [ SmellSub; if (noun ~= nothing && noun has animate) return L__M(##Smell, 2, noun); L__M(##Smell, 1, noun); ]; [ SorrySub; L__M(##Sorry, 1, noun); ]; [ SqueezeSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate && noun ~= player) return L__M(##Squeeze, 1, noun); L__M(##Squeeze, 2, noun); ]; [ StrongSub; L__M(##Strong, 1, noun); ]; [ SwimSub; L__M(##Swim, 1, noun); ]; [ SwingSub; L__M(##Swing, 1, noun); ]; [ TasteSub; if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Taste, 2, noun); L__M(##Taste, 1, noun); ]; [ TellSub; if (noun == actor) return L__M(##Tell, 1, noun); if (RunLife(noun, ##Tell)) return; L__M(##Tell, 2, noun); ]; [ ThinkSub; L__M(##Think, 1, noun); ]; [ ThrowAtSub; if (ObjectIsUntouchable(noun)) return; if (second > 1) { action = ##ThrownAt; if (RunRoutines(second, before)) { action = ##ThrowAt; rtrue; } action = ##ThrowAt; } if (noun has worn && ImplicitDisrobe(noun)) return; if (second hasnt animate) return L__M(##ThrowAt, 1, noun); if (RunLife(second, ##ThrowAt)) return; L__M(##ThrowAt, 2, noun); ]; [ TieSub; if (noun has animate) return L__M(##Tie, 2, noun); L__M(##Tie, 1, noun); ]; [ TouchSub; if (noun == actor) return L__M(##Touch, 3, noun); if (ObjectIsUntouchable(noun)) return; if (noun has animate) return L__M(##Touch, 1, noun); L__M(##Touch, 2,noun); ]; [ TurnSub; if (ObjectIsUntouchable(noun)) return; if (noun == player) return L__M(##Turn, 1, noun); if (noun == actor) return L__M(##Turn, 5, noun); if (noun has static) return L__M(##Turn, 2, noun); if (noun has scenery) return L__M(##Turn, 3, noun); if (noun has animate) return L__M(##Turn, 5, noun); L__M(##Turn, 4, noun); ]; [ WaitSub; if (AfterRoutines()) rtrue; L__M(##Wait, 1, noun); ]; [ WakeSub; L__M(##Wake, 1, noun); ]; [ WakeOtherSub; if (ObjectIsUntouchable(noun)) return; if (RunLife(noun, ##WakeOther)) return; L__M(##WakeOther, 1, noun); ]; [ WaveSub; if (noun == player) return L__M(##Wave, 2 ,noun, second); if (noun == actor) return L__M(##Wave, 3, noun, second); if (noun notin actor && ImplicitTake(noun)) return L__M(##Wave, 1, noun); L__M(##Wave, 2, noun, second); ]; [ WaveHandsSub; if (noun) return L__M(##WaveHands, 2, noun); L__M(##WaveHands, 1, noun); ]; [ YesSub; L__M(##Yes); ]; ! ---------------------------------------------------------------------------- ! Debugging verbs ! ---------------------------------------------------------------------------- #Ifdef DEBUG; [ TraceOnSub; parser_trace = 1; "[Trace on.]"; ]; [ TraceLevelSub; parser_trace = noun; print "[Parser tracing set to level ", parser_trace, ".]^"; ]; [ TraceOffSub; parser_trace = 0; "Trace off."; ]; [ RoutinesOnSub; debug_flag = debug_flag | DEBUG_MESSAGES; "[Message listing on.]"; ]; [ RoutinesOffSub; debug_flag = debug_flag & ~DEBUG_MESSAGES; "[Message listing off.]"; ]; [ RoutinesVerboseSub; debug_flag = debug_flag | (DEBUG_VERBOSE|DEBUG_MESSAGES); "[Verbose message listing on.]"; ]; [ ActionsOnSub; debug_flag = debug_flag | DEBUG_ACTIONS; "[Action listing on.]"; ]; [ ActionsOffSub; debug_flag = debug_flag & ~DEBUG_ACTIONS; "[Action listing off.]"; ]; [ TimersOnSub; debug_flag = debug_flag | DEBUG_TIMERS; "[Timers listing on.]"; ]; [ TimersOffSub; debug_flag = debug_flag & ~DEBUG_TIMERS; "[Timers listing off.]"; ]; #Ifdef VN_1610; [ ChangesOnSub; debug_flag = debug_flag | DEBUG_CHANGES; "[Changes listing on.]"; ]; [ ChangesOffSub; debug_flag = debug_flag & ~DEBUG_CHANGES; "[Changes listing off.]"; ]; #Ifnot; [ ChangesOnSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; [ ChangesOffSub; "[Changes listing available only from Inform 6.2 onwards.]"; ]; #Endif; ! VN_1610 #Ifdef TARGET_ZCODE; [ PredictableSub i; i = random(-100); "[Random number generator now predictable.]"; ]; #Ifnot; ! TARGET_GLULX; [ PredictableSub; @setrandom 100; "[Random number generator now predictable.]"; ]; #Endif; ! TARGET_; [ XTestMove obj dest; if (~~obj ofclass Object) "[Not an object.]"; if (~~dest ofclass Object) "[Destination not an object.]"; if ((obj <= InformLibrary) || (obj == LibraryMessages) || (obj in 1)) "[Can't move ", (name) obj, ": it's a system object.]"; while (dest) { if (dest == obj) "[Can't move ", (name) obj, ": it would contain itself.]"; dest = parent(dest); } rfalse; ]; [ XPurloinSub; if (XTestMove(noun, player)) return; move noun to player; give noun moved ~concealed; "[Purloined.]"; ]; [ XAbstractSub; if (XTestMove(noun, second)) return; move noun to second; "[Abstracted.]"; ]; [ XObj obj f; if (parent(obj) == 0) print (name) obj; else print (a) obj; print " (", obj, ") "; if (f && parent(obj)) print "in ~", (name) parent(obj), "~ (", parent(obj), ")"; new_line; if (child(obj) == 0) rtrue; if (obj == Class) ! ??? WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+NOARTICLE_BIT, 1); else WriteListFrom(child(obj), NEWLINE_BIT+INDENT_BIT+ALWAYS_BIT+ID_BIT+FULLINV_BIT, 1); ]; [ XTreeSub i; if (noun && ~~noun ofclass Object) "[Not an object.]"; if (noun == 0) { objectloop (i) if (i ofclass Object && parent(i) == 0) XObj(i); } else XObj(noun, true); ]; [ GotoSub; if ((~~noun ofclass Object) || parent(noun)) "[Not a safe place.]"; PlayerTo(noun); ]; [ GoNearSub x; if (~~noun ofclass Object) "[Not a safe place.]"; x = noun; while (parent(x)) x = parent(x); PlayerTo(x); ]; [ Print_ScL obj; print_ret ++x_scope_count, ": ", (a) obj, " (", obj, ")"; ]; [ ScopeSub; if (noun && ~~noun ofclass Object) "[Not an object.]"; x_scope_count = 0; LoopOverScope(Print_ScL, noun); if (x_scope_count == 0) "Nothing is in scope."; ]; #Ifdef TARGET_GLULX; [ GlkListSub id val; id = glk_window_iterate(0, gg_arguments); while (id) { print "Window ", id, " (", gg_arguments-->0, "): "; val = glk_window_get_type(id); switch (val) { 1: print "pair"; 2: print "blank"; 3: print "textbuffer"; 4: print "textgrid"; 5: print "graphics"; default: print "unknown"; } val = glk_window_get_parent(id); if (val) print ", parent is window ", val; else print ", no parent (root)"; val = glk_window_get_stream(id); print ", stream ", val; val = glk_window_get_echo_stream(id); if (val) print ", echo stream ", val; print "^"; id = glk_window_iterate(id, gg_arguments); } id = glk_stream_iterate(0, gg_arguments); while (id) { print "Stream ", id, " (", gg_arguments-->0, ")^"; id = glk_stream_iterate(id, gg_arguments); } id = glk_fileref_iterate(0, gg_arguments); while (id) { print "Fileref ", id, " (", gg_arguments-->0, ")^"; id = glk_fileref_iterate(id, gg_arguments); } val = glk_gestalt(gestalt_Sound, 0); if (val) { id = glk_schannel_iterate(0, gg_arguments); while (id) { print "Soundchannel ", id, " (", gg_arguments-->0, ")^"; id = glk_schannel_iterate(id, gg_arguments); } } ]; #Endif; ! TARGET_; #Endif; ! DEBUG ! ---------------------------------------------------------------------------- ! Finally: the mechanism for library text (the text is in the language defn) ! ---------------------------------------------------------------------------- [ L__M act n x1 x2 s; if (keep_silent == 2) return; s = sw__var; sw__var = act; if (n == 0) n = 1; L___M(n, x1, x2); sw__var = s; ]; [ L___M n x1 x2 s; s = action; lm_n = n; lm_o = x1; lm_s = x2; action = sw__var; if (RunRoutines(LibraryMessages, before)) { action = s; rfalse; } if (LibraryExtensions.RunWhile(ext_messages, false )) { action = s; rfalse; } action = s; LanguageLM(n, x1, x2); ]; ! ============================================================================== Undef LIBRARY_STAGE; Constant LIBRARY_STAGE = AFTER_VERBLIB; #Ifnot; ! LIBRARY_STAGE < AFTER_VERBLIB but ~= AFTER_PARSER ! (this shouldn't happen because if 'parser' isn't there, LIBRARY_STAGE isn't defined) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; #Ifnot; ! LIBRARY_STAGE >= AFTER_VERBLIB: already included Message "Warning: 'verblib' included twice; ignoring second inclusion. (Ignore this if this is on purpose.)"; #Endif; #Ifnot; ! LIBRARY_STAGE is not defined (likely, 'parser' hasn't been included) Message "Error: 'parser' needs to be correctly included before including 'verblib'. This will cause a big number of errors!"; #Endif; ! ============================================================================== ! ============================================================================== ! INFIX: Support for the optional library debugger extension "Infix". ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! This file is automatically Included in your game file by "Grammar" if you ! supply the -X compiler switch. ! ============================================================================== System_file; ! ------------------------------------------------------------------------------ #Ifdef DEBUG; Constant INFIXTT_NUMBER 0; Constant INFIXTT_ARRAY 1; Constant INFIXTT_ROUTINE 2; Constant INFIXTT_CONSTANT 3; Constant INFIXTT_DWORD 4; Constant INFIXTT_ACTION 5; Constant INFIXTT_ATTRIBUTE 6; Constant INFIXTT_PROPERTY 7; Constant INFIXTT_GLOBAL 8; Constant INFIXTT_NAMEDOBJECT 9; Constant INFIXTT_SYSFUN 10; Constant INFIXTT_STATICSTRING 11; Constant INFIXTT_LOGICAL 12; Global infix_term_type; Global infix_data1; Global infix_data2; Global infix_lvalue; Global infix_parsed_lvalue; Array infix_tolowercase -> 256; #Ifdef VN_1630; Array infix_text buffer 126; #Ifnot; Array infix_text -> 128; #Endif; ! VN_ [ InfixPrintAttribute x; print (string) #attribute_names_array-->x; ]; [ InfixPrintProperty x; print (property) x; ]; #Ifdef TARGET_GLULX; [ InfixPrintGIProperty x; print (property) x; ]; #Endif; ! TARGET_ [ InfixPrintGlobal x; print (string) #global_names_array-->x; ]; [ InfixPrintAction x; print (string) #action_names_array-->(x-#lowest_action_number); ]; [ InfixPrintRoutine x; print (string) #routine_names_array-->(x-#lowest_routine_number); ]; [ InfixPrintConstant x; print (string) #constant_names_array-->(x-#lowest_constant_number); ]; [ InfixPrintArray x; print (string) #array_names_array-->(x-#lowest_array_number); ]; [ InfixPrintFakeAction x; print (string) #fake_action_names_array-->(x-#lowest_fake_action_number); ]; [ InfixPrintPA x n; for (n=#lowest_routine_number : n<=#highest_routine_number : n++) { if (x == Symb__Tab(INFIXTT_ROUTINE, n)) { print (InfixPrintRoutine) n; return; } } print "Routine(", x, ")"; ]; [ InfixMatchPrule PrintingRule range1 range2 wa wl t i i2 it2 itlc j k plus; itlc = infix_tolowercase; if (itlc->255 == 0) { for (j=0 : j<256 : j++) itlc->j = j; itlc->'A' = 'a'; itlc->'B' = 'b'; itlc->'C' = 'c'; itlc->'D' = 'd'; itlc->'E' = 'e'; itlc->'F' = 'f'; itlc->'G' = 'g'; itlc->'H' = 'h'; itlc->'I' = 'i'; itlc->'J' = 'j'; itlc->'K' = 'k'; itlc->'L' = 'l'; itlc->'M' = 'm'; itlc->'N' = 'n'; itlc->'O' = 'o'; itlc->'P' = 'p'; itlc->'Q' = 'q'; itlc->'R' = 'r'; itlc->'S' = 's'; itlc->'T' = 't'; itlc->'U' = 'u'; itlc->'V' = 'v'; itlc->'W' = 'w'; itlc->'X' = 'x'; itlc->'Y' = 'y'; itlc->'Z' = 'z'; } switch (PrintingRule) { InfixPrintAttribute: if (wa->0 == '~') { wl--; wa++; plus = 100; } ! A tilde t = #attribute_names_array; InfixPrintProperty: t = #property_names_array; #Ifdef TARGET_GLULX; InfixPrintGIProperty: t = #identifiers_table-->2; #Endif; ! TARGET_ InfixPrintAction: t = #action_names_array; InfixPrintFakeAction: t = #fake_action_names_array; InfixPrintGlobal: t = #global_names_array; InfixPrintRoutine: t = #routine_names_array; InfixPrintAction: t = #constant_names_array; InfixPrintArray: t = #array_names_array; } i2 = range2-range1; it2 = infix_text+WORDSIZE; for (i=0 : i<=i2 : i++) { #ifdef TARGET_ZCODE; infix_text-->0 = 62; @output_stream 3 infix_text; if (t) print (string) t-->i; else PrintingRule(i+range1); @output_stream -3; #ifnot; ! TARGET_GLULX if (t) PrintToBuffer(infix_text, 62, t-->i); else PrintToBuffer(infix_text, 62, PrintingRule, i+range1); #endif; ! TARGET_ k = infix_text-->0; if (k ~= wl) jump XL; if (itlc->(it2->0) ~= itlc->(wa->0)) jump XL; for (j=1 : j(it2->j) ~= itlc->(wa->j)) jump XL; parsed_number = i + range1 + plus; rtrue; .XL; } rfalse; ]; [ InfixActionToken; if (InfixMatchPrule(InfixPrintAction, #lowest_action_number, #highest_action_number, WordAddress(wn), WordLength(wn))) { wn++; infix_lvalue = parsed_number; return 0; } if (InfixMatchPrule(InfixPrintFakeAction, #lowest_fake_action_number, #highest_fake_action_number, WordAddress(wn), WordLength(wn))) { wn++; infix_lvalue = parsed_number; return 0; } return -1; ]; [ InfixRvalueTerm n w i initial_wn wa wl sign base digit dcount; initial_wn = wn; infix_parsed_lvalue = -1; infix_term_type = INFIXTT_NUMBER; w = NextWordStopped(); if (w == -1) return -1; wa = WordAddress(wn-1); wl = WordLength(wn-1); if (wa->0 == '-' or '$' or '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9') { ! Parse decimal, hex or binary number sign = 1; base = 10; dcount = 0; if (wa->0 == '-') { sign = -1; wl--; wa++; } else { if (wa->0 == '$') { base = 16; wl--; wa++; } if (wa->0 == '$') { base = 2; wl--; wa++; } } if (wl == 0) return -1; n = 0; while (wl > 0) { if (wa->0 >= 'a') digit = wa->0 - 'a' + 10; else digit = wa->0 - '0'; dcount++; #Ifdef TARGET_ZCODE; switch (base) { 2: if (dcount == 17) return -1; 10: if (dcount == 6) return -1; if (dcount == 5) { if (n > 3276) return -1; if (n == 3276) { if (sign == 1 && digit > 7) return -1; if (sign == -1 && digit > 8) return -1; } } 16: if (dcount == 5) return -1; } #Ifnot; ! TARGET_GLULX switch (base) { 2: if (dcount == 33) return -1; 10: if (dcount == 11) return -1; if (dcount == 10) { if (n > 214748363) return -1; } 16: if (dcount == 9) return -1; } #Endif; ! TARGET_ if (digit >= 0 && digit < base) n = base*n + digit; else return -1; wl--; wa++; } parsed_number = n*sign; return 1; } ! Parse character constant 'a' if (wl == 3 && wa->0 == ''' && wa->2 == ''') { parsed_number = wa->1; return 1; } ! ##Action, 'dword' switch (w) { '##': infix_term_type = INFIXTT_ACTION; w = NextWordStopped(); if (w == -1) return -1; wn--; if (InfixActionToken() == 0) return 1; return -1; '^^': infix_term_type = INFIXTT_DWORD; w = NextWordStopped(); if (w == -1) return -1; parsed_number = w; return 1; } ! Test for attribute, property, class name, variable name, array name, routine ! name, constant name wn--; if ((wa->0 >= 'a' && wa->0 <= 'z') || (wa->0 >= 'A' && wa->0 <= 'Z') || wa->0 == '_') { infix_term_type = INFIXTT_ATTRIBUTE; if (InfixMatchPrule(InfixPrintAttribute, #lowest_attribute_number, #highest_attribute_number, wa, wl)) { wn++; return 1; } infix_term_type = INFIXTT_PROPERTY; #Ifdef TARGET_ZCODE; if (InfixMatchPrule(InfixPrintProperty, #lowest_property_number, #highest_property_number, wa, wl)) { wn++; return 1; } #Ifnot; ! TARGET_ if (InfixMatchPrule(InfixPrintProperty, #lowest_property_number, #identifiers_table-->1 - 1, wa, wl)) { wn++; return 1; } if (InfixMatchPrule(InfixPrintGIProperty, INDIV_PROP_START, #highest_property_number, wa, wl)) { wn++; return 1; } #Endif; ! TARGET_ infix_term_type = INFIXTT_GLOBAL; if (InfixMatchPrule(InfixPrintGlobal, #lowest_global_number, #highest_global_number, wa, wl)) { infix_parsed_lvalue = parsed_number-16; parsed_number = #globals_array-->infix_parsed_lvalue; wn++; return 1; } infix_term_type = INFIXTT_ARRAY; if (InfixMatchPrule(InfixPrintArray, #lowest_array_number, #highest_array_number, wa, wl)) { infix_parsed_lvalue = parsed_number; parsed_number = Symb__Tab(INFIXTT_ARRAY,parsed_number); infix_data1 = temp__global3; infix_data2 = temp__global2; wn++; return 1; } infix_term_type = INFIXTT_ROUTINE; if (InfixMatchPrule(InfixPrintRoutine, #lowest_routine_number, #highest_routine_number, wa, wl)) { infix_parsed_lvalue = parsed_number; parsed_number = Symb__Tab(INFIXTT_ROUTINE,parsed_number); infix_data1 = temp__global3; infix_data2 = temp__global2; wn++; return 1; } infix_term_type = INFIXTT_CONSTANT; if (InfixMatchPrule(InfixPrintConstant, #lowest_constant_number, #highest_constant_number, wa, wl)) { infix_parsed_lvalue = parsed_number; parsed_number = Symb__Tab(INFIXTT_CONSTANT,parsed_number); infix_data1 = temp__global3; infix_data2 = temp__global2; wn++; return 1; } switch (w) { 'parent', 'child', 'children', 'random', 'metaclass', 'sibling': parsed_number = w; infix_parsed_lvalue = INFIXTT_SYSFUN; wn++; return 1; } } infix_term_type = INFIXTT_NAMEDOBJECT; wn = initial_wn; i = ParseToken(SCOPE_TT, InfixBigScope); if (i == GPR_REPARSE) return i; if (i > GPR_MULTIPLE) { print "(", (name) i, " (", i, "))^"; parsed_number = i; return 1; } return -1; ]; ! end of InfixRvalueTerm [ InfixBigScope x; if (scope_stage == 1) return false; ! No multiples here if (scope_stage == 2) { objectloop (x ofclass Object) PlaceInScope(x); return true; ! That's the whole scope } print "; I'm unable to make any sense of that term.^"; ]; [ InfixCheckLineSpaced wa wl i force altered; for (i=1 : i<=NumberWords() : i++) { wa = WordAddress(i); wl = WordLength(i); if (wl > 3 && wa->0 == ''' && wa->(wl-1) == ''') { wa->(wl-1) = ' '; if (wa->(wl-2) == '/' && wa->(wl-3) == '/') { wa->(wl-2) = ' '; wa->(wl-3) = ' '; } LTI_Insert(wa-buffer, '''); LTI_Insert(wa-buffer + 2, ' '); altered = true; break; } } for (i=WORDSIZE : ii = LowerCase(buffer->i); #Endif; ! TARGET_ force = false; if (buffer->i == '-' && buffer->(i+1) == '-' && buffer->(i+2) == '>') force = true; if (force) { if (i>WORDSIZE && buffer->(i-1) ~= ' ') { LTI_Insert(i++, ' '); altered = true; } if (buffer->(i+3) ~= ' ') { LTI_Insert(i+3, ' '); i++; altered = true; } i = i + 2; continue; } if (buffer->i == ':' && buffer->(i+1) == ':') force = true; if (buffer->i == '-' && buffer->(i+1) == '>') force = true; if (buffer->i == '.' && buffer->(i+1) == '&') { buffer->i = ']'; force = true; } if (buffer->i == '.' && buffer->(i+1) == '#') { buffer->i = ']'; force = true; } if (buffer->i == ']' && buffer->(i+1) == '&') force = true; if (buffer->i == ']' && buffer->(i+1) == '#') force = true; if (buffer->i == '+' && buffer->(i+1) == '+') force = true; if (buffer->i == '-' && buffer->(i+1) == '-') force = true; if (buffer->i == '&' && buffer->(i+1) == '&') force = true; if (buffer->i == '|' && buffer->(i+1) == '|') force = true; if (buffer->i == '~' && buffer->(i+1) == '~') force = true; if (buffer->i == '=' && buffer->(i+1) == '=') force = true; if (buffer->i == '~' && buffer->(i+1) == '=') force = true; if (buffer->i == '>' && buffer->(i+1) == '=') force = true; if (buffer->i == '<' && buffer->(i+1) == '=') force = true; if (buffer->i == '#' && buffer->(i+1) == '#') force = true; if (force) { if (i > WORDSIZE && buffer->(i-1) ~= ' ') { LTI_Insert(i++, ' '); altered = true; } if (buffer->(i+2) ~= ' ') { LTI_Insert(i+2, ' '); i++; altered = true; } i = i + 1; continue; } if (buffer->i == '+') force = true; if (buffer->i == '-') force = true; if (buffer->i == '*') force = true; if (buffer->i == '/') force = true; if (buffer->i == '%') force = true; if (buffer->i == '(') force = true; if (buffer->i == ')') force = true; if (buffer->i == '<' && buffer->(i-1) ~= ';') force = true; if (buffer->i == '>') force = true; if (buffer->i == ',') force = true; if (buffer->i == '.') force = true; if (buffer->i == '&') force = true; if (buffer->i == '|') force = true; if (buffer->i == '~') force = true; if (buffer->i == '=') force = true; if (force) { if (i > WORDSIZE && buffer->(i-1) ~= ' ') { LTI_Insert(i++, ' '); altered = true; } if (buffer->(i+1) ~= ' ') { LTI_Insert(i+1, ' '); i++; altered = true; } } } for (i=WORDSIZE : ii == '~') { buffer->i = '['; altered = true; } return altered; ]; ! end of InfixCheckLineSpaced Array InfixRV_rvals --> 32; Array InfixRV_lvals --> 32; Array InfixRV_op --> 32; Array InfixRV_lop --> 32; Array InfixRV_rop --> 32; Array InfixRV_types --> 32; Array InfixRV_commas --> 32; [ InfixInBounds addr index n; if (addr < #array__start || addr > #array__end) rtrue; for (n=#lowest_array_number : n<=#highest_array_number : n++) { if (addr == Symb__Tab(INFIXTT_ARRAY, n)) { if (temp__global3 == 1 or 3) temp__global2=temp__global2*WORDSIZE+WORDSIZE-1; if (index > temp__global2) { print "Array index out of range"; rfalse; } } } rtrue; ]; [ InfixRvalue acc w i n flag base expecting_term max maxi lop rop lvalside a b sysfun_f; if (InfixCheckLineSpaced()) return GPR_REPARSE; ! w = wn; for (i=0 : i<10 : i++) { wn = w; InfixRvalueTerm(); print i, "^"; } ! wn = w; expecting_term = true; base = 0; do { w = NextWordStopped(); if (expecting_term) { switch (w) { '-//': InfixRV_rvals-->n = 'unary-'; InfixRV_types-->n = base + 8; '[//': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 6; '[[': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 2; '++': InfixRV_rvals-->n = 'pre++'; InfixRV_types-->n = base + 9; '--': InfixRV_rvals-->n = 'pre--'; InfixRV_types-->n = base + 9; '(//': InfixRV_rvals-->n = w; InfixRV_types-->n = -3; base=base+100; ')//': InfixRV_rvals-->n = w; InfixRV_types-->n = -3; base=base-100; if (base < 0) { wn--; flag = true; } -1: flag = true; default: wn--; if (InfixRValueTerm() == 1) { InfixRV_rvals-->n = parsed_number; InfixRV_lvals-->n = infix_parsed_lvalue; InfixRV_types-->n = -1; expecting_term = false; } else flag = true; } } else { expecting_term = true; switch (w) { comma_word: InfixRV_rvals-->n = w; InfixRV_types-->n = base; '=//': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 1; '&&', '||': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 2; '==', '[=', '>//', '>=', 'n = w; InfixRV_types-->n = base + 3; 'or': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 4; '+//', '-//': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 5; '*//', '@{2f}//', '%//', '&//', '|//': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 6; '->', '-->': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 7; ']&', ']#': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 10; THEN1__WD: InfixRV_rvals-->n = w; InfixRV_types-->n = base + 12; '::': InfixRV_rvals-->n = w; InfixRV_types-->n = base + 13; '(//': InfixRV_rvals-->n = '(rcall'; InfixRV_types-->n = base + 11; base = base + 100; ')//': InfixRV_rvals-->n = w; InfixRV_types-->n = -3; base = base - 100; if (base < 0) { wn--; flag = true; } expecting_term = false; '++': InfixRV_rvals-->n = 'post++'; InfixRV_types-->n = base + 9; expecting_term = false; '--': InfixRV_rvals-->n = 'post--'; InfixRV_types-->n = base + 9; expecting_term = false; default: flag = true; } } n++; } until (flag || n == 32); if (base > 0) return -1; n--; if (n == 0) return -1; wn--; for (i=0 : ii ~= -3) acc = InfixRV_rvals-->i; InfixRV_op-->i = acc; } for (::) { ! for (i=0 : ii == -1) print InfixRV_rvals-->i, " "; ! else if (InfixRV_types-->i == -3) print " # "; ! else if (InfixRV_types-->i == -2) print " ## "; ! else print (address) InfixRV_rvals-->i, "_", InfixRV_types-->i, " "; ! } ! new_line; max = -2; for (i=0 : ii > max) { max = InfixRV_types-->i; maxi = i; } if (max == -1) { parsed_number = InfixRV_rvals-->maxi; return 1; } lop = maxi-1; rop = maxi+1; while (lop >= 0 && InfixRV_types-->lop < -1) lop--; while (rop < n && InfixRV_types-->rop < -1) rop++; if (lop >= 0) InfixRV_lop-->maxi = InfixRV_rvals-->lop; if (rop < n) InfixRV_rop-->maxi = InfixRV_rvals-->rop; flag = false; infix_term_type = INFIXTT_NUMBER; switch (InfixRV_rvals-->maxi) { comma_word: acc = (InfixRV_rvals-->rop); '=//', 'pre++', 'post++', 'pre--', 'post--': lvalside = lop; switch (InfixRV_rvals-->maxi) { '=//': acc = (InfixRV_rvals-->rop); 'pre++': acc = (InfixRV_rvals-->rop) + 1; lvalside = rop; 'pre--': acc = (InfixRV_rvals-->rop) - 1; lvalside = rop; 'post++': acc = (InfixRV_rvals-->lop) + 1; 'post--': acc = (InfixRV_rvals-->lop) - 1; } switch (InfixRV_op-->lvalside) { THEN1__WD: (InfixRV_lop-->lvalside).(InfixRV_rop-->lvalside) = acc; '->': if (InfixInBounds(InfixRV_lop-->lvalside, InfixRV_rop-->lvalside)) (InfixRV_lop-->lvalside)->(InfixRV_rop-->lvalside) = acc; '-->': if (InfixInBounds(InfixRV_lop-->lvalside, WORDSIZE * InfixRV_rop-->lvalside)) (InfixRV_lop-->lvalside)-->(InfixRV_rop-->lvalside) = acc; default: w = InfixRV_lvals-->lvalside; if (w == -1) return -1; #ifdef TARGET_ZCODE; @storew #globals_array w acc; #ifnot; @astore #globals_array w acc; #endif; } switch(InfixRV_rvals-->maxi) { 'post++': acc--; 'post--': acc++; } '(rcall': sysfun_f = false; switch (InfixRV_op-->lop) { THEN1__WD: a = InfixRV_lop-->lop; b = InfixRV_rop-->lop; default: a = InfixRV_rvals-->lop; b = call; if (InfixRV_lvals-->lop == INFIXTT_SYSFUN) sysfun_f = true; } w = 0; i = maxi + 1; base = 100; if (InfixRV_types-->i == -1 && InfixRV_rvals-->i == ')//') { if (sysfun_f) return -1; acc = a.b(); } else { while (base > 0) { if (InfixRV_types-->i == -3 && InfixRV_rvals-->i == ')//') base = base - 100; if (InfixRV_types-->i == -3 && InfixRV_rvals-->i == '(//') base = base + 100; if (InfixRV_op-->i == '(rcall') base = base + 100; if (base == 100 && InfixRV_op-->i == comma_word) { InfixRV_commas-->(w++) = i; ! print "Comma found at ", i, "^"; } i++; } ! print "Num args = ", w + 1, "^"; ! for (i=0 : i(InfixRV_commas-->i), "^"; ! print "arg: ", InfixRV_rvals-->rop, "^"; switch (w+1) { 1: if (sysfun_f) { b = InfixRV_rvals-->rop; infix_term_type = INFIXTT_NAMEDOBJECT; switch(a) { 'metaclass': acc = metaclass(b); 'parent': acc = parent(b); 'child': acc = child(b); 'children': acc = children(b); infix_term_type = INFIXTT_NUMBER; 'random': acc = random(b); infix_term_type = INFIXTT_NUMBER; 'sibling': acc = sibling(b); } } else acc = a.b(InfixRV_rvals-->rop); 2: if (sysfun_f) return -1; acc = a.b(InfixRV_lop-->(InfixRV_commas-->0), InfixRV_rvals-->rop); 3: if (sysfun_f) return -1; acc = a.b(InfixRV_lop-->(InfixRV_commas-->0), InfixRV_lop-->(InfixRV_commas-->1), InfixRV_rvals-->rop); 4: if (sysfun_f) return -1; acc = a.b(InfixRV_lop-->(InfixRV_commas-->0), InfixRV_lop-->(InfixRV_commas-->1), InfixRV_lop-->(InfixRV_commas-->2), InfixRV_rvals-->rop); 5: if (sysfun_f) return -1; acc = a.b(InfixRV_lop-->(InfixRV_commas-->0), InfixRV_lop-->(InfixRV_commas-->1), InfixRV_lop-->(InfixRV_commas-->2), InfixRV_lop-->(InfixRV_commas-->3), InfixRV_rvals-->rop); default: return -1; } } '+//': acc = (InfixRV_rvals-->lop) + (InfixRV_rvals-->rop); '-//': acc = (InfixRV_rvals-->lop) - (InfixRV_rvals-->rop); '*//': acc = (InfixRV_rvals-->lop) * (InfixRV_rvals-->rop); '@{2f}//': acc = (InfixRV_rvals-->lop) / (InfixRV_rvals-->rop); '%//': acc = (InfixRV_rvals-->lop) % (InfixRV_rvals-->rop); THEN1__WD: acc = (InfixRV_rvals-->lop) . (InfixRV_rvals-->rop); '->': acc = (InfixRV_rvals-->lop) -> (InfixRV_rvals-->rop); '-->': acc = (InfixRV_rvals-->lop) --> (InfixRV_rvals-->rop); ']&': acc = (InfixRV_rvals-->lop) .& (InfixRV_rvals-->rop); ']#': acc = (InfixRV_rvals-->lop) .# (InfixRV_rvals-->rop); '::': acc = (InfixRV_rvals-->lop) :: (InfixRV_rvals-->rop); '&//': acc = (InfixRV_rvals-->lop) & (InfixRV_rvals-->rop); '|//': acc = (InfixRV_rvals-->lop) | (InfixRV_rvals-->rop); '&&': acc = (InfixRV_rvals-->lop) && (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '||': acc = (InfixRV_rvals-->lop) || (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'lop) < (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '<=': acc = (InfixRV_rvals-->lop) <= (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '>//': acc = (InfixRV_rvals-->lop) > (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '>=': acc = (InfixRV_rvals-->lop) >= (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '==': acc = (InfixRV_rvals-->lop) == (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '[=': acc = (InfixRV_rvals-->lop) ~= (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'has': acc = (InfixRV_rvals-->lop) has (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'hasnt': acc = (InfixRV_rvals-->lop) hasnt (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'in': acc = (InfixRV_rvals-->lop) in (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'notin': acc = (InfixRV_rvals-->lop) notin (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'provides': acc = (InfixRV_rvals-->lop) provides (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; 'ofclass': acc = (InfixRV_rvals-->lop) ofclass (InfixRV_rvals-->rop); infix_term_type = INFIXTT_LOGICAL; '[[': acc = ~~ (InfixRV_rvals-->rop); flag = true; '[//': acc = ~ (InfixRV_rvals-->rop); flag = true; 'unary-': acc = - (InfixRV_rvals-->rop); flag = true; } ! end of switch(InfixRV_rvals-->maxi) InfixRV_rvals-->maxi = acc; InfixRV_types-->maxi = -1; if (rop < n) InfixRV_types-->rop = -2; if (flag == false && lop >= 0) InfixRV_types-->lop = -2; } ! end of for (::) ]; ! end of InfixRvalue ! ------------------------------------------------------------------------ [ InfixWelcomeSub; print "; Welcome to the ~Infix~ debugger (1/040828), which makes the following verbs available:^^ ~; ~: evaluates this Inform expression: e.g. ~; location~ will print the value of the variable ~location~, ~; 3*5+1~ will print 16, ~; children(old cloth bag)~ will tell you how many items are in it. (You can name objects either by their names inside the source code, such as ~d_obj~, or by typing the names by which the game's parser would normally know them, such as ~floor~: the effect is the same.)^ Any expression is allowed except that you can't use double-quoted strings of text: but you can send messages, call routines or assign values to variables, properties and array entries. ^ ~; score++~ is one way to get on in the world. ^ ~; deadflag = true~ is one way to get out of it. ^ ~; StopDaemon(nasty little dwarf)~ will keep you from being pestered.^ Conditions like ~; score>20~ are also allowed and print either 1 (if true) or 0 (if false).^^"; print "~;examine ~ or ~;x ~ gives full details of whatever it is. For instance, ~;x ##Take~ describes the Take action; ~;x Class~ the class Class; ~;x 'drop'~ the dictionary word ~drop~ and so on for numbers, routines, arrays and of course objects. ~;xo~ examines something as an object, so for instance ~;x location~ tells you about the variable ~location~, but ~;xo location~ tells you what object it refers to.^^"; print "~;give~, ~;remove~ and ~;move~ work like the corresponding Inform statements.^^"; print "~;<~ causes an action: for instance, ~;< Eat cheese~.^^"; print "~;watch~ or ~;w~ can set a watch on various activities: type just ~;w~ for details.^^"; print "~;inventory~ or ~;i~ describes the contents of this story file.^"; ]; [ InfixEvalSub; InfixExamineP(true); ]; [ InfixActionSub; print "; <", (InfixPrintAction) infix_lvalue; if (noun) print " (", (the) noun, ")"; if (second) print " (", (the) second, ")"; print ">^"; if (second) <<(infix_lvalue) noun second>>; if (noun) <<(infix_lvalue) noun>>; <<(infix_lvalue)>>; ]; [ InfixGiveSub f t; print "; give (", (the) noun, ") "; if (second < 0) { second = ~second; f=true; } #Ifdef VN_1630; t = NUM_ATTR_BYTES * 8; #Ifnot; t = 48; #Endif; ! VN_ if (second < 0 || second >= t) ""; if (f) print "@@126"; print (DebugAttribute) second; #Ifdef TARGET_ZCODE; if (f) @clear_attr noun second; else @set_attr noun second; #Ifnot; ! TARGET_GLULX t = second + 8; if (f) @astorebit noun t 0; ! give noun ~second; else @astorebit noun t 1; ! give noun second; #Endif; ! TARGET_ if (t); ! quell unused n variable warning new_line; ]; [ InfixMoveSub; print "; move (", (the) noun, ") to (", (the) second, ")^"; move noun to second; ]; [ InfixRemoveSub; print "; remove (", (the) noun, ")^"; remove noun; ]; [ InfixHex x y; #Ifdef TARGET_ZCODE; y = (x & $7f00) / $100; if (x < 0) y = y + $80; #Ifnot; ! TARGET_GLULX y = (x & $7f000000) / $1000000; if (x < 0) y = y + $80; print (Infixhexdigit) y/$10, (Infixhexdigit) y; y = x & $ff0000 / $10000; print (Infixhexdigit) y/$10, (Infixhexdigit) y; y = (x & $ff00) / $100; #Endif; ! TARGET_ x = x & $ff; print (Infixhexdigit) y/$10, (Infixhexdigit) y, (Infixhexdigit) x/$10, (Infixhexdigit) x; ]; [ Infixhexdigit x; x = x % $10; if (x < 10) print x; else print (char) 'a'+x-10; ]; [ InfixExamineOSub; infix_data1 = metaclass(noun); #Ifdef TARGET_GLULX; ! different coding for Glulx if (infix_data1 == Object) infix_data1 = 2; if (infix_data1 == Class) infix_data1 = 1; #Endif; ! TARGET_ infix_term_type = INFIXTT_CONSTANT; InfixExamineP(false); ]; [ InfixExamineSSub; infix_term_type = INFIXTT_STATICSTRING; InfixExamineP(false); ]; [ InfixExamineSub; InfixExamineP(false); ]; [ InfixExamineP brief x a b w flag lines; switch (infix_term_type) { INFIXTT_NUMBER: if (brief) "; == ", noun; print "; The number ", noun, " == $", (InfixHex) noun; if (noun >= 32 && noun < 127) print " == '", (char) noun, "'"; new_line; INFIXTT_NAMEDOBJECT: print "~", (name) noun, "~ (", noun, ")^"; if (brief) return; <>; INFIXTT_CONSTANT: if (brief) "; == ", noun; switch (infix_data1 & 15) { nothing: print "; Constant ", (InfixPrintConstant) infix_parsed_lvalue, " == ", noun, "^"; 2: <>; 1: print "Class ", (name) noun, "^"; objectloop (a ofclass noun) { if (flag) print ", "; else print "Contains: "; print (name) a, " (", a, ")"; flag=true; } if (flag == false) "No object is of this class"; } new_line; INFIXTT_ATTRIBUTE: if (brief) "; == ", noun; if (noun >= 48 || noun < 0) "; No such attribute"; print "; Attribute ", (InfixPrintAttribute) noun, " (numbered ", noun, ")^"; objectloop (x has noun) { if (flag) print ", "; else print "Each of these ~has ", (InfixPrintAttribute) noun, "~: "; print (name) x, " (", x, ")"; flag = true; } if (flag == false) "No object ~has ", (InfixPrintAttribute) noun, "~"; new_line; INFIXTT_PROPERTY: if (brief) "; == ", noun; print "; Property ", (property) noun, " (numbered ", noun, ")^"; objectloop (x provides noun) { if (flag) print ", "; else print "Provided by: "; print (name) x, " (", x, ")"; flag = true; } if (flag == false) "Which is not provided by any object"; new_line; INFIXTT_DWORD: if (brief) "; == ", noun; if (noun == 0) "; This word is not in the dictionary"; a = noun->#dict_par1; print "; Dictionary word '", (address) noun; if (a & 4) print "//p"; print "' (address ", noun, ")"; if (a) { print ": "; if (a & 2) print "meta "; if (a & 1) print "verb "; if (a & 8) print "preposition "; if (a & 4) print "pluralising "; if (a & 128) print "noun "; } new_line; if (a & 1) <>; INFIXTT_ROUTINE: if (brief) "; == ", noun; print "; Routine ", (InfixPrintRoutine) infix_parsed_lvalue, " (number ", infix_parsed_lvalue, ", packed address ", noun, ")^"; INFIXTT_GLOBAL: if (brief) "; == ", noun; print "; Global ", (InfixPrintGlobal) infix_parsed_lvalue, " == ", noun, "^"; INFIXTT_ARRAY: if (brief) "; == ", noun; print "; Array ", (InfixPrintArray) infix_parsed_lvalue, " "; infix_data1 = infix_data1 % 16; switch (infix_data1) { 0: print "->"; a=0; 1: print "-->"; a=0; 2: print "string"; a=1; 3: print "table"; a=1; 4: print "buffer"; a=WORDSIZE; } print " ", infix_data2 + 1 - a, "^; == "; b = infix_data2; for (w=b : w>=a : w--) if (infix_data1 == 0 or 2 or 4) { if (noun->w) break; } else { if (noun-->w) break; } if (b-w < 5) w=b; for (: x<=w : x++) { if (infix_data1 == 0 or 2 or 4) print noun->x, " "; else print noun-->x, " "; if (x+1 == a) print ": "; } if (w < b) print "(then ", b-w, " zero entries)"; new_line; INFIXTT_ACTION: if (brief) "; == ", noun; if (noun >= #lowest_fake_action_number && noun <= #highest_fake_action_number) "; Fake action ", (InfixPrintFakeAction) noun, " (numbered ", noun, ")^Is not generated by any grammar"; print "; Action ", (InfixPrintAction) noun, " (numbered ", noun, ")^"; #Ifdef TARGET_ZCODE; w = HDR_DICTIONARY-->0; for (b=0 : b<(HDR_DICTIONARY-->0 + 5)-->0 : b++) { w = HDR_DICTIONARY-->0 + 7 + b*9; if ((w->#dict_par1) & 1) { a = (HDR_STATICMEMORY-->0)-->($ff-(w->#dict_par2)); lines = a->0; a++; for (: lines>0 : lines--) { a = UnpackGrammarLine(a); if (action_to_be == noun) { print "'", (address) w, "' "; DebugGrammarLine(); new_line; flag = true; } } } } #Ifnot; ! TARGET_GLULX for (b=0 : b < #dictionary_table-->0 : b++) { w = #dictionary_table + WORDSIZE + b*(DICT_WORD_SIZE + 7); if ((w->#dict_par1) & 1) { a = (#grammar_table)-->(DictionaryWordToVerbNum(w) + 1); lines = a->0; a++; for (: lines>0 : lines--) { a = UnpackGrammarLine(a); if (action_to_be == noun) { print "'", (address) w, "' "; DebugGrammarLine(); new_line; flag = true; } } } } #Endif; ! TARGET_ if (flag == 0) "Is not generated by any grammar"; INFIXTT_SYSFUN: if (brief) "; == ", noun; "; System function ~", (address) infix_parsed_lvalue, "~ has not been overridden by any routine and so has its standard definition."; INFIXTT_STATICSTRING: if (brief) "; == ", noun; if (metaclass(noun) ~= String) "; ", noun, " is not a string."; print "~", (string) noun, "~^"; INFIXTT_LOGICAL: if (noun == true) "; true"; if (noun == false) "; false"; "; ", noun; } new_line; ]; ! end of InfixExamineP [ InfixDescribeWatchSub x y z s flag aflag; print "; The Infix ~;watch~ verb allows you to set a watch on any named routine(s) or objects: for instance ~;watch ScoreSub~ or ~;watch silver bars~. You can also: ^ ~;watch objects~: changes to attribute or property settings"; if (debug_flag & 8) print " (on)"; else print " (off)"; print ";^ ~;watch timers~: the running of timers and daemons each turn"; if (debug_flag & 4) print " (on)"; else print " (off)"; print ";^ ~;watch messages~: all messages sent"; if (debug_flag & 1) print " (on)"; else print " (off)"; print ";^ ~;watch actions~: all actions generated"; if (debug_flag & 2) print " (on)"; else print " (off)"; print ".^~~;watch~ can be abbreviated to ~;w~ and use ~off~ to stop watching: for instance ~;w location off~.^"; aflag = debug_flag; objectloop (x has infix__watching) flag = true; aflag = aflag || flag; if (flag) print "The following objects are currently being watched: "; flag = false; objectloop (x has infix__watching) { if (flag) print ", "; flag = true; print (name) x, " (", x, ")"; } if (flag) new_line; s = (#highest_routine_number - #lowest_routine_number); if (s%8 == 0) s=s/8; else s=s/8+1; for (flag=false,x=0 : xx) flag = true; aflag = aflag || flag; if (flag) print "The following routines are currently being watched: "; for (x=0,flag=false : xx) & y) { if (flag) print ", "; flag = true; print (InfixPrintRoutine) #lowest_routine_number + x*8 + z; } } } if (flag) new_line; if (aflag == false) "At present, nothing is being watched."; ]; [ InfixWatchOnSub i j k l; if (noun == 0) return InfixDescribeWatchSub(); if (infix_term_type == INFIXTT_ROUTINE) { i = infix_parsed_lvalue/8; for (j=0,k=1 : ji; l = l | k; #ifdef TARGET_ZCODE; @storeb #routine_flags_array i l; #ifnot; ! TARGET_GLULX @astoreb #routine_flags_array i l; #endif; ! TARGET_ "; Watching routine ", (InfixPrintRoutine) infix_parsed_lvalue, "."; } if (metaclass(noun) == Object) { give noun infix__watching; "; Watching object ~", (name) noun, "~ (", noun, ")."; } InfixDescribeWatchSub(); ]; [ InfixWatchOffSub i j k l; if (noun == 0) return InfixDescribeWatchSub(); if (infix_term_type == INFIXTT_ROUTINE) { i = infix_parsed_lvalue/8; for (j=0,k=1 : ji; l = l & (~k); #ifdef TARGET_ZCODE; @storeb #routine_flags_array i l; #ifnot; ! TARGET_GLULX @astoreb #routine_flags_array i l; #endif; ! TARGET "; Not watching ", (InfixPrintRoutine) infix_parsed_lvalue, "."; } if (metaclass(noun) == Object) { #ifdef TARGET_ZCODE; @clear_attr noun infix__watching; #ifnot; ! TARGET_GLULX @astorebit noun (infix__watching+8) 0; #endif; ! TARGET_ "; Not watching object ~", (name) noun, "~ (", noun, ")."; } InfixDescribeWatchSub(); ]; [ InfixList from to tab filter i flag; print "^ "; for (i=from : i<=to : i++) if (tab-->(i-from)) { flag = true; if (tab == #array_names_array) { Symb__Tab(INFIXTT_ARRAY, i); flag = ~~(temp__global3 & 16); } if (tab == #routine_names_array) { Symb__Tab(INFIXTT_ROUTINE,i); flag = ~~(temp__global3 & 16); } if (tab == #constant_names_array) { Symb__Tab(INFIXTT_CONSTANT,i); flag = (~~(temp__global3 & 16)) && (temp__global3 % 16 == filter); } if (flag) print (string) tab-->(i-from), " "; } new_line; ]; [ InfixInvSub i; print (string) Story, (string) Headline; print " ", (number) #highest_object_number - #lowest_object_number + 1, " objects;^"; print " non-library object-name constants:"; InfixList(#lowest_constant_number, #highest_constant_number, #constant_names_array, 2); print " ", (number) #highest_class_number - #lowest_class_number + 1, " classes:^ "; for (i=#lowest_class_number : i<=#highest_class_number : i++) print (name) #class_objects_array-->i, " "; new_line; print " non-library arrays:"; InfixList(#lowest_array_number, #highest_array_number, #array_names_array); print " non-library routines:"; InfixList(#lowest_routine_number, #highest_routine_number, #routine_names_array); print " non-library constants:"; InfixList(#lowest_constant_number, #highest_constant_number, #constant_names_array, 0); print " (common) properties:"; #Ifdef TARGET_ZCODE; InfixList(#lowest_property_number, INDIV_PROP_START-1, #property_names_array); #Ifnot; ! TARGET_GLULX InfixList(#lowest_property_number, #identifiers_table-->1 - 1, #property_names_array); #Endif; ! TARGET_ print " (individual) properties:"; #Ifdef TARGET_ZCODE; InfixList(INDIV_PROP_START, #highest_property_number, #property_names_array + 126); #Ifnot; ! TARGET_GLULX InfixList(INDIV_PROP_START, #highest_property_number, #identifiers_table-->2); #Endif; ! TARGET_ print " attributes:"; InfixList(#lowest_attribute_number, #highest_attribute_number, #attribute_names_array); if (true) return; print " variables:"; InfixList(#lowest_global_number, #highest_global_number, #global_names_array); print " actions:"; InfixList(#lowest_action_number, #highest_action_number, #action_names_array); print " fake actions:"; InfixList(#lowest_fake_action_number, #highest_fake_action_number, #fake_action_names_array); ]; Verb meta ';i' ';inv' ';inventory' * -> InfixInv; Verb meta ';x' ';examine' * InfixRvalue -> InfixExamine; Verb meta ';xo' ';examineo' * InfixRvalue -> InfixExamineO; Verb meta ';xs' ';examines' * InfixRvalue -> InfixExamineS; Verb meta ';<' * InfixActionToken -> InfixAction * InfixActionToken InfixRvalue -> InfixAction * InfixActionToken InfixRvalue InfixRvalue -> InfixAction; Verb meta ';//' * -> InfixWelcome * InfixRvalue -> InfixEval; Verb meta ';give' * InfixRvalue InfixRvalue -> InfixGive; Verb meta ';move' * InfixRvalue "to" InfixRvalue -> InfixMove; Verb meta ';remove' * InfixRvalue -> InfixRemove; Verb meta ';watch' ';w' * -> InfixWatchOn * "timers"/"daemons" -> TimersOn * "timers"/"daemons" "off" -> TimersOff * "actions" -> ActionsOn * "actions" "off" -> ActionsOff * "messages" -> RoutinesOn * "messages" "off" -> RoutinesOff * "objects" -> ChangesOn * "objects" "off" -> ChangesOff * InfixRvalueTerm -> InfixWatchOn * InfixRvalueTerm "off" -> InfixWatchOff; #Endif; ! DEBUG ! ============================================================================== This is version 6.12.2 of the Inform Library, Copyright Graham Nelson 1993-2004, David Griffith 2012-2018 Full release notes and instructions are available at http://www.inform-fiction.org and http://www.ifarchive.org/indexes/if-archiveXinfocomXcompilersXinform6.html The Git repository is at https://gitlab.com/DavidGriffith/inform6lib Bug reports may be filed at the above Gitlab page, the Inform Bug Tracker at http://inform7.com/mantis/, or directly with David Griffith Thanks go to: Sarganar, David Kinder, Fredrik Ramsberg, Roger Firth, Marshall Vandegrift, Nathan Summers, Emerick Rogul, Martin Bays, Cedrick Knight, Jesse McGrew, Nathan Schwartzman, Andrew Plotkin, Vince Laviano, Jesse Pavel ! ============================================================================== ! ENGLISH: Language Definition File ! ! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180611 ! ! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 ! ! This code is licensed under either the traditional Inform license as ! described by the DM4 or the Artistic License version 2.0. See the ! file COPYING in the distribution archive. ! ! This file is automatically Included in your game file by "parserm". ! Strictly, "parserm" includes the file named in the "language__" variable, ! whose contents can be defined by+language_name=XXX compiler setting (with a ! default of "english"). ! ! Define the constant DIALECT_US before including "Parser" to obtain American ! English. ! ============================================================================== System_file; #Ifndef LIBRARY_ENGLISH; ! if this file is already included, ! don't try to include it again. ! ------------------------------------------------------------------------------ ! Part I. Preliminaries ! ------------------------------------------------------------------------------ Constant EnglishNaturalLanguage; ! Needed to keep old pronouns mechanism Class CompassDirection with number 0, article "the", description [; if (location provides compass_look && location.compass_look(self)) rtrue; if (self.compass_look()) rtrue; L__M(##Look, 7, self); ], compass_look false, parse_name [; return -1; ] has scenery; Object Compass "compass" has concealed; #Ifndef WITHOUT_DIRECTIONS; CompassDirection -> n_obj with short_name "north", door_dir n_to, name 'n//' 'north'; CompassDirection -> s_obj with short_name "south", door_dir s_to, name 's//' 'south'; CompassDirection -> e_obj with short_name "east", door_dir e_to, name 'e//' 'east'; CompassDirection -> w_obj with short_name "west", door_dir w_to, name 'w//' 'west'; CompassDirection -> ne_obj with short_name "northeast", door_dir ne_to, name 'ne' 'northeast'; CompassDirection -> nw_obj with short_name "northwest", door_dir nw_to, name 'nw' 'northwest'; CompassDirection -> se_obj with short_name "southeast", door_dir se_to, name 'se' 'southeast'; CompassDirection -> sw_obj with short_name "southwest", door_dir sw_to, name 'sw' 'southwest'; CompassDirection -> u_obj with short_name "up above", door_dir u_to, name 'u//' 'up' 'ceiling' 'above' 'sky'; CompassDirection -> d_obj with short_name "ground", door_dir d_to, name 'd//' 'down' 'floor' 'below' 'ground'; #endif; ! WITHOUT_DIRECTIONS CompassDirection -> in_obj with short_name "inside", door_dir in_to; CompassDirection -> out_obj with short_name "outside", door_dir out_to; ! ------------------------------------------------------------------------------ ! Part II. Vocabulary ! ------------------------------------------------------------------------------ Constant AGAIN1__WD = 'again'; Constant AGAIN2__WD = 'g//'; Constant AGAIN3__WD = 'again'; Constant OOPS1__WD = 'oops'; Constant OOPS2__WD = 'o//'; Constant OOPS3__WD = 'oops'; Constant UNDO1__WD = 'undo'; Constant UNDO2__WD = 'undo'; Constant UNDO3__WD = 'undo'; Constant ALL1__WD = 'all'; Constant ALL2__WD = 'each'; Constant ALL3__WD = 'every'; Constant ALL4__WD = 'everything'; Constant ALL5__WD = 'both'; Constant AND1__WD = 'and'; Constant AND2__WD = 'and'; Constant AND3__WD = 'and'; Constant BUT1__WD = 'but'; Constant BUT2__WD = 'except'; Constant BUT3__WD = 'but'; Constant ME1__WD = 'me'; Constant ME2__WD = 'myself'; Constant ME3__WD = 'self'; Constant OF1__WD = 'of'; Constant OF2__WD = 'of'; Constant OF3__WD = 'of'; Constant OF4__WD = 'of'; Constant OTHER1__WD = 'another'; Constant OTHER2__WD = 'other'; Constant OTHER3__WD = 'other'; Constant THEN1__WD = 'then'; Constant THEN2__WD = 'then'; Constant THEN3__WD = 'then'; Constant NO1__WD = 'n//'; Constant NO2__WD = 'no'; Constant NO3__WD = 'no'; Constant YES1__WD = 'y//'; Constant YES2__WD = 'yes'; Constant YES3__WD = 'yes'; Constant AMUSING__WD = 'amusing'; Constant FULLSCORE1__WD = 'fullscore'; Constant FULLSCORE2__WD = 'full'; Constant QUIT1__WD = 'q//'; Constant QUIT2__WD = 'quit'; Constant RESTART__WD = 'restart'; Constant RESTORE__WD = 'restore'; Array LanguagePronouns table ! word possible GNAs connected ! to follow: to: ! a i ! s p s p ! mfnmfnmfnmfn 'it' $$001000111000 NULL 'him' $$100000100000 NULL 'her' $$010000010000 NULL 'them' $$000111000111 NULL; Array LanguageDescriptors table ! word possible GNAs descriptor connected ! to follow: type: to: ! a i ! s p s p ! mfnmfnmfnmfn 'my' $$111111111111 POSSESS_PK 0 'this' $$111111111111 POSSESS_PK 0 'these' $$000111000111 POSSESS_PK 0 'that' $$111111111111 POSSESS_PK 1 'those' $$000111000111 POSSESS_PK 1 'his' $$111111111111 POSSESS_PK 'him' 'her' $$111111111111 POSSESS_PK 'her' 'their' $$111111111111 POSSESS_PK 'them' 'its' $$111111111111 POSSESS_PK 'it' 'the' $$111111111111 DEFART_PK NULL 'a//' $$111000111000 INDEFART_PK NULL 'an' $$111000111000 INDEFART_PK NULL 'some' $$000111000111 INDEFART_PK NULL 'lit' $$111111111111 light NULL 'lighted' $$111111111111 light NULL 'unlit' $$111111111111 (-light) NULL; Array LanguageNumbers table 'one' 1 'two' 2 'three' 3 'four' 4 'five' 5 'six' 6 'seven' 7 'eight' 8 'nine' 9 'ten' 10 'eleven' 11 'twelve' 12 'thirteen' 13 'fourteen' 14 'fifteen' 15 'sixteen' 16 'seventeen' 17 'eighteen' 18 'nineteen' 19 'twenty' 20; ! ------------------------------------------------------------------------------ ! Part III. Translation ! ------------------------------------------------------------------------------ [ LanguageToInformese; ]; ! ------------------------------------------------------------------------------ ! Part IV. Printing ! ------------------------------------------------------------------------------ Constant LanguageAnimateGender = male; Constant LanguageInanimateGender = neuter; Constant LanguageContractionForms = 2; ! English has two: ! 0 = starting with a consonant ! 1 = starting with a vowel [ LanguageContraction text; if (text->0 == 'a' or 'e' or 'i' or 'o' or 'u' or 'A' or 'E' or 'I' or 'O' or 'U') return 1; return 0; ]; Array LanguageArticles --> ! Contraction form 0: Contraction form 1: ! Cdef Def Indef Cdef Def Indef "The " "the " "a " "The " "the " "an " ! Articles 0 "The " "the " "some " "The " "the " "some "; ! Articles 1 ! a i ! s p s p ! m f n m f n m f n m f n Array LanguageGNAsToArticles --> 0 0 0 1 1 1 0 0 0 1 1 1; [ LanguageDirection d; switch (d) { n_to: print "north"; s_to: print "south"; e_to: print "east"; w_to: print "west"; ne_to: print "northeast"; nw_to: print "northwest"; se_to: print "southeast"; sw_to: print "southwest"; u_to: print "up"; d_to: print "down"; in_to: print "in"; out_to: print "out"; default: return RunTimeError(9,d); } ]; [ LanguageNumber n f; if (n == 0) { print "zero"; rfalse; } if (n < 0) { print "minus "; n = -n; } if (n >= 1000) { print (LanguageNumber) n/1000, " thousand"; n = n%1000; f = 1; } if (n >= 100) { if (f == 1) print ", "; print (LanguageNumber) n/100, " hundred"; n = n%100; f = 1; } if (n == 0) rfalse; #Ifdef DIALECT_US; if (f == 1) print " "; #Ifnot; if (f == 1) print " and "; #Endif; switch (n) { 1: print "one"; 2: print "two"; 3: print "three"; 4: print "four"; 5: print "five"; 6: print "six"; 7: print "seven"; 8: print "eight"; 9: print "nine"; 10: print "ten"; 11: print "eleven"; 12: print "twelve"; 13: print "thirteen"; 14: print "fourteen"; 15: print "fifteen"; 16: print "sixteen"; 17: print "seventeen"; 18: print "eighteen"; 19: print "nineteen"; 20 to 99: switch (n/10) { 2: print "twenty"; 3: print "thirty"; 4: print "forty"; 5: print "fifty"; 6: print "sixty"; 7: print "seventy"; 8: print "eighty"; 9: print "ninety"; } if (n%10 ~= 0) print "-", (LanguageNumber) n%10; } ]; [ LanguageTimeOfDay hours mins i; i = hours%12; if (i == 0) i = 12; if (i < 10) print " "; print i, ":", mins/10, mins%10; if ((hours/12) > 0) print " pm"; else print " am"; ]; [ LanguageVerb i; switch (i) { 'i//','inv','inventory': print "take inventory"; 'l//': print "look"; 'x//': print "examine"; 'z//': print "wait"; default: rfalse; } rtrue; ]; ! ---------------------------------------------------------------------------- ! LanguageVerbIsDebugging is called by SearchScope. It should return true ! if word w is a debugging verb which needs all objects to be in scope. ! ---------------------------------------------------------------------------- #Ifdef DEBUG; [ LanguageVerbIsDebugging w; if (w == 'purloin' or 'tree' or 'abstract' or 'gonear' or 'scope' or 'showobj') rtrue; rfalse; ]; #Endif; ! ---------------------------------------------------------------------------- ! LanguageVerbLikesAdverb is called by PrintCommand when printing an UPTO_PE ! error or an inference message. Words which are intransitive verbs, i.e., ! which require a direction name as an adverb ('walk west'), not a noun ! ('I only understood you as far as wanting to touch /the/ ground'), should ! cause the routine to return true. ! ---------------------------------------------------------------------------- [ LanguageVerbLikesAdverb w; if (w == 'look' or 'go' or 'push' or 'walk') rtrue; rfalse; ]; ! ---------------------------------------------------------------------------- ! LanguageVerbMayBeName is called by NounDomain when dealing with the ! player's reply to a "Which do you mean, the short stick or the long ! stick?" prompt from the parser. If the reply is another verb (for example, ! LOOK) then then previous ambiguous command is discarded /unless/ ! it is one of these words which could be both a verb /and/ an ! adjective in a 'name' property. ! ---------------------------------------------------------------------------- [ LanguageVerbMayBeName w; if (w == 'long' or 'short' or 'normal' or 'brief' or 'full' or 'verbose') rtrue; rfalse; ]; Constant NKEY__TX = "N = next subject"; Constant PKEY__TX = "P = previous"; Constant QKEY1__TX = " Q = resume game"; Constant QKEY2__TX = "Q = previous menu"; Constant RKEY__TX = "RETURN = read subject"; Constant NKEY1__KY = 'N'; Constant NKEY2__KY = 'n'; Constant PKEY1__KY = 'P'; Constant PKEY2__KY = 'p'; Constant QKEY1__KY = 'Q'; Constant QKEY2__KY = 'q'; Constant SCORE__TX = "Score: "; Constant MOVES__TX = "Moves: "; Constant TIME__TX = "Time: "; Constant CANTGO__TX = "You can't go that way."; Constant FORMER__TX = "your former self"; Constant MYFORMER__TX = "my former self"; Constant YOURSELF__TX = "yourself"; Constant MYSELF__TX = "myself"; Constant YOU__TX = "You"; Constant DARKNESS__TX = "Darkness"; Constant THOSET__TX = "those things"; Constant THAT__TX = "that"; Constant OR__TX = " or "; Constant NOTHING__TX = "nothing"; Constant IS__TX = " is"; Constant ARE__TX = " are"; Constant IS2__TX = "is "; Constant ARE2__TX = "are "; Constant WAS__TX = " was"; Constant WERE__TX = " were"; Constant WAS2__TX = "was "; Constant WERE2__TX = "were "; Constant AND__TX = " and "; Constant WHOM__TX = "whom "; Constant WHICH__TX = "which "; Constant COMMA__TX = ", "; Constant COLON__TX = ": "; ! ---------------------------------------------------------------------------- ! FYI on nominative pronouns versus accusative pronouns... ! Consider the sentence "She hit him.". ! "She" is in the nominative case. It appears at the beginning of a sentence. ! "him" is in the accusative case. It won't appear at the beginning. ! ---------------------------------------------------------------------------- ! Accusative [ ThatOrThose obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "me"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "you"; return; } if (obj has pluralname) { print "those"; return; } if (obj has female) { print "her"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "him"; return; } print "that"; ]; ! Accusative [ ItOrThem obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "myself"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "yourself"; return; } if (obj has pluralname) { print "them"; return; } if (obj has female) { print "her"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "him"; return; } print "it"; ]; ! Nominative [ CThatOrThose obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { print "I"; return; } if (player.narrative_voice == 3) { CDefart(player); return; } } print "You"; return; } if (obj has pluralname) { print "Those"; return; } if (obj has female) { print "She"; return; } if (obj has male or animate) { if (obj hasnt neuter) { print "He"; return; } } print "That"; ]; ! Nominative [ CTheyreorThats obj; if (obj == player) { if (player provides narrative_voice) { if (player.narrative_voice == 1) { Tense("I'm", "I was"); return; } if (player.narrative_voice == 3) { CDefart(player); Tense("'s", " was"); return; } } Tense("You're", "You were"); return; } if (obj has pluralname) { Tense("They're", "They were"); return; } if (obj has female) { Tense("She's", "She was"); return; } if (obj has male or animate) { if (obj hasnt neuter) { Tense("He's", "He was"); return; } } Tense("That's", "That was"); ]; [ IsOrAre obj; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { if (obj has pluralname || obj == player) print "were"; else print "was"; return; } if (obj has pluralname || obj == player) print "are"; else print "is"; return; ]; [ nop x; x = x; ]; ! print rule to absorb unwanted return value [ SubjectNotPlayer obj reportage v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v2 = past; v3 = past; } if (reportage && actor ~= player) { L__M(##Miscellany, 60, actor); if (obj == actor) { print (theActor) obj, " ", (string) v3; return; } else if (obj has pluralname) { print (the) obj, " ", (string) v2; return; } else {print (the) obj, " ", (string) v3; return;} } else if (obj has pluralname) { print (The) obj, " ", (string) v2; return;} else { print (The) obj, " ", (string) v3; return;} ]; [ CSubjectVoice obj v1 v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v1 = past; v2 = past; v3 = past; } else { if (v2 == 0) v2 = v1; if (v3 == 0) v3 = v1; } if (obj ~= player) { print (string) v3; return; } if (player provides narrative_voice) switch (player.narrative_voice) { 1: print (string) v1; return; 2: ! Do nothing. 3: print (string) v3; return; default: RunTimeError(16, player.narrative_voice); } print (string) v2; return; ]; [ CSubjectVerb obj reportage nocaps v1 v2 v3 past; if (past && player provides narrative_tense && player.narrative_tense == PAST_TENSE) { v1 = past; v2 = past; v3 = past; } else { if (v2 == 0) v2 = v1; if (v3 == 0) v3 = v1; } if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: print "I ", (string) v1; return; 2: ! Do nothing. 3: CDefart(player); print " ", (string) v3; return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) { print "you ", (string) v2; return; } print "You ", (string) v2; return; } SubjectNotPlayer(obj, reportage, v2, v3); ]; [ CSubjectIs obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'm", "I was"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" is", " was"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you're", "you were"); else Tense("You're", "You were"); return; } SubjectNotPlayer(obj, reportage, "are", "is", "was"); ]; [ CSubjectIsnt obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'm not", "I wasn't"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" isn't", " wasn't"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you aren't", "you weren't"); else Tense("You aren't", "You weren't"); return; } SubjectNotPlayer(obj, reportage, "aren't", "isn't", "wasn't"); ]; [ CSubjectHas obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I've", "I had"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" has", " had"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you've", "you'd"); else Tense("You've", "You'd"); return; } SubjectNotPlayer(obj, reportage, "have", "has", "had"); ]; [ CSubjectWill obj reportage nocaps; if (obj == player) { if (player provides narrative_voice) switch (player.narrative_voice) { 1: Tense("I'll", "I would've"); return; 2: ! Do nothing. 3: CDefart(player); Tense(" will", " would've"); return; default: RunTimeError(16, player.narrative_voice); } if (nocaps) Tense("you'll", "you'd"); else Tense("You'll", "You'd"); return; } SubjectNotPlayer(obj, reportage, "will", "will", "would"); ]; [ CSubjectCan obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "can", 0, "can", "could"); ]; [ CSubjectCant obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "can't", 0, "can't", "couldn't"); ]; [ CSubjectDont obj reportage nocaps; CSubjectVerb(obj, reportage, nocaps, "don't", 0, "doesn't", "didn't"); ]; [ OnesSelf obj; if (obj == player) { if (player provides narrative_voice) switch(player.narrative_voice) { 1: print (string) MYSELF__TX; return; 2: ! Do nothing. 3: if (obj has female) {print "herself"; return;} print "himself"; return; default: RunTimeError(16, player.narrative_voice); } print "yourself"; return; } if (obj has male) { print "himself"; return; } if (obj has female) {print "herself"; return; } print "itself"; return; ]; [ Possessive obj caps; if (obj == player) { if (player provides narrative_voice) switch(player.narrative_voice) { 1: if (caps) print "M"; else print "m"; print "y"; return; 2: ! Do nothing. 3: CDefart(player); print "'s"; return; default: RunTimeError(16, player.narrative_voice); } if (caps) print "Y"; else print "y"; print "our"; return; } if (caps) print "H"; else print "h"; if (obj has male) { print "is"; return; } if (obj has female) { print "er"; return; } if (caps) print "I"; else { print "i"; print "ts"; return; } ]; [ PossessiveCaps obj; Possessive(obj, true); ]; [ theActor obj; if (obj == player) { if (obj provides narrative_voice) { switch (obj.narrative_voice) { 1: print "I"; return; 2: ! Do nothing. 3: if (obj has neuter) { print "it"; return; } if (obj has female) { print "she"; return; } print "he"; return; default: RunTimeError(16, player.narrative_voice); } } print "you"; return; } if (obj has pluralname) { print "they"; return; } if (obj has female) { print "she"; return; } if (obj has male or animate) if (obj hasnt neuter) { print "he"; return; } print "that"; ]; [ SupportObj obj s1 s2; if (obj has supporter) print (string) s1; else print (string) s2; ]; [ PluralObj obj s1 s2 past; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { print (string) past; return; } if (obj has pluralname) print (string) s1; else print (string) s2; ]; ! ---------------------------------------------------------------------------- ! Tense is a little helper function to present the correct tense of a ! verb. The first parameter is the verb in present tense. The second ! parameter is the verb in past tense. If the second parameter is ! omitted, then nothing will be printed if the appropriate tense is past. ! ---------------------------------------------------------------------------- [ Tense present past; if (player provides narrative_tense && player.narrative_tense == PAST_TENSE) { if (past == false) return; print (string) past; } else print (string) present; ]; [ DecideAgainst; CSubjectVerb(actor, false, false, "decide",0,"decides","decided"); print " that"; Tense("'s not", " wasn't"); " such a good idea."; ]; #Ifdef TARGET_ZCODE; [ LowerCase c; ! for ZSCII matching ISO 8859-1 switch (c) { 'A' to 'Z': c = c + 32; 202, 204, 212, 214, 221: c--; 217, 218: c = c - 2; 158 to 160, 167, 168, 208 to 210: c = c - 3; 186 to 190, 196 to 200: c = c - 5 ; 175 to 180: c = c - 6; } return c; ]; [ UpperCase c; ! for ZSCII matching ISO 8859-1 switch (c) { 'a' to 'z': c = c - 32; 201, 203, 211, 213, 220: c++; 215, 216: c = c + 2; 155 to 157, 164, 165, 205 to 207: c = c + 3; 181 to 185, 191 to 195: c = c + 5 ; 169 to 174: c = c + 6; } return c; ]; #Ifnot; ! TARGET_GLULX [ LowerCase c; return glk_char_to_lower(c); ]; [ UpperCase c; return glk_char_to_upper(c); ]; #Endif; ! TARGET_ [ LanguageLM n x1 x2; Answer,Ask: print "There "; Tense("is", "was"); " no reply."; ! Ask: see Answer Attack: print "Violence "; Tense("isn't", "wasn't"); " the answer to this one."; Blow: CSubjectCant(actor,true); " usefully blow ", (thatorthose) x1, "."; Burn: switch (n) { 1: print "This dangerous act would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Buy: print "Nothing "; Tense("is", "was"); " on sale."; Climb: switch (n) { 1: print "Climbing ", (ThatOrThose) x1, " would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Close: switch (n) { 1: CSubjectIs(x1,true); print " not something ", (theActor) actor; Tense(" can close", " could have closed"); "."; 2: CSubjectIs(x1,true); " already closed."; 3: CSubjectVerb(actor,false,false,"close",0,"closes","closed"); " ", (the) x1, "."; 4: "(first closing ", (the) x1, ")"; } CommandsOff: switch (n) { 1: "[Command recording off.]"; #Ifdef TARGET_GLULX; 2: "[Command recording already off.]"; #Endif; ! TARGET_ } CommandsOn: switch (n) { 1: "[Command recording on.]"; #Ifdef TARGET_GLULX; 2: "[Commands are currently replaying.]"; 3: "[Command recording already on.]"; 4: "[Command recording failed.]"; #Endif; ! TARGET_ } CommandsRead: switch (n) { 1: "[Replaying commands.]"; #Ifdef TARGET_GLULX; 2: "[Commands are already replaying.]"; 3: "[Command replay failed. Command recording is on.]"; 4: "[Command replay failed.]"; 5: "[Command replay complete.]"; #Endif; ! TARGET_ } Consult: CSubjectVerb(actor,true,false,"discover",0,"discovers","discovered"); print " nothing of interest in "; if (x1 == player) { OnesSelf(x1); ".";} else print_ret (the) x1, "."; Cut: switch (n) { 1: print "Cutting ", (ThatOrThose) x1, " up would "; Tense("achieve", "have achieved"); " little."; 2: DecideAgainst(); } Dig: print "Digging would "; Tense("achieve", "have achieved"); " nothing here."; Disrobe: switch (n) { 1: CSubjectIsnt(actor,true); " wearing ", (ThatOrThose) x1, "."; 2: CSubjectVerb(actor,false,false,"take off",0,"takes off", "took off"); " ", (the) x1, "."; } Drink: print "There"; Tense("'s", " was"); " nothing suitable to drink here."; Drop: switch (n) { 1: CSubjectIs(x1,true); " already here."; 2: CSubjectVerb(actor, false, false, "haven't got", 0, "hasn't got", "didn't have"); " ", (the) x1, "."; 3: "(first taking ", (the) x1, " off)"; 4: "Dropped."; } Eat: switch (n) { 1: CSubjectIs(x1,true); " plainly inedible."; 2: CSubjectVerb(actor,false,false,"eat",0,"eats", "ate"); print " ", (the) x1; if (actor == player) ". Not bad."; else "."; } EmptyT: switch (n) { 1: CSubjectCant(x1,true); " contain things."; 2: CSubjectIs(x1,true); " closed."; 3: CSubjectIs(x1,true); " empty already."; 4: print "That wouldn't "; Tense("empty", "have emptied"); " anything."; } Enter: switch (n) { 1: print "But "; CSubjectIs(actor,true,true); " already ", (nop) SupportObj(x1,"on ","in "), (the) x1, "."; 2: CSubjectIs(x1,true); print " not something ", (theActor) actor; Tense(" can ", " could "); switch (x2) { 'stand': "stand on."; 'sit': "sit down on."; 'lie': "lie down on."; default: "enter."; } 3: CSubjectCant(actor,true); " get into the closed ", (name) x1, "."; 4: CSubjectCan(actor,true); " only get into something free-standing."; 5: CSubjectVerb(actor,false,false,"get",0,"gets","got"); SupportObj(x1," onto"," into"); " ", (the) x1, "."; 6: "(getting ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, ")"; 7: if (x1 has supporter) "(getting onto ", (the) x1, ")"; if (x1 has container) "(getting into ", (the) x1, ")"; "(entering ", (the) x1, ")"; } Examine: switch (n) { 1: "Darkness, noun. An absence of light to see by."; 2: CSubjectVerb(actor,true,false,"see",0,"sees","saw"); " nothing special about ", (the) x1, "."; 3: CSubjectIs(x1,true); Tense(" currently"); print " switched "; if (x1 has on) "on."; else "off."; } Exit: switch (n) { 1: print "But "; CSubjectIsnt(actor,true,true); " in anything at the moment."; 2: CSubjectCant(actor,false); " get out of the closed ", (name) x1, "."; 3: CSubjectVerb(actor,false,false,"get",0,"gets", "got"); print " "; SupportObj(x1,"off","out of"); " ", (the) x1, "."; 4: CSubjectIsnt(actor,true); print " "; SupportObj(x1,"on","in"); " ", (the) x1, "."; 5: "(first getting ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, ")"; 6: CSubjectVerb(actor,false,false,"stand",0,"stands","stood"); " up."; } Fill: switch (n) { 1: print "There "; Tense("isn't", "wasn't"); " anything obvious with which to fill ", (the) x1, "."; 2: print "Filling ", (the) x1, " from ", (the) x2; Tense(" doesn't", " didn't"); " make sense."; } FullScore: switch (n) { 1: if (deadflag) print "The score was "; else print "The score is "; "made up as follows:^"; 2: "finding sundry items"; 3: "visiting various places"; 4: print "total (out of ", MAX_SCORE; ")"; } GetOff: print "But "; CSubjectIsnt(actor,true,true); " on ", (the) x1, " at the moment."; Give: switch (n) { 1: CSubjectIsnt(actor,true); " holding ", (the) x1, "."; 2: CSubjectVerb(actor,false,false,"juggle",0,"juggles","juggled"); print " ", (the) x1, " for a while, but "; CSubjectVoice(actor,"don't","don't","doesn't","didn't"); " achieve much."; 3: CSubjectDont(x1,true); " seem interested."; 4: CSubjectVerb(actor,false,false,"hand over",0,"hands over","handed over"); " ", (the) x1, "."; } Go: switch (n) { 1: CSubjectWill(actor,true); Tense(" have", " had"); " to get ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, " first."; 2: CSubjectCant(actor,true); " go that way."; 3: CSubjectIs (actor,true); " unable to climb ", (the) x1, "."; 4: CSubjectIs (actor,true); " unable to descend by ", (the) x1, "."; 5: CSubjectCant(actor,true); " since ", (the) x1, " ", (IsOrAre) x1, " in the way."; 6: CSubjectCant(actor,true); " since ", (the) x1, " ", (nop) PluralObj(x1,"lead","leads","led"), " nowhere."; 7: CSubjectVerb(actor,false,false,"depart",0,"departs","departed"); "."; } Insert: switch (n) { 1: CSubjectVerb(actor,true,false,"need",0,"needs","needed"); print " to be holding ", (the) x1, " before ", (theActor) actor; Tense(" can", " could"); " put ", (ItOrThem) x1, " into something else."; 2: CSubjectCant(x1,true); " contain things."; 3: CSubjectIs (x1,true); " closed."; 4: CSubjectWill(actor,true); Tense(" need", " needed"); " to take ", (ItOrThem) x1, " off first."; 5: CSubjectCant(actor,true); " put something inside itself."; 6: "(first taking ", (ItOrThem) x1, " off)"; 7: print "There "; Tense(" is", " was"); " no more room in ", (the) x1, "."; 8: "Done."; 9: CSubjectVerb(actor,false,false,"put",0,"puts","put"); " ", (the) x1, " into ", (the) x2, "."; } Inv: switch (n) { 1: CSubjectIs (actor,false); " carrying nothing."; 2: CSubjectIs (actor,false); print " carrying"; 3: ":"; 4: "."; } Jump: CSubjectVerb(actor,false,false,"jump",0,"jumps","jumped"); " on the spot, fruitlessly."; JumpIn: print "Jumping in ", (the) x1, " "; Tense("would achieve", "would have achieved"); " nothing here."; JumpOn: print "Jumping upon ", (the) x1, " "; Tense("would achieve", "would have achieved"); " nothing here."; JumpOver: switch (n) { 1: CSubjectVerb(actor,true,false,"achieve",0,"achieve","achieved"); " nothing by this."; 2: DecideAgainst(); } Kiss: "Keep your mind on the game."; Listen: CSubjectVerb(actor,true,false,"hear",0,"hears","heard"); " nothing unexpected."; ListMiscellany: switch (n) { 1: print " (providing light)"; 2: print " (which ", (IsOrAre) x1, " closed)"; 3: print " (closed and providing light)"; 4: print " (which ", (IsOrAre) x1, " empty)"; 5: print " (empty and providing light)"; 6: print " (which ", (IsOrAre) x1, " closed and empty)"; 7: print " (closed, empty and providing light)"; 8: print " (providing light and being worn"; 9: print " (providing light"; 10: print " (being worn"; 11: print " (which ", (IsOrAre) x1, " "; 12: print "open"; 13: print "open but empty"; 14: print "closed"; 15: print "closed and locked"; 16: print " and empty"; 17: print " (which ", (IsOrAre) x1, " empty)"; 18: print " containing "; 19: print " (on "; 20: print ", on top of "; 21: print " (in "; 22: print ", inside "; } LMode1: print (string) Story, " is now in its "; if (initial_lookmode == 1) print "normal "; "~brief~ printing mode, which gives long descriptions of places never before visited and short descriptions otherwise."; LMode2: print (string) Story, " is now in its "; if (initial_lookmode ~= 1 or 3) print "normal "; "~verbose~ mode, which always gives long descriptions of locations (even if you've been there before)."; LMode3: print (string) Story, " is now in its "; if (initial_lookmode == 3) print "normal "; "~superbrief~ mode, which always gives short descriptions of locations (even if you haven't been there before)."; Lock: switch (n) { 1: CSubjectDont(x1,true); print " seem to be something ", (theActor) actor; Tense(" can", " could"); " lock."; 2: CSubjectIs (x1,true); " locked at the moment."; 3: CSubjectWill(actor,true); " first have to close ", (the) x1, "."; 4: CSubjectDont(x1,true); " seem to fit the lock."; 5: CSubjectVerb(actor,false,false,"lock",0,"locks","locked"); " ", (the) x1, "."; } Look: switch (n) { 1: print " (on ", (the) x1, ")"; 2: print " (in ", (the) x1, ")"; 3: print " (as ", (object) x1, ")"; 4: print "^On ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; 5,6: if (x1 ~= location) { if (x1 has supporter) print "^On "; else print "^In "; print (the) x1, " ", (theActor) actor, " "; Tense("can", "could"); } else { new_line; CSubjectCan(actor,false); } if (n == 5) print " also"; print " see "; WriteListFrom(child(x1), ENGLISH_BIT+RECURSE_BIT+PARTINV_BIT+TERSE_BIT+CONCEAL_BIT+WORKFLAG_BIT); if (x1 ~= location) "."; else " here."; 7: CSubjectVerb(actor,true,false,"see",0,"sees", "saw"); " nothing unexpected in that direction."; } LookUnder: switch (n) { 1: print "But it"; Tense("'s", " was"); " dark."; 2: CSubjectVerb(actor,true,false,"find",0,"finds", "found"); " nothing of interest."; } Mild: "Quite."; Miscellany: switch (n) { 1: "(considering the first sixteen objects only)^"; 2: "Nothing to do!"; 3: print " "; CSubjectVerb(player, false, false, "died", "have died", "has died"); print " "; 4: print " "; CSubjectVerb(player, false, false, "won", "have won", "has won"); print " "; 5: print "^Would you like to RESTART, RESTORE a saved game"; #Ifdef DEATH_MENTION_UNDO; print ", UNDO your last move"; #Endif; if (TASKS_PROVIDED == 0) print ", give the FULL score for that game"; if (deadflag == 2 && AMUSING_PROVIDED == 0) print ", see some suggestions for AMUSING things to do"; SerialComma(3); print " or QUIT?"; 6: "[Your interpreter does not provide ~undo~. Sorry!]"; #Ifdef TARGET_ZCODE; 7: "~Undo~ failed. [Not all interpreters provide it.]"; #Ifnot; ! TARGET_GLULX 7: "[You cannot ~undo~ any further.]"; #Endif; ! TARGET_ 8: "Please give one of the answers above."; 9: print "^It "; Tense("is now", "was"); print " pitch dark in "; Tense("here", "there"); "!"; 10: "I beg your pardon?"; 11: "[You can't ~undo~ what hasn't been done!]"; 12: "[Can't ~undo~ twice in succession. Sorry!]"; 13: "[Previous turn undone.]"; 14: "Sorry, that can't be corrected."; 15: "Think nothing of it."; 16: "~Oops~ can only correct a single word."; 17: print "It "; Tense("is", "was"); print " pitch dark, and ", (theActor) actor; Tense(" can't", " couldn't"); " see a thing."; 18: print "yourself"; 19: "As good-looking as ever."; 20: "To repeat a command like ~frog, jump~, just say ~again~, not ~frog, again~."; 21: CSubjectCan(actor,true); " hardly repeat that."; 22: CSubjectCant(actor, true); " begin with a comma."; 23: CSubjectVerb(actor, true, false, "seem", "seem", "seems", "seemed"); print " to want to talk to someone, but I "; Tense("can't", "couldn't"); " see whom."; 24: CSubjectCant(actor, true); " talk to ", (the) x1, "."; 25: "To talk to someone, try ~someone, hello~ or some such."; 26: "(first taking ", (the) x1, ")"; 27: "I didn't understand that sentence."; 28: print "I only understood you as far as wanting to "; 29: "I didn't understand that number."; 30: CSubjectCant(actor,true); " see any such thing."; 31: CSubjectVerb(actor, true, false, "seem", "seem", "seems", "seemed"); " to have said too little!"; 32: CSubjectIsnt(actor); " holding that!"; 33: "You can't use multiple objects with that verb."; 34: "You can only use multiple objects once on a line."; 35: "I'm not sure what ~", (address) x1, "~ refers to."; 36: "You excepted something not included anyway!"; 37: CSubjectCan(actor,true); " only do that to something animate."; #Ifdef DIALECT_US; 38: "That's not a verb I recognize."; #Ifnot; 38: "That's not a verb I recognise."; #Endif; 39: "That's not something you need to refer to in the course of this game."; 40: CSubjectCant(actor,true); " see ~", (address) x1, "~ (", (the) x2, ") at the moment."; 41: "I didn't understand the way that finished."; 42: if (x1 == 0) print "None"; else print "Only ", (number) x1; print " of those "; if (x1 == 1) print "is"; else print "are"; " available."; 43: "Nothing to do!"; 44: print "There "; Tense("is", "was"); " nothing to ", (address) x1, "."; 45: print "Who do you mean, "; 46: print "Which do you mean, "; 47: "Sorry, you can only have one item here. Which exactly?"; 48: print "Whom "; CSubjectVoice(player, "do", "do", "does", "did"); print " "; CSubjectVerb(player, false, true, "want", "want", "want", "want"); if (x1 ~= player && x1 ~= nothing) print " ", (the) x1; print " to "; PrintCommand(); "?"; 49: print "What "; CSubjectVoice(player, "do", "do", "does", "did"); print " "; CSubjectVerb(player, false, true, "want", "want", "want", "want"); if (x1 ~= player && x1 ~= nothing) print " ", (the) x1; print " to "; PrintCommand(); "?"; 50: print "The score has just gone "; if (x1 > 0) print "up"; else { x1 = -x1; print "down"; } print " by ", (number) x1, " point"; if (x1 > 1) print "s"; 51: "(Since something dramatic has happened, your list of commands has been cut short.)"; 52: "^Type a number from 1 to ", x1, ", 0 to redisplay or press ENTER."; 53: "^[Please press SPACE.]"; 54: "[Comment recorded.]"; 55: "[Comment NOT recorded.]"; 56: "."; 57: "?"; 58: "(first taking ", (the) x1, " ", (nop) SupportObj(x2,"off","out of"), " ", (the) x2, ")"; 59: "You'll have to be more specific."; 60: print (The) x1, " observes that "; } No,Yes: "That was a rhetorical question."; NotifyOff: "Score notification off."; NotifyOn: "Score notification on."; Objects: switch (n) { 1: "Objects ", (nop) CSubjectVerb(actor, false, true, "have", "have", "has"), " handled:^"; 2: "None."; 3: print " (worn)"; 4: print " (held)"; 5: print " (given away)"; 6: print " (in ", (name) x1, ")"; 7: print " (in ", (the) x1, ")"; 8: print " (inside ", (the) x1, ")"; 9: print " (on ", (the) x1, ")"; 10: print " (lost)"; } Open: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor; Tense(" can open", " could have opened"); "."; 2: CSubjectVerb(x1,true,false,"seem",0,"seems","seemed"); " to be locked."; 3: CSubjectIs (x1,true); " already open."; 4: CSubjectVerb(actor,false,false,"open",0,"opens","opened"); print " ", (the) x1; Tense(", revealing ", " and revealed "); if (WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT) == 0) "nothing."; "."; 5: CSubjectVerb(actor,false,false,"open",0,"opens","opened"); " ", (the) x1, "."; 6: "(first opening ", (the) x1, ")"; } Order: CSubjectHas(x1,false); " better things to do."; Places: switch (n) { 1: print "You have visited: "; 2: "."; } Pray: print "Nothing practical "; Tense("results", "resulted"); " from ", (Possessive) actor, " prayer."; Prompt: print "^>"; Pronouns: switch (n) { 1: print "At the moment, "; 2: print "means "; 3: print "is unset"; 4: "no pronouns are known to the game."; 5: "."; } Pull,Push,Turn: switch (n) { 1: if (player provides narrative_voice && player.narrative_voice == 3) { print_ret (The) player, " ", (nop) Tense("isn't", "wasn't"), " likely to help matters by punishing ", (OnesSelf) player, " that way."; } else { "Punishing ", (OnesSelf) player, " that way ", (nop) Tense("isn't", "wasn't"), " likely to help matters."; } 2: CSubjectIs (x1,true); " fixed in place."; 3: CSubjectIs (actor,true); " unable to."; 4: print "Nothing obvious "; Tense("happens", "happened"); "."; 5: print "That would "; Tense("be", "have been"); " less than courteous."; 6: DecideAgainst(); } ! Push: see Pull PushDir: switch (n) { 1: print "That really "; Tense("wouldn't", "didn't"); " serve any purpose."; 2: print "That's "; Tense("not", "wasn't"); " a direction."; 3: print "Not that way ", (theActor) actor; Tense(" can't", "couldn't"); "."; } PutOn: switch (n) { 1: CSubjectVerb(actor,true,false,"need",0,"needs","needed"); print " to be holding ", (the) x1, " before ", (theActor) actor; Tense(" can", " could"); " put ", (ItOrThem) x1, " on top of something else."; 2: CSubjectCant(actor,true,true); " put something on top of itself."; 3: print "Putting things on ", (the) x1, " would"; Tense(" achieve", "'ve achieved"); " nothing."; 4: CSubjectVerb(actor,true,false,"lack",0,"lacks","lacked"); " the dexterity."; 5: "(first taking ", (ItOrThem) x1, " off)"; 6: print "There "; Tense("is", "was"); " no more room on ", (the) x1, "."; 7: "Done."; 8: CSubjectVerb(actor,false,false,"put",0,"puts","put"); " ", (the) x1, " on ", (the) x2, "."; } Quit: switch (n) { 1: print "Please answer yes or no."; 2: print "Are you sure you want to quit? "; } Remove: switch (n) { 1: CSubjectIs (x1,true); " unfortunately closed."; 2: print "But "; CSubjectIsnt(x1,true); " there now."; 3: "Removed."; } Restart: switch (n) { 1: print "Are you sure you want to restart? "; 2: "Failed."; } Restore: switch (n) { 1: "Restore failed."; 2: "Ok."; } Rub: switch (n) { 1: CSubjectVerb(actor,true,false,"achieve",0,"achieves","achieved"); " nothing by this."; 2: DecideAgainst(); } Save: switch (n) { 1: "Save failed."; 2: "Ok."; } Score: switch (n) { 1: if (deadflag) print "In that game you scored "; else print "You have so far scored "; print score, " out of a possible ", MAX_SCORE, ", in ", turns, " turn"; if (turns ~= 1) print "s"; return; 2: "There is no score in this story."; } ScriptOff: switch (n) { 1: "Transcripting is already off."; 2: "^End of transcript."; 3: "Attempt to end transcript failed."; } ScriptOn: switch (n) { 1: "Transcripting is already on."; 2: print "Start of a transcript of"; VersionSub(); 3: "Attempt to begin transcript failed."; } Search: switch (n) { 1: print "But it"; Tense("'s", " was"); " dark."; 2: print "There "; Tense("is", "was"); " nothing on ", (the) x1, "."; 3: print "On ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; 4: CSubjectVerb(actor,true,false,"find",0,"finds","found"); " nothing of interest."; 5: CSubjectCant(actor,true); " see inside, since ", (the) x1, " ", (IsOrAre) x1, " closed."; 6: "", (The) x1, " ", (IsOrAre) x1, " empty."; 7: print "In ", (the) x1; WriteListFrom(child(x1), ENGLISH_BIT+TERSE_BIT+CONCEAL_BIT+ISARE_BIT); "."; } ! Preceding "No," unable to be used for Set and SetTo Set: CSubjectCant(actor,true); " set ", (ThatOrThose) x1, "."; SetTo: CSubjectCant(actor,true); " set ", (ThatOrThose) x1, " to anything."; Show: switch (n) { 1: CSubjectIsnt(actor,true); " holding ", (the) x1, "."; 2: CSubjectIs (x1,true); " unimpressed."; } Sing: print (PossessiveCaps) actor, " singing "; Tense("is", "was"); " abominable."; Sleep: CSubjectIsnt(actor,true); " feeling especially drowsy."; Smell: switch (n) { 1: CSubjectVerb(actor,true,false,"smell",0,"smells","smelled"); " nothing unexpected."; 2: DecideAgainst(); } #Ifdef DIALECT_US; Sorry: "Oh, don't apologize."; #Ifnot; Sorry: "Oh, don't apologise."; #Endif; Squeeze: switch (n) { 1: DecideAgainst(); 2: CSubjectVerb(actor,true,false,"achieve",0,"achieves","achieved"); " nothing by this."; } Strong: print "Real adventurers "; Tense ("do", "did"); " not use such language."; Swim: print "There"; Tense("'s not", " wasn't"); " enough water to swim in."; Swing: print "There"; Tense("'s", " was"); " nothing sensible to swing here."; SwitchOff: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor, " "; Tense("can", "could"); " switch."; 2: CSubjectIs (x1,true); " already off."; 3: CSubjectVerb(actor,false,false,"switch",0,"switches","switched"); " ", (the) x1, " off."; } SwitchOn: switch (n) { 1: CSubjectIs (x1,true); print " not something ", (theActor) actor, " "; Tense("can", "could"); " switch."; 2: CSubjectIs (x1,true); " already on."; 3: CSubjectVerb(actor,false,false,"switch",0,"switches","switched"); " ", (the) x1, " on."; } Take: switch (n) { 1: "Taken."; 2: CSubjectIs (actor,false); " always self-possessed."; 3: print "I don't suppose ", (the) x1, " would "; Tense("care", "have cared"); " for that."; 4: CSubjectWill(actor,true); print " have "; Tense("", "had "); "to get ", (nop) SupportObj(x1,"off","out of"), " ", (the) x1, " first."; 5: CSubjectVerb(actor,true,false,"already have",0,"already has","already had"); " ", (ThatOrThose) x1, "."; 6: CSubjectVerb(x2,true,false,"seem",0,"seems","seemed"); " to belong to ", (the) x1, "."; 7: CSubjectVerb(x2,true,false,"seem",0,"seems","seemed"); " to be a part of ", (the) x1, "."; 8: CSubjectIs (x1,true); " not available."; 9: CSubjectIs (x1,true); " not open."; 10: CSubjectIs (x1,true); " hardly portable."; 11: CSubjectIs (x1,true); " fixed in place."; 12: CSubjectIs (actor,true); " carrying too many things already."; 13: "(putting ", (the) x1, " into ", (the) x2, " to make room)"; } Taste: switch (n) { 1: CSubjectVerb(actor,true,false,"taste",0,"tastes","tasted"); " nothing unexpected."; 2: DecideAgainst(); } Tell: switch (n) { 1: CSubjectVerb(actor,false,false,"talk",0,"talks","talked"); " to ", (OnesSelf) actor, " for a while."; 2: print "This provoke"; Tense("s", "d"); " no reaction."; } Think: "What a good idea."; ThrowAt: switch (n) { 1: "Futile."; 2: CSubjectVerb(actor,true,false,"lack",0,"lacks","lacked"); print " the nerve when it "; Tense("comes", "came"); " to the crucial moment."; } Tie: switch (n) { 1: CSubjectVerb(actor,true,false,"would",0,0); Tense(" achieve", " have achieved"); " nothing by this."; 2: DecideAgainst(); } Touch: switch (n) { 1: DecideAgainst(); 2: CSubjectVerb(actor,true,false,"feel",0,"feels","felt"); " nothing unexpected."; 3: print "That really "; Tense("wouldn't", "didn't"); " serve any purpose."; } ! Turn: see Pull. Unlock: switch (n) { 1: CSubjectDont(x1,true); print " seem to be something ", (theActor) actor; Tense(" can unlock", " could have unlocked"); "."; 2: CSubjectIs (x1,true); " unlocked at the moment."; 3: CSubjectDont(x1,true); " seem to fit the lock."; 4: CSubjectVerb(actor,false,false,"unlock",0,"unlocks","unlocked"); " ", (the) x1, "."; 5: "(first unlocking ", (the) x1, ")"; } VagueGo: CSubjectWill(actor); print " have "; Tense("", "had "); "to say which compass direction to go in."; Verify: switch (n) { 1: "The game file has verified as intact."; 2: "The game file did not verify as intact, and may be corrupt."; } Wait: print "Time passe"; Tense("s", "d"); "."; Wake: print "The dreadful truth is, this "; Tense("is", "was"); " not a dream."; WakeOther:print "That seem"; Tense("s", "ed"); " unnecessary."; Wave: switch (n) { 1: print "But "; CSubjectIsnt(actor,true,true); " holding ", (ThatOrThose) x1, "."; 2: CSubjectVerb(actor,false,false,"look",0,"looks","looked"); print " ridiculous waving ", (the) x1; if (x2) " at ", (the) x2, "."; "."; 3: DecideAgainst(); } WaveHands: CSubjectVerb(actor,false,false,"wave",0,"waves","waved"); switch (n) { 1: ! nothing 2: print " at ", (the) x1; } ", feeling foolish."; Wear: switch (n) { 1: CSubjectCant(actor,true); " wear ", (ThatOrThose) x1, "!"; 2: CSubjectIs (actor,true); " not holding ", (ThatOrThose) x1, "!"; 3: CSubjectIs (actor,true); " already wearing ", (ThatOrThose) x1, "!"; 4: CSubjectVerb(actor,false,false,"put on",0,"puts on","put on"); " ", (the) x1, "."; } ! Yes: see No. ]; ! ============================================================================== Constant LIBRARY_ENGLISH; ! for dependency checking. #Endif; ! ==============================================================================