home *** CD-ROM | disk | FTP | other *** search
/ Sunny 1,000 Collection / SUNNY1000.iso / Files / Dos / infoco3 / LIBRARY.ZIP / parser.h < prev    next >
Encoding:
Text File  |  1995-10-24  |  125.2 KB  |  3,852 lines

  1. ! ----------------------------------------------------------------------------
  2. !  "PARSER" header for Inform 5
  3. !
  4. !  (c) Graham Nelson 1993, 1994, 1995, but freely usable (see documentation)
  5. ! ----------------------------------------------------------------------------
  6.  
  7. Constant LibSerial "951023";
  8. Constant LibRelease "5/12";
  9. System_file;
  10. Constant MAX_TIMERS  64;
  11. Array  the_timers  -> MAX_TIMERS;
  12. Array  timer_flags -> MAX_TIMERS;
  13. Array  buffer          string 120;
  14. Array  parse           string 64;
  15.  
  16. Attribute animate;
  17. Attribute clothing;
  18. Attribute concealed;
  19. Attribute container;
  20. Attribute door;
  21. Attribute edible;
  22. Attribute enterable;
  23. Attribute female;
  24. Attribute general;
  25. Attribute light;
  26. Attribute lockable;
  27. Attribute locked;
  28. Attribute moved;
  29. Attribute on;
  30. Attribute open;
  31. Attribute openable;
  32. Attribute proper;
  33. Attribute scenery;
  34. Attribute scored;
  35. Attribute static;
  36. Attribute supporter;
  37. Attribute switchable;
  38. Attribute talkable;
  39. Attribute transparent;
  40. Attribute visited;
  41. Attribute workflag;
  42. Attribute worn;
  43.  
  44. Attribute absent alias female;      !  Please, no psychoanalysis
  45.  
  46. Property additive before $ffff;
  47. Property additive after  $ffff;
  48. Property additive life   $ffff;
  49.  
  50. Property long n_to;  Property long s_to; !  Slightly wastefully, these are
  51. Property long e_to;  Property long w_to; !  long (they might be routines)
  52. Property long ne_to; Property long se_to;
  53. Property long nw_to; Property long sw_to;
  54. Property long u_to;  Property long d_to;
  55. Property long in_to; Property long out_to;
  56.  
  57. Property door_to     alias n_to;     !  For economy: these properties are
  58. Property when_closed alias s_to;     !  used only by objects which
  59. Property with_key    alias e_to;     !  aren't rooms
  60. Property door_dir    alias w_to;
  61. Property invent      alias u_to;
  62. Property plural      alias d_to;
  63. Property add_to_scope alias se_to;
  64. Property list_together alias sw_to;
  65. Property react_before alias ne_to;
  66. Property react_after  alias nw_to;
  67. Property grammar     alias in_to;
  68. Property orders      alias out_to;
  69.  
  70. Property long initial;
  71. Property when_open   alias initial;
  72. Property when_on     alias initial;
  73. Property when_off    alias when_closed;
  74. Property long description;
  75. Property additive describe $ffff;
  76. Property article "a";
  77.  
  78. Property cant_go "You can't go that way.";
  79.  
  80. Property long found_in;         !  For fiddly reasons this can't alias
  81.  
  82. Property long time_left;
  83. Property long number;
  84. Property additive time_out $ffff;
  85. Property daemon alias time_out;
  86. Property additive each_turn $ffff;
  87.  
  88. Property capacity 100;
  89.  
  90. Property long short_name 0;
  91. Property long parse_name 0;
  92.  
  93. ! The following definitions, commented out, define pre-Inform 5.2 and now
  94. ! obselete names as aliases for the standard names:
  95.  
  96. ! Property preroutine alias before; Property desc        alias description;
  97. ! Property postroutine alias after; Property longdesc    alias description;
  98. ! Property liferoutine alias life;  Property timeleft    alias time_left;
  99. ! Property initpos     alias initial;
  100. ! Property portalto    alias door_to;
  101. ! Property closedpos   alias when_closed;
  102. ! Property dirprop     alias door_dir;
  103. ! Property cantgo      alias cant_go;
  104. ! Attribute portal alias door;
  105.  
  106.  
  107. Fake_Action LetGo;
  108. Fake_Action Receive;
  109. Fake_Action ThrownAt;
  110. Fake_Action Order;
  111. Fake_Action TheSame;
  112. Fake_Action PluralFound;
  113. Fake_Action Miscellany;
  114. Fake_Action Prompt;
  115. Fake_Action NotUnderstood;
  116. [ Main; PlayTheGame(); ];
  117.  
  118. Constant NULL $ffff;
  119.  
  120. ! ----------------------------------------------------------------------------
  121. !  Attribute and property definitions
  122. !  The compass, directions, darkness and player objects
  123. !  Definitions of fake actions
  124. !  Library global variables
  125. !  Private parser variables
  126. !  Keyboard reading
  127. !  Parser, level 0: outer shell, conversation, errors
  128. !                1: grammar lines
  129. !                2: tokens
  130. !                3: object lists
  131. !                4: scope and ambiguity resolving
  132. !                5: object comparisons
  133. !                6: word comparisons
  134. !                7: reading words and moving tables about
  135. !  Main game loop
  136. !  Action processing
  137. !  Menus
  138. !  Time: timers and daemons
  139. !  Considering light
  140. !  Changing player personality
  141. !  Printing short names
  142. ! ----------------------------------------------------------------------------
  143.  
  144. ! ----------------------------------------------------------------------------
  145. ! Construct the compass - a dummy object containing the directions, which also
  146. ! represent the walls in whatever room the player is in (these are given the
  147. ! general-purpose "number" property for the programmer's convenience)
  148. ! ----------------------------------------------------------------------------
  149.  
  150. Object compass "compass" has concealed;
  151.  
  152. #IFNDEF WITHOUT_DIRECTIONS;
  153. Object n_obj "north wall" compass      
  154.   with name "n" "north" "wall",       article "the", door_dir n_to, number 0
  155.   has  scenery;
  156. Object s_obj "south wall" compass      
  157.   with name "s" "south" "wall",       article "the", door_dir s_to, number 0
  158.   has  scenery;
  159. Object e_obj "east wall" compass      
  160.   with name "e" "east" "wall",        article "the", door_dir e_to, number 0
  161.    has  scenery;
  162. Object w_obj "west wall" compass       
  163.   with name "w" "west" "wall",        article "the", door_dir w_to, number 0
  164.    has  scenery;
  165. Object ne_obj "northeast wall" compass 
  166.   with name "ne" "northeast" "wall",  article "the", door_dir ne_to, number 0
  167.   has  scenery;
  168. Object se_obj "southeast wall" compass
  169.   with name "se" "southeast" "wall",  article "the", door_dir se_to, number 0
  170.   has  scenery;
  171. Object nw_obj "northwest wall" compass
  172.   with name "nw" "northwest" "wall",  article "the", door_dir nw_to, number 0
  173.   has  scenery;
  174. Object sw_obj "southwest wall" compass
  175.   with name "sw" "southwest" "wall",  article "the", door_dir sw_to, number 0
  176.   has  scenery;
  177. Object u_obj "ceiling" compass         
  178.   with name "u" "up" "ceiling",       article "the", door_dir u_to, number 0
  179.    has  scenery;
  180. Object d_obj "floor" compass
  181.   with name "d" "down" "floor",       article "the", door_dir d_to, number 0
  182.    has  scenery;
  183. #ENDIF;
  184. Object out_obj "outside" compass
  185.   with                                article "the", door_dir out_to, number 0
  186.    has  scenery;
  187. Object in_obj "inside" compass
  188.   with                                article "the", door_dir in_to, number 0
  189.    has  scenery;
  190.  
  191. ! ----------------------------------------------------------------------------
  192. ! The other dummy object is "Darkness", not really a place but it has to be
  193. ! an object so that the name on the status line can be "Darkness":
  194. ! we also create the player object
  195. ! ----------------------------------------------------------------------------
  196.  
  197. Object thedark "Darkness"
  198.   with initial 0,
  199.        short_name "Darkness",
  200.        description "It is pitch dark, and you can't see a thing.";
  201.  
  202. Object selfobj "yourself"
  203.   with description "As good-looking as ever.", number 0,
  204.        before $ffff, after $ffff, life $ffff, each_turn $ffff,
  205.        time_out $ffff, describe $ffff, capacity 100,
  206.        parse_name 0, short_name 0, orders 0,
  207.   has  concealed animate proper transparent;
  208.  
  209. ! ----------------------------------------------------------------------------
  210. ! Globals: note that the first one defined gives the status line place, the
  211. ! next two the score/turns
  212. ! ----------------------------------------------------------------------------
  213.  
  214. Global location = 1;
  215. Global sline1 = 0;
  216. Global sline2 = 0;
  217.  
  218. Global the_time = NULL;
  219. Global time_rate = 1;
  220. Global time_step = 0;
  221.  
  222. Global score = 0;
  223. Global turns = 1;
  224. Global player;
  225.  
  226. Global lightflag = 1;
  227. Global real_location = thedark;
  228. Global print_player_flag = 0;
  229. Global deadflag = 0;
  230.  
  231. Global transcript_mode = 0;
  232.  
  233. Global last_score = 0;
  234. Global notify_mode = 1;       ! Score notification
  235.  
  236. Global places_score = 0;
  237. Global things_score = 0;
  238. Global lookmode = 1;
  239. Global lastdesc = 0;
  240.  
  241. Global top_object = 0;
  242. Global standard_interpreter = 0;
  243.  
  244. ! ----------------------------------------------------------------------------
  245. ! Parser variables accessible to the rest of the game
  246. ! ----------------------------------------------------------------------------
  247.  
  248. Array  inputobjs       --> 16;       ! To hold parameters
  249. Global toomany_flag    = 0;          ! Flag for "take all made too many"
  250. Global actor           = 0;          ! Person asked to do something
  251. Global actors_location = 0;          ! Like location, but for the actor
  252. Global action          = 0;          ! Thing he is asked to do
  253. Global inp1            = 0;          ! First parameter
  254. Global inp2            = 0;          ! Second parameter
  255. Global self            = 0;          ! Object whose routines are being run
  256. Global noun            = 0;          ! First noun
  257. Global second          = 0;          ! Second noun
  258. Array  multiple_object --> 64;       ! List of multiple parameters
  259. Global special_word    = 0;          ! Dictionary address of "special"
  260. Global special_number  = 0;          ! The number, if a number was typed
  261. Global special_number2 = 0;          ! Second number, if two numbers typed
  262. Global parsed_number   = 0;          ! For user-supplied parsing routines
  263. global multiflag;                    ! Multiple-object flag
  264. global notheld_mode  = 0;            ! To do with implicit taking
  265. global onotheld_mode = 0;            !
  266. global meta;                         ! Verb is a meta-command (such as "save")
  267. global reason_code;                  ! Reason for calling a life
  268. global sw__var         = 0;          ! Switch variable (used for embeddeds)
  269. global consult_from;                 ! Word that "consult"átopic starts on
  270. global consult_words;                ! ...and number of words in topic
  271.  
  272. #IFV5;
  273. global undo_flag = 0;                ! Can the interpreter provide "undo"?
  274. #ENDIF;
  275.  
  276. global parser_trace = 0;             ! Set this to 1 to make the parser trace
  277.                                      ! tokens and lines
  278. global debug_flag = 0;               ! For debugging information
  279. global lm_n;                         ! Parameters for LibraryMessages
  280. global lm_o;
  281.  
  282. Constant REPARSE_CODE 10000;
  283.  
  284. Constant PARSING_REASON        0;
  285. Constant TALKING_REASON        1;
  286. Constant EACH_TURN_REASON      2;
  287. Constant REACT_BEFORE_REASON   3;
  288. Constant REACT_AFTER_REASON    4;
  289. Constant LOOPOVERSCOPE_REASON  5;
  290. Constant TESTSCOPE_REASON      6;
  291.  
  292. ! ----------------------------------------------------------------------------
  293. ! The parser, beginning with variables private to itself:
  294. ! ----------------------------------------------------------------------------
  295.  
  296. Array  buffer2   string 120;    ! Buffers for supplementary questions
  297. Array  parse2    string 64;     !
  298. Array  parse3    string 64;     !
  299.  
  300. global wn;                      ! Word number (counts from 1)
  301. global num_words;               ! Number of words typed
  302. global verb_word;               ! Verb word (eg, take in "take all" or
  303.                                 ! "dwarf, take all") - address in dictionary
  304. global verb_wordnum;            ! and the number in typing order (eg, 1 or 3)
  305.  
  306. global multi_mode;              ! Multiple mode
  307. global multi_wanted;            ! Number of things needed in multitude
  308. global multi_had;               ! Number of things actually found
  309. global multi_context;           ! What token the multi-object was accepted for
  310.  
  311. Array pattern --> 8;            ! For the current pattern match
  312. global pcount;                  ! and a marker within it
  313. Array pattern2 --> 8;           ! And another, which stores the best match
  314. global pcount2;                 ! so far
  315.  
  316. global parameters;              ! Parameters (objects) entered so far
  317. global params_wanted;           ! Number needed (may change in parsing)
  318.  
  319. global nsns;                    ! Number of special_numbers entered so far
  320.  
  321. global inferfrom;               ! The point from which the rest of the
  322.                                 ! command must be inferred
  323. global inferword;               ! And the preposition inferred
  324.  
  325. global oops_from = 0;           ! The "first mistake" point, where oops acts
  326. global saved_oops = 0;          ! Used in working this out
  327. Array  oops_heap --> 5;         ! Used temporarily by "oops" routine
  328.  
  329. Constant MATCH_LIST_SIZE 128;
  330. Array  match_list    -> 128;
  331.                                 ! An array of matched objects so far
  332. Array  match_classes -> 128;
  333.                                 ! An array of equivalence classes for them
  334. global number_matched;          ! How many items in it?  (0 means none)
  335. global number_of_classes;       ! How many equivalence classes?
  336. global match_length;            ! How many typed words long are these matches?
  337. global match_from;              ! At what word of the input do they begin?
  338.  
  339. global parser_action;           ! For the use of the parser when calling
  340. global parser_one;              ! user-supplied routines
  341. global parser_two;              !
  342.  
  343. global vague_word;              ! Records which vague word ("it", "them", ...)
  344.                                 ! caused an error
  345. global vague_obj;               ! And what it was thought to refer to
  346.  
  347. global itobj=0;                 ! The object which is currently "it"
  348. global himobj=0;                ! The object which is currently "him"
  349. global herobj=0;                ! The object which is currently "her"
  350.  
  351. global lookahead;               ! The token after the object now being matched
  352. global indef_mode;              ! "Indefinite" mode - ie, "take a brick" is in
  353.                                 ! this mode
  354. global indef_type;              ! Bit-map holding types of specification
  355. global indef_wanted;            ! Number of items wanted (100 for all)
  356. global indef_guess_p;           ! Plural-guessing flag
  357. global allow_plurals;           ! Whether they are presently allowed or not
  358. global not_holding;             ! Object to be automatically taken as an
  359.                                 ! implicit command
  360. Array  kept_results --> 16;     ! The delayed command (while the take happens)
  361.  
  362. global saved_wn;                ! These are temporary variables for Parser()
  363. global saved_token;             ! (which hasn't enough spare local variables)
  364.  
  365. global held_back_mode = 0;      ! Flag: is there some input from last time
  366. global hb_wn = 0;               ! left over?  (And a save value for wn)
  367.  
  368. global best_etype;              ! Error number used within parser
  369. global etype;                   ! Error number used for individual lines
  370.  
  371. global last_command_from;       ! For sorting out "then again"
  372. global last_command_to;         !
  373.  
  374. global token_was;               ! For noun filtering by user routines
  375.  
  376. global advance_warning;         ! What a later-named thing will be
  377.  
  378. global placed_in_flag;          ! To do with PlaceInScope
  379. global length_of_noun;          ! Set by NounDomain to number of words in noun
  380.  
  381. global action_to_be;            ! So the parser can "cheat" in one case
  382. global dont_infer;              ! Another dull flag
  383.  
  384. global scope_reason = PARSING_REASON;   ! For "each_turn" and reactions
  385.  
  386. global scope_token;             ! For scope:Routine tokens
  387. global scope_error;
  388. global scope_stage;
  389.  
  390. global ats_flag = 0;            ! For AddToScope routines
  391. global ats_hls;                 !
  392.  
  393. global take_all_rule = 1;
  394. global usual_grammar_after = 0;
  395.  
  396. #IFV5;
  397. global just_undone = 0;         ! Can't have two successive UNDOs
  398. #ENDIF;
  399.  
  400. Global pretty_flag=1;
  401. Global menu_nesting = 0;
  402.  
  403. Global item_width=8;
  404. Global item_name="Nameless item";
  405. Global menu_item=0;
  406.  
  407. Global active_timers = 0;
  408.  
  409. ! ----------------------------------------------------------------------------
  410. !  Variables for Verblib
  411. ! ----------------------------------------------------------------------------
  412.  
  413. global inventory_stage = 1;
  414. global c_style;
  415. global lt_value;
  416. global listing_together;
  417. global listing_size;
  418. global wlf_indent;
  419. global inventory_style;
  420. global keep_silent;
  421. global receive_action;
  422. #ifdef DEBUG;
  423. global xcommsdir;
  424. Global x_scope_count;
  425. #endif;
  426.  
  427. ! ----------------------------------------------------------------------------
  428. !  The comma_word is a special word, used to substitute commas in the input
  429. ! ----------------------------------------------------------------------------
  430.  
  431. Constant comma_word 'xcomma';
  432.  
  433. ! ----------------------------------------------------------------------------
  434. !  In Advanced games only, the DrawStatusLine routine does just that: this is
  435. !  provided explicitly so that it can be Replace'd to change the style, and
  436. !  as written it emulates the ordinary Standard game status line, which is
  437. !  drawn in hardware
  438. ! ----------------------------------------------------------------------------
  439. #IFV5;
  440. [ DrawStatusLine i width posa posb;
  441.    @split_window 1; @set_window 1; @set_cursor 1 1; style reverse;
  442.    width = 0->33; posa = width-26; posb = width-13;
  443.    spaces (width-1);
  444.    @set_cursor 1 2;  PrintShortName(location);
  445.    if ((0->1)&2 == 0)
  446.    {   if (width > 76)
  447.        {   @set_cursor 1 posa; print "Score: ", sline1;
  448.            @set_cursor 1 posb; print "Moves: ", sline2;
  449.        }
  450.        if (width > 63 && width <= 76)
  451.        {   @set_cursor 1 posb; print sline1, "/", sline2;
  452.        }
  453.    }
  454.    else
  455.    {   @set_cursor 1 posa; print "Time: ";
  456.        i=sline1%12; if (i<10) print " ";
  457.        if (i==0) i=12;
  458.        print i, ":";
  459.        if (sline2<10) print "0";
  460.        print sline2;
  461.        if ((sline1/12) > 0) print " pm"; else print " am";
  462.    }
  463.    @set_cursor 1 1; style roman; @set_window 0;
  464. ];
  465. #ENDIF;
  466.  
  467. ! ----------------------------------------------------------------------------
  468. !  The Keyboard routine actually receives the player's words,
  469. !  putting the words in "a_buffer" and their dictionary addresses in
  470. !  "a_table".  It is assumed that the table is the same one on each
  471. !  (standard) call.
  472. !
  473. !  It can also be used by miscellaneous routines in the game to ask
  474. !  yes-no questions and the like, without invoking the rest of the parser.
  475. !
  476. !  Return the number of words typed
  477. ! ----------------------------------------------------------------------------
  478.  
  479. [ Keyboard  a_buffer a_table  nw i w x1 x2;
  480.  
  481.     DisplayStatus();
  482.     .FreshInput;
  483.  
  484. !  Save the start of the table, in case "oops" needs to restore it
  485. !  to the previous time's table
  486.  
  487.     for (i=0:i<10:i++) oops_heap->i = a_table->i;
  488.  
  489. !  In case of an array entry corruption that shouldn't happen, but would be
  490. !  disastrous if it did:
  491.  
  492.    a_buffer->0 = 120;
  493.    a_table->0 = 64;
  494.  
  495. !  Print the prompt, and read in the words and dictionary addresses
  496.  
  497.     L__M(##Prompt);
  498.     AfterPrompt();
  499.     #IFV3; read a_buffer a_table; #ENDIF;
  500.     temp_global = 0;
  501.     #IFV5; read a_buffer a_table DrawStatusLine; #ENDIF;
  502.     nw=a_table->1;
  503.  
  504. !  If the line was blank, get a fresh line
  505.     if (nw == 0)
  506.     { L__M(##Miscellany,10); jump FreshInput; }
  507.  
  508. !  Unless the opening word was "oops" or its abbreviation "o", return
  509.  
  510.     w=a_table-->1;
  511.     if (w == #n$o or 'oops') jump DoOops;
  512.  
  513. #IFV5;
  514. !  Undo handling
  515.  
  516.     if ((w == 'undo')&&(parse->1==1))
  517.     {   if (turns==1)
  518.         {   L__M(##Miscellany,11); jump FreshInput;
  519.         }
  520.         if (undo_flag==0)
  521.         {   L__M(##Miscellany,6); jump FreshInput;
  522.         }
  523.         if (undo_flag==1) jump UndoFailed;
  524.         if (just_undone==1)
  525.         {   L__M(##Miscellany,12); jump FreshInput;
  526.         }
  527.         @restore_undo i;
  528.         if (i==0)
  529.         {   .UndoFailed;
  530.             L__M(##Miscellany,7);
  531.         }
  532.         jump FreshInput;
  533.     }
  534.     @save_undo i;
  535.     just_undone=0;
  536.     undo_flag=2;
  537.     if (i==-1) undo_flag=0;
  538.     if (i==0) undo_flag=1;
  539.     if (i==2)
  540.     {   style bold;
  541.         print (name) location, "^";
  542.         style roman;
  543.         L__M(##Miscellany,13);
  544.         just_undone=1;
  545.         jump FreshInput;
  546.     }
  547. #ENDIF;
  548.  
  549.     return nw;
  550.  
  551.     .DoOops;
  552.     if (oops_from == 0)
  553.     {   L__M(##Miscellany,14); jump FreshInput; }
  554.     if (nw == 1)
  555.     {   L__M(##Miscellany,15); jump FreshInput; }
  556.     if (nw > 2)
  557.     {   L__M(##Miscellany,16); jump FreshInput; }
  558.  
  559. !  So now we know: there was a previous mistake, and the player has
  560. !  attempted to correct a single word of it.
  561. !
  562. !  Oops is very primitive: it gets the text buffer wrong, for instance.
  563. !
  564. !  Take out the 4-byte table entry for the supplied correction:
  565. !  restore the 10 bytes at the front of the table, which were over-written
  566. !  by what the user just typed: and then replace the oops_from word entry
  567. !  with the correction one.
  568. !
  569.     x1=a_table-->3; x2=a_table-->4;
  570.     for (i=0:i<10:i++) a_table->i = oops_heap->i;
  571.     w=2*oops_from - 1;
  572.     a_table-->w = x1;
  573.     a_table-->(w+1) = x2;
  574.  
  575.     return nw;
  576. ];
  577.  
  578. Constant STUCK_PE     1;
  579. Constant UPTO_PE      2;
  580. Constant NUMBER_PE    3;
  581. Constant CANTSEE_PE   4;
  582. Constant TOOLIT_PE    5;
  583. Constant NOTHELD_PE   6;
  584. Constant MULTI_PE     7;
  585. Constant MMULTI_PE    8;
  586. Constant VAGUE_PE     9;
  587. Constant EXCEPT_PE    10;
  588. Constant ANIMA_PE     11;
  589. Constant VERB_PE      12;
  590. Constant SCENERY_PE   13;
  591. Constant ITGONE_PE    14;
  592. Constant JUNKAFTER_PE 15;
  593. Constant TOOFEW_PE    16;
  594. Constant NOTHING_PE   17;
  595. Constant ASKSCOPE_PE  18;
  596.  
  597. ! ----------------------------------------------------------------------------
  598. !  The Parser routine is the heart of the parser.
  599. !
  600. !  It returns only when a sensible request has been made, and puts into the
  601. !  "results" buffer:
  602. !
  603. !  Word 0 = The action number
  604. !  Word 1 = Number of parameters
  605. !  Words 2, 3, ... = The parameters (object numbers), but
  606. !                    00 means "multiple object list goes here"
  607. !                    01 means "special word goes here"
  608. !
  609. !  (Some of the global variables above are really local variables for this
  610. !  routine, because the Z-machine only allows up to 15 local variables per
  611. !  routine, and Parser runs out.)
  612. !
  613. !  To simplify the picture a little, a rough map of this routine is:
  614. !
  615. !  (A)    Get the input, do "oops" and "again"
  616. !  (B)    Is it a direction, and so an implicit "go"?  If so go to (K)
  617. !  (C)    Is anyone being addressed?
  618. !  (D)    Get the verb: try all the syntax lines for that verb
  619. !  (E)        Go through each token in the syntax line
  620. !  (F)           Check (or infer) an adjective
  621. !  (G)            Check to see if the syntax is finished, and if so return
  622. !  (H)    Cheaply parse otherwise unrecognised conversation and return
  623. !  (I)    Print best possible error message
  624. !  (J)    Retry the whole lot
  625. !  (K)    Last thing: check for "then" and further instructions(s), return.
  626. !
  627. !  The strategic points (A) to (K) are marked in the commentary.
  628. !
  629. !  Note that there are three different places where a return can happen.
  630. !
  631. ! ----------------------------------------------------------------------------
  632.  
  633. [ Parser  results   syntax line num_lines line_address i j
  634.                     token l m;
  635.  
  636. !  **** (A) ****
  637.  
  638. !  Firstly, in "not held" mode, we still have a command left over from last
  639. !  time (eg, the user typed "eat biscuit", which was parsed as "take biscuit"
  640. !  last time, with "eat biscuit" tucked away until now).  So we return that.
  641.  
  642.     if (notheld_mode==1)
  643.     {   for (i=0:i<8:i++) results-->i=kept_results-->i;
  644.         notheld_mode=0; rtrue;
  645.     }
  646.  
  647.     if (held_back_mode==1)
  648.     {   held_back_mode=0;
  649.         for (i=0:i<64:i++) parse->i=parse2->i;
  650.         new_line;
  651.         jump ReParse;
  652.     }
  653.  
  654.   .ReType;
  655.  
  656.     Keyboard(buffer,parse);
  657.  
  658.   .ReParse;
  659.  
  660. !  Initially assume the command is aimed at the player, and the verb
  661. !  is the first word
  662.  
  663.     num_words=parse->1;
  664.     wn=1;
  665.     BeforeParsing();
  666.     num_words=parse->1;
  667.     if (parser_trace>=4)
  668.     {   print "[ ", num_words, " to parse: ";
  669.         for (i=1:i<=num_words:i++)
  670.         {   j=parse-->((i-1)*2+1);
  671.             if (j == 0) print "? ";
  672.             else
  673.             {   if (UnsignedCompare(j, 0-->4)>=0
  674.                     && UnsignedCompare(j, 0-->2)<0) print_addr j;
  675.                 else print j; print " ";
  676.             }
  677.         }
  678.         print "]^";
  679.     }
  680.  
  681.     verb_wordnum=1;
  682.     actor=player; actors_location=location;
  683.     usual_grammar_after = 0;
  684.  
  685.   .AlmostReParse;
  686.  
  687.     token_was = 0; ! In case we're still in "user-filter" mode from last round
  688.     scope_token = 0;
  689.     action_to_be = NULL;
  690.  
  691. !  Begin from what we currently think is the verb word
  692.  
  693.   .BeginCommand;
  694.     wn=verb_wordnum;
  695.     verb_word = NextWordStopped();
  696.  
  697. !  If there's no input here, we must have something like
  698. !  "person,".
  699.  
  700.     if (verb_word==-1)
  701.     {   best_etype = STUCK_PE; jump GiveError; }
  702.  
  703. !  Now try for "again" or "g", which are special cases:
  704. !  don't allow "again" if nothing has previously been typed;
  705. !  simply copy the previous parse table and ReParse with that
  706.  
  707.     if (verb_word==#n$g) verb_word='again';
  708.     if (verb_word=='again')
  709.     {   if (actor~=player)
  710.         {   print "To repeat a command like ~frog, jump~, just say \
  711.              ~again~, not ~frog, again~.^"; jump ReType; }
  712.         if (parse3->1==0)
  713.         {   print "You can hardly repeat that.^"; jump ReType; }
  714.         for (i=0:i<64:i++) parse->i=parse3->i;
  715.         jump ReParse;
  716.     }
  717.  
  718. !  Save the present parse table in case of an "again" next time
  719.  
  720.     if (verb_word~='again')
  721.         for (i=0:i<64:i++)
  722.             parse3->i=parse->i;
  723.  
  724.     if (usual_grammar_after==0)
  725.     {   i = RunRoutines(actor, grammar);
  726.         if (parser_trace>=2 && actor.grammar~=0 or NULL)
  727.             print " [Grammar property returned ", i, "]^";
  728.         if (i<0) { usual_grammar_after = verb_wordnum; i=-i; }
  729.         if (i==1)
  730.         {   results-->0 = action;
  731.             results-->1 = noun;
  732.             results-->2 = second;
  733.             rtrue;
  734.         }
  735.         if (i~=0) { verb_word = i; wn--; verb_wordnum--; }
  736.         else
  737.         {   wn = verb_wordnum; verb_word=NextWord();
  738.         }
  739.     }
  740.     else usual_grammar_after=0;
  741.  
  742. !  **** (B) ****
  743.  
  744. !  If the first word is not listed as a verb, it must be a direction
  745. !  or the name of someone to talk to
  746. !  (NB: better avoid having a Mr Take or Mrs Inventory around...)
  747.  
  748.     if (verb_word==0 || ((verb_word->#dict_par1) & 1) == 0)
  749.     {   
  750.  
  751. !  So is the first word an object contained in the special object "compass"
  752. !  (i.e., a direction)?  This needs use of NounDomain, a routine which
  753. !  does the object matching, returning the object number, or 0 if none found,
  754. !  or REPARSE_CODE if it has restructured the parse table so that the whole parse
  755. !  must be begun again...
  756.  
  757.         wn=verb_wordnum;
  758.         l=NounDomain(compass,0,0); if (l==REPARSE_CODE) jump ReParse;
  759.  
  760. !  If it is a direction, send back the results:
  761. !  action=GoSub, no of arguments=1, argument 1=the direction.
  762.  
  763.         if (l~=0)
  764.         {   results-->0 = ##Go;
  765.             results-->1 = 1;
  766.             results-->2 = l;
  767.             jump LookForMore;
  768.         }
  769.  
  770. !  **** (C) ****
  771.  
  772. !  Only check for a comma (a "someone, do something" command) if we are
  773. !  not already in the middle of one.  (This simplification stops us from
  774. !  worrying about "robot, wizard, you are an idiot", telling the robot to
  775. !  tell the wizard that she is an idiot.)
  776.  
  777.         if (actor==player)
  778.         {   for (j=2:j<=num_words:j++)
  779.             {   i=NextWord(); if (i==comma_word) jump Conversation;
  780.             }
  781.  
  782.             verb_word=UnknownVerb(verb_word);
  783.             if (verb_word~=0) jump VerbAccepted;
  784.         }
  785.  
  786.         best_etype=VERB_PE; jump GiveError;
  787.  
  788. !  NextWord nudges the word number wn on by one each time, so we've now
  789. !  advanced past a comma.  (A comma is a word all on its own in the table.)
  790.  
  791.       .Conversation;
  792.         j=wn-1;
  793.         if (j==1) { print "You can't begin with a comma.^"; jump ReType; }
  794.  
  795. !  Use NounDomain (in the context of "animate creature") to see if the
  796. !  words make sense as the name of someone held or nearby
  797.  
  798.         wn=1; lookahead=1;
  799.         scope_reason = TALKING_REASON;
  800.         l=NounDomain(player,actors_location,6);
  801.         scope_reason = PARSING_REASON;
  802.         if (l==REPARSE_CODE) jump ReParse;
  803.  
  804.         if (l==0) { print "You seem to want to talk to someone, \
  805.                          but I can't see whom.^"; jump ReType; }
  806.  
  807. !  The object addressed must at least be "talkable" if not actually "animate"
  808. !  (the distinction allows, for instance, a microphone to be spoken to,
  809. !  without the parser thinking that the microphone is human).
  810.  
  811.         if (l hasnt animate && l hasnt talkable)
  812.         {   print "You can't talk to "; DefArt(l); print ".^"; jump ReType; }
  813.  
  814. !  Check that there aren't any mystery words between the end of the person's
  815. !  name and the comma (eg, throw out "dwarf sdfgsdgs, go north").
  816.  
  817.         if (wn~=j)
  818.         {   print "To talk to someone, try ~someone, hello~ or some such.^";
  819.             jump ReType;
  820.         }
  821.  
  822. !  The player has now successfully named someone.  Adjust "him", "her", "it":
  823.  
  824.         ResetVagueWords(l);
  825.  
  826. !  Set the global variable "actor", adjust the number of the first word,
  827. !  and begin parsing again from there.
  828.  
  829.         verb_wordnum=j+1; actor=l;
  830.         actors_location=l;
  831.         while (parent(actors_location)~=0)
  832.             actors_location=parent(actors_location);
  833.         if (parser_trace>=1)
  834.             print "[Actor is ", (the) actor, " in ",
  835.                 (name) actors_location, "]^";
  836.         jump BeginCommand;
  837.     }
  838.  
  839. !  **** (D) ****
  840.  
  841.    .VerbAccepted;
  842.  
  843. !  We now definitely have a verb, not a direction, whether we got here by the
  844. !  "take ..." or "person, take ..." method.  Get the meta flag for this verb:
  845.  
  846.     meta=((verb_word->#dict_par1) & 2)/2;
  847.  
  848. !  Now let i be the corresponding verb number, stored in the dictionary entry
  849. !  (in a peculiar 255-n fashion for traditional Infocom reasons)...
  850.  
  851.     i=$ff-(verb_word->#dict_par2);
  852.  
  853. !  ...then look up the i-th entry in the verb table, whose address is at word
  854. !  7 in the Z-machine (in the header), so as to get the address of the syntax
  855. !  table for the given verb...
  856.  
  857.     syntax=(0-->7)-->i;
  858.  
  859. !  ...and then see how many lines (ie, different patterns corresponding to the
  860. !  same verb) are stored in the parse table...
  861.  
  862.     num_lines=(syntax->0)-1;
  863.  
  864. !  ...and now go through them all, one by one.
  865. !  To prevent vague_word 0 being misunderstood,
  866.  
  867.    vague_word='it'; vague_obj=itobj;
  868.  
  869.    if (parser_trace>=1)
  870.    {    print "[Parsing for the verb '"; print_addr verb_word;
  871.         print "' (", num_lines+1, " lines)]^";
  872.    }
  873.  
  874.    best_etype=STUCK_PE;
  875. !  "best_etype" is the current failure-to-match error - it is by default
  876. !  the least informative one, "don't understand that sentence"
  877.  
  878.  
  879. !  **** (E) ****
  880.  
  881.     for (line=0:line<=num_lines:line++)
  882.     {   line_address = syntax+1+line*8;
  883.  
  884.         if (parser_trace>=1)
  885.         {   print "[Line ", line, ": ", line_address->0, " parameters: ";
  886.             for (pcount=1:pcount<=6:pcount++)
  887.             {   token=line_address->pcount;
  888.                 print token, " ";
  889.             }
  890.             print " -> action ", line_address->7, "]^";
  891.         }
  892.  
  893. !  We aren't in "not holding" or inferring modes, and haven't entered
  894. !  any parameters on the line yet, or any special numbers; the multiple
  895. !  object is still empty.
  896.  
  897.         not_holding=0;
  898.         inferfrom=0;
  899.         parameters=0;
  900.         params_wanted = line_address->0;
  901.         nsns=0;
  902.         multiple_object-->0 = 0;
  903.         multi_context = 0;
  904.         etype=STUCK_PE;
  905.         action_to_be = line_address->7;
  906.  
  907. !  Put the word marker back to just after the verb
  908.  
  909.         wn=verb_wordnum+1;
  910.  
  911. !  An individual "line" contains six tokens...  There's a preliminary pass
  912. !  first, to parse late tokens early if necessary (because of mi or me).
  913. !  We also check to see whether the line contains any "multi"s.
  914.  
  915.         advance_warning=-1; indef_mode=0;
  916.         for (i=0,m=0,pcount=1:pcount<=6:pcount++)
  917.         {   scope_token=0;
  918.             token=line_address->pcount;
  919.             if (token==2) m++;
  920.             if (token<180) i++;
  921.             if (token==4 or 5 && i==1)
  922.             {   if (parser_trace>=2) print " [Trying look-ahead]^";
  923.                 pcount++;
  924.                 while (pcount<=6 && line_address->pcount>=180) pcount++;
  925.                 token=line_address->(pcount-1);
  926.                 if (token>=180)
  927.                 {   j=AdjectiveAddress(token);
  928.  
  929.                     !  Now look for word with j, move wn, parse next
  930.                     !  token...
  931.                     while (wn <= num_words)
  932.                     {   if (NextWord()==j)
  933.                         {   l = NounDomain(actors_location,actor,token);
  934.                             if (parser_trace>=2)
  935.                             {   print " [Forward token parsed: ";
  936.                                 if (l==REPARSE_CODE) print "re-parse request]^";
  937.                                 if (l==1) print "but multiple found]^";
  938.                                 if (l==0) print "hit error ", etype, "]^";
  939.                             }
  940.                             if (l==REPARSE_CODE) jump ReParse;
  941.                             if (l>=2)
  942.                             {   advance_warning = l;
  943.                                 if (parser_trace>=3)
  944.                                 {   DefArt(l); print "]^";
  945.                                 }
  946.                             }
  947.                         }
  948.                     }
  949.                 }
  950.             }
  951.         }
  952.  
  953. !  Slightly different line-parsing rules will apply to "take multi", to
  954. !  prevent "take all" behaving correctly but misleadingly when there's
  955. !  nothing to take.
  956.  
  957.         take_all_rule = 0;
  958.         if (m==1 && params_wanted==1 && action_to_be==##Take)
  959.             take_all_rule = 1;
  960.  
  961. !  And now start again, properly, forearmed or not as the case may be.
  962.  
  963.         not_holding=0;
  964.         inferfrom=0;
  965.         parameters=0;
  966.         nsns=0;
  967.         multiple_object-->0 = 0;
  968.         etype=STUCK_PE;
  969.         action_to_be = line_address->7;
  970.         wn=verb_wordnum+1;
  971.  
  972. !  "Pattern" gradually accumulates what has been recognised so far,
  973. !  so that it may be reprinted by the parser later on
  974.  
  975.         for (pcount=1:pcount<=6:pcount++)
  976.         {   pattern-->pcount=0; scope_token=0;
  977.  
  978.             token=line_address->pcount;
  979.  
  980.             if (parser_trace>=2)
  981.             {   print " [Token ",pcount, " is ", token, ": ";
  982.                 if (token<16)
  983.                 {   if (token==0) print "<noun> or null";
  984.                     if (token==1) print "<held>";
  985.                     if (token==2) print "<multi>";
  986.                     if (token==3) print "<multiheld>";
  987.                     if (token==4) print "<multiexcept>";
  988.                     if (token==5) print "<multiinside>";
  989.                     if (token==6) print "<creature>";
  990.                     if (token==7) print "<special>";
  991.                     if (token==8) print "<number>";
  992.                 }
  993.                 if (token>=16 && token<48)
  994.                     print "<noun filter by routine ",token-16, ">";
  995.                 if (token>=48 && token<80)
  996.                     print "<general parse by routine ",token-48, ">";
  997.                 if (token>=80 && token<128)
  998.                     print "<scope parse by routine ",token-80, ">";
  999.                 if (token>=128 && token<180)
  1000.                     print "<noun filter by attribute ",token-128, ">";
  1001.                 if (token>180)
  1002.                 {   print "<adjective ",255-token, " '";
  1003.                     print_addr AdjectiveAddress(token); print "'>";
  1004.                 }
  1005.                 print " at word number ", wn, "]^";
  1006.             }
  1007.  
  1008. !  Lookahead is set to the token after this one, or 8 if there isn't one.
  1009. !  (Complicated because the line is padded with 0's.)
  1010.  
  1011.             m=pcount+1; lookahead=8;
  1012.             if (m<=6) lookahead=line_address->m;
  1013.             if (lookahead==0)
  1014.             {   m=parameters; if (token<=7) m++;
  1015.                 if (m>=params_wanted) lookahead=8;
  1016.             }
  1017.  
  1018. !  **** (F) ****
  1019.  
  1020. !  When the token is a large number, it must be an adjective:
  1021. !  remember the adjective number in the "pattern".
  1022.  
  1023.             if (token>180)
  1024.             {   pattern-->pcount = REPARSE_CODE+token;
  1025.  
  1026. !  If we've run out of the player's input, but still have parameters to
  1027. !  specify, we go into "infer" mode, remembering where we are and the
  1028. !  adjective we are inferring...
  1029.  
  1030.                 if (wn > num_words)
  1031.                 {   if (inferfrom==0 && parameters<params_wanted)
  1032.                     { inferfrom=pcount; inferword=token; }
  1033.  
  1034. !  Otherwise, this line must be wrong.
  1035.  
  1036.                     if (inferfrom==0) break;
  1037.                 }
  1038.  
  1039. !  Whereas, if the player has typed something here, see if it is the
  1040. !  required adjective... if it's wrong, the line must be wrong,
  1041. !  but if it's right, the token is passed (jump to finish this token).
  1042.  
  1043.                 if (wn <= num_words && token~=AdjectiveWord()) break;
  1044.                 jump TokenPassed;
  1045.             }
  1046.  
  1047. !  **** (G) ****
  1048. !  Check now to see if the player has entered enough parameters...
  1049. !  (since params_wanted is the number of them)
  1050.  
  1051.             if (parameters == params_wanted)
  1052.             {  
  1053.  
  1054. !  If the player has entered enough parameters already but there's still
  1055. !  text to wade through: store the pattern away so as to be able to produce
  1056. !  a decent error message if this turns out to be the best we ever manage,
  1057. !  and in the mean time give up on this line
  1058.  
  1059. !  However, if the superfluous text begins with a comma, "and" or "then" then
  1060. !  take that to be the start of another instruction
  1061.  
  1062.                 if (wn <= num_words)
  1063.                 {   l=NextWord();
  1064.                     if (l=='then' or comma_word)
  1065.                     {   held_back_mode=1; hb_wn=wn-1; }
  1066.                     else
  1067.                     {   for (m=0:m<8:m++) pattern2-->m=pattern-->m;
  1068.                         pcount2=pcount;
  1069.                         etype=UPTO_PE; break;
  1070.                     }
  1071.                 }
  1072.  
  1073. !  Now, we may need to revise the multiple object because of the single one
  1074. !  we now know (but didn't when the list was drawn up).
  1075.  
  1076.                 if (parameters>=1 && results-->2 == 0)
  1077.                 {   l=ReviseMulti(results-->3);
  1078.                     if (l~=0) { etype=l; break; }
  1079.                 }
  1080.                 if (parameters>=2 && results-->3 == 0)
  1081.                 {   l=ReviseMulti(results-->2);
  1082.                     if (l~=0) { etype=l; break; }
  1083.                 }
  1084.  
  1085. !  To trap the case of "take all" inferring only "yourself" when absolutely
  1086. !  nothing else is in the vicinity...
  1087.  
  1088.                 if (take_all_rule==2 && results-->2 == actor)
  1089.                 {   best_etype = NOTHING_PE; jump GiveError;
  1090.                 }
  1091.  
  1092.                 if (parser_trace>=1)
  1093.                     print "[Line successfully parsed]^";
  1094.  
  1095. !  At this point the line has worked out perfectly, and it's a matter of
  1096. !  sending the results back...
  1097. !  ...pausing to explain any inferences made (using the pattern)...
  1098.  
  1099.                 if (inferfrom~=0)
  1100.                 {   print "("; PrintCommand(inferfrom,1); print ")^";
  1101.                 }
  1102.  
  1103. !  ...and to copy the action number, and the number of parameters...
  1104.  
  1105.                 results-->1 = params_wanted;
  1106.                 results-->0 = line_address->7;
  1107.  
  1108. !  ...and to reset "it"-style objects to the first of these parameters, if
  1109. !  there is one (and it really is an object)...
  1110.  
  1111.                 if (parameters > 0 && results-->2 >= 2)
  1112.                     ResetVagueWords(results-->2);
  1113.  
  1114. !  ...and declare the user's input to be error free...
  1115.  
  1116.                 oops_from = 0;
  1117.  
  1118. !  ...and worry about the case where an object was allowed as a parameter
  1119. !  even though the player wasn't holding it and should have been: in this
  1120. !  event, keep the results for next time round, go into "not holding" mode,
  1121. !  and for now tell the player what's happening and return a "take" request
  1122. !  instead...
  1123.  
  1124.                 if (not_holding~=0 && actor==player)
  1125.                 {   notheld_mode=1;
  1126.                     for (i=0:i<8:i++) kept_results-->i = results-->i;
  1127.                     results-->0 = ##Take;
  1128.                     results-->1 = 1;
  1129.                     results-->2 = not_holding;
  1130.                     print "(first taking "; DefArt(not_holding); print ")^";
  1131.                 }
  1132.  
  1133. !  (Notice that implicit takes are only generated for the player, and not
  1134. !  for other actors.  This avoids entirely logical, but misleading, text
  1135. !  being printed.)
  1136.  
  1137. !  ...and finish.
  1138.  
  1139.                 if (held_back_mode==1) { wn=hb_wn; jump LookForMore; }
  1140.                 rtrue;
  1141.             }
  1142.  
  1143. !  Otherwise, the player still has at least one parameter to specify: an
  1144. !  object of some kind is expected, and this we hand over to POL.
  1145.  
  1146.             if (token==6 && (action_to_be==##Answer or ##Ask or ##AskFor
  1147.                              || action_to_be==##Tell))
  1148.                 scope_reason=TALKING_REASON;
  1149.             l=ParseObjectList(results,token);
  1150.             scope_reason=PARSING_REASON;
  1151.             if (parser_trace>=3)
  1152.             {   print "  [Parse object list replied with";
  1153.                 if (l==REPARSE_CODE) print " re-parse request]^";
  1154.                 if (l==0) print " token failed, error type ", etype, "]^";
  1155.                 if (l==1) print " token accepted]^";
  1156.             }
  1157.             if (l==REPARSE_CODE) jump ReParse;
  1158.             if (l==0)    break;
  1159.  
  1160. !  The token has been successfully passed; we are ready for the next.
  1161.  
  1162.             .TokenPassed;
  1163.         }
  1164.  
  1165. !  But if we get here it means that the line failed somewhere, so we continue
  1166. !  the outer for loop and try the next line...
  1167.  
  1168.         if (etype>best_etype) best_etype=etype;
  1169.  
  1170. !  ...unless the line was something like "take all" which failed because
  1171. !  nothing matched the "all", in which case we stop and give an error now.
  1172.  
  1173.         if (take_all_rule == 2 && etype==NOTHING_PE) break;
  1174.    }
  1175.  
  1176. !  So that if we get here, each line for the specified verb has failed.
  1177.  
  1178. !  **** (H) ****
  1179.  
  1180.   .GiveError;
  1181.         etype=best_etype;
  1182.  
  1183. !  Errors are handled differently depending on who was talking.
  1184.  
  1185. !  If the command was addressed to somebody else (eg, "dwarf, sfgh") then
  1186. !  it is taken as conversation which the parser has no business in disallowing.
  1187.  
  1188.     if (actor~=player)
  1189.     {   if (usual_grammar_after>0)
  1190.         {   verb_wordnum = usual_grammar_after;
  1191.             jump AlmostReParse;
  1192.         }
  1193.         wn=verb_wordnum;
  1194.         special_word=NextWord();
  1195.         if (special_word=='xcomma')
  1196.         {   special_word=NextWord();
  1197.             verb_wordnum++;
  1198.         }
  1199.         special_number=TryNumber(verb_wordnum);
  1200.         results-->0=##NotUnderstood;
  1201.         results-->1=2;
  1202.         results-->2=1;
  1203.         results-->3=actor;
  1204.         consult_from = verb_wordnum; consult_words = num_words-consult_from+1;
  1205.         rtrue;
  1206.     }
  1207.  
  1208. !  **** (I) ****
  1209.  
  1210. !  If the player was the actor (eg, in "take dfghh") the error must be printed,
  1211. !  and fresh input called for.  In three cases the oops word must be jiggled.
  1212.  
  1213.     if (ParserError(etype)~=0) jump ReType;
  1214.  
  1215.     if (etype==STUCK_PE)
  1216.              {   print "I didn't understand that sentence.^"; oops_from=1; }
  1217.     if (etype==UPTO_PE)
  1218.              {   print "I only understood you as far as wanting to ";
  1219.                  for (m=0:m<8:m++) pattern-->m = pattern2-->m;
  1220.                  pcount=pcount2; PrintCommand(0,1); print ".^";
  1221.              }
  1222.     if (etype==NUMBER_PE)
  1223.                  print "I didn't understand that number.^";
  1224.     if (etype==CANTSEE_PE)
  1225.              {   print "You can't see any such thing.^";
  1226.                  oops_from=saved_oops; }
  1227.     if (etype==TOOLIT_PE)
  1228.                  print "You seem to have said too little!^";
  1229.     if (etype==NOTHELD_PE)
  1230.              {   print "You aren't holding that!^";
  1231.                  oops_from=saved_oops; }
  1232.     if (etype==MULTI_PE)
  1233.                  print "You can't use multiple objects with that verb.^";
  1234.     if (etype==MMULTI_PE)
  1235.                  print "You can only use multiple objects once on a line.^";
  1236.     if (etype==VAGUE_PE)
  1237.              {   print "I'm not sure what ~"; print_addr vague_word;
  1238.                  print "~ refers to.^"; }
  1239.     if (etype==EXCEPT_PE)
  1240.                  print "You excepted something not included anyway!^";
  1241.     if (etype==ANIMA_PE)
  1242.                  print "You can only do that to something animate.^";
  1243.     if (etype==VERB_PE)
  1244.                  print "That's not a verb I recognise.^";
  1245.     if (etype==SCENERY_PE)
  1246.                  print "That's not something you need to refer to \
  1247.                         in the course of this game.^";
  1248.     if (etype==ITGONE_PE)
  1249.              {   print "You can't see ~"; print_addr vague_word;
  1250.                  print "~ ("; DefArt(vague_obj); print ") at the moment.^"; }
  1251.     if (etype==JUNKAFTER_PE)
  1252.                  print "I didn't understand the way that finished.^";
  1253.     if (etype==TOOFEW_PE)
  1254.              {   if (multi_had==0) print "None";
  1255.                  else { print "Only "; EnglishNumber(multi_had); }
  1256.                  print " of those ";
  1257.                  if (multi_had==1) print "is"; else print "are";
  1258.                  print " available.^"; }
  1259.     if (etype==NOTHING_PE)
  1260.              {   if (multi_wanted==100) print "Nothing to do!^";
  1261.                  else print "There are none at all available!^";  }
  1262.     if (etype==ASKSCOPE_PE)
  1263.     {            scope_stage=3; indirect(scope_error); }
  1264.  
  1265. !  **** (J) ****
  1266.  
  1267. !  And go (almost) right back to square one...
  1268.  
  1269.     jump ReType;
  1270.  
  1271. !  ...being careful not to go all the way back, to avoid infinite repetition
  1272. !  of a deferred command causing an error.
  1273.  
  1274.  
  1275. !  **** (K) ****
  1276.  
  1277. !  At this point, the return value is all prepared, and we are only looking
  1278. !  to see if there is a "then" followed by subsequent instruction(s).
  1279.     
  1280.    .LookForMore;
  1281.  
  1282.    if (wn>num_words) rtrue;
  1283.  
  1284.    i=NextWord();
  1285.    if (i=='then' || i==comma_word)
  1286.    {   if (wn>num_words)
  1287.        { parse2->1=(parse2->1)-1; held_back_mode = 0; rtrue; }
  1288.        if (actor==player) j=0; else j=verb_wordnum-1;
  1289.        last_command_from = j+1; last_command_to = wn-2;
  1290.        i=NextWord();
  1291.        if (i=='again' or #n$g)
  1292.        {   for (i=0: i<j: i++)
  1293.            {   parse2-->(2*i+1) = parse-->(2*i+1);
  1294.                parse2-->(2*i+2) = parse-->(2*i+2);
  1295.            }
  1296.            for (i=last_command_from:i<=last_command_to:i++, j++)
  1297.            {   parse2-->(2+2*j) = parse-->(2*i);
  1298.                parse2-->(1+2*j) = parse-->(2*i-1);
  1299.            }
  1300.            for (i=wn:i<=num_words:i++, j++)
  1301.            {   parse2-->(2+2*j) = parse-->(2*i);
  1302.                parse2-->(1+2*j) = parse-->(2*i-1);
  1303.            }
  1304.            parse2->1=j; held_back_mode = 1; rtrue;
  1305.        }
  1306.        else wn--;
  1307.        for (i=0: i<j: i++)
  1308.        {   parse2-->(2*i+1) = parse-->(2*i+1);
  1309.            parse2-->(2*i+2) = parse-->(2*i+2);
  1310.        }
  1311.        for (i=wn:i<=num_words:i++, j++)
  1312.        {   parse2-->(2+2*j) = parse-->(2*i);
  1313.            parse2-->(1+2*j) = parse-->(2*i-1);
  1314.        }
  1315.        parse2->1=j; held_back_mode = 1; rtrue;
  1316.    }
  1317.    best_etype=UPTO_PE; jump GiveError;
  1318. ];
  1319.  
  1320. ! ----------------------------------------------------------------------------
  1321. !  NumberWord - fairly self-explanatory
  1322. ! ----------------------------------------------------------------------------
  1323.  
  1324. [ NumberWord o;
  1325.   if (o=='one') return 1;
  1326.   if (o=='two') return 2;
  1327.   if (o=='three') return 3;
  1328.   if (o=='four') return 4;
  1329.   if (o=='five') return 5;
  1330.   if (o=='six') return 6;
  1331.   if (o=='seven') return 7;
  1332.   if (o=='eight') return 8;
  1333.   if (o=='nine') return 9;
  1334.   if (o=='ten') return 10;
  1335.   if (o=='eleven') return 11;
  1336.   if (o=='twelve') return 12;
  1337.   if (o=='thirteen') return 13;
  1338.   if (o=='fourteen') return 14;
  1339.   if (o=='fifteen') return 15;
  1340.   if (o=='sixteen') return 16;
  1341.   if (o=='seventeen') return 17;
  1342.   if (o=='eighteen') return 18;
  1343.   if (o=='nineteen') return 19;
  1344.   if (o=='twenty') return 20;
  1345.   return 0;
  1346. ];
  1347.  
  1348. ! ----------------------------------------------------------------------------
  1349. !  Descriptors()
  1350. !
  1351. !  Handles descriptive words like "my", "his", "another" and so on.
  1352. !  Skips "the", and leaves wn pointing to the first misunderstood word.
  1353. !
  1354. !  Allowed to set up for a plural only if allow_p is set
  1355. !
  1356. !  Returns error number, or 0 if no error occurred
  1357. ! ----------------------------------------------------------------------------
  1358.  
  1359. Constant OTHER_BIT    1;     !  These will be used in Adjudicate()
  1360. Constant MY_BIT       2;     !  to disambiguate choices
  1361. Constant THAT_BIT     4;
  1362. Constant PLURAL_BIT   8;
  1363. Constant ITS_BIT     16;
  1364. Constant HIS_BIT     32;
  1365. Constant LIT_BIT     64;
  1366. Constant UNLIT_BIT  128;
  1367.  
  1368. [ Descriptors context  o flag n;
  1369.  
  1370.    indef_mode=0; indef_type=0; indef_wanted=0; indef_guess_p=0;
  1371.  
  1372.    for (flag=1:flag==1:)
  1373.    {   o=NextWord(); flag=0;
  1374.        if (o=='the') flag=1;
  1375.        if (o==#n$a or 'an' or 'any' || o=='either' or 'anything')
  1376.                             { indef_mode=1; flag=1; }
  1377.        if (o=='another' or 'other')
  1378.                             { indef_mode=1; flag=1;
  1379.                               indef_type = indef_type | OTHER_BIT; }
  1380.        if (o=='my' or 'this' or 'these')
  1381.                             { indef_mode=1; flag=1;
  1382.                               indef_type = indef_type | MY_BIT; }
  1383.        if (o=='that' or 'those')
  1384.                             { indef_mode=1; flag=1;
  1385.                               indef_type = indef_type | THAT_BIT; }
  1386.        if (o=='its')
  1387.                             { indef_mode=1; flag=1;
  1388.                               indef_type = indef_type | ITS_BIT; }
  1389.        if (o=='his' or 'your')
  1390.                             { indef_mode=1; flag=1;
  1391.                               indef_type = indef_type | HIS_BIT; }
  1392.        if (o=='lit' or 'lighted')
  1393.                             { indef_mode=1; flag=1;
  1394.                               indef_type = indef_type | LIT_BIT; }
  1395.        if (o=='unlit')
  1396.                             { indef_mode=1; flag=1;
  1397.                               indef_type = indef_type | UNLIT_BIT; }
  1398.        if (o=='all' or 'each' or 'every' || o=='everything')
  1399.                             { indef_mode=1; flag=1; indef_wanted=100;
  1400.                               if (take_all_rule == 1)
  1401.                                   take_all_rule = 2;
  1402.                               indef_type = indef_type | PLURAL_BIT; }
  1403.        if (allow_plurals==1)
  1404.        {   n=NumberWord(o);
  1405.            if (n>1)         { indef_guess_p=1;
  1406.                               indef_mode=1; flag=1; indef_wanted=n;
  1407.                               indef_type = indef_type | PLURAL_BIT; }
  1408.        }
  1409.        if (flag==1 && NextWord() ~= 'of') wn--;  ! Skip 'of' after these
  1410.    }
  1411.    wn--;
  1412.    if ((indef_wanted > 0) && (context<2 || context>5)) return MULTI_PE;
  1413.    return 0;
  1414. ];
  1415.  
  1416. ! ----------------------------------------------------------------------------
  1417. !  CreatureTest: Will this person do for a "creature" token?
  1418. ! ----------------------------------------------------------------------------
  1419.  
  1420. [ CreatureTest obj;
  1421.   if (obj has animate) rtrue;
  1422.   if (obj hasnt talkable) rfalse;
  1423.   if (action_to_be==##Ask or ##Answer or ##Tell
  1424.       || action_to_be==##AskFor) rtrue;
  1425.   rfalse;
  1426. ];
  1427.  
  1428. ! ----------------------------------------------------------------------------
  1429. !  ParseObjectList: Parses tokens 0 to 179, from the current word number wn
  1430. !
  1431. !  Returns:
  1432. !    REPARSE_CODE for "reconstructed input, please re-parse from scratch"
  1433. !    1            for "token accepted"
  1434. !    0            for "token failed"
  1435. !
  1436. !  (A)            Preliminaries and special/number tokens
  1437. !  (B)            Actual object names (mostly subcontracted!)
  1438. !  (C)            and/but and so on
  1439. !  (D)            Returning an accepted token
  1440. !
  1441. ! ----------------------------------------------------------------------------
  1442.  
  1443. [ ParseObjectList results token  l o i j k
  1444.                                  and_parity single_object desc_wn many_flag;
  1445.  
  1446.     many_flag=0; and_parity=1; dont_infer=0;
  1447.  
  1448. !  **** (A) ****
  1449. !  We expect to find a list of objects next in what the player's typed.
  1450.  
  1451.   .ObjectList;
  1452.  
  1453.    if (parser_trace>=3) print "  [Object list from word ", wn, "]^";
  1454.  
  1455. !  Take an advance look at the next word: if it's "it" or "them", and these
  1456. !  are unset, set the appropriate error number and give up on the line
  1457. !  (if not, these are still parsed in the usual way - it is not assumed
  1458. !  that they still refer to something in scope)
  1459.  
  1460.     o=NextWord(); wn--;
  1461.     if (o=='it' or 'them')
  1462.     {   vague_word=o; vague_obj=itobj;
  1463.         if (itobj==0) { etype=VAGUE_PE; return 0; }
  1464.     }
  1465.     if (o=='him')
  1466.     {   vague_word=o; vague_obj=himobj;
  1467.         if (himobj==0) { etype=VAGUE_PE; return 0; }
  1468.     }
  1469.     if (o=='her')
  1470.     {   vague_word=o; vague_obj=herobj;
  1471.         if (herobj==0) { etype=VAGUE_PE; return 0; }
  1472.     }
  1473.     if (o=='me' or 'myself' or 'self')
  1474.     {   vague_word=o; vague_obj=player;
  1475.     }
  1476.  
  1477. !  Firstly, get rid of tokens 7 and 8 ("special" and "number"), and
  1478. !  tokens which are entirely handed out to outside routines
  1479.  
  1480.     if (token==7)
  1481.     {   l=TryNumber(wn);
  1482.         if (l~=-1000)
  1483.         {   if (nsns==0) special_number=l; else special_number2=l;
  1484.             nsns++;
  1485.             if (parser_trace>=3)
  1486.                 print "  [Read special as the number ", l, "]^";
  1487.         }
  1488.         if (parser_trace>=3)
  1489.             print "  [Read special word at word number ", wn, "]^";
  1490.         special_word=NextWord(); single_object=1; jump PassToken;
  1491.     }
  1492.     if (token==8)
  1493.     {   l=TryNumber(wn++);
  1494.         if (l==-1000) { etype=NUMBER_PE; rfalse; }
  1495.         if (parser_trace>=3) print "  [Read number as ", l, "]^";
  1496.         if (nsns++==0) special_number=l; else special_number2=l;
  1497.         single_object=1; jump PassToken;
  1498.     }
  1499.  
  1500.     if (token>=48 && token<80)
  1501.     {   l=indirect(#preactions_table-->(token-48));
  1502.         if (parser_trace>=3)
  1503.             print "  [Outside parsing routine returned ", l, "]^";
  1504.         if (l<0) rfalse;
  1505.         if (l==0) { params_wanted--; rtrue; }  ! An adjective after all...
  1506.         if (l==1)
  1507.         {   if (nsns==0) special_number=parsed_number;
  1508.             else special_number2=parsed_number;
  1509.             nsns++;
  1510.         }
  1511.         if (l==REPARSE_CODE) return l;
  1512.         single_object=l; jump PassToken;
  1513.     }
  1514.  
  1515.     if (token>=80 && token<128)
  1516.     {   scope_token = #preactions_table-->(token-80);
  1517.         scope_stage = 1;
  1518.         l=indirect(scope_token);
  1519.         if (parser_trace>=3)
  1520.             print "  [Scope routine returned multiple-flag of ", l, "]^";
  1521.         if (l==1) token=2; else token=0;
  1522.     }
  1523.  
  1524.     token_was=0;
  1525.     if (token>=16)
  1526.     {   token_was = token;
  1527.         token=0;
  1528.     }
  1529.  
  1530. !  Otherwise, we have one of the tokens 0 to 6, all of which really do mean
  1531. !  that objects are expected.
  1532.  
  1533. !  So now we parse any descriptive words
  1534.  
  1535.     allow_plurals = 1; desc_wn = wn;
  1536.     .TryAgain;
  1537.  
  1538.     l=Descriptors(token); if (l~=0) { etype=l; return 0; }
  1539.  
  1540. !  **** (B) ****
  1541.  
  1542. !  This is an actual specified object, and is therefore where a typing error
  1543. !  is most likely to occur, so we set:
  1544.  
  1545.     oops_from=wn;
  1546.  
  1547. !  In either case below we use NounDomain, giving it the token number as
  1548. !  context, and two places to look: among the actor's possessions, and in the
  1549. !  present location.  (Note that the order depends on which is likeliest.)
  1550.  
  1551. !  So, two cases.  Case 1: token not equal to "held" (so, no implicit takes)
  1552. !  but we may well be dealing with multiple objects
  1553.  
  1554.     if (token~=1)
  1555.     {   i=multiple_object-->0;
  1556.         if (parser_trace>=3)
  1557.             print "  [Calling NounDomain on location and actor]^";
  1558.         l=NounDomain(actors_location, actor, token);
  1559.         if (l==REPARSE_CODE) return l;                    ! Reparse after Q&A
  1560.         if (l==0) { etype=CantSee(); jump FailToken; }  ! Choose best error
  1561.         if (parser_trace>=3)
  1562.         {   if (l>1)
  1563.             {   print "  [ND returned "; DefArt(l); print "]^"; }
  1564.             else
  1565.             {   print "  [ND appended to the multiple object list:^";
  1566.                 k=multiple_object-->0;
  1567.                 for (j=i+1:j<=k:j++)
  1568.                 {   print "  Entry ", j, ": "; CDefArt(multiple_object-->j);
  1569.                     print " (", multiple_object-->j, ")^";
  1570.                 }
  1571.                 print "  List now has size ", k, "]^";
  1572.             }
  1573.         }
  1574.         if (l==1)
  1575.         {   if (many_flag==0)
  1576.             {   many_flag=1;
  1577.             }
  1578.             else                                  ! Merge with earlier ones
  1579.             {   k=multiple_object-->0;            ! (with either parity)
  1580.                 multiple_object-->0 = i;
  1581.                 for (j=i+1:j<=k:j++)
  1582.                 {   if (and_parity==1) MultiAdd(multiple_object-->j);
  1583.                     else MultiSub(multiple_object-->j);
  1584.                 }
  1585.         if (parser_trace>=3)
  1586.             print "  [Merging ", k-i, " new objects to the ", i, " old ones]^";
  1587.             }
  1588.         }
  1589.         else
  1590.         {   if (token==6 && CreatureTest(l)==0)   ! Animation is required
  1591.             {   etype=ANIMA_PE; jump FailToken; } ! for token 6
  1592.             if (many_flag==0)
  1593.                 single_object = l;
  1594.             else
  1595.             {   if (and_parity==1) MultiAdd(l); else MultiSub(l);
  1596.                 if (parser_trace>=3)
  1597.                 {   print "  [Combining "; DefArt(l); print " with list]^";
  1598.                 }
  1599.             }
  1600.         }
  1601.     }
  1602.  
  1603. !  Case 2: token is "held" (which fortunately can't take multiple objects)
  1604. !  and may generate an implicit take
  1605.  
  1606.     if (token==1)
  1607.     {   l=NounDomain(actor,actors_location,token);       ! Same as above...
  1608.         if (l==REPARSE_CODE) return l;
  1609.         if (l==0) { etype=CantSee(); return l; }
  1610.  
  1611. !  ...until it produces something not held by the actor.  Then an implicit
  1612. !  take must be tried.  If this is already happening anyway, things are too
  1613. !  confused and we have to give up (but saving the oops marker so as to get
  1614. !  it on the right word afterwards).
  1615. !  The point of this last rule is that a sequence like
  1616. !
  1617. !      > read newspaper
  1618. !      (taking the newspaper first)
  1619. !      The dwarf unexpectedly prevents you from taking the newspaper!
  1620. !
  1621. !  should not be allowed to go into an infinite repeat - read becomes
  1622. !  take then read, but take has no effect, so read becomes take then read...
  1623. !  Anyway for now all we do is record the number of the object to take.
  1624.  
  1625.         o=parent(l);
  1626.         if (o~=actor)
  1627.         {   if (notheld_mode==1)
  1628.             {   saved_oops=oops_from; etype=NOTHELD_PE; jump FailToken;
  1629.             }
  1630.             not_holding = l;
  1631.             if (parser_trace>=3)
  1632.             {   print "  [Allowing object "; DefArt(l); print " for now]^";
  1633.             }
  1634.         }
  1635.         single_object = l;
  1636.     }
  1637.  
  1638. !  The following moves the word marker to just past the named object...
  1639.  
  1640.     wn = oops_from + match_length;
  1641.  
  1642. !  **** (C) ****
  1643.  
  1644. !  Object(s) specified now: is that the end of the list, or have we reached
  1645. !  "and", "but" and so on?  If so, create a multiple-object list if we
  1646. !  haven't already (and are allowed to).
  1647.  
  1648.     .NextInList;
  1649.  
  1650.     o=NextWord();
  1651.  
  1652.     if (o=='and' or 'but' or 'except' || o==comma_word)
  1653.     {
  1654.         if (parser_trace>=3)
  1655.         {   print "  [Read '"; print_addr o; print "']^";
  1656.         }
  1657.  
  1658.         if (token<2 || token>=6) { etype=MULTI_PE; jump FailToken; }
  1659.  
  1660.         if (o=='but' or 'except') and_parity = 1-and_parity;
  1661.  
  1662.         if (many_flag==0)
  1663.         {   multiple_object-->0 = 1;
  1664.             multiple_object-->1 = single_object;
  1665.             many_flag=1;
  1666.             if (parser_trace>=3)
  1667.             {   print "  [Making new list from ";
  1668.                 DefArt(single_object); print "]^";
  1669.             }
  1670.         }
  1671.         dont_infer = 1; inferfrom=0;              ! Don't print (inferences)
  1672.         jump ObjectList;                          ! And back around
  1673.     }
  1674.  
  1675.     wn--;   ! Word marker back to first not-understood word
  1676.  
  1677. !  **** (D) ****
  1678.  
  1679. !  Happy or unhappy endings:
  1680.  
  1681.     .PassToken;
  1682.  
  1683.     if (many_flag==1)
  1684.     {   single_object = 0;
  1685.         multi_context = token;
  1686.     }
  1687.     else
  1688.     {   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  1689.         {   if (indef_wanted<100 && indef_wanted>1)
  1690.             {   multi_had=1; multi_wanted=indef_wanted;
  1691.                 etype=TOOFEW_PE;
  1692.                 jump FailToken;
  1693.             }
  1694.         }
  1695.     }
  1696.     results-->(parameters+2) = single_object;
  1697.     parameters++;
  1698.     pattern-->pcount = single_object;
  1699.     return 1;
  1700.  
  1701.     .FailToken;
  1702.  
  1703. !  If we were only guessing about it being a plural, try again but only
  1704. !  allowing singulars (so that words like "six" are not swallowed up as
  1705. !  Descriptors)
  1706.  
  1707.     if (allow_plurals==1 && indef_guess_p==1)
  1708.     {   allow_plurals=0; wn=desc_wn; jump TryAgain;
  1709.     }
  1710.     return 0;
  1711. ];
  1712.  
  1713. ! ----------------------------------------------------------------------------
  1714. !  NounDomain does the most substantial part of parsing an object name.
  1715. !
  1716. !  It is given two "domains" - usually a location and then the actor who is
  1717. !  looking - and a context (i.e. token type), and returns:
  1718. !
  1719. !   0    if no match at all could be made,
  1720. !   1    if a multiple object was made,
  1721. !   k    if object k was the one decided upon,
  1722. !   REPARSE_CODE if it asked a question of the player and consequently rewrote all
  1723. !        the player's input, so that the whole parser should start again
  1724. !        on the rewritten input.
  1725. !
  1726. !   In the case when it returns 1<k<REPARSE_CODE, it also sets the variable
  1727. !   length_of_noun to the number of words in the input text matched to the
  1728. !   noun.
  1729. !   In the case k=1, the multiple objects are added to multiple_object by
  1730. !   hand (not by MultiAdd, because we want to allow duplicates).
  1731. ! ----------------------------------------------------------------------------
  1732.  
  1733. [ NounDomain domain1 domain2 context  first_word i j k l oldw
  1734.                                       answer_words marker;
  1735.  
  1736.   if (parser_trace>=4) print "   [NounDomain called at word ", wn, "^";
  1737.  
  1738.   match_length=0; number_matched=0; match_from=wn; placed_in_flag=0;
  1739.  
  1740.   SearchScope(domain1, domain2, context);
  1741.  
  1742.   if (parser_trace>=4) print "   [ND made ", number_matched, " matches]^";
  1743.  
  1744.   wn=match_from+match_length;
  1745.  
  1746. !  If nothing worked at all, leave with the word marker skipped past the
  1747. !  first unmatched word...
  1748.  
  1749.   if (number_matched==0) { wn++; rfalse; }
  1750.  
  1751. !  Suppose that there really were some words being parsed (i.e., we did
  1752. !  not just infer).  If so, and if there was only one match, it must be
  1753. !  right and we return it...
  1754.  
  1755.   if (match_from <= num_words)
  1756.   {   if (number_matched==1) { i=match_list-->0; return i; }
  1757.  
  1758. !  ...now suppose that there was more typing to come, i.e. suppose that
  1759. !  the user entered something beyond this noun.  Use the lookahead token
  1760. !  to check that if an adjective comes next, it is the right one.  (If
  1761. !  not then there must be a mistake like "press red buttno" where "red"
  1762. !  has been taken for the noun in the mistaken belief that "buttno" is
  1763. !  some preposition or other.)
  1764. !
  1765. !  If nothing ought to follow, then similarly there must be a mistake,
  1766. !  (unless what does follow is just a full stop, and or comma)
  1767.  
  1768.       if (wn<=num_words)
  1769.       {   i=NextWord(); wn--;
  1770.           if ((i~='and' or comma_word or 'then')
  1771.               && (i~='but' or 'except'))
  1772.           {   if (lookahead==8) rfalse;
  1773.               if (lookahead>8)
  1774.               {   if (lookahead~=AdjectiveWord())
  1775.                   { wn--; if (parser_trace>=3)
  1776.                     print "   [ND failed at lookahead at word ", wn, "]^";
  1777.                     rfalse;
  1778.                   }
  1779.                   wn--;
  1780.               }
  1781.           }
  1782.       }
  1783.   }
  1784.  
  1785. !  Now look for a good choice, if there's more than one choice...
  1786.  
  1787.   number_of_classes=0;
  1788.   
  1789.   if (number_matched==1) i=match_list-->0;
  1790.   if (number_matched>1)
  1791.   {   i=Adjudicate(context);
  1792.       if (i==-1) rfalse;
  1793.       if (i==1) rtrue;       !  Adjudicate has made a multiple
  1794.                              !  object, and we pass it on
  1795.   }
  1796.  
  1797. !  If i is non-zero here, one of two things is happening: either
  1798. !  (a) an inference has been successfully made that object i is
  1799. !      the intended one from the user's specification, or
  1800. !  (b) the user finished typing some time ago, but we've decided
  1801. !      on i because it's the only possible choice.
  1802. !  In either case we have to keep the pattern up to date,
  1803. !  note that an inference has been made and return.
  1804. !  (Except, we don't note which of a pile of identical objects.)
  1805.  
  1806.   if (i~=0)
  1807.   {   if (dont_infer==1) return i;
  1808.       if (inferfrom==0) inferfrom=pcount;
  1809.       pattern-->pcount = i;
  1810.       return i;
  1811.   }
  1812.  
  1813. !  If we get here, there was no obvious choice of object to make.  If in
  1814. !  fact we've already gone past the end of the player's typing (which
  1815. !  means the match list must contain every object in scope, regardless
  1816. !  of its name), then it's foolish to give an enormous list to choose
  1817. !  from - instead we go and ask a more suitable question...
  1818.  
  1819.   if (match_from > num_words) jump Incomplete;
  1820.  
  1821. !  Now we print up the question, using the equivalence classes as worked
  1822. !  out by Adjudicate() so as not to repeat ourselves on plural objects...
  1823.  
  1824.   if (context==6) print "Who"; else print "Which";
  1825.   print " do you mean, ";
  1826.   j=number_of_classes; marker=0;
  1827.   for (i=1:i<=number_of_classes:i++)
  1828.   {   
  1829.       while (((match_classes-->marker) ~= i)
  1830.              && ((match_classes-->marker) ~= -i)) marker++;
  1831.       k=match_list-->marker;
  1832.  
  1833.       if (match_classes-->marker > 0) DefArt(k); else InDefArt(k);
  1834.  
  1835.       if (i<j-1)  print ", ";
  1836.       if (i==j-1) print " or ";
  1837.   }
  1838.   print "?^";
  1839.  
  1840. !  ...and get an answer:
  1841.  
  1842.   .WhichOne;
  1843.   answer_words=Keyboard(buffer2, parse2);
  1844.  
  1845.   first_word=(parse2-->1);
  1846.  
  1847. !  Take care of "all", because that does something too clever here to do
  1848. !  later on:
  1849.  
  1850.   if ((first_word=='all' or 'both' or 'everything')
  1851.       || (first_word=='every' or 'each'))
  1852.   {   
  1853.       if (context>=2 && context<=5)
  1854.       {   l=multiple_object-->0;
  1855.           for (i=0:i<number_matched && l+i<63:i++)
  1856.           {   k=match_list-->i;
  1857.               multiple_object-->(i+1+l) = k;
  1858.           }
  1859.           multiple_object-->0 = i+l;
  1860.           rtrue;
  1861.       }
  1862.       print "Sorry, you can only have one item here.  Which one exactly?^";
  1863.       jump WhichOne;
  1864.   }
  1865.  
  1866. !  If the first word of the reply can be interpreted as a verb, then
  1867. !  assume that the player has ignored the question and given a new
  1868. !  command altogether.
  1869. !  (This is one time when it's convenient that the directions are
  1870. !  not themselves verbs - thus, "north" as a reply to "Which, the north
  1871. !  or south door" is not treated as a fresh command but as an answer.)
  1872.  
  1873.   j=first_word->#dict_par1;
  1874.   if (0~=j&1)
  1875.   {   Copy(buffer, buffer2);
  1876.       Copy(parse, parse2);
  1877.       return REPARSE_CODE;
  1878.   }
  1879.  
  1880. !  Now we insert the answer into the original typed command, as
  1881. !  words additionally describing the same object
  1882. !  (eg, > take red button
  1883. !       Which one, ...
  1884. !       > music
  1885. !  becomes "take music red button".  The parser will thus have three
  1886. !  words to work from next time, not two.)
  1887. !
  1888. !  To do this we use MoveWord which copies in a word.
  1889.  
  1890.   oldw=parse->1;
  1891.   parse->1 = answer_words+oldw;
  1892.  
  1893.   for (k=oldw+answer_words : k>match_from : k--)
  1894.       MoveWord(k, parse, k-answer_words);
  1895.  
  1896.   for (k=1:k<=answer_words:k++)
  1897.       MoveWord(match_from+k-1, parse2, k);
  1898.  
  1899. !  Having reconstructed the input, we warn the parser accordingly
  1900. !  and get out.
  1901.  
  1902.   return REPARSE_CODE;
  1903.  
  1904. !  Now we come to the question asked when the input has run out
  1905. !  and can't easily be guessed (eg, the player typed "take" and there
  1906. !  were plenty of things which might have been meant).
  1907.  
  1908.   .Incomplete;
  1909.  
  1910.   if (context==6) print "Whom"; else print "What";
  1911.   print " do you want";
  1912.   if (actor~=player) { print " "; DefArt(actor); }
  1913.   print " to "; PrintCommand(0,1); print "?^";
  1914.  
  1915.   answer_words=Keyboard(buffer2, parse2);
  1916.  
  1917.   first_word=(parse2-->1);
  1918.  
  1919. !  Once again, if the reply looks like a command, give it to the
  1920. !  parser to get on with and forget about the question...
  1921.  
  1922.   j=first_word->#dict_par1;
  1923.   if (0~=j&1)
  1924.   {   Copy(buffer, buffer2);
  1925.       Copy(parse, parse2);
  1926.       return REPARSE_CODE;
  1927.   }
  1928.  
  1929. !  ...but if we have a genuine answer, then we adjoin the words
  1930. !  typed onto the expression.  But if we've just inferred something
  1931. !  which wasn't actually there, we must adjoin that as well.  (Note
  1932. !  the sneaky use of "it" to match an object inferred this time round.)
  1933.  
  1934.   oldw=parse->1;
  1935.   if (inferfrom==0)
  1936.       for (k=1:k<=answer_words:k++)
  1937.           MoveWord(oldw+k, parse2, k);
  1938.   else
  1939.   {   j=pcount-inferfrom;
  1940.       for (k=1:k<=answer_words:k++)
  1941.           MoveWord(oldw+k+j, parse2, k);
  1942.       for (j=inferfrom:j<pcount:j++)
  1943.       {   if (pattern-->j >= 2 && pattern-->j < REPARSE_CODE)
  1944.           {   parse2-->1 = 'it'; itobj = pattern-->j;
  1945.           }
  1946.           else parse2-->1 = AdjectiveAddress((pattern-->j) - REPARSE_CODE);
  1947.           MoveWord(oldw+1+j-inferfrom, parse2, 1);
  1948.           answer_words++;
  1949.       }
  1950.   }
  1951.   parse->1 = answer_words+oldw;
  1952.  
  1953. !  And go back to the parser.
  1954.   return REPARSE_CODE;
  1955. ];
  1956.  
  1957. ! ----------------------------------------------------------------------------
  1958. !  The Adjudicate routine tries to see if there is an obvious choice, when
  1959. !  faced with a list of objects (the match_list) each of which matches the
  1960. !  player's specification equally well.
  1961. !
  1962. !  To do this it makes use of the context (the token type being worked on).
  1963. !  It counts up the number of obvious choices for the given context
  1964. !  (all to do with where a candidate is, except for 6 (animate) which is to
  1965. !  do with whether it is animate or not);
  1966. !
  1967. !  if only one obvious choice is found, that is returned;
  1968. !
  1969. !  if we are in indefinite mode (don't care which) one of the obvious choices
  1970. !    is returned, or if there is no obvious choice then an unobvious one is
  1971. !    made;
  1972. !
  1973. !  at this stage, we work out whether the objects are distinguishable from
  1974. !    each other or not: if they are all indistinguishable from each other,
  1975. !    then choose one, it doesn't matter which;
  1976. !
  1977. !  otherwise, 0 (meaning, unable to decide) is returned (but remember that
  1978. !    the equivalence classes we've just worked out will be needed by other
  1979. !    routines to clear up this mess, so we can't economise on working them
  1980. !    out).
  1981. !
  1982. !  Returns -1 if an error occurred
  1983. ! ----------------------------------------------------------------------------
  1984.  
  1985. [ Adjudicate context i j k good_ones last n ultimate flag offset;
  1986.  
  1987.   if (parser_trace>=4)
  1988.       print "   [Adjudicating match list of size ", number_matched, "^";
  1989.  
  1990.   j=number_matched-1; good_ones=0; last=match_list-->0;
  1991.   for (i=0:i<=j:i++)
  1992.   {   n=match_list-->i;
  1993.       if (n hasnt concealed)
  1994.       {   ultimate=n;
  1995.           do
  1996.               ultimate=parent(ultimate);
  1997.           until (ultimate==actors_location or actor or 0);
  1998.  
  1999.           if (context==0 && ultimate==actors_location &&
  2000.               (token_was==0 || UserFilter(n)==1)) { good_ones++; last=n; }
  2001.           if (context==1 && parent(n)==actor)     { good_ones++; last=n; }
  2002.           if (context==2 && ultimate==actors_location) 
  2003.                                                   { good_ones++; last=n; }
  2004.           if (context==3 && parent(n)==actor)     { good_ones++; last=n; }
  2005.  
  2006.           if (context==4 or 5)
  2007.           {   if (advance_warning==-1)
  2008.               {   if (parent(n)==actor) { good_ones++; last=n; }
  2009.               }
  2010.               else
  2011.               {   if (context==4 && parent(n)==actor && n~=advance_warning)
  2012.                   { good_ones++; last=n; }
  2013.                   if (context==5 && parent(n)==actor && n in advance_warning)
  2014.                   { good_ones++; last=n; }
  2015.               }
  2016.           }
  2017.           if (context==6 && CreatureTest(n)==1)   { good_ones++; last=n; }
  2018.       }
  2019.   }
  2020.   if (good_ones==1) return last;
  2021.  
  2022.   ! If there is ambiguity about what was typed, but it definitely wasn't
  2023.   ! animate as required, then return anything; higher up in the parser
  2024.   ! a suitable error will be given.  (This prevents a question being asked.)
  2025.   !
  2026.   if (context==6 && good_ones==0) return match_list-->0;
  2027.  
  2028.   if (indef_mode==1 && indef_type & PLURAL_BIT ~= 0)
  2029.   {   if (context<2 || context>5) { etype=MULTI_PE; return -1; }
  2030.       i=0; number_of_classes=1; offset=multiple_object-->0;
  2031.       for (j=BestGuess():j~=-1 && i<indef_wanted
  2032.            && i+offset<63:j=BestGuess())
  2033.       {   flag=0;
  2034.           if (j hasnt concealed && j hasnt worn) flag=1;
  2035.           if (context==3 or 4 && parent(j)~=actor) flag=0;
  2036.           k=ChooseObjects(j,flag);
  2037.           if (k==1) flag=1; else { if (k==2) flag=0; }
  2038.           if (flag==1)
  2039.           {   i++; multiple_object-->(i+offset) = j;
  2040.               if (parser_trace>=4)
  2041.               print "   Accepting it^";
  2042.           }
  2043.           else
  2044.           {   if (parser_trace>=4)
  2045.                   print "   Rejecting it^";
  2046.           }
  2047.       }
  2048.       if (i<indef_wanted && indef_wanted<100)
  2049.       {   etype=TOOFEW_PE; multi_wanted=indef_wanted;
  2050.           multi_had=multiple_object-->0;
  2051.           return -1;
  2052.       }
  2053.       multiple_object-->0 = i+offset;
  2054.       multi_context=context;
  2055.       if (parser_trace>=4)
  2056.           print "   Made multiple object of size ", i, "]^";
  2057.       return 1;
  2058.   }
  2059.  
  2060.   for (i=0:i<number_matched:i++) match_classes-->i=0;
  2061.  
  2062.   n=1;
  2063.   for (i=0:i<number_matched:i++)
  2064.       if (match_classes-->i==0)
  2065.       {   match_classes-->i=n++; flag=0;
  2066.           for (j=i+1:j<number_matched:j++)
  2067.               if (match_classes-->j==0
  2068.                   && Identical(match_list-->i, match_list-->j)==1)
  2069.               {   flag=1;
  2070.                   match_classes-->j=match_classes-->i;
  2071.               }
  2072.           if (flag==1) match_classes-->i = 1-n;
  2073.       }
  2074.   n--;
  2075.  
  2076.   if (parser_trace>=4)
  2077.   {   print "   Difficult adjudication with ", n, " equivalence classes:^";
  2078.       for (i=0:i<number_matched:i++)
  2079.       {   print "   "; CDefArt(match_list-->i);
  2080.           print " (", match_list-->i, ")  ---  ",match_classes-->i, "^";
  2081.       }
  2082.   }
  2083.  
  2084.   number_of_classes = n;
  2085.  
  2086.   if (n>1 && indef_mode==0)
  2087.   {   j=0; good_ones=0;
  2088.       for (i=0:i<number_matched:i++)
  2089.       {   k=ChooseObjects(match_list-->i,2);
  2090.           if (k==j) good_ones++;
  2091.           if (k>j) { j=k; good_ones=1; last=match_list-->i; }
  2092.       }
  2093.       if (good_ones==1)
  2094.       {   if (parser_trace>=4)
  2095.               print "   ChooseObjects picked a best.]^";
  2096.           return last;
  2097.       }
  2098.       if (parser_trace>=4)
  2099.           print "   Unable to decide: it's a draw.]^";
  2100.       return 0;
  2101.   }
  2102.  
  2103. !  When the player is really vague, or there's a single collection of
  2104. !  indistinguishable objects to choose from, choose the one the player
  2105. !  most recently acquired, or if the player has none of them, then
  2106. !  the one most recently put where it is.
  2107.  
  2108.   if (indef_mode==0) indef_type=0;
  2109.   if (n==1) dont_infer = 1;
  2110.  
  2111.   return BestGuess();
  2112. ];
  2113.  
  2114. ! ----------------------------------------------------------------------------
  2115. !  ReviseMulti  revises the multiple object which already exists, in the
  2116. !    light of information which has come along since then (i.e., the second
  2117. !    parameter).  It returns a parser error number, or else 0 if all is well.
  2118. !    This only ever throws things out, never adds new ones.
  2119. ! ----------------------------------------------------------------------------
  2120.  
  2121. [ ReviseMulti second_p  i low;
  2122.  
  2123.   if (parser_trace>=4)
  2124.       print "   Revising multiple object list of size ", multiple_object-->0,
  2125.             " with 2nd ", object second_p, "^";
  2126.  
  2127.   if (multi_context==4 or 5)
  2128.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2129.       {   if ( (multi_context==4 && multiple_object-->i ~= second_p)
  2130.                || (multi_context==5 && multiple_object-->i in second_p))
  2131.           {   low++; multiple_object-->low = multiple_object-->i;
  2132.           }
  2133.       }
  2134.       multiple_object-->0 = low;
  2135.   }
  2136.  
  2137.   if (multi_context==2)
  2138.   {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2139.           if (parent(multiple_object-->i)==parent(actor)) low++;
  2140.       if (parser_trace>=4)
  2141.           print "   Token 2 plural case: number with actor ", low, "^";
  2142.       if (take_all_rule==2 || low>0)
  2143.       {   for (i=1, low=0:i<=multiple_object-->0:i++)
  2144.           {   if (parent(multiple_object-->i)==parent(actor))
  2145.               {   low++; multiple_object-->low = multiple_object-->i;
  2146.               }
  2147.           }
  2148.           multiple_object-->0 = low;
  2149.       }
  2150.   }
  2151.  
  2152.   i=multiple_object-->0;
  2153.   if (parser_trace>=4)
  2154.       print "   Done: new size ", i, "^";
  2155.   if (i==0) return NOTHING_PE;
  2156.   return 0;
  2157. ];
  2158.  
  2159. ! ----------------------------------------------------------------------------
  2160. !  ScoreMatchL  scores the match list for quality in terms of what the
  2161. !  player has vaguely asked for.  Points are awarded for conforming with
  2162. !  requirements like "my", and so on.  If the score is less than the
  2163. !  threshold, block out the entry to -1.
  2164. !  The scores are put in the match_classes array, which we can safely
  2165. !  reuse by now.
  2166. ! ----------------------------------------------------------------------------
  2167.  
  2168. [ ScoreMatchL  its_owner its_score obj i threshold a_s l_s;
  2169.  
  2170.   if (indef_type & OTHER_BIT ~= 0) threshold=40;
  2171.   if (indef_type & MY_BIT ~= 0)    threshold=threshold+40;
  2172.   if (indef_type & THAT_BIT ~= 0)  threshold=threshold+40;
  2173.   if (indef_type & ITS_BIT ~= 0)   threshold=threshold+40;
  2174.   if (indef_type & HIS_BIT ~= 0)   threshold=threshold+40;
  2175.   if (indef_type & LIT_BIT ~= 0)   threshold=threshold+40;
  2176.   if (indef_type & UNLIT_BIT ~= 0) threshold=threshold+40;
  2177.  
  2178.   if (parser_trace>=4) print "   Scoring match list with type ", indef_type,
  2179.       ", threshold ", threshold, ":^";
  2180.  
  2181.   a_s = 30; l_s = 20;
  2182.   if (action_to_be == ##Take or ##Remove) { a_s=20; l_s=30; }
  2183.  
  2184.   for (i=0:i<number_matched:i++)
  2185.   {   obj = match_list-->i; its_owner = parent(obj); its_score=0;
  2186.       if (its_owner==actor)   its_score=a_s;
  2187.       if (its_owner==actors_location) its_score=l_s;
  2188.       if (its_score==0 && its_owner~=compass) its_score=10;
  2189.  
  2190.       if (indef_type & OTHER_BIT ~=0
  2191.           &&  obj~=itobj or himobj or herobj)
  2192.           its_score=its_score+40;
  2193.       if (indef_type & MY_BIT ~=0  &&  its_owner==actor)
  2194.           its_score=its_score+40;
  2195.       if (indef_type & THAT_BIT ~=0  &&  its_owner==actors_location)
  2196.           its_score=its_score+40;
  2197.       if (indef_type & LIT_BIT ~=0  &&  obj has light)
  2198.           its_score=its_score+40;
  2199.       if (indef_type & UNLIT_BIT ~=0  &&  obj hasnt light)
  2200.           its_score=its_score+40;
  2201.       if (indef_type & ITS_BIT ~=0  &&  its_owner==itobj)
  2202.           its_score=its_score+40;
  2203.       if (indef_type & HIS_BIT ~=0  &&  its_owner has animate
  2204.           && GetGender(its_owner)==1)
  2205.           its_score=its_score+40;
  2206.  
  2207.       its_score=its_score + ChooseObjects(obj,2);
  2208.  
  2209.       if (its_score < threshold) match_list-->i=-1;
  2210.       else
  2211.       {   match_classes-->i=its_score;
  2212.           if (parser_trace >= 4)
  2213.           {   print "   "; CDefArt(match_list-->i);
  2214.               print " (", match_list-->i, ") in "; DefArt(its_owner);
  2215.               print " scores ",its_score, "^";
  2216.           }
  2217.       }
  2218.   }
  2219.   number_of_classes=2;
  2220. ];
  2221.  
  2222. ! ----------------------------------------------------------------------------
  2223. !  BestGuess makes the best guess it can out of the match list, assuming that
  2224. !  everything in the match list is textually as good as everything else;
  2225. !  however it ignores items marked as -1, and so marks anything it chooses.
  2226. !  It returns -1 if there are no possible choices.
  2227. ! ----------------------------------------------------------------------------
  2228.  
  2229. [ BestGuess  earliest its_score best i;
  2230.  
  2231.   if (number_of_classes~=1) ScoreMatchL();
  2232.  
  2233.   earliest=0; best=-1;
  2234.   for (i=0:i<number_matched:i++)
  2235.   {   if (match_list-->i >= 0)
  2236.       {   its_score=match_classes-->i;
  2237.           if (its_score>best) { best=its_score; earliest=i; }
  2238.       }
  2239.   }
  2240.   if (parser_trace>=4)
  2241.   {   if (best<0)
  2242.           print "   Best guess ran out of choices^";
  2243.       else
  2244.       {   print "   Best guess "; DefArt(match_list-->earliest);
  2245.           print  " (", match_list-->earliest, ")^";
  2246.       }
  2247.   }
  2248.   if (best<0) return -1;
  2249.   i=match_list-->earliest;
  2250.   match_list-->earliest=-1;
  2251.   return i;
  2252. ];
  2253.  
  2254. ! ----------------------------------------------------------------------------
  2255. !  Identical decides whether or not two objects can be distinguished from
  2256. !  each other by anything the player can type.  If not, it returns true.
  2257. ! ----------------------------------------------------------------------------
  2258.  
  2259. [ Identical o1 o2 p1 p2 n1 n2 i j flag;
  2260.  
  2261. !  print "Id on ", o1, " (", object o1, ") and ", o2, " (", object o2, ")^";
  2262.  
  2263.   if (o1==o2) rtrue;  ! This should never happen, but to be on the safe side
  2264.   if (o1==0 || o2==0) rfalse;  ! Similarly
  2265.   if (parent(o1)==compass || parent(o2)==compass) rfalse; ! Saves time
  2266.  
  2267. !  What complicates things is that o1 or o2 might have a parsing routine,
  2268. !  so the parser can't know from here whether they are or aren't the same.
  2269. !  If they have different parsing routines, we simply assume they're
  2270. !  different.  If they have the same routine (which they probably got from
  2271. !  a class definition) then the decision process is as follows:
  2272. !
  2273. !     the routine is called (with self being o1, not that it matters)
  2274. !       with noun and second being set to o1 and o2, and action being set
  2275. !       to the fake action TheSame.  If it returns -1, they are found
  2276. !       identical; if -2, different; and if >=0, then the usual method
  2277. !       is used instead.
  2278.  
  2279.   if (o1.parse_name~=0 || o2.parse_name~=0)
  2280.   {   if (o1.parse_name ~= o2.parse_name) rfalse;
  2281.       parser_action=##TheSame; parser_one=o1; parser_two=o2;
  2282.       j=wn; i=RunRoutines(o1,parse_name); wn=j;
  2283.       if (i==-1) rtrue; if (i==-2) rfalse;
  2284.   }
  2285.  
  2286. !  This is the default algorithm: do they have the same words in their
  2287. !  "name" (i.e. property no. 1) properties.  (Note that the following allows
  2288. !  for repeated words and words in different orders.)
  2289.  
  2290.   p1 = o1.&1; n1 = (o1.#1)/2;
  2291.   p2 = o2.&1; n2 = (o2.#1)/2;
  2292.  
  2293. !  for (i=0:i<n1:i++) { print_addr p1-->i; print " "; } new_line;
  2294. !  for (i=0:i<n2:i++) { print_addr p2-->i; print " "; } new_line;
  2295.  
  2296.   for (i=0:i<n1:i++)
  2297.   {   flag=0;
  2298.       for (j=0:j<n2:j++)
  2299.           if (p1-->i == p2-->j) flag=1;
  2300.       if (flag==0) rfalse;
  2301.   }
  2302.  
  2303.   for (j=0:j<n2:j++)
  2304.   {   flag=0;
  2305.       for (i=0:i<n1:i++)
  2306.           if (p1-->i == p2-->j) flag=1;
  2307.       if (flag==0) rfalse;
  2308.   }
  2309.  
  2310. !  print "Which are identical!^";
  2311.   rtrue;
  2312. ];
  2313.  
  2314. ! ----------------------------------------------------------------------------
  2315. !  PrintCommand reconstructs the command as it presently reads, from
  2316. !  the pattern which has been built up
  2317. !
  2318. !  If from is 0, it starts with the verb: then it goes through the pattern.
  2319. !  The other parameter is "emptyf" - a flag: if 0, it goes up to pcount:
  2320. !  if 1, it goes up to pcount-1.
  2321. !
  2322. !  Note that verbs and prepositions are printed out of the dictionary:
  2323. !  and that since the dictionary may only preserve the first six characters
  2324. !  of a word (in a V3 game), we have to hand-code the longer words needed.
  2325. !
  2326. !  (Recall that pattern entries are 0 for "multiple object", 1 for "special
  2327. !  word", 2 to 999 are object numbers and REPARSE_CODE+n means the preposition n)
  2328. ! ----------------------------------------------------------------------------
  2329.  
  2330. [ PrintCommand from emptyf i j k f;
  2331.   if (from==0)
  2332.   {   i=verb_word; from=1; f=1;
  2333. #IFV3;
  2334.       if (i=='inventory') { print "take an inventory"; jump VerbPrinted; }
  2335.       if (i=='examine')   { print "examine";           jump VerbPrinted; }
  2336.       if (i=='discard')   { print "discard";           jump VerbPrinted; }
  2337.       if (i=='swallow')   { print "swallow";           jump VerbPrinted; }
  2338.       if (i=='embrace')   { print "embrace";           jump VerbPrinted; }
  2339.       if (i=='squeeze')   { print "squeeze";           jump VerbPrinted; }
  2340.       if (i=='purchase')  { print "purchase";          jump VerbPrinted; }
  2341.       if (i=='unscrew')   { print "unscrew";           jump VerbPrinted; }
  2342.       if (i=='describe')  { print "describe";          jump VerbPrinted; }
  2343.       if (i=='uncover')   { print "uncover";           jump VerbPrinted; }
  2344.       if (i=='discard')   { print "discard";           jump VerbPrinted; }
  2345.       if (i=='transfer')  { print "transfer";          jump VerbPrinted; }
  2346. #ENDIF;
  2347.       if (i==#n$l)         { print "look";              jump VerbPrinted; }
  2348.       if (i==#n$z)         { print "wait";              jump VerbPrinted; }
  2349.       if (i==#n$x)         { print "examine";           jump VerbPrinted; }
  2350.       if (i==#n$i or 'inv') { print "inventory";        jump VerbPrinted; }
  2351.       if (PrintVerb(i)==0) print_addr i;
  2352.   }
  2353.   .VerbPrinted;
  2354.   j=pcount-emptyf;
  2355.   for (k=from:k<=j:k++)
  2356.   {   if (f==1) print_char ' ';
  2357.       i=pattern-->k;
  2358.       if (i==0) { print "those things"; jump TokenPrinted; }
  2359.       if (i==1) { print "that"; jump TokenPrinted; }
  2360.       if (i>=REPARSE_CODE)
  2361.       {   i=AdjectiveAddress(i-REPARSE_CODE);
  2362. #IFV3;
  2363.           if (i=='against') { print "against";      jump TokenPrinted; }
  2364. #ENDIF;
  2365.           print_addr i;
  2366.       }
  2367.       else DefArt(i);
  2368.       .TokenPrinted;
  2369.       f=1;
  2370.   }
  2371. ];
  2372.  
  2373. ! ----------------------------------------------------------------------------
  2374. !  The CantSee routine returns a good error number for the situation where
  2375. !  the last word looked at didn't seem to refer to any object in context.
  2376. !
  2377. !  The idea is that: if the actor is in a location (but not inside something
  2378. !  like, for instance, a tank which is in that location) then an attempt to
  2379. !  refer to one of the words listed as meaningful-but-irrelevant there
  2380. !  will cause "you don't need to refer to that in this game" rather than
  2381. !  "no such thing" or "what's 'it'?".
  2382. !  (The advantage of not having looked at "irrelevant" local nouns until now
  2383. !  is that it stops them from clogging up the ambiguity-resolving process.
  2384. !  Thus game objects always triumph over scenery.)
  2385. ! ----------------------------------------------------------------------------
  2386.  
  2387. [ CantSee  i w e;
  2388.     saved_oops=oops_from;
  2389.  
  2390.     if (scope_token~=0) { scope_error = scope_token; return ASKSCOPE_PE; }
  2391.  
  2392.     wn--; w=NextWord();
  2393.     e=CANTSEE_PE;
  2394.     if (w==vague_word) e=ITGONE_PE;
  2395.     i=parent(actor);
  2396.     if (i has visited && Refers(i,w)==1) e=SCENERY_PE;
  2397.     if (etype>e) return etype;
  2398.     return e;
  2399. ];
  2400.  
  2401. ! ----------------------------------------------------------------------------
  2402. !  The MultiAdd routine adds object "o" to the multiple-object-list.
  2403. !
  2404. !  This is only allowed to hold 63 objects at most, at which point it ignores
  2405. !  any new entries (and sets a global flag so that a warning may later be
  2406. !  printed if need be).
  2407. ! ----------------------------------------------------------------------------
  2408.  
  2409. [ MultiAdd o i j;
  2410.   i=multiple_object-->0;
  2411.   if (i==63) { toomany_flag=1; rtrue; }
  2412.   for (j=1:j<=i:j++)
  2413.       if (o==multiple_object-->j) 
  2414.           rtrue;
  2415.   i++;
  2416.   multiple_object-->i = o;
  2417.   multiple_object-->0 = i;
  2418. ];
  2419.  
  2420. ! ----------------------------------------------------------------------------
  2421. !  The MultiSub routine deletes object "o" from the multiple-object-list.
  2422. !
  2423. !  It returns 0 if the object was there in the first place, and 9 (because
  2424. !  this is the appropriate error number in Parser()) if it wasn't.
  2425. ! ----------------------------------------------------------------------------
  2426.  
  2427. [ MultiSub o i j k et;
  2428.   i=multiple_object-->0; et=0;
  2429.   for (j=1:j<=i:j++)
  2430.       if (o==multiple_object-->j)
  2431.       {   for (k=j:k<=i:k++)
  2432.               multiple_object-->k = multiple_object-->(k+1);
  2433.           multiple_object-->0 = --i;
  2434.           return et;
  2435.       }
  2436.   et=9; return et;
  2437. ];
  2438.  
  2439. ! ----------------------------------------------------------------------------
  2440. !  The MultiFilter routine goes through the multiple-object-list and throws
  2441. !  out anything without the given attribute "attr" set.
  2442. ! ----------------------------------------------------------------------------
  2443.  
  2444. [ MultiFilter attr  i j o;
  2445.   .MFiltl;
  2446.   i=multiple_object-->0;
  2447.   for (j=1:j<=i:j++)
  2448.   {   o=multiple_object-->j;
  2449.       if (o hasnt attr) { MultiSub(o); jump Mfiltl; }
  2450.   }
  2451. ];
  2452.  
  2453. ! ----------------------------------------------------------------------------
  2454. !  The UserFilter routine consults the user's filter (or checks on attribute)
  2455. !  to see what already-accepted nouns are acceptable
  2456. ! ----------------------------------------------------------------------------
  2457.  
  2458. [ UserFilter obj;
  2459.  
  2460.   if (token_was>=128)
  2461.   {   if (obj has (token_was-128)) rtrue;
  2462.       rfalse;
  2463.   }
  2464.   noun=obj;
  2465.   return (indirect(#preactions_table-->(token_was-16)));
  2466. ];
  2467.  
  2468. ! ----------------------------------------------------------------------------
  2469. !  MoveWord copies word at2 from parse buffer b2 to word at1 in "parse"
  2470. !  (the main parse buffer)
  2471. ! ----------------------------------------------------------------------------
  2472.  
  2473. [ MoveWord at1 b2 at2 x y;
  2474.   x=at1*2-1; y=at2*2-1;
  2475.   parse-->x++ = b2-->y++;
  2476.   parse-->x = b2-->y;
  2477. ];
  2478.  
  2479. ! ----------------------------------------------------------------------------
  2480. !  SearchScope  domain1 domain2 context
  2481. !
  2482. !  Works out what objects are in scope (possibly asking an outside routine),
  2483. !  but does not look at anything the player has typed.
  2484. ! ----------------------------------------------------------------------------
  2485.  
  2486. [ SearchScope domain1 domain2 context i;
  2487.  
  2488.   i=0;
  2489. !  Everything is in scope to the debugging commands
  2490.  
  2491. #ifdef DEBUG;
  2492.   if (scope_reason==PARSING_REASON
  2493.       && (verb_word == 'purloin' or 'tree' or 'abstract'
  2494.           || verb_word == 'gonear' or 'scope'))
  2495.   {   for (i=selfobj+1:i<=top_object:i++) PlaceInScope(i);
  2496.       rtrue;
  2497.   }
  2498. #endif;
  2499.  
  2500. !  First, a scope token gets priority here:
  2501.  
  2502.   if (scope_token ~= 0)
  2503.   {   scope_stage=2;
  2504.       if (indirect(scope_token)~=0) rtrue;
  2505.   }
  2506.  
  2507. !  Next, call any user-supplied routine adding things to the scope,
  2508. !  which may circumvent the usual routines altogether if they return true:
  2509.  
  2510.   if (actor==domain1 or domain2 && InScope(actor)~=0) rtrue;
  2511.  
  2512. !  Pick up everything in the location except the actor's possessions;
  2513. !  then go through those.  (This ensures the actor's possessions are in
  2514. !  scope even in Darkness.)
  2515.  
  2516.   if (context==5 && advance_warning ~= -1)
  2517.   {   if (IsSeeThrough(advance_warning)==1)
  2518.           ScopeWithin(advance_warning, 0, context);
  2519.   }
  2520.   else
  2521.   {   ScopeWithin(domain1, domain2, context);
  2522.       ScopeWithin(domain2,0,context);
  2523.   }
  2524. ];
  2525.  
  2526. ! ----------------------------------------------------------------------------
  2527. !  IsSeeThrough is used at various places: roughly speaking, it determines
  2528. !  whether o being in scope means that the contents of o are in scope.
  2529. ! ----------------------------------------------------------------------------
  2530.  
  2531. [ IsSeeThrough o;
  2532.   if (o has supporter
  2533.       || (o has transparent)
  2534.       || (o has container && o has open))
  2535.       rtrue;
  2536.   rfalse;
  2537. ];
  2538.  
  2539. ! ----------------------------------------------------------------------------
  2540. !  PlaceInScope is provided for routines outside the library, and is not
  2541. !  called within the parser (except for debugging purposes).
  2542. ! ----------------------------------------------------------------------------
  2543.  
  2544. [ PlaceInScope thing;
  2545.    if (scope_reason~=PARSING_REASON or TALKING_REASON)
  2546.    {   DoScopeAction(thing); rtrue; }
  2547.    wn=match_from; TryGivenObject(thing); placed_in_flag=1;
  2548. ];
  2549.  
  2550. ! ----------------------------------------------------------------------------
  2551. !  DoScopeAction
  2552. ! ----------------------------------------------------------------------------
  2553.  
  2554. [ DoScopeAction thing s p1;
  2555.   s = scope_reason; p1=parser_one;
  2556.   if (parser_trace>=5)
  2557.   {   print "[DSA on ", (the) thing, " with reason = ", scope_reason,
  2558.       " p1 = ", parser_one, " p2 = ", parser_two, "]^";
  2559.   }
  2560.   switch(scope_reason)
  2561.   {   REACT_BEFORE_REASON:
  2562.           if (thing.react_before==0 or NULL) return;
  2563.           if (parser_trace>=2)
  2564.           {   print "[Considering react_before for ", (the) thing, "]^"; }
  2565.           if (parser_one==0) parser_one = RunRoutines(thing,react_before);
  2566.       REACT_AFTER_REASON:
  2567.           if (thing.react_after==0 or NULL) return;
  2568.           if (parser_trace>=2)
  2569.           {   print "[Considering react_after for ", (the) thing, "]^"; }
  2570.           if (parser_one==0) parser_one = RunRoutines(thing,react_after);
  2571.       EACH_TURN_REASON:
  2572.           if (thing.&each_turn==0) return;
  2573.           if (parser_trace>=2)
  2574.           {   print "[Considering each_turn for ", (the) thing, "]^"; }
  2575.           PrintOrRun(thing, each_turn);
  2576.       TESTSCOPE_REASON:
  2577.           if (thing==parser_one) parser_two = 1;
  2578.       LOOPOVERSCOPE_REASON:
  2579.           indirect(parser_one,thing); parser_one=p1;
  2580.   }
  2581.   scope_reason = s;
  2582. ];
  2583.  
  2584. ! ----------------------------------------------------------------------------
  2585. !  ScopeWithin looks for objects in the domain which make textual sense
  2586. !  and puts them in the match list.  (However, it does not recurse through
  2587. !  the second argument.)
  2588. ! ----------------------------------------------------------------------------
  2589.  
  2590. [ ScopeWithin domain nosearch context;
  2591.  
  2592.    if (domain==0) rtrue;
  2593.  
  2594. !  multiexcept doesn't have second parameter in scope
  2595.    if (context==4 && domain==advance_warning) rtrue;
  2596.  
  2597. !  Special rule: the directions (interpreted as the 12 walls of a room) are
  2598. !  always in context.  (So, e.g., "examine north wall" is always legal.)
  2599. !  (Unless we're parsing something like "all", because it would just slow
  2600. !  things down then, or unless the context is "creature".)
  2601.  
  2602.    if (indef_mode==0 && domain==actors_location
  2603.        && scope_reason==PARSING_REASON && context~=6) ScopeWithin(compass);
  2604.  
  2605. !  Look through the objects in the domain
  2606.  
  2607.    objectloop (domain in domain) ScopeWithin_O(domain, nosearch, context);
  2608. ];
  2609.  
  2610. [ ScopeWithin_O domain nosearch context i ad n;
  2611.  
  2612. !  If the scope reason is unusual, don't parse.
  2613.  
  2614.       if (scope_reason~=PARSING_REASON or TALKING_REASON)
  2615.       {   DoScopeAction(domain); jump DontAccept; }
  2616.  
  2617. !  If we're beyond the end of the user's typing, accept everything
  2618. !  (NounDomain will sort things out)
  2619.  
  2620.       if (match_from > num_words)
  2621.       {   i=parser_trace; parser_trace=0;
  2622.           if (i>=5) { print "     Out of text: matching "; DefArt(domain);
  2623.                       new_line; }
  2624.           MakeMatch(domain,1);
  2625.           parser_trace=i; jump DontAccept;
  2626.       }
  2627.  
  2628. !  "it" or "them" matches to the it-object only.  (Note that (1) this means
  2629. !  that "it" will only be understood if the object in question is still
  2630. !  in context, and (2) only one match can ever be made in this case.)
  2631.  
  2632.       wn=match_from;
  2633.       i=NounWord();
  2634.       if (i==1 && itobj==domain)   MakeMatch(itobj,1);
  2635.       if (i==2 && himobj==domain)  MakeMatch(himobj,1);
  2636.       if (i==3 && herobj==domain)  MakeMatch(herobj,1);
  2637.       if (i==4 && player==domain)  MakeMatch(player,1);
  2638.  
  2639. !  Construing the current word as the start of a noun, can it refer to the
  2640. !  object?
  2641.  
  2642.       wn--; TryGivenObject(domain);
  2643.  
  2644.       .DontAccept;
  2645.  
  2646. !  Shall we consider the possessions of the current object, as well?
  2647. !  Only if it's a container (so, for instance, if a dwarf carries a
  2648. !  sword, then "drop sword" will not be accepted, but "dwarf, drop sword"
  2649. !  will).
  2650. !  Also, only if there are such possessions.
  2651. !
  2652. !  Notice that the parser can see "into" anything flagged as
  2653. !  transparent - such as a dwarf whose sword you can get at.
  2654.  
  2655.       if (child(domain)~=0 && domain ~= nosearch && IsSeeThrough(domain)==1)
  2656.           ScopeWithin(domain,0,context);
  2657.  
  2658. !  Drag any extras into context
  2659.  
  2660.    ad = domain.&add_to_scope;
  2661.    if (ad ~= 0)
  2662.    {   if (UnsignedCompare(ad-->0,top_object) > 0)
  2663.        {   ats_flag = 2+context;
  2664.            RunRoutines(domain, add_to_scope);
  2665.            ats_flag = 0;
  2666.        }
  2667.        else
  2668.        {   n=domain.#add_to_scope;
  2669.            for (i=0:(2*i)<n:i++)
  2670.                ScopeWithin_O(ad-->i,0,context);
  2671.        }
  2672.    }
  2673. ];
  2674.  
  2675. [ AddToScope obj;
  2676.    if (ats_flag>=2)
  2677.        ScopeWithin_O(obj,0,ats_flag-2);
  2678.    if (ats_flag==1)
  2679.    {   if  (HasLightSource(obj)==1) ats_hls = 1;
  2680.    }
  2681. ];
  2682.  
  2683. ! ----------------------------------------------------------------------------
  2684. !  MakeMatch looks at how good a match is.  If it's the best so far, then
  2685. !  wipe out all the previous matches and start a new list with this one.
  2686. !  If it's only as good as the best so far, add it to the list.
  2687. !  If it's worse, ignore it altogether.
  2688. !
  2689. !  The idea is that "red panic button" is better than "red button" or "panic".
  2690. !
  2691. !  number_matched (the number of words matched) is set to the current level
  2692. !  of quality.
  2693. !
  2694. !  We never match anything twice, and keep at most 64 equally good items.
  2695. ! ----------------------------------------------------------------------------
  2696.  
  2697. [ MakeMatch obj quality i;
  2698.    if (parser_trace>=5) print "    Match with quality ",quality,"^";
  2699.    if (token_was~=0 && UserFilter(obj)==0)
  2700.    {   if (parser_trace>=5) print "    Match filtered out^";
  2701.        rtrue;
  2702.    }
  2703.    if (quality < match_length) rtrue;
  2704.    if (quality > match_length) { match_length=quality; number_matched=0; }
  2705.    else
  2706.    {   if (2*number_matched>=MATCH_LIST_SIZE) rtrue;
  2707.        for (i=0:i<number_matched:i++)
  2708.            if (match_list-->i==obj) rtrue;
  2709.    }
  2710.    match_list-->number_matched++ = obj;
  2711.    if (parser_trace>=5) print "    Match added to list^";
  2712. ];
  2713.  
  2714. ! ----------------------------------------------------------------------------
  2715. !  TryGivenObject tries to match as many words as possible in what has been
  2716. !  typed to the given object, obj.  If it manages any words matched at all,
  2717. !  it calls MakeMatch to say so.  There is no return value.
  2718. ! ----------------------------------------------------------------------------
  2719.  
  2720. [ TryGivenObject obj threshold k w j;
  2721.  
  2722.    if (parser_trace>=5)
  2723.    {   print "    Trying "; DefArt(obj);
  2724.        print " (", obj, ") at word ", wn, "^";
  2725.    }
  2726.  
  2727. !  If input has run out and we're in indefinite mode, then always match,
  2728. !  with only quality 0 (this saves time).
  2729.  
  2730.    if (indef_mode ~=0 && wn > parse->1) { MakeMatch(obj,0); rfalse; }
  2731.  
  2732. !  Ask the object to parse itself if necessary, sitting up and taking notice
  2733. !  if it says the plural was used:
  2734.  
  2735.    if (obj.parse_name~=0)
  2736.    {   parser_action=-1; j=wn;
  2737.        k=RunRoutines(obj,parse_name);
  2738.        if (k>0)
  2739.        {   wn=j+k;
  2740.            .MMbyPN;
  2741.            if (parser_action == ##PluralFound)
  2742.            {   if (allow_plurals == 0) jump NoWordsMatch;
  2743.                if (indef_mode==0)
  2744.                {   indef_mode=1; indef_type=0; indef_wanted=0; }
  2745.                indef_type=indef_type | PLURAL_BIT;
  2746.                if (indef_wanted==0) indef_wanted=100;
  2747.            }
  2748.            MakeMatch(obj,k); rfalse;
  2749.        }
  2750.        if (k==0) jump NoWordsMatch;
  2751.    }
  2752.  
  2753. !  The default algorithm is simply to count up how many words pass the
  2754. !  Refers test:
  2755.  
  2756.    w = NounWord();
  2757.    if ((w==1 && obj==itobj)
  2758.        || (w==2 && obj==himobj)
  2759.        || (w==3 && obj==herobj)
  2760.        || (w==4 && obj==player)) { MakeMatch(obj,1); rfalse; }
  2761.   
  2762.    j=--wn;
  2763.    threshold = ParseNoun(obj);
  2764.    if (threshold>=0 && parser_trace>=5)
  2765.        print "    ParseNoun returned ", threshold, "^";
  2766.    if (threshold<0) wn++;
  2767.    if (threshold>0) { k=threshold; jump MMbyPN; }
  2768.  
  2769.    if (threshold==0 || Refers(obj,w)==0)
  2770.    {   .NoWordsMatch;
  2771.        if (indef_mode~=0) MakeMatch(obj,0);
  2772.        rfalse;
  2773.    }
  2774.  
  2775.    if (threshold<0)
  2776.    {   threshold=1; while (0~=Refers(obj,NextWord())) threshold++;
  2777.    }
  2778.  
  2779.    MakeMatch(obj,threshold);
  2780.  
  2781.    if (parser_trace>=5) print "    Matched^";
  2782. ];
  2783.  
  2784. ! ----------------------------------------------------------------------------
  2785. !  Refers works out whether the word with dictionary address wd can refer to
  2786. !  the object obj, by seeing if wd is listed in the "names" property of obj.
  2787. ! ----------------------------------------------------------------------------
  2788.  
  2789. [ Refers obj wd   k l m;
  2790.     if (obj==0) rfalse;
  2791.     k=obj.&1; l=(obj.#1)/2-1;
  2792.     for (m=0:m<=l:m++)
  2793.         if (wd==k-->m) rtrue;
  2794.     rfalse;
  2795. ];
  2796.  
  2797. ! ----------------------------------------------------------------------------
  2798. !  NounWord (which takes no arguments) returns:
  2799. !
  2800. !   1  if the next word is "it" or "them",
  2801. !   2  if the next word is "him",
  2802. !   3  if the next word is "her",
  2803. !   4  if "me", "myself", "self"
  2804. !   0  if the next word is unrecognised or does not carry the "noun" bit in
  2805. !      its dictionary entry,
  2806. !   or the address in the dictionary if it is a recognised noun.
  2807. !
  2808. !  The "current word" marker moves on one.
  2809. ! ----------------------------------------------------------------------------
  2810.  
  2811. [ NounWord i;
  2812.    i=NextWord();
  2813.    if (i=='it' or 'them') return 1;
  2814.    if (i=='him') return 2;
  2815.    if (i=='her') return 3;
  2816.    if (i=='me' or 'myself' or 'self') return 4;
  2817.    if (i==0) rfalse;
  2818.    if ((i->#dict_par1)&128 == 0) rfalse;
  2819.    return i;
  2820. ];
  2821.  
  2822. ! ----------------------------------------------------------------------------
  2823. !  AdjectiveWord (which takes no arguments) returns:
  2824. !
  2825. !   0  if the next word is listed in the dictionary as possibly an adjective,
  2826. !   or its adjective number if it is.
  2827. !
  2828. !  The "current word" marker moves on one.
  2829. ! ----------------------------------------------------------------------------
  2830.  
  2831. [ AdjectiveWord i j;
  2832.    j=NextWord();
  2833.    if (j==0) rfalse;
  2834.    i=j->#dict_par1;
  2835.    if (i&8 == 0) rfalse;
  2836.    return(j->#dict_par3);
  2837. ];
  2838.  
  2839. ! ----------------------------------------------------------------------------
  2840. !  AdjectiveAddress works out the address in the dictionary of the word
  2841. !  corresponding to the given adjective number.
  2842. !
  2843. !  It should never produce the given error (which would mean that Inform
  2844. !  had set up the adjectives table incorrectly).
  2845. ! ----------------------------------------------------------------------------
  2846.  
  2847. [ AdjectiveAddress number m;
  2848.    m=#adjectives_table;
  2849.    for (::)
  2850.    {   if (number==m-->1) return m-->0;
  2851.        m=m+4;
  2852.    }
  2853.    m=#adjectives_table;
  2854.    print "<Adjective not found>";
  2855.    return m;
  2856. ];
  2857.  
  2858. ! ----------------------------------------------------------------------------
  2859. !  NextWord (which takes no arguments) returns:
  2860. !
  2861. !  0            if the next word is unrecognised,
  2862. !  comma_word   if it is a comma character
  2863. !               (which is treated oddly by the Z-machine, hence the code)
  2864. !  or the dictionary address if it is recognised.
  2865. !  The "current word" marker is moved on.
  2866. !
  2867. !  NextWordStopped does the same, but returns -1 when input has run out
  2868. ! ----------------------------------------------------------------------------
  2869.  
  2870. [ NextWord i j k;
  2871.    if (wn > parse->1) { wn++; rfalse; }
  2872.    i=wn*2-1; wn++;
  2873.    j=parse-->i;
  2874.    if (j==0)
  2875.    {   k=wn*4-3; i=buffer->(parse->k);
  2876.        if (i==',') j=comma_word;
  2877.        if (i=='.') j='then';
  2878.    }
  2879.    return j;
  2880. ];   
  2881.  
  2882. [ NextWordStopped;
  2883.    if (wn > parse->1) { wn++; return -1; }
  2884.    return NextWord();
  2885. ];
  2886.  
  2887. [ WordAddress wordnum;
  2888.    return buffer + parse->(wordnum*4+1);
  2889. ];
  2890.  
  2891. [ WordLength wordnum;
  2892.    return parse->(wordnum*4);
  2893. ];
  2894.  
  2895. ! ----------------------------------------------------------------------------
  2896. !  TryNumber is the only routine which really does any character-level
  2897. !  parsing, since that's normally left to the Z-machine.
  2898. !  It takes word number "wordnum" and tries to parse it as an (unsigned)
  2899. !  decimal number, returning
  2900. !
  2901. !  -1000                if it is not a number
  2902. !  the number           if it has between 1 and 4 digits
  2903. !  10000                if it has 5 or more digits.
  2904. !
  2905. !  (The danger of allowing 5 digits is that Z-machine integers are only
  2906. !  16 bits long, and anyway this isn't meant to be perfect.)
  2907. !
  2908. !  Using NumberWord, it also catches "one" up to "twenty".
  2909. !
  2910. !  Note that a game can provide a ParseNumber routine which takes priority,
  2911. !  to enable parsing of odder numbers ("x45y12", say).
  2912. ! ----------------------------------------------------------------------------
  2913.  
  2914. [ TryNumber wordnum   i j c num len mul tot d digit;
  2915.  
  2916.    i=wn; wn=wordnum; j=NextWord(); wn=i;
  2917.    j=NumberWord(j); if (j>=1) return j;
  2918.  
  2919.    i=wordnum*4+1; j=parse->i; num=j+buffer; len=parse->(i-1);
  2920.  
  2921.    tot=ParseNumber(num, len);  if (tot~=0) return tot;
  2922.  
  2923.    if (len>=4) mul=1000;
  2924.    if (len==3) mul=100;
  2925.    if (len==2) mul=10;
  2926.    if (len==1) mul=1;
  2927.  
  2928.    tot=0; c=0; len=len-1;
  2929.  
  2930.    for (c=0:c<=len:c++)
  2931.    {   digit=num->c;
  2932.        if (digit=='0') { d=0; jump digok; }
  2933.        if (digit=='1') { d=1; jump digok; }
  2934.        if (digit=='2') { d=2; jump digok; }
  2935.        if (digit=='3') { d=3; jump digok; }
  2936.        if (digit=='4') { d=4; jump digok; }
  2937.        if (digit=='5') { d=5; jump digok; }
  2938.        if (digit=='6') { d=6; jump digok; }
  2939.        if (digit=='7') { d=7; jump digok; }
  2940.        if (digit=='8') { d=8; jump digok; }
  2941.        if (digit=='9') { d=9; jump digok; }
  2942.        return -1000;
  2943.      .digok;
  2944.        tot=tot+mul*d; mul=mul/10;
  2945.    }
  2946.    if (len>3) tot=10000;
  2947.    return tot;
  2948. ];
  2949.  
  2950. ! ----------------------------------------------------------------------------
  2951. !  ResetVagueWords does, assuming that i was the object last referred to
  2952. ! ----------------------------------------------------------------------------
  2953.  
  2954. [ ResetVagueWords i;
  2955.    if (i has animate && i~=player)
  2956.    {   if (GetGender(i)==1) himobj=i;
  2957.        else herobj=i;
  2958.    }
  2959.    else itobj=i;
  2960. ];
  2961.  
  2962. ! ----------------------------------------------------------------------------
  2963. !  GetGender returns 0 if the given animate object is female, and 1 if male
  2964. !  (not all games will want such a simple decision function!)
  2965. ! ----------------------------------------------------------------------------
  2966.  
  2967. [ GetGender person;
  2968.    if (person hasnt female) rtrue;
  2969.    rfalse;
  2970. ];
  2971.  
  2972. ! ----------------------------------------------------------------------------
  2973. !  For copying buffers
  2974. ! ----------------------------------------------------------------------------
  2975.  
  2976. [ Copy bto bfrom i size;
  2977.    size=bto->0;
  2978.    for (i=1:i<=size:i++) bto->i=bfrom->i;
  2979. ];
  2980.  
  2981. ! ----------------------------------------------------------------------------
  2982. !  Useful routine: unsigned comparison (for addresses in Z-machine)
  2983. !    Returns 1 if x>y, 0 if x=y, -1 if x<y
  2984. !  ZRegion(addr) returns 1 if object num, 2 if in code area, 3 if in strings
  2985. ! ----------------------------------------------------------------------------
  2986.  
  2987. [ UnsignedCompare x y u v;
  2988.   if (x==y) return 0;
  2989.   if (x<0 && y>=0) return 1;
  2990.   if (x>=0 && y<0) return -1;
  2991.   u = x&$7fff; v= y&$7fff;
  2992.   if (u>v) return 1;
  2993.   return -1;
  2994. ];
  2995.  
  2996. [ ZRegion addr;
  2997.   if (addr==0) return 0;
  2998.   if (addr>=1 && addr<=top_object) return 1;
  2999.   if (UnsignedCompare(addr, #strings_offset)>=0) return 3;
  3000.   if (UnsignedCompare(addr, #code_offset)>=0) return 2;
  3001.   return 0;
  3002. ];
  3003.  
  3004. [ PrintOrRun obj prop flag a;
  3005.   if (obj.#prop > 2) return RunRoutines(obj,prop);
  3006.   if (obj.prop==NULL) rfalse;
  3007.   a=ZRegion(obj.prop);
  3008.   if (a==0 or 1) "** Expected string/routine as prop value! **";
  3009.   if (a==3) { print_paddr obj.prop; if (flag==0) new_line; rtrue; }
  3010.   return RunRoutines(obj,prop);
  3011. ];
  3012.  
  3013. [ ValueOrRun obj prop a;
  3014.   a=ZRegion(obj.prop);
  3015.   if (a==2) return RunRoutines(obj,prop);
  3016.   return obj.prop;
  3017. ];
  3018.  
  3019. [ RunRoutines obj prop i j k l m ssv;
  3020.  
  3021.    if (obj==thedark && prop~=initial) obj=real_location;
  3022.    if (obj.prop==NULL or 0) rfalse;
  3023.  
  3024. #IFDEF DEBUG;
  3025.  if (debug_flag & 1 ~= 0 && prop~=short_name)
  3026.  { print "[Running ";
  3027.    if (prop==before)   { print "before";   jump DebugPrt; }
  3028.    if (prop==after)    { print "after";    jump DebugPrt; }
  3029.    if (prop==life)     { print "life";     jump DebugPrt; }
  3030.    if (prop==each_turn) { print "each_turn"; jump DebugPrt; }
  3031.    if (prop==describe) { print "describe"; jump DebugPrt; }
  3032.    if (prop==initial)  { print "initial";  jump DebugPrt; }
  3033.    if (prop==n_to)     { print "n_to/door_to";   jump DebugPrt; }
  3034.    if (prop==s_to)     { print "s_to/when_closed";   jump DebugPrt; }
  3035.    if (prop==e_to)     { print "e_to/with_key";   jump DebugPrt; }
  3036.    if (prop==w_to)     { print "w_to/door_dir";   jump DebugPrt; }
  3037.    if (prop==ne_to)    { print "ne_to/react_before";   jump DebugPrt; }
  3038.    if (prop==nw_to)    { print "nw_to/react_after";   jump DebugPrt; }
  3039.    if (prop==se_to)    { print "se_to/add_to_scope";   jump DebugPrt; }
  3040.    if (prop==sw_to)    { print "sw_to/list_together";   jump DebugPrt; }
  3041.    if (prop==u_to)     { print "u_to/invent";   jump DebugPrt; }
  3042.    if (prop==d_to)     { print "d_to/plural";   jump DebugPrt; }
  3043.    if (prop==in_to)    { print "in_to/grammar";   jump DebugPrt; }
  3044.    if (prop==out_to)   { print "out_to/orders";   jump DebugPrt; }
  3045.    if (prop==time_out) { print "daemon/time_out";   jump DebugPrt; }
  3046.    if (prop==parse_name) { print "parse_name";   jump DebugPrt; }
  3047.    print "property ",prop;
  3048.    .DebugPrt;
  3049.    print " for ", object obj,"]^";
  3050.  }
  3051. #ENDIF;
  3052.  
  3053.    j=obj.∝ k=obj.#prop; m=self; self=obj;
  3054.    ssv=sw__var;
  3055.    if (prop==life) sw__var=reason_code;
  3056.    else sw__var=action;
  3057.    if (prop~=life or orders)
  3058.    {   noun=inp1; second=inp2;
  3059.    }
  3060.    for (i=0:i<k/2:i++)
  3061.    {   if (j-->i == NULL) { self=m; sw__var=ssv; rfalse; }
  3062.        l=ZRegion(j-->i);
  3063.        if (l==2)
  3064.        {   l=indirect(j-->i);
  3065.            if (l~=0) { self=m; sw__var=ssv; return l; }
  3066.        }
  3067.        else
  3068.        {   if (l==3) { print_paddr j-->i; new_line; }
  3069.            else print "** Entry in property list not routine or string **^";
  3070.        }
  3071.    }
  3072.    self=m; sw__var=ssv;
  3073.    rfalse;
  3074. ];
  3075.  
  3076. ! ----------------------------------------------------------------------------
  3077. !  End of the parser proper: the remaining routines are its front end.
  3078. ! ----------------------------------------------------------------------------
  3079.  
  3080. [ DisplayStatus;
  3081.    if (the_time==NULL)
  3082.    {   sline1=score; sline2=turns; }
  3083.    else
  3084.    {   sline1=the_time/60; sline2=the_time%60; }
  3085. ];
  3086.  
  3087. [ SetTime t s;
  3088.    the_time=t; time_rate=s; time_step=0;
  3089.    if (s<0) time_step=0-s;
  3090. ];
  3091.  
  3092. [ NotifyTheScore i;
  3093.   print "^[Your score has just gone ";
  3094.   if (last_score > score) { i=last_score-score; print "down"; }
  3095.   else { i=score-last_score; print "up"; }
  3096.   print " by "; EnglishNumber(i); print " point";
  3097.   if (i>1) print "s"; print ".]^";
  3098. ];
  3099.  
  3100. [ PlayTheGame i j k l;
  3101.  
  3102.    standard_interpreter = $32-->0;
  3103.  
  3104.    player = selfobj;
  3105.    top_object = #largest_object-255;
  3106.    selfobj.capacity = MAX_CARRIED;
  3107.  
  3108.    j=Initialise();
  3109.    last_score = score;
  3110.    move player to location;
  3111.    while (parent(location)~=0) location=parent(location);
  3112.    objectloop (i in player) give i moved ~concealed;
  3113.  
  3114.    if (j~=2) Banner();
  3115.  
  3116.    lightflag=OffersLight(parent(player));
  3117.    if (lightflag==0) { real_location=location; location=thedark; }
  3118.  
  3119.    <Look>;
  3120.  
  3121.    for (i=1:i<=100:i++) j=random(i);
  3122.  
  3123.    while (deadflag==0)
  3124.    {   if (score ~= last_score)
  3125.        {   if (notify_mode==1) NotifyTheScore();
  3126.            last_score=score;
  3127.        }
  3128.        .Error;
  3129.        inp1=0; inp2=0; action=0; meta=0;
  3130.        inputobjs-->0 = 0; inputobjs-->1 = 0;
  3131.        inputobjs-->2 = 0; inputobjs-->3 = 0;
  3132.        Parser(inputobjs);
  3133.  
  3134.        onotheld_mode=notheld_mode; notheld_mode=0;
  3135.  
  3136.        if (actor~=player)
  3137.        {   if (meta==1)
  3138.            {   print "Only you can do that!^"; jump timeslice; }
  3139.            action=inputobjs-->0;
  3140.            inp1=inputobjs-->2;
  3141.            inp2=inputobjs-->3;
  3142.            if (action==##GiveR)
  3143.            {   inp2=inputobjs-->2;
  3144.                inp1=inputobjs-->3; action=##Give;
  3145.            }
  3146.            if (action==##ShowR)
  3147.            {   inp2=inputobjs-->2;
  3148.                inp1=inputobjs-->3; action=##Show;
  3149.            }
  3150.            noun=inp1; second=inp2;
  3151.            if (inputobjs-->1 == 0) noun=0;
  3152.            if (inputobjs-->1 < 2)  second=0;
  3153.            ProcessPrepare();
  3154.            if (action==##Tell && inp1==player)
  3155.            {   noun=actor; actor=player; AskSub(); jump timeslice;
  3156.            }
  3157.            if (RunLife(actor,##Order)==0)
  3158.            {   if (action==##NotUnderstood)
  3159.                {   second=actor; actor=player; AnswerSub(); jump timeslice;
  3160.                }
  3161.                L__M(##Order,1,actor);
  3162.            }
  3163.            jump timeslice;
  3164.        }
  3165.  
  3166.        if (toomany_flag==1)
  3167.        {   toomany_flag=0; L__M(##Miscellany,1); }
  3168.        if (action==0)
  3169.        {   action=inputobjs-->0;
  3170.            i=inputobjs-->1;
  3171.            inp1=inputobjs-->2;
  3172.            inp2=inputobjs-->3;
  3173.        }
  3174.        else i=2;
  3175.  
  3176.        if (i==0) { inp1=0; inp2=0; }
  3177.        if (i==1) { inp2=0; }
  3178.  
  3179.        multiflag=0;
  3180.        if (i==0) Process(0,0,action);
  3181.        else
  3182.        {   if (inp1~=0) Process(inp1,inp2,action);
  3183.            else
  3184.            {   multiflag=1;
  3185.                j=multiple_object-->0;
  3186.                if (j==0) { L__M(##Miscellany,2); jump Error; }
  3187.                i=location;
  3188.                for (k=1:k<=j:k++)
  3189.                {   if (deadflag~=0) break;
  3190.                    if (location~=i)
  3191.                    {   print "(Since something dramatic has happened, \
  3192.                               your list of commands has been cut short.)^";
  3193.                        break;
  3194.                    }
  3195.                    l=multiple_object-->k; ResetVagueWords(l);
  3196.                    PrintShortName(l); print ": "; Process(l,inp2,action);
  3197.                }
  3198.            }
  3199.        }
  3200.  
  3201.        .timeslice;
  3202.        if (notheld_mode==1) meta=1;
  3203.        if (deadflag==0 && meta==0) EndTurnSequence();
  3204.    }
  3205.  
  3206.    if (deadflag~=2) AfterLife();
  3207.    if (deadflag==0) jump Error;
  3208.  
  3209.    print "^^    ";
  3210.    #IFV5; style bold; #ENDIF;
  3211.    print "***";
  3212.    if (deadflag==1) L__M(##Miscellany,3);
  3213.    if (deadflag==2) L__M(##Miscellany,4);
  3214.    if (deadflag>2)  { print " "; DeathMessage(); print " "; }
  3215.    print "***";
  3216.    #IFV5; style roman; #ENDIF;
  3217.    print "^^^";
  3218.    ScoreSub();
  3219.    DisplayStatus();
  3220.  
  3221.    .RRQPL;
  3222.    L__M(##Miscellany,5);
  3223.    .RRQL;
  3224.    print "> ";
  3225.    #IFV3; read buffer parse; #ENDIF;
  3226.    temp_global=0;
  3227.    #IFV5; read buffer parse DrawStatusLine; #ENDIF;
  3228.    i=parse-->1;
  3229.    if (i=='quit' or #n$q) quit;
  3230.    if (i=='restart')      @restart;
  3231.    if (i=='restore')      { RestoreSub(); jump RRQPL; }
  3232.    if (i=='fullscore' or 'full' && TASKS_PROVIDED==0)
  3233.    {   new_line; FullScoreSub(); jump RRQPL; }
  3234.    if (deadflag==2 && i=='amusing' && AMUSING_PROVIDED==0)
  3235.    {   new_line; Amusing(); jump RRQPL; }
  3236. #IFV5;
  3237.    if (i=='undo')
  3238.    {   if (undo_flag==0)
  3239.        {   L__M(##Miscellany,6);
  3240.            jump RRQPL;
  3241.        }
  3242.        if (undo_flag==1) jump UndoFailed2;
  3243.        @restore_undo i;
  3244.        if (i==0)
  3245.        {   .UndoFailed2; L__M(##Miscellany,7);
  3246.        }
  3247.        jump RRQPL;
  3248.    }
  3249. #ENDIF;
  3250.    L__M(##Miscellany,8);
  3251.    jump RRQL;
  3252. ];
  3253.  
  3254. #ifdef DEBUG;
  3255. Array debug_anames table
  3256. [;##Inv "Inv";
  3257.   ##InvTall "InvTall";
  3258.   ##InvWide "InvWide";
  3259.   ##Take "Take";
  3260.   ##Drop "Drop";
  3261.   ##Remove "Remove";
  3262.   ##PutOn "PutOn";
  3263.   ##Insert "Insert";
  3264.   ##Transfer "Transfer";
  3265.   ##Empty "Empty";
  3266.   ##Enter "Enter";
  3267.   ##Exit "Exit";
  3268.   ##GetOff "GetOff";
  3269.   ##Go "Go";
  3270.   ##GoIn "GoIn";
  3271.   ##Look "Look";
  3272.   ##Examine "Examine";
  3273.   ##Search "Search";
  3274.   ##Give "Give";
  3275.   ##Show "Show";
  3276.   ##Unlock "Unlock";
  3277.   ##Lock "Lock";
  3278.   ##SwitchOn "SwitchOn";
  3279.   ##SwitchOff "SwitchOff";
  3280.   ##Open "Open";
  3281.   ##Close "Close";
  3282.   ##Disrobe "Disrobe";
  3283.   ##Wear "Wear";
  3284.   ##Eat "Eat";
  3285.   ##Yes "Yes";
  3286.   ##No "No";
  3287.   ##Burn "Burn";
  3288.   ##Pray "Pray";
  3289.   ##Wake "Wake";
  3290.   ##WakeOther "WakeOther";
  3291.   ##Consult "Consult";
  3292.   ##Kiss "Kiss";
  3293.   ##Think "Think";
  3294.   ##Smell "Smell";
  3295.   ##Listen "Listen";
  3296.   ##Taste "Taste";
  3297.   ##Touch "Touch";
  3298.   ##Dig "Dig";
  3299.   ##Cut "Cut";
  3300.   ##Jump "Jump";
  3301.   ##JumpOver "JumpOver";
  3302.   ##Tie "Tie";
  3303.   ##Drink "Drink";
  3304.   ##Fill "Fill";
  3305.   ##Sorry "Sorry";
  3306.   ##Strong "Strong";
  3307.   ##Mild "Mild";
  3308.   ##Attack "Attack";
  3309.   ##Swim "Swim";
  3310.   ##Swing "Swing";
  3311.   ##Blow "Blow";
  3312.   ##Rub "Rub";
  3313.   ##Set "Set";
  3314.   ##SetTo "SetTo";
  3315.   ##WaveHands "WaveHands";
  3316.   ##Wave "Wave";
  3317.   ##Pull "Pull";
  3318.   ##Push "Push";
  3319.   ##PushDir "PushDir";
  3320.   ##Turn "Turn";
  3321.   ##Squeeze "Squeeze";
  3322.   ##LookUnder "LookUnder";
  3323.   ##ThrowAt "ThrowAt";
  3324.   ##Answer "Answer";
  3325.   ##Buy "Buy";
  3326.   ##Ask "Ask";
  3327.   ##Tell "Tell";
  3328.   ##AskFor "AskFor";
  3329.   ##Sing "Sing";
  3330.   ##Climb "Climb";
  3331.   ##Wait "Wait";
  3332.   ##Sleep "Sleep";
  3333.   ##Order "Order";
  3334. ];
  3335. [ DebugParameter w x n l;
  3336.   x=0-->4; x=x+(x->0)+1; l=x->0; n=(x+1)-->0; x=w-(x+3);
  3337.   print w;
  3338.   if (w>=1 && w<=top_object) print " (", (name) w, ")";
  3339.   if (x%l==0 && (x/l)<n) print " ('", (address) w, "')";
  3340. ];
  3341. [ DebugAction a i;
  3342.   for (i=1:i<=debug_anames-->0:i=i+2)
  3343.   {   if (debug_anames-->i==a)
  3344.       {   print (string) debug_anames-->(i+1); rfalse; }
  3345.   }
  3346.   print a;
  3347. ];
  3348. [ TraceAction source ar;
  3349.   if (source<2) { print "[Action "; DebugAction(action); }
  3350.   else
  3351.   {   if (ar==##Order)
  3352.       {   print "[Order to "; PrintShortName(actor); print ": ";
  3353.           DebugAction(action);
  3354.       }
  3355.       else
  3356.       {   print "[Life rule "; DebugAction(ar); }
  3357.   }
  3358.   if (noun~=0)   { print " with noun "; DebugParameter(noun);  }
  3359.   if (second~=0) { print " and second "; DebugParameter(second); }
  3360.   if (source==0) print " (from parser)";
  3361.   if (source==1) print " (from outside)";
  3362.   print "]^";
  3363. ];
  3364. #endif;
  3365.  
  3366. [ TestScope obj act a al sr x y;
  3367.   x=parser_one; y=parser_two;
  3368.   parser_one=obj; parser_two=0; a=actor; al=actors_location;
  3369.   sr=scope_reason; scope_reason=TESTSCOPE_REASON;
  3370.   if (act==0) actor=player; else actor=act;
  3371.   actors_location=actor;
  3372.   while (parent(actors_location)~=0)
  3373.       actors_location=parent(actors_location);
  3374.   SearchScope(location,player,0); scope_reason=sr; actor=a;
  3375.   actors_location=al; parser_one=x; x=parser_two; parser_two=y;
  3376.   return x;
  3377. ];
  3378.  
  3379. [ LoopOverScope routine act x y a al;
  3380.   x = parser_one; y=scope_reason; a=actor; al=actors_location;
  3381.   parser_one=routine; if (act==0) actor=player; else actor=act;
  3382.   actors_location=actor;
  3383.   while (parent(actors_location)~=0)
  3384.       actors_location=parent(actors_location);
  3385.   scope_reason=LOOPOVERSCOPE_REASON;
  3386.   SearchScope(actors_location,actor,0);
  3387.   parser_one=x; scope_reason=y; actor=a; actors_location=al;
  3388. ];
  3389.  
  3390. [ BeforeRoutines;
  3391.   if (GamePreRoutine()~=0) rtrue;
  3392.   if (RunRoutines(player,orders)~=0) rtrue;
  3393.   if (location~=0 && RunRoutines(location,before)~=0) rtrue;
  3394.   scope_reason=REACT_BEFORE_REASON; parser_one=0;
  3395.   SearchScope(location,player,0); scope_reason=PARSING_REASON;
  3396.   if (parser_one~=0) rtrue;
  3397.   if (inp1>1 && RunRoutines(inp1,before)~=0) rtrue;
  3398.   rfalse;
  3399. ];
  3400.  
  3401. [ AfterRoutines;
  3402.   scope_reason=REACT_AFTER_REASON; parser_one=0;
  3403.   SearchScope(location,player,0); scope_reason=PARSING_REASON;
  3404.   if (parser_one~=0) rtrue;
  3405.   if (location~=0 && RunRoutines(location,after)~=0) rtrue;
  3406.   if (inp1>1 && RunRoutines(inp1,after)~=0) rtrue;
  3407.   return GamePostRoutine();
  3408. ];
  3409.  
  3410. [ R_Process acti i j sn ss sa sse;
  3411.    sn=inp1; ss=inp2; sa=action; sse=self;
  3412.    inp1 = i; inp2 = j; noun=i; second=j; action=acti;
  3413.  
  3414. #IFDEF DEBUG;
  3415.    if (debug_flag & 2 ~= 0) TraceAction(1);
  3416. #ENDIF;
  3417.  
  3418.    if ((meta==1 || BeforeRoutines()==0) && action<256)
  3419.    {   indirect(#actions_table-->action);
  3420.        self=sse; inp1=sn; noun=sn; inp2=ss; second=ss; action=sa; rfalse;
  3421.    }
  3422.    self=sse; inp1=sn; noun=sn; inp2=ss; second=ss; action=sa; rtrue;
  3423. ];
  3424.  
  3425. [ ProcessPrepare;
  3426.    if (inp1==1) noun=special_number;
  3427.    if (inp2==1)
  3428.    {   if (inp1==1) second=special_number2;
  3429.        else second=special_number;
  3430.    }
  3431. ];
  3432.  
  3433. [ Process i j acti;
  3434.    inp1 = i; inp2 = j; noun=i; second=j; action=acti;
  3435.    ProcessPrepare();
  3436. #IFDEF DEBUG;
  3437.    if (debug_flag & 2 ~= 0) TraceAction(0);
  3438. #ENDIF;
  3439.    if (meta==1 || BeforeRoutines()==0)
  3440.        indirect(#actions_table-->action);
  3441. ];
  3442.  
  3443. [ RunLife a j i;
  3444. #IFDEF DEBUG;
  3445.    if (debug_flag & 2 ~= 0) TraceAction(2, j);
  3446. #ENDIF;
  3447.    if (j==##Order)
  3448.    {   i=RunRoutines(player,orders);
  3449.        if (i~=0) return i;
  3450.        i=RunRoutines(a,orders);
  3451.        if (i~=0) return i;
  3452.    }
  3453.    reason_code = j; return RunRoutines(a,life);
  3454. ];
  3455.  
  3456. [ LowKey_Menu menu_choices EntryR ChoiceR lines main_title i j;
  3457.   menu_nesting++;
  3458.  .LKRD;
  3459.   menu_item=0;
  3460.   lines=indirect(EntryR);
  3461.   main_title=item_name;
  3462.  
  3463.   print "--- "; print_paddr main_title; print " ---^^";
  3464.   if (ZRegion(menu_choices)==3) print_paddr menu_choices;
  3465.   else indirect(menu_choices);
  3466.  
  3467.  .LKML;
  3468.   print "^Type a number from 1 to ", lines,
  3469.         ", 0 to redisplay or press ENTER.^> ";
  3470.  
  3471.    #IFV3; read buffer parse; #ENDIF;
  3472.    temp_global=0;
  3473.    #IFV5; read buffer parse DrawStatusLine; #ENDIF;
  3474.    i=parse-->1;
  3475.    if (i=='quit' or #n$q || parse->1==0)
  3476.    {   menu_nesting--; if (menu_nesting>0) rfalse;
  3477.        if (deadflag==0) <<Look>>;
  3478.        rfalse;
  3479.    }
  3480.    i=TryNumber(1);
  3481.    if (i<1 || i>lines) jump LKML;
  3482.    menu_item=i;
  3483.    j=indirect(ChoiceR);
  3484.    if (j==2) jump LKRD;
  3485.    if (j==3) rfalse;
  3486.    jump LKML;
  3487. ];
  3488.  
  3489. #IFV3;
  3490. [ DoMenu menu_choices EntryR ChoiceR;
  3491.   LowKey_Menu(menu_choices,EntryR,ChoiceR);
  3492. ];
  3493. #ENDIF;
  3494.  
  3495. #IFV5;
  3496. [ DoMenu menu_choices EntryR ChoiceR
  3497.          lines main_title main_wid cl i j oldcl pkey;
  3498.   if (pretty_flag==0)
  3499.   {   LowKey_Menu(menu_choices,EntryR,ChoiceR);
  3500.       rfalse;
  3501.   }
  3502.   menu_nesting++;
  3503.   menu_item=0;
  3504.   lines=indirect(EntryR);
  3505.   main_title=item_name; main_wid=item_width;
  3506.   cl=7;
  3507.   .ReDisplay;
  3508.       oldcl=0;
  3509.       @erase_window $ffff;
  3510.       i=lines+7;
  3511.       @split_window i;
  3512.       i = 0->33;
  3513.       if (i==0) i=80;
  3514.       @set_window 1;
  3515.       @set_cursor 1 1;
  3516.       style reverse;
  3517.       spaces(i); j=i/2-main_wid;
  3518.       @set_cursor 1 j;
  3519.       print_paddr main_title;
  3520.       @set_cursor 2 1; spaces(i);
  3521.       @set_cursor 2 2; print "N = next subject";
  3522.       j=i-12; @set_cursor 2 j; print "P = previous";
  3523.       @set_cursor 3 1; spaces(i);
  3524.       @set_cursor 3 2; print "RETURN = read subject";
  3525.       j=i-17; @set_cursor 3 j;
  3526.       if (menu_nesting==1)
  3527.           print "  Q = resume game";
  3528.       else
  3529.           print "Q = previous menu";
  3530.       style roman;
  3531.       @set_cursor 5 2; font off;
  3532.  
  3533.       if (ZRegion(menu_choices)==3) print_paddr menu_choices;
  3534.       else indirect(menu_choices);
  3535.  
  3536.       .KeyLoop;
  3537.       if (cl~=oldcl)
  3538.       {   if (oldcl>0) { @set_cursor oldcl 4; print " "; }
  3539.           @set_cursor cl 4; print ">";
  3540.       }
  3541.       oldcl=cl;
  3542.       @read_char 1 0 0 pkey;
  3543.       if (pkey=='N' or 'n' or 130)
  3544.           { cl++; if (cl==7+lines) cl=7; jump KeyLoop; }
  3545.       if (pkey=='P' or 'p' or 129)
  3546.           { cl--; if (cl==6)  cl=6+lines; jump KeyLoop; }
  3547.       if (pkey=='Q' or 'q' or 27) { jump QuitHelp; }
  3548.       if (pkey==10 or 13)
  3549.       {   @set_window 0; font on;
  3550.           new_line; new_line; new_line;
  3551.  
  3552.           menu_item=cl-6;
  3553.           indirect(EntryR);
  3554.  
  3555.           @erase_window $ffff;
  3556.           @split_window 1;
  3557.           i = 0->33; if (i==0) { i=80; }
  3558.           @set_window 1; @set_cursor 1 1; style reverse; spaces(i);
  3559.           j=i/2-item_width;
  3560.           @set_cursor 1 j;
  3561.           print_paddr item_name;
  3562.           style roman; @set_window 0; new_line;
  3563.  
  3564.           i = indirect(ChoiceR);
  3565.           if (i==2) jump ReDisplay;
  3566.           if (i==3) jump QuitHelp;
  3567.  
  3568.           print "^[Please press SPACE.]^";
  3569.           @read_char 1 0 0 pkey; jump ReDisplay;
  3570.       }
  3571.       jump KeyLoop;
  3572.       .QuitHelp;
  3573.       menu_nesting--; if (menu_nesting>0) rfalse;
  3574.       font on; @set_cursor 1 1;
  3575.       @erase_window $ffff; @set_window 0;
  3576.       new_line; new_line; new_line;
  3577.       if (deadflag==0) <<Look>>;
  3578. ];  
  3579. #ENDIF;
  3580.  
  3581. [ TimerE; "** Too many timers/daemons! Increase MAX_TIMERS **"; ];
  3582. [ TimerE2 obj; print "** Object "; PrintShortName(obj);
  3583.       " has no time_left property! **"; ];
  3584. [ TimerE3 obj; print "** Object "; PrintShortName(obj);
  3585.       " both timer and daemon! **"; ];
  3586.  
  3587. [ StartTimer obj timer i;
  3588.    for (i=0:i<active_timers:i++)
  3589.        if (the_timers-->i==obj)
  3590.        {   if (timer_flags->i==2) TimerE3(obj);
  3591.            rfalse;
  3592.        }
  3593.    for (i=0:i<active_timers:i++)
  3594.        if (the_timers-->i==0) jump FoundTSlot;
  3595.    i=active_timers++;
  3596.    if (i*2>=MAX_TIMERS) TimerE();
  3597.    .FoundTSlot;
  3598.    if (obj.&time_left==0) TimerE2(obj);
  3599.    the_timers-->i=obj; timer_flags->i=1; obj.time_left=timer;
  3600. ];
  3601.  
  3602. [ StopTimer obj i;
  3603.    for (i=0:i<active_timers:i++)
  3604.        if (the_timers-->i==obj) jump FoundTSlot2;
  3605.    rfalse;
  3606.    .FoundTSlot2;
  3607.    if (obj.&time_left==0) TimerE2(obj);
  3608.    the_timers-->i=0; obj.time_left=0;
  3609. ];
  3610.  
  3611. [ StartDaemon obj i;
  3612.    for (i=0:i<active_timers:i++)
  3613.        if (the_timers-->i==obj)
  3614.        {   if (timer_flags->i==1) TimerE3(obj);
  3615.            rfalse;
  3616.        }
  3617.    for (i=0:i<active_timers:i++)
  3618.        if (the_timers-->i==0) jump FoundTSlot3;
  3619.    i=active_timers++;
  3620.    if (i*2>=MAX_TIMERS) TimerE();
  3621.    .FoundTSlot3;
  3622.    the_timers-->i=obj; timer_flags->i=2;
  3623. ];
  3624.  
  3625. [ StopDaemon obj i;
  3626.    for (i=0:i<active_timers:i++)
  3627.        if (the_timers-->i==obj) jump FoundTSlot4;
  3628.    rfalse;
  3629.    .FoundTSlot4;
  3630.    the_timers-->i=0;
  3631. ];
  3632.  
  3633. [ EndTurnSequence i j;
  3634.  
  3635.    turns++;
  3636.    if (the_time~=NULL)
  3637.    {   if (time_rate>=0) the_time=the_time+time_rate;
  3638.        else
  3639.        {   time_step--;
  3640.            if (time_step==0)
  3641.            {   the_time++;
  3642.                time_step = -time_rate;
  3643.            }
  3644.        }
  3645.        the_time=the_time % 1440;
  3646.    }
  3647. #IFDEF DEBUG;
  3648.    if (debug_flag & 4 ~= 0)
  3649.    {   for (i=0: i<active_timers: i++)
  3650.        {   j=the_timers-->i;
  3651.            if (j~=0)
  3652.            {   PrintShortName(j);
  3653.                if (timer_flags->i==2) print ": daemon";
  3654.                else
  3655.                { print ": timer with ", j.time_left, " turns to go"; }
  3656.                new_line;
  3657.            }
  3658.        }
  3659.    }
  3660. #ENDIF;
  3661.    for (i=0: deadflag==0 && i<active_timers: i++)
  3662.    {   j=the_timers-->i;
  3663.        if (j~=0)
  3664.        {   if (timer_flags->i==2) RunRoutines(j,daemon);
  3665.            else
  3666.            {   if (j.time_left==0)
  3667.                {   StopTimer(j);
  3668.                    RunRoutines(j,time_out);
  3669.                }
  3670.                else
  3671.                    j.time_left=j.time_left-1;
  3672.            }
  3673.        }
  3674.    }
  3675.    if (deadflag==0)
  3676.    {   scope_reason=EACH_TURN_REASON; verb_word=0;
  3677.        DoScopeAction(location); SearchScope(location,player,0);
  3678.        scope_reason=PARSING_REASON;
  3679.    }
  3680.    if (deadflag==0) TimePasses();
  3681.    if (deadflag==0)
  3682.    {   AdjustLight();
  3683.        objectloop (i in player)
  3684.            if (i hasnt moved)
  3685.            {   give i moved;
  3686.                if (i has scored)
  3687.                {   score=score+OBJECT_SCORE;
  3688.                    things_score=things_score+OBJECT_SCORE;
  3689.                }
  3690.            }
  3691.    }
  3692. ];
  3693.  
  3694. [ AdjustLight flag i;
  3695.    i=lightflag;
  3696.    lightflag=OffersLight(parent(player));
  3697.  
  3698.    if (i==0 && lightflag==1)
  3699.    {   location=real_location; if (flag==0) <Look>;
  3700.    }
  3701.  
  3702.    if (i==1 && lightflag==0)
  3703.    {   real_location=location; location=thedark;
  3704.        if (flag==0) { NoteArrival();
  3705.                       return L__M(##Miscellany, 9); }
  3706.    }
  3707.    if (i==0 && lightflag==0) location=thedark;
  3708. ];
  3709.  
  3710. [ OffersLight i j;
  3711.    if (i==0) rfalse;
  3712.    if (i has light) rtrue;
  3713.    objectloop (j in i)
  3714.        if (HasLightSource(j)==1) rtrue;
  3715.    if (i has enterable || IsSeeThrough(i)==1)
  3716.        return OffersLight(parent(i));
  3717.    rfalse;
  3718. ];
  3719.  
  3720. [ HasLightSource i j ad;
  3721.    if (i==0) rfalse;
  3722.    if (i has light) rtrue;
  3723.    if (i has enterable || IsSeeThrough(i)==1)
  3724.    {   objectloop (i in i)
  3725.            if (HasLightSource(i)==1) rtrue;
  3726.    }
  3727.    ad = i.&add_to_scope;
  3728.    if (parent(i)~=0 && ad ~= 0)
  3729.    {   if (ad-->0 > top_object)
  3730.        {   ats_hls = 0; ats_flag = 1;
  3731.            RunRoutines(i, add_to_scope);
  3732.            ats_flag = 0; if (ats_hls == 1) rtrue;
  3733.        }
  3734.        else
  3735.        {   for (j=0:(2*j)<i.#add_to_scope:j++)
  3736.                if (HasLightSource(ad-->j)==1) rtrue;
  3737.        }
  3738.    }
  3739.    rfalse;
  3740. ];
  3741.  
  3742. [ SayProS x;
  3743.   if (x==0) print "is unset";
  3744.   else { print "means "; DefArt(x); }
  3745. ];
  3746.  
  3747. [ PronounsSub;
  3748.   print "At the moment, ~it~ "; SayProS(itobj);
  3749.   print ", ~him~ "; SayProS(himobj);
  3750.   if (player==selfobj) print " and"; else print ",";
  3751.   print " ~her~ "; SayProS(herobj);
  3752.   if (player==selfobj) ".";
  3753.   print " and ~me~ means ", object player; ".";
  3754. ];
  3755.  
  3756. [ ChangePlayer obj flag i;
  3757.   if (obj.&number==0) "** Player objects must have ~number~ prop **";
  3758.   if (actor==player) actor=obj;
  3759.   give player ~transparent ~concealed;
  3760.   i=obj; while(parent(i)~=0) { if (i has animate) give i transparent;
  3761.                                i=parent(i); }
  3762.   if (player==selfobj) player.short_name="your former self";
  3763.   player.number=real_location; player=obj;
  3764.   if (player==selfobj) player.short_name=NULL;
  3765.   give player transparent concealed animate proper;
  3766.   i=player; while(parent(i)~=0) i=parent(i); location=i;
  3767.   real_location=player.number;
  3768.   if (real_location==0) real_location=location;
  3769.   lightflag=OffersLight(parent(player));
  3770.   if (lightflag==0) location=thedark;
  3771.   print_player_flag=flag;
  3772. ];
  3773.  
  3774. [ ChangeDefault prop val;
  3775.    (0-->5)-->(prop-1) = val;
  3776. ];
  3777.  
  3778. [ RandomEntry tab;
  3779.   if (tab-->0==0) "** Table size 0 **";
  3780.   return tab-->(random(tab-->0));
  3781. ];
  3782.  
  3783. [ Indefart o;
  3784.    if (o hasnt proper) { PrintOrRun(o,article,1); print " "; }
  3785.    PrintShortName(o);
  3786. ];
  3787.  
  3788. [ Defart o;
  3789.    if (o hasnt proper) print "the "; PrintShortName(o);
  3790. ];
  3791.  
  3792. [ CDefart o;
  3793.    if (o hasnt proper) print "The "; PrintShortName(o);
  3794. ];
  3795.  
  3796. [ PrintShortName o;
  3797.    if (o==0) { print "nothing"; rtrue; }
  3798.    if (o>top_object || o<0) { print "<no such object>"; rtrue; }
  3799.    if (o==player) { print "yourself"; rtrue; }
  3800.    if (o.&short_name~=0 && PrintOrRun(o,short_name,1)~=0) rtrue;
  3801.    print_obj o;
  3802. ];
  3803.  
  3804. ! Provided for, e.g.,  print (DirectionName) obj.door_dir;
  3805.  
  3806. [ DirectionName d;
  3807.    switch(d)
  3808.    {   n_to: print "north";
  3809.        s_to: print "south";
  3810.        e_to: print "east";
  3811.        w_to: print "west";
  3812.        ne_to: print "northeast";
  3813.        nw_to: print "northwest";
  3814.        se_to: print "southeast";
  3815.        sw_to: print "southwest";
  3816.        u_to: print "up";
  3817.        d_to: print "down";
  3818.        in_to: print "in";
  3819.        out_to: print "out";
  3820.        default: "** No such direction **";
  3821.    }
  3822. ];
  3823.  
  3824. [ Banner i;
  3825. #IFV5; style bold; #ENDIF;
  3826.    print (string) Story;
  3827. #IFV5; style roman; #ENDIF;
  3828.    print (string) Headline;
  3829.    print "Release ", (0-->1) & $03ff, " / Serial number ";
  3830.    for (i=18:i<24:i++) print_char 0->i;
  3831.    print " / Inform v"; inversion;
  3832.    print " Library ", (string) LibRelease;
  3833. #ifdef DEBUG;
  3834.    print " D";
  3835. #endif;
  3836.    new_line;
  3837.    if (standard_interpreter > 0)
  3838.        print "Standard interpreter ",
  3839.            standard_interpreter/256, ".", standard_interpreter%256, "^";
  3840. ];
  3841.  
  3842. [ VersionSub;
  3843.   Banner();
  3844. #IFV5;
  3845.   print "Interpreter ", 0->$1e, " Version ", char 0->$1f, " / ";
  3846. #ENDIF;
  3847.   print "Library serial number ", (string) LibSerial, "^";
  3848. ];
  3849.  
  3850. ! ----------------------------------------------------------------------------
  3851.  
  3852.